硬件和软件之间的桥梁--带有逻辑的硬件

101 阅读13分钟

从电信号到逻辑世界

物理世界中的电信号如何被转化为可供计算的数学概念。这一转化是连接软件思维与硬件现实的关键的桥梁。

二进制抽象

在硬件层面,并不理解数字“1”或“0”,也不理解“真”或“假”,它只理解物理现象,也就是高低电位。

经过抽象,将连续变化的物理电压抽象为两个离散的,明确的物理状态:

  • 高电平:一个相对较高的电压(eg: +5v)被赋予逻辑值 “1”。可以代表“True” or “开”。
  • 低电平:一个相对较低的电压(eg: 0v)被赋予逻辑值 “0”。可以代表“False” or “关”。

它允许工程师们暂时搁置复杂的物理学问题(如晶体管内部的电子流动),而进入一个由布尔代数主导的、清晰且可预测的逻辑世界。正是这一抽象,使得我们可以用19世纪发展的数学体系来精确设计和分析21世纪的电子电路。没有这个基础,每一个电路设计都将是一个难以解决的物理学难题。后续讨论的一切,都建立在这个将电信号视为逻辑值的基本“契约”之上。

基础构建模块

逻辑门是一种简单的电子电路,它接收一个或多个二进制输入信号,并根据一个固定的逻辑规则,产生一个单一的输出信号。

最基础的逻辑门有三种:与门 (AND)、或门 (OR) 和非门 (NOT)。  

  • 与门 (AND Gate)

    • 功能: 只有当所有输入都为1时,输出才为1。否则,输出为0 。
    • 电路符号与真值表: Y = A·B
  • 或门 (OR Gate)

    • 功能: 只要任何一个输入为1,输出就为1。只有当所有输入都为0时,输出才为0 。
    • 电路符号与真值表: Y = A+B
  • 非门 (NOT Gate)

    • 功能: 输出是输入的逻辑反相。如果输入是1,输出就是0;如果输入是0,输出就是1 。它也被称为反相器。
    • 电路符号与真值表: Y = image.png

通用门和衍生门

除了上述三种基础门,还有一些由它们组合而成的衍生门。其中,与非门 (NAND) 和或非门 (NOR) 被称为“通用门”,因为仅使用其中任意一种,就可以构建出其他所有类型的逻辑门,这在芯片制造中具有极高的效率优势 。

  • 与非门 (NAND Gate): 相当于一个与门后面接一个非门。当所有输入都为1时,输出为0;否则输出为1 。  

  • 或非门 (NOR Gate): 相当于一个或门后面接一个非门。当所有输入都为0时,输出为1;否则输出为0 。  

  • 异或门 (XOR Gate): 当两个输入不相同时,输出为1;当两个输入相同时,输出为0 。  

  • 同或门 (XNOR Gate): 当两个输入相同时,输出为1;当两个输入不相同时,输出为0。它也被称为“等价门” 。

在硬件中实现条件逻辑

如何将这些简单的“是”与“非”组合起来,形成更复杂的程序逻辑,例如软件中最常见的 if-else 语句?

逻辑门本身擅长执行固定的计算,例如加法或比较。但一个真正的“程序”需要具备决策能力——即根据某个条件,选择执行不同的操作或处理不同的数据。需要构建一个硬件结构,它能够根据一个控制信号的值,从多个数据路径中选择一个。这正是 if-else 语句在硬件中的物理体现。

多路选择器(MUX):硬件决策者

解决上述问题的关键元件是多路选择器 (Multiplexer, MUX) ,它也被称为数据选择器 。MUX是实现条件逻辑的基础硬件单元。

接下来以最简单的 2-to-1 MUX(二选一多路选择器)为例。

具体来说,问题可以被形式化为:如何设计一个电路,它有三个输入——一个控制信号 S 和两个数据输入 I0I1,以及一个输出 Y。要求当 S 为0时,输出 Y 的值等于 I0;当 S 为1时,输出 Y 的值等于 I1

从0开始构建一个MUX

接下来使用基础逻辑门构建一个MUX:

注意:下面使用 !S 代表 image.png

  1. 准备控制信号: 使用一个 NOT 门将选择信号 S 反相,得到 !S。
  2. 使用一个 AND 门,将反相后的选择信号 !S 和数据输入 I0 连接到其输入端。该与门的输出为 !S⋅I0。
  3. 构建路径二: 使用另一个 AND 门,将原始选择信号 S 和数据输入 I1 连接到其输入端。该与门的输出为 S⋅I1。
  4. 合并路径: 使用一个 OR 门,将前两个AND门的输出连接到其输入端。该或门的最终输出 Y 就是 (S⋅I0)+(S⋅I1)。

