加法器
需求
让我们来实现一个 2 位加法器:即输入两个非负整数,输出这两个数的和。要求只要输入变化,输出就随之变化。
根据上面的需求,可以设计如下的输入输出信号:
输入:
- a: 宽度为 2,表示输入的第一个非负整数
- b: 宽度为 2,表示输入的第二个非负整数
输出:
- c: 宽度为 2,表示- a+b,溢出的部分舍弃
波形
根据上面的需求,既然输出 c 信号是随着输入信号变化而变化,我们就可以画出下面的波形:
可以看到,a 和 b 可以随时变化,而 c 也会立即更新为求和以后的结果。
电路
对于这一类 输出仅随着输入变化而变化 的信号,我们通常使用 组合逻辑 来实现。它的特点是输出完全依赖于输入,没有内部状态,和时间无关。
根据真值表,可以得到输出与输入的关系(a_0 表示 a 的最低位):
- \(c_0=a_0 \oplus b_0\) 最低位异或
- \(c_1=(a_1 \oplus b_1) \oplus (a_0 \land b_0)\) 第 1 位异或再加进位
电路图如下:
代码
最后再用 HDL 来实现如上的功能。虽然上面我们推导了加法的逻辑电路,但实际上写 HDL 的时候,我们直接写 a+b 就可以了,EDA 工具会自动完成逻辑的转换。
首先,根据前面确定的输出输出信号编写 module:
module add2 (
  input wire [1:0] a,
  input wire [1:0] b,
  output wire [1:0] c
);
  // TODO
endmodule
通常,当我们声明一个宽度为 n 的信号的时候,采用的是 [n-1:0] 的写法,可以理解为一共有 n 位,下标从高到底是 n-1 到 0。其余部分可以忽略其含义,将它们作为模板记住即可。这里有一个很容易犯的错误是在 output wire [1:0] c 后面多写了一个逗号,通常是在复制粘贴的时候忘记删除。
接着,我们要把电路实现放在 module 内部。对于组合电路,直接构造 a+b 的电路,然后把结果 连接 到输出信号 c 即可。
  assign c = a + b;
请注意,不要把这里的 assign c = a + b 理解为赋值,而是把它看成信号的连接:通过一系列的逻辑门(比如上面提到的异或门 XOR、与门 AND),计算得到 a+b 的结果,再把结果连接到输出信号 c 上。
首先,根据前面确定的输出信号编写 entity:
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity add2 is
    Port ( a : in  STD_LOGIC_VECTOR (1 downto 0);
           b : in  STD_LOGIC_VECTOR (1 downto 0);
           c : out STD_LOGIC_VECTOR (1 downto 0));
end add2;
通常,当我们声明一个宽度为 n 的信号的时候,采用的是 STD_LOGIC_VECTOR (n-1 downto 0) 的写法,可以理解为一共有 n 位,下标从高到底是 n-1 到 0。其余部分可以忽略其含义,将它们作为模板记住即可。
接着,我们要把电路实现放在 architecture 内部。对于组合电路,直接构造 a+b 的电路,然后把结果 连接 到输出信号 c 即可。
architecture behavior of add2 is
begin
  c <= a + b;
end behavior;
请注意,不要把这里的 <= 理解为赋值,而是把它看成信号的连接:通过一系列的逻辑门(比如上面提到的异或门 XOR、与门 AND),计算得到 a+b 的结果,再把结果连接到输出信号 c 上。
总结
回顾上面的电路,可以看到它最大的特点是 输入一变化,输出就跟着变,并且与时间无关,只要给定了输入,那么它的输出就是确定的(类比数学上的函数 output = f(input)),这种电路我们称之为 组合电路(组合逻辑电路)。
为了在硬件上搭建一个组合电路,在 VHDL 中,我们直接对输入信号进行一系列的计算(a+b),把结果通过 output <= a+b; 语句 连接 到了输出信号;在 Verilog 中,我们同样对输入信号进行了计算(a+b),把结果通过 assign output = a+b; 语句 连接 到了输出信号。
组合逻辑很复杂怎么办,比如 if-then-else 的逻辑?
看到这里你可能会有一个疑问,如果这个计算过程很复杂怎么办?按照目前的代码编写方式,我们只能把 if (a) then b else c 改写成 a ? b : c,但是逻辑更加复杂以后,代码可读性会急剧下降,例如 a ? (b ? c : d) : (e ? f : g)。之后我们会介绍如何在代码中实现更复杂的组合逻辑。