Verilog HDL学习笔记(五)| 第5章 门级建模(上)——基本门原语与门延迟

5.1 门的类型

Verilog提供了预定义的门级原语,可以直接在代码中调用,就像调用函数一样方便。

一、与/或门类(and/or)

这类门的特点是:一个输出,多个输入

实例化语法:

复制代码
<门类型> [实例名] (输出, 输入1, 输入2, ...);

示例:

复制代码
// 基本门的实例化
and a1(out, in1, in2);          // 2输入与门
nand na1(out, in1, in2);        // 2输入与非门
or or1(out, in1, in2);          // 2输入或门
nor nor1(out, in1, in2);        // 2输入或非门
xor x1(out, in1, in2);          // 2输入异或门
xnor nx1(out, in1, in2);        // 2输入同或门

// 多输入门(3输入与非门)
nand na1_3inp(out, in1, in2, in3);

// 实例名可以省略(Quartus允许)
and(out, in1, in2);             // 合法的门实例引用

💡 实例名是可选的:与模块实例化不同,门级原语的实例名可以省略。但当需要引用该门时(如延迟标注),建议加上名字。

真值表(以2输入为例):

输入A 输入B and nand or nor xor xnor
0 0 0 1 0 1 0 1
0 1 0 1 1 0 1 0
1 0 0 1 1 0 1 0
1 1 1 0 1 0 0 1

💡 真值表中的 xz 如何处理?如果任一输入为 xz,输出通常为 x(因为值不确定)。具体规则见教材表5.1。

二、缓冲器/非门类(buf/not)

这类门的特点是:多个输出,一个输入 。端口列表中最后一个是输入,前面的都是输出。

门类型 关键字 功能
缓冲器 buf 输出 = 输入(信号增强)
非门 not 输出 = 输入取反

语法要点:端口列表的最后一个信号是输入!

复制代码
// 单输出
buf b1(out1, in);
not n1(out1, in);

// 多输出(所有输出值相同)
buf b1_2out(out1, out2, in);    // 两个输出都是 in 的值
not n1_2out(out1, out2, in);    // 两个输出都是 ~in

// 实例名可省略
not(out1, in);                  // 合法的门实例引用

⚠️ 常见错误buf/not 门可以有多个输出 ,但只有一个输入 。输入必须放在端口列表的最后一位

真值表:

buf not
in → out in → out
0 → 0 0 → 1
1 → 1 1 → 0
x → x x → x
z → x z → x

💡 对于缓冲器和非门,如果输入是 z,输出是 x(因为高阻态是不确定值,经过逻辑门后成为不确定状态)。

三、三态门(带控制端的缓冲器/非门)

这类门在控制信号有效时传递数据,无效时输出高阻态 z。常用于总线驱动场景。

门类型 控制逻辑 功能
bufif1 控制=1时导通 高电平有效的三态缓冲器
bufif0 控制=0时导通 低电平有效的三态缓冲器
notif1 控制=1时导通 高电平有效的三态非门
notif0 控制=0时导通 低电平有效的三态非门
复制代码
// 语法:门类型 (输出, 输入, 控制)
bufif1 b1(out, in, enable);     // enable=1时 out=in,否则 out=z
bufif0 b0(out, in, enable);     // enable=0时 out=in,否则 out=z
notif1 n1(out, in, enable);     // enable=1时 out=~in,否则 out=z

真值表(以 bufif1 为例):

输入 data 控制 control 输出 out
0 0 z
1 0 z
0 1 0
1 1 1
x 1 x

应用场景 :当多个驱动源连接到同一条总线时,使用三态门确保同一时刻只有一个驱动源有效(其他都处于高阻态 z),避免信号冲突。

四、实例数组

当需要对向量的每一位 都实例化同一个门时,使用实例数组可以大幅简化代码。

复制代码
wire [7:0] out, in1, in2;

// 一条语句 = 8个与非门
nand n_gate[7:0](out, in1, in2);

// 等价于以下8条语句:
// nand n_gate0(out[0], in1[0], in2[0]);
// nand n_gate1(out[1], in1[1], in2[1]);
// ...
// nand n_gate7(out[7], in1[7], in2[7]);

💡 实例数组的本质:编译时展开为多个独立的门实例,每个实例的索引与向量位一一对应。这在处理总线信号时非常高效。

5.2 门延迟(Gate Delays)

