从电信号到逻辑世界
物理世界中的电信号如何被转化为可供计算的数学概念。这一转化是连接软件思维与硬件现实的关键的桥梁。
二进制抽象
在硬件层面,并不理解数字“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 =
通用门和衍生门
除了上述三种基础门,还有一些由它们组合而成的衍生门。其中,与非门 (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 和两个数据输入 I0 和 I1,以及一个输出 Y。要求当 S 为0时,输出 Y 的值等于 I0;当 S 为1时,输出 Y 的值等于 I1。
从0开始构建一个MUX
接下来使用基础逻辑门构建一个MUX:
注意:下面使用 !S 代表
- 准备控制信号: 使用一个 NOT 门将选择信号
S反相,得到 !S。 - 使用一个 AND 门,将反相后的选择信号 !S 和数据输入 I0 连接到其输入端。该与门的输出为 !S⋅I0。
- 构建路径二: 使用另一个 AND 门,将原始选择信号
S和数据输入 I1 连接到其输入端。该与门的输出为 S⋅I1。 - 合并路径: 使用一个 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是 Verilog 和 VHDL 。
以下是一个使用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)相加,并将结果存储在一个名为触发器 的记忆单元中。
初始状态: 输入A和B都为
0。电路正确计算出A + B = 0。指令下达: 现在,同时将输入A和B都变为
1。A + B的正确最终结果应该是0(并产生一个进位1)。竞争开始: 假设信号A到达加法器的路径比信号B的路径更短(即传播延迟更小)。
3.1.
A=1的信号变化首先到达加法器。在极短的一瞬间,电路看到的输入是A=1和B=0(B的旧值)。于是,它错误地计算出结果为1。 3.2. 几纳秒之后,B=1的信号变化终于到达。此时,电路看到了正确的输入A=1和B=1,并将计算结果更新为正确的0。上述短暂的、不正确的
1被称为毛刺(Glitch)。在一个不受控制的电路中,记忆单元(触发器)可能会在正确的最终值
0被计算出来之前,就看到并错误地锁存了这个值为1的毛刺。这就是一个典型的竞争冒险:由于不可预测的时序,最终存储的结果是错误的 。上述问题的解决方案:电路中的记忆元件(触发器)被设计为边沿触发 (Edge-triggered)的。这意味着它们会忽略输入端发生的一切变化,除了在时钟“滴答”的那一精确瞬间——例如,上升沿 (rising edge),即时钟信号从低电平跳变到高电平的瞬间。
下面我们看看这种机制如何解决上面加法器的问题:
- 时钟的频率(速度)被有意设置得足够慢,以确保两次时钟“滴答”之间的时间间隔(时钟周期)长于加法器电路的最坏情况传播延迟 。这保证了无论中间出现多少毛刺,加法器的输出都有足够的时间在下一次时钟滴答到来之前,稳定到其最终的、正确的值。
- 忽略竞争过程: 在我们的例子中,输入发生变化,加法器的输出短暂地出现毛刺
1,然后稳定为正确的0。触发器看到了这一切,但它什么也不做,因为它在等待时钟的上升沿。- 拍摄“快照”: 时钟信号终于从低电平跳变到高电平。在那个精确的瞬间,触发器“醒来”,对它的输入拍摄一张“快照”,并把这个值存储起来。到这个时候,输入信号早已稳定在正确的值
0上。那个毛刺早已过去,触发器从未对它采取任何行动。- 保持状态: 触发器现在牢牢地保持着值
0,并且会忽略其输入线上的任何后续变化,直到下一个时钟上升沿到来 。