第10章:逻辑门电路设计

本章对应原书第 305~314 页,属于「第四篇 数字电路篇」。

本章在讲什么?(先看这个)

数字世界里的一切运算,归根结底都建立在几种最简单的电路单元上——与门、或门、非门,它们就像积木中的基础块。这一章教你用 Verilog 语言把这三种门电路"画"到 FPGA 里,并完整走一遍开发流程:画框图 → 列真值表 → 画波形图 → 写代码 → 仿真验证 → 上板点亮 LED。这是你第一次把"代码"变成"电路",也是以后所有章节的基本功。学会了这一章,你就掌握了 FPGA 开发的"套路",后面学的只是更复杂的电路而已。

核心概念拆解

逻辑门电路简介

数字电路里常用的逻辑门电路(Logic Gate)有与门、或门和非门等。为了简化逻辑,实际电路中还扩展了与非门、或非门、异或门和同或门。这些门电路是构成数字电路的基本单元——好比汉字里的笔画,笔画虽少,组合起来却能写成千上万的文章。

在 FPGA 里,这些门电路不需要真的去搭三极管、电阻,而是用 Verilog 语言"描述"出来,由综合工具自动映射成 FPGA 内部的查找表等资源。

与门

与门(AND Gate)是执行"与"运算的基本门电路。它有多个输入端、1 个输出端。规则很好记:只有所有输入同时为高电平 1,输出才为 1;只要有一个是 0,输出就是 0

生活化的理解:两把锁串联在同一扇门上,两把钥匙都有(都是1),门才能开。

与门的表达式是 F = A & B,其中 & 就是 Verilog 里定义的"与"逻辑符。

图10.2.1 与门电路

上图是与门的一种电路实现:用二极管和电阻搭出来。A、B 任意一端接低电平,输出就被拉低;两端都接高电平(+Vcc),Y 才输出高电平。

图10.2.2 与门逻辑符号(国际标准)

与门真值表可以整理成一句口诀:"有0出0,全1出1。" 二输入与门是最常见的数字电路基本单元。

图10.2.3 与门的波形图

波形图是数字工程师的"第二语言":只有当 A 和 B 的波形同时为高时,Y 的波形才是高。以后设计任何电路,都建议先画波形再写代码。

编写代码

module and_gate(
    input       A,  //输入A
    input       B,  //输入B

    output      Y   //输出Y
   );

//assign 相当于一条连线,将A和B相与后的值连接到Y。
assign  Y = A & B;

endmodule

assign 语句相当于一条导线,把右边表达式的结果直接连到左边信号。左边信号必须是 wire 型,右边一变左边立刻跟着变——这是描述组合逻辑最简单的方式。

仿真验证(Testbench)

TestBench 用来验证功能模块是否符合预期,主要分三步: 1) 向被测模块的输入接口添加激励; 2) 对被测模块的顶层接口进行信号例化; 3) 判断输出是否满足设计预期。

