第 7 章

代码密度的经济学

把时间拨回到1994年初。在剑桥切斯特顿路那座改建仓库的二层,ARM应用工程团队保存着一份不断更新的技术反馈登记册——一本黑色封面的A4活页夹,按季度归档,按主题分类。1993年第四季度的分册在“成本相关”标签下新增了七份文件,其中一份来自夏普公司半导体事业部的传真件被标记为优先审阅。发件人是夏普的一位资深应用工程师,正文列出了SH-1微控制器评估项目中对ARM6核的测试数据:在相同的功能规格下,ARM32编译后的代码体积比竞争对手的16位架构大约40%。夏普目标产品的物料清单中,存储器成本占比为36%。传真的末尾附有一行手写注释,是ARM现场应用工程师在转发前添加的:“客户表示,除非代码密度问题得到解决,否则下一轮选型将不再考虑ARM内核。”

这份传真的日期是1993年11月18日。它不是第一个发出此类警告的文件。从1992年下半年开始,类似的反馈以不同的形式从不同的渠道涌入ARM的工程部。德州仪器在1993年3月的一份应用报告中指出,在相同的嵌入式控制基准程序上,ARM6的代码尺寸比TI自家16位微控制器大了35%到45%。GEC Plessey半导体在同年6月的客户评估中报告了类似的数据。这些文件共同描绘出一个清晰的模式:ARM的32位精简指令集在性能功耗比上的优势是真实的,但在嵌入式系统的成本结构中,这个优势正被代码体积膨胀所侵蚀。问题的根源深植于ARM架构的设计基因。1985年,苏菲·威尔逊和史蒂夫·弗伯在设计ARM1时,面对的是Acorn公司紧张的财务状况和极度有限的VLSI设计资源。ARM1仅有个25,000晶体管,甚至没有乘法部件。固定长度32位指令集是那个约束下的理性产物:它消除了指令译码的复杂度,省去了微码ROM的硅片面积,让控制逻辑足够简单以至于可以用随机逻辑实现。

在当时的工程条件下,这是唯一能让32位处理器在有限预算内成功流片的路径。但当这个架构被推向嵌入式市场时,固定长度指令集的代价开始显现。在8位或16位宽度的存储器总线上,取一条32位指令需要两次或四次存储器访问。而实际执行的指令中有相当比例的操作——寄存器间数据移动、简单算术运算、短距离分支——完全可以用更少的位数表达。代码体积膨胀不仅消耗存储器带宽,更直接转化为硅片面积和系统成本。在PC市场,几十KB的额外代码无关紧要。在嵌入式市场,每KB都对应着实实在在的制造成本。ARM7TDMI项目正式启动于1993年第二季度,但它的概念根源可以追溯到更早。1992年底,ARM的应用工程团队开始系统性地收集授权客户对ARM6和ARM7核的技术反馈。他们建立了一个四级分类体系:性能、功耗、接口、成本。到1993年春季,成本相关反馈占全部技术反馈的47%,其中代码密度问题占成本反馈的62%。

工程总监迈克·穆勒在一次内部技术评审会上展示了汇总数据,演示文稿的最后一页只写了一行字:“如果我们不解决代码密度,市场会替我们解决——用别人的处理器。”这个判断在ARM内部没有遇到实质性的反驳。辩论的焦点不是要不要行动,而是如何行动。三条技术路径被提出来。第一条是编译器优化路径:通过改进代码生成策略,在不改变硬件的前提下减小代码体积。工程团队对此做了快速评估。他们选取了一套典型的嵌入式控制程序——中断处理、外设寄存器操作、简单状态机逻辑——用当时可用的最佳编译器生成代码,然后手工优化到理论上限。结果令人失望:即使在最优情况下,编译器优化也只能带来约8%到12%的代码密度提升,远不足以弥合与16位架构之间30%到40%的差距。编译器不是答案。第二条是后压缩方案:保持指令集不变,在存储器接口层面增加硬件压缩和解压缩逻辑。这个方案在纸面上具有吸引力——不需要修改指令集架构,不需要重新训练开发工具链。但工程团队很快发现了两个致命障碍。

