天天看点

【验证小白】无法忍受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";
}
           

继续阅读