一 UDP定义规则
UDP,全名:User-defined primitives。 用户自己定义的原语。
UDP可分为:combinational UDP(组合逻辑)和 sequential UDP(时序逻辑)。
1.1 组合逻辑UDP
combinational UDP用于构建组合逻辑模型,常见有mux模块.
如下案例为2选1的mux,sel为1时,out为in1;sel为0时,out为in0。
请注意:信号列表中,输出信号out在第一个。状态表中,out处于最后一列。
1.2 时序逻辑UDP
sequential UDP用于构建寄存器DFF和锁存器latch模型。自然而来,sequential UDP可以是边沿触发,也可以是电平触发。
- 时序逻辑 UDP 的输出端必须声明为 reg 型
- 时序逻辑 UDP 可以用 initial 语句初始化
- 状态表格式也稍有不同,时序UDP 格式 : ... : <current_state> : <next_state> ;
- 时序逻辑 UDP 状态表每行由 3 部分组成:输入部分、当前状态和输出状态,用冒号":"隔开
- current_state 就是输出寄存器的当前值, next_state 就是输出寄存器的新值;next_state 由输入和 current_state 共同决定;
- 状态表的输入项可以是电平,也可以是跳边沿的形式
1.2.1 电平触发UDP
电平触发 UDP 的输出是根据输入电平状态的改变而改变。
带有清零端的 D 锁存器的功能描述为:
清零端为 1 时,输出端恒为 0 ;
清零端为 0 、使能控制端为 1 时,锁存器透明,输出端等于输入端;
清零端为 0 、使能控制端为 0 时,锁存器呈保持状态,输出端保持不变。
其真值表为(q 表示当前状态,q+ 表示下一个状态):
其实编写 UDP 的过程,可以理解为换一种格式编写真值表的过程。
带有清零端的 D 锁存器的 UDP 可以描述如下:
primitive d_latch(q, clear, en, d);
output q ;
reg q ;
input d, en, clear ;
initial
q = 0 ;
table
//clear en d :q :q+ ;
1 ? ? :? :0 ; //clear
0 0 ? :? :- ; //"-" means stable
0 1 0 :? :0 ; //q = d
0 1 1 :? :1 ;
endtable
endprimitive
当然,也可以在罗列端口信号时就声明其类型,并且赋初值。
primitive d_latch2(
output reg q = 0,
input clear, en, d);
......
endprimitive
如下案例为一个低电平触发的latch。使用reg申明了一个内部变量,表示当前状态q,即当前udp的输出状态。table中的q+代表的是下一个有效周期的输出状态。
1.2.2 边沿触发UDP
边沿触发 UDP 的输出,是根据输入跳边沿和(或)输入电平状态的改变而改变。
下面举例,带有异步复位端(RST)且在时钟下降沿采集信号的 D 触发器的"真值表":
此"真值表"中还加入了上下沿的概念,是为了方便编写 UDP 代码。
此 D 触发器的时序逻辑 UDP 描述如下:
primitive D_TRI(
output reg Q = 0,
input RST, CP, D);
table
//RST CP D :Q :Q+ ;
//(描述1) 清零
1 ? ? :? :0 ; //RST=1 时清零
(??) ? ? :? :- ; //忽略 RST 边沿变化
//(描述2) 时钟下降沿采集
0 (10) 0 :? :0 ; //时钟下降沿采集信号
0 (10) 1 :? :1 ;
//(描述3)possible negedge
0 (1x) ? :? :- ; //可能是时钟下降沿时保持
0 (x0) ? :? :- ;
//(描述4) 时钟上升沿保持
0 (0?) ? :? :- ; //时钟上升沿时保持
//(描述5)possible posedge
0 (x1) ? :? :- ; //可能是时钟上升沿时保持
//(描述6) 非时钟沿变化时,即便数据有跳变,输出仍然保持
0 ? (??) :? :- ;
endtable
endprimitive // D_TRI
注意:
(1)状态表每行多个输入部分,最多只能有一个跳边沿,例如下面状态表的表述是错误的。
table
......
(10) (10) 1 :? :1 ;
endtable
(2)电平触发的状态表输入项,其优先级高于边沿触发的状态表输入项。若两者在同一时刻出现,则输出端的状态由电平触发的状态表决定。
例如上述 D 触发器中,RST 可以看做是电平触发,CP 可以看做是边沿触发。当 RST 上升沿与 CP 端下降沿同时刻来临时,输出端会变为 0 ,如下图 cap 时刻。当然,实际的时序应该避免时钟和复位边沿同时到来。
(3)边沿触发 UDP 中,必须为每一个输入信号都指定边沿变化时输出信号的变化情况,否则在该信号的跳变沿处可能会造成输出端为 X 。
例如缺少 RST 边沿变化的说明:
//(??) ? ? :? :- ; //忽略 RST 边沿变化
则在 RST 下降沿输出会变为 x。
再例如缺少时钟稳定、D 端数据变化时的说明:
//(4) 非时钟沿变化时,即便数据有跳变,输出仍然保持
//0 ? (??) :? :- ;
则 D 端数据变化的边沿处也会使输出为 x。
如下所示为一个上升沿触发寄存器的模型。其中NOTIFIER信号用于建立时间和保持时间检查,通过建立时间和保持时间检查,则NOTIFIER不变,时序检测不通过则NOTIFIER翻转,udp_dff输出为x态。后仿真中的NOTIFIER是干什么用的!-CSDN博客
二 UDP 状态表符号缩写
三 UDP 设计指导
针对数字设计时是选择使用 module 还是 primitive,要从设计需求、复杂度等方面进行综合考虑。下面给出一些指导性的建议。
- UDP 只能进行功能性建模,不能对电路时序和制造工艺(例如 CMOS,TTL等)进行建模。使用 UDP 的主要目的是以类似于真值表的简洁形式对数字设计进行建模,而 module 可以包含电路时序,并指定制造工艺。
- UDP 只能完成有一个输出端口的数字设计。当输出端口大于一个时,只能用 module。
- UDP 是使用内存中的查找表实现的,当输入端口较多时,输入端口的组合将会呈指数增长。UDP 输入端口的数量也会受到仿真器的限制。因此输入端口较多时不宜使用 UDP。
- 选择使用 UDP 以后,一定要尽可能的用缩写符完整的描述 UDP 状态表。漏掉输入的组合情况,输出端可能会出现 X 的状态,造成设计错误。