第一个是实时性问题。嵌入式系统对中断响应时间有严格要求,而压缩和解压缩引入的可变延迟让最坏情况的中断延迟变得难以预测。第二个是随机访问问题。处理器执行分支指令后需要跳转到任意地址取指,压缩后的代码在存储器中是变长编码,硬件必须维护一套复杂的地址映射表才能定位目标指令。这两个问题在桌面和服务器环境中可以通过缓存和预取机制缓解,但在成本敏感的嵌入式系统中,它们意味着额外的硅片面积和设计复杂度。路径二被搁置。剩下的是第三条路径:在32位指令集之外增加一套16位指令子集,形成双宽度指令集架构。这正是ARM7TDMI最终采纳的方案,但它的具体实现远非“增加16位指令”那么简单。真正的挑战在于,如何在不破坏32位RISC架构核心优势的前提下,嵌入一套高效的压缩指令子集。苏菲·威尔逊在1993年夏天接手了Thumb指令集的定义工作。

她的设计笔记——用铅笔写在学校笔记本上的草稿,后来被装订成一本非正式的工程档案——记录了一个远比“截断”复杂的设计过程。威尔逊面对的核心问题是编码效率与表达能力之间的权衡。16位编码空间只有65536种可能的组合。如果简单地将32位指令截断为16位,要么大幅削减可用指令的数量,要么大幅削减每条指令的表达能力——减少可访问的寄存器数量、缩小立即数范围、限制寻址模式。这两种削减都会导致完成同样功能需要更多条指令,从而部分甚至完全抵消每条指令位数减半带来的代码密度收益。威尔逊的突破在于,她没有把Thumb设计成ARM指令集的子集,而是设计成一套与ARM指令集语义等价但编码方式完全独立的指令体系。Thumb指令不是ARM指令的简单截断,而是经过重新编码的压缩表达。她利用了ARM指令集中隐含的冗余信息。在ARM状态下,数据处理指令的源寄存器和目标寄存器是独立编码的,但在绝大多数实际程序中,至少有一个源寄存器与目标寄存器相同。

Thumb利用这一统计特性,采用2地址格式而非3地址格式,将节省出的编码空间用于扩大其他字段。ARM的条件执行机制允许每条指令都携带4位条件码,但在实际代码中,超过80%的指令使用“总是执行”条件。Thumb取消了通用条件执行,只保留分支指令的条件码,再次释放了大量编码空间。这些设计决策的背后,是威尔逊对嵌入式程序行为模式的深入量化分析。她收集了ARM授权客户提供的大量实际应用程序——包括夏普的微控制器固件、德州仪器的DSP控制代码、GEC Plessey的电信设备软件——然后逐条统计指令使用频率、寄存器使用模式、立即数分布和分支距离分布。统计分析的结果直接塑造了Thumb的编码格式。最常用的8个通用寄存器被映射到Thumb指令的3位寄存器字段中,不常用的寄存器通过特殊指令间接访问。立即数字段的宽度根据实际程序中的立即数分布被设定为5位、8位和11位三种格式,分别覆盖了约70%、90%和98%的常数值。

分支偏移量的范围经过计算,确保覆盖嵌入式控制程序中超过95%的分支距离。这份统计分析工作在ARM内部形成了一份超过200页的技术报告,详细记录了指令集设计所依据的每一条经验数据。它代表了一种与伯克利RISC研究传统既延续又断裂的设计方法论。延续在于,两者都强调量化分析作为设计决策的基础。大卫·帕特森在CS 292R课程中教导学生测量真实程序的指令混合比例,威尔逊在做同样的事情。断裂在于,伯克利RISC的量化分析导出了一个追求规整性和正交性的结论——固定长度指令、三地址格式、大量寄存器、延迟分支——而威尔逊的分析导出了一个看似相反的结论:在特定的应用约束下,规整性必须让位于编码密度,正交性必须让位于统计优化。这不是对RISC哲学的背叛,而是对RISC哲学中隐含前提的揭示。伯克利RISC的优化目标是“用最少的周期执行程序”,其隐含前提是存储器带宽充足、代码体积不构成成本约束。

