
乘法是算术的四种基本运算之一,是我们从小就学习的一个过程。但是,计算机——一台由硅和电构成的机器——是如何执行这种抽象计算的呢?这个问题弥合了纯粹数学与物理工程之间的鸿沟。阵列乘法器是其中最优雅和最基础的答案之一,它将我们熟悉的竖式乘法算法直接在架构上转化为一个数字电路。它的设计揭示了复杂的计算是如何由浩瀚而有序的简单逻辑门构成的。
本文将深入探讨阵列乘法器的世界,全面概述其设计和应用。第一章“原理与机制”将把乘法器解构为其核心组件,解释与门和全加器如何排列以生成和累加部分积。我们将探讨其结构规整性与速度、尺寸性能之间的内在权衡。随后,“应用与跨学科联系”一章将展示这个基本电路如何被改造、优化和集成,成为复杂系统中的关键构建模块,从数字信号处理和科学计算,到现代 CPU 和 FPGA 的核心。
要真正理解一台机器,我们必须超越其功能,去欣赏其形式。一个执行像乘法这样抽象运算的设备,在物理世界中是如何存在的?阵列乘法器是对这个问题的一个完美回答,它将一种数学算法直接转化为硅和导线的物理结构。它证明了计算并非魔法,而是巧妙排布的物理学。
让我们回到基础。你最初是如何学习乘以多位数,比如 的?你可能不是直接知道答案,而是遵循了一个算法。你首先将 乘以 的第一位数(即 ),得到 。然后你将 乘以 的第二位数(也是 ),并将这第二个结果 向左移一位后写下。最后,你将这两个中间结果(部分积)相加,得到最终答案。
二进制乘法的工作方式完全相同,但更简单。由于乘数的数字只能是 或 ,每个部分积要么是被乘数的一个移位副本,要么是一串零。因此,挑战不在于生成这些部分积——这部分很简单——而在于将它们全部加起来。阵列乘法器就是这种笔算方法的物理体现。它为算法中的每一个操作都创建了一个专用的空间。
假设我们想将一个 位的被乘数与一个 位的乘数相乘。第一步是生成所有的部分积。这涉及到将 位的被乘数的每一位与 位的乘数的每一位进行“与”运算。这需要一个 的与门网格。例如,将一个 7 位数与一个 5 位数相乘,需要一个由 个与门组成的简单而优雅的网格,以便一次性计算出所有必要的位级乘积。这是最纯粹的蛮力法:一个巨大、并行的简单逻辑运算阵列,构成了整个计算的基础。
真正的巧妙之处在于如何对这些部分积求和。阵列乘法器使用一个由简单加法电路组成的网格。其基本构建模块是全加器,这是一个能将三个单位相加并产生一个两位结果(一个和位和一个进位位)的微小电路。
这些全加器按行排列,每一行负责将一个部分积加到上面各行累加的和中。让我们想象一下这个结构中的单行,如 的情景。一行中的一个 4 位加法器接收两个 4 位数。一个是新的部分积(由我们的与门生成),另一个是前一行的部分和,通过巧妙的移位以正确对齐列。该加法器计算这两个数的和。产生的和位被传递到下一行的同一列,而每个位置的进位输出位则被对角传递到下一行中更高位的列。
这就形成了一个优美的级联效应。计算的波浪从右上角开始,向下和向左传播,穿过整个阵列。每个全加器执行其微小的求和操作,并将其结果传递给相邻的加法器。该结构完全是组合逻辑的,意味着它没有内部存储器或时钟来对其操作进行排序。输出纯粹是当前输入的函数。一旦你施加输入数字 和 ,信号就会像水流过渠道网络一样,简单地通过这片逻辑门海洋传播,直到最终稳定的乘积出现在输出端。这个“算法”不是在时间上执行的,而是在空间上铺展开的。
这种算法的直接空间映射带来了深远的影响。首先是其规模。由于每个操作都有其专用的硬件,组件数量增长迅速。对于一个 的乘法器,你需要 个与门和大约 个加法器。逻辑块的总数与比特数的平方成正比,即 。一个 64 位的乘法器不仅仅是一个 32 位乘法器的两倍大;它大约是四倍大!
第二个影响是速度。关键路径——决定电路整体速度的最长信号延迟——通常是穿过阵列的一条对角线。在右上角产生的进位信号可能需要一直“涟漪传播”到左下角,每行都要穿过一个加法器。这产生了一个与比特数成线性关系的延迟,即 。
这似乎效率不高。确实,存在更巧妙的设计,比如华莱士树乘法器。华莱士树放弃了整齐的逐行求和方式。取而代之的是,它使用一个加法器树,以更并行的方式对所有部分积求和。它将进位向下传递到树的下一层,而不是横向穿过一行,从而在对数时间内将多个数压缩为两个数 [@problem-id:1977472]。这极大地减少了延迟,从 降至 [@problem-id:3652057]。对于一个 64 位乘法器,这意味着其速度可能比简单的阵列设计快四倍以上 [@problem-id:3652057] [@problem-id:1977475]。
那么,如果阵列乘法器更大更慢,为什么它仍然是数字设计的基石呢?答案在于其简洁和规整之美。华莱士树尽管速度快,却是由各种不同长度的导线混乱地连接其加法器。其布局不规则且复杂。相比之下,阵列乘法器是一个完美的、晶体般的网格。每个单元都与其邻居相同,所有连接都是短而局部的。这种规整性对于设计复杂微芯片的工程师来说是天赐之物。它使布局更容易生成,布线可预测,时序分析也更容易。这种可预测性意味着最终制造出的芯片更有可能按预期工作,从而提高良率并减少制造工艺偏差的影响。阵列乘法器是工程权衡中的一个优美范例:有时,最优雅的解决方案不是最快的,而是最有序和最可靠的。
我们的简单模型假设是正数。当然,现实世界充满了负数。数字系统通常使用一种称为二进制补码的格式来表示有符号数。用一个简单的阵列乘法器来乘二进制补码数会引入一个新的复杂问题:符号位扩展。当一个部分积由一个负的被乘数形成时,它的符号位(最高有效位,对于负数是‘1’)必须一直向左扩展,以在最终求和中保持其值。这给加法器阵列增加了额外的逻辑,增加了其复杂性。需要这种特殊处理的行数取决于乘数的位模式。虽然像布斯算法这样的替代方法可以巧妙地减少有符号数的部分积数量,但基本的阵列乘法器必须正面解决符号扩展问题。
最后,让我们退后一步,将乘法器不看作一个抽象的图表,而是一个物理对象。因为它是一个嵌入在同步系统中的组合电路,其计算结果所需的时间是恒定的。系统时钟被设置为足够慢,以适应最坏情况的传播延迟。无论你计算 还是 ,答案都在一个时钟周期内给出。这提供了一个关键的恒定时间保证。
但正是在这里,逻辑与物理分道扬镳。虽然时间是恒定的,但消耗的能量却不是。CMOS 电路的动态功耗与状态转换(从 0 到 1 或从 1 到 0)的晶体管数量成正比。计算 几乎不引起内部切换。而乘以两个大的、复杂的数则会随着信号在阵列中涟漪传播而引起大量的活动。这意味着乘法器的功耗是数据依赖的。这一物理事实具有深远的影响。它创建了一个“侧信道”——一个可观察到的物理属性,泄露了正在处理的秘密数据的信息,这是一个在安全敏感应用中可能被利用的漏洞。
因此,阵列乘法器不仅仅是一个电路。它是一个算法的物理体现,一个关于速度与秩序之间权衡的研究,以及一个展示抽象的逻辑确定性如何让位于可变的物理行为的迷人例子。其简单、网格化的结构是数字计算世界中一个优美而基础的原则。
既然我们已经拆解了阵列乘法器,看到了那些小小的与门和加法器是如何协同工作的,现在让我们看看我们能用这台奇妙的机器做些什么。就像一位技艺精湛的钟表匠了解每一个齿轮一样,一个优秀的工程师不仅能组装设备,还能巧妙地让它展现出令人惊奇的新功能。我们研究过的这个简单、规整的逻辑网格不仅仅是一个学术练习;它是现代计算世界的基石,在从你手机里的处理器到描绘宇宙的超级计算机等一切设备中默默运行。它的故事是一段从简单算术到系统设计前沿的旅程。
通用工具固然好,但通常,最优雅的解决方案来自于为特定任务打磨该工具。考虑计算一个数的平方的任务,即计算 。一个通用的 4 位乘法器使用 个与门来计算所有可能的部分积 。但是当我们计算 时,被乘数和乘数是相同的!这意味着部分积 与 是相同的。我们不需要计算两次。此外,对角线项 就是 本身,根本不需要门电路。仅仅通过注意到这种对称性,我们就可以构建一个专门的“平方器”电路,它比其通用父辈要小得多,效率也高得多,只需要 个与门而不是 个。这是对硬件设计核心的一次美妙洞察:一个数学上的发现直接转化为一个更高效的物理现实。
如果我们的工具只是几乎适合工作呢?假设我们有一个功能完好的无符号乘法器,但我们需要乘以用二进制补码表示的有符号数。我们必须从头开始吗?完全不必。我们可以巧妙地利用现有的无符号乘法器,在其输出端添加一个小型而精巧的“校正电路”。该逻辑考虑了二进制补码表示的数学特性,有效地减去了将负数视为一个大的正数所引入的误差。这种模块化的方法——重用一个核心模块并添加外围逻辑来调整其功能——是工程学中的一个强大原则,节省了巨大的设计工作量。
这种改造的主题延伸到像数字信号处理(DSP)这样的专业领域。在处理音频或视频信号时,乘法会产生一个特殊的问题:溢出。如果计算超出了可表示的最大值,它通常会“回绕”,将一个大的正数变成一个大的负数。在音频中,这会产生可听见且令人不悦的“咔哒”声;在图像中,它可能产生奇怪颜色的像素。解决方案不是让值回绕,而是对其进行“饱和”处理——即,将其钳位在最大值。我们可以在我们的乘法器上增加简单的比较器逻辑来实现这个饱和规则,从而创建一个对 DSP 应用具有鲁棒性的数据通路。核心乘法器保持不变,但我们为它包裹上了一层为信号世界量身定制的“安全网”。
当我们放大视野,会发现乘法器很少是最终目的地;它是一个基本的砖块,用于建造更大、更奇妙的计算殿堂。现代计算中最引人入胜的思想之一是可重构性。如果我们能让我们的硬件按需改变其功能呢?
想象一个 的乘法器。通过在其内部结构中小心地插入微小的数字开关(多路复用器),我们可以控制部分积的流动。用一个单一的控制信号,我们就可以命令乘法器忽略输入上下半部分之间的“交叉项”。当我们这样做时,神奇的事情发生了:单个 乘法器转变成了两个独立的 乘法器,并行工作。这就是 SIMD(单指令多数据)处理的精髓,这项技术是渲染惊人图形的 GPU 和驱动机器学习的 AI 加速器的核心。它揭示了乘法器不是一个僵硬、单一的模块,而是一个可塑的计算结构。
在抽象阶梯上再攀一层,我们遇到了科学计算的世界,它依赖于浮点数——科学记数法的数字等价物。处理器的浮点单元(FPU)必须能乘可以大到天文数字或小到无穷小的数。它是如何做到的呢?在 FPU 乘法引擎的核心,正是我们熟悉的整数乘法器。FPU 首先分离两个数的符号位、指数和尾数(有效数字)。它用一个异或门处理符号位,用一个简单的加法器处理指数。然后,它将两个尾数传递给一个大的整数乘法器来计算它们的乘积。最后,它将新的符号位、新的指数和尾数的乘积结合起来,执行最后的规格化和舍入步骤,以产生最终的浮点结果。这个优美的层次结构展示了复杂操作是如何由更简单、易于理解的组件构建的,其中整数乘法器构成了计算核心。
简单、规整的阵列乘法器很优雅,但对于大数来说,它很慢。关键路径——决定其速度的最长逻辑链——沿对角线穿过加法器阵列。对于一个 位乘法器,这个延迟与 成正比。这就像一个救火桶接力队:一条长长的队伍,每个人都必须等待前面的人。对于像实时视频处理这样的应用,这简直太慢了。
对速度的需求催生了一项深刻的架构创新:华莱士树。华莱士树不是在一个长链中相加部分积,而是使用多层进位保存加法器(CSA)来并行求和,很像一个淘汰赛的赛程图。在每一层,它接收三个数并将其规约为两个,而无需等待进位传播。所需的层数只随 的对数增长,从而大大缩短了关键路径。
即使有了快速的华莱士树,在现代高频处理器中,总的组合逻辑延迟对于单个时钟周期来说可能还是太长了。解决方案是流水线化。我们将乘法过程分解成一个由寄存器分隔的流水线阶段。对于一个华莱士树乘法器,第一级可能是布斯编码和部分积生成,接下来的几级可能是华莱士树规约,最后一级是进位传播加法器。虽然每次乘法从开始到结束仍然需要几个周期才能完成(延迟),但流水线可以每个时钟周期接受一对新的操作数(高吞吐量)。这确保了计算引擎能够跟上为其提供数据的内存系统,防止处理器“挨饿”。与简单的阵列乘法器相比,华莱士树固有的较短组合路径使其更容易高效地进行流水线化,用更少的级数就能达到相同的时钟速度。
让我们将这些思想置于当今最先进技术的背景下。
当我们将快速、流水线化的乘法器集成到 CPU 中时,它必须与处理器的其余部分共存并共享资源。乘法指令的延迟通常比简单的加法指令长。如果乘法结果准备好写回到寄存器的那个周期,正好有另一条指令想通过其正常的流水线阶段将结果写入同一个寄存器堆,这可能导致“交通堵塞”。这是一种结构性冒险。为了防止这种冲突,处理器的控制逻辑(一个“记分板”)必须智能地将其中一条指令停顿一个周期。这种必要的停顿在流水线中引入了一个微小的气泡,稍微降低了处理器的整体性能,即每周期指令数(IPC)。这是一个完美的实践例子,说明了在一个复杂系统中,没有哪个组件是孤立存在的;它的性能与整体交织在一起。
对性能的追求常常导向并行化。为了实现一个 32 抽头的 FIR 滤波器(一个常见的 DSP 任务),我们应该使用一个工作 32 次的乘法器,还是 32 个并行工作的乘法器?并行选项似乎更快。然而,这里有一个隐藏的成本:漏电功耗。在现代晶体管中,即使电路没有主动切换,它也会泄漏少量电流。如果我们有 32 个乘法器都通电,即使是那些等待数据的乘法器,它们全部的漏电总和也可能非常巨大。分析可能会显示,32 个并行乘法器消耗的总能量实际上远大于单个时分复用乘法器,而这一切都是因为漏电。这是现代超大规模集成电路(VLSI)设计中的一个关键教训,在速度、面积和功耗之间进行权衡是其核心挑战。
最后,考虑一下现场可编程门阵列(FPGA)的世界,这是终极的数字游乐场。FPGA 为设计者提供了一片通用的查找表(LUT)海洋,任何数字电路都可以从中构建。但它也为常用功能提供了专门的、硬化的模块。现代 FPGA 包含专用的 DSP Slice,这本质上是高度优化、流水线化的整数乘法器。如果一个设计者需要一个 的乘法器,他应该用数百个 LUT 从头构建一个华莱士树,还是使用一个专用的 DSP Slice?答案几乎总是使用专用模块。它更快、更小,功耗也低得多。通用结构则保留给那些专用模块无法完成的任务,例如构建一个巨大的进位保存加法器树,以对一个大型点积引擎中多个 DSP Slice 的输出求和。这说明了所有计算机体系结构中最深刻的主题之一:通用灵活性与专用效率之间的权衡。
从一个简单的逻辑门网格出发,我们经历了一场关于优化、改造和架构革命的旅程。阵列乘法器,无论是其基本形式还是其高级后代,都不仅仅是一个电路图。它是一个基本的思想,一个证明了简单、规整的结构如何能够被组合、改造和提炼,以构建驱动我们世界的复杂、强大而优美的计算引擎。
1101 (被乘数 = 13)
x 1011 (乘数 = 11)
-------
1101 (部分积 0)
1101 (部分积 1, 左移)
0000 (部分积 2, 左移)
1101 (部分积 3, 左移)
---------
10001111 (最终积 = 143)