在真实电路中,信号通过逻辑门需要时间------这就是门延迟。Verilog允许你为每个门指定延迟值。

一、三种延迟类型

延迟类型 含义
上升延迟(rise delay) 输出从 0xz 变化到 1 所需的时间
下降延迟(fall delay) 输出从 1xz 变化到 0 所需的时间
关断延迟(turn-off delay) 输出变化到 z(高阻态)所需的时间

延迟值的指定方式:

复制代码
// 1个延迟值:所有类型都使用这个值
and #5 a1(out, in1, in2);           // 所有延迟 = 5

// 2个延迟值:上升延迟、下降延迟(关断延迟取两者中较小的)
and #(4, 6) a2(out, in1, in2);      // 上升=4, 下降=6, 关断=min(4,6)=4

// 3个延迟值:上升延迟、下降延迟、关断延迟
bufif0 #(3, 4, 5) b1(out, in, control); // 上升=3, 下降=4, 关断=5

⚠️ 关断延迟的默认值 :如果指定了2个延迟值,关断延迟取上升延迟和下降延迟中较小的那个。

二、最小/典型/最大延迟

受制造工艺影响,实际芯片的延迟值会在一定范围内波动。Verilog允许指定最小值、典型值、最大值,仿真时可以选择使用哪一个。

复制代码
// 语法:最小值 : 典型值 : 最大值
and #(4:5:6) a1(out, in1, in2);     // 上升延迟:min=4, typ=5, max=6

// 2个延迟值(上升延迟和下降延迟都指定三种值)
and #(3:4:5, 5:6:7) a2(out, in1, in2);

// 3个延迟值(上升、下降、关断都指定三种值)
and #(2:3:4, 3:4:5, 4:5:6) a3(out, in1, in2);

仿真器命令行控制:

命令选项 使用的延迟值
+maxdelays 使用最大值
+typdelays 使用典型值(默认)
+mindelays 使用最小值
复制代码
# 使用最大延迟值运行仿真
> verilog test.v +maxdelays

三、门延迟示例

考虑一个简单的组合逻辑电路:

复制代码
out = (a · b) + c

    a ──┐
        ├── and #5 ── e ──┐
    b ──┘                  ├── or #4 ── out
    c ─────────────────────┘

Verilog描述:

复制代码
module D(out, a, b, c);
    output out;
    input a, b, c;
    wire e;

    and #5 a1(e, a, b);      // 与门延迟 5 个单位
    or #4 o1(out, e, c);     // 或门延迟 4 个单位
endmodule

💡 从任一输入到输出out的最大路径延迟:5 + 4 = 9 个时间单位。在时序仿真中,输出变化会在这个延迟之后才出现。

仿真波形解读:

复制代码
时间轴:  0    5    10   15   20   25
        ──┬────┬────┬────┬────┬────┬──
输入变化 │    │    │    │    │    │
         │    │    │    │    │    │
输出变化 │         │              │
         │    ←──延迟──→          │
  • 输入变化后,e 经过5个时间单位变化

  • out 再经过4个时间单位变化

  • 总延迟 = 9个时间单位

5.3 综合实例:四选一多路选择器(门级实现)

一、设计需求

用门级原语实现一个四选一多路选择器

  • 输入:4个数据 i0i1i2i3,2位选择信号 s1s0

  • 输出:out

  • 真值表:

s1 s0 out
0 0 i0
0 1 i1 module mux4_to_1(out, i0, i1, i2, i3, s1, s0); output out; input i0, i1, i2, i3; input s1, s0; wire s1n, s0n; // 选择信号的反相 wire y0, y1, y2, y3; // 4个与门的输出 // 生成反相信号 not (s1n, s1); not (s0n, s0); // 4个与门:每个数据通道对应一个 and (y0, i0, s1n, s0n); and (y1, i1, s1n, s0); and (y2, i2, s1, s0n); and (y3, i3, s1, s0); // 或门:合并4个通道 or (out, y0, y1, y2, y3); endmodule
1 0 i2
1 1 i3

二、门级电路设计

使用以下逻辑等式:

复制代码
out = (i0 & ~s1 & ~s0) | (i1 & ~s1 & s0) | (i2 & s1 & ~s0) | (i3 & s1 & s0)

三、Verilog门级描述