下面的逻辑流对应上述MUX:

  • if (S == 1) 时:
    • 此时,!S 的值为0。
    • 第一个AND门的输出 (!S⋅I0) 变为 (0⋅I0)=0。
    • 第二个AND门的输出 (S⋅I1) 变为 (1⋅I1)=I1。
    • 最终,OR门计算 Y=0+I1,因此 Y=I1​。
    • 这完全对应 if 分支:then Output = I1
  • else (S == 0) 时:
    • 此时,!S 的值为1。
    • 第一个AND门的输出 (!S⋅I0) 变为 (1⋅I0)=I0。
    • 第二个AND门的输出 (S⋅I1) 变为 (0⋅I1)=0。
    • 最终,OR门计算 Y=I0+0,因此 Y=I0。
    • 这完全对应 else 分支:else Output = I0

通过这个分析,可以看到,一个简单的2-to-1 MUX在物理上完美地实现了 if (S) then Y=I1 else Y=I0 的逻辑。

从抽象设计到物理现实

尽管通过手动连接逻辑门可以构建出条件逻辑,但现代复杂的数字系统(如CPU或GPU)包含数十亿个晶体管,手动设计已无可能。取而代之的是一套高度自动化的设计流程,它将抽象的程序思想转化为具体的物理电路。

如今的数字电路设计师不再手绘逻辑门图,而是使用专门的编程语言——硬件描述语言 (Hardware Description Language, HDL) 来描述电路的功能和结构。最主流的两种HDL是 VerilogVHDL

以下是一个使用Verilog编写的2-to-1 MUX的行为模型代码示例:

