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 |
💡 真值表中的 x 和 z 如何处理?如果任一输入为 x 或 z,输出通常为 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) | 输出从 0、x、z 变化到 1 所需的时间 |
| 下降延迟(fall delay) | 输出从 1、x、z 变化到 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个数据
i0、i1、i2、i3,2位选择信号s1、s0 -
输出:
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位)。 -
a和b:两个 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)