复制代码
module mux4_to_1(out, i0, i1, i2, i3, s1, s0);
    output out;
    input i0, i1, i2, i3;
    input s1, s0;

    wire s1n, s0n;          // 选择信号的反相
    wire y0, y1, y2, y3;    // 4个与门的输出

    // 生成反相信号
    not (s1n, s1);
    not (s0n, s0);

    // 4个与门:每个数据通道对应一个
    and (y0, i0, s1n, s0n);
    and (y1, i1, s1n, s0);
    and (y2, i2, s1, s0n);
    and (y3, i3, s1, s0);

    // 或门:合并4个通道
    or (out, y0, y1, y2, y3);
endmodule

逐行分析:

代码 说明
8-9 not (s1n, s1); 生成 s1 的反相信号 s1n
10 not (s0n, s0); 生成 s0 的反相信号 s0n
12-15 and (y0, i0, s1n, s0n); 当 s1=0, s0=0 时,y0=i0
16 or (out, y0, y1, y2, y3); 合并4个通道

四、Testbench

复制代码
module stimulus;
    reg IN0, IN1, IN2, IN3;
    reg s1, s0;
    wire OUT;

    // 实例化
    mux4_to_1 mux(OUT, IN0, IN1, IN2, IN3, s1, s0);

    initial begin
        // 设置输入数据
        IN0 = 1; IN1 = 0; IN2 = 1; IN3 = 0;
        $display("IN0=%b, IN1=%b, IN2=%b, IN3=%b", IN0, IN1, IN2, IN3);

        // 测试各种选择组合
        #1 {s1, s0} = 2'b00; $display("s1=%b s0=%b OUT=%b", s1, s0, OUT);
        #1 {s1, s0} = 2'b01; $display("s1=%b s0=%b OUT=%b", s1, s0, OUT);
        #1 {s1, s0} = 2'b10; $display("s1=%b s0=%b OUT=%b", s1, s0, OUT);
        #1 {s1, s0} = 2'b11; $display("s1=%b s0=%b OUT=%b", s1, s0, OUT);
        #1 $finish;
    end
endmodule

仿真输出:

复制代码
IN0=1, IN1=0, IN2=1, IN3=0
s1=0 s0=0 OUT=1    (选择 i0=1)
s1=0 s0=1 OUT=0    (选择 i1=0)
s1=1 s0=0 OUT=1    (选择 i2=1)
s1=1 s0=1 OUT=0    (选择 i3=0)

5.4 综合实例:四位脉动进位加法器

一、设计层次

复制代码
四位脉动进位加法器 (fulladd4)
    ├── 一位全加器 (fulladd) fa0
    ├── 一位全加器 (fulladd) fa1
    ├── 一位全加器 (fulladd) fa2
    └── 一位全加器 (fulladd) fa3

一位全加器的逻辑方程:

复制代码
sum   = a ⊕ b ⊕ c_in
c_out = (a & b) | (a & c_in) | (b & c_in)

二、一位全加器(门级实现)

复制代码
module fulladd(sum, c_out, a, b, c_in);
    output sum, c_out;
    input a, b, c_in;

    wire s1, c1, c2;

    xor (s1, a, b);
    and (c1, a, b);
    xor (sum, s1, c_in);
    and (c2, s1, c_in);
    or  (c_out, c2, c1);
endmodule

💡 一位全加器可以由 2 个异或门、2 个与门、1 个或门构成,共 5 个门。

三、四位脉动进位加法器

复制代码
module fulladd4(sum, c_out, a, b, c_in);
    output [3:0] sum;
    output c_out;
    input [3:0] a, b;
    input c_in;

    wire c1, c2, c3;    // 内部进位链

    // 脉动进位:前一级的c_out连接到后一级的c_in
    fulladd fa0(sum[0], c1, a[0], b[0], c_in);
    fulladd fa1(sum[1], c2, a[1], b[1], c1);
    fulladd fa2(sum[2], c3, a[2], b[2], c2);
    fulladd fa3(sum[3], c_out, a[3], b[3], c3);
endmodule

模块端口定义

复制代码
module fulladd4(sum, c_out, a, b, c_in);
    output [3:0] sum;    // 4位和输出
    output c_out;        // 最高位进位输出
    input [3:0] a, b;    // 两个4位加数
    input c_in;          // 来自低位的进位输入
  • sum[3:0]:计算结果的低 4 位,对应 a + b + c_in 的结果(不包括溢出位)。

  • c_out:最高位产生的进位,用于判断结果是否溢出(例如 1111 + 0001 会输出 c_out=1,表示结果需要第5位)。

  • ab:两个 4 位输入数,按位相加。

  • c_in:从外部传入的进位,通常用于扩展加法器的位数,例如多个 4 位加法器级联形成 8 位加法器。