ARM7TDMI的优化目标是“用最小的系统成本完成功能”,其显性约束是存储器价格在系统总成本中占比极高。两个目标都是理性的,但它们指向不同的工程决策。Thumb指令集的硬件实现同样体现了这种实用主义逻辑。在ARM7TDMI的流水线中,Thumb指令在取指阶段被取出,在译码阶段被实时转换为等效的ARM32位指令。这个转换逻辑由一块不到500个逻辑门的组合电路实现。对比ARM7TDMI整体约74000个晶体管的规模,Thumb解压缩逻辑的硬件开销不到1%。用不到1%的晶体管预算换取约30%的代码密度提升,这是一笔在工程经济学上几乎无法拒绝的交易。但交易并非没有代价。Thumb指令集的功能覆盖是不完整的。它无法直接访问ARM架构的所有寄存器,无法执行乘累加操作,无法处理协处理器指令,无法直接操作状态寄存器。任何需要这些功能的程序段必须切换回ARM状态执行。

ARM7TDMI提供了一条BX指令来实现ARM状态和Thumb状态之间的切换,切换本身消耗一个周期。对于绝大多数嵌入式控制程序而言,这种切换的开销可以忽略不计——性能敏感的代码段通常只占总代码量的5%到10%,完全可以用ARM指令编写,而占代码量90%以上的控制逻辑和胶水代码可以用Thumb指令编写,获得最大程度的密度收益。但对于需要密集数字信号处理的程序,ARM和Thumb状态切换的频率可能显著增加,从而侵蚀密度收益带来的性能优势。威尔逊在设计笔记中坦率地承认了这一点:Thumb是一种有针对性的优化,目标明确,边界清晰,超出这个边界,它的效用会下降。1994年4月,ARM7TDMI的RTL设计完成冻结。随后三个月的验证工作暴露了Thumb解压缩逻辑中的几个边界条件错误——主要集中在ARM和Thumb状态切换时流水线中的残留指令处理——但这些错误在正式流片前全部修复。

1994年7月,ARM7TDMI的设计数据被送往VLSI Technology的制造线。首批工程样片于1994年10月返回剑桥。测试结果超出了大多数人的预期。在夏普提供的那套典型微控制器固件上——这套固件此前被用作ARM6代码密度问题的基准测试——ARM7TDMI的Thumb代码体积比ARM32代码减小了34%,略高于设计目标的30%。更关键的是,性能损失远低于担忧。由于Thumb指令的存储器访问次数减少,在16位存储器总线上的实际执行速度反而比ARM32代码快了约10%。在32位存储器总线上,Thumb代码的执行速度比ARM32代码慢了约15%——额外的解压缩周期和指令数量增加是主要原因——但这个性能差距完全在嵌入式应用的容忍范围之内。对于绝大多数目标应用而言,代码密度提升带来的成本节约远远超过了性能的轻微下降。德州仪器是第一个做出正式回应的主要客户。

1994年12月,TI的混合信号微控制器部门提交了一份内部评估报告,对比了ARM7TDMI核与竞争对手的16位架构在汽车发动机控制单元应用中的系统成本。报告结论明确:采用ARM7TDMI核,系统存储器需求从128KB降至96KB,存储器成本降低约1.2美元,处理器核授权费增加约0.3美元,净系统成本降低约0.9美元。对于一个年产量百万级的汽车零部件而言,0.9美元的单件成本差异意味着每年近百万美元的利润增量。TI的评估报告用一句简洁的判断收尾:Thumb改变了成本等式。这句判断精准地概括了ARM7TDMI的历史意义。它不是一个性能升级,而是一个成本重构。在ARM7TDMI之前,32位RISC处理器在嵌入式市场的价值主张主要建立在性能功耗比之上——你可以获得比16位架构更高的计算吞吐量,而消耗的功率大致相当。但这个价值主张在面对成本敏感的嵌入式市场时遇到了一个玻璃天花板:性能优势只有在系统愿意为更大存储器买单时才能兑现。

ARM7TDMI打破了这层天花板。它让32位处理器的系统成本逼近甚至低于16位架构,同时保留了32位架构的性能扩展空间。这是一场从技术逻辑到经济逻辑的转换,它将ARM的竞争优势从“更好的处理器”重新定义为“更便宜的系统”。这种转换的深远影响在随后几年逐渐显现。1995年,ARM的授权客户数量从前一年的18家跃升至37家。1996年,这个数字达到56家。新加入的授权方不再只是那些对32位性能有刚性需求的前沿系统设计者——他们从一开始就是ARM的目标客户——而是大量传统上使用8位和16位微控制器的中低端嵌入式设备制造商。这些制造商的决策逻辑与早期采用者截然不同。他们不关心指令集的优雅性,不关心RISC与CISC的哲学辩论,甚至不太关心处理器的绝对性能。他们关心的是物料清单成本、开发工具链的成熟度、供应商的稳定性和第二货源的可获得性。

