虽说verdi、modelsim都是用来调试波形, modelsim与verdi相比,最大的缺点是波形不会全dump,wave窗口拉不全的话需要重新跑,而verdi边运行边查看。具体做法是,在使用tcl指令,在运行仿真时,设置仿真时间,每次run完毕之后,在nWave窗口中file——>自动加载(shift+L快捷键),异常方便!
二、FSDB波形
Verdi只能查看fsdb格式的波形,而VCS可以生成供DVE查看的vpd格式的波形,如果想要输出fsdb格式的波形,需要额外在testbench中添加fsdb指令,或者通过脚本进行设置。FSDB全称为(Fast Signal Data Base):快速信号数据库,fsdb文件是Verdi使用的一种专用数据格式,fsdb通过verilog的PLI接口实现,如:fsdbDumpfile("f0.fsdb"); //指定生成的fsdb文件的文件名fsdbDumpars(0,top); //指定dump的变量
三、开发环境与配置
使用Verdi前需要对开发环境进行配置,使得系统在调用Verdi时找到其路径,在使用verdi时找到其库文件与路径,所以要注意三个变量和三条指令的使用:Verdi_HOME/NOVAS_HOME 仿真器默认,为设置PATH做准备 PATH 让系统能够找到verdi,如果不设置PATH,在调用Veidi时系统无法识别。
LD_LIBRARY_PATH 为了使得Verdi在运行过程中找到需要的库文件,对Library的位置在开发环境中进行配置,可通过:
echo $LD_LIBRARY_PATH | sed ‘s/:/\n/g’
进行查看,库文件中包含.so(共享对象文件,shared object),pli.a(静态库)等库文件,还需要*.tab(表格文件)来获取对应信息、索引等。
echo 用于获取环境变量,获取并打印,打印环境变量指令为:
echo $PATH | sed ‘s/\ :/\n/g’
which 查询当前路径是否设置成功,若设置成功会显示一个软件路径,查看verdi路径:which verdi
uname 查询当前系统信息,如硬件平台否为:x86_64
四、VCS+Verdi 如何dump波形
在dump波形时会用到那些命令,解决的是生成fsdb波形的问题,为了生成.fsbd格式的文件,可以使用verilog波形函数,也可以使用ucli/tcl接口:
(一)使用Verilog系统函数 作为小白,我觉得这种方式很友好,通过Verilog的PLI接口实现,在tb中添加两个函数:initial begin$fsdbDumfile(“uart.fsdb”); //指定生成的fsdb文件的文件名$fsdbDumpars(0,uart_byte_tx_tb); //指定dump的层次,0表示存储所有的wave,tb为起始层end
(二)、使用ucli/tcl接口
使用ucli/tcl接口时无需在tb中调用与fsdbDumpvars()函数,仅需在脚本中进行设置即可。在运行仿真时,打开ucli接口,通过Tcl脚本对fsdb进行设置,设置fsdb文件的文件名,设置fsdb文件的集成类型和起始文件:
global env
# tcl脚本引用环境变量,Makefile中通过export定义
fsdbDumpfile "$env(demo_name).fsdb"
# 设置波形文件名,受环境变量env(demo_name)控制
# demo_name在makefile中使用exportdemo_name=demo_fifo
fsdbDumpvars 0 "tb_top"
# 设置波形的顶层和层次,表示将tb_top作为顶层,Dump所有层次
run
+fsdb+autoflush+fsdb+f+autoflush:用于开启一边仿真以一边Dump波形的功能,在不开启该功能时,运行完仿真之后,未退出命令行,直接在新终端中启动Verdi调用波性文件的话是一个用文件,没有波形,这是因为只有在结束仿真之后,波形才会Dump为静态文件供verdi调用,没有出现波形的原因是此时的.fsdb只是一个空文件,波形还未Dump,如下图所示:
此时可以在仿真的命令行中键入:fsdbDumpflush,启动波形Dump,在另一个终端中启动verdi加载波形,波形正常加载:
verdi优于modelsim也正是因此,通过tcl语言的控制,每次设置run时间,不断的加载仿真波形,十分方便! VCS编译环节 无论采用调用系统函数还是调用tcl脚本,编译时两种操作相同,都是为了生成一个二进制可执行文件,其重点是VCS与Verdi的库进行连接:-LDFLAGS#表示下面要加载(load)的标志,将要传递VCS的Linker链接库,与接下来的两条指令配合使用。-rdynamic#加载动态库,提示需要加载动态库,如*库文件名录下的.so文件-P $(Verdi_HOME)/share/PLI/VCS/LINUX64/novas.tab#加载表格文件 $(Verdi_HOME)/share/PLI/VCS/LINUX64/pli.a#加载静态库
五、Makefile脚本设计
要用到的基本指令为:编译、仿真、verdi加载fsdb波形、清除文件 所以在脚本中设计伪指令:
.PHONY: com sim run_verdi clean
运行编译时会生成一个二进制可执行文件供仿真使用,该文件默认名为:simv,可通过脚本设置:
OUTPUT = uart #也可以自己设置名字 运行编译,设计编译指令,设置编译开关:
vcs表示运行编译,+v2k表示支持verilog2001标准,-timesacle=1ns/1ns用于设置仿真时间精度,-debug_all用于设置debug开关,-f dile_list.f用于设置编译文件,-o (OUTPUT)用于设置输出二进制可执行文件的文件名,-full64表示VCS为64位版本。
verdi加载fsdb文件显示波形:-f file_list.f制定要加载的.v文件,-ssf $(OUTPUT).fsdb 表示打开verdi时默认自动加载.fsdb仿真文件,-nologo表示不显示欢迎界面。
六、两种fsdb文件生成方式的比较 基于系统函数 :优点:熟悉verilog代码,接受较快
缺点:
1.需要重新编译系统,浪费时间(不使用valuevalueplusargs时); 2.Verilog是低级语言,对于文本处理比较困难,不支持正则表达式
基于ucli/tcl接口:优点:
1.不需要重新编译仿真顶层;
2.使用高级语言接口,容易完成复杂处理,例如传递变量,例如使用正则表达式;
3.交互式接口,控制灵活,仿真过程可修改dump信息,如dumpon/dumpoff
七、Verdi快捷键
熟悉设计的IO Driver【D】(Input) 哪些信号驱动了当前信号,可以在nWAVE界面中进行查看。Load【L】 当前信号驱动了哪些信号,可以在nWAVE界面中进行查看。
操作演示 :最后分享一下我所用的工程,是一个UART发送的测试,其工程代码为:module uart_byte_tx( Clk, Rst_n, data_byte, send_en, baud_set, Rs232_Tx, Tx_Done, uart_state); input Clk; input Rst_n; input [7:0]data_byte; input send_en; input [2:0]baud_set; output reg Rs232_Tx; output reg Tx_Done; output reg uart_state; reg bps_clk; //波特率时钟 reg [15:0]div_cnt;//分频计数器 reg [15:0]bps_DR;//分频计数最大值 reg [3:0]bps_cnt;//波特率时钟计数器 reg [7:0]r_data_byte; localparam START_BIT = 1'b0; localparam STOP_BIT = 1'b1; always@(posedge Clk or negedge Rst_n) if(!Rst_n) uart_state <= 1'b0; else if(send_en) uart_state <= 1'b1; else if(bps_cnt == 4'd11) uart_state <= 1'b0; else uart_state <= uart_state; always@(posedge Clk or negedge Rst_n) if(!Rst_n) r_data_byte <= 8'd0; else if(send_en) r_data_byte <= data_byte; else r_data_byte <= r_data_byte; always@(posedge Clk or negedge Rst_n) if(!Rst_n) bps_DR <= 16'd5207; else begin case(baud_set) 0:bps_DR <= 16'd5207; 1:bps_DR <= 16'd2603; 2:bps_DR <= 16'd1301; 3:bps_DR <= 16'd867; 4:bps_DR <= 16'd433; default:bps_DR <= 16'd5207; endcase end //counter always@(posedge Clk or negedge Rst_n) if(!Rst_n) div_cnt <= 16'd0; else if(uart_state)begin if(div_cnt == bps_DR) div_cnt <= 16'd0; else div_cnt <= div_cnt + 1'b1; end else div_cnt <= 16'd0; // bps_clk gen always@(posedge Clk or negedge Rst_n) if(!Rst_n) bps_clk <= 1'b0; else if(div_cnt == 16'd1) bps_clk <= 1'b1; else bps_clk <= 1'b0; //bps counter always@(posedge Clk or negedge Rst_n) if(!Rst_n) bps_cnt <= 4'd0; else if(bps_cnt == 4'd11) bps_cnt <= 4'd0; else if(bps_clk) bps_cnt <= bps_cnt + 1'b1; else bps_cnt <= bps_cnt; always@(posedge Clk or negedge Rst_n) if(!Rst_n) Tx_Done <= 1'b0; else if(bps_cnt == 4'd11) Tx_Done <= 1'b1; else Tx_Done <= 1'b0; always@(posedge Clk or negedge Rst_n) if(!Rst_n) Rs232_Tx <= 1'b1; else begin case(bps_cnt) 0:Rs232_Tx <= 1'b1; 1:Rs232_Tx <= START_BIT; 2:Rs232_Tx <= r_data_byte[0]; 3:Rs232_Tx <= r_data_byte[1]; 4:Rs232_Tx <= r_data_byte[2]; 5:Rs232_Tx <= r_data_byte[3]; 6:Rs232_Tx <= r_data_byte[4]; 7:Rs232_Tx <= r_data_byte[5]; 8:Rs232_Tx <= r_data_byte[6]; 9:Rs232_Tx <= r_data_byte[7]; 10:Rs232_Tx <= STOP_BIT; default:Rs232_Tx <= 1'b1; endcase end endmodule
testbench为:
`define clk_period 20module uart_byte_tx_tb; reg Clk; reg Rst_n; reg [7:0]data_byte; reg send_en; reg [2:0]baud_set; wire Rs232_Tx; wire Tx_Done; wire uart_state; uart_byte_tx uart_byte_tx( .Clk(Clk), .Rst_n(Rst_n), .data_byte(data_byte), .send_en(send_en), .baud_set(baud_set), .Rs232_Tx(Rs232_Tx), .Tx_Done(Tx_Done), .uart_state(uart_state) ); initial Clk = 1; always#(`clk_period/2)Clk = ~Clk; initial begin Rst_n = 1'b0; data_byte = 8'd0; send_en = 1'd0; baud_set = 3'd4; #(`clk_period*20 + 1 ) Rst_n = 1'b1; #(`clk_period*50); data_byte = 8'haa; send_en = 1'd1; #`clk_period; send_en = 1'd0; @(posedge Tx_Done) #(`clk_period*5000); data_byte = 8'h55; send_en = 1'd1; #`clk_period; send_en = 1'd0; @(posedge Tx_Done) #(`clk_period*5000); $finish; end // initial begin: fsdb_generate // $fsdbDumpfile("uart.fsdb"); // $fsdbDumpvars(0,uart_byte_tx_tb); // end endmodule
Makefile脚本为:
.PHONY: com sim run_verdi cleanOUTPUT =uartexport demo_name=$(OUTPUT)#ALL_DEFINE = + define + BAUD_9600#compile command VCS =vcs +v2k -timescale=1ns/1ns \ -debug_all \ -LDFLAGS \ -rdynamic \ -P $(Verdi_HOME)/share/PLI/VCS/LINUX64/novas.tab \ $(Verdi_HOME)/share/PLI/VCS/LINUX64/pli.a \ -full64 \ +vcs_lic+wait \ -f file_list.f \ -o ${OUTPUT} \ -l compile.log VERDI=verdi -f file_list.f \ -ssf $(OUTPUT).fsdb \ -nologo \ -l v.log #sim commandSIM = ./$(OUTPUT) \ -ucli -i ../scripts/dump_fsdb_vcs.tcl \ +fsdb+autoflush \ -l sim.log #start compilecom: $(VCS)#start simulationsim: $(SIM)#run verdirun_verdi: $(VERDI) cleanclean: rm -rf ./verdiLog ./dff ./csrc *.daidir *log *.vpd *.vdb simv* *.key *race.out* *.rc *.fsdb *.vpd *.log *.conf *.dat *.conf uart
tcl 指令为:
global envfsdbDumpfile "$env(demo_name).fsdb"fsdbDumpvars 0 "uart_byte_tx_tb"run 10000ns
好啦,本次更新到此结束,最后衷心的感谢相量子大哥与公众号数字ICer的大哥,一位大哥提供了装好了虚拟机,另一位大哥在使用过程中提供了诸多使用技巧,感谢大家的关注,我可能要停更一段时间了,如果有什么问题,欢迎后台交流,共同学习进步!
加油!