时序逻辑 UDP 与组合逻辑 UDP 在定义形式和行为功能上均有不同,主要区别如下:
... : <current_state> : <next_state> ; 表示时序逻辑的 UDP 主要分为 2 种:电平触发 UDP 与 边沿触发 UDP。
电平触发 UDP 的输出是根据输入电平状态的改变而改变。
带有清零端的 D 锁存器的功能描述为:
其真值表为(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
边沿触发 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 ;
- //possible negedge
- 0 (1x) ? :? :- ; //可能是时钟下降沿时保持
- 0 (x0) ? :? :- ;
- //(3) 时钟上升沿保持
- 0 (0?) ? :? :- ; //时钟上升沿时保持
- //possible posedge
- 0 (x1) ? :? :- ; //可能是时钟上升沿时保持
- //(4) 非时钟沿变化时,即便数据有跳变,输出仍然保持
- 0 ? (??) :? :- ;
- endtable
- endprimitive // D_TRI
对此触发器进行简单的仿真,testbench 描述如下。
- `timescale 1ns/1ps
- module test ;
- reg D, CP = 0 ;
- reg RST ;
- wire Q ;
- always #5 CP = ~CP ;
- //data driver
- initial begin
- D = 0 ;
- #12 D = 1 ;
- #10 D = 0 ;
- #14 D = 1 ;
- #3 D = 0 ;
- #18 D = 0 ;
- end
- //reset driver
- initial begin
- RST = 0 ;
- #3 RST = 1 ;
- #2 RST = 0 ;
- #22 RST = 1 ;
- #1 RST = 0 ;
- end
- D_TRI u_d_trigger(Q, RST, CP, D);
- initial begin
- forever begin
- #100;
- //$display("---gyc---%d", $time);
- if ($time >= 1000) begin
- $finish ;
- end
- end
- end
- endmodule // test
需要注意的是:
状态表每行多个输入部分,最多只能有一个跳边沿,例如下面状态表的表述是错误的。
- table
- ......
- (10) (10) 1 :? :1 ;
- endtable
电平触发的状态表输入项,其优先级高于边沿触发的状态表输入项。若两者在同一时刻出现,则输出端的状态由电平触发的状态表决定。
例如上述 D 触发器中,RST 可以看做是电平触发,CP 可以看做是边沿触发。当 RST 上升沿与 CP 端下降沿同时刻来临时,输出端会变为 0 ,如下图 cap 时刻。当然,实际的时序应该避免时钟和复位边沿同时到来。

边沿触发 UDP 中,必须为每一个输入信号都指定边沿变化时输出信号的变化情况,否则在该信号的跳变沿处可能会造成输出端为 X 。
例如缺少 RST 边沿变化的说明:
//(??) ? ? :? :- ; //忽略 RST 边沿变化
则在 RST 下降沿输出会变为 x。

再例如缺少时钟稳定、D 端数据变化时的说明:
- //(4) 非时钟沿变化时,即便数据有跳变,输出仍然保持
- //0 ? (??) :? :- ;
则 D 端数据变化的边沿处也会使输出为 x。
UDP 状态表的电平和跳变沿缩写符号及说明如下表所示。
| 缩写符 | 含义 | 说明 |
|---|---|---|
| ? | 0, 1, x | 只能用于输入 |
| b | 1, 1 | 只能用于输入 |
| - | 保持原值不变 | 只能用于输出 |
| (ab) | 信号由 a 变 b | 用作输入端边沿的指示 |
| r | (01) | 信号的上升沿 |
| f | (10) | 信号的下降沿 |
| p | (01), (0x) 或 (x1) | 可能是信号的上升沿 |
| n | (10), (1x) 或 (x0) | 可能是信号的下降沿 |
| * | (??) | 信号任意边沿的变化 |
针对数字设计时是选择使用 module 还是 primitive,要从设计需求、复杂度等方面进行综合考虑。下面给出一些指导性的建议。