数字IC设计工具教程——VCS(lab2)
参考:Synopsys VCS lab2
注意:由于UCLI(Unified Command-Line Interface)debug较困难,一般不常用!
文章目录
Getting Started
我们将再次使用以下进位选择的8-bit加法器:
我们在实验文件中嵌入了错误。 目标是使用调试技术来定位和修复错误。 我们看到的块图是Verilog代码打算实现的内容。这个实验分为两部分。 每个部分都有自己的相关任务。
下面是概览:
- 编译并仿真加法器以记录错误是什么。
- 在Part A中,将Verilog系统任务调用插入设计文件中,然后编译、仿真和解释Verilog系统任务调用输出,以尝试将错误点固定。在找到并纠正错误之前,您将重复此过程。
- 在Part B中,将启用UCLI调试器进行编译。 使用UCLI交互式模拟控制来完成代码的执行并定位错误源。更正错误并仿真以确认校正有效。
Part A: 调用Verilog的系统任务debug
Task 1: 有用的VCS开关
进入lab2 Part A目录。
从此实验开始,将使用各种编译和运行时开关。 万一忘记了这些开关是什么,可以通过以下快速方式获取提醒:
shell> vcs -full64 -h
此命令将为您提供常用VCS编译时和运行时开关的列表,以及对其功能的简要说明。
Task 2: 编译执行第一次通过验证
编译并仿真进位选择加法器。
shell> vcs -full64 -f adder.f -R
一个错误被报告:
输入a=01,b=01,进位cin=0,但和sum=00而不是02(十六进制)。错误可能在任何的RTL模块中。我们将检查每一个操作。
Task 3: Debug错误
在调试中,必须能够通过设计跟踪错误。 如果选择仅使用Verilog系统任务调用调试,通常有两种跟踪错误的方法。 可以直接在RTL模块中插入Verilog系统任务调用,也可以在testbench中添加Verilog系统任务调用。
最好在testbench中添加调试语句。 原因有二:一是testbench通常是进行结果检查的地方。 使用Verilog系统任务调用以及结果检查程序可以有效地提供断点功能。 第二,出于性能原因,希望避免重新编译。通过将调试语句仅放在testbench中,您可以将重新编译限制为仅testbench文件。
修改testbench,以便通过设计层次结构跟踪错误。首先,查看testbench,选择Verilog系统任务调用放置的位置。 输入以下UNIX命令:
1. shell> more addertb.v
查看testbench如下:
最佳插入点是在检测错误之后。在此处插入更多$display
系统任务调用,以便通过设计层次结构跟踪错误。首先查看add8模块中发生的情况。
(注:当在编译仿真时命令中加入参数+monitoroff
后,$test\$plusargs("monitoroff")
为True,否则为False)
在addertb.v中加入下面三行于$finish之前。
$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);
再编译仿真加法器看看add8模块级的结果。
2. shell> vcs -full64 -f adder.f -R
插入$display
后的结果如下:
add4(u1)的输出sum[1]应该是1,相反,它错误地被计算为0。去add4(u1)看看发生了什么。
在addertb.v中加入下面三行于$finish之前。
$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);
将系统任务$finish
改为$stop
创建一个断点。
通过此修改,每次检测到错误时,都会打印$display中列出的结果,此时将停止testbench。
再次编译运行testbench:
3. shell> vcs -full64 -f adder.f -R
当VCS遇到我们嵌入的$stop系统任务调用时,它会将您放入UCLI调试器中。 您将看到UCLI提示 ucli%
。
首先看$display信息:
lsb操作看起来不错,但是,第二个lsb有问题。 看起来未加入进位。
继续仿真到下一个错误点,看看是否可以推断这真的是一个问题。 继续使用"run
"进行仿真。 在继续之前,请尝试其他一些UCLI命令,看看它们是否有效。
4. ucli% run
仿真应该继续至下一个错误点并停止,可以看到:
注意到,问题似乎出在fa模块如何处理进位中。
退出UCLI命令模式:
5. ucli% quit
查看fa.v文件的内容:
6. shell> more fa.v
fa.v文件内容如下:
可以看到fa.v模块中全加器的进位未加入,修改为assign {cout, sum} = a + b + cin
并重新编译,显示编译成功。
取消monitor系统任务进行仿真,这样可以提高仿真速度。
7. shell> simv +monitoroff
仿真结果如下:
Part B: 用VCS UCLI调试器debug
Task 1: 编译执行第一次通过验证
在Part A中,您可能注意到的一件事是,使用Verilog系统任务调用插入进行调试是一项大量工作。 不断执行完整的修改-编译-模拟-验证循环。 数据输入不仅耗时,此调试回路中的其他步骤也非常耗时。 在Part B,我们将使用UCLI命令集来简化调试过程。
在Part A,您需要使用笔和纸来帮助您诊断问题。UCLI简化,但并不能消除对笔和纸的需求。
切换至lab2 Part B目录。
再次,编译和仿真加法器,看看它是否正常工作。
shell> vcs -full64 -f adder.f -R
注意在时间50时出现了错误:
Task 2: 启用UCLI调试器开关进行编译
重新编译,这一次,启用UCLI调试器。
首先,通过将文件$finish修改为$stop,在addertb.v中添加硬断点:
启用UCLI进行编译和仿真。
shell> vcs -full64 -f adder.f -R -debug_all -ucli
当VCS遇到我们嵌入的$stop
系统任务调用时,它会进入UCLI调试器中。 将看到UCLI提示符ucli%
。
Task 3: 用UCLI调试器debug
调用UCLI调试器时,会将被放置在仿真时间0。
如果您忘记了UCLI可用命令,请尝试以下UCLI命令快速摘要:
ucli% help
要了解我们在设计层次结构中的位置,输入:
要到达问题区域,输入:
查看addertb中定义的信号。
列出所有信号/端口/范围等。要查看感兴趣的信号值,请使用获取命令。
sum_test总线不正确。 跟踪sum_test信号以查找问题。 使用scope
命令进入u1(add8)模块。
在调试中,通常需要多次打印出同一组变量的值。为这个通常执行的任务创建一个别名将使事情变得容易得多。一旦我们创建了别名,执行任务可以通过键入别名。
可以在此处查看错误的详细信息。 2:1mux应选择"0"的sum_0值,但选择了"1"的sum_1值。 这可能是问题所在。
将UCLI窗口保留(不要退出UCLI)。 打开一个新的UNIX窗口,看看add8.v文件。
看看mux代码。 对mux的输入被颠倒。 事实上,sum和carry mux都有相同的错误。
幸运的是,可以在不离开UCLI环境的情况下仿真校正。如果将sum_0加法器的进位更改为"1",并将sum_1加法器的进位更改为"0",则将有效地纠正问题。
请注意zero_add_cin和one_add_cin信号的当前值。
将zero_add_cin信号强制到"1",将one_add_cin信号强制到"0"。
要查看所有值,请使用创建的"prvars"别名。但是,在创建别名时,没有包括zero_add_cin和one_add_cin。由于需要查看所有值,因此需要将它们添加到命令行中。
进位值已更改,sum[7:4] 保持不变。 这是因为我们没有增加仿真时间以允许执行新的值。 将仿真时间执行10时间增量。
看一下目前的值。
现在,所有值都应该是正确的。 返回testbench级别并验证加法器是否正常工作。
这些都是正确的结果。 设置一个断点,其中c4在add8模块中添加从"0"到"1"的更改,并查看这是否有效。
您已将仿真从60到26400,而不会遇到错误。 看看这些值。
VCS遇到了c4从"0"到"1"的过渡,并停止了仿真。 但是,除了尚未遇到这些事件之外,可能还有其他事件。 在此特定情况下,4位MSB(在 addertb.u1 中的sum_1)添加发生在 u1.c4 信号转换后。因此,sum_test的当前值尚未更新。 你所看到的是以前sum_test的值。
您需要将仿真至少执行到下一个时间步骤,以查看当前时间步骤中所有事件的全部效果。
现在看起来对了。移除断点并继续仿真。
我们可以通过删除断点序号来删除断点。
重新仿真。
您已成功检测到错误,并在仿真中解决了错误。
如果厌倦了一遍又一遍地重新键入相同的命令,使用日志文件作为生成脚本文件的参考。这将简化调试命令条目。
我们已经生成了一个脚本文件。 在此脚本文件中,定义常用别名并执行仿真的进位固定命令。
(注意:记住这不是一个真正的解决方法! 它仅用于允许仿真在不离开UCLI环境的情况下继续。)
重新打开仿真。
您可以看到,zero_add_cin和one_add_cin已设置为仿真值。 试试别名tb。
这个别名有效! 让我们仿真一下。
祝贺! 退出UCLI并查看仿真日志文件。 日志文件应显示在仿真会话中所做的一切。
可以使用此文件的内容作为生成脚本文件的参考。
Part C: 获得帮助
Task 1: 提交文件寻求帮助
如果您需要从VCS支持团队获得帮助,则有两个开关可以使与支持人员的工作更加顺畅。 它们是-ID
和-Xman=4
。
您应该看到正在使用的VCS版本和工作站操作系统信息的摘要。 这将有助于支持人员快速关注与特定 VCS 版本和工作站操作系统相关的问题。
当电话支持不能解决您的问题时,您的下一个行动计划应该是提交一个测试案例供Synopsys工程师使用,为此目的使用 –Xman=4
开关。
应该看到已生成文件tokens.v,打开看看包含所有测试案例。
/*
instances: 0
nodes: 6 (0)
node widths: 44 (0)
process: 1 (0)
ports: 1 (0)
portconnects: 5 (0)
*/
`portcoerce
`inline
// No timescale specified
module addertb;
reg [7:0] a_test;
reg [7:0] 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);
initial begin
for (test = 0; (test <= 18'h1ffff); test = (test + 1)) begin
cin_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 = %b, cout = %b", a_test,
b_test, sum_test, cin_test, cout_test);
$stop;
end
#(50) ;
end
$display("*** Testbench successfully completed! ***");
$finish;
end
endmodule
/*
instances: 0
nodes: 12 (0)
node widths: 39 (0)
contassign: 4 (0)
ports: 5 (0)
ports: 3 (0)
portconnects: 15 (0)
*/
// No timescale specified
module add8(a, b, cin, sum, cout);
input [7:0] a;
input [7:0] b;
input cin;
output cout;
output [7:0] sum;
wire c4;
wire c8_0;
wire c8_1;
wire zero_add_cin;
wire one_add_cin;
wire [7:4] sum_0;
wire [7:4] sum_1;
assign sum[7:4] = (c4 ? sum_0 : sum_1);
assign cout = (c4 ? c8_0 : c8_1);
assign zero_add_cin = 0;
assign one_add_cin = 1;
add4 u1(a[3:0], b[3:0], cin, sum[3:0], c4);
add4 zero_add(a[7:4], b[7:4], zero_add_cin, sum_0, c8_0);
add4 one_add(a[7:4], b[7:4], one_add_cin, sum_1, c8_1);
endmodule
/*
instances: 0
nodes: 6 (0)
node widths: 17 (0)
ports: 5 (0)
ports: 4 (0)
portconnects: 20 (0)
*/
// No timescale specified
module add4(a, b, cin, sum, cout);
input [3:0] a;
input [3:0] b;
input cin;
output cout;
output [3:0] sum;
wire [3:1] c;
fa u1(a[0], b[0], cin, sum[0], c[1]);
fa u2(a[1], b[1], c[1], sum[1], c[2]);
fa u3(a[2], b[2], c[2], sum[2], c[3]);
fa u4(a[3], b[3], c[3], sum[3], cout);
endmodule
/*
instances: 0
nodes: 5 (0)
node widths: 5 (0)
contassign: 1 (0)
ports: 5 (0)
*/
// No timescale specified
module fa(a, b, cin, sum, cout);
input a;
input b;
input cin;
output sum;
output cout;
assign {cout, sum} = ((a + b) + cin);
endmodule
/* Design Summary
modules: 4
udps: 0
mod flatinsts: 0
udp flatinsts: 0
nodes: 29 (0)
node widths: 105 (0)
process: 1 (0)
gates: 0 (0)
contassigns: 5 (0)
ports: 15 (0)
modinsts: 8 (0)
udpinsts: 0 (0)
portconnects: 40 (0)
*/
// END: VCS tokens