[原创]利用system verilog快速构建单元仿真

在一些单元模块仿真时,往往需要构建一定格式的数据激励,如某个处理TCP报文的单元模块,需要构建符合TCP报文格式的激励。基于verilog的激励生成,大致有两种方法:

  1. txt文件法。将符合需求的数据记录于txt,仿真时调用。

  2. 直接合成法。利用verilog在tb中直接合成激励。

这两种方法的优点是直观,但不够灵活。其一,当激励的数据结构复杂时,构建起来比较麻烦;其二,当被测对象的输入协议改动时,往往牵一发而动全身,需要对tb做整体的检查。

利用system verilog构建单元测试可以克服上述缺点,同时还具备其它优秀特性,如带约束的随机激励生成、测试覆盖率分析。

本人根据实践,总结出基于system verilog的单元测试解决方法,希望对大家有帮助,并共同探讨。

下面以msgx_cfg模块的测试说明该方法,重点讲述如何从原来的基于verilog测试tb转为基于system verilog的测试tb。这个过渡包括四个步骤:

  1. 修改tb文件类型。由.v改为.sv。

  2. 在tb.sv中增加一个class。

  3. 在tb.sv中写一个数据转信号的task。

  4. 调用task,享受sv带来的测试快感。

1 单元仿真的激励要求

该模块结构


本文重点讲述激励产生,因此省略模块所有输出接口。该模块数据输入接口信号如下:

信号名

位宽(bit)

描述

i_data_valid

1

电平,为高表示数据有效

i_data

64

数据

i_eop_p

1

脉冲,数据尾标志


对于i_data的格式要求如下:

 

63:56

55:48

47:40

39:32

31:24

23:16

15:8

7:0

beat1

msg_type

pld_len

b

 

c[47:16]

   

beat2

c[15:0]

 

payload

     

beat3

payload

       

       


其中各字段说明:

名称

长度(byte)

说明

msg_type

1

消息类型

pld_len

1

payload的byte数,最小6

b

2

 

c

6

 

payload

pld_len

长度取决于pld_len

2 修改tb

根据上述输入激励要求,利用sv快速构建单元仿真激励。

2.1 修改tb文件类型

原来msgx_cfg_tb.v的内容如下:

 1 module msgx_cfg_tb;
 2 
 3 reg clk;
 4 reg    rst_n;
 5 reg i_data_valid;
 6 reg [63:0]    i_ data;
 7 reg    i_eop_p;
 8 
 9 msgx_cfg DUT(
10 .clk            (clk    ),
11 .rst_n          (rst_n    ),                              
12 
13 .i_data_valid    (i_data_valid    ),
14 .i_rdfifo_data    (i_ data ),
15 .i_eop_p        (i_eop_p    ), 
16 
17 //其它接口,省略
18 
19 );
20 
21 initial begin
22    clk = 0;
23    forever #5ns clk = ~clk;
24 end
25 
26 initial begin
27    rst_n = 0;
28    #50ns;
29    rst_n = 1;
30 end
31 
32 endmodule

 

将文件名改为msgx_cfg_tb.sv。

2.2 在tb.sv中增加一个class

在msgx_cfg_tb.sv头部,增加一个class,其作用是产生“输入激励要求”描述的数据。

 1 class msg_data_class;
 2 
 3 //声明data中的所有字段
 4 rand bit[7:0] msg_type;
 5 rand bit[7:0] pld_len;
 6 rand bit[15:0] b;
 7 rand bit[47:0] c;
 8 rand bit[7:0] payload[];
 9 
10 //用于存储最终数据data
11 bit[63:0] data[];
12 
13 //随机约束
14 constraint legal_payload_size_c {pld_len > 6;  payload.size == pld_len;}
15 
16 //自定义pack()函数,将字段打包进动态数组data
17 function void pack();
18     data = {>>{msg_type, pld_len, b, c, payload}};
19 endfunction
20 
21 //重写class内置函数: post_randomize(),该函数在class随机化后会自动调用
22 function void post_randomize ();
23     pack(); //打包
24 endfunction : post_randomize
25 
26 endclass

 

2.3 在tb.sv中增加一个task

现在,我们的msgx_cfg_tb.sv中有一个描述数据组成的class,一个module,接下来,需要在module内把数据转化为接口信号。我们在module内写一个task,完成这个任务。

 

 1 module msgx_cfg_tb;
 2         
 3 reg             clk;
 4 reg            rst_n;
 5 
 6 reg             i_data_valid;
 7 reg [63:0]        i_ data;    
 8 reg            i_eop_p;
 9 
10 //声明class
11 msg_data_class msg;
12 
13 msgx_cfg DUT(
14     .clk            (clk            ),
15     .rst_n          (rst_n        ),
16                                    
17     .i_data_valid    (i_data_valid    ),
18     .i_rdfifo_data    (i_ data         ),    
19     .i_eop_p        (i_eop_p        ),
20     
21     //其它接口,省略
22 );
23 
24 initial begin
25     clk = 0;
26     forever #5ns clk = ~clk;
27 end
28 
29 initial begin
30     rst_n = 0;
31     #50ns;
32     rst_n = 1;
33 end
34 
35 //新增task
36 task data2signal(msg_data_class msg);
37     @(posedge clk);
38     i_data_valid <= 1;
39     i_data <= 64’h0;
40     for(int i=0; i<msg.data.size; i++) begin
41         i_data <= msg.data[i];
42         i_eop_p <= i==msg.data.size-1 ? 1:0;
43         @(posedge clk);
44     end
45     i_data_valid <= 0;    
46     i_data <= 64’h0;    
47 endtask
48 
49 endmodule
50     

 

当data2signal task写好后,在测试case编写过程中专注于数据层构建就可以了。

2.4调用task,享受sv带来的测试快感

让我们在module内写第一个测试case。为了便于case的可重现性,我们把每一个case封装成一个task,在主过程中调用各个task,这样做的好处是测试后期一旦有改动可以将前期case方便的再做测试。

 1 //主过程
 2 initial begin
 3         test_case1();
 4 end
 5 
 6 //第一个测试case
 7 task test_case1();
 8         $display(“------------- start of test_case1 -------------”);
 9         msg = new();  
10         wait(rst_n);
11         @(posedge clk);
12         
13         //随机化
14         assert (msg.randomize with {msg_type == 8’d1;});
15 
16         //调用task: data2signal()
17         data2signal(msg);
18         $display(“------------- end of test_case1 -------------”);
19 endtask

 

在test_case1的task中,通过assert()语句控制随机产生msg_type字段为1的数据包,而其它一些字段(pld_len和payload的长度),由msg_data_class中的constraint约束。注意,当assert()中的约束与constraint中约束冲突时,仿真器会给出告警,此时产生的数据是不受控、没有意义的,因此仿真时需要留意告警信息,写tb时也注意做好约束规划和分配,避免产生冲突

posted on 2014-10-09 21:15  shanelan  阅读(2925)  评论(0编辑  收藏  举报

导航