系统框图:
知识点回顾:
default input #1ns output #1ns;
input #1 的含义是:当tb采样dut的输出信号时,会采用时钟边沿信号的#1 之前的信号,此处应该注意的是 tb的input信号是由dut的输出信号驱动的,而dut的输出是理想的信号,不存在延迟#1。
output #1 的含义是:tb 输出信号(驱动dut)时会延迟#1。
总体上会使得输出相对于时钟上升沿有一个时钟单位的延迟,但是输入不会有一个单位的超前。
fork jion:全部执行完毕,才会继续执行;
fork join_none:执行子程序的时候,父线程也会继续执行;
fork jion_any:任一语句执行完毕,就会继续执行父线程;
1接口的使用:
使用intf.ck来驱动数据,发现在时钟上升沿的1ns后才接收数据。
设置空闲周期:
function automatic void set_idle_cycles(int n);
idle_cycles = n;
//.........................................
repeat(idle_cycles) chnl_idle();
存在空闲周期且分别设为0、1、2
2仿真的结束:
task automatic burst_test();
// verification component initializationi
chnl0_gen.initialize(0);
chnl1_gen.initialize(1);
chnl2_gen.initialize(2);
chnl0_init.set_name("chnl0_init");
chnl1_init.set_name("chnl1_init");
chnl2_init.set_name("chnl2_init");
chnl0_init.set_idle_cycles(0);
chnl1_init.set_idle_cycles(0);
chnl2_init.set_idle_cycles(0);
$display("basic_test initialized components");
wait (rstn === 1'b1);
repeat(5) @(posedge clk);
$display("basic_test started testing DUT");
// Please check the SV book for fork-join basic knowledge
// and get understood it is for parallel thread running
fork
begin
repeat(500) chnl0_init.chnl_write(chnl0_gen.get_data());
chnl0_init.chnl_idle();
end
begin
repeat(500) chnl1_init.chnl_write(chnl1_gen.get_data());
chnl1_init.chnl_idle();
end
begin
repeat(500) chnl2_init.chnl_write(chnl2_gen.get_data());
chnl2_init.chnl_idle();
end
join
fork
wait(chnl0_init.intf.ch_margin == 'h20);
wait(chnl1_init.intf.ch_margin == 'h20);
wait(chnl2_init.intf.ch_margin == 'h20);
join
$display("basic_test finished testing DUT");
endtask
burst_test部分代码主要模仿,最后结束测试的的代码,利用fork join语句块书写,只要三个chnl_initiator都变满了,就停止仿真,变满不需要同时。
task automatic fifo_full_test();
// verification component initializationi
chnl0_gen.initialize(0);
chnl1_gen.initialize(1);
chnl2_gen.initialize(2);
chnl0_init.set_name("chnl0_init");
chnl1_init.set_name("chnl1_init");
chnl2_init.set_name("chnl2_init");
chnl0_init.set_idle_cycles(0);
chnl1_init.set_idle_cycles(0);
chnl2_init.set_idle_cycles(0);
$display("fifo_full_test started testing DUT");
fork: fork_all_run
forever chnl0_init.chnl_write(chnl0_gen.get_data());
forever chnl1_init.chnl_write(chnl1_gen.get_data());
forever chnl2_init.chnl_write(chnl2_gen.get_data());
join_none
$display("fifo_full_test: 3 initiators running now");
$display("fifo_full_test: waiting 3 channel fifos to be full");
fork
wait(chnl0_init.intf.ch_margin == 0);
wait(chnl1_init.intf.ch_margin == 0);
wait(chnl2_init.intf.ch_margin == 0);
join
$display("fifo_full_test: 3 channel fifos have reached full");
$display("fifo_full_test: stop 3 initiators running");
disable fork_all_run;
$display("fifo_full_test: set and ensure all agents' initiator are idle state");
fork
chnl0_init.chnl_idle();
chnl1_init.chnl_idle();
chnl2_init.chnl_idle();
join
$display("fifo_full_test waiting DUT transfering all of data");
fork
wait(chnl0_init.intf.ch_margin == 'h20);
wait(chnl1_init.intf.ch_margin == 'h20);
wait(chnl2_init.intf.ch_margin == 'h20);
join
$display("fifo_full_test: 3 channel fifos have transferred all data");
$display("fifo_full_test finished testing DUT");
endtask
3:类的例化和类的成员:
主要是module中的数据转移到了class里面,两部分代码比较。module可以直接传递接口,但是class需要用set_interface把接口指针传递到组件里面,组件是在module里面例化,在class里面是在initial里面实例化。
initial begin
basic_test();
burst_test();
fifo_full_test();
$display("*****************all of tests have been finished********************");
$finish();
end
//...........................
chnl_initiator chnl0_init(chnl0_if);
chnl_initiator chnl1_init(chnl1_if);
chnl_initiator chnl2_init(chnl2_if);
chnl_generator chnl0_gen();
chnl_generator chnl1_gen();
chnl_generator chnl2_gen();
initial begin
chnl0_init = new("chnl0_init");
chnl1_init = new("chnl1_init");
chnl2_init = new("chnl2_init");
chnl0_gen = new(0);
chnl1_gen = new(1);
chnl2_gen = new(2);
// assign the interface handle to each chnl_initiator objects
chnl0_init.set_interface(chnl0_if);
chnl1_init.set_interface(chnl1_if);
chnl2_init.set_interface(chnl2_if);
$display("*****************all of tests have been finished********************");
basic_test();
burst_test();
fifo_full_test();
$finish();
end
4包的定义和类的继承:
tb3和tb4的最大区别是多了一个agent,agent包含了generator和initiator
class chnl_fifo_full_test extends chnl_root_test;
function new(int ntrans = 1_000_000, string name = "chnl_fifo_full_test");
super.new(ntrans, name);
foreach(agent[i]) begin
this.agent[i].init.set_idle_cycles(0);
end
$display("%s configured objects", this.name);
endfunction
task run(); //开始结束都会打印信息。
$display("%s started testing DUT", this.name);
fork: fork_all_run
agent[0].run();
agent[1].run();
agent[2].run();
join_none //运行三个agent,join_none会继续执行父线程,不必等待任何一个子线程。
$display("%s: 3 agents running now", this.name);
$display("%s: waiting 3 channel fifos to be full", this.name); //三个fifo为满的时候,单个都拉低过的时候就结束测试,不是同时,打印信息。
fork
wait(agent[0].vif.ch_margin == 0);
wait(agent[1].vif.ch_margin == 0);
wait(agent[2].vif.ch_margin == 0);
join
$display("%s: 3 channel fifos have reached full", this.name);
$display("%s: stop 3 agents running", this.name);
disable fork_all_run; //disable会杀死所有的进程
$display("%s: set and ensure all agents' initiator are idle state", this.name);
fork
agent[0].init.chnl_idle();
agent[1].init.chnl_idle();
agent[2].init.chnl_idle();
join
$display("%s waiting DUT transfering all of data", this.name); //容量分别到达32个的时候,打印信息,已经传输全部信息,把那个结束测试DUT
fork
wait(agent[0].vif.ch_margin == 'h20);
wait(agent[1].vif.ch_margin == 'h20);
wait(agent[2].vif.ch_margin == 'h20);
join
$display("%s: 3 channel fifos have transferred all data", this.name);
$display("%s finished testing DUT", this.name);
endtask
endclass: chnl_fifo_full_test
endpackage: chnl_pkg
module tb4_ref;
logic clk;
logic rstn;
logic [31:0] mcdt_data;
logic mcdt_val;
logic [ 1:0] mcdt_id;
mcdt dut(
.clk_i (clk )
,.rstn_i (rstn )
,.ch0_data_i (chnl0_if.ch_data )
,.ch0_valid_i (chnl0_if.ch_valid )
,.ch0_ready_o (chnl0_if.ch_ready )
,.ch0_margin_o(chnl0_if.ch_margin )
,.ch1_data_i (chnl1_if.ch_data )
,.ch1_valid_i (chnl1_if.ch_valid )
,.ch1_ready_o (chnl1_if.ch_ready )
,.ch1_margin_o(chnl1_if.ch_margin )
,.ch2_data_i (chnl2_if.ch_data )
,.ch2_valid_i (chnl2_if.ch_valid )
,.ch2_ready_o (chnl2_if.ch_ready )
,.ch2_margin_o(chnl2_if.ch_margin )
,.mcdt_data_o (mcdt_data )
,.mcdt_val_o (mcdt_val )
,.mcdt_id_o (mcdt_id )
);
// clock generation
initial begin
clk <= 0;
forever begin
#5 clk <= !clk;
end
end
// reset trigger
initial begin
#10 rstn <= 0;
repeat(10) @(posedge clk);
rstn <= 1;
end
// import defined class from chnl_pkg
import chnl_pkg::*;
chnl_intf chnl0_if(.*);
chnl_intf chnl1_if(.*);
chnl_intf chnl2_if(.*);
chnl_basic_test basic_test;
chnl_burst_test burst_test;
chnl_fifo_full_test fifo_full_test;
initial begin
basic_test = new();
burst_test = new();
fifo_full_test = new();
basic_test.set_interface(chnl0_if, chnl1_if, chnl2_if);
burst_test.set_interface(chnl0_if, chnl1_if, chnl2_if);
fifo_full_test.set_interface(chnl0_if, chnl1_if, chnl2_if);
basic_test.run();
burst_test.run();
fifo_full_test.run();
$display("*****************all of tests have been finished********************");
$finish();
end
endmodule
软件环境的运行离不开以下三个方面,创建组件、传递接口和运行
对于接口的传递,简单来说就是set_interface往上对接channel的interface,往下对接agent的interface,然后对接initiator中的数据。