|
马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?注册
×
虽然已经入夏,但一直阴雨绵绵,闲来无事,无意中发现一个用AI生成完整且可运行的UVM Testbench的方法。以一个FIFO为DUT,下面是AI生成对应的Testbench的一个总结,麻雀虽小,五脏俱全。关键点概述DUT 接口
定义了 fifo_if 接口,包含 FIFO 的所有信号:时钟、复位、读写使能、数据输入输出、状态标志等 接口信号包括:full, empty, almost_full, almost_empty, overflow, underflow
事务项 (Transaction) 序列 (Sequence) fifo_sequence 生成测试序列 先填充 FIFO (16 次写操作) 等待 100ns 让 FIFO 变满 然后清空 FIFO (16 次读操作)
驱动 (Driver) 从序列获取事务并驱动到 DUT 接口 在时钟上升沿应用信号
监视器 (Monitor) 观察 DUT 接口上的活动 检测有效的读写操作并发送到分析端口 忽略当 FIFO 满时的写操作和空时的读操作
记分板 (Scoreboard) 比较写入和读出的数据 统计匹配和不匹配的数量 检查写入和读取的事务数量是否一致 在报告阶段输出结果
代理 (Agent) 环境 (Environment) 测试 (Test) base_test 提供基本框架 basic_test 执行基本 FIFO 测试 使用 objection 机制控制测试流程
顶层模块 (Top Module) 实例化 DUT 和接口 生成时钟和复位信号 设置 UVM 配置数据库 运行测试并检查最终结果
测试流程初始化阶段:时钟、复位生成 UVM 启动:配置虚拟接口并运行测试 测试执行: 先执行 16 次写操作填充 FIFO 等待 FIFO 变满 再执行 16 次读操作清空 FIFO
结果检查: 报告结果:输出匹配/不匹配统计
验证功能验证 FIFO 的基本读写功能 检查 FIFO 满和空状态行为 验证数据一致性 (写入和读出的数据是否匹配) 检查事务完整性 (写入和读取的数量是否一致)
可扩展性
下面是生成的testbench的代码:(对天发誓,一个字母没改)
`timescale 1ns/1ps
`include "uvm_macros.svh"
import uvm_pkg::*;
// Interface for FIFO DUT
interface fifo_if(input bit clk);
logic rst_n;
logic wr_en;
logic [31:0] data_in;
logic rd_en;
logic [31:0] data_out;
logic full;
logic empty;
logic almost_full;
logic almost_empty;
logic overflow;
logic underflow;
endinterface
// FIFO Transaction Item
class fifo_transaction extends uvm_sequence_item;
rand bit [31:0] data;
rand bit wr_en;
rand bit rd_en;
`uvm_object_utils_begin(fifo_transaction)
`uvm_field_int(data, UVM_ALL_ON)
`uvm_field_int(wr_en, UVM_ALL_ON)
`uvm_field_int(rd_en, UVM_ALL_ON)
`uvm_object_utils_end
function new(string name = "fifo_transaction");
super.new(name);
endfunction
constraint valid_ops {
wr_en != rd_en;
}
endclass
// FIFO Sequence
class fifo_sequence extends uvm_sequence #(fifo_transaction);
`uvm_object_utils(fifo_sequence)
rand int num_transactions = 20;
fifo_transaction transactions[$];
function new(string name = "fifo_sequence");
super.new(name);
endfunction
task body();
// First fill the FIFO
for (int i = 0; i < 16; i++) begin
fifo_transaction tx;
`uvm_create(tx)
tx.wr_en = 1;
tx.rd_en = 0;
assert(tx.randomize());
transactions.push_back(tx);
`uvm_send(tx)
end
// Wait for FIFO to be full
#100;
// Then empty the FIFO
for (int i = 0; i < 16; i++) begin
fifo_transaction tx;
`uvm_create(tx)
tx.wr_en = 0;
tx.rd_en = 1;
tx.data = 0; // Data doesn't matter for reads
transactions.push_back(tx);
`uvm_send(tx)
end
endtask
endclass
// FIFO Driver
class fifo_driver extends uvm_driver #(fifo_transaction);
`uvm_component_utils(fifo_driver)
virtual fifo_if vif;
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
if (!uvm_config_db#(virtual fifo_if)::get(this, "", "vif", vif)) begin
`uvm_fatal("NOVIF", "Virtual interface not set for driver")
end
endfunction
task run_phase(uvm_phase phase);
forever begin
fifo_transaction tx;
seq_item_port.get_next_item(tx);
@(posedge vif.clk);
vif.wr_en <= tx.wr_en;
vif.rd_en <= tx.rd_en;
vif.data_in <= tx.data;
seq_item_port.item_done();
end
endtask
endclass
// FIFO Monitor
class fifo_monitor extends uvm_monitor;
`uvm_component_utils(fifo_monitor)
virtual fifo_if vif;
uvm_analysis_port #(fifo_transaction) ap;
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
ap = new("ap", this);
if (!uvm_config_db#(virtual fifo_if)::get(this, "", "vif", vif)) begin
`uvm_fatal("NOVIF", "Virtual interface not set for monitor")
end
endfunction
task run_phase(uvm_phase phase);
forever begin
fifo_transaction tx = fifo_transaction::type_id::create("tx");
@(posedge vif.clk);
if (vif.wr_en && !vif.full) begin
tx.wr_en = 1;
tx.rd_en = 0;
tx.data = vif.data_in;
ap.write(tx);
end
if (vif.rd_en && !vif.empty) begin
tx = fifo_transaction::type_id::create("tx");
tx.wr_en = 0;
tx.rd_en = 1;
tx.data = vif.data_out;
ap.write(tx);
end
end
endtask
endclass
// FIFO Scoreboard
class fifo_scoreboard extends uvm_scoreboard;
`uvm_component_utils(fifo_scoreboard)
uvm_analysis_imp #(fifo_transaction, fifo_scoreboard) item_export;
fifo_transaction write_q[$];
fifo_transaction read_q[$];
int write_count = 0;
int read_count = 0;
int match_count = 0;
int mismatch_count = 0;
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
item_export = new("item_export", this);
endfunction
function void write(fifo_transaction tx);
if (tx.wr_en) begin
write_q.push_back(tx);
write_count++;
end
else if (tx.rd_en) begin
read_q.push_back(tx);
read_count++;
end
endfunction
function void report_phase(uvm_phase phase);
super.report_phase(phase);
// Compare write and read transactions
while (write_q.size() > 0 && read_q.size() > 0) begin
fifo_transaction write_tx = write_q.pop_front();
fifo_transaction read_tx = read_q.pop_front();
if (write_tx.data !== read_tx.data) begin
`uvm_error("SCOREBOARD", $sformatf("Data mismatch! Expected: 0x%0h, Actual: 0x%0h",
write_tx.data, read_tx.data))
mismatch_count++;
end
else begin
match_count++;
end
end
// Check for transaction count mismatch
if (write_count != read_count) begin
`uvm_error("SCOREBOARD", $sformatf("Transaction count mismatch! Writes: %0d, Reads: %0d",
write_count, read_count))
end
`uvm_info("SCOREBOARD", $sformatf("Scoreboard Results: Matches=%0d, Mismatches=%0d, Writes=%0d, Reads=%0d",
match_count, mismatch_count, write_count, read_count), UVM_LOW)
endfunction
endclass
// FIFO Agent
class fifo_agent extends uvm_agent;
`uvm_component_utils(fifo_agent)
fifo_driver driver;
fifo_monitor monitor;
uvm_sequencer #(fifo_transaction) sequencer;
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
monitor = fifo_monitor::type_id::create("monitor", this);
if (get_is_active() == UVM_ACTIVE) begin
driver = fifo_driver::type_id::create("driver", this);
sequencer = uvm_sequencer#(fifo_transaction)::type_id::create("sequencer", this);
end
endfunction
function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
if (get_is_active() == UVM_ACTIVE) begin
driver.seq_item_port.connect(sequencer.seq_item_export);
end
if (!uvm_config_db#(virtual fifo_if)::get(this, "", "vif", driver.vif)) begin
`uvm_fatal("NOVIF", "Virtual interface not set for driver")
end
if (!uvm_config_db#(virtual fifo_if)::get(this, "", "vif", monitor.vif)) begin
`uvm_fatal("NOVIF", "Virtual interface not set for monitor")
end
endfunction
endclass
// FIFO Environment
class fifo_env extends uvm_env;
`uvm_component_utils(fifo_env)
fifo_agent agent;
fifo_scoreboard scoreboard;
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
agent = fifo_agent::type_id::create("agent", this);
scoreboard = fifo_scoreboard::type_id::create("scoreboard", this);
uvm_config_db#(uvm_active_passive_enum)::set(this, "agent", "is_active", UVM_ACTIVE);
endfunction
function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
agent.monitor.ap.connect(scoreboard.item_export);
endfunction
endclass
// Base Test
class base_test extends uvm_test;
`uvm_component_utils(base_test)
fifo_env env;
fifo_sequence seq;
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
env = fifo_env::type_id::create("env", this);
seq = fifo_sequence::type_id::create("seq");
endfunction
task run_phase(uvm_phase phase);
phase.raise_objection(this);
seq.start(env.agent.sequencer);
phase.drop_objection(this);
endtask
endclass
// Basic Test
class basic_test extends base_test;
`uvm_component_utils(basic_test)
function new(string name = "basic_test", uvm_component parent = null);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
endfunction
task run_phase(uvm_phase phase);
phase.raise_objection(this);
`uvm_info(get_type_name(), "Starting basic FIFO test", UVM_LOW)
seq.start(env.agent.sequencer);
// Wait for all transactions to complete
#200;
phase.drop_objection(this);
endtask
endclass
// Top Module
module top;
import uvm_pkg::*;
bit clk;
bit rst_n;
// Instantiate DUT interface
fifo_if fif_if(clk);
// Instantiate DUT
FIFO #(
.DATA_WIDTH(32),
.FIFO_DEPTH(16),
.ALMOST_FULL_THRESH(12),
.ALMOST_EMPTY_THRESH(4)
) dut (
.clk(clk),
.rst_n(fif_if.rst_n),
.wr_en(fif_if.wr_en),
.data_in(fif_if.data_in),
.rd_en(fif_if.rd_en),
.data_out(fif_if.data_out),
.full(fif_if.full),
.empty(fif_if.empty),
.almost_full(fif_if.almost_full),
.almost_empty(fif_if.almost_empty),
.overflow(fif_if.overflow),
.underflow(fif_if.underflow)
);
// Clock generation
initial begin
clk = 0;
forever #5 clk = ~clk;
end
// Reset generation
initial begin
fif_if.rst_n = 0;
#20 fif_if.rst_n = 1;
end
// UVM test setup
initial begin
uvm_config_db#(virtual fifo_if)::set(null, "uvm_test_top.env*", "vif", fif_if);
run_test("basic_test");
end
// Final check
final begin
if (uvm_report_server::get_server().get_severity_count(UVM_FATAL) > 0) begin
$display("Simulation finished with UVM_FATAL errors");
$finish(2);
end
else if (uvm_report_server::get_server().get_severity_count(UVM_ERROR) > 0) begin
$display("Simulation finished with UVM_ERROR errors");
$finish(1);
end
else begin
$display("Test passed successfully");
$finish(0);
end
end
endmodule
完整代码
完整版DUT,Testbench和Testcase 代码打包文件:
FIFO_release.zip
(8.1 KB , 下载次数:
3 )
实现方式
1. AIDEV Project Workspace
2. 输入dut fifo的功能需求,选择生成和编译选项,填入额外的test需求,提交,坐等代码生成,发入你邮箱。
|
|