// 2-to-1 MUX 的行为级 Verilog 代码
module mux_2_to_1 (
  output reg y,      // 输出端口 y,类型为 reg
  input      i0, i1,  // 输入端口 i0, i1
  input      sel     // 选择控制输入 sel
);
  // always 块描述了电路的行为
  // @(*) 表示只要任何输入 (i0, i1, sel) 发生变化,就重新计算
  always @(*) begin
    if (sel == 1'b1) begin
      y = i1;
    end else begin
      y = i0;
    end
  end
endmodule

程序在何处变成硬件

2-to-1 MUX对应的代码翻译为电路,这个过程称为综合 (Synthesis),综合是一个自动化的过程,它将高级的HDL代码“编译”成一个门级网表——一个详细描述了电路中所有逻辑门及其相互连接关系的清单。

综合:

  • 综合工具首先解析HDL代码,检查语法,并将其转换成一个内部的、与具体技术无关的布尔逻辑表示。
  • 接着,工具会应用各种布尔代数定律和算法来简化逻辑表达式,移除冗余的逻辑,以达到减少门数量、降低功耗和提升运行速度的目的。
  • 最后,工具将优化后的通用逻辑结构,映射到目标物理器件(如某个具体的FPGA芯片)的技术库中。技术库包含了该芯片上实际可用的基本单元(如查找表、触发器等)及性能参数。此步骤将抽象的逻辑门转换成具体的、可实现的物理单元 。

综合生成的门级网表最终需要在一个物理芯片上实现。下面描述现场可编程门阵列 (Field-Programmable Gate Array, FPGA)中的电路实现过程。

它是一种市售的、标准化的芯片,内部含有海量的、未指定具体功能的逻辑单元(通常基于查找表LUT,其本质是小型RAM或MUX)和可编程的布线资源 。综合过程的最终产物是一个被称为“比特流” (bitstream) 的配置文件。当这个文件被加载到FPGA上时,它会配置FPGA内部的逻辑单元和连线,从而实现HDL代码所描述的特定电路 。

目前许多在通用CPU上运行缓慢的算法(软件),可以被重新用HDL实现,并部署到FPGA上,从而获得数量级的性能提升。

程序什么时候被执行?

为了精确地实现用户最初“收到高电位信号就能执行逻辑”的想法,需要引入两个关键的控制信号:使能信号 (Enable Signal)时钟信号 (Clock Signal)

  • 使能信号 (Enable Signal): 使能信号就像一个电路的“总开关”或“门卫” 。当使能信号有效时(高电平或低电平,取决于设计),电路正常工作,根据输入产生输出。当它无效时,电路可以被置于一个已知的静默状态(例如输出强制为0),或者进入高阻态(Hi-Z),相当于在电路上断开连接 。这非常符合用户关于单个信号控制逻辑执行的直观想法,常用于节约功耗或在多个设备间共享数据总线。
  • 时钟信号 (Clock Signal): 对于更复杂的、包含记忆元件(状态)的时序电路 (Sequential Circuits) ,仅有使能信号是不够的。这时需要一个时钟信号来同步整个系统的所有操作。时钟是一个以恒定频率在髙低电平间振荡的信号,如同乐队的节拍器。电路中的所有状态变化(如数据的存储)都只在时钟的特定边沿(例如,从低到高的上升沿)发生 。这确保了在下一次操作开始前,所有数据信号都已经在电路中稳定下来,从而避免了时序混乱和竞争冒险 (race conditions) 的问题。

时钟信号的重要性: 信号的传播不是瞬时的

想象一个大工厂,经理大喊一声命令。工厂里不同位置的工人会因为距离远近,在略微不同的时间点听到这个命令。数字电路也面临类似的问题。当一个输入信号发生变化时(例如,从逻辑“0”变为“1”),这个变化需要一段微小但有限的时间,才能通过导线和逻辑门,最终影响到输出 。这个“传播时间”就被称为传播延迟

至关重要的是,电路中不同的信号路径可能有不同的传播延迟 。一个信号只经过一个逻辑门,会比另一个需要穿过五个逻辑门的信号更快到达。

上述问题导致的后果:时序混乱与“竞争冒险”

这种到达时间的差异可能会导致混乱。当多个本应同时到达并被处理的信号,实际上在略微不同的时间点到达时,就会产生竞争冒险 。此时,电路的行为变得不可预测,其结果取决于哪个信号“赢得了比赛” 。

举例:一个简单的加法器电路

想象一个非常简单的电路,它的功能是将两个比特(A和B)相加,并将结果存储在一个名为触发器 的记忆单元中。

  1. 初始状态: 输入A和B都为0。电路正确计算出 A + B = 0

  2. 指令下达: 现在,同时将输入A和B都变为1A + B 的正确最终结果应该是0(并产生一个进位1)。

  3. 竞争开始: 假设信号A到达加法器的路径比信号B的路径更短(即传播延迟更小)。

    3.1. A=1的信号变化首先到达加法器。在极短的一瞬间,电路看到的输入是A=1B=0(B的旧值)。于是,它错误地计算出结果为1。 3.2. 几纳秒之后,B=1的信号变化终于到达。此时,电路看到了正确的输入A=1B=1,并将计算结果更新为正确的0

上述短暂的、不正确的1被称为毛刺(Glitch)

在一个不受控制的电路中,记忆单元(触发器)可能会在正确的最终值  0被计算出来之前,就看到并错误地锁存了这个值为1的毛刺。这就是一个典型的竞争冒险:由于不可预测的时序,最终存储的结果是错误的 。

上述问题的解决方案:电路中的记忆元件(触发器)被设计为边沿触发 (Edge-triggered)的。这意味着它们会忽略输入端发生的一切变化,除了在时钟“滴答”的那一精确瞬间——例如,上升沿 (rising edge),即时钟信号从低电平跳变到高电平的瞬间。

下面我们看看这种机制如何解决上面加法器的问题:

  1. 时钟的频率(速度)被有意设置得足够慢,以确保两次时钟“滴答”之间的时间间隔(时钟周期)长于加法器电路的最坏情况传播延迟 。这保证了无论中间出现多少毛刺,加法器的输出都有足够的时间在下一次时钟滴答到来之前,稳定到其最终的、正确的值。
  2. 忽略竞争过程: 在我们的例子中,输入发生变化,加法器的输出短暂地出现毛刺1,然后稳定为正确的0。触发器看到了这一切,但它什么也不做,因为它在等待时钟的上升沿。
  3. 拍摄“快照”: 时钟信号终于从低电平跳变到高电平。在那个精确的瞬间,触发器“醒来”,对它的输入拍摄一张“快照”,并把这个值存储起来。到这个时候,输入信号早已稳定在正确的值0上。那个毛刺早已过去,触发器从未对它采取任何行动。
  4. 保持状态: 触发器现在牢牢地保持着值0,并且会忽略其输入线上的任何后续变化,直到下一个时钟上升沿到来 。