接上一篇:🔗interface clocking block使用 及 verdi capture delta cycle
Glitch: a transition that occurs on a signal before the signal settles to its intended value.
Glitch一般由竞争导致,有些glitch的时间宽度为0,这种glitch需要加上+fsdb+glitch=0dump才可以在波形上查看到。nWave上View -> Display Glitch在波形上*标记glitch 。
Glitch的危害:🔗eetop_为啥时钟毛刺会毁了整个设计 – min pulse width
示例1:
构造一个delta time = 0的glitch:
module top;
logic val;
initial begin
val = 0;
#10;
val = 1;
end
initial begin
#10;
val = 0;
end
always @(posedge val or negedge val) begin
$display("time:%0t,val:%b",$realtime,val);
end
endmodule
打印结果:
time:0,val:0
time:10,val:0
从波形上可以看到#10处val值的跳变,从 0->1->0。但是display打印结果只有一个,似乎posedge和negedge中有一个没有被捕捉到。
示例2:
其实0->1 1->0的跳变确实在仿真器中发生了,只不过这两个行为都发生在Active区,导致always @(posedge val or negedge val)只被触发一次。
改成如下,分别通过always @(posedge val ) always @(negedge val)两个进程process捕捉,则可以同时打印。
module top;
logic val;
initial begin
val = 0;
#10;
val = 1;
end
initial begin
#10;
val = 0;
end
always @(negedge val) begin
$display("time:%0t,val:%b",$realtime,val);
end
always @(posedge val ) begin
$display("time:%0t,val:%b",$realtime,val);
end
endmodule
打印结果:
time:0,val:0
time:10,val:0
time:10,val:0
示例3:
将上例中的val = 0改成val <= 0,看看会有怎样的改变。
module top;
logic val;
initial begin
val = 0;
#10;
val = 1;
end
initial begin
#10;
val <= 0;
end
always @(posedge val or negedge val) begin
$display("time:%0t,val:%b",$realtime,val);
end
endmodule
打印结果:
time:0,val:0
time:10,val:1
time:10,val:0
此时glitch处posedge negedge都被捕捉到。=发生在Actice区,<=发生在NBA区。
posedge处的结果val=1,而在示例2中,val=0。
示例4
这次将val变化置于同一个process中。
module top;
logic val;
initial begin
val = 0;
#10;
val = 1;
val = 0;
#10;
end
always @(negedge val) begin
$display("time:%0t,val:%b",$realtime,val);
end
always @(posedge val ) begin
$display("time:%0t,val:%b",$realtime,val);
end
endmodule
vcs打印结果:
time:0,val:0
和示例1中使用always @(posedge val or negedge val)情况一样,推测vcs对于同一个process的同一个timeslot的同一个region中的同一个参数的调度,只会发生一次。(扯:看上去有点像C编译器对没有使用 voliate修饰寄存器存取而优化了一样)
irun打印结果:
time:10,val:0
time:10,val:0
示例5
module top;
logic val;
initial begin
val = 0;
#10;
val = 1;
val = #0 0;
#10;
end
always @(negedge val) begin
$display("time:%0t,val:%b",$realtime,val);
end
always @(posedge val ) begin
$display("time:%0t,val:%b",$realtime,val);
end
endmodule
打印结果:
time:0,val:0
time:10,val:1
time:10,val:0
和示例3的打印结果一致,#0的事件发生在In-active区,但是delta expand并不支持In-active区的查看,只显示了Active区。
Note:
- 上述示例仿真时是否加上
+fsdb+delta并不会影响vcs打印结果。 - irun仿真时,默认不打印
0时刻, 其他结果与vcs一致。(示例4除外) - 对于sv调度机制,IEEE规定了一些准则,但是还有一些没有规定,这部分各家EDA工具实现略有差异,存在不确定性
Nondeterminism。
最后一个实际示例:
module A(input wire clk,
input wire rst_n,
output wire req_rst);
reg req_rst_r;
always @(posedge clk or negedge rst_n) begin
if(~rst_n)
req_rst_r <=0;
else
req_rst_r <=1;
end
assign req_rst = req_rst_r;
endmodule
module delay_sync #(parameter SYNC_DEPTH = 2)
(input wire clk,
input wire resetn_in,
output wire resetn_out);
reg [SYNC_DEPTH-1:0] in_dly;
always @(posedge clk or negedge resetn_in) begin
if(~resetn_in)
in_dly <= {SYNC_DEPTH{1'h1}};
else
in_dly <= {in_dly[SYNC_DEPTH-2:0],1'b0};
end
assign resetn_out = in_dly[SYNC_DEPTH-1];
endmodule
module top;
logic clk;
logic rst_n;
logic resetn_out;
always #5 clk = ~clk;
initial begin
clk = 'b0;
rst_n = 'b0;
#12;
rst_n = 'b1;
end
assign reset_n = rst_n & resetn_out;
A u_A(.clk(clk),
.rst_n(reset_n),
.req_rst(req_rst));
delay_sync #(.SYNC_DEPTH(4)) u_delay_sync(
.clk(clk),
.resetn_in(req_rst),
.resetn_out(resetn_out));
initial begin
#60;
$finish();
end
initial begin
$fsdbDumpfile("wave.fsdb");
$fsdbDumpvars;
end
endmodule
上述示例module A req_rst请求reset,通过delay_sync延迟4个cycle。glitch发生在module A rst_n信号上。
vcs对NBA中的信号分三次调度:
NBA(0): After delay 4 cycle, in_dly[3] transit from 1 to 0, leading assert A.rst_n
NBA(1): in module A, assert rest_nleading dis-assert A.req_rst
NBA(2): in module delay_sync,req_rst as reset signal. dis-assert req_rstleading in_dly[3] transit from 0 to 1, leading dis-assert A.rest_n。
Since A.rst_n change 1->0->1in same time slot,result in delta time =0 glitch.
避免上述glitch发生,可以在A.rst_n路径上加上delay。
从波形上看,A.rst_n虽然被采样到了,并触发了reset行为,但是不满足真实器件的恢复时间Recovery time和去除时间Removal time。
功能仿真一般不会check Recovery/Removal time,GateSim中可以在Specify block中判断:
带SDF的仿真中也可以反标加入 Recovery/Removal time的check:
需要保持良好的RTL coding style避免违例。
恢复时间(Recovery time):与同步电路中的建立时间类似,是指异步控制信号(如寄存器的异步清除和置位控制信号)在“下个时钟沿”来临之前变无效的最小时间长度。这个时间的意义是,异步控制信号在时钟上升沿来临Trecovery时间就要保持稳定,如果保证不了这个最小恢复时间,也就是说“下个时钟沿”来临时,这个异步控制信号不能保证正常执行。
去除时间(Removal time):与同步电路中的保持时间类似,是指异步控制信号(如寄存器的异步清除和置位控制信号)在“有效时钟沿”之后变无效的最小时间长度。这个时间的意义是,异步控制信号在时钟上升沿后仍需保持Tremoval的稳定时间,如果保证不了这个去除时间,也就是说这个异步控制信号的解除与“有效时钟沿”离得太近,那么依旧不能保证这一异步控制信号能正常执行。
————————————————
版权声明:本文为CSDN博主「CLL_caicai」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:blog.csdn.net/CLL_caicai/…
异步复位同步释放
对于RESET的处理,一般采用异步复位同步释放的方式,参考🔗亚稳态相关:三种复位方式详解—同步复位/异步复位/异步复位同步释放 🔗eetop_同步复位电路 reset synchronizer
通过两级寄存器完成同步,消除亚稳态:
同步复位电路 reset synchronizer 的输出 rstn_sync 相对于时钟 clk 有一个固定的时序关系。 在 PR (place & route) 中,工具构造 buffer tree 去 buffer rstn_sync,并算出 buffer 后 rstn_sync 到后续每个 D flip flop 复位端的时间,通过插入或调整 buffer, 保障后续电路中每一个 D flip flop 的复位端都满足 removal/recovery 要求。 反之,如果 rstn_sync 相对于时钟 clk 没有有一个固定的关系,那工具就无从下手了,也就无法保障 removal/recovery 要求了。
顺便说一下,综合工具是不查 removal/recovery time 的。 如果复位电路上有错,通常会在设计流程很后面才发现,改起来代价也会高一些。 所以一定要在一开始就重视复位电路。
1.组合逻辑的输出都有毛刺的可能。2. 尽量避免模拟工程师做的时钟电路,避免在clock path用组合逻辑。
常见的形式采用移位寄存器:
module RESET_SYNC #(parameter SYNC_DEPTH = 2)
(input wire clk,
input wire resetn_in,
output wire resetn_out);
reg [SYNC_DEPTH-1:0] rn_sync;
always @(posedge clk or negedge resetn_in) begin
if(~resetn_in)
rn_sync <= {SYNC_DEPTH{1'h0}};
else
rn_sync <= {rn_sync[SYNC_DEPTH-2:0],1'b1};
end
assign resetn_out = rn_sync[SYNC_DEPTH-1];
endmodule
RESET_SYNC可以用于同步复位,也以为用于IP的restndelay于clock之后产生。
时钟切换
真实器件中,glitch不会是delta time =0的,这是仿真的结果。常见的是在assign clk_o = sel ? clk1:clk2;切换时,产生glitch。 参考🔗verilog glitch_free两个时钟切换电路
🔗时钟切换