棋子 · 2023年12月29日

【芯片验证】可能是RTL定向验证的巅峰之作

前言

兄弟萌,

当你着手开始写新的RTL时,想不想边写边编译边清理bug呢?

当你对一个语法感到费劲,需不需要立刻有个环境做一下实验呢?

当你费了九牛二虎之力终于写完个模块时,有没有立刻看看波形的悸动呢?

当你完成了初步的debug之后,要不要来些独立自主的随机测试呢?

那么请不要犹豫,果断来入手一波疑似RTL定向验证的巅峰之作auto_testbench吧!

工程路径

https://gitee.com/gjm9999/auto_testbench

功能列表

  • 基于输入生成可编译仿真的RTL验证与执行环境;
  • 自动例化顶层,进行时钟和复位驱动;
  • 自动对端口进行随机;
  • 针对握手接口进行协议随机优化;
  • 可选择增加自动比对环境;
  • 生成用于实验的空仿真环境;

使用说明

脚本工作与linux环境,下载工程后执行命令:

{$script_path}/auto_testbench [-f module.v] [-v]

-f:后缀rtl的文件名,推荐在rtl所在目录下进行执行,也支持带有路径,如不加则生成空工程用于仿真;

-v:表示需要自动生成比对环境代码,否则只生成仿真环境;

生成的仿真环境如下(以v1版本下的bypass_fifo.v为例):

.
├── cfg
│   ├── cfg.mk
│   ├── check_fail.pl
│   ├── run.do
│   └── tb.f
├── sim
│   └── Makefile
├── top
│   └── testbench.sv
└── ver
    └── bypass_fifo_pkg.sv

top目录下的testbench.sv是赏心悦目的顶层文件:

image.png

在sim目录下执行:

make run [seed=xxx] [wave=on/off][mode=xxx]

可进行环境仿真,仿真结果会放置于mode同名目录下,可进行波形debug:

verdi -ssf [mode]/wave/xxx.fsdb &

使用示例

波形测试

bypass_fifo是本工具的一个测试模块,其接口如下:

module bypass_fifo #(
    parameter DEPTH = 8,
    parameter WIDTH = 128
)(
    input               clk,
    input               rst_n,
    
    input               data_in_valid,
    input  [WIDTH -1:0] data_in,
    input               data_in_power,
    output              data_in_ready,
    
    output              data_out_valid,
    output [WIDTH -1:0] data_out,
    input               data_out_ready
);

其功能是一个握手型fifo,但是只有data_in_power为1的数据会被输出,其他数据会被丢弃。咱先不说这个功能为啥这么奇怪(事实上这是一对多fifo的一部分),就单纯聊怎么验证这个模块。

在bypass_fifo.v的同级目录下输入:

{$script_path}/auto_testbench -f bypass_fifo.v

会得到如下的提示内容:

##====================================================================##
Gen over! please cd ./bypass_fifo_verification/sim
You need modify ./bypass_fifo_verification/top/testbench.sv
    like cp ./bypass_fifo_verification_bak/top/testbench.sv ./bypass_fifo_verification/top/
You need modify ./bypass_fifo_verification/cfg/tb.f
    like cp ./bypass_fifo_verification_bak/cfg/tb.f ./bypass_fifo_verification/cfg/
##====================================================================##

提示内容的意思就是:

现在你可以到sim目录去跑仿真了。如果你之前目录下有一个bypass_fifo_verification那我已经把它改名成bypass_fifo_verification_bak目录了,你看看要不要把旧的testbench.sv拷贝过来用,顺便看看tb.f是不是也得拷过来呀。

不管这些,直接跳转到./bypass_fifo_verification/sim目录,执行make cmp进行编译。正常来说编译会直接通过,遇到错了就根据报错进行排查。如果报文件缺失,那么请打开 ./bypass_fifo_verification/cfg/tb.f:

+libext+.v+.sv  
-y /home/ICer/gitee_path/auto_testbench/src/  
  
/home/ICer/gitee_path/auto_testbench/src/bypass_fifo.v  
../top/testbench.sv

这个文件里会把bypass_fifo.v所在的目录设置为全局搜索目录,如果这样不足以涵盖内部调用的模块,那么请进一步修改该文件。

修改掉全部的编译bug后编译即可通过,之后打开 ./bypass_fifo_verification/top/testbench.sv文件,按照自己的需求修改。在该文件中对信号进行了四种力度的随机:

  • valid握手信号:在与对应的ready信号握手后,随机跳变;
  • ready握手信号:每拍随机跳变;
  • info握手信号:在对应的valid&ready信号握手后,或者valid为0时,随机跳变;
  • 其他信号:initial内随机赋初始值;

