天天看點

【驗證小白】無法忍受modelsim?陣地轉移,虛拟機centOS+makefile+vcs+verdi繼續驗證學習——搭建簡單的superbench前言目标sv代碼VCS+verdi指令makefile組織結果驗收附代碼

前言

之前的系列都是運作于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類型的關鍵字,判斷編譯是否通過;

結果驗收

目錄結構

【驗證小白】無法忍受modelsim?陣地轉移,虛拟機centOS+makefile+vcs+verdi繼續驗證學習——搭建簡單的superbench前言目标sv代碼VCS+verdi指令makefile組織結果驗收附代碼

執行代碼

【驗證小白】無法忍受modelsim?陣地轉移,虛拟機centOS+makefile+vcs+verdi繼續驗證學習——搭建簡單的superbench前言目标sv代碼VCS+verdi指令makefile組織結果驗收附代碼

檢視波形

make verdi &打開Verdi,吃進fsdb檔案即可;

【驗證小白】無法忍受modelsim?陣地轉移,虛拟機centOS+makefile+vcs+verdi繼續驗證學習——搭建簡單的superbench前言目标sv代碼VCS+verdi指令makefile組織結果驗收附代碼

附代碼

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";
}
           

繼續閱讀