上一节介绍了如何使用verilator保存vcd波形文件,本节将详细介绍vcd波形文件的格式,我们先给一个例子:
verilog代码文件:
module multi_logic (a,b,c,out,clk);
input a,b,c,clk;
output out;
reg a,b,c,clk,out,tmp_ou;
add add1(.a (a), .b(b), .clk(clk), .out(tmp_ou));
always @(posedge clk) begin
out <= tmp_ou ^ c;
end
endmodule
module add(a, b, clk, out);
input a, b, clk;
output out;
reg a, b, clk, out;
always @(posedge clk) begin
out <= a + b;
end
endmodule
cpp主文件:
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include "Vmulti_logic.h" // create `multi_logic.v`,so use `Vmulti_logic.h`
#include "verilated.h"
#include "verilated_vcd_c.h" //可选,如果要导出vcd则需要加上
int main(int argc, char** argv, char** env) {
VerilatedContext* contextp = new VerilatedContext;
contextp->commandArgs(argc, argv);
Vmulti_logic* multiLogic = new Vmulti_logic{contextp};
VerilatedVcdC* tfp = new VerilatedVcdC; //初始化VCD对象指针
contextp->traceEverOn(true); //打开追踪功能
multiLogic->trace(tfp, 0); //
tfp->open("wave.vcd"); //设置输出的文件wave.vcd
int clk = 1;
int count = 0;
while (!contextp->gotFinish()) {
count++;
int a = rand() & 1;
int b = rand() & 1;
int c = rand() & 1;
clk = (clk + 1) % 2;
multiLogic->a = a;
multiLogic->b = b;
multiLogic->c = c;
multiLogic->clk = clk;
multiLogic->eval();
printf("a = %d, b = %d, c = %d, clk = %d, out = %d\n",
a, b, c, clk, multiLogic->out);
tfp->dump(contextp->time()); //dump wave
contextp->timeInc(1); //推动仿真时间
if (count > 5)
break;
//assert(top->f == ~((a&b) | (~(c&d))));
}
delete multiLogic;
tfp->close();
delete contextp;
return 0;
}
打印结果:
a = 1, b = 0, c = 1, clk = 0, out = 0
a = 1, b = 1, c = 1, clk = 1, out = 1
a = 0, b = 0, c = 1, clk = 0, out = 1
a = 1, b = 0, c = 1, clk = 1, out = 1
a = 0, b = 1, c = 1, clk = 0, out = 1
a = 0, b = 0, c = 0, clk = 1, out = 1
wave.vcd 内容:
$timescale 1ps $end
$scope module TOP $end
$var wire 1 # a $end
$var wire 1 $ b $end
$var wire 1 % c $end
$var wire 1 & clk $end
$var wire 1 ' out $end
$scope module multi_logic $end
$var wire 1 # a $end
$var wire 1 $ b $end
$var wire 1 % c $end
$var wire 1 & clk $end
$var wire 1 ' out $end
$var wire 1 ( tmp_ou $end
$scope module add1 $end
$var wire 1 # a $end
$var wire 1 $ b $end
$var wire 1 & clk $end
$var wire 1 ( out $end
$upscope $end
$upscope $end
$upscope $end
$enddefinitions $end
#0
1#
0$
1%
0&
0'
0(
#1
1$
1&
1'
#2
0#
0$
0&
#3
1#
1&
1(
#4
0#
1$
0&
#5
0$
0%
1&
0(
我们来看vcd文件的内容,首先会给出层次结构下的所有信号,每有一个新的层次结构都以 #scope 开始 #upscope结束。每一个信号都有一个对应的替换符号来替代:
# -> a
$ -> b
% -> c
& -> clk
' -> out
( -> tmp_ou
随后,每个 #time都是一个时间戳,表示一个新的cycle的开始,每个信号变量通过对应替换符号表示,且第一个时间戳会保存所有信号的值,之后的时间戳只保存变化的信号值,显然信号的符号替代以及只保存变化量都是为了节省空间占用。
通过这个简单的例子,可以帮助我们很快的理解vcd波形文件的基本格式。当然还有很多其他的波形文件(fsdb、SHM、VPD等),目前verilator并不支持,如果感兴趣我们后面也可以讨论下,增加相应的内容。