之前的系列都是运行于windows+modelsim环境的,之所以这样运行呢,是因为我当时手头只有这个工具;
【验证小白】系列前文
但是今时不同往日,我终于找到了一个工具齐全的虚拟机centOS(忙猜很多人应该都是在用这个虚拟机),于是乎我决定把验证环境搬家,顺便也再好好的重构下;
当然了那是以后的事,这次的任务是搭建一个基本的superbench平台。
通过makefile搭建superbench平台,支持VCS仿真和verdi定位功能,包括如下指令:
make cmp:VCS编译tb.f中的v/sv文件;
make ncrun tc=xxx wave=on/off seed=xxxxxx:运行编译后的simv文件,根据传惨执行仿真;
make run:cmp + ncrun;
make verdi:打开verdi并关联rtl结构;
make clean:删除编译生成的文件;
make clean_all:生成编译和仿真生成的文件;
要进行仿真的verilog代码很简单,只有时钟复位和一些简单设置;
`define DELAY(N, clk) begin \
repeat(N) @(posedge clk);\
#1ps;\
end
module testbench();
timeunit 1ns;
timeprecision 1ps;
string tc_name;
int tc_seed;
bit clk, rst_n;
initial $timeformat(-9,3,"ns",6);
initial begin
if(!$value$plusargs("tc_name=%s", tc_name)) $error("no tc_name!");
else $display("tc name = %0s", tc_name);
if(!$value$plusargs("ntb_random_seed=%0d", tc_seed)) $error("no tc_seed");
else $display("tc seed = %0d", tc_seed);
end
initial forever #5ns clk = ~clk;
initial begin
`DELAY(30, clk);
rst_n = 1'b1;
$display("rst_n over, urandom -> %0d", $urandom);
end
`ifdef WAVE_ON
initial begin
$fsdbDumpfile($sformatf("./sim_base/wave/%s_%0d.fsdb", tc_name, tc_seed));
$fsdbDumpvars(0);
end
`endif
endmodule
VCS的指令仿佛星辰大海,本次用到了如下的指令;
vcs -f ./tb.f -P $(NOVAS_HOME)/share/PLI/VCS/LINUX64/verdi.tab \
$(NOVAS_HOME)/share/PLI/VCS/LINUX64/pli.a \
-l $(SIM_LOG) \
-o $(EXEC_SIMV) \
-timescale=1ns/1ps -unit_timescale=1ns/1ps \
-top testbench \
+v2k +define+RTL_SAIF +notimingcheck +nospecify +vpi +memcbk +vcsd \
-sv_pragma -lca -kdb -sverilog -full64 -sverilog -debug_all -ntb_opts uvm-1.2 \
针对关键的解释几个好了:
-f:用于指定文件list,常用的命令行有-f/-F/-v/-y,其中作用和区别我们有时间再聊,先挖个坑好了;
-R:编译之后立即执行仿真,由于我是cmp和run分开的,因此没有加这个选项;
-P:对verdi的支持,“指定使用的PLI,这里指定verdi下的PLI,因为verdi是需要fsdb文件来显示波形的,而fsdb文件的生成,需要系统函数(如$fsdbDumpfile,$fsdbDumpvars等等),而这些系统函数,vcs中是没有的,但是在verdi的PLI中有,所以这里需要指定一下”;
-l:制定输出log文件路径;
-o:指定输出simv文件路径;
-timescale=1ns/1ps -unit_timescale=1ns/1ps:设定环境timescale,详见timescale设置规则;
-top testbench:设定顶层模块名称,目前的验证环境是单顶层,顶层名称即module名称;
-ntb_opts uvm-1.2:支持UVM1.2版本库;
-sverilog:支持system verilog语法;
-debug_all:支持交互式仿真;
-kdb:编译时生成后续verdi使用的KDB库;
-full64:64bit simulation
-memcbk:支持多维数组或mem波形dump;
其他的有幸去大家再查一下好啦~在此不再赘述;
simv +ntb_random_seed=$(SEED) +tc_name=$(tc) +ucli -assert
simv是用来仿真的可执行文件,+ntb_random_seed=$(SEED)是传入随机种子,+tc_name=$(tc)传入tc名字,+ucli打开ucli特性,-assert打开断言检查;
verdi -simflow -dbdir simv.daidir
好的,接下来将以上的指令行封装进makefile中,共封装伪目标如下:
.PHONY: cmp ncrun run verdi clean clean_all
export seed := random
export tc := sanity
export wave := off
暂时只支持三个用户传参,分别是仿真种子seed,用例名称tc,波形开关wave;
ifeq ($(seed), random)
SEED := $(shell python -c "from random import randint; print randint(0,99999999)")
#SEED := $(shell perl -e "print int(rand(100000000))")
else
SEED := $(seed)
endif
export PRE_PROC:= mkdir -p ./sim_base/log ./sim_base/exec ./sim_base/wave
export VERDI_P := $(NOVAS_HOME)/share/PLI/VCS/LINUX64/verdi.tab \
$(NOVAS_HOME)/share/PLI/VCS/LINUX64/pli.a
export SIM_BASE := ./sim_base
export SIM_LOG := $(SIM_BASE)/log/sim.log
export RUN_LOG := $(SIM_BASE)/log/$(tc)_$(SEED).log
export EXEC_SIMV := $(SIM_BASE)/exec/simv
export FILELIST := ./tb.f
ifeq ($(wave), on)
CMP_OPTIONS += +define+WAVE_ON
endif
export CMP_OPTIONS :=
CMP_OPTIONS += +v2k +define+RTL_SAIF +notimingcheck +nospecify +vpi +memcbk +vcsd
CMP_OPTIONS += -sverilog -full64 -sverilog -debug_all -ntb_opts uvm-1.2
CMP_OPTIONS += -sv_pragma -lca -kdb
CMP_OPTIONS += -top testbench
CMP_OPTIONS += -timescale=1ns/1ps -unit_timescale=1ns/1ps
将之前提到的VCS命令行进行封装,最后封装伪目标make cmp:
cmp: clean
@$(PRE_PROC)
@vcs -f ./tb.f -P $(VERDI_P) -l $(SIM_LOG) -o $(EXEC_SIMV) $(CMP_OPTIONS)
cmp执行时候会先执行clean,将之前的编译文件清除:
clean:
@rm -rf $(SIM_BASE)/exec csrc DVEfiles novas.conf novas.rc vc_hdrs.h ucli.key verdiLog vfastLog *.fsdb *.log *.vcd
编译后执行仿真,将仿真指令simv封装到make ncrun中:
export RUN_OPTIONS :=
RUN_OPTIONS += +ntb_random_seed=$(SEED) +tc_name=$(tc) +ucli -assert
ncrun:
@$(EXEC_SIMV) $(RUN_OPTIONS) -l $(RUN_LOG)
@check_fail.pl $(RUN_LOG)
@echo "[Note] report log path: $(RUN_LOG)"
说明两点:
1. SEED的产生和使用着实让我伤脑筋,详见【makefile】makefile中产生能稳定使用的随机数
2.check_fail.pl用来检查成生log中有无error类型的关键字,判断编译是否通过;
make verdi &打开Verdi,吃进fsdb文件即可;
简
直
太
完
美
了
!
.PHONY: cmp ncrun run verdi clean clean_all
# -R: immediately run after cmp
# $$RANDOM
export seed := random
export tc := sanity
export wave := off
ifeq ($(seed), random)
SEED := $(shell python -c "from random import randint; print randint(0,99999999)")
#SEED := $(shell perl -e "print int(rand(100000000))")
else
SEED := $(seed)
endif
export PRE_PROC:= mkdir -p ./sim_base/log ./sim_base/exec ./sim_base/wave
export VERDI_P := $(NOVAS_HOME)/share/PLI/VCS/LINUX64/verdi.tab \
$(NOVAS_HOME)/share/PLI/VCS/LINUX64/pli.a
export SIM_BASE := ./sim_base
export SIM_LOG := $(SIM_BASE)/log/sim.log
export RUN_LOG := $(SIM_BASE)/log/$(tc)_$(SEED).log
export EXEC_SIMV := $(SIM_BASE)/exec/simv
export FILELIST := ./tb.f
export CMP_OPTIONS :=
CMP_OPTIONS += +v2k +define+RTL_SAIF +notimingcheck +nospecify +vpi +memcbk +vcsd
CMP_OPTIONS += -sverilog -full64 -sverilog -debug_all -ntb_opts uvm-1.2
CMP_OPTIONS += -sv_pragma -lca -kdb
CMP_OPTIONS += -top testbench
CMP_OPTIONS += -timescale=1ns/1ps -unit_timescale=1ns/1ps
export RUN_OPTIONS :=
RUN_OPTIONS += +ntb_random_seed=$(SEED) +tc_name=$(tc) +ucli -assert
ifeq ($(wave), on)
CMP_OPTIONS += +define+WAVE_ON
endif
cmp: clean
@$(PRE_PROC)
@vcs -f ./tb.f -P $(VERDI_P) -l $(SIM_LOG) -o $(EXEC_SIMV) $(CMP_OPTIONS)
ncrun:
@$(EXEC_SIMV) $(RUN_OPTIONS) -l $(RUN_LOG)
@check_fail.pl $(RUN_LOG)
@echo "[Note] report log path: $(RUN_LOG)"
run: cmp ncrun
verdi:
verdi -simflow -dbdir $(SIM_BASE)/exec/simv.daidir
clean:
@rm -rf $(SIM_BASE)/exec csrc DVEfiles novas.conf novas.rc vc_hdrs.h ucli.key verdiLog vfastLog *.fsdb *.log *.vcd
clean_all: clean
@rm -rf $(SIM_BASE)
checkfail.pl
#!/usr/bin/perl -w
my $pass = 1;
while(){
if($_ =~ /error|Error|failed|Failed/){
$pass = 0;
last;
}
}
if($pass == 1){
print "\033[42;37m SIMULATION PASS! \033[0m \n";
print_pass();
} else {
print "\033[41;37m SIMULATION FAIL! \033[0m \n";
print_fail();
}
sub print_pass{
print "\n";
print "############# # ############## ##############"; print "\n";
print "############# ### ############## ##############"; print "\n";
print "## ## # # # ## ##"; print "\n";
print "## ## ## ## ## ##"; print "\n";
print "## ## ## ## ## ##"; print "\n";
print "## ## ## ## ## ##"; print "\n";
print "############# ############## ############## ##############"; print "\n";
print "############# ############## ############## ##############"; print "\n";
print "## ## ## ## ##"; print "\n";
print "## ## ## ## ##"; print "\n";
print "## ## ## ## ##"; print "\n";
print "## ## ## ## ##"; print "\n";
print "## ## ## ############## ##############"; print "\n";
print "## ## ## ############## ##############"; print "\n";
print "\n";
}
sub print_fail{
print "\n";
print "############# # ############## ##"; print "\n";
print "############# ### ############## ##"; print "\n";
print "## # # # ## ##"; print "\n";
print "## ## ## ## ##"; print "\n";
print "## ## ## ## ##"; print "\n";
print "## ## ## ## ##"; print "\n";
print "############# ############## ## ##"; print "\n";
print "############# ############## ## ##"; print "\n";
print "## ## ## ## ##"; print "\n";
print "## ## ## ## ##"; print "\n";
print "## ## ## ## ##"; print "\n";
print "## ## ## ## ##"; print "\n";
print "## ## ## ############## ##############"; print "\n";
print "## ## ## ############## ##############"; print "\n";
print "\n";
}