实验2主要让我们逐渐从使用硬件盒子验证过渡到软件盒子(class) 来验证设计,理解类的继承和层次包含关系。
接口的使用
修改lab1的代码,使用接口来连接验证组件和DUT,所以验证组件chnl_initiator
的端口变得非常“干净”,即chnl_intf intf
。在使用接口之前需要定义接口chnl_intf
和内部的接口,同时也要声明一个时钟块,它的功能是为了消除可能存在的竞争问题,确保时钟驱动数据之间有一定的延迟,以便于DUT顺利采样。
引入接口后的代码,例化的实例包括了只产生数据的channel generator
,只负责发送数据的channel initiator
以及作为验证组件和DUT之间的接口chnl_intf
实验要求:
channel_initiator
发送的数据例如valid
和data
与时钟clk
均在同一个变化沿,没有任何延迟。这种0延迟的数据发送不利于波形的查看,要在之前的代码基础上使用intf.ck
的方式来做数据驱动,并且再观察波形,查看驱动的数据与时钟上升沿的延迟。- 为了更好地控制相邻数据之间的空闲间隔,引入了一个变量
idle_cycles
,它表示相邻有效数据之间的间隔。之前的代码会使得有效数据之间保持固定的一个空闲周期,需要使用idle_cycles
来灵活控制有效数据之间的空闲周期。
1 | interface chnl_intf(input clk, input rstn); |
2 | logic [31:0] ch_data; |
3 | logic ch_valid; |
4 | logic ch_ready; |
5 | logic [5:0] ch_margin; |
6 | clocking drv_ck @(posedge clk); |
7 | default input #1ns output #1ns; |
8 | output ch_data, ch_valid; |
9 | input ch_ready, ch_margin; |
10 | endclocking |
11 | endinterface |
12 | |
13 | module chnl_initiator(chnl_intf intf); |
14 | string name; |
15 | int idle_cycles = 1; |
16 | function automatic void set_idle_cycles(int n); |
17 | idle_cycles = n; |
18 | endfunction |
19 | function automatic void set_name(string s); |
20 | name = s; |
21 | endfunction |
22 | task automatic chnl_write(input logic[31:0] data); |
23 | @(posedge intf.clk); |
24 | // USER TODO 1.1 |
25 | // Please use the clocking drv_ck of chnl_intf to drive data |
26 | intf.drv_ck.ch_valid <= 1; |
27 | intf.drv_ck.ch_data <= data; |
28 | wait(intf.ch_ready === 'b1); |
29 | $display("%t channel initiator [%s] sent data %x", $time, name, data); |
30 | // USER TODO 1.2 |
31 | // Apply variable idle_cycles and decide how many idle cycles to be |
32 | // inserted between two sequential data |
33 | repeat(idle_cycles) chnl_idle(); |
34 | endtask |
35 | task automatic chnl_idle(); |
36 | @(posedge intf.clk); |
37 | // USER TODO 1.1 |
38 | // Please use the clocking drv_ck of chnl_intf to drive data |
39 | intf.drv_ck.ch_valid <= 0; |
40 | intf.drv_ck.ch_data <= 0; |
41 | endtask |
42 | endmodule |
仿真的结束
在tb module,利用fork-join语句来实现三个chnl_initiator同时发送数据的要求。同时将不同的test也组装到task中,以此来区分不同的测试内容,这是由于每一个测试任务的测试目的和要求都不相同。
实验要求:
- 参考
basic_test()
, 实现burst_test()
方法,使得每个chnl_initiator
的idle_cycles
设置为0,同时发送500个数据,最后结束测试。 - 实现
fifo_full_test()
方法,使得无论采取什么数值的idle_cycles
,也无论发送多少个数据,只要各个chnl_initiator
的不停发送使得对应的channel缓存变为满标志(ready拉低),那么可以在三个channel都拉低ready时(不必要同时拉低,可以先后拉低即可),便可以立即结束测试
1 | // each channel send data with idle_cycles inside [1:3] |
2 | // each channel send out 200 data |
3 | // then to finish the test |
4 | task automatic basic_test(); |
5 | // verification component initializationi |
6 | chnl0_gen.initialize(0); |
7 | chnl1_gen.initialize(1); |
8 | chnl2_gen.initialize(2); |
9 | chnl0_init.set_name("chnl0_init"); |
10 | chnl1_init.set_name("chnl1_init"); |
11 | chnl2_init.set_name("chnl2_init"); |
12 | chnl0_init.set_idle_cycles($urandom_range(1, 3)); |
13 | chnl1_init.set_idle_cycles($urandom_range(1, 3)); |
14 | chnl2_init.set_idle_cycles($urandom_range(1, 3)); |
15 | $display("basic_test initialized components"); |
16 | wait (rstn === 1'b1); |
17 | repeat(5) @(posedge clk); |
18 | $display("basic_test started testing DUT"); |
19 | // Please check the SV book for fork-join basic knowledge |
20 | // and get understood it is for parallel thread running |
21 | fork |
22 | repeat(100) chnl0_init.chnl_write(chnl0_gen.get_data()); |
23 | repeat(100) chnl1_init.chnl_write(chnl1_gen.get_data()); |
24 | repeat(100) chnl2_init.chnl_write(chnl2_gen.get_data()); |
25 | join |
26 | // waiting until transfering all of the data |
27 | fork |
28 | wait(chnl0_init.intf.ch_margin == 'h20); |
29 | wait(chnl1_init.intf.ch_margin == 'h20); |
30 | wait(chnl2_init.intf.ch_margin == 'h20); |
31 | join |
32 | $display("basic_test finished testing DUT"); |
33 | endtask |
34 | |
35 | // USER TODO 2.1 |
36 | // each channel send data with idle_cycles == 0 |
37 | // each channel send out 500 data |
38 | // then to finish the test |
39 | task automatic burst_test(); |
40 | // initiator |
41 | chnl0_init.set_name("chnl0_init"); |
42 | chnl1_init.set_name("chnl1_init"); |
43 | chnl2_init.set_name("chnl2_init"); |
44 | |
45 | chnl0_init.set_idle_cycles(0); |
46 | chnl1_init.set_idle_cycles(0); |
47 | chnl2_init.set_idle_cycles(0); |
48 | // generator |
49 | chnl0_gen.initialize(0); |
50 | chnl1_gen.initialize(1); |
51 | chnl2_gen.initialize(2); |
52 | $display("burst_test initialized components"); |
53 | wait (rstn === 1'b1); |
54 | repeat(5) @(posedge clk); |
55 | $display("burst_test started testing DUT"); |
56 | fork |
57 | begin |
58 | repeat(500) chnl0_init.chnl_write(chnl0_gen.get_data()); |
59 | chnl0_init.chnl_idle(); |
60 | end |
61 | begin |
62 | repeat(500) chnl1_init.chnl_write(chnl1_gen.get_data()); |
63 | chnl1_init.chnl_idle(); |
64 | end |
65 | begin |
66 | repeat(500) chnl2_init.chnl_write(chnl2_gen.get_data()); |
67 | chnl2_init.chnl_idle(); |
68 | end |
69 | join |
70 | |
71 | fork |
72 | wait(chnl0_init.intf.ch_margin == 'h20); |
73 | wait(chnl1_init.intf.ch_margin == 'h20); |
74 | wait(chnl2_init.intf.ch_margin == 'h20); |
75 | join |
76 | $display("burst_test finished testung DUT"); |
77 | |
78 | endtask |
79 | |
80 | // USER TODO 2.2 |
81 | // The test should be immediately finished when all of channels |
82 | // have been reached fifo full state, but not all reaching |
83 | // fifo full at the same time |
84 | task automatic fifo_full_test(); |
85 | // initiator |
86 | chnl0_init.set_name("chnl0_init"); |
87 | chnl1_init.set_name("chnl1_init"); |
88 | chnl2_init.set_name("chnl2_init"); |
89 | |
90 | chnl0_init.set_idle_cycles(0); |
91 | chnl1_init.set_idle_cycles(0); |
92 | chnl2_init.set_idle_cycles(0); |
93 | // generator |
94 | chnl0_gen.initialize(0); |
95 | chnl1_gen.initialize(1); |
96 | chnl2_gen.initialize(2); |
97 | $display("burst_test initialized components"); |
98 | wait (rstn === 1'b1); |
99 | repeat(5) @(posedge clk); |
100 | $display("fifo_full_test started testing DUT"); |
101 | // fork join_none,执行内部语句时,父线程继续执行 |
102 | fork |
103 | forever chnl0_init.chnl_write(chnl0_gen.get_data()); |
104 | forever chnl1_init.chnl_write(chnl1_gen.get_data()); |
105 | forever chnl2_init.chnl_write(chnl2_gen.get_data()); |
106 | join_none |
107 | // 分别三个都满足,并非同时 |
108 | fork |
109 | wait(chnl0_init.intf.ch_margin == 0); |
110 | wait(chnl1_init.intf.ch_margin == 0); |
111 | wait(chnl2_init.intf.ch_margin == 0); |
112 | join |
113 | $display("fifo_full_test finished testing DUT"); |
114 | |
115 | $display("fifo_full_test: set and ensure all agents' initiator are idle state"); |
116 | fork |
117 | chnl0_init.chnl_idle(); |
118 | chnl1_init.chnl_idle(); |
119 | chnl2_init.chnl_idle(); |
120 | join |
121 | |
122 | $display("fifo_full_test waiting DUT transfering all of data"); |
123 | fork |
124 | wait(chnl0_init.intf.ch_margin == 'h20); |
125 | wait(chnl1_init.intf.ch_margin == 'h20); |
126 | wait(chnl2_init.intf.ch_margin == 'h20); |
127 | join |
128 | $display("fifo_full_test: 3 channel fifos have transferred all data"); |
129 | |
130 | $display("fifo_full_test finished testing DUT"); |
131 | endtask |
fork join_none
在执行内部语句时父线程继续执行,fork join
只要内部语句分别都满足,并非同时满足,就可跳出
类的例化和类的成员
将之前用来封装验证功能的硬件盒子(module)中的数据和内容移植到软件盒子(class)中来。掌握类的基本例化,类的成员变量访问权限以及类的成员方法如何定义和使用。
实验要求:
- 将module的
chnl_initiator
和chnl_generator
改成class的chnl_initiator
和chnl_generator
,同时定义一个用来封装发送数据的类chnl_trans
。 - 由于每一个
chnl_initiator
都需要使用接口chnl_intf
来发送数据,在发送数据之前需要确保chnl_initiator
中的接口不是悬空的,即需要由外部被传递。通过调用chnl_initiator
的方法来完成接口的传递。 - 注意class里面声明接口的指针必须要加上
virtual
关键字。
1 | class chnl_trans; |
2 | int data; |
3 | int id; |
4 | int num; |
5 | endclass |
6 | |
7 | class chnl_initiator; |
8 | local string name; |
9 | local int idle_cycles; |
10 | virtual chnl_intf intf; |
11 | |
12 | function new(string name = "chnl_initiator"); |
13 | this.name = name; |
14 | this.idle_cycles = 1; |
15 | endfunction |
16 | |
17 | function void set_idle_cycles(int n); |
18 | this.idle_cycles = n; |
19 | endfunction |
20 | |
21 | function void set_name(string s); |
22 | this.name = s; |
23 | endfunction |
24 | |
25 | function void set_interface(virtual chnl_intf intf); |
26 | if(intf == null) |
27 | $error("interface handle is NULL, please check if target interface has been intantiated"); |
28 | else |
29 | this.intf = intf; |
30 | endfunction |
31 | |
32 | task chnl_write(input chnl_trans t); |
33 | @(posedge intf.clk); |
34 | // USER TODO 1.1 |
35 | // Please use the clocking drv_ck of chnl_intf to drive data |
36 | intf.drv_ck.ch_valid <= 1; |
37 | intf.drv_ck.ch_data <= t.data; |
38 | |
39 | @(negedge intf.clk); |
40 | wait(intf.ch_ready === 'b1); |
41 | $display("%t channel initiator [%s] sent data %x", $time, name, t.data); |
42 | // USER TODO 1.2 |
43 | // Apply variable idle_cycles and decide how many idle cycles to be |
44 | // inserted between two sequential data |
45 | repeat(this.idle_cycles) chnl_idle(); |
46 | endtask |
47 | |
48 | task chnl_idle(); |
49 | @(posedge intf.clk); |
50 | // USER TODO 1.1 |
51 | // Please use the clocking drv_ck of chnl_intf to drive data |
52 | intf.drv_ck.ch_valid <= 0; |
53 | intf.drv_ck.ch_data <= 0; |
54 | endtask |
55 | endclass |
56 | |
57 | // USER TODO 3.4 |
58 | // check if the object use is correct? |
59 | class chnl_generator; |
60 | chnl_trans trans[$]; |
61 | int num; |
62 | int id; |
63 | chnl_trans t; |
64 | function new(int n); |
65 | this.id = n; |
66 | this.num = 0; |
67 | //t = new(); |
68 | endfunction |
69 | function chnl_trans get_trans(); |
70 | t = new(); |
71 | t.data = 'h00C0_0000 + (this.id<<16) + this.num; |
72 | t.id = this.id; |
73 | t.num = this.num; |
74 | this.num++; |
75 | this.trans.push_back(t); |
76 | return t; |
77 | endfunction |
78 | endclass |
79 | // module tb |
80 | module tb3; |
81 | logic clk; |
82 | logic rstn; |
83 | logic [31:0] mcdt_data; |
84 | logic mcdt_val; |
85 | logic [ 1:0] mcdt_id; |
86 | |
87 | mcdt dut( |
88 | .clk_i (clk ) |
89 | ,.rstn_i (rstn ) |
90 | ,.ch0_data_i (chnl0_if.ch_data ) |
91 | ,.ch0_valid_i (chnl0_if.ch_valid ) |
92 | ,.ch0_ready_o (chnl0_if.ch_ready ) |
93 | ,.ch0_margin_o(chnl0_if.ch_margin ) |
94 | ,.ch1_data_i (chnl1_if.ch_data ) |
95 | ,.ch1_valid_i (chnl1_if.ch_valid ) |
96 | ,.ch1_ready_o (chnl1_if.ch_ready ) |
97 | ,.ch1_margin_o(chnl1_if.ch_margin ) |
98 | ,.ch2_data_i (chnl2_if.ch_data ) |
99 | ,.ch2_valid_i (chnl2_if.ch_valid ) |
100 | ,.ch2_ready_o (chnl2_if.ch_ready ) |
101 | ,.ch2_margin_o(chnl2_if.ch_margin ) |
102 | ,.mcdt_data_o (mcdt_data ) |
103 | ,.mcdt_val_o (mcdt_val ) |
104 | ,.mcdt_id_o (mcdt_id ) |
105 | ); |
106 | // clock generation |
107 | initial begin |
108 | clk <= 0; |
109 | forever begin |
110 | #5 clk <= !clk; |
111 | end |
112 | end |
113 | |
114 | // reset trigger |
115 | initial begin |
116 | #10 rstn <= 0; |
117 | repeat(10) @(posedge clk); |
118 | rstn <= 1; |
119 | end |
120 | |
121 | chnl_intf chnl0_if(.*); |
122 | chnl_intf chnl1_if(.*); |
123 | chnl_intf chnl2_if(.*); |
124 | |
125 | chnl_initiator chnl0_init; |
126 | chnl_initiator chnl1_init; |
127 | chnl_initiator chnl2_init; |
128 | chnl_generator chnl0_gen; |
129 | chnl_generator chnl1_gen; |
130 | chnl_generator chnl2_gen; |
131 | |
132 | initial begin |
133 | // USER TODO 3.1 |
134 | // instantiate the components chn0/1/2_init chnl0/1/2_gen |
135 | chnl0_init = new("chnl0_init"); |
136 | chnl1_init = new("chnl1_init"); |
137 | chnl2_init = new("chnl2_init"); |
138 | |
139 | chnl0_gen = new(0); |
140 | chnl1_gen = new(1); |
141 | chnl2_gen = new(2); |
142 | |
143 | |
144 | // USER TODO 3.2 |
145 | // assign the interface handle to each chnl_initiator objects |
146 | chnl0_init.set_interface(chnl0_if); |
147 | chnl1_init.set_interface(chnl1_if); |
148 | chnl2_init.set_interface(chnl2_if); |
149 | // USER TODO 3.3 |
150 | // START TESTs |
151 | basic_test(); |
152 | burst_test(); |
153 | fifo_full_test(); |
154 | |
155 | $display("*****************all of tests have been finished********************"); |
156 | $finish(); |
157 | end |
158 | endmodule |
包的定义和类的继承
进一步引入新的类chnl_agent
、chnl_root_test
、chnl_basic_test
、chnl_burst_test
、chnl_fifo_full_test
,同时将所有的类(都是与channel相关的验证组件类)封装到专门包裹软件类的容器package chnl_pkg
中且完成编译。编译后的chnl_pkg
会被默认编译到work中,与其它的module是一同并列放置的。
chnl_agent
:将它作为一个标准组件单元,包括generator、driver(initiator)和monitor。所以chnl_initiator
和chnl_generator
应该放在agent中例化- task实现的测试任务由类来实现:父类
chnl_root_test
,子类chnl_basic_test
、chnl_burst_test
、chnl_fifo_full_test
实验要求:
- 将各个类封装到
package chnl_pkg
中,然后需要从chnl_pkg
中引入其中的类,并在module tb4 中声明类的句柄 - 实现类
chnl_burst_test
、chnl_fifo_full_test
- 例化已经声明过的三个组件
- 完成从test一层的接口传递任务,使得其内部各个组件都可以得到需要的接口
1 |
|
2 | |
3 | interface chnl_intf(input clk, input rstn); |
4 | logic [31:0] ch_data; |
5 | logic ch_valid; |
6 | logic ch_ready; |
7 | logic [ 5:0] ch_margin; |
8 | clocking drv_ck @(posedge clk); |
9 | default input #1ns output #1ns; |
10 | output ch_data, ch_valid; |
11 | input ch_ready, ch_margin; |
12 | endclocking |
13 | endinterface |
14 | |
15 | package chnl_pkg; |
16 | class chnl_trans; |
17 | int data; |
18 | int id; |
19 | int num; |
20 | endclass: chnl_trans |
21 | |
22 | class chnl_initiator; |
23 | local string name; |
24 | local int idle_cycles; |
25 | local virtual chnl_intf intf; |
26 | |
27 | function new(string name = "chnl_initiator"); |
28 | this.name = name; |
29 | this.idle_cycles = 1; |
30 | endfunction |
31 | |
32 | function void set_idle_cycles(int n); |
33 | this.idle_cycles = n; |
34 | endfunction |
35 | |
36 | function void set_name(string s); |
37 | this.name = s; |
38 | endfunction |
39 | |
40 | function void set_interface(virtual chnl_intf intf); |
41 | if(intf == null) |
42 | $error("interface handle is NULL, please check if target interface has been intantiated"); |
43 | else |
44 | this.intf = intf; |
45 | endfunction |
46 | |
47 | task chnl_write(input chnl_trans t); |
48 | @(posedge intf.clk); |
49 | // USER TODO 1.1 |
50 | // Please use the clocking drv_ck of chnl_intf to drive data |
51 | intf.drv_ck.ch_valid <= 1; |
52 | intf.drv_ck.ch_data <= t.data; |
53 | @(negedge intf.clk); |
54 | wait(intf.ch_ready === 'b1); |
55 | $display("%t channel initiator [%s] sent data %x", $time, name, t.data); |
56 | // USER TODO 1.2 |
57 | // Apply variable idle_cycles and decide how many idle cycles to be |
58 | // inserted between two sequential data |
59 | repeat(this.idle_cycles) chnl_idle(); |
60 | endtask |
61 | |
62 | task chnl_idle(); |
63 | @(posedge intf.clk); |
64 | // USER TODO 1.1 |
65 | // Please use the clocking drv_ck of chnl_intf to drive data |
66 | intf.drv_ck.ch_valid <= 0; |
67 | intf.drv_ck.ch_data <= 0; |
68 | endtask |
69 | endclass: chnl_initiator |
70 | |
71 | class chnl_generator; |
72 | chnl_trans trans[$]; |
73 | int num; |
74 | int id; |
75 | function new(int n); |
76 | this.id = n; |
77 | this.num = 0; |
78 | endfunction |
79 | function chnl_trans get_trans(); |
80 | chnl_trans t = new(); |
81 | t.data = 'h00C0_0000 + (this.id<<16) + this.num; |
82 | t.id = this.id; |
83 | t.num = this.num; |
84 | this.num++; |
85 | this.trans.push_back(t); |
86 | return t; |
87 | endfunction |
88 | endclass: chnl_generator |
89 | |
90 | class chnl_agent; |
91 | chnl_generator gen; |
92 | chnl_initiator init; |
93 | local int ntrans; |
94 | virtual chnl_intf vif; |
95 | function new(string name = "chnl_agent", int id = 0, int ntrans = 1); |
96 | this.gen = new(id); |
97 | this.init = new(name); |
98 | this.ntrans = ntrans; |
99 | endfunction |
100 | function void set_ntrans(int n); |
101 | this.ntrans = n; |
102 | endfunction |
103 | function void set_interface(virtual chnl_intf vif); |
104 | this.vif = vif; |
105 | init.set_interface(vif); |
106 | endfunction |
107 | task run(); |
108 | repeat(this.ntrans) this.init.chnl_write(this.gen.get_trans()); |
109 | this.init.chnl_idle(); // set idle after all data sent out |
110 | endtask |
111 | endclass: chnl_agent |
112 | |
113 | class chnl_root_test; |
114 | chnl_agent agent[3]; |
115 | protected string name; |
116 | function new(int ntrans = 100, string name = "chnl_root_test"); |
117 | foreach(agent[i]) begin |
118 | this.agent[i] = new($sformatf("chnl_agent%0d",i), i, ntrans); |
119 | end |
120 | this.name = name; |
121 | $display("%s instantiate objects", this.name); |
122 | endfunction |
123 | task run(); |
124 | $display("%s started testing DUT", this.name); |
125 | fork |
126 | agent[0].run(); |
127 | agent[1].run(); |
128 | agent[2].run(); |
129 | join |
130 | $display("%s waiting DUT transfering all of data", this.name); |
131 | fork |
132 | wait(agent[0].vif.ch_margin == 'h20); |
133 | wait(agent[1].vif.ch_margin == 'h20); |
134 | wait(agent[2].vif.ch_margin == 'h20); |
135 | join |
136 | $display("%s: 3 channel fifos have transferred all data", this.name); |
137 | $display("%s finished testing DUT", this.name); |
138 | endtask |
139 | function void set_interface(virtual chnl_intf ch0_vif, virtual chnl_intf ch1_vif, virtual chnl_intf ch2_vif); |
140 | agent[0].set_interface(ch0_vif); |
141 | agent[1].set_interface(ch1_vif); |
142 | agent[2].set_interface(ch2_vif); |
143 | endfunction |
144 | endclass |
145 | // each channel send data with idle_cycles inside [1:3] |
146 | // each channel send out 200 data |
147 | // then to finish the test |
148 | class chnl_basic_test extends chnl_root_test; |
149 | function new(int ntrans = 200, string name = "chnl_basic_test"); |
150 | super.new(ntrans, name); |
151 | foreach(agent[i]) begin |
152 | this.agent[i].init.set_idle_cycles($urandom_range(1, 3)); |
153 | end |
154 | $display("%s configured objects", this.name); |
155 | endfunction |
156 | endclass: chnl_basic_test |
157 | // USER TODO 4.2 |
158 | // Refer to chnl_basic_test, and extend another 2 tests |
159 | // chnl_burst_test, chnl_fifo_full_test |
160 | // each channel send data with idle_cycles == 0 |
161 | // each channel send out 500 data |
162 | // then to finish the test |
163 | class chnl_burst_test extends chnl_root_test; |
164 | //USER TODO |
165 | function new(int ntrans = 500, string name = "chnl_burst_test"); |
166 | super.new(ntrans, name); |
167 | foreach(agent[i]) begin |
168 | this.agent[i].init.set_idle_cycles(0); |
169 | end |
170 | $display("%s configured objects", this.name); |
171 | endfunction |
172 | endclass: chnl_burst_test |
173 | // USER TODO 4.2 |
174 | // The test should be immediately finished when all of channels |
175 | // have been reached fifo full state, but not all reaching |
176 | // fifo full at the same time |
177 | class chnl_fifo_full_test extends chnl_root_test; |
178 | // USER TODO |
179 | function new(int ntrans = 1_000_000, string name = "chnl_fifo_full_test"); |
180 | super.new(ntrans, name); |
181 | foreach(agent[i]) begin |
182 | this.agent[i].init.set_idle_cycles(0); |
183 | end |
184 | $display("%s configured objects", this.name); |
185 | endfunction |
186 | task run(); |
187 | $display("%s started testing DUT", this.name); |
188 | fork: fork_all_run |
189 | agent[0].run(); |
190 | agent[1].run(); |
191 | agent[2].run(); |
192 | join_none |
193 | $display("%s: 3 agents running now", this.name); |
194 | |
195 | $display("%s: waiting 3 channel fifos to be full", this.name); |
196 | fork |
197 | wait(agent[0].vif.ch_margin == 0); |
198 | wait(agent[1].vif.ch_margin == 0); |
199 | wait(agent[2].vif.ch_margin == 0); |
200 | join |
201 | $display("%s: 3 channel fifos have reached full", this.name); |
202 | |
203 | $display("%s: stop 3 agents running", this.name); |
204 | disable fork_all_run; |
205 | $display("%s: set and ensure all agents' initiator are idle state", this.name); |
206 | fork |
207 | agent[0].init.chnl_idle(); |
208 | agent[1].init.chnl_idle(); |
209 | agent[2].init.chnl_idle(); |
210 | join |
211 | |
212 | $display("%s waiting DUT transfering all of data", this.name); |
213 | fork |
214 | wait(agent[0].vif.ch_margin == 'h20); |
215 | wait(agent[1].vif.ch_margin == 'h20); |
216 | wait(agent[2].vif.ch_margin == 'h20); |
217 | join |
218 | $display("%s: 3 channel fifos have transferred all data", this.name); |
219 | |
220 | $display("%s finished testing DUT", this.name); |
221 | endtask |
222 | endclass: chnl_fifo_full_test |
223 | |
224 | endpackage: chnl_pkg |
225 | |
226 | module tb4_ref; |
227 | logic clk; |
228 | logic rstn; |
229 | logic [31:0] mcdt_data; |
230 | logic mcdt_val; |
231 | logic [ 1:0] mcdt_id; |
232 | |
233 | mcdt dut( |
234 | .clk_i (clk ) |
235 | ,.rstn_i (rstn ) |
236 | ,.ch0_data_i (chnl0_if.ch_data ) |
237 | ,.ch0_valid_i (chnl0_if.ch_valid ) |
238 | ,.ch0_ready_o (chnl0_if.ch_ready ) |
239 | ,.ch0_margin_o(chnl0_if.ch_margin ) |
240 | ,.ch1_data_i (chnl1_if.ch_data ) |
241 | ,.ch1_valid_i (chnl1_if.ch_valid ) |
242 | ,.ch1_ready_o (chnl1_if.ch_ready ) |
243 | ,.ch1_margin_o(chnl1_if.ch_margin ) |
244 | ,.ch2_data_i (chnl2_if.ch_data ) |
245 | ,.ch2_valid_i (chnl2_if.ch_valid ) |
246 | ,.ch2_ready_o (chnl2_if.ch_ready ) |
247 | ,.ch2_margin_o(chnl2_if.ch_margin ) |
248 | ,.mcdt_data_o (mcdt_data ) |
249 | ,.mcdt_val_o (mcdt_val ) |
250 | ,.mcdt_id_o (mcdt_id ) |
251 | ); |
252 | // clock generation |
253 | initial begin |
254 | clk <= 0; |
255 | forever begin |
256 | #5 clk <= !clk; |
257 | end |
258 | end |
259 | // reset trigger |
260 | initial begin |
261 | #10 rstn <= 0; |
262 | repeat(10) @(posedge clk); |
263 | rstn <= 1; |
264 | end |
265 | // USER TODO 4.1 |
266 | // import defined class from chnl_pkg |
267 | import chnl_pkg::*; |
268 | |
269 | chnl_intf chnl0_if(.*); |
270 | chnl_intf chnl1_if(.*); |
271 | chnl_intf chnl2_if(.*); |
272 | |
273 | chnl_basic_test basic_test; |
274 | chnl_burst_test burst_test; |
275 | chnl_fifo_full_test fifo_full_test; |
276 | |
277 | initial begin |
278 | basic_test = new(); |
279 | burst_test = new(); |
280 | fifo_full_test = new(); |
281 | // USER TODO 4.4 |
282 | // assign the interface handle to each chnl_initiator objects |
283 | basic_test.set_interface(chnl0_if, chnl1_if, chnl2_if); |
284 | burst_test.set_interface(chnl0_if, chnl1_if, chnl2_if); |
285 | fifo_full_test.set_interface(chnl0_if, chnl1_if, chnl2_if); |
286 | // USER TODO 4.5 |
287 | // START TESTs |
288 | basic_test.run(); |
289 | burst_test.run(); |
290 | fifo_full_test.run(); |
291 | $display("*****************all of tests have been finished********************"); |
292 | $finish(); |
293 | end |
294 | endmodule |
验证环境结构
结构
软件和硬件通过接口连接,所有的类封装到chnl_pkg
中并编译到work库中。
接口传递 interface 的指针如何传递到 initiator?
通过
chnl_root_test
的set_interface()
function, 然后又调用agent
的set_interface()
将值传入initiator。仿真是怎样运行的?
通过test的run()让每个agent的run()运行起来,然后再让各个generator和initiator运行起来。