【IC验证】verilog及systemverilog特殊特性的分析
verilog及systemverilog特殊特性的分析
- 1.概述
- 2.赋值延迟
- (0)总结
- (1)情况一:initial中进行阻塞赋值和非阻塞赋值(不延迟)
- a代码
- b 电路图
- c 结果
- (2)时钟
- a 代码
- b 电路图
- c 结果
- (3)always过程块实现时序逻辑(延迟)
- a 代码
- b 电路图
- c 结果
- (4)always过程块实现组合逻辑(延迟)
- a 代码
- b 电路图
- c 结果
- (5)assign连续赋值语句实现组合逻辑(延迟)
- a 代码
- b 电路图
- c 结果
- (6)接口中的时钟(延迟)
- a 代码
- b 电路图
- c 结果
- (7)触发事件(延迟)
- a代码
- b 电路图
- c 结果
- 3.仿真软件的采样时刻
- (1)在过程块中:时钟触发沿和数据改变沿同时刻
- a代码
- b结果
- (2)在clocking block中:时钟触发沿和数据改变沿同时刻(完全一致)
- a代码
- b结果
- 4.并行块代码的执行
- 1.代码的执行时间(按照仿真结果总结出的,不具有理论依据)
- 5.对模块中成员的调用
- (1)问题
- (2)例子(以模块为例)
- 6.模块和类的静态与动态
1.概述
本文主要汇总分析verilog及systemverilog学习过程中出现的特殊问题;
2.赋值延迟
(0)总结
always过程块实现时序逻辑和组合逻辑和连续赋值语句赋值要消耗仿真时间;
接口内部的时钟,会比外部提供的时钟,存在延迟;
触发事件,@xxx,存在延迟;
(1)情况一:initial中进行阻塞赋值和非阻塞赋值(不延迟)
a代码
module test_tb;
reg [3:0] data1;
reg [3:0] data2;
initial begin
data1 = 0;
data2 <= 0;
#5;
data1 = 1;
data2 <= 1;
end
endmodule
b 电路图
initial过程块不可综合;
c 结果
结论:initial过程块中直接赋值不消耗时间;
(2)时钟
a 代码
module test_tb;
reg clk;
initial begin
clk = 1'b0;
end
always#(10) clk = !clk;
endmodule
b 电路图
延时语句不可综合;
c 结果
结论:时钟赋值不消耗时间;
(3)always过程块实现时序逻辑(延迟)
a 代码
module test_tb;
parameter T =20;
reg clk;
reg rst_n;
initial begin
clk = 1'b0;
rst_n = 1'b0;
#(T);
rst_n = 1'b1;
end
always#(T/2) clk = !clk;
reg [3:0] cnt1;
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
cnt1 <= 'd0;
else
cnt1 <= cnt1+1'b1;
end
endmodule
b 电路图
c 结果
结论:综合出寄存器,赋值需要消耗时间;
(4)always过程块实现组合逻辑(延迟)
a 代码
module test_tb;
parameter T =20;
reg clk;
reg rst_n;
initial begin
clk = 1'b0;
rst_n = 1'b0;
#(T);
rst_n = 1'b1;
end
always#(T/2) clk = !clk;
reg [3:0] cnt1;
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
cnt1 <= 'd0;
else
cnt1 <= cnt1+1'b1;
end
reg [3:0] cnt2;
always@(*)begin
cnt2 = cnt1;
end
endmodule
b 电路图
c 结果
结论:综合出数据线,赋值需要消耗时间;
(5)assign连续赋值语句实现组合逻辑(延迟)
a 代码
module test_tb;
parameter T =20;
reg clk;
reg rst_n;
initial begin
clk = 1'b0;
rst_n = 1'b0;
#(T);
rst_n = 1'b1;
end
always#(T/2) clk = !clk;
reg [3:0] cnt1;
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
cnt1 <= 'd0;
else
cnt1 <= cnt1+1'b1;
end
wire [3:0] cnt2;
assign cnt2 = (cnt1 == 1)?1'b1:1'b0;
endmodule
b 电路图
c 结果
结论:综合出组合逻辑电路,赋值需要消耗时间;
(6)接口中的时钟(延迟)
a 代码
interface my_if(input bit clk);
bit [7:0] sig1;
bit [7:0] sig2;
clocking cb@(posedge clk);
input sig1;
output sig2;
endclocking
endinterface
module test_blocking1_test;
reg clk;
my_if mif(clk);
initial begin
clk = 0;
end
always#3 clk = !clk;
endmodule
b 电路图
不可综合语句;
c 结果
结论:
接口中的时钟跳变,会比顶层时钟跳变,延迟;
(7)触发事件(延迟)
a代码
module test_tb;
parameter T = 20;
reg clk;
reg rst_n;
reg cnt;
initial begin
clk = 0;
rst_n = 0;
cnt = 0;
#(T);
rst_n = 1;
#(T);
@(posedge clk);
cnt = 1;
end
always#(T/2) clk = !clk;
endmodule
b 电路图
不可综合语句;
c 结果
3.仿真软件的采样时刻
(1)在过程块中:时钟触发沿和数据改变沿同时刻
a代码
说明:时钟clk1在T/2倍数处改变,输入数据in1 也在T/2倍数处改变;
module test_tb;
parameter T = 20;
reg clk1;
reg [3:0] in1;
reg rst_n;
initial begin
clk1 = 1'b0;
rst_n = 1'b0;
in1 = 'd0;
#(T);
rst_n = 1'b1;
end
always#(T/2) in1 = in1 + 1'b1;
always#(T/2) clk1 = !clk1;
reg [3:0] data_out1;
always@(posedge clk1 or negedge rst_n)begin
if(!rst_n)
data_out1 <= 'd0;
else
data_out1 <= in1;
end
endmodule
b结果
questasim:
结论:仿真器采样为数据跳变沿右侧的值;
vcs(DVE):
结论:仿真器采样为数据跳变沿右侧的值;
vcs(verdi):
结论:仿真器采样为数据跳变沿右侧的值;
(2)在clocking block中:时钟触发沿和数据改变沿同时刻(完全一致)
a代码
b结果
questasim:
结论:仿真器采样为采样时刻左侧的值;
vcs(DVE):
结论:仿真器采样为采样时刻右侧的值;
vcs(verdi):
结论:仿真器采样为采样时刻右侧的值;
说明:时钟clk1在3倍数处改变,输入数据sig1 第二次变化在时钟跳变沿处改变;
为什么两种clk翻转时刻不同???
4.并行块代码的执行
1.代码的执行时间(按照仿真结果总结出的,不具有理论依据)
(1)在verilog实现的硬件电路中,同一时刻不同过程块中的并行语句,是并行执行;
(2)在verilog和systemverilog仿真时,同一时刻同一过程块中的串行语句,是串行执行;
见例子1中,“aaa”、“bbb”、“ccc”的打印语句,是同一过程块中的串行语句,是串行执行;
(3)在verilog和systemverilog仿真时,同一时刻不同过程块中的并行语句,是串行执行;
见例子1中“aaa”、“bbb”、“ccc”三个打印语句和"eee"打印语句,是不同过程块中的并行语句,所以会串行执行;
(因为“aaa”、“bbb”、“ccc”打印语句过程块在前,优先打印)
(4)在verilog和systemverilog仿真时,部分语句会消耗一个很小的时刻的;
如:“#”
见例子2中,两过程块均延时1个时间单位,然后分别打印aaa、ccc,所以结果aaa在前、ccc在后;
而在第一个过程块中通过执行两次**#1表示延迟两个时间单位,第二个过程块通过执行一次#2同样表示延迟两个时间单位。但是过程块1用了两次#会消耗两个很小的时刻,过程块2只用了1次#只会消耗1个很小的时刻,所以会先打印ddd再打印bbb**
(5)例子
例子1
module test_ft_tb;
initial begin
$display("aaa");
$display("bbb");
$display("ccc");
#10;
$display("ddd");
end
initial begin
$display("eee");
#10;
$display("fff");
end
endmodule
结果:
例子2
module test_ft_tb;
initial begin
#1;
$display("aaa");
#1;
#1;
$display("bbb");
end
initial begin
#1;
$display("ccc");
#2;
$display("ddd");
end
endmodule
结果:
5.对模块中成员的调用
(1)问题
systemverilog中,在一个区域中,声明了一个类(或模块
),那么在这个区域中就可以调用这个类(或模块
)中的成员(变量和方法);
(2)例子(以模块为例)
代码:
module add_module;
int data_arr [$];
task automatic get_data(input int data_in);
data_arr.push_back(data_in);
endtask
task automatic add_data(output int add_re);
foreach(data_arr[i])begin
add_re = add_re + data_arr[i];
end
endtask
endmodule
module test_ft_tb1;
add_module u1();
initial begin
int re;
u1.get_data(1);
u1.get_data(2);
u1.get_data(3);
$display("data_arr = %p",u1.data_arr);
u1.add_data(re);
$display("result is %0d",re);
end
endmodule
结果:
6.模块和类的静态与动态
模块是静态的,创建于编译时刻;
类是动态的,创建于实例化时刻。