夏普的反馈不是孤例。在ARM应用工程团队保存的那本黑色封面的登记册中,1994年第一季度的分册记录了另外三份具有类似指向的文件。GEC Plessey半导体在1994年2月提交了一份长达14页的评估报告,对比了ARM6核与日立H8/300H系列在电信线路卡控制器应用中的系统成本。报告的核心发现直截了当:尽管ARM6在中断响应时间和数据处理吞吐量上均优于H8/300H,但其代码体积比后者大出38%,导致系统所需的EPROM从64KB增加到88KB,SRAM从8KB增加到12KB。在GEC Plessey的目标年产量下,这部分额外的存储器成本约为每片0.7英镑。报告的计算表格显示,只有当ARM6的授权费比H8/300H低0.7英镑以上时,总系统成本才能持平。而当时ARM6的授权费大约在1.5到2英镑之间,日立H8/300H的授权费约为1.2英镑。成本等式对ARM不利。

这份报告在ARM内部引发了一场小型危机。工程总监迈克·穆勒将报告复印了十份,分发给所有高级工程师,附上一张手写便条:“请仔细阅读第7页到第9页的成本分析。这不是性能问题,这是算术问题。”穆勒的用词是经过选择的。“算术问题”意味着它不是可以通过技术辩论来解决的——它不是关于RISC哲学优越性的争论,而是关于硅片面积、存储器价格和制造成本的简单加减。在算术层面,ARM正在输掉这场竞争,无论它的指令集在理论上多么优雅。

ARM7TDMI的设计过程必须在这样的压力下被理解。它不是一次从容不迫的架构演进,而是一次迫在眉睫的生存回应。1993年夏天,当苏菲·威尔逊开始定义Thumb指令集时,她面对的不仅是技术约束,还有一个明确的市场截止日期。夏普的下一轮微控制器选型定于1994年第三季度。德州仪器的汽车发动机控制单元项目计划在1995年初完成设计冻结。GEC Plessey的电信线路卡正在竞标英国电信的一项大规模基础设施升级合同,标书提交截止日期是1994年6月。每一个潜在客户的时间表都在倒逼ARM的工程进度。威尔逊的设计笔记中有一页标注了日期——1993年8月17日——上面列出了一个倒推的时间线:指令集定义必须在9月底前完成,RTL编码在12月底前完成,验证在1994年3月底前完成,流片在6月底前完成,样片在10月底前返回。旁边用红笔写着一行字:“没有延期空间。”

这种时间压力直接影响了Thumb指令集的设计策略。威尔逊没有时间去探索多种编码方案并逐一评估。她必须依赖已有的量化数据,做出快速但有理有据的决策。这就是为什么那份超过200页的统计分析报告如此关键——它不是学术研究的产物,而是工程设计加速器。威尔逊在1993年9月的一份内部备忘录中写道:“我们没有时间去发明新的指令集设计理论。我们只能测量真实程序的行为,然后让数据告诉我们答案。”这种以数据驱动设计决策的方法论,在当时的处理器行业中并不普遍。大多数指令集架构的设计仍然依赖于设计师的直觉和经验判断,辅以有限的基准程序测试。威尔逊的做法更像是经验科学而非传统工程:收集大量样本,统计行为模式,根据统计结果优化编码格式,然后验证优化效果。她的设计笔记中包含了大量手绘的频率分布直方图和累积分布曲线——立即数使用频率、寄存器对出现频率、分支偏移量分布、指令类型混合比例。这些图表不是事后整理的文档,而是设计过程本身的核心工具。

有一个细节可以说明这种方法的严谨程度。在确定Thumb指令中立即数字段的宽度时,威尔逊面临一个典型的编码空间分配问题。16位编码空间有限,每增加一位立即数宽度,就必须从其他地方减少一位——要么减少可编码的指令类型数量,要么减少寄存器字段的宽度。威尔逊的做法是绘制一条“收益递减曲线”。她统计了实际程序中所有立即数的分布,然后计算不同位宽下能够直接编码的立即数比例。5位可以覆盖约70%的情况,6位约80%,7位约87%,8位约90%,9位约93%,10位约95%。曲线的斜率在8位之后明显放缓——增加额外的位数带来的覆盖率提升越来越小。威尔逊据此选择了5位、8位和11位三种立即数格式,分别对应高频率小数值、中等频率中等数值和低频率大数值三种场景。这个决策不是基于任何指令集设计的美学原则,而是纯粹的成本效益分析:每一比特的编码空间都被分配到能产生最大边际收益的地方。

