[SystemVerilog语法拾遗] systemverilog中各种延时方式详解

[SystemVerilog语法拾遗] systemverilog中各种延时方式详解

最近在做系统集成的时候经常会用到模块间信号连接的时候增加delay的操作,而systemverilog语法中支持各式各样的delay添加方法。
sv中有3种类型的赋值: 阻塞赋值、非阻塞赋值和连续赋值。
延时(#1)写的位置有2种:
当延时(#1)写在整个表达式最前面时,三种类型的赋值,延时结果一致是:等待_忽略中间变化再计算
当延时(= #1)写在等于号(=)后面时:
1.连续赋值语法错误排除。
2.阻塞赋值是:先计算后延时再赋值
3.非阻塞赋值是:直接跟随
这样组合下来总共有6中添加延时的方式,如下图所示:
在这里插入图片描述
延时分类图示

下文为转载文章,详细介绍各种delay方式的示例原理,最后补充说明文章中一处错误的讲解。

Verilog语法之延时

说明
LHS: Left-Hand-Side,左式;
RHS: Right-Hand-Side,右式;
`timescale 1ns/100ps;

连续赋值中的延时

LHS中加延时

assign #2 y = ~a;
描述:非门的延时为2ns,输入端信号变化到输出端体现出来需要2ns时间。即a发生变化后,经过2ns时间,y才发生变化。
注意:任意小于2ns的信号变化脉冲都被过滤掉。例如:在这里插入图片描述

在a从1变化为0时,由于0的持续时长仅1ns,变化将不体现在输出端上,输出端在一次变化后将一直维持在0。
解释:无论何时输入信号a发生变化,都会立即产生并执行一个计算事件,计算RHS的值,即~a,这个过程可表示为assign tmp = ~a,并且是立即执行。若a此时为0,则tmp=1。产生并执行计算事件的同时,产生一个更新事件,即assign y = tmp,但并没有被立即执行,而是被调度到当前仿真事件以后的2ns时刻去执行。而在更新事件被执行之前,若产生新的更新事件,即a从0变化为1,旧的更新事件将被替换,即tmp=0,并在新的更新事件产生的时刻之后的2ns执行新的更新事件。所以assign #2 y = ~a; 等价于:在这里插入图片描述

连续赋值语句中变化小于延时的脉冲被过滤的特点也体现了连续赋值没有记忆功能的特点,不管如何延时,计算事件在何时产生并执行,实际更新事件执行时都是用更新时间执行时刻的输入信号去计算RHS,在赋值给LHS。
assign #(2,3) y = ~a;
描述:非门的上升沿延时(输出变为1)为2ns,下降沿延时(输出变为0)为3ns;关闭延时(输出变成z,高阻态)和输出变为x的延时为2和3中的最小值,即2ns。
assign #(2,3,4) y = ~a;
描述:非门的上升沿延时(输出变为1)为2ns,下降沿延时(输出变为0)为3ns;关闭延时为4ns;输出为x的延时为2,3和4中的最小值,即2ns。
assign #(2:3:4,3:4:5) y = ~a;
描述:表示上升延时的min:typ:max为2:3:4;下降延时的min:typ:max为3:4:5。

RHS中加延时 // 将产生语法错误

由于线网类型没有记忆功能,assign 语句中给RHS加延时,将产生语法错误。如:
在这里插入图片描述

仿真

在这里插入图片描述
在这里插入图片描述

阻塞赋值中的延时

LHS中加延时

#5 y = a ^ b;
描述:当某时刻T时,a或b发生了变化,导致always语句开始执行,然后遇到#5,立刻将该always进程挂起。等到5ns后,再将T+5ns时刻的a^b赋值给y。T~T+5ns的时间之内,a和b上的任何变化都被忽略了。
解释:T时刻事件触发,然后执行#5。由于是阻塞赋值,在5ns时间内该always块一直在执行等待操作,触发事件的接收被阻塞,即错过事件触发,故a和b上的任何变化都被忽略了。而与其并行执行的always块的触发事件则不会被阻塞。等价于:在这里插入图片描述

#(3,5) y = a ^ b; //产生语法错误

RHS中加延时

y = #5 a ^ b;
描述:当某个时刻T时,a或b发生了变化,导致always 语句开始执行,先将T时刻变化后的a和b异或,然后该always进程挂起。等5ns以后,再将T时刻的a^b的结果赋值给y。在T到T+5ns的时间之内,a和b上的任何变化都被忽略了。
解释:等价于:在这里插入图片描述

y = #(3,5) a ^ b; //将产生语法错误

仿真

在这里插入图片描述
在这里插入图片描述

非阻塞赋值中的延时

LHS中加延时

#5 y <= a ^ b;
描述:当某个时刻T,a发生了变化,导致always语句开始执行,然后遇到#5,立刻将该always进程挂起。等到5ns以后,always语句被重新激活,将T+5ns时刻的a^b赋值给y。在T~T+5ns时间之内,a和b上的任何变化都被忽略了。
解释:由于先进行延迟再进行非阻塞赋值,故结果与阻塞赋值时一样。而#5延时先执行,没有产生事件调度,所以触发事件也可以理解为被阻塞了。等价于:
添加图片注释,不超过 140 字(可选)

#(4,5) y <= a ^ b; // 产生语法错误

RHS中加延时

y <= #5 a ^ b;//无损delay最常用的方式
描述:当某个时刻T,a发生了变化,导致always语句开始执行y = a ^ b。首先计算a^b的值,然后将赋值给y的更新事件调度到T+5ns以后执行。在T~T+5ns上a和b的任何变化都不会忽略,总是在变化后的5ns时刻体现在y上。
解释:**更新事件调度会在等待执行队列中等待,由于非阻塞赋值具有记忆性,所以不会被替换。**等价于:
在这里插入图片描述

y <= #(4,5) a ^ b; //产生语法错误

仿真

在这里插入图片描述
在这里插入图片描述

参考

《轻松成为设计高手:Verilog HDL实用精解》
verilog中仿真延迟的添加
————————————————
版权声明:本文为优快云博主「小学鸡」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接

补充说明

这里我需要补充说明一下原文中的一个错误,就是关于无损delay里面注释代码和等效代码实际出来的效果是不一样的,等效代码并不能产生无损delay的效果,以下面代码为例:
在这里插入图片描述

仿真波形如下:
在这里插入图片描述

可以看到信号fff就是aaa的delay 10us之后的无损输出,而上文中跟它所谓等效的写法对应的ddd输出并不是aaa delay 10us之后的输出,这里大家要注意一下,红色框的写法不仅简洁,而且准确。

### SystemVerilog 中断言内部赋值的用法 在SystemVerilog中,可以在断言语句内执行变量赋值操作。这有助于调试和验证设计行为。下面展示如何在断言上下文中使用赋值。 #### 使用 `always` 块中的即时断言语句 (Immediate Assertions) 可以利用立即断言来检查条件并同时更新状态变量: ```systemverilog logic error_flag; integer counter; // 初始化信号 initial begin error_flag = 0; counter = 0; end // 检查输入数据合法性的同时设置错误标志位 always @(posedge clk) begin if (!reset_n) begin error_flag <= 0; // 清除错误标记 counter <= 0; end else begin if (!(input_data inside {[0 : MAX_VAL]})) begin $display("Error: Invalid input data %d", input_data); error_flag <= 1; // 设置错误标记 counter <= counter + 1; // 记录发生次数 end else begin error_flag <= 0; // 正常情况下清除错误标记 end end end ``` 此代码片段展示了在一个时钟边沿触发的过程中,在检测到非法输入时不仅报告错误而且设置了指示器变量[^1]。 #### 序列化属性中的延迟赋值 对于序列化的属性定义,允许通过特殊的语法结构来进行延迟赋值。这种技术特别适用于需要跟踪事件之间的时间关系的情况: ```systemverilog property p_delayed_assignment; logic a, b; int delay_count; @(posedge clk) disable iff (!reset_n) ($rose(a), delay_count=0) |-> ##[1:$] (b && delay_count++); endproperty assert property(p_delayed_assignment); initial begin delay_count = 0; end ``` 这里的关键在于表达式 `$rose(a)` 表明当信号a上升沿到来之后立即将delay_count初始化为零;而后续每当满足`(b && delay_count++)`条件时,则会递增计数值[^2]。 #### 结合过程性赋值与断言 有时可能希望基于某些复杂的逻辑组合或历史信息做出判断。此时可以通过引入临时寄存器保存中间结果实现这一点: ```systemverilog bit prev_state; wire curr_state = some_combinational_logic(); // 追踪当前状态变化情况 always_ff @(posedge clk or negedge reset_n) begin if (!reset_n) prev_state <= 0; else prev_state <= curr_state; end // 利用前一周期的状态来做进一步分析 assert property ( @(posedge clk) disable iff(!reset_n) !(prev_state & ~curr_state)); // 确保不会出现不允许的状态转换 ``` 这段程序说明了怎样借助辅助存储单元记录先前时刻的信息以便于更精细地控制断言的行为。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值