`timescale 1ns / 1ns

module tb_and_gate();

//define reg
reg  A;
reg  B;

//define wire
wire     Y;

//初始化
initial begin
    A = 1'b0;
    B = 1'b0;
    #100
    A = 1'b0;
    B = 1'b1;
    #100
    A = 1'b1;
    B = 1'b0;
    #100
    A = 1'b1;
    B = 1'b1;
end

//例化逻辑与模块
and_gate and_gate_inst(
    .A(A), //输入A
    .B(B), //输入B

    .Y(Y)  //输出Y
);

endmodule

几个关键点: - 第 1 行 1ns/1ns 表示仿真时间单位和仿真精度都是 1ns; - testbench 模块名后面不写端口列表,因为它不会被别的模块例化; - 在 initial/always 块中赋值的变量要声明为 reg 型(A、B 由我们手动给激励);输出 Y 由被测模块驱动,声明为 wire 型; - initial 块先让 A=B=0,然后每隔 100ns 依次切换到 01、10、11 四种组合,正好覆盖真值表全部情况。

图10.2.4 与门的仿真波形

仿真波形与真值表完全一致,说明与门电路设计正确。

上板验证

开发板右下侧板载 5 个按键和 4 个 LED。最左边的红色按键是 PL 端复位按键;它右边两个按键连接至 ZYNQ 的 PL 引脚,最右边两个连接至 PS 引脚。最左边两个 LED 连接 PL 端,最右边两个连接 PS 端。

图10.2.5 按键与LED实物图

图10.2.6 底板按键原理图

图10.2.7 底板LED灯原理图

从原理图可以看出:按键默认为高电平,按下后为低电平;LED 灯为高电平点亮。每个按键都串了一个 10K 限流电阻,防止按键按下时电源直接接地造成短路。本实验只用 PL 端外设。

引脚约束(A 用 PL_KEY0,B 用 PL_KEY1,Y 用 PL_LED0):

信号名 方向 管脚 端口说明 电平标准
A input L14 PL_KEY0(底板) LVCMOS33
B input K16 PL_KEY1(底板) LVCMOS33
Y output H15 PL_LED0(底板) LVCMOS33
set_property -dict {PACKAGE_PIN L14 IOSTANDARD LVCMOS33} [get_ports A]
set_property -dict {PACKAGE_PIN K16 IOSTANDARD LVCMOS33} [get_ports B]
set_property -dict {PACKAGE_PIN H15 IOSTANDARD LVCMOS33} [get_ports Y]

图10.2.8 IO Planning引脚约束界面

RTL 原理图:在 Vivado 左侧 Flow Navigator → RTL ANALYSIS → Open Elaborated Design → Schematic 可以打开 RTL 原理图,直观看到代码和电路的对应关系。

图10.2.9 打开RTL原理图

图10.2.10 RTL原理图

图中 RTL_AND 就是一个与门,A、B 是两个输入信号,输出 Y 由 A 与 B 经过与门输出。注意:RTL 分析原理图只反映 RTL 层面,不是代码和 FPGA 内部资源的对应关系图;想看真实资源映射,需要综合后在 Flow Navigator → SYNTHESIS → Open Synthesized Design → Schematic 查看。

下载步骤:生成比特流文件后,点击 Open Hardware Manager,将 Xilinx 下载器一端连电脑、另一端连开发板 JTAG 下载口,再连接电源。

图10.2.12 领航者开发板连接实物图

注意!一定要先连接好下载器与 JTAG 接口,再给开发板上电!带电热插拔 JTAG 接口有一定概率会损坏接口。

连接成功后点击 Auto Connect → Program Device 下载。下载完成后核心板上的 PL 配置完成灯(LED3)点亮。

图10.2.16 下载成功的开发板

实验现象:按键默认高电平,A、B 均为高,与门输出高电平,PL_LED0 点亮。按下 PL_KEY0(A 变低)或 PL_KEY1(B 变低),Y 变低,LED 熄灭。两个键都松开时 LED 又亮——这就是"全1出1,有0出0"的实物演示。

或门

或门(OR Gate)执行"或"运算。含义是:决定一件事的任意一个条件具备,事件就会发生。它也是多输入、单输出:任意一个输入为 1,输出就为 1;全部输入为 0 时,输出才为 0

生活化理解:家里两个开关并联控制一盏灯,按任意一个开关灯都会亮。

表达式 F = A | B| 是 Verilog 的"或"逻辑符。

图10.3.1 或门电路

图10.3.2 或门逻辑符号(国际标准)

或门真值表口诀:"有1出1,全0出0。"

图10.3.3 或门的波形图

编写代码

module or_gate(
   input       A,  //输入A
   input       B,  //输入B

   output      Y   //输出Y
   );

//assign 相当于一条连线,将A和B相或后的值连接到Y。
assign  Y = A | B;

endmodule

仿真验证:或门的 TB 完全可以复用与门的仿真代码,只需把例化的模块改成或门模块即可。

`timescale 1ns / 1ns

module tb_or_gate ();

reg  A;
reg  B;

wire     Y;

initial begin
    A = 1'b0;
    B = 1'b0;
    #100
    A = 1'b0;
    B = 1'b1;
    #100
    A = 1'b1;
    B = 1'b0;
    #100
    A = 1'b1;
    B = 1'b1;
end

//例化逻辑或模块
or_gate or_gate_inst(
    .A(A), //输入A
    .B(B), //输入B

    .Y(Y) //输出Y
    );

endmodule

图10.3.4 或门的仿真波形

仿真结果与或门真值表一致,设计正确。

上板验证:或门实验与与门实验采用完全相同的管脚约束,上板步骤直接参考与门一节即可。区别只在于现象:因为"有1出1",默认按键都为高时 LED 亮;按下任意一个按键,只要另一个还是高电平,LED 依然亮——只有两个键同时按下才熄灭。