同样的逻辑贯穿了整个Thumb指令集的设计。为什么选择2地址格式而非ARM的3地址格式?因为统计分析显示,超过70%的数据处理指令中,至少有一个源寄存器与目标寄存器相同。在这些情况下,3地址格式中有一个寄存器字段是冗余的——它重复编码了已经隐含在目标寄存器中的信息。消除这个冗余,将节省出的3到4位编码空间重新分配给立即数字段或更大的操作码空间,在统计意义上能够产生更大的代码密度收益。为什么取消通用条件执行?因为在实际代码中,超过80%的指令使用“总是执行”条件,条件码字段在大多数指令中是浪费的。将条件执行限制在分支指令上,释放出的4位编码空间可以用于扩大分支偏移量范围或增加更多的16位指令类型。每一个设计决策都可以被追溯到具体的量化依据,每一个量化依据都来自真实嵌入式程序的统计分析。

这种设计方法论的后果是,Thumb指令集在美学上是“丑陋”的——它缺乏RISC指令集通常具有的规整性和正交性。指令格式不统一,寄存器访问不对称,立即数编码不规则,功能覆盖不完整。如果以伯克利RISC的设计原则作为评判标准,Thumb几乎是一个反面教材。但威尔逊在1993年10月的一份设计评审会议上,对类似的批评做出了明确回应。根据会议记录,她说:“我们不是在为教科书设计指令集。我们是在为客户设计降低存储器成本的方法。如果不对称的编码格式能够节省10%的代码体积,那么不对称就是正确的设计选择。”这段话后来在ARM内部被反复引用,成为公司工程文化的一个标志性表达。

Thumb解压缩逻辑的硬件实现同样遵循了这种实用主义原则。设计团队评估了两种实现方案。方案A是在流水线的取指阶段之后增加一个独立的译码阶段,将Thumb指令转换为中间表示,然后送入原有的ARM译码逻辑。这个方案的优势是模块化——Thumb解压缩与ARM译码完全分离,便于验证和调试。

ARM7TDMI在这些维度上逐一给出了可接受的答案,而Thumb带来的代码密度优势在物料清单成本这个最敏感的维度上给出了不可拒绝的答案。但这枚硬币还有另一面。Thumb的成功也埋下了一个结构性张力。ARM7TDMI的微架构本质上仍然是一个32位设计,其执行核心的吞吐能力——每个时钟周期最多完成一条数据处理指令——在1994年的嵌入式市场中绰绰有余。但随着应用程序复杂度的增长,这个性能上限开始变得可见。Thumb指令集通过减小代码体积降低了系统成本,但它无法改变执行核心的吞吐量上限。当嵌入式设备开始承担更复杂的任务——图形用户界面、通信协议栈、实时音视频处理——ARM7TDMI的性能瓶颈开始暴露。ARM在成本端取得的优势越大,它在性能端积累的压力就越具体。1995年春天,ARM的工程团队开始着手规划下一代ARM8架构。性能提升是核心目标,但团队很快发现,在ARM7TDMI的三级流水线和统一总线架构基础上,性能提升的空间极为有限。

更深的流水线需要更复杂的控制逻辑和更大的硅片面积,这与嵌入式市场的成本敏感性形成直接冲突。超标量设计在当时的技术条件下几乎不可行——它需要的晶体管预算和功耗预算都远超嵌入式市场的承受范围。ARM陷入了自己成功制造的困境:Thumb让ARM核变得足够便宜以至于无处不在,但无处不在意味着应用场景的急剧扩展,而扩展的应用场景需要更强大的性能,更强大的性能需要更复杂的设计,更复杂的设计推高成本,成本上升侵蚀Thumb赢得的价格优势。这不是一个技术问题,而是一个结构性的创新约束。ARM7TDMI解决了代码密度问题,证明了精简指令集可以通过经济性重构赢得市场。但它同时揭示了一个新的困局:当成本不再是障碍,性能本身——纯粹的、未经妥协的性能——将成为下一场战役的主战场。而ARM内部是否拥有打赢这场战役所需的全部能力,在1995年的那个春天,仍然是一个悬而未决的问题。