前言
之前的系列都是運作于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:生成編譯和仿真生成的檔案;
sv代碼
要進行仿真的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+verdi指令
VCS的指令仿佛星辰大海,本次用到了如下的指令;
cmp編譯階段指令
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;
其他的有幸去大家再查一下好啦~在此不再贅述;
run仿真階段VCS指令
simv +ntb_random_seed=$(SEED) +tc_name=$(tc) +ucli -assert
simv是用來仿真的可執行檔案,+ntb_random_seed=$(SEED)是傳入随機種子,+tc_name=$(tc)傳入tc名字,+ucli打開ucli特性,-assert打開斷言檢查;
verdi檢視波形指令
verdi -simflow -dbdir simv.daidir
makefile組織
好的,接下來将以上的指令行封裝進makefile中,共封裝僞目标如下:
.PHONY: cmp ncrun run verdi clean clean_all
使用者傳參數
export seed := random
export tc := sanity
export wave := off
暫時隻支援三個使用者傳參,分别是仿真種子seed,用例名稱tc,波形開關wave;
封裝CMP
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
封裝NCRUM
編譯後執行仿真,将仿真指令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檔案即可;
簡
直
太
完
美
了
!
附代碼
makefile
.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(<ARGV>){
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";
}