非门

非门(NOT Gate)就是取反:事情要发生,取反后就是不发生。它只有 1 个输入端、1 个输出端:输入为 1,输出为 0;输入为 0,输出为 1。

表达式 F = ~A~ 是 Verilog 的"非"逻辑符。

图10.4.1 非门电路

上图用三极管实现非门:A 输入 0V 时三极管截止,Y 输出 5V;A 输入 5V 时三极管导通,Y 输出 0V——正好实现"取反"。

图10.4.2 非门逻辑符号(国际标准)

图10.4.3 非门的波形图

波形图上,Y 的波形就是 A 的上下翻转。

编写代码(这次用 4 位数据演示,一次对 4 个 bit 同时取反):

module not_gate(
   input   [3:0]   A,  //4位数据的输入信号A

   output  [3:0]   Y   //4位数据的输出信号Y
);

//assign 相当于一条连线,将A非后的值赋给Y。
//assign  Y[3:0] = ~A[3:0];
assign  Y = ~A;

endmodule

注意:如果输入端和输出端位数相同,可以直接省略位数的声明(如注释中的写法)。

仿真验证

`timescale 1ns / 1ns

module tb_not_gate();

//define reg
reg [3:0]    A;

//define wire
wire [3:0]   Y;

//初始化
initial begin
    A = 4'b0000;
    #100
    A = 4'b1111;
    #100
    A = 4'b1001;
    #100
    A = 4'b0110;
    #100
    A = 4'b0101;
    #100
    A = 4'b1010;
end

//例化逻辑非模块
not_gate not_gate_inst(
    .A(A),  //输入A

    .Y(Y)   //输出Y
    );

endmodule

图10.4.4 非门的仿真波形

仿真结果与非门真值表一致。

上板验证:本次用了 4 位输入和 4 位输出,而板卡 LED 灯数目不够,所以不再进行上板测试。有兴趣的同学可以把输入输出改成 2 位,自己修改引脚分配做上板拓展练习。

本章总结

本章通过与门、或门、非门三个最基本的逻辑门,完整演示了一个 FPGA 设计项目的全流程。需要掌握:

  • 逻辑门用法:&(与)、|(或)、~(非);
  • 学会绘制和查看波形图;
  • 仿真中常用的 initial 语句块。

initial 的标准形式:

initial begin
A = 0;
B = 0;
C = 0;
end

initial 语句是不可以被综合的,一般只出现在 testbench 中,而不出现在 RTL 代码里。initial 块中的语句上电后只执行一次,一般用于初始化输入信号——如果不赋初始值,仿真信号可能会产生不定态(既不是0也不是1的x态)。begin...end 相当于 C 语言里的大括号,同一个语句块给多个变量赋值时要加上它。

图片对照

本章图片已按讲解位置分散插入各小节,包括与门/或门/非门的电路图、逻辑符号、波形图、仿真波形以及上板验证的硬件图。

初学者容易踩的坑

  • 忘记给 testbench 信号赋初值:initial 不初始化的话,仿真波形会出现红色不定态(x),看不到有意义的结果。
  • reg 和 wire 搞混:在 initial/always 块中赋值的激励信号必须是 reg 型;被测模块输出的信号必须接 wire 型,写反了综合或仿真直接报错。
  • testbench 写了端口列表:testbench 不会被别的模块例化,模块名后不要带端口。
  • 上电顺序搞反:一定要先连接好下载器和 JTAG 接口,再给开发板上电,带电热插拔 JTAG 有损坏接口的风险。
  • 只仿真不上板、或只上板不仿真:仿真通过只能说明逻辑正确,还要考虑板级现象(比如按键是低有效)才能解释上板现象;两者结合才是完整验证。
  • 混淆"有0出0"和"有1出1":与门和或门口诀记反了,上板现象就完全对不上,建议对着真值表再推一遍。

小结 & 下一步

本章用与门、或门、非门三个最简单的电路,学会了"框图 → 真值表 → 波形图 → 代码 → 仿真 → 上板"的完整开发套路,以及 &|~ 三个运算符和 initial 语句的用法。建议动手做两个拓展:一是把与门和非门组合,设计一个与非门电路;二是把或门和非门组合,设计一个或非门——各走一遍波形绘制、代码编写和仿真验证,巩固本章所学。