Verilog 时序逻辑 UDP

在 Verilog 中,UDP(User-Defined Primitive,用户定义原语) 是一种用于定义自定义逻辑行为的机制,可以用来建模组合逻辑或时序逻辑。时序逻辑 UDP 专门用于描述具有状态保持功能的逻辑,例如触发器、锁存器等。与组合逻辑 UDP 不同,时序逻辑 UDP 包含状态表,能够记住之前的状态(即输出值),并根据输入和当前状态决定下一个状态。

以下是对 Verilog 时序逻辑 UDP 的详细说明,包括语法、结构、示例以及注意事项。


1. 时序逻辑 UDP 的基本结构

时序逻辑 UDP 使用 primitive 关键字定义,包含一个状态表来描述输入、当前状态和下一状态之间的关系。其基本语法如下:

primitive udp_name (output Q, input in1, in2, ..., clk, rst);
    output Q;              // 输出端口,通常是 reg 类型(用于存储状态)
    input in1, in2, ..., clk, rst; // 输入端口
    reg Q;                 // 输出必须声明为 reg,因为需要保持状态

    table
        // 输入 当前状态 : 下一状态
        in1 in2 ... clk rst Q : Q_next;
        // 更多表项...
    endtable
endprimitive
  • 关键字
  • primitiveendprimitive:定义 UDP 的开始和结束。
  • output Q:输出端口,通常为单一标量(不能是向量)。
  • input:输入端口,可以有多个。
  • tableendtable:定义状态表的开始和结束。
  • reg Q:声明输出为寄存器类型,用于存储当前状态。
  • 状态表
  • 每行表示一种输入和当前状态的组合,以及对应的下一状态输出。
  • 格式为:输入信号 当前状态 : 下一状态;
  • 输入和状态可以是 0, 1, x(未知)或 ?(通配符,代表 0、1、x)。
  • 特殊符号:
    • r:表示上升沿(0->1)。
    • f:表示下降沿(1->0)。
    • p:表示正脉冲(0->1->0)。
    • n:表示负脉冲(1->0->1)。
    • *:表示任意变化(0->1, 1->0, 0->x, 等等)。