内部进位链

复制代码
wire c1, c2, c3;    // 内部进位链

这里定义了 3 根内部导线,用于连接相邻两位全加器之间的进位传递:

  • c1:第 0 位加法器产生的进位 → 送往第 1 位加法器的进位输入。

  • c2:第 1 位加法器产生的进位 → 送往第 2 位加法器的进位输入。

  • c3:第 2 位加法器产生的进位 → 送往第 3 位加法器的进位输入。

  • 第 3 位加法器的进位输出直接连接到模块的输出端口 c_out

这种"前一级进位输出接后一级进位输入"的方式,就是行波进位(或脉动进位)名称的由来。

级联实例化

复制代码
fulladd fa0(sum[0], c1, a[0], b[0], c_in);
fulladd fa1(sum[1], c2, a[1], b[1], c1);
fulladd fa2(sum[2], c3, a[2], b[2], c2);
fulladd fa3(sum[3], c_out, a[3], b[3], c3);

这里连续实例化了 4 个 1 位全加器模块 fulladd,每个模块的端口连接方式如下:

实例 输出 sum 输出进位 输入 a 输入 b 输入进位
fa0 sum0 c1 a0 b0 c_in
fa1 sum1 c2 a1 b1 c1
fa2 sum2 c3 a2 b2 c2
fa3 sum3 c_out a3 b3 c3

每一位的进位输出,都作为下一位的进位输入,这样进位信号像"波浪"一样从低位向高位逐级传递,因此称为行波进位

四、Testbench

复制代码
module stimulus;
    reg [3:0] A, B;
    reg C_IN;
    wire [3:0] SUM;
    wire C_OUT;

    fulladd4 FA1_4(SUM, C_OUT, A, B, C_IN);

    initial begin
        $monitor($time, " A=%b B=%b C_IN=%b C_OUT=%b SUM=%b",
                 A, B, C_IN, C_OUT, SUM);

        A = 4'd0; B = 4'd0; C_IN = 1'b0;
        #5 A = 4'd3; B = 4'd4;
        #5 A = 4'd9; B = 4'd9;
        #5 A = 4'd10; B = 4'd15;
        #5 A = 4'd10; B = 4'd5; C_IN = 1'b1;
        #5 $finish;
    end
endmodule

仿真输出:

复制代码
0 A=0000 B=0000 C_IN=0 C_OUT=0 SUM=0000
5 A=0011 B=0100 C_IN=0 C_OUT=0 SUM=0111   (3+4=7)
10 A=1001 B=1001 C_IN=0 C_OUT=1 SUM=0010  (9+9=18,进位1)
15 A=1010 B=1111 C_IN=0 C_OUT=1 SUM=1001  (10+15=25,进位1)
20 A=1010 B=0101 C_IN=1 C_OUT=1 SUM=0000  (10+5+1=16,进位1)
相关推荐
老当益壮梁奶奶24 分钟前
Linux软件编程学习笔记(十一):深入浅出 TCP 协议与 Socket 编程实践
linux·c语言·笔记·学习·tcp/ip
清钟沁桐32 分钟前
mlir 编译器学习笔记之十四 -- cpu算子自动生成
笔记·学习·mlir
minglie139 分钟前
多项式分裂域的自同构群的求法
学习
每天题库1 小时前
机修钳工题库计算题专练,尺寸公差公式熟练应用
学习·安全·考试·题库·考证
Seraphina361 小时前
记一次实验:利用缓存服务器规范化技术进行网页缓存欺骗
经验分享·笔记·网络安全·缓存
dear_bi_MyOnly1 小时前
数组字符串深度解析:从入门到卡牌实战
开发语言·c++·学习
苏灵凯2 小时前
IT疑难杂症诊疗室:从故障定位到根治的技术实战指南
笔记·ai·域名·agent·deepseek
冰暮流星2 小时前
marketdown之表格
笔记
山岚的运维笔记2 小时前
mysql 专业笔记 -- 第 1 章:MySQL 入门
运维·数据库·笔记·后端·学习·mysql·dba