举个例子,对于data_in_power,其生成逻辑是这样的:

always @(posedge clk or negedge rst_n)begin
    if(~rst_n)begin
        data_in_power <= 'x;
    end
    else if(data_in_valid && data_in_ready)begin
        data_in_power <= $urandom;
    end
    else if(data_in_valid == 0)begin
        data_in_power <= $urandom;
    end
end

上述内容由于涉及到信号识别以及信号匹配,所以很容易出问题(比如aa_bb_rvalid应该找bb_aa_rready,但是可能误找到bb_aa_wready),所以一般需要检查和按照需求修改下。

修改完成后,在sim目录下键入make run seed=0 wave=on进行仿真,等待pass(必然pass因为都没有比对):

image.png

之后通过verdi -ssf sim_base/wave/sanity_0.fsdb &打开波形即可:

image.png

自动比对

对于一种场景的模块,建议加入自动比对:单一握手型输出的模块。仍以bypass_fifo为例,如果想要加入自动比对,那么在生成时后缀-v即可:

{$script_path}/auto_testbench -f bypass_fifo.v -v

此时生成的环境中,多了一个文件./bypass_fifo_verification/ver/bypass_fifo_pkg.sv:

package bypass_fifo_pkg;

    parameter ERROR_DEBUG_CNT = 5;
    parameter DEPTH = 8;
    parameter WIDTH = 128;

    int error_cnt = 0;
    bit check_en  = 0;

    typedef struct{
        bit [WIDTH -1:0] data_in;
        bit  data_in_power;
    } data_in_valid_struct;
    data_in_valid_struct data_in_valid_bus_q[$];

    typedef struct{
        bit [WIDTH -1:0] data_out;
    } data_out_valid_struct;
    data_out_valid_struct rm_q[$];
    data_out_valid_struct data_out_valid_bus_q[$];

endpackage

简单说下思路,工具将若干输入和输出封装为struct,并且声明输入输出队列和一个输出类型的rm队列。在testbench中补充了四个task:

in_queue_gain:获取输入数据,并写入输入队列;

out_queue_gain:获取输出数据,并写入输出队列;

rm_queue_gain:使用输入队列,预期输出数据,并写入rm队列;

queue_check:对比输出队列与rm队列,对比不通过则报错,这个task是固定的不需要修改;

对bypass_fifo而言,前两个方法已经生成正确,不需要修改:

task in_queue_gain();
  while(1)begin
    @(negedge clk);
    if(data_in_valid && data_in_ready)begin
      data_in_valid_struct data_in_valid_dat;
      data_in_valid_dat.data_in = data_in;
      data_in_valid_dat.data_in_power = data_in_power;
      data_in_valid_bus_q.push_back(data_in_valid_dat);
    end//if-end 
  end//while-end 
endtask: in_queue_gain

task out_queue_gain();
  while(1)begin
    @(negedge clk);
    if(data_out_valid && data_out_ready)begin
      data_out_valid_struct data_out_valid_dat;
      data_out_valid_dat.data_out = data_out;
      data_out_valid_bus_q.push_back(data_out_valid_dat);
    end//if-end 
  end//while-end 
endtask: out_queue_gain

而需要修改的是对rm的预期,根据data_in_power判定数据是否需要输出:

task rm_queue_gain();
  data_in_valid_struct data_in_valid_dat;
  data_out_valid_struct data_out_valid_dat;
  while(1)begin
    wait(data_in_valid_bus_q.size > 0);
    data_in_valid_dat = data_in_valid_bus_q.pop_front();
    if(data_in_valid_dat.data_in_power === 1'b1)begin
        data_out_valid_dat.data_out = data_in_valid_dat.data_in;
        rm_q.push_back(data_out_valid_dat);
    end
  end
endtask: rm_queue_gain

完成修改后,将./bypass_fifo_verification/ver/bypass_fifo_pkg.sv中的check_en修改为1,重新跑仿真:

image.png

此时会发现比对未通过(这个模块确实有功能bug),打开波形确认'hb985d7ad这个数是怎么个情况:

image.png

发现'hb985d7ad这个数输出没有握手就data_out_valid跳变了,显然是功能性bug。

作者:尼德兰的喵
文章来源:芯时代青年

推荐阅读

更多IC设计干货请关注IC设计专栏。欢迎添加极术小姐姐微信(id:aijishu20)加入技术交流群,请备注研究方向。
推荐阅读
关注数
19610
内容数
1303
主要交流IC以及SoC设计流程相关的技术和知识
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息