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
前两种负责改变仿真状态,最后的模型同步负责保存前两种的状态改变,同步到波形图中。若没有最后的模型同步,仿真信号不会更新到波形图中。
模型大致总结如下:
注意点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();
若上升沿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(); // 模型同步
可见,同步了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(); // 模型同步
看到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(); // 模型同步
简写宏
据此理解书写,倒有种写固定管线OpenGL的感觉。
对于有时钟信号的tb,用宏包装一下更舒服(写法还是从os那儿学的)
#define step(statements) do { \
contextp->timeInc(1); \
top->clk = !top->clk; \
{statements} \
top->eval(); \
} while (0)
使用时仅需
step(); //无信号改变
step({
top->xxx = 0x12;
// ...
}); // 延时完改变信号