2. 时序逻辑 UDP 的特点

  • 状态保持:时序逻辑 UDP 的输出 Qreg 类型,能够记住当前状态(不像组合逻辑 UDP 仅依赖输入)。
  • 时钟控制:通常包含时钟信号(如 clk)来触发状态变化。
  • 适用场景:常用于建模简单的时序逻辑单元,例如 D 触发器、T 触发器、锁存器等。
  • 限制
  • UDP 不能包含复杂的行为描述(如循环或条件语句)。
  • 输出必须是单一标量(不能是向量)。
  • 不支持延迟建模(不能直接在 UDP 中指定 # 延迟)。

3. 时序逻辑 UDP 示例

示例 1:D 触发器(D Flip-Flop)

以下是一个简单的 D 触发器 UDP,带同步复位(reset)。

primitive dff_udp (output Q, input D, CLK, RST);
    output Q;
    input D, CLK, RST;
    reg Q;

    table
        // D  CLK  RST  Q  : Q_next
           ?   ?    1   ?  : 0;     // 复位:RST=1 时,Q=0
           0   r    0   ?  : 0;     // CLK 上升沿,D=0,Q=0
           1   r    0   ?  : 1;     // CLK 上升沿,D=1,Q=1
           ?   (?0) 0   ?  : -;     // CLK 无上升沿,保持状态
           ?   ?    0   ?  : -;     // 其他情况,保持状态
    endtable
endprimitive

说明

  • D:数据输入。
  • CLK:时钟输入,r 表示上升沿触发。
  • RST:同步复位,高电平有效。
  • Q:当前状态(存储在 reg Q 中)。
  • Q_next:下一状态。
  • -:表示状态保持(输出不变)。
  • ?:通配符,代表 0, 1, 或 x
  • RST=1 时,输出 Q 被置为 0
  • CLK 有上升沿(r)且 RST=0 时,Q 跟随 D 的值。
  • 其他情况下,Q 保持不变(-)。

测试代码

module test_dff;
    reg D, CLK, RST;
    wire Q;

    dff_udp u_dff (Q, D, CLK, RST);

    initial begin
        $monitor("Time=%0t D=%b CLK=%b RST=%b Q=%b", $time, D, CLK, RST, Q);
        D = 0; CLK = 0; RST = 1;
        #10 RST = 0;
        #10 D = 1; CLK = 1; // CLK 上升沿
        #10 CLK = 0;
        #10 D = 0; CLK = 1; // CLK 上升沿
        #10 CLK = 0;
        #10 RST = 1;        // 复位
        #10 $finish;
    end
endmodule

示例 2:T 触发器(T Flip-Flop)

以下是一个 T 触发器的 UDP 实现,带异步复位。

primitive tff_udp (output Q, input T, CLK, RST);
    output Q;
    input T, CLK, RST;
    reg Q;

    table
        // T  CLK  RST  Q  : Q_next
           ?   ?    1   ?  : 0;     // 异步复位:RST=1 时,Q=0
           0   r    0   ?  : -;     // CLK 上升沿,T=0,Q 保持
           1   r    0   0  : 1;     // CLK 上升沿,T=1,Q=0->1
           1   r    0   1  : 0;     // CLK 上升沿,T=1,Q=1->0
           ?   (?0) 0   ?  : -;     // CLK 无上升沿,保持状态
           ?   ?    0   ?  : -;     // 其他情况,保持状态
    endtable
endprimitive

说明

  • T:触发输入,当 T=1CLK 上升沿时,Q 翻转。
  • RST:异步复位,高电平有效。
  • T=0 时,Q 保持不变;当 T=1 时,QCLK 上升沿翻转。

4. 时序逻辑 UDP 的设计注意事项

  1. 单一输出
  • UDP 只能有一个输出端口,且必须是标量(不能是向量)。
  • 如果需要多位输出,需为每位定义单独的 UDP。
  1. 状态表完整性
  • 状态表必须覆盖所有可能的输入和当前状态组合,否则未定义的行为可能导致仿真错误。
  • 使用 ? 通配符可以简化表,但需确保不遗漏关键情况。
  1. 时钟边沿
  • 使用 r(上升沿)或 f(下降沿)指定时钟触发条件。
  • 非时钟边沿的情况通常用 - 表示状态保持。
  1. 不支持延迟
  • UDP 本身不支持直接指定传播延迟(如 #5)。
  • 如果需要延迟,可以在模块中调用 UDP 时通过 specify 块或连续赋值添加延迟。
  1. 仿真与综合
  • 时序逻辑 UDP 可用于仿真和综合,但综合工具对 UDP 的支持有限(通常只支持简单的触发器或锁存器)。
  • 复杂逻辑建议使用 always 块或模块描述。
  1. 初始化
  • UDP 的初始状态(reg Q)未定义,需通过复位信号明确初始化。
  • 可以在仿真中通过 $deposit 或测试bench设置初始值。

5. specify 块的关系

时序逻辑 UDP 本身不直接支持延迟建模,但可以通过模块中的 specify 块为 UDP 的输入到输出路径添加时序约束。例如:

module dff_with_delay (input D, CLK, RST, output Q);
    wire Q_int;

    dff_udp u_dff (Q_int, D, CLK, RST);

    assign #2 Q = Q_int; // 添加 2ns 传播延迟

    specify
        (D => Q) = 3;     // D 到 Q 的路径延迟 3ns
        (CLK => Q) = 4;   // CLK 到 Q 的路径延迟 4ns
        $setup(D, posedge CLK, 2); // 设置时间 2ns
        $hold(posedge CLK, D, 1);  // 保持时间 1ns
    endspecify
endmodule

6. 常见应用场景

  • 触发器建模:如 D 触发器、T 触发器、JK 触发器等。
  • 锁存器:建模电平敏感的时序逻辑。
  • 简单状态机:描述小型状态转换逻辑。
  • 标准单元库:在 ASIC 设计中,UDP 可用于定义标准单元的时序行为。

7. 常见问题与解答

  • Q:时序逻辑 UDP 和 always 块的区别?
  • A:UDP 使用状态表定义行为,适合简单逻辑,代码紧凑但灵活性有限。always 块支持复杂逻辑和条件语句,适合更复杂的时序逻辑设计。
  • Q:如何在 UDP 中处理多位信号?
  • A:UDP 仅支持单比特输出,多位信号需为每位定义单独的 UDP 实例。
  • Q:UDP 是否支持综合?
  • A:大多数综合工具支持简单的时序逻辑 UDP(如触发器),但复杂 UDP 可能不被支持,需查阅工具文档。

8. 扩展建议

如果你需要更复杂的时序逻辑,建议:

  • 使用 always 块和 reg 变量实现灵活的状态机或触发器。
  • 结合 specify 块为模块添加精确的时序约束。
  • 对于 ASIC 设计,参考标准单元库的时序特性,使用 SDF(Standard Delay Format)文件进行后仿真。

如果你有具体的时序逻辑 UDP 代码或需要针对某个触发器/锁存器的实现,请提供更多细节,我可以帮你优化或提供更详细的示例!

类似文章

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注