verilator书写C++版模块testbench

默认顶层模型名称为top,环境名称为contextp

const std::unique_ptr<VerilatedContext> contextp{new VerilatedContext};
const std::unique_ptr<Vxxx> top{new Vxxx{contextp.get(), "TOP"}};
// xxx为自定义待测模块名,由Makefile生成类在Vxxx.h中

仿真信号的操控

总共3种语句:

  • 信号延时: contextp->timeInc(1);,数字为设定的时间宽度,单位默认ps,表示先前状态的时延
  • 信号赋值: top->xxx = xxx;,xxx为信号名称,表示时延之后信号的新值
  • 模型同步: top->eval();,就是Evaluate Model

前两种负责改变仿真状态,最后的模型同步负责保存前两种的状态改变,同步到波形图中。若没有最后的模型同步,仿真信号不会更新到波形图中。

模型大致总结如下:
image

注意点1

前两种语句的顺序无所谓

信号延时表示以之前top->eval()记录的状态保持多少时间;信号赋值表示在时延之后信号应该是什么值。两种语句任意调换顺序都不会影响波形变化。

注意点2

根据前述总结模型,仿真过程中时序逻辑上升沿的判断应在模型同步之后。

在时钟上升沿处手动调整信号,则上升沿处读入的是调整后的信号,举例如下

    contextp->timeInc(1);
    top->clk = !top->clk;
        top->div_signed_i = 0;
        top->Z_i = 0x00000010;
        top->D_i = 0x00000004;
        top->div_valid = 1;	// 调高
        top->res_ready = 1;
    top->eval();

    // specific test
    std::cout << "[specific test]" << std::endl;
    div_expRes(top->Z_i, top->D_i, top->div_signed_i, expRes);

    contextp->timeInc(1);
    top->clk = !top->clk;
    top->div_valid = 0;	// 调低,此处为时钟上升沿
    top->eval();

image 若上升沿div_valid为高,完成握手则div_status变为1。此处没有,说明仿真时上升沿得到的div_valid为后赋的值0

注意点3

对于每次模型同步,信号延时语句的数值会相加;而信号赋值则会后语句覆盖前语句,举例如下

① top->eval()前单次信号赋值和时延:

    // init signal
    top->Z_i = 1;
    top->rst_n = 0;
    top->clk = 0;
    top->eval();	// 别忘了这一个,初始的赋值

    contextp->timeInc(1); // 保持当前信号情况,持续1ps时间
    top->clk = !top->clk; // 信号改变
    top->Z_i = 2;
    top->eval();          // 模型同步

image 可见,同步了1ps

② top->eval()前多次信号赋值和时延:

    // init signal
    top->Z_i = 1;
    top->rst_n = 0;
    top->clk = 0;
    top->eval();	// 别忘了这一个,初始的赋值

    contextp->timeInc(1); // 保持当前信号情况,持续1ps时间
    top->clk = !top->clk; // 信号改变
    top->Z_i = 2;
    contextp->timeInc(1);
    top->Z_i = 1;
    top->eval();          // 模型同步

image 看到clk为1(若再取反则变为0);Z_i为1

③ 用两个top->eval()分别记录两次状态变化:

    // init signal
    top->Z_i = 1;
    top->rst_n = 0;
    top->clk = 0;
    top->eval();

    contextp->timeInc(1); // 保持当前信号情况,持续1ps时间
    top->clk = !top->clk; // 信号改变
    top->Z_i = 2;
    top->eval();

    contextp->timeInc(1);
    top->clk = !top->clk;
    top->Z_i = 1;
    top->eval();          // 模型同步

image

简写宏

据此理解书写,倒有种写固定管线OpenGL的感觉。

对于有时钟信号的tb,用宏包装一下更舒服(写法还是从os那儿学的)

#define step(statements) do { \
        contextp->timeInc(1); \
        top->clk = !top->clk; \
            {statements}      \
        top->eval();          \
    } while (0)

使用时仅需

step();	//无信号改变
step({
    top->xxx = 0x12;
    // ...
});	// 延时完改变信号
posted @ 2023-01-19 22:34  Xlucidator  阅读(687)  评论(0编辑  收藏  举报