当前位置: 代码迷 >> 综合 >> VCS 仿真教程 lab2
  详细解决方案

VCS 仿真教程 lab2

热度:32   发布时间:2023-12-18 22:47:21.0

lab2当中主要练习ucli的使用,lab2的加法器结构框图如下图所示:

这次给的教程文件当中已经提前埋好了bug,教程所做的就是debug的全流程。

PartA和PartB分别用的是修改源文件和利用ucli的办法。

 

PartA

1. 首先进入总目录下的./lab2/parta当中,利用.f文件运行命令:

$ vcs –f adder.f -R

编译顺利通过,但是仿真的过程出现了报错:

***ERROR at time = 25750 ***
a = 01, b = 01, sum = 00;  cin = 0, cout = 0

在这里应该得到sum=02 ,现在需要找到出错的原因在哪里。

 

2.Verilog system task call

verilog的task call(系统调用)可以放在RTL模块代码中也可以放在testbench当中,在实际的debug过程中,更好的选择是将task call放在testbench当中。愿意有二:

(1)testbench本来就是用来检验程序结果的,使用task call以后更容易设置断点。

(2)可以避免重复编译,省略资源和时间。

现在我们用more命令,查看addertb.v文件的内容:

$ more addertb.v
module addertb;
reg [7:0] a_test, b_test;
wire [7:0] sum_test;
reg cin_test;
wire cout_test;
reg [17:0] test;add8 u1(a_test, b_test, cin_test, sum_test, cout_test); //DUT(Device Under Test)的例化initial
if (!$test$plusargs("monitoroff")) //判定输入的命令当中是否有"monitoroff"字段$monitor ($time, "  %h + %h = %h;  cin = %h, cout = %h",a_test, b_test, sum_test, cin_test, cout_test);//若没有则开始监控过程initial
beginfor (test = 0; test <= 18'h1ffff; test = test +1) begincin_test = test[16];a_test = test[15:8];b_test = test[7:0];#50;if ({cout_test, sum_test} !== (a_test + b_test + cin_test)) begin$display("***ERROR at time = %0d ***", $time);     //显示出错时的时间和数据信息$display("a = %h, b = %h, sum = %h;  cin = %h, cout = %h",a_test, b_test, sum_test, cin_test, cout_test);$finish;  //检测到出错后结束仿真,相当于一个端点end#50;end$display("*** Testbench Successfully completed! ***");$finish;//养成一个插入仿真完成信息的好习惯,若信息未弹出则可能表示程序进入了无限循环
end
endmodule

几个task call的详细解释如下:

  • $display   提供了显示字符串和多个参数值的功能。
  • $monitor  提供了监控和输出参数列表中的表达式和变量值的功能。当启动一个带有一个或多个参数的$monitor任务时,每当参数列表中的变量或表达式的值发生变化时整个参数列表中变量或表达式的值都将输出显示。如果同一时刻有两个或多个表达式的值发生变化,只输出一次。 参数可以是$time,用于标明变化时刻。
  • $time 表示当前时间
  • $finish  表示停止仿真
  • $test$plusargs   使用时,该函数会判断参数string是否定义,从而返回0或非0。例如本例中就是用来判断命令中是否“定义”了"monitoroff"(其实就是是否启用了这个选项)

参考资料:https://blog.csdn.net/kevindas/article/details/80380144

                 https://www.cnblogs.com/pengwangguoyh/articles/3167498.html

 

3.在仿真代码当中插入新的$display,追踪出错根源。

    if ({cout_test, sum_test} !== (a_test + b_test + cin_test)) begin$display("***ERROR at time = %0d ***", $time);$display("a = %h, b = %h, sum = %h;  cin = %h, cout = %h",a_test, b_test, sum_test, cin_test, cout_test);$display("\nIn add8(u1)");                               //新增$display("a = %b, b = %b, sum = %b; cin = %b, cout = %b",//新增u1.a, u1.b, u1.sum, u1.cin, u1.cout); $display("\nIn add4(u1)");                               //新增$display("a = %b, b = %b, sum = %b;  cin = %b, c = %b, cout = %b",//新增u1.u1.a, u1.u1.b, u1.u1.sum, u1.u1.cin, u1.u1.c, u1.u1.cout);$stop;   //从$finish变为$stop,只暂停不结束

插入完后执行仿真,弹出如下的新报错信息并进入ucli模式(即命令交互模式)

注:此处没有加入参数来开启ucli,因此启用的只是“MINI UCLI”,此时只能用简单的“run”“quit”“dump”等命令。

In add8(u1)
a = 00000001, b = 00000001, sum = 00000000; cin = 0, cout = 0In add4(u1)
a = 0001, b = 0001, sum = 0000;  cin = 0, c = 001, cout = 0
$stop at time 25750 Scope: addertb File: addertb.v Line: 34
ucli% 

发现adder4之内生成的进位是没有问题的(c=001),但是sum输出的数据有误或者说c的进位没有传递到sum(sum=0000,应该是0010)

在这里教程提醒我们输入一个小数点“.”来继续执行仿真,但是又出现了问题

ucli% .
invalid command name "."

在网上找了半天也没有找到小数点代表了什么命令,也有可能是因为教程中提示的是CLI而实际上进入的是UCLI所以才会出现错误。

总之我用run命令继续执行仿真,仿真到了下一个error point以后又停住了:

***ERROR at time = 25950 ***
a = 01, b = 03, sum = 02;  cin = 0, cout = 0In add8(u1)
a = 00000001, b = 00000011, sum = 00000010; cin = 0, cout = 0In add4(u1)
a = 0001, b = 0011, sum = 0010;  cin = 0, c = 001, cout = 0

到这里就能很明显地看出来进位信号的传递出现了问题,根本没有实现“全加器”的功能,那么我们下一步进入到fa.v中debug。

 

4.用quit命令可以退出ucli模式,然后打开fa.v

$ more fa.v
module fa(a, b, cin, sum, cout);
input a, b, cin;
output sum, cout;
assign {cout, sum} = a + b;
endmodule

终于知道问题在哪里了,cin根本没有加上去。我们把表达式修改好,再次执行仿真:

发现因为一直启用着$monitor选项所以出现了大量的监控信息,这时可以启用"monitoroff"选项:

$ simv +monitoroff
Chronologic VCS simulator copyright 1991-2014
Contains Synopsys proprietary information.
Compiler version I-2014.03; Runtime version I-2014.03;  Oct 26 15:00 2019
*** Testbench Successfully completed! ***
$finish called from file "addertb.v", line 39.
$finish at simulation time             13107200V C S   S i m u l a t i o n   R e p o r t 
Time: 13107200
CPU Time:      0.600 seconds;       Data structure size:   0.0Mb

所有的监控信息都被折叠,CPU时间也减少了三分之一。

 

PartB

PartB中利用ucli命令进行debug。

1.进入总目录下的./lab2/partb,运行文件夹中的.f文件,发现在仿真过程当中出错了。

$ vcs -f adder.f -R
……
……
***ERROR at time = 50 ***
a = 00, b = 00, sum = 10;  cin = 0, cout = 0
$finish called from file "addertb.v", line 25.
$finish at simulation time                   50

应当得到结果sum=00,但实际得到结果sum=10,可能是adder4的进位出了错误。

和parta同样,我们修改addertb.v中的语句,但这次只是把$finish改成$stop,剩余交给ucli命令处理。

 if ({cout_test, sum_test} != {a_test + b_test + cin_test)) begin$display(“***ERROR at time = %0d ***”, $time);$display("a = %h, b = %h, sum = %h;  cin = %b, cout = %b",a_test, b_test, sum_test, cin_test, cout_test);$stop;   //原为$finish

 

2.进入ucli模式

现在我们启用新的选项 -debug_all -ucli,启用选项后当VCS遭遇$stop时会自动进入UCLI当中。

$ vcs –f adder.f –R –debug_all -ucli
……
……
$stop at time 0
ucli%
  • help 可以快速查询UCLI命令
  • scope 查询当前所在的电路层次

参考资料:https://blog.csdn.net/kevindas/article/details/78991759

紧接着输入以下命令:

ucli% run
***ERROR at time = 50 ***
a = 00, b = 00, sum = 10;  cin = 0, cout = 0
addertb.v, 25 :       $stop;
ucli% show //列出信息
sum_test
cout_test
a_test
b_test
cin_test
test
u1
ucli% get sum_test -radix hex //显示sum_test的值
'h10
  • show 显示所有的信号/接口/层次等信息;-ports选项可以只列出端口;-value选项可以直接显示信号值
  • get 查看某个信号的值;-radix选项可以指定进制,默认是二进制。一次只能get一个信号

接下来追踪信号的错误来源,进入u1(add8)当中:

ucli% scope u1
addertb.u1
ucli% show -ports -value -radix hex
a 'h00
b 'h00
cin 'h0
sum 'h10
cout 'h0

 

3.alias命令

  • alias 在一次debug当中经常需要反复查看同一个变量来确定错误来源,因此可以将常用命令集成在alias(别名)当中简化步骤。建立alias以后只要输入alias的名字就相当于执行了alias里面的所有命令。
ucli% alias vars show -value -radix hex a b sum sum_0 sum_1 cin c4
vars
ucli% vars
a 'h00
b 'h00
sum 'h10
sum_0 'h0
sum_1 'h1
cin 'h0
c4 'h0

在这里vars就是alias的名字,sum_0和sum_1分别表示sum的第0位和第1位。可以看到是add8中的高位输出出了问题,因此我们打开另一个终端(不要退出UCLI),查看add8.v文件:

$ more add8.v
……
……
assign sum[7:4] = c4?sum_0:sum_1;
assign cout = c4?c8_0:c8_1;

现在看出问题在于mux的判定条件反了,所以会在输入都为0的情况下输出1。

 

4.先不修改源文件,经过思考我们知道在不改变电路结构的情况下只要让zero_add_cin和one_add_cin的值翻过来(负负得正)就可以解决这个问题。

我们用force命令修改这两个信号的值并查看结果:

ucli% show zero_add_cin one_add_cin -value
zero_add_cin 'b0
one_add_cin 'b1
ucli% force zero_add_cin 1
ucli% force one_add_cin 0
ucli% vars zero_add_cin one_add_cin
a 'h00
b 'h00
sum 'h10
sum_0 'h0
sum_1 'h1
cin 'h0
c4 'h0
zero_add_cin 'h1
one_add_cin 'h0
  • force 强制改变或存放一个变量/信号值

此时zero_add_cin和one_add_cin已经改变,但是输出值没有变,这是因为进程还没有往下进行,变化还没有反映在sum当中。

让仿真执行10个单位时间再进行查看,已经变成了正确的结果:

ucli% run 10
60 s
ucli% vars
a 'h00
b 'h00
sum 'h00
sum_0 'h1
sum_1 'h0
cin 'h0
c4 'h0

回到上一层电路,仍然得到正确的结果,说明当前状态下debug正确:

ucli% scope -up
addertb
ucli% show a_test b_test cin_test cout_test sum_test -value
a_test 'b00000000
b_test 'b00000000
cin_test 'b0
cout_test 'b0
sum_test 'b00000000

 

5.