Skip to content

RISC-V高性能处理器

2023年,中国科学院计算所的开源处理器"香山"昆明湖版本在SPEC CPU 2006上的估算跑分超过了10分/GHz——这意味着这颗开源RISC-V处理器的IPC已经接近了ARM Cortex-A76的水平。更重要的是,香山的全部RTL代码开源在GitHub上,任何人都可以阅读、学习、甚至修改它的微架构实现。这对处理器设计教育和研究的意义是革命性的。

在过去的几十年中,高性能处理器设计一直被少数几家公司(Intel、AMD、ARM、Apple)垄断,其微架构实现细节被视为最高级别的商业秘密。学术界和教育界只能通过论文中的抽象描述和性能计数器的间接推测来理解处理器内部的工作方式。香山的开源改变了这一局面——它第一次让全世界的研究者和工程师能够直接阅读一个对标ARM A76级别的高性能处理器的每一行Chisel/Verilog代码,理解每一个微架构决策背后的权衡。

回到本书的核心命题:处理器设计的本质是在有限的晶体管预算和功耗约束下,通过投机与并行的层层叠加来逼近指令吞吐率的理论上限。RISC-V高性能处理器的设计空间与ARM和x86的设计空间有着惊人的重叠——TAGE分支预测、分布式发射队列、统一PRF寄存器重命名、Store-to-Load转发——这些在前面各章中详细讨论过的技术(第 15.0 章的TAGE预测器、第 24.0 章的寄存器重命名、第 22.0 章的解码器设计),在香山的开源代码中有着直接的、可审查的实现。RISC-V ISA的独特特征——无条件标志寄存器、32个通用寄存器、RVC变长指令、弱内存模型(RVWMO)——则在某些微架构层面上提供了不同于ARM/x86的设计自由度和约束。

本章将深入分析当前最具代表性的高性能RISC-V处理器设计。我们首先聚焦于中国科学院计算技术研究所的香山处理器——这是目前最为开放的高性能RISC-V处理器项目,其全部RTL代码以开源形式发布,为学术界和工业界提供了一个前所未有的研究平台。接着,我们将分析SiFive的P系列商业核心,以及Ventana Veyron、平头哥C910/C920和BOOM等其他重要设计。通过对比这些处理器在前端、后端和存储子系统上的设计选择,读者可以理解RISC-V高性能处理器设计的共性与差异,以及RISC-V ISA的特征如何影响微架构决策(参见第 19.0 章中对RISC-V指令集编码设计的详细讨论)。

香山处理器

香山(XiangShan)处理器是中国科学院计算技术研究所(ICT)主导开发的开源高性能RISC-V处理器项目。该项目从2020年正式启动,采用Chisel硬件描述语言实现,所有RTL代码在GitHub上以Apache 2.0许可证开源。香山项目的目标不仅是设计一款高性能的RISC-V处理器核心,更是建立一个可持续演进的开源处理器基础设施,使全球的研究者和工程师都能在此基础上进行微架构创新。

香山处理器的开发采用迭代式策略,每一代以北京的一个湖泊命名。第一代"雁栖湖"(Yanqihu)在2021年流片,验证了核心微架构;第二代"昆明湖"(Kunminghu)在2023年流片,大幅提升了性能,目标对标ARM Cortex-A76。后续的代次持续推进性能和功能的演进。

雁栖湖微架构

雁栖湖是香山处理器的第一代微架构实现,其主要目标是验证一个完整的高性能乱序超标量流水线的正确性,同时建立可复用的设计框架。雁栖湖的关键微架构参数如下:

  • 取指宽度:每周期取指32字节(可包含多条指令),支持RVC压缩指令。

  • 解码宽度:6-wide解码,即每周期最多解码6条指令。这是一个相当激进的设计选择——作为对比,ARM Cortex-A76为4-wide解码,Intel Golden Cove为6-wide解码。

  • ROB容量:256项。定义了乱序窗口的大小,决定了处理器能够跨越多长的延迟来发现指令级并行性。

  • 物理寄存器:192个整数物理寄存器,192个浮点物理寄存器。采用统一的物理寄存器文件(PRF)方案进行寄存器重命名。

  • 发射队列:分布式发射队列设计,整数和浮点各有独立的发射队列。

  • 分支预测:采用TAGE-SC-L复合分支预测器,这是当前学术界已知性能最优的分支预测方案之一。

  • 存储子系统:L1 ICache 64KB,L1 DCache 64KB,私有L2 Cache 1MB(inclusive),共享L3 Cache。

性能分析 1 — 雁栖湖的性能定位

雁栖湖在28nm工艺下流片,主频约1.3GHz。在SPEC CPU 2006基准测试中,其单线程性能大约在ARM Cortex-A72至A73之间的水平。虽然绝对性能受限于工艺节点和首次流片的保守设计,但雁栖湖验证了以下关键点:

参数雁栖湖Cortex-A72Cortex-A76
解码宽度634
ROB容量256128128
整数PRF192128160
L1 ICache64KB48KB64KB
L1 DCache64KB32KB64KB
L2 Cache1MB0.5–2MB0.25–0.5MB
分支预测TAGE-SC-LTAGE变体TAGE变体

从参数上看,雁栖湖的微架构规格并不逊于甚至超过了A76。然而,性能不仅取决于微架构参数的"大小",更取决于每个子模块的实现质量——分支预测的准确率、Cache的替换策略、旁路网络的完整性、调度器的公平性等。雁栖湖作为第一代设计,这些方面还有优化空间,昆明湖对此进行了系统性改进。

雁栖湖的一个重要设计哲学是参数化与可配置性。由于使用Chisel语言编写,几乎所有的微架构参数——ROB深度、发射队列深度、物理寄存器数量、Cache容量和相联度——都可以通过配置参数灵活调整。这使得研究者可以方便地探索不同微架构配置对性能、面积和功耗的影响,是商业处理器设计工具无法提供的优势。

设计提示

开源处理器项目面临的一个核心挑战是设计验证。商业处理器公司拥有数十年积累的验证方法学、专有的形式验证工具和庞大的测试向量库。香山项目通过以下策略应对这一挑战:(1)使用DiffTest框架,将香山的执行结果与一个经过充分验证的RISC-V模拟器(NEMU)逐条指令对比;(2)运行完整的Linux内核和SPEC基准测试作为系统级验证;(3)利用开源社区的力量进行bug报告和修复。DiffTest方法论已被证明是一种高效的处理器验证方法,可以在RTL仿真阶段发现绝大多数的功能性bug。

昆明湖微架构

昆明湖是香山处理器的第二代微架构,在雁栖湖的基础上进行了全面的优化和增强。昆明湖的设计目标是在14nm工艺下达到ARM Cortex-A76级别的单线程性能,同时增加RISC-V向量扩展(RVV 1.0)的支持。

香山昆明湖全微架构框图。前端:6-wide取指+TAGE-SC-L预测+FTQ解耦。中端:6-wide解码/重命名/分发。后端:分布式发射队列驱动整数/浮点/访存三组执行单元。存储系统:64KB L1I/D + 1MB non-inclusive L2。
香山昆明湖全微架构框图。前端:6-wide取指+TAGE-SC-L预测+FTQ解耦。中端:6-wide解码/重命名/分发。后端:分布式发射队列驱动整数/浮点/访存三组执行单元。存储系统:64KB L1I/D + 1MB non-inclusive L2。

与雁栖湖相比,昆明湖的主要改进包括:

  1. 更大的分支预测器(BPU):TAGE预测器的表项数量增加了一倍以上,方向预测准确率提升约0.5–1个百分点。在高性能处理器中,分支预测准确率从98%提升到99%可以带来10%以上的IPC提升,因此BPU的改进对整体性能至关重要。

  2. 更大的ROB:从256项扩展,以支持更深的乱序窗口。更大的ROB允许处理器在Cache miss等长延迟事件中继续取指和解码,维持更高的指令级并行度。

  3. 向量扩展支持:增加了RVV 1.0向量处理单元,支持128位VLEN的向量运算。向量单元与标量后端共享发射和提交逻辑,但拥有独立的向量寄存器文件和向量执行通道。

  4. 改进的存储子系统:L2 Cache从inclusive策略改为non-inclusive策略,减少了因inclusion victim导致的L1 Cache行被驱逐的问题。预取器(prefetcher)也进行了升级,增加了对stride和stream模式的检测能力。

  5. 优化的时序路径:通过微架构重构和流水线重新分段,关键路径的延迟减少,使得在相同工艺下能够达到更高的主频。

硬件描述 1 — 昆明湖的向量扩展实现

昆明湖对RISC-V向量扩展(RVV 1.0)的支持是RISC-V高性能核心中较早的完整实现之一。其向量单元的关键设计参数包括:

  • VLEN = 128位:每个向量寄存器为128位宽。RVV规范允许VLEN从128位到65536位的任意2的幂次值,昆明湖选择128位主要出于面积和功耗的考量——更宽的VLEN意味着更大的向量寄存器文件和更宽的数据通路。

  • 向量寄存器文件:32个逻辑向量寄存器,每个128位。物理向量寄存器数量大于32,用于向量指令的寄存器重命名。

  • 执行通道:支持整数向量运算(加减、比较、移位等)和浮点向量运算(单精度/双精度加减乘)。

  • 访存:向量load/store通过标量MemBlock的load/store端口进行,支持unit-stride、strided和indexed三种访存模式。

RVV的一个独特设计特点是其可变长度向量(variable-length vector)编程模型。与ARM SVE类似,RVV允许程序通过vsetvli指令动态设置向量长度,使得同一段向量代码可以在不同VLEN的硬件上运行而无需重新编译。这种设计对硬件实现提出了额外的挑战——控制逻辑需要根据当前的vl(向量长度)和vtype(向量类型)动态调整数据通路的行为。

参数雁栖湖昆明湖
工艺节点28nm14nm
解码宽度66
ROB容量256>>256
整数PRF192>>192
浮点PRF192>>192
发射队列总深度分布式分布式(改进)
L1 ICache64KB64KB
L1 DCache64KB64KB
L2 Cache1MB (inclusive)1MB (non-inclusive)
L2预取基本stridestride + stream
分支预测TAGE-SC-L改进TAGE-SC-L
向量扩展RVV 1.0 (VLEN=128)
目标频率\sim1.3GHz\sim2.0GHz
性能目标A72级A76级

香山处理器两代微架构的关键参数对比

前端设计

香山处理器的前端(Frontend)负责从指令存储器中取出指令并进行解码,为后端的乱序执行引擎提供源源不断的指令流。前端的设计质量直接决定了处理器能否维持高吞吐量——如果前端无法以足够快的速度供应指令,再强大的后端执行资源也将被闲置。

分支预测单元(BPU)

香山处理器采用了一个高度复杂的多级分支预测器架构,其核心是TAGE-SC-L(TAgged GEometric history length with Statistical Corrector and Loop predictor)预测器。TAGE-SC-L是由André Seznec提出的复合预测方案,在多届CBP(Championship Branch Prediction)竞赛中获得最佳成绩。

香山的BPU采用多流水线级的设计:

  1. 第0级——uBTB(micro Branch Target Buffer):这是最快的预测器,1个周期返回预测结果。uBTB是一个小容量但低延迟的分支目标缓冲区,存储最近使用的分支指令的目标地址和方向预测。它充当"快速路径",使BPU在大多数情况下能在1个周期内提供下一次取指的地址。

  2. 第1级——TAGE + FTB:TAGE预测器是主预测器,使用多个具有不同历史长度的表(几何级数增长的历史长度,如8, 16, 32, 64, 128周期)进行标签匹配。FTB(Fetch Target Buffer)存储取指块中分支指令的信息,包括分支类型、偏移和目标地址。TAGE + FTB的预测延迟为2–3个周期。

  3. 第2级——SC(Statistical Corrector):统计校正器对TAGE的预测结果进行"二次校正"。当TAGE的预测置信度较低时,SC利用额外的局部和全局历史信息来修正预测方向。SC能够捕获TAGE遗漏的某些分支模式。

  4. 循环预测器(Loop Predictor):专门针对循环分支的预测器,通过检测循环的迭代次数模式来预测循环退出时机。

香山处理器BPU的多级流水线架构。快速的uBTB提供1周期预测,后续级在延迟较高时提供更精确的预测并覆盖前级的结果。
香山处理器BPU的多级流水线架构。快速的uBTB提供1周期预测,后续级在延迟较高时提供更精确的预测并覆盖前级的结果。
::: info 硬件描述 2 — 香山TAGE-SC-L的具体配置

香山昆明湖的TAGE-SC-L预测器是CBP竞赛冠军方案的直接工程化实现。其具体配置直接对应了第 15.0 章中讨论的TAGE预测器算法:

TAGE主预测器:包含12个标签表(T1–T12),历史长度呈几何级数增长:L1=4,L2=6,L3=9,L4=13,L5=20,L6=30,L7=45,L8=68,L9=101,L10=152,L11=228,L12=341L_1=4, L_2=6, L_3=9, L_4=13, L_5=20, L_6=30, L_7=45, L_8=68, L_9=101, L_{10}=152, L_{11}=228, L_{12}=341周期。每个表约512–4096项,每项包含:3位预测计数器(饱和计数器)、8–14位标签(用于减少别名)、2位有效位(用于替换策略)。

存储预算分配

  • TAGE 12表总存储量:约30–40KB SRAM

  • 基础预测器(bimodal):约4KB

  • SC(统计校正器)6表:约8KB

  • Loop预测器:约2KB(16项,每项存储循环次数和计数器)

  • 总BPU存储量:约44–54KB

SC(Statistical Corrector)的作用:SC是TAGE的"修正器"。当TAGE的预测置信度较低时(预测计数器接近0,即"弱预测"),SC利用额外的局部历史和全局历史信息对TAGE的预测结果进行二次校正。SC在CBP竞赛的评测中将TAGE的MPKI降低了约0.3–0.5,虽然幅度不大但在高性能核心中这0.3–0.5 MPKI的改进可以带来约3%–5%的IPC提升。

Loop预测器的精确模式:Loop预测器检测具有固定迭代次数的循环。当检测到一个分支连续taken NN次后not-taken,再连续taken NN次后not-taken,则记录NN为循环次数。下次遇到该分支时,Loop预测器精确地预测前NN次为taken、第N+1N+1次为not-taken。在循环密集的代码(如矩阵运算、信号处理)中,Loop预测器可以将循环退出分支的误预测率从约1%降至接近0%。

这些参数配置与第 15.0 章中讨论的理论直接对应:TAGE的几何历史长度分布确保了对不同"历史依赖深度"的分支都有合适的预测器;SC的二次校正利用了TAGE遗漏的弱相关性;Loop预测器通过精确计数消除了TAGE在循环退出点的系统性偏差。

:::

这种多级BPU设计的核心思想是延迟与精度的权衡。uBTB虽然准确率较低(约90%),但延迟仅1周期,可以立即为IFU提供下一个取指地址,避免前端出现气泡。当TAGE和SC的更精确预测在2–4个周期后到达时,如果与uBTB的预测不一致,BPU会发出重定向请求,覆盖之前的预测结果。这种"先快后准"的策略在大多数情况下实现了接近1周期的有效预测延迟,同时保持了TAGE级别的预测精度。

取指目标队列(FTQ)

FTQ(Fetch Target Queue)是香山前端设计中一个极其重要的创新结构。FTQ位于BPU和IFU(Instruction Fetch Unit)之间,起到解耦缓冲(decoupling buffer)的作用。

硬件描述 3 — FTQ的设计动机与结构

在传统的处理器前端中,BPU和IFU是紧耦合的——BPU每周期产生一个预测,IFU立即使用该预测进行取指。这种设计的问题在于:

  1. 带宽不匹配:BPU可以在每周期产生一个预测结果(一个取指块的地址),但IFU的取指可能因为ICache miss而停顿。在BPU持续预测而IFU停顿时,预测结果将丢失。

  2. 重定向开销:当后端发现分支预测错误时,需要同时冲刷BPU和IFU的流水线,如果两者紧耦合,恢复逻辑将更加复杂。

  3. 预取机会:BPU可以"跑在前面",预测未来的取指地址,这些地址可以用于ICache预取,减少未来的ICache miss。

FTQ通过在BPU和IFU之间插入一个FIFO队列来解决上述问题。每个FTQ表项存储一个取指块(Fetch Block)的信息,包括:

  • 起始PC地址

  • 块内有效指令的范围(从起始到第一条被预测taken的分支或块尾)

  • BPU的预测信息(分支方向、目标地址、预测器状态用于训练)

  • 取指块的长度

在正常运行中,BPU以最大带宽连续产生预测结果并写入FTQ尾部,IFU以自己的节奏从FTQ头部读取取指请求。当IFU因ICache miss停顿时,BPU可以继续预测并将结果缓存在FTQ中;当IFU恢复时,可以立即从FTQ中获取预测结果而无需等待BPU重新计算。

FTQ的深度(在香山中通常为32–64项)决定了BPU能够"跑在前面"的距离。更深的FTQ意味着BPU可以在ICache miss期间继续预测更多的未来取指地址,为ICache预取提供更多的提示。但FTQ过深也有代价——在分支预测错误时,FTQ中大量的预测结果需要被冲刷,且更深的FTQ需要更多的存储面积。

硬件描述 4 — 昆明湖FTQ的详细设计

昆明湖对FTQ进行了重要的增强,使其成为前端性能的关键基础设施。

FTQ的表项结构。每个FTQ表项在昆明湖中包含以下字段:

  • 起始PC(start_pc):本取指块的起始虚拟地址。

  • 有效结束偏移(end_offset):取指块中最后一条有效指令的偏移位置。对于顺序代码,end_offset指向块尾(32字节窗口的末尾);对于含有taken分支的代码,end_offset指向taken分支所在的位置。

  • 分支类型(br_type):取指块结束的原因——是块尾自然结束,还是因为条件分支taken,还是因为无条件跳转/函数调用/函数返回。

  • 预测目标地址(target_pc):如果取指块因taken分支结束,记录分支的预测目标地址。IFU使用此地址发起下一次取指。

  • BPU预测器状态(bp_meta):BPU各级预测器的元数据,用于在分支结果确定后进行训练更新。这些元数据包括TAGE预测器的匹配表号、预测计数器值、SC预测器的校正值等,总大小约64–128位。

  • 预测置信度(confidence):BPU对本次预测的置信度评估。低置信度的预测可能触发IFU的"预取两个方向"策略——同时预取预测目标和顺序路径的指令,以减少误预测时的ICache miss延迟。

  • ICache预取提示(prefetch_hint):一个标志位,指示IFU是否应该为该取指块的目标地址发起ICache预取请求。当BPU"跑在前面"产生的预测地址尚未被IFU消费时,ICache预取器可以使用这些地址提前预热ICache。

FTQ的读写端口。FTQ作为BPU和IFU之间的解耦缓冲,需要支持以下并发操作:

  1. BPU写入(1个写端口):BPU每周期产生一个预测结果,写入FTQ尾部。在多级BPU的覆盖(override)操作中,后级预测器可能修改FTQ中已有的表项——这需要一个额外的修改端口或通过读-修改-写操作实现。

  2. IFU读取(1个读端口):IFU每周期从FTQ头部读取一个取指请求。

  3. BPU训练读取(1个读端口):当后端确认了分支的实际结果后,需要从FTQ中读取对应表项的bp_meta信息,用于训练BPU。这一读取与IFU的读取是异步的——训练读取可能访问FTQ中任意位置的表项(不一定是头部),因此需要一个随机访问端口。

  4. 冲刷操作(控制端口):当后端检测到分支误预测时,FTQ中从误预测分支之后的所有表项需要被冲刷(标记为无效)。冲刷操作通过移动FTQ的尾指针到误预测点来实现,时间复杂度为O(1)O(1)

FTQ深度的选择。昆明湖的FTQ深度为64项(雁栖湖为32项)。64项FTQ意味着BPU可以"跑在前面"最多64个取指块——假设每个取指块平均覆盖6条指令,64项FTQ可以覆盖约384条指令的预测。在ICache miss延迟约为20周期(L2 hit)的情况下,BPU在IFU停顿期间可以产生20个预测(每周期1个),远小于64项FTQ的容量。这意味着在正常运行中,FTQ几乎不会满——BPU产生的预测总是可以立即写入FTQ。

FTQ的主要价值在极端场景中体现:当IFU因ICache miss而长时间停顿(如L2 miss需要100+周期),BPU可以持续预测并将结果缓存在FTQ中。这些预测结果中的取指地址可以被用于ICache预取——即使IFU暂时无法消费这些预测,预取操作可以提前将目标指令行加载到ICache中,使得IFU在恢复后可以命中ICache而非再次miss。

指令取指单元(IFU)与解码

IFU从FTQ获取取指请求后,访问ICache(或ICache miss时访问L2 Cache),取回指令数据。由于RISC-V支持RVC压缩指令扩展(16位指令),IFU还需要处理变长指令的对齐和边界检测。

香山的IFU每周期从ICache读取一个取指块(Fetch Block),通常为32字节。在支持RVC的情况下,一个32字节的取指块可能包含16条16位指令或8条32位指令,或它们的混合。IFU中的预解码(Pre-decode)逻辑快速扫描取指块中的每条指令,判断指令长度(16位或32位)和指令类型(是否为分支指令),为后续的解码和BPU训练提供信息。

硬件描述 5 — RVC压缩指令对前端的挑战:与ARM A64的关键差异

RISC-V的RVC压缩指令扩展是RISC-V与ARM A64在前端设计上的最大差异。RVC引入了16位的压缩指令,与标准的32位指令混合出现在代码流中。这对前端设计带来了以下挑战:

1. 指令边界检测。在ARM A64中,取指块中的第nn条指令始终从偏移4n4n字节处开始——不需要任何指令边界检测逻辑。在RISC-V中,由于指令可能是16位或32位,取指块中的指令起始位置不再是固定偏移。前端需要一个预解码扫描器来确定每条指令的起始位置和长度。

预解码的基本逻辑是:检查每条可能指令的最低2位。如果最低2位不是11,则为16位压缩指令;如果最低2位是11,则为32位标准指令。这一检查可以并行完成(每个2字节对齐的位置独立检查),但需要后续的串行扫描来确定哪些位置是有效的指令起始。

2. 跨取指块的32位指令。当一条32位指令的前2字节位于一个取指块的末尾,后2字节位于下一个取指块的开头时,该指令跨越了取指块边界。处理器需要将两个取指块的数据拼接起来才能解码这条指令。这在ARM A64中不会发生(32位指令总是4字节对齐,不可能跨越64字节的取指块边界)。

香山的IFU通过保留一个指令残余缓冲器(instruction residual buffer)来处理跨块指令——将上一个取指块的尾部2字节与当前取指块的头部2字节拼接,形成一条完整的32位指令。

3. RVC展开。压缩指令在解码前被展开(expand)为对应的32位标准指令。例如,c.add rd, rs2被展开为add rd, rd, rs2。展开逻辑是一个纯组合电路,延迟约为1–2个FO4门。在6-wide解码器中,需要6个并行的展开器,但面积很小。

4. 对解码宽度的影响量化。RVC的存在使得每个32字节取指块中可能包含16–32条指令(纯RVC时32条,纯RV32时8条)。在典型的RISC-V代码中,RVC指令的比例约为30%–50%。以40%的RVC比例计算:

每条指令的平均字节数:0.4×2+0.6×4=3.20.4 \times 2 + 0.6 \times 4 = 3.2字节

32字节取指块中的平均指令数:32/3.2=1032 / 3.2 = 10

这意味着6-wide解码器在大多数周期中都能被充分利用(取指块中有足够的指令供解码),RVC实际上提升了解码器的有效利用率——因为更高的代码密度意味着每个取指块包含更多指令。

与ARM A64的量化对比。ARM A64固定32位编码,每32字节取指块包含8条指令。在6-wide解码下,一个取指块可以供8/61.38/6 \approx 1.3个周期的解码。在RISC-V(40% RVC)下,一个取指块可以供10/61.710/6 \approx 1.7个周期的解码。RISC-V的代码密度优势使得I-Cache的有效容量增加了约30%——64KB RISC-V I-Cache的代码覆盖范围约等于84KB的ARM A64 I-Cache。

解码级(Decode)将预解码后的指令进一步翻译为处理器内部的微操作(μ\muOp)。RISC-V指令集的一个优势是其编码格式高度规整——所有32位指令的opcode位于固定位置,源寄存器和目标寄存器的编号也在固定的位域中。这使得RISC-V的解码逻辑比x86简单得多,不需要复杂的变长指令解码器或微码ROM。香山的6-wide解码器可以每周期解码6条RISC-V指令,每条指令生成1个μ\muOp(少数复杂指令可能生成2个)。

设计提示

RISC-V指令集的规整编码格式为高性能处理器的解码级设计提供了显著优势。在x86处理器中,解码级是前端最复杂的部分——变长指令(1–15字节)的边界检测、前缀解析、微码展开等逻辑占用了大量面积和功耗,且成为前端的时序瓶颈。Intel从Sandy Bridge开始引入μ\muOp Cache来缓解解码瓶颈。在RISC-V中,由于指令格式高度规整(32位或16位,两种固定长度),解码器的复杂度大幅降低,6-wide解码器的面积和延迟都远小于x86的等效设计。这是RISC风格ISA在微架构层面的固有优势。

后端设计

香山处理器的后端(Backend)是乱序超标量执行引擎的核心,包括寄存器重命名、分发(Dispatch)、发射(Issue)、执行(Execute)和提交(Commit)等阶段。

寄存器重命名与分发

经过解码后的μ\muOp进入重命名级。重命名级维护一个寄存器别名表(RAT),将每条指令的源和目标逻辑寄存器映射到物理寄存器。香山采用统一PRF(Unified Physical Register File)方案,整数和浮点各有独立的PRF和RAT。

重命名级每周期处理6条μ\muOp(与解码宽度匹配)。对于每条μ\muOp,重命名级需要:

  1. 从空闲列表(Free List)中分配一个新的物理寄存器作为目标。

  2. 读取RAT,将源逻辑寄存器翻译为当前映射的物理寄存器。

  3. 更新RAT,将目标逻辑寄存器的映射更新为新分配的物理寄存器。

  4. 将指令信息写入ROB尾部。

硬件描述 6 — 香山昆明湖的寄存器重命名详细设计

昆明湖的寄存器重命名采用基于SRAM的RAT(Register Alias Table),与第 24.0 章第 25.0 章中讨论的统一PRF方案直接对应。

RAT的物理实现。RISC-V RV64GC定义了32个整数寄存器(x0–x31,其中x0硬连线为0)和32个浮点寄存器(f0–f31)。RAT为每个逻辑寄存器维护一个到物理寄存器的映射。

  • 整数RAT:31项有效条目(x0不参与重命名),每项存储一个物理寄存器编号。192个整数物理寄存器需要log2(192)=8\lceil\log_2(192)\rceil = 8位编码。因此整数RAT的总存储量为31×8=24831 \times 8 = 248位。

  • 浮点RAT:32项条目,每项8位(192个浮点物理寄存器)。总存储量32×8=25632 \times 8 = 256位。

6-wide RAT的端口需求。每周期6条μ\muops,每条最多2个源操作数和1个目标操作数:

  • 读端口(查找源寄存器映射):6×2=126 \times 2 = 12个读端口

  • 写端口(更新目标寄存器映射):6×1=66 \times 1 = 6个写端口

  • 总端口数:18个

直接实现18端口的SRAM面积与(18)2(18)^2成正比——约为单端口SRAM的324倍。为降低面积,香山可能采用以下优化策略:

  1. RAT复制(RAT Replication):将RAT复制为多份,每份提供部分读端口。例如复制为3份,每份提供4个读端口和2个写端口(所有副本同步写入以保持一致性)。3份×\times6端口的面积约为1份18端口面积的3×36/324=33%3 \times 36 / 324 = 33\%

  2. 分离的读RAT和写RAT:使用一个写RAT(6写端口,用于更新映射)和多个读RAT(各4个读端口,用于查询映射)。写RAT的更新被广播到所有读RAT。

  3. 检查点存储:为每个分支指令保存RAT快照,用于误预测恢复。昆明湖估计支持约16个并发分支检查点。每个检查点存储一份完整的RAT(约504位),16个检查点总共16×504=806416 \times 504 = 8064\approx 1KB SRAM。

RISC-V x0寄存器的特殊处理。RISC-V的x0寄存器硬连线为0——读取x0始终返回0,写入x0的操作被丢弃。在重命名阶段:

  • 如果源操作数为x0,重命名逻辑不查询RAT,而是直接标记该操作数为"立即数0"或指向一个专用的"零物理寄存器"。

  • 如果目标寄存器为x0,重命名逻辑不分配物理寄存器,该μ\muop被标记为"无目标"或转换为NOP(如果没有其他副作用)。这节省了一个物理寄存器分配。

  • 常见模式如mv x0, rs1(丢弃rs1的值)可以在解码阶段就被识别并消除(零化消除,zero-idiom elimination)。

这种对x0的特殊处理在每个周期中可以节省约5%–8%的物理寄存器分配(RISC-V编译器经常使用x0作为目标来实现"无目标"操作),使得192个物理寄存器在有效利用率上接近ARM的更大PRF。

6-wide重命名的一个关键挑战是同周期内的依赖处理。如果同一周期的6条指令中,后面的指令依赖前面指令的目标寄存器,则后面指令的源寄存器应该映射到前面指令刚刚分配的物理寄存器,而不是RAT中的旧映射。这需要在重命名级内部实现一个前递(forwarding)网络,检查同周期内的WAW/RAW依赖。

重命名后的指令被分发到相应的发射队列。香山采用分布式发射队列设计:

  • 整数发射队列(IntIQ):分为多个子队列,分别服务不同的整数执行端口。

  • 浮点发射队列(FpIQ):服务浮点执行端口。

  • 存储发射队列(MemIQ):服务load和store端口。

分布式发射队列的优势在于每个子队列的规模较小(通常12–16项),选择逻辑(select logic)的延迟和面积随队列深度超线性增长,因此小队列可以实现更快的发射判决。代价是分发逻辑需要将指令路由到正确的子队列,且存在子队列之间负载不均衡的风险。

执行单元

香山的执行单元分为多个功能组:

  • 整数ALU:4个整数ALU端口,执行加减、逻辑、移位等基本运算,延迟1周期。

  • 整数乘法器:1–2个乘法端口,执行mul/mulh等指令,延迟3周期。

  • 整数除法器:1个除法端口,执行div/rem等指令,延迟可变(通常8–32周期)。

  • 分支单元:1–2个分支执行端口,计算分支目标地址并与BPU预测结果比较。

  • 浮点/向量单元:浮点加法、乘法、FMA(Fused Multiply-Add)端口,以及在昆明湖中新增的向量执行端口。

  • 访存单元:详见下一小节的MemBlock设计。

香山处理器后端执行引擎的总体结构。分布式发射队列将指令路由到不同类型的执行单元,所有指令通过ROB顺序提交。
香山处理器后端执行引擎的总体结构。分布式发射队列将指令路由到不同类型的执行单元,所有指令通过ROB顺序提交。

提交机制

香山的提交(Commit)级每周期最多从ROB头部提交6条指令。提交按程序顺序进行,保证精确异常和精确中断。当ROB头部的指令标记为"已完成"(Complete)且无异常时,该指令被提交:

  • 对于寄存器写入指令:释放该指令目标逻辑寄存器之前映射的旧物理寄存器(将其归还空闲列表)。

  • 对于store指令:将store数据从Store Buffer写入DCache。

  • 对于分支指令:如果分支预测正确,正常提交;如果预测错误,在此前的执行阶段就已经触发了流水线冲刷。

存储子系统

存储子系统是高性能处理器设计中最复杂也最关键的部分之一。香山处理器的存储子系统包括MemBlock(访存执行单元)、DCache、TLB、预取器和Cache一致性模块。

MemBlock设计

MemBlock是香山处理器中负责所有load和store操作执行的模块。其核心设计参数为2个load端口 + 2个store端口

硬件描述 7 — MemBlock的双端口设计

每周期2个load和2个store的设计对DCache的端口要求提出了严峻的挑战。DCache需要支持每周期2次读(load)和最多1次写(store commit),这要求DCache的数据RAM至少是3端口的(2R1W),或者使用banked结构来模拟多端口。

MemBlock的关键子结构包括:

  • Load Queue(LQ):记录所有in-flight的load操作。LQ用于实现load-store依赖检查——当一个load执行时,需要与所有更早的、尚未提交的store进行地址比较(store-to-load forwarding)。如果存在地址匹配的store且该store的数据已经就绪,则直接将store的数据转发给load,而无需访问DCache。

  • Store Queue(SQ):记录所有in-flight的store操作的地址和数据。Store操作在执行阶段只计算地址和准备数据,实际写入DCache要等到store指令提交时(store commit)才执行,以保证精确异常语义。

  • Store Buffer(SB):已提交但尚未写入DCache的store操作的缓冲区。SB用于合并连续的store操作,减少DCache的写入次数。

参数
Load端口数2
Store端口数2
Load Queue深度80
Store Queue深度64
Store Buffer深度16
DCache容量64KB
DCache相联度8路
DCache行大小64B
DTLB项数128

2-Load+2-Store端口的DCache实现策略。昆明湖的MemBlock需要支持每周期2个load和2个store的并行操作,这对DCache的物理实现提出了严峻的端口需求。在64KB 8路组相联的DCache上实现2R2W(2读2写)的多端口SRAM是面积和功耗上的巨大挑战。香山项目采用了多bank交织(multi-bank interleaving)策略来解决这一问题。

硬件描述 8 — DCache多Bank设计的详细实现

昆明湖的64KB DCache被分为8个bank,每个bank为8KB。bank的索引使用Cache行地址的低3位(bits[8:6]),使得连续的64字节Cache行被交替分布在不同bank中。每个bank是一个标准的单端口SRAM(1R或1W每周期),通过bank级的仲裁来支持整体的多端口访问。

2个Load端口的bank冲突处理。当两个load在同一周期访问同一bank时,发生bank冲突(bank conflict)。处理策略如下:

  1. 优先服务Load端口0(较老的load)。

  2. Load端口1的请求被推迟到下一周期重试。

  3. MemBlock的调度器在发射load时尝试避免bank冲突——通过检查两个load的地址的bank索引位是否相同。如果相同,调度器推迟其中一个load的发射。

在典型的工作负载中,8个bank的配置使得bank冲突的概率约为1/8=12.5%1/8 = 12.5\%(假设地址随机分布)。这意味着平均每8个双load周期中有1个会发生bank冲突,有效的双load吞吐量约为2×(10.125/2)1.8752 \times (1 - 0.125/2) \approx 1.875 load/周期——接近理论上限。

Store端口的实现。Store操作在MemBlock中分为两个阶段:

  • 地址计算阶段:2个store地址端口同时计算2个store的虚拟地址,进行TLB翻译,并将地址写入Store Queue。这一阶段不访问DCache的数据SRAM。

  • 数据提交阶段:已提交的store从Store Buffer中按顺序写入DCache。每周期最多1次DCache写入(与2个load读取共享bank资源)。store的写入可以与load的读取并行——只要它们访问不同的bank。

这种设计意味着MemBlock每周期的峰值DCache访问为2个load读 + 1个store写 = 3次SRAM访问。通过8-bank的交织设计,3次访问落在不同bank的概率很高(约8×7×68365.6%\frac{8 \times 7 \times 6}{8^3} \approx 65.6\%),在大多数周期中可以实现全速并行。

Load Queue的age-based依赖检查。每个load执行时需要与Store Queue中所有更老的store进行地址比较。对于80项Load Queue和64项Store Queue,每个load需要与最多64个store表项进行比较——这是一个64位宽的CAM(Content Addressable Memory)操作。两个load端口需要同时执行这一检查,总计每周期2×64=1282 \times 64 = 128次地址比较。

香山通过以下方式优化这一开销:

  1. 部分地址比较:不比较完整的虚拟地址(64位),而是先比较低位(如低12位,页内偏移),只有低位匹配时才进行完整地址比较。这减少了CAM的宽度和功耗。

  2. 年龄过滤:只与比当前load更老的store进行比较。通过ROB序号(age)来判断先后顺序,使用年龄矩阵(age matrix)快速确定哪些store表项需要参与比较。

  3. 推测性转发:当一个store的地址与load的地址匹配且store数据已就绪时,直接从Store Queue转发数据,延迟约为5–6周期。如果没有匹配,load正常从DCache读取(延迟4周期)。

存储消歧(Memory Disambiguation)

存储消歧是乱序处理器中一个微妙而关键的问题。当一个load指令在程序顺序中位于一个store指令之后时,如果两者访问的地址相同,load必须获取store写入的值。但在乱序执行中,load可能先于store执行(地址更早可用),此时load无法知道后续的store是否会写入相同的地址。

硬件描述 9 — 存储消歧的第一性原理分析

存储消歧问题的本质是:在乱序执行中,如何安全地让Load越过更老的Store执行

保守方案(所有Load等待所有更老的Store完成):完全安全,但严重限制了ILP。在典型工作负载中,约25%的指令是Load,约10%是Store。如果每条Load都必须等待所有更老的Store的地址计算完成,平均等待时间约为3–5个周期(假设Store地址计算需要1–3周期),等效CPI增加0.25×4=1.00.25 \times 4 = 1.0——对IPC从5到约2.5的毁灭性影响。

激进方案(所有Load立即执行,事后检查):最大化ILP,但在Store-Load地址冲突时需要代价高昂的流水线冲刷。违规频率约为每万条指令0.1–1次,每次冲刷代价约20–40周期。在最坏情况下(每万条指令1次违规),CPI增加0.0001×30=0.0030.0001 \times 30 = 0.003——几乎可以忽略。

结论:激进方案的性能远优于保守方案(CPI增加0.003 vs 1.0),这就是为什么几乎所有高性能处理器都选择了激进的推测Load策略。

但激进方案需要一个高效的违规检测机制——这就是Load Queue和Store Queue的核心功能。

香山采用了保守+推测相结合的存储消歧策略:

  1. Store-to-Load Forwarding:当load执行时,检查SQ中所有更早的store。如果存在地址匹配且数据已就绪的store,直接转发数据。

  2. 推测执行:当load的地址已经计算完毕但某些更早的store的地址尚未计算时,load可以推测执行(假设不存在地址冲突)。

  3. 违规检测:当更早的store的地址计算完毕后,与所有推测执行的更年轻的load进行地址比较。如果发现冲突(某个load应该从该store获取数据但没有),触发流水线冲刷,从违规的load开始重新执行。

性能分析 2 — Store-to-Load违规的性能影响

Store-to-load违规是乱序处理器中最昂贵的性能事件之一。一次违规导致从违规load开始的所有后续指令被冲刷并重新执行,代价通常在20–40个周期。在SPEC CPU基准测试中,store-to-load违规的频率通常为每万条指令0.1–1次,但由于每次违规的代价极高,累计的性能影响可达1%–5%。

更高级的处理器(如Intel的某些设计)使用内存依赖预测器(Memory Dependence Predictor)来减少违规频率。预测器记录历史上导致违规的load-store对,当检测到相同的load-store模式时,延迟load的执行直到对应的store完成。香山在昆明湖中也引入了类似的内存依赖预测机制。

Cache层次结构

香山的Cache层次结构设计如下:

  • L1 ICache:64KB,8路组相联,行大小64字节。ICache是只读的,设计相对简单,但需要支持每周期向IFU提供一个32字节的取指块。

  • L1 DCache:64KB,8路组相联,行大小64字节。DCache是整个存储子系统中最关键的组件之一,需要支持每周期2次load读和1次store写。采用VIPT(Virtually Indexed, Physically Tagged)寻址方式,在不增加TLB查找延迟的情况下实现物理地址的标签匹配。

  • L2 Cache:1MB,8路组相联,行大小64字节。L2是处理器核心私有的,雁栖湖采用inclusive策略,昆明湖改为non-inclusive策略。L2 Cache包含一个硬件预取器(stride prefetcher和stream prefetcher),用于预测并提前加载将被访问的Cache行。

  • L3 Cache:多核共享的末级缓存(LLC),容量和配置取决于SoC级的设计。

香山RTL代码追踪

香山处理器的全部RTL代码使用Chisel硬件描述语言编写,托管在GitHub上。Chisel是基于Scala的硬件构建语言,其核心优势是参数化生成器——通过Scala的类型系统和函数式编程能力,以极其紧凑的代码表达复杂的参数化硬件结构。以下通过一个简化的SystemVerilog示例展示香山ROB循环队列的核心设计思路。

verilog
module rob #(
    parameter ROB_DEPTH = 256,
    parameter COMMIT_WIDTH = 6,
    parameter DISPATCH_WIDTH = 6,
    parameter ADDR_WIDTH = $clog2(ROB_DEPTH)
)(
    input  logic                    clk, rst_n,
    // Dispatch interface (from rename stage)
    input  logic                    dispatch_valid [DISPATCH_WIDTH],
    input  rob_entry_t              dispatch_entry [DISPATCH_WIDTH],
    output logic [ADDR_WIDTH-1:0]   dispatch_idx   [DISPATCH_WIDTH],
    output logic                    rob_full,
    // Writeback interface (from execution units)
    input  logic                    wb_valid [DISPATCH_WIDTH],
    input  logic [ADDR_WIDTH-1:0]   wb_idx   [DISPATCH_WIDTH],
    input  logic                    wb_exception [DISPATCH_WIDTH],
    // Commit interface
    output logic                    commit_valid [COMMIT_WIDTH],
    output rob_entry_t              commit_entry [COMMIT_WIDTH],
    // Redirect (from branch misprediction)
    input  logic                    redirect_valid,
    input  logic [ADDR_WIDTH-1:0]   redirect_idx
);
    // Circular queue pointers
    logic [ADDR_WIDTH:0] head_ptr, tail_ptr;  // extra bit for full/empty
    logic [ADDR_WIDTH:0] count;

    // ROB storage
    rob_entry_t entries [ROB_DEPTH];
    logic       completed [ROB_DEPTH];
    logic       has_exception [ROB_DEPTH];

    assign count = tail_ptr - head_ptr;
    assign rob_full = (count >= ROB_DEPTH - DISPATCH_WIDTH);

    // Dispatch: write new entries at tail
    always_ff @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            tail_ptr <= '0;
        end else if (redirect_valid) begin
            // On misprediction: snap tail back to redirect point + 1
            tail_ptr <= {1'b0, redirect_idx} + 1;
        end else begin
            for (int i = 0; i < DISPATCH_WIDTH; i++) begin
                if (dispatch_valid[i] && !rob_full) begin
                    automatic logic [ADDR_WIDTH-1:0] idx;
                    idx = tail_ptr[ADDR_WIDTH-1:0] + i[ADDR_WIDTH-1:0];
                    entries[idx]       <= dispatch_entry[i];
                    completed[idx]     <= 1'b0;
                    has_exception[idx] <= 1'b0;
                    dispatch_idx[i]     = idx;
                end
            end
            // Advance tail pointer
            tail_ptr <= tail_ptr + count_valid(dispatch_valid);
        end
    end

    // Writeback: mark entries as completed
    always_ff @(posedge clk) begin
        for (int i = 0; i < DISPATCH_WIDTH; i++) begin
            if (wb_valid[i]) begin
                completed[wb_idx[i]]     <= 1'b1;
                has_exception[wb_idx[i]] <= wb_exception[i];
            end
        end
    end

    // Commit: retire entries from head (in-order)
    always_ff @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            head_ptr <= '0;
        end else begin
            automatic int committed = 0;
            for (int i = 0; i < COMMIT_WIDTH; i++) begin
                automatic logic [ADDR_WIDTH-1:0] idx;
                idx = head_ptr[ADDR_WIDTH-1:0] + i[ADDR_WIDTH-1:0];
                if (completed[idx] && !has_exception[idx]
                    && (head_ptr + i) != tail_ptr) begin
                    commit_valid[i] <= 1'b1;
                    commit_entry[i] <= entries[idx];
                    committed = i + 1;
                end else begin
                    commit_valid[i] <= 1'b0;
                    break;  // In-order: stop at first incomplete
                end
            end
            head_ptr <= head_ptr + committed;
        end
    end

    // Helper function: count valid dispatches
    function automatic int count_valid(
        input logic valids [DISPATCH_WIDTH]);
        int cnt = 0;
        for (int i = 0; i < DISPATCH_WIDTH; i++)
            if (valids[i]) cnt++;
        return cnt;
    endfunction
endmodule

设计提示

上述代码是高度简化的教学版本,真实的香山ROB实现在Chisel中约有1000+行代码,包含以下简化版中省略的复杂特性:(1) 分支检查点恢复——每个分支指令在ROB中记录RAT快照的索引,误预测时通过快照恢复而非逐条回滚;(2) 异常处理——异常指令到达ROB头部时触发精确异常,所有后续指令被作废;(3) 向量指令支持——一条向量指令可能对应多个ROB条目(LMUL>>1时),需要特殊的分配和提交逻辑;(4) Store Queue协调——Store指令的ROB提交需要与Store Queue的提交同步。

香山选择Chisel而非Verilog/SystemVerilog的关键原因是参数化生成。在上述简化SV代码中,修改ROB深度需要在多处手动调整相关参数。在Chisel中,所有参数通过配置对象统一管理——修改ROB深度只需更改一处配置值,Chisel编译器会自动传播到所有相关模块。这对微架构设计空间探索至关重要。

硬件描述 10 — 香山Chisel代码的模块结构

香山的GitHub仓库(XiangShan)的核心代码组织反映了微架构的模块化设计:

  • frontend/:前端模块,包含BPU(分支预测)、FTQ(取指目标队列)、IFU(指令取指)、ICache等子模块。BPU子目录下可以找到uBTB、FTB、TAGE、SC、RAS等各预测器组件的独立Chisel源文件。

  • backend/:后端模块,包含Decode(解码器)、Rename(重命名)、Dispatch(分发)、Issue(发射队列)、ExeUnit(执行单元)、ROB(重排序缓冲区)等子模块。

  • mem/:存储子系统,包含MemBlock(访存执行单元)、DCache(数据Cache)、LoadQueue、StoreQueue等。

  • cache/:L2 Cache的完整实现,包括inclusive/non-inclusive策略、预取器和一致性逻辑。

  • huancun/:L3 Cache/一致性互连的实现,提供多核间的Cache一致性支持。

以分支预测器为例,研究者如果想替换TAGE预测器为自己的实现,只需:(1) 创建一个新的Chisel模块实现TAGE的标准接口;(2) 在BPU的配置文件中将TAGE替换为新模块;(3) 重新编译Chisel代码生成Verilog。整个过程不需要修改前端的其他模块——这种可插拔的设计是香山作为研究平台的核心价值。

案例研究 1 — 香山开源生态的影响

香山处理器项目的开源策略产生了超出处理器设计本身的广泛影响。截至2025年,香山的GitHub仓库已累计获得数千颗Star,全球多个大学和研究机构基于香山平台进行了微架构研究,涵盖分支预测、Cache替换策略、预取算法、电源管理等多个方向。

这种开源模式的核心价值在于:

  1. 可重现性:研究者提出的微架构改进可以在真实的、完整的处理器RTL上验证,而不仅仅是在简化的模拟器上。

  2. 可比较性:不同研究团队基于同一基线设计进行优化,结果之间具有可比性。

  3. 教育价值:学生可以阅读和理解一个真实的高性能处理器的完整源代码,这在商业处理器领域几乎不可能。

  4. 产业协作:商业公司可以基于香山的设计进行定制和商业化,加速RISC-V高性能处理器的产业应用。

DiffTest验证方法论也已经被其他RISC-V处理器项目采用,成为开源处理器验证的事实标准。

香山昆明湖的分支预测器训练流程

分支预测器的训练(training/update)是BPU设计中与预测同等重要但经常被忽视的部分。当后端确认了一条分支的实际方向(taken/not-taken)和实际目标地址后,这一信息需要被回传到BPU以更新预测器的状态。训练的效率直接影响了预测器适应新代码路径的速度。

香山的训练流程

  1. 分支解析:后端的分支执行单元计算分支的实际方向和目标地址,与BPU的预测结果进行比较。

  2. 训练信息收集:如果分支预测正确,仍需更新预测器状态(如TAGE的饱和计数器)——正确的预测也需要强化。如果预测错误,需要触发流水线冲刷并更新预测器。

  3. 从FTQ读取元数据:训练需要使用BPU在预测时保存的元数据(bp_meta),包括TAGE的匹配表号、SC的校正值等。这些元数据存储在FTQ中,训练逻辑通过FTQ的随机读端口读取。

  4. TAGE表更新:根据实际结果和预测元数据,更新TAGE对应表的饱和计数器。如果预测来自表TkT_k且正确,则将TkT_k的计数器向对应方向饱和。如果预测错误,则将计数器反方向调整,同时可能在更长历史的表Tk+1T_{k+1}中分配一个新表项。

  5. SC更新:SC的训练在TAGE之后进行。如果SC的校正是错误的(SC改变了TAGE的预测但最终结果与TAGE一致),则SC的相关计数器被弱化。

  6. Loop预测器更新:如果分支被识别为循环退出点,更新循环迭代次数计数器。

训练延迟的影响。从分支解析到预测器更新完成的延迟通常为10–20周期。在此期间,如果同一分支再次被预测,BPU使用的是旧的预测器状态。香山通过训练旁路(training bypass)缓解这一问题——当一条分支的训练信息正在写入TAGE表时,如果同一分支的新预测请求到达,预测逻辑直接使用训练缓冲区中的最新计数器值而非SRAM中的旧值。这种旁路将训练延迟的有效影响从10–20周期降至约2–3周期。

设计权衡 1 — 分支预测器的存储预算分配

香山昆明湖的BPU总存储量约44–54KB。这一预算如何在各组件间分配,是一个需要仔细权衡的设计决策。

当前分配:TAGE 30–40KB + SC 8KB + Loop 2KB + uBTB 2KB + FTB 2–4KB

替代分配(更大TAGE):TAGE 40–50KB + SC 4KB + Loop 1KB + uBTB 1KB + FTB 2KB

定量分析:增大TAGE 10KB可降低MPKI约0.1–0.2。减小SC 4KB将增加MPKI约0.05–0.1。减小uBTB 1KB将增加前端气泡约1%–2%。

净效果:MPKI降低约0.05–0.1,但前端气泡增加1%–2%。两者相互抵消,整体IPC差异<<1%。

结论:在总预算约50KB的范围内,各组件的分配已接近局部最优。进一步改善需要增加总预算到100KB+,但面积和功耗代价需要评估。这也是为什么Cortex-X5将BPU存储量增至约72KB——更大的总预算使TAGE可以使用更多的表项和更长的历史,在MPKI上获得约5%–8%的改进。

SiFive P系列

SiFive是RISC-V商业核心IP的领先提供商,由RISC-V架构的发明者之一Andrew Waterman和其他UC Berkeley的创始成员于2015年创立。SiFive的产品线涵盖从嵌入式微控制器(E系列)、中等性能应用处理器(U系列)到高性能核心(P系列和X系列)。P系列是SiFive面向高端应用处理器和汽车市场的旗舰产品,代表了商业RISC-V核心IP在性能维度上的最高水平。

P670/P870的微架构

SiFive P670

SiFive P670(原名Performance P670)是SiFive于2022年推出的高性能RISC-V应用处理器核心IP,主要面向移动设备、可穿戴设备和汽车信息娱乐系统等对单线程性能有较高要求的市场。

P670的关键微架构特征包括:

  • 13级流水线:相对较深的流水线设计,有利于提高主频。作为对比,ARM Cortex-A76为13级,Cortex-A78为11–13级,可见P670的流水线深度处于同类核心的典型范围内。

  • 乱序执行:采用乱序超标量架构,支持多条指令的并行发射和乱序执行。具体的发射宽度未公开,但据分析约为5-wide。

  • 分支预测:多级分支预测器,包含BTB、间接分支预测和返回地址栈(RAS)。

  • 多核集群:支持最多4核的集群配置,核间共享L2 Cache。

  • RISC-V扩展:支持RV64GCB(基础整数+压缩+位操作)以及RVV 1.0向量扩展。

设计权衡 2 — 流水线深度的权衡

P670选择13级流水线深度反映了一个经典的微架构设计权衡。更深的流水线意味着每级的逻辑量更少,关键路径更短,可以达到更高的时钟频率。但更深的流水线也带来了更大的分支误预测惩罚(penalty = 流水线深度,约13周期),以及更多的流水线寄存器面积和功耗开销。

考虑以下简化的性能模型。设流水线深度为DD,基础CPI\mathrm{CPI}(无停顿)为CPIbase\mathrm{CPI}_{\mathrm{base}},分支频率为fbf_b,分支误预测率为rmpr_{mp},则实际CPI\mathrm{CPI}为:

CPI=CPIbase+fbrmpD\mathrm{CPI}= \mathrm{CPI}_{\mathrm{base}} + f_b \cdot r_{mp} \cdot D

而时钟频率fclkf_{\mathrm{clk}}与流水线深度近似成正比:fclkDf_{\mathrm{clk}} \propto D(在一定范围内)。因此,每秒执行的指令数(IPC×fclk\mathrm{IPC}\times f_{\mathrm{clk}})存在一个最优的DD值。研究表明,在典型工作负载下,最优流水线深度在12–16级之间,这与P670的13级设计吻合。

SiFive P870

SiFive P870是P670的性能增强版本,于2023年发布,目标市场扩展到数据中心、网络设备和高级驾驶辅助系统(ADAS)等对性能要求更高的领域。

P870相对于P670的主要增强包括:

  • 更宽的流水线:发射宽度进一步增加到估计7–8 wide,支持更多指令的并行执行。

  • 更大的乱序窗口:ROB和物理寄存器文件的容量增加,允许跨越更长的延迟事件维持指令级并行性。ROB估计扩展到192–256项,接近ARM Cortex-A76级别。

  • 增强的向量单元:更宽的向量执行通路,支持更高吞吐量的向量运算。VLEN可配置为128或256位。

  • 更大的Cache层次结构:L1 DCache可能增至64KB,L2 Cache扩展至1–2MB。

  • 多线程支持:支持2-way SMT(硬件多线程),在单个核心上同时执行2个线程,提高执行资源的利用率。SMT的引入是P870的一个关键差异化特性——在RISC-V高性能核心中,香山和BOOM均不支持SMT。

硬件描述 11 — SiFive P870的微架构深度分析

P870的微架构设计反映了SiFive在商业RISC-V核心IP领域的技术积累。以下是P870几个关键设计选择的深入分析。

SMT的实现。P870是少数支持SMT的RISC-V核心之一。SMT的实现需要在核心内部复制或分区以下资源:

  • 复制资源:程序计数器(PC)、通用寄存器的体系结构映射(RAT)、CSR寄存器(Control and Status Registers,RISC-V特有)、分支历史寄存器。

  • 静态分区资源:ROB表项(每线程一半)、Load Queue和Store Queue(每线程一半)。

  • 动态共享资源:物理寄存器文件、发射队列、执行端口、L1 I/D Cache、L2 Cache。

RISC-V的SMT实现比x86简单但比ARM复杂。RISC-V的32个整数寄存器(包括x0的硬编码零寄存器)和32个浮点寄存器需要为每个线程独立维护RAT映射。RISC-V特有的CSR寄存器(如mstatus、mtvec、mepc等)在SMT模式下的处理需要特别注意——某些CSR是全局的(如mhartid需要反映当前硬件线程的ID),某些是线程私有的(如mepc、mcause需要每线程独立保存)。

P870的分支预测器。P870的分支预测器据分析采用了多级方案:

  • L0预测器:小型的uBTB,1周期延迟,约64–128项。

  • L1预测器:TAGE方向预测器 + BTB,2–3周期延迟。TAGE使用4–6级历史表,总存储约16–32KB。

  • RAS:支持16–24项深度的返回地址栈,带推测性保护(shadow RAS)。

P870的预测器规格在RISC-V商业核心中属于中上水平,与ARM Cortex-A76的预测器规模相当。但由于RISC-V的beq/bne/blt等分支指令直接比较两个寄存器(不经过flags寄存器),分支执行单元需要内置比较器,延迟比ARM/x86的基于flags的分支稍长(可能多1周期),这需要预测器提供更高的精度来补偿稍长的误预测惩罚。

P870的乱序窗口与执行资源分析。P870的ROB估计为192–256项,搭配7–8 wide的发射宽度。与香山昆明湖的256项ROB + 6-wide解码相比,P870选择了更宽的解码但相似深度的ROB。这一选择的逻辑是:在SMT模式下,ROB被两个线程共享(每线程分得96–128项),更宽的前端可以让两个线程共同利用执行资源,提高总吞吐量。

性能分析 3 — P870 SMT模式下的资源分配分析

SiFive P870的2-way SMT实现涉及以下资源分配策略(基于推测分析):

步骤1:静态分区资源

ROB按线程静态对半分:每线程约96–128项。这确保了一个线程的长延迟操作不会饿死另一个线程的ROB资源。

Load Queue和Store Queue同样静态对半分:每线程约40项LQ + 32项SQ。

步骤2:动态共享资源

物理寄存器文件在两个线程间动态共享。假设总共256个整数物理寄存器,每个线程可以使用的物理寄存器数量取决于当前的分配情况——活跃线程可以使用更多寄存器,但需要保留最少32个(等于体系结构寄存器数)给另一个线程。

发射队列动态共享:一个线程的指令和另一个线程的指令可以在同一周期从同一个发射队列发射到不同的执行端口,实现了执行资源的最大化利用。

步骤3:SMT的IPC影响

假设单线程IPC = 4.0(单线程模式下的7–8 wide前端足以供给IPC 4)。在2-way SMT模式下:

  • 每线程IPC约下降15%–25%(由于ROB对半分和Cache竞争):每线程IPC \approx 3.0–3.4

  • 总吞吐IPC \approx 2×3.2=6.42 \times 3.2 = 6.4,相比单线程的4.0提升约60%

SMT的价值在于:用约10%的面积增加(主要是复制RAT和分支历史寄存器)获得约60%的多线程吞吐提升。这在服务器和ADAS等多线程工作负载中意义重大。

但SMT也带来了安全风险:共享的执行端口和Cache可能成为Spectre-SMT攻击的侧信道。SiFive需要在P870中实现适当的微架构防御措施(如执行端口的时序隔离、Cache的分区等)。

P870的目标市场定位。P870的设计瞄准了以下市场:

  • 高级驾驶辅助系统(ADAS):自动驾驶的感知和决策模块需要高性能的实时处理能力。P870的SMT支持可以同时处理感知数据(如激光雷达点云处理)和控制逻辑(如路径规划),提高单核心的利用率。

  • 数据中心边缘计算:5G基站和边缘服务器需要能效比高的处理器核心。P870的RVV 1.0向量扩展可以加速信号处理和数据压缩等通信工作负载。

  • 高端嵌入式:工业控制、医疗设备等对确定性实时性能有要求的应用。P870的乱序执行虽然增加了最坏情况延迟的不确定性,但SiFive通过提供精细的性能计数器和分析工具来帮助客户进行最坏情况分析。

:::

参数P670P870
流水线深度13级13+级
发射宽度\sim5-wide>>5-wide
乱序执行是(增强)
向量扩展RVV 1.0RVV 1.0(增强)
目标市场移动/汽车服务器/ADAS
SMT
集群配置最多4核最多4核+
性能定位\simA76级>>A76级

SiFive P系列核心的关键参数对比

设计选择的权衡

作为一家核心IP提供商,SiFive的设计哲学与专注于特定产品的处理器公司(如Apple、Qualcomm)有本质不同。SiFive需要提供高度可配置的核心IP,使不同客户能够根据自己的目标市场和面积/功耗/性能需求进行定制。这种商业模式对微架构设计产生了深远的影响。

可配置性与性能的权衡

高度可配置的核心IP设计往往需要在某些方面做出妥协。例如:

  • Cache配置:客户可能需要从16KB到128KB的L1 Cache选择。这要求Cache的控制逻辑必须参数化,但参数化的设计往往不如针对特定配置手动优化的设计高效。

  • 执行单元数量:客户可能需要不同数量的ALU或浮点单元。这要求发射逻辑和旁路网络能够适应不同的执行单元配置。

  • 向量单元VLEN:RVV允许VLEN从128位到任意2的幂次。不同客户可能需要不同的VLEN,这要求向量数据通路的宽度可配置。

RISC-V ISA对微架构的影响

RISC-V的模块化ISA设计对处理器微架构有几方面的影响:

硬件描述 12 — RISC-V无条件标志寄存器的微架构影响

RISC-V与ARM/x86的一个关键ISA差异是没有条件标志寄存器(flags register)。在x86中,几乎所有ALU指令都会隐式更新EFLAGS寄存器(零标志ZF、进位标志CF、溢出标志OF等),条件分支指令通过检查EFLAGS来决定跳转方向。在ARM A64中,只有带"S"后缀的指令(如ADDSSUBS)才更新NZCV标志。

RISC-V完全消除了条件标志寄存器——分支指令直接比较两个寄存器的值:beq rs1, rs2, offset。这一设计对微架构的影响是深远的:

1. 消除了flags引起的假依赖。在x86中,add eax, 1sub ebx, 1虽然没有数据依赖,但都更新EFLAGS,形成了通过EFLAGS的假依赖(false dependency)。乱序处理器需要额外的逻辑(如flags重命名、部分flags合并)来打破这一假依赖。RISC-V不存在这一问题——两条独立的add指令在寄存器重命名后完全独立,可以自由调度。

2. 简化了寄存器重命名。x86处理器需要为EFLAGS寄存器维护额外的RAT条目和物理寄存器。Intel的EFLAGS重命名尤其复杂——EFLAGS由多个独立的位域(CF、ZF、OF等)组成,不同的指令更新不同的位域,需要部分写入(partial write)和合并(merge)逻辑。RISC-V完全消除了这一复杂性。

3. 分支执行单元需要比较器。在ARM/x86中,条件分支只需检查flags寄存器的某些位——这是一个简单的位测试操作,延迟约0.5个FO4。在RISC-V中,分支执行单元需要内置一个64位比较器(用于beq/bne等指令),延迟约2–3个FO4。这可能使RISC-V的分支解析比ARM多约0.5–1个周期——但这一额外延迟被消除flags假依赖带来的IPC提升所抵消。

4. 宏融合的影响。ARM的cmp + b.cond模式可以通过宏融合合并为一条μ\muop。RISC-V的beq rs1, rs2, offset天然就是一条指令(一条μ\muop),不需要宏融合。但如果RISC-V程序需要先计算后比较(如sub t0, a0, a1 + beq t0, x0, label),则需要两条μ\muops——RISC-V处理器也可以实现这对指令的宏融合来减少μ\muop数量。

  1. 扩展的可选性:RISC-V将ISA分为基础指令集(RV32I/RV64I)和多个标准扩展(M、A、F、D、C、V等),处理器设计者可以根据需要选择性地实现。这对微架构的影响是解码器和执行单元需要模块化设计,可以根据配置启用或禁用特定功能。

  2. 压缩指令(C扩展):16位的压缩指令提高了代码密度,但对前端设计带来了复杂性——取指块中可能包含16位和32位指令的混合,需要预解码逻辑来检测指令边界。此外,32位指令可能跨越取指块边界(在16位对齐的情况下),需要特殊的处理逻辑。

  3. 向量扩展(V扩展):RVV的可变长度向量模型(LMUL和VL机制)增加了微架构的复杂性。处理器需要在运行时根据vtypevl动态调整向量操作的行为,这比固定宽度的SIMD指令(如ARM NEON的固定128位操作)更复杂。

  4. 原子操作(A扩展):RISC-V的原子操作(AMO指令和LR/SC指令对)的内存顺序模型(RVWMO)相对较弱,允许处理器有更多的内存操作重排序自由度,有利于高性能实现。但LR/SC的实现需要在Cache一致性协议中增加reservation set的支持。

设计提示

RISC-V的弱内存模型(RVWMO)是一个值得注意的ISA级设计选择。与x86的TSO(Total Store Order)模型相比,RVWMO对硬件实现更加友好——处理器可以更自由地重排序不相关的内存操作,减少了Store Buffer和Load Queue中维护顺序的逻辑。然而,这也意味着软件(特别是编译器和并发库)需要在适当的位置插入fence指令来保证多线程程序的正确性。SiFive的P系列在实现中充分利用了RVWMO的宽松性,允许load指令越过更早的无关store指令执行,提高了存储子系统的吞吐量。

SiFive的核心集群架构

SiFive P系列支持多核心集群配置。一个典型的集群包含2–4个P670或P870核心,共享一个L2 Cache,通过SiFive的TileLink互连总线连接到系统级的LLC和内存控制器。

集群架构的关键设计决策包括:

  • Cache一致性:集群内的核心通过窥探(snoop)协议维持L1 Cache的一致性,集群间通过目录(directory)协议维持一致性。这种混合方案在集群规模较小时使用低延迟的窥探协议,在全芯片规模上使用可扩展的目录协议。

  • L2 Cache共享:集群内的核心共享L2 Cache,减少了每核的面积开销,同时利用了核间的数据局部性(一个核心访问的数据经常被同一集群中的其他核心访问)。

  • TileLink总线:SiFive自主设计的片上互连协议,支持缓存一致性事务。TileLink也是开源的(以SiFive的许可证),与AMBA/AXI等ARM生态的总线协议竞争。

硬件描述 13 — TileLink vs AMBA CHI:RISC-V互连协议的对比

TileLink和AMBA CHI是RISC-V和ARM生态中两个最重要的片上互连协议。两者的设计哲学反映了各自生态的特征。

TileLink的设计特点

  • 开源开放:TileLink的规范是完全开源的,任何人都可以实现兼容的互连。这与RISC-V ISA的开放理念一致。

  • 简洁的通道模型:TileLink定义了5个逻辑通道(A=Acquire, B=Probe, C=Release, D=Grant, E=GrantAck),覆盖了所有的一致性事务类型。相比AMBA CHI的数十种消息类型,TileLink的协议复杂度更低。

  • 层次化设计:TileLink定义了三个一致性级别——TL-UL(Uncached Lightweight,无Cache)、TL-UH(Uncached Heavyweight,有原子操作)、TL-C(Cached,完整的一致性支持)。不同的IP模块可以根据需要选择适当的级别。

AMBA CHI的优势

  • 成熟度:CHI已经在数十亿颗芯片中被验证,拥有丰富的硬件IP生态(如ARM的CMN互连网络)。

  • 性能优化:CHI支持更细粒度的一致性操作(如部分Cache行写入、DMT(Direct Memory Transfer)等),在高端SoC中可以实现更低的延迟和更高的带宽利用率。

  • 多芯片扩展:CHI定义了跨芯片的一致性扩展(CHI.E),支持多芯片Socket的一致性,这对服务器市场至关重要。

对RISC-V处理器设计的影响:SiFive的P系列使用TileLink,这限制了其与ARM生态IP(如Mali GPU、CoreLink互连)的直接集成。Ventana Veyron选择了AMBA CHI兼容的互连方案,以便与更广泛的IP生态集成。香山的多核版本也在探索CHI兼容的互连——这是RISC-V高性能处理器走向产品化的重要一步。

未来的趋势可能是:RISC-V生态逐步向CHI靠拢(或开发CHI的开源实现),以利用ARM生态中已有的大量IP模块(GPU、NPU、ISP等),而TileLink保持在学术和小规模设计中的地位。

其他高性能RISC-V核心

除了香山和SiFive P系列外,全球还有多个团队在开发高性能RISC-V处理器核心。本节介绍其中最具代表性的三个设计:Ventana Veyron、平头哥C910/C920和BOOM。

Ventana Veyron

Ventana Micro Systems成立于2018年,总部位于美国加利福尼亚,由一群来自Intel、AMD和ARM的资深处理器架构师创立。Ventana的目标是设计服务器级别的高性能RISC-V处理器,直接与ARM Neoverse系列和Intel Xeon竞争。

Veyron V1的微架构

Ventana的首款产品Veyron V1于2022年发布,是当时单线程性能最高的RISC-V处理器核心之一。Veyron V1的微架构特点包括:

  • 宽发射乱序核心:Veyron V1采用宽发射(wide-issue)的乱序超标量设计,发射宽度据报道为4–6 wide。大容量的ROB和物理寄存器文件支持深度乱序执行。

  • 高性能分支预测器:采用多级分支预测方案,针对服务器工作负载优化,减少因长跳转和间接分支导致的预测错误。

  • 大容量Cache:L1 ICache和DCache的容量较大(据报道L1 DCache为64KB),L2 Cache为私有配置(每核1–2MB),L3 Cache采用共享设计。

  • 高带宽存储子系统:支持DDR5内存,多通道配置,总内存带宽满足服务器工作负载的需求。

  • 全面的ISA支持:实现RV64GCVH(包含Hypervisor扩展),支持虚拟化,满足云计算和数据中心的需求。

Chiplet架构

Ventana Veyron最引人注目的设计特点是其chiplet架构。与传统的单芯片(monolithic die)设计不同,Ventana采用了将计算核心和I/O功能分布在多个小芯片上的策略:

  • 计算chiplet:每个计算chiplet包含16个Veyron核心和共享的L3 Cache。使用先进工艺(如5nm/7nm)制造,优化频率和性能密度。

  • I/O chiplet:I/O chiplet包含内存控制器、PCIe控制器、以太网接口等I/O功能。使用成熟工艺(如12nm/14nm)制造,降低成本并简化模拟电路设计。

  • 互连:多个chiplet通过高速die-to-die互连(如UCIe)集成在一个封装中。目标是在单封装中集成最多128个核心。

Ventana Veyron的chiplet架构示意图。多个计算chiplet通过UCIe高速互连集成在一个封装中,I/O chiplet使用成熟工艺降低成本。
Ventana Veyron的chiplet架构示意图。多个计算chiplet通过UCIe高速互连集成在一个封装中,I/O chiplet使用成熟工艺降低成本。
::: warning 性能分析 4 — Ventana 192核设计的挑战量化

Ventana计划在单封装中集成最多192核(12个计算Chiplet ×\times 16核),这一规模带来了以下量化的设计挑战:

步骤1:Cache一致性的可扩展性

192核的一致性域需要管理的Cache行总数:假设每核L1 DCache 64KB + L2 Cache 1MB = 1088KB的私有Cache,192核共约192×1088=204  MB192 \times 1088 = 204\;\text{MB}的私有Cache行。以64字节Cache行计算,总共约204×106/643.2×106204 \times 10^6 / 64 \approx 3.2 \times 10^6个Cache行需要被一致性目录追踪。

目录的存储量:每个Cache行需要一个位向量记录哪些核心持有副本。192核的位向量为192位(24字节)。但192位的全位向量过于庞大——通常使用受限指针(limited pointer)方案,每个目录条目记录最多KK个核心的ID(K=4K = 488),超过KK个核心时回退到广播。每个指针需要log2(192)=8\lceil\log_2(192)\rceil = 8位,4个指针共32位(4字节)。3.2×1063.2 \times 10^6个条目的目录存储量约为3.2×106×4=12.8  MB3.2 \times 10^6 \times 4 = 12.8\;\text{MB}——这需要在I/O Chiplet中集成约13MB的目录SRAM。

步骤2:内存带宽需求

192核全速运行时的峰值内存带宽需求:假设每核每周期产生0.5次L2 miss(在服务器工作负载中典型),L2 miss的数据量为64字节,核心频率3GHz。

总带宽需求:192×0.5×64×3×109=18.4  TB/s192 \times 0.5 \times 64 \times 3 \times 10^9 = 18.4\;\text{TB/s}

这一带宽需求远超任何现有的内存技术。实际中,192核不会同时全速产生L2 miss——L3 Cache的过滤效应将L3 miss率降至L2 miss率的约10%–20%,实际的DRAM带宽需求约为1.83.7  TB/s1.8\text{--}3.7\;\text{TB/s}。这仍然需要多通道DDR5或HBM3才能满足。

步骤3:互连延迟的影响

在4个计算Chiplet的配置中,跨Chiplet的一致性事务延迟约50–80ns。在192核的12 Chiplet配置中,最远的两个Chiplet之间可能需要经过2–3跳互连,延迟可能达到100–150ns。这接近DRAM访问延迟的2倍,意味着跨Chiplet的Cache共享在某些情况下比直接访问DRAM更慢——操作系统必须进行NUMA感知的线程和数据放置以避免这一问题。

:::

Chiplet架构对RISC-V服务器处理器的意义在于:

  1. 成本优化:计算核心使用最先进的工艺追求性能,I/O使用成熟工艺降低成本——两种芯片的良率都高于将所有功能集成在单一先进工艺die上的方案。

  2. 可扩展性:通过增加计算chiplet的数量,可以在同一封装中灵活配置核心数(16/32/64/128核),覆盖不同的市场段。

  3. 设计复用:同一计算chiplet设计可以用于不同的产品SKU,减少设计和验证成本。

硬件描述 14 — Ventana Veyron Chiplet互连的技术细节

Ventana的Chiplet架构面临的核心挑战是计算Chiplet之间的缓存一致性die-to-die通信延迟。以下是其互连方案的技术分析。

UCIe互连。Ventana采用了UCIe(Universal Chiplet Interconnect Express)标准作为die-to-die互连协议。UCIe由Intel、AMD、ARM等多家公司联合制定,定义了Chiplet之间的物理层(PHY)、链路层(Link Layer)和协议层(Protocol Layer)接口标准。UCIe的关键特性包括:

  • 高密度互连:UCIe的bump pitch可低至25μ\mum(标准封装)或36μ\mum(先进封装),每mm边缘可提供数十GB/s的带宽。

  • 一致性协议支持:UCIe的协议层可以承载CXL.cache和CXL.mem等缓存一致性协议,使得不同Chiplet上的核心可以维护一致的Cache状态。

  • 低延迟:UCIe的die-to-die延迟约为2–5ns(取决于封装技术),远低于PCIe的数百纳秒级延迟。

跨Chiplet一致性协议。Ventana的多Chiplet系统使用目录式一致性协议来管理跨die的缓存一致性。I/O Chiplet上的一致性目录跟踪每个Cache行在各计算Chiplet上的存在状态。一致性事务的流程如下:

  1. 计算Chiplet A上的核心发起一个读请求(如L3 miss)。

  2. 请求通过UCIe链路发送到I/O Chiplet上的一致性目录。

  3. 目录查询确定该Cache行的当前持有者——如果在计算Chiplet B上(Modified状态),目录向Chiplet B发送snoop请求。

  4. Chiplet B响应snoop,将数据通过I/O Chiplet转发给Chiplet A(或直接通过Chiplet间的互连转发,如果拓扑支持)。

  5. 目录更新状态信息。

跨Chiplet一致性事务的总延迟约为50–80ns(包括两次UCIe跨越和目录查询),与AMD EPYC的跨CCD延迟(45–70ns)处于相似的量级。

NUMA拓扑的管理。128核的Ventana处理器(4个计算Chiplet ×\times 16核/Chiplet)形成了一个4节点的NUMA系统。每个计算Chiplet优先访问I/O Chiplet上距离最近的内存控制器,形成本地和远程内存域。操作系统需要通过ACPI SLIT(System Locality Information Table)了解各节点之间的距离,并据此进行NUMA感知的内存分配和线程调度。

Ventana的NUMA管理借鉴了AMD EPYC的成功经验——提供灵活的NPS(NUMA Per Socket)配置,允许用户根据工作负载特征选择NPS1(统一内存访问,最大带宽)或NPS4(局部化内存访问,最低延迟)。

设计提示

Ventana选择Chiplet架构而非单片设计,是RISC-V服务器处理器在当前发展阶段的最佳策略之一。RISC-V核心的IP积累不如x86/ARM深厚,一次设计128核的大Die不仅面积巨大(良率低),而且验证难度极高。Chiplet架构将复杂性分解为可管理的小Die——每个16核的计算Chiplet可以独立验证和流片,降低了设计风险和流片成本。同时,Chiplet架构还使Ventana能够快速迭代——如果需要更新核心微架构,只需重新设计计算Chiplet,I/O Chiplet可以复用。这种模块化的设计方法论对于资源有限的RISC-V创业公司尤为重要。

案例研究 2 — Veyron与ARM Neoverse的竞争定位

Ventana Veyron的明确竞争目标是ARM Neoverse系列(特别是Neoverse V2和N2),后者已经在数据中心市场占据了显著份额(AWS Graviton、Ampere Altra等)。两者的竞争维度包括:

  • 单线程性能:Veyron V1的SPECint2017单线程性能据报道接近Neoverse N2级别。但ARM在持续迭代(N3、V3),RISC-V需要更快的性能提升速度才能追赶。

  • 核心密度:Veyron的128核chiplet设计在核心密度上与Ampere Altra(128核monolithic)和AWS Graviton 4(96核)竞争。

  • 软件生态:这是RISC-V面临的最大挑战。ARM Neoverse已有成熟的Linux发行版、编译器优化、数据库和中间件支持。RISC-V的软件生态虽在快速发展但仍有明显差距。

  • 许可费用:RISC-V的开放ISA意味着Ventana无需向任何ISA持有者支付许可费用,这在理论上可以降低芯片成本。但实际上,ARM的许可费在芯片总成本中占比很小(通常1%–3%),许可费用的节省可能不足以构成决定性的竞争优势。

真正能让RISC-V服务器处理器获得市场份额的是差异化创新——例如利用RISC-V ISA的可定制性添加特定领域的自定义指令扩展(加密、AI推理等),这是ARM和x86的封闭ISA无法提供的。

平头哥C910/C920

平头哥半导体(T-Head Semiconductor)是阿里巴巴集团旗下的半导体子公司,专注于RISC-V处理器核心和SoC的设计。平头哥在RISC-V高性能核心领域有两款重要的产品:C910和C920。

C910的微架构

C910(原名"玄铁C910")是平头哥于2019年发布的高性能RISC-V处理器核心,也是当时性能最高的公开可用的RISC-V核心之一。C910的核心RTL代码已在GitHub上开源(OpenC910),为RISC-V社区提供了重要的参考设计。

C910的微架构特点:

  • 12级流水线:适中的流水线深度,在频率和分支误预测惩罚之间取得平衡。

  • 3-wide解码:每周期最多解码3条指令。3-wide的解码宽度对应中等性能定位——比2-wide的典型中端核心更宽,但不及ARM A76的4-wide或香山的6-wide。

  • 双发射:采用2-wide乱序发射,拥有8个功能单元端口(2个ALU、1个乘法器、1个除法器、2个load/store、1个浮点加法、1个浮点乘法)。

  • ROB:64项ROB,乱序窗口大小较为保守。

  • 分支预测:采用TAGE变体的分支预测器,以及BTB、RAS和间接分支预测器。

  • Cache:L1 ICache 64KB、L1 DCache 64KB(均4路组相联),可配置的L2 Cache(最大1MB)。

  • ISA支持:RV64GCV,支持RISC-V向量扩展(早期版本,非RVV 1.0标准)。

参数C910香山雁栖湖SiFive U74
解码宽度362
发射宽度2 (OoO)6 (OoO)1 (顺序)
ROB容量64256
流水线深度12\sim148
L1 ICache64KB64KB32KB
L1 DCache64KB64KB32KB
L2 Cache最大1MB1MB2MB (共享)
向量支持有(早期版本)
开源

平头哥C910与同期RISC-V核心的微架构参数对比

C910被用于平头哥的TH1520 SoC中,该SoC包含4个C910核心,主频约1.8GHz,采用12nm工艺制造。TH1520被多个RISC-V开发板(如Lichee Pi 4A、BeagleV-Ahead)采用,是2023–2024年最广泛使用的高性能RISC-V平台之一,能够运行完整的Linux桌面环境。

C920的改进

C920是C910的继任者,在多个维度上进行了显著增强:

  • 更宽的发射:发射宽度增加,支持更多指令的并行执行。

  • 更大的乱序窗口:ROB和发射队列的容量增加。

  • 标准向量扩展:全面支持RVV 1.0标准,VLEN为128位或256位。这解决了C910使用早期非标准向量扩展的兼容性问题。

  • 改进的分支预测:更大的TAGE表和更精确的间接分支预测。

  • 增强的存储子系统:更大的L2 Cache,改进的预取策略,更高的存储带宽。

  • 更高的频率目标:通过流水线优化和时序收敛改进,支持更高的工作频率。

C920的性能目标是在相同工艺节点下达到C910的1.5–2倍单线程性能,使其在绝对性能上与ARM Cortex-A76或A77相当。

硬件描述 15 — C910与香山的设计哲学对比

C910和香山代表了两种截然不同的RISC-V高性能核心设计哲学:

C910的"实用主义"路线。C910由一个工业级芯片设计团队(平头哥/阿里巴巴)开发,目标是快速交付一个可量产的、达到商业水平可靠性的处理器核心。C910的设计选择体现了工业界的务实:

  • 保守的乱序窗口(64项ROB):减少验证复杂度和面积,接受IPC的适度损失。

  • Verilog实现:使用工业界最广泛使用的HDL,确保EDA工具链的完全兼容和成熟的物理实现流程。

  • 早期的向量扩展支持:率先实现向量指令(虽然是非标准版本),为后续的标准化提供了实践经验。

  • 面向SoC集成:C910从一开始就设计为可集成到完整SoC中(TH1520),包含完整的中断控制器、调试接口和电源管理接口。

香山的"极限探索"路线。香山由一个学术研究团队(中科院计算所)主导,目标是在开源框架下探索RISC-V的性能上限。香山的设计选择体现了学术界的追求:

  • 激进的乱序窗口(256+项ROB):追求最高的IPC,接受更大的面积和验证复杂度。

  • Chisel实现:使用高级HDL,追求设计空间探索的灵活性和代码的高度参数化,接受生成Verilog可读性差的代价。

  • 最先进的分支预测(TAGE-SC-L):直接实现学术界的最优方案,不受工程成本的过度约束。

  • 开放式研究平台:设计不仅服务于特定产品,更服务于全球的微架构研究社区。

两种路线的结果截然不同但同样有价值:C910率先实现了可量产的RISC-V Linux桌面平台(TH1520被多个开发板采用),证明了RISC-V在实际产品中的可行性。香山则在IPC上远超C910(约3.8 vs 约2.0),为RISC-V的性能潜力设定了更高的标杆。两个项目的经验和技术积累都在推动RISC-V高性能处理器的整体进步。

性能分析 5 — 平头哥处理器的性能演进

平头哥的处理器核心经历了从嵌入式到高性能的演进:

核心年份流水线发射性能定位
C90620205级顺序单发低功耗IoT
C90820218级顺序双发中等性能
C910201912级OoO 2-wide高性能 (\simA73)
C920202312+级OoO 增强高性能 (\simA76)

这种演进路径展示了一个典型的处理器核心产品线策略:从简单的顺序核心开始积累设计经验和验证基础设施,逐步向更高性能的乱序核心演进。每一代核心的设计和验证经验都被用于加速下一代的开发。

C910的开源价值

C910是少数完整开源的工业级高性能RISC-V处理器核心之一(另一个重要的开源高性能核心是香山)。C910使用Verilog HDL编写(而非香山的Chisel),这使得使用传统EDA工具链的工程师可以更直接地阅读和修改其代码。

C910的开源对RISC-V生态的贡献体现在:

  • 参考实现:提供了一个经过流片验证的乱序RISC-V核心实现,可作为其他设计的参考。

  • 教育资源:大学课程可以使用C910的源代码作为高性能处理器设计的教学材料。

  • 定制基础:企业可以基于C910进行领域特定的定制,例如添加自定义指令扩展或修改Cache层次结构。

BOOM

BOOM(Berkeley Out-of-Order Machine)是UC Berkeley开发的开源RISC-V乱序超标量处理器核心,是学术界最具影响力的RISC-V处理器研究平台之一。BOOM与RISC-V指令集同源——它们都诞生于UC Berkeley的计算机体系结构研究组。

BOOM的设计哲学

BOOM的设计哲学与香山或SiFive P系列有根本性的不同。BOOM的首要目标不是追求最高的绝对性能或工业级的成熟度,而是作为一个研究平台,使研究者能够快速探索和验证微架构创新。这一定位决定了BOOM的几个关键特征:

  1. 高度参数化:BOOM使用Chisel编写,几乎所有微架构参数都可以通过Scala配置类进行调整。研究者可以在不修改RTL代码的情况下,通过改变参数生成不同配置的处理器——例如从2-wide顺序核心到6-wide深度乱序核心。

  2. 模块化设计:BOOM的各子模块(前端、重命名、发射队列、执行单元、LSU等)之间通过清晰定义的接口交互,便于单独替换或修改某个子模块而不影响其他部分。

  3. 与Rocket生态集成:BOOM基于UC Berkeley的Rocket Chip生态系统,可以复用Rocket Chip中的非核心组件——TileLink总线、Debug Module、CLINT/PLIC中断控制器等。这使得BOOM可以直接生成完整的、可在FPGA上运行的SoC,而不仅仅是一个独立的核心。

  4. 可综合和可验证:BOOM的RTL是完全可综合的(可以映射到FPGA或ASIC),并且可以通过运行Linux内核和应用程序进行系统级验证。

BOOM的微架构

BOOM的微架构是一个经典的乱序超标量流水线,其默认配置的关键参数如下:

  • 取指:可配置的取指宽度(默认为4字节/周期或8字节/周期),支持RVC压缩指令。

  • 解码:可配置的解码宽度(1-wide到4-wide,典型配置为2-wide或4-wide)。

  • 分支预测:支持多种可插拔的分支预测器实现,包括GShare、TAGE和tournament predictor。研究者可以方便地替换分支预测器来评估不同预测方案的性能。

  • 寄存器重命名:统一PRF方案,物理寄存器数量可配置(典型值为96–128个整数物理寄存器)。

  • ROB:可配置深度(典型值为32–128项)。

  • 发射:支持"统一发射队列"和"分布式发射队列"两种配置。

  • 执行:可配置的执行单元数量和类型。

  • LSU:Load Queue和Store Queue的深度可配置,支持store-to-load forwarding和推测执行。

参数SmallMediumLarge
解码宽度124
取指宽度4B8B16B
ROB深度3264128
整数PRF5280128
发射队列8项×\times116项×\times224项×\times3
Load Queue81632
Store Queue81632
L1 DCache16KB32KB64KB
分支预测GShareTAGETAGE

BOOM不同配置的微架构参数

BOOM作为研究平台的价值

硬件描述 16 — BOOM的参数化设计空间探索

BOOM的核心价值在于其参数化微架构——通过修改Scala配置类中的参数,可以快速生成不同配置的处理器并评估其PPA特性。以下示例展示了BOOM中一个典型的配置参数集:

  • fetchWidth:取指宽度(字节数),影响前端带宽

  • decodeWidth:解码宽度,决定每周期处理的指令数

  • numRobEntries:ROB深度,决定乱序窗口大小

  • numIntPhysRegisters:整数物理寄存器数量

  • numFpPhysRegisters:浮点物理寄存器数量

  • numIssueSlotEntries:每个发射队列的深度

  • numLdqEntries:Load Queue深度

  • numStqEntries:Store Queue深度

  • enableBranchPrediction:启用/禁用分支预测器

  • branchPredictor:分支预测器类型(GShare/TAGE/Tournament)

研究者可以在几分钟内生成数十种不同配置的BOOM,并在FPGA上运行相同的基准测试来获取性能数据。这种快速设计空间探索能力在商业处理器设计中是不可能的——一个商业核心的参数变更通常需要数周的重新验证才能确保正确性。

BOOM的参数化能力使其特别适合以下类型的研究:

  1. Pareto前沿分析:给定面积预算,找到IPC最高的微架构配置。

  2. 敏感度分析:固定其他参数,单独改变一个参数(如ROB深度),观察IPC如何变化。

  3. 协同效应分析:同时改变多个参数,发现参数之间的交互效应(如更大的ROB需要配套更多的物理寄存器才能发挥作用)。

BOOM在学术研究中的应用非常广泛,已经被用于多个微架构研究方向:

  1. 分支预测研究:研究者可以在BOOM中实现和评估新的分支预测算法,直接在完整的处理器RTL上获得SPEC基准测试的性能数据。

  2. Cache和内存系统研究:通过修改BOOM的Cache替换策略、预取器或TLB设计,评估这些改变对整体性能的影响。

  3. 安全性研究:BOOM被用于研究和验证针对Spectre/Meltdown等微架构安全漏洞的硬件防御机制。由于BOOM的RTL是完全开放的,研究者可以精确地分析推测执行的侧信道泄漏路径并实现硬件级的防护。

  4. 功耗与面积探索:通过综合不同配置的BOOM到ASIC标准单元库,研究者可以获得面积和功耗数据,评估微架构参数对物理实现的影响。

  5. 加速器集成:BOOM可以通过RoCC(Rocket Custom Coprocessor)接口或TileLink总线与自定义硬件加速器集成,研究异构计算的微架构方案。

设计提示

BOOM与香山的定位差异值得注意。BOOM追求的是研究的灵活性——最大限度地降低微架构探索的门槛,让研究者可以在几天内实现一个新想法并在完整的处理器上验证。香山追求的是工业级的性能和成熟度——其代码经过多次流片验证,设计质量接近商业处理器。两者在开源RISC-V生态中扮演互补的角色:BOOM适合快速原型验证和概念探索,香山适合需要真实性能数据和物理实现参考的研究。

BOOM与Rocket的关系

BOOM与UC Berkeley的另一款开源RISC-V核心Rocket有密切的关系。Rocket是一个顺序执行(in-order)的RISC-V核心,设计简洁、成熟度高,被广泛用于RISC-V生态的基础设施。BOOM可以看作Rocket在乱序执行维度上的扩展——BOOM复用了Rocket Chip SoC的大部分非核心组件(总线、调试模块、中断控制器、Boot ROM等),只替换了核心流水线部分。

这种架构复用策略意味着:

  • 在Rocket Chip SoC中,可以将Rocket核心替换为BOOM核心,立即获得一个可运行Linux的乱序超标量RISC-V SoC。

  • 可以在同一个SoC中混合使用Rocket和BOOM核心(异构多核),Rocket作为效率核心,BOOM作为性能核心。

  • BOOM的验证基础设施与Rocket共享——任何能在Rocket上运行的软件(包括Linux内核)也应该能在BOOM上运行,这为BOOM的功能验证提供了丰富的测试用例。

案例研究 3 — BOOM的Spectre安全研究贡献

BOOM在微架构安全研究中扮演了独特的角色。2018年Spectre/Meltdown漏洞被发现后,安全研究社区迫切需要一个可以在RTL层面精确分析推测执行侧信道的处理器平台。商业处理器(Intel、AMD、ARM)的RTL不可公开获取,研究者只能通过性能计数器和时间测量来间接推测微架构行为。BOOM填补了这一空白。

BOOM上已验证的安全研究包括

  1. Spectre-v1(边界检查绕过):研究者在BOOM的RTL上精确复现了Spectre-v1攻击,验证了推测执行的Load操作确实会修改DCache状态,从而泄露推测路径上的秘密数据。随后在BOOM上实现并验证了硬件级的防御措施——投机Load隔离(Speculative Load Isolation),将推测路径上的Load结果标记为"推测的",在确认推测正确之前不允许其影响Cache状态。

  2. Spectre-v2(间接分支注入):验证了间接分支预测器(BTB、间接分支预测表)可以被攻击者训练来重定向受害者的执行流,并在BOOM上评估了BTB隔离IBPB(Indirect Branch Prediction Barrier)等防御措施的性能开销。

  3. 新型攻击的发现:UC Berkeley的研究组利用BOOM发现了新的侧信道泄露路径——例如通过发射队列的竞争(一个推测Load占用了发射队列条目,影响了另一个线程的指令发射时序)。这些攻击在商业处理器上很难发现,因为研究者无法观察发射队列的内部状态。

BOOM的安全研究经验表明:开源硬件对安全研究至关重要。在闭源处理器的世界中,安全漏洞的发现和修复只能依赖少数芯片厂商的内部团队。在开源处理器的世界中,全球的安全研究社区都可以审计和改进处理器的安全性——这与开源软件在安全性方面的优势("given enough eyeballs, all bugs are shallow")高度一致。

特性香山昆明湖C910BOOM LargeRocketSiFive U74
执行模型OoOOoOOoO顺序顺序
解码宽度63412
ROB深度256+64128
分支预测TAGE-SC-LTAGETAGEgsharegshare
L1 I/D64/64KB64/64KB32/64KB16/16KB32/32KB
L2 Cache1MB1MB2MB
向量支持RVV 1.0早期V
HDLChiselVerilogChiselChiselChisel
开源
流片验证FPGAFPGA
定位高性能研究产品级学术研究基础设施商业IP

RISC-V开源处理器核心的综合对比

RISC-V高性能处理器的共性与差异

通过前面对多款RISC-V高性能处理器的分析,我们可以总结出一些共性设计选择和关键差异点。

前端设计的共性

所有高性能RISC-V处理器在前端设计上面临共同的挑战和解决方案:

  1. 变长指令处理:所有支持RVC扩展的核心都需要处理16位和32位指令的混合。预解码逻辑需要快速确定取指块中每条指令的长度和起始位置。这是RISC-V相对于ARM AArch64(固定32位指令,Thumb-2在AArch64中不再使用)的额外复杂性,但远不及x86的1–15字节变长指令复杂。

  2. 多级分支预测:所有高性能核心都采用了多级分支预测方案——一个快速但不太精确的低延迟预测器和一个较慢但更精确的高延迟预测器。这反映了分支预测中延迟与精度的根本权衡。

  3. TAGE预测器的普及:TAGE或其变体已成为RISC-V高性能核心的标准分支预测方案(参见第 15.0 章中的详细分析)。香山使用TAGE-SC-L,平头哥使用TAGE变体,BOOM也提供TAGE作为高性能配置的选项。TAGE的几何级数历史长度表设计在实际工作负载中表现优异,是当前最优的方向预测方案之一。

后端设计的差异

不同RISC-V核心在后端设计上的差异主要体现在乱序窗口大小和发射组织方式上。

参数香山昆明湖C910Veyron V1BOOM Large
解码宽度634–64
ROB深度>>25664大(未公开)128
整数PRF>>192128
发射队列类型分布式分布式可配置
Load端口2221–2
Store端口211–21

主要RISC-V高性能核心的后端关键参数对比

一个显著的差异是ROB的深度:香山的256+项ROB远大于C910的64项。更大的ROB允许处理器在遇到Cache miss(通常100+周期的延迟)时继续取指和解码,维持更高的IPC。但更大的ROB也带来了面积、功耗和恢复延迟的增加。C910选择较小的ROB,反映了其更注重面积效率和功耗控制的设计取向。

ROB深度对不同类型工作负载IPC的影响(示意,归一化到ROB=256时的SPECint IPC)。整数工作负载在ROB=128时接近饱和,而访存密集型工作负载在更大的ROB下仍有明显收益。
ROB深度对不同类型工作负载IPC的影响(示意,归一化到ROB=256时的SPECint IPC)。整数工作负载在ROB=128时接近饱和,而访存密集型工作负载在更大的ROB下仍有明显收益。
::: warning 性能分析 6 — 乱序窗口深度的第一性原理推导

ROB的最优深度可以从Little定律(Little’s Law)出发进行第一性原理推导。

步骤1:Little定律应用于ROB

Little定律:L=λ×WL = \lambda \times W,其中LL是系统中的平均"顾客数"(=ROB中的在飞指令数),λ\lambda是到达率(=解码吞吐率),WW是平均逗留时间(=指令从进入ROB到提交的平均时间)。

步骤2:计算平均逗留时间

指令在ROB中的逗留时间由两部分组成:(1) 执行延迟(等待操作数就绪和实际执行),(2) 等待提交(已执行完毕但前面有更老的未完成指令阻塞提交)。

在无Cache miss的理想情况下,平均逗留时间约为10101515周期(典型的执行流水线深度+平均等待时间)。

在有Cache miss的情况下,一次L2 miss会导致miss指令在ROB中逗留约100100周期(DRAM访问延迟),且该miss指令之后的所有指令都被阻塞在ROB中无法提交(即使它们已经执行完毕)。

步骤3:计算ROB需求

设L2 miss率为mm次/条指令,miss延迟为PP周期,正常逗留时间为T0T_0周期,解码吞吐率为λ\lambda条/周期。

平均逗留时间:W=T0+m×P×λ×T0W = T_0 + m \times P \times \lambda \times T_0(近似)

更精确的建模需要考虑miss事件的排队效应。简化情况下:

ROB需求 λ×W=λ×(T0+m×P)\geq \lambda \times W = \lambda \times (T_0 + m \times P)

代入香山参数:λ=4\lambda = 4(实际IPC),T0=12T_0 = 12(正常逗留时间),m=0.005m = 0.005(每200条指令1次L2 miss),P=100P = 100(L2 miss延迟):

L=4×(12+0.005×100)=4×12.5=50L = 4 \times (12 + 0.005 \times 100) = 4 \times 12.5 = 50

步骤4:解释与结论

计算结果显示平均ROB占用仅约50项,远小于256项的ROB深度。但这是平均值——ROB需要足够大以应对最坏情况(连续多次L2 miss)。在连续2次L2 miss的场景中:

Lburst=4×(12+2×100)=4×212=848L_{\text{burst}} = 4 \times (12 + 2 \times 100) = 4 \times 212 = 848

这远超256项ROB的容量,说明在连续miss场景中,256项ROB仍然不够——处理器将因ROB满而停顿前端。这解释了为什么Apple选择630项ROB:更大的ROB可以覆盖更极端的burst miss场景。

但ROB的面积与深度线性增长,256项ROB在大部分时间已经足够(P99利用率约65%–80%),进一步增大到630项的边际IPC收益约5%–8%,不足以证明面积翻倍的代价——这是香山选择256项而非更大ROB的理由。

:::

存储子系统的对比

存储子系统的设计差异反映了各核心的不同性能目标和市场定位。

  • L1 Cache容量:大多数高性能RISC-V核心选择64KB的L1 ICache和DCache,这与ARM高端核心的选择一致(如A76/A77的64KB L1)。SiFive U74等中端核心使用32KB L1。

  • L2 Cache策略:inclusive vs. non-inclusive是一个重要的设计选择。Inclusive策略简化了一致性处理但浪费了有效容量(L2必须包含L1中的所有数据),non-inclusive策略提高了有效容量但增加了一致性逻辑的复杂度。

  • 预取器:高端核心(如香山昆明湖、Veyron)通常配备更复杂的硬件预取器,支持stride和stream模式检测。中端核心可能只有简单的next-line预取。

  • 一致性协议:对于多核配置,Cache一致性协议的选择至关重要。SiFive使用TileLink协议,香山的多核版本使用基于目录的MESI/MOESI协议。

L1 DCache容量对不同工作负载命中率的影响(4路组相联,64B行大小)。香山和A76选择64KB,Apple Firestorm选择128KB。数据库工作负载的工作集更大,对更大Cache更敏感。
L1 DCache容量对不同工作负载命中率的影响(4路组相联,64B行大小)。香山和A76选择64KB,Apple Firestorm选择128KB。数据库工作负载的工作集更大,对更大Cache更敏感。
::: tip 设计提示

专家洞察:存储子系统的"木桶效应"。在高性能处理器的存储子系统设计中,性能受限于最弱的环节。仅增大L1 Cache而不改进L2预取器,L1 miss后的惩罚不变;仅增加Load端口而不扩大TLB,TLB miss率不降反升(更多的并发Load增加了TLB压力)。香山的存储子系统设计体现了均衡投资的原则:2个Load端口+2个Store端口的配置与64KB 8路DCache、128项DTLB、80项LQ和64项SQ形成了合理的匹配——每个参数都处于边际收益递减的拐点附近,没有明显的短板也没有过度投资。这种均衡设计在工艺面积受限的开源项目中尤为重要。

:::

RISC-V ISA对高性能设计的影响

RISC-V ISA的某些特征对高性能处理器设计有独特的影响:

  1. 零寄存器(x0):RISC-V的x0寄存器硬连线为0。在寄存器重命名时,x0不需要分配物理寄存器,写入x0的指令可以被优化掉(例如mv x0, rs1等效于NOP)。这简化了重命名逻辑并节省了物理寄存器。

  2. 无条件标志寄存器:与ARM/x86不同,RISC-V的分支指令直接比较两个寄存器的值(如beq rs1, rs2, offset),而不是依赖条件标志寄存器(flags register)。这消除了对flags寄存器的隐式依赖,简化了寄存器重命名和乱序执行——在x86中,许多指令都会更新flags寄存器,形成额外的假相关。

  3. 32个通用寄存器:RISC-V有32个整数寄存器(包括x0),比x86-64的16个多一倍。更多的逻辑寄存器减少了编译器因寄存器不足而产生的溢出(spill)和假相关,有利于编译器生成更高效的代码。

  4. FENCE指令:RISC-V使用显式的fence指令来实现内存屏障,而不是像x86那样隐式地保证较强的内存顺序。这使得处理器在没有fence指令的代码段中可以更自由地重排序内存操作,提高存储子系统的吞吐量。

  5. LR/SC vs CAS:RISC-V使用LR/SC(Load-Reserved/Store-Conditional)指令对实现原子的读-修改-写操作,而不是x86的CAS(Compare-And-Swap)指令。LR/SC的实现需要在Cache一致性协议中维护reservation set,但其优势是可以避免ABA问题,且在硬件实现上可以更灵活。

硬件描述 17 — RISC-V LR/SC的微架构实现细节

LR/SC(Load-Reserved / Store-Conditional)是RISC-V实现原子操作的核心指令对。其微架构实现比x86的CMPXCHG更灵活但也带来了独特的挑战。

LR指令的实现。LR(Load-Reserved)执行一次正常的Load操作,同时在处理器中设置一个保留标记(reservation)。保留标记通常包括:

  • 保留地址:LR访问的物理地址(或Cache行地址)。

  • 保留有效位:标记当前存在活跃的保留。

  • 保留粒度:RISC-V规范允许保留粒度为实现定义(可以是单个字或整个Cache行)。大多数实现选择Cache行粒度(64字节),因为这与Cache一致性协议的操作粒度一致。

SC指令的实现。SC(Store-Conditional)尝试将数据写入之前LR保留的地址。如果保留仍然有效(没有被其他核心的写入或snoop无效化),SC成功执行写入并返回0(成功)到目标寄存器。如果保留已失效,SC不执行写入并返回非零值(失败)。

保留失效的触发条件。以下事件会使保留失效:

  1. 另一个核心的Store操作写入了同一Cache行(通过一致性协议的Invalidation snoop传播到本核心)。

  2. 本核心执行了另一条LR指令(覆盖了旧的保留)。

  3. 发生了上下文切换或中断(操作系统通过执行一条对保留地址的Store来显式清除保留)。

  4. 某些实现在保留超时(如1024周期)后也会清除保留,以防止死锁。

与Cache一致性协议的交互。LR/SC的保留机制需要与Cache一致性协议深度集成。在MESI协议中:

  • LR将目标Cache行获取到ExclusiveModified状态——即使LR本身是一条读操作。这是因为后续的SC可能需要写入,预先获取Exclusive可以避免SC时再次进行一致性升级(从Shared到Modified)。

  • 如果在LR和SC之间,该Cache行被其他核心的snoop降级为Shared或Invalid,保留自动失效。

面积开销。LR/SC的硬件开销主要是每核一个保留寄存器(约48位:物理地址 + 有效位 + 粒度信息)和保留检查逻辑(在每次snoop到达时比较snoop地址与保留地址)。总面积约0.001  mm20.001\;\text{mm}^2@N5——可以忽略不计。

相比之下,x86的CMPXCHG需要在执行阶段同时执行一次Load(比较当前值)和一条条件Store(如果匹配则写入新值),这需要在一次原子操作中锁定Cache行。x86的实现通常使用Bus LockCache Lock来保证原子性,这些机制比LR/SC的保留标记方案更复杂(特别是在多核环境下)。

设计权衡 3 — RISC-V vs ARM:高性能实现的ISA级差异

从高性能处理器设计的角度看,RISC-V和ARM AArch64在ISA层面的关键差异及其对微架构的影响包括:

  • 解码复杂度:RISC-V(含RVC)有32位和16位两种指令长度,AArch64固定32位。RISC-V的变长指令增加了一定的前端复杂度,但远不及x86。在6-wide解码器中,RISC-V的额外复杂度对面积和时序的影响约5%–10%。

  • 宏融合(Macro-Fusion)机会:AArch64的比较+条件分支是两条独立指令(cmp + b.eq),可以通过宏融合合并为一条μ\muOp。RISC-V的条件分支直接比较两个寄存器(beq rs1, rs2, offset),天然只需要一条μ\muOp,但代价是分支执行单元需要一个比较器。总体上,RISC-V在分支处理上略占优势(一条指令 vs 两条可能融合的指令)。

  • 立即数编码:RISC-V的立即数编码格式比较碎片化(I-type、S-type、B-type、U-type各有不同的立即数位域布局),解码器需要额外的逻辑来重组立即数。AArch64的立即数格式相对统一。这一差异在实际面积影响上很小(<<1%),但增加了设计的复杂度。

  • 向量扩展:RISC-V的RVV和ARM的SVE都采用可变长度向量模型,在微架构实现上面临类似的挑战。两者的关键区别在于RVV的LMUL(寄存器组合)机制,允许将多个向量寄存器组合使用,提供更灵活的向量长度配置,但增加了寄存器重命名和调度的复杂度。

案例研究 4 — RISC-V高性能处理器的第一性原理:为什么不用VLIW

一个自然的问题是:为什么RISC-V高性能处理器(香山、SiFive P870、Ventana Veyron)都选择了乱序超标量架构,而不是更"简单"的VLIW(Very Long Instruction Word)架构?

VLIW的基本思想是将ILP的发现责任从硬件转移到编译器——编译器在编译时分析指令间的依赖关系,将可以并行执行的指令打包成一个"超长指令字",硬件直接并行执行这些指令而无需运行时的依赖检查和乱序调度。这消除了乱序超标量中最复杂和最耗面积的硬件——发射队列的CAM匹配、唤醒逻辑和选择逻辑。

VLIW的结构性缺陷。尽管VLIW在面积效率上有优势,但其性能天花板受限于以下因素:

  1. 无法动态适应Cache miss:乱序超标量处理器在遇到Cache miss时可以继续执行后续无关指令(通过ROB暂存结果),VLIW没有这种能力——一条指令的Cache miss会导致整个VLIW字停顿。这在内存密集型工作负载中会导致严重的性能损失。

  2. 代码膨胀:VLIW需要在运行时资源不足时插入NOP操作来填充空闲的执行槽位。在典型的通用计算代码中,VLIW字中的NOP比例可达40%–60%,这既浪费了I-Cache容量也浪费了内存带宽。

  3. 二进制兼容性:VLIW字的宽度与具体的硬件配置绑定——为4-wide VLIW编译的代码不能在6-wide VLIW上高效运行(多出的2个执行槽位无法利用),反之3-wide代码在4-wide机器上也无法利用额外的执行槽位。这与RISC-V ISA的开放性和可移植性理念直接矛盾。

  4. 编译器的ILP发现能力有限:编译器只能在静态代码中发现ILP,无法跨越运行时的分支(因为不知道分支结果)、无法跨越运行时的内存别名(因为不知道两个指针是否指向同一地址)。乱序超标量通过投机执行(分支预测+推测Load)可以越过这些静态障碍发现更多ILP。

Intel Itanium的教训。Intel的EPIC/Itanium架构是VLIW(及其变体EPIC)在高性能通用处理器中的最知名尝试。Itanium在2001–2017年间共推出了多代产品,但从未在通用计算性能上达到同期x86乱序超标量的水平。Itanium的失败是VLIW在通用高性能计算中不可行的最有力证据。

RISC-V的正确选择。SiFive在P870的设计中明确选择了乱序超标量而非VLIW,这一决策基于上述分析。RISC-V ISA的设计本身也不利于VLIW——RISC-V的可变长度指令(RVC)、动态的向量长度(RVV的vsetvli)和弱内存模型都是为乱序超标量优化的特性。未来的RISC-V高性能处理器预计将继续沿着乱序超标量的路线发展,而非转向VLIW。

性能追赶的路径

到2025年,RISC-V高性能核心的单线程性能大约处于ARM Cortex-A76至A78级别,相当于ARM 2018–2020年的水平。与ARM和x86的最新高性能核心相比,RISC-V仍存在2–3年的性能差距。缩小这一差距的关键路径包括:

  1. 微架构的持续优化:更大的乱序窗口、更精确的分支预测、更高效的存储子系统。这些改进不依赖ISA变化,而是纯粹的微架构工程。

  2. 先进工艺的采用:许多RISC-V核心目前使用12nm–28nm工艺,而ARM/x86的旗舰核心已在3nm–5nm工艺上实现。工艺的升级可以带来显著的频率和能效提升。

  3. 编译器和软件栈的优化:GCC和LLVM对RISC-V的优化仍在快速迭代中。更好的指令调度、寄存器分配和自动向量化可以在不改变硬件的情况下提升实际性能。编译器优化的潜力不可低估——在ARM生态中,从GCC 4.x到GCC 13.x的编译器改进为同一硬件带来了约20%–30%的性能提升。RISC-V的编译器优化目前相当于ARM约2018年的水平,仍有巨大的优化空间。

  4. ISA扩展的标准化:位操作扩展(B扩展)、加密扩展(K扩展)、Profile标准等新扩展的广泛采用可以提升特定工作负载的性能。

硬件描述 18 — RISC-V Profile标准对高性能处理器的影响

RISC-V ISA的模块化设计是其最大的优势之一——但也带来了碎片化的风险。不同厂商的RISC-V处理器可能支持不同的扩展组合,导致软件兼容性问题。RISC-V International推出的Profile标准正是为了解决这一问题。

RVA23 Profile(2024年批准)定义了"应用处理器"级别的RISC-V处理器必须支持的最小功能集:

  • 基础:RV64IMAFDCZicsr_Zifencei(64位整数+乘除+原子+单精度浮点+双精度浮点+压缩指令+CSR访问+指令栅栏)

  • 位操作:Zba(地址计算)、Zbb(基本位操作)、Zbs(单位操作)

  • 向量:V(RVV 1.0向量扩展,VLEN\geq128)

  • 虚拟化:H(Hypervisor扩展)

  • 内存:Svnapot(页面大小扩展)、Svpbmt(页面属性扩展)

  • 加密:Zbkb、Zbkc、Zbkx、Zknd、Zkne、Zknh(AES和SHA加密加速)

RVA23 Profile对高性能处理器设计的影响是:任何声称兼容RVA23的处理器必须实现上述所有扩展。这意味着:

  1. 解码器需要支持所有RVA23中定义的指令——包括位操作、向量、虚拟化和加密指令。解码器的复杂度相比纯RV64GC增加约30%–50%。

  2. 执行后端需要包含向量执行通道(至少128位VLEN)和加密执行单元(AES/SHA加速器)。

  3. 内存子系统需要支持Hypervisor的二级地址翻译(VS-stage和G-stage TLB),增加了TLB的复杂度。

对于香山等研究性处理器,全面支持RVA23 Profile是实现"通用应用处理器"定位的必要条件——缺少任何一个必需扩展都将导致无法运行针对RVA23编译的软件(如未来的Ubuntu RISC-V发行版)。

设计提示

专家洞察:RISC-V的"Android时刻"正在到来。如果说RISC-V的"Linux时刻"是在服务器领域,那么RISC-V的"Android时刻"将发生在移动和嵌入式领域。Android系统对RISC-V的正式支持已经在Google的路线图中——一旦Android RISC-V版本发布,RISC-V处理器将有机会进入全球最大的计算设备市场(每年超过10亿台智能手机和平板)。

但"Android时刻"需要的不仅仅是ISA支持——还需要:(1) 性能达到ARM Cortex-A78/X1级别(当前Android旗舰的最低标准);(2) GPU生态的支持(Android的UI和游戏严重依赖GPU);(3) 基带/ISP/NPU等SoC级IP的RISC-V版本。这些条件预计在2027–2029年间逐步满足。

从微架构角度看,RISC-V移动处理器将面临与ARM Cortex-X系列相同的挑战——如何在严格的功耗预算(3–5W TDP)下最大化单线程IPC。本章分析的香山昆明湖的各种微架构技术(TAGE-SC-L预测、FTQ解耦、分布式发射队列等)将直接应用于未来的RISC-V移动处理器中。从第 43.0 章中Cortex-X系列的演进经验来看,RISC-V移动核心的解码宽度预计将从当前的6-wide逐步扩展到8–10-wide,ROB从256项扩展到400+项——走过与ARM类似但更加压缩的演进路径。

开源与商业的协同

RISC-V高性能处理器生态的一个独特特征是开源设计与商业设计的协同发展。香山和BOOM等开源项目为学术研究和人才培养提供了平台,SiFive和Ventana等商业公司则将研究成果转化为可部署的产品。

这种协同关系的具体体现包括:

  • 人才流动:开源项目培养的处理器设计人才流向商业公司,将开源社区积累的经验带入商业产品开发。

  • 技术外溢:开源项目中验证的微架构技术(如DiffTest验证方法论)被商业项目采用。

  • 基准工具:开源社区开发的性能分析工具和基准测试框架被商业和开源项目共同使用,促进了公平的性能比较和竞争。

  • ISA扩展的共同演进:商业公司和开源社区共同参与RISC-V ISA扩展的标准化过程,确保新扩展在实现上的可行性。

设计提示

专家洞察:开源处理器的"Linux时刻"。回顾Linux操作系统的发展历程,我们可以看到RISC-V开源处理器可能正在经历类似的"Linux时刻"。1990年代,Linux被视为一个学术玩具——性能不及商业UNIX,软件生态几乎为零,企业用户不会认真考虑。到2000年代,Linux的性能和可靠性逐步追赶,在Web服务器和科学计算领域开始获得实际部署。到2010年代,Linux在服务器、云计算和嵌入式领域已经占据主导地位。

RISC-V开源处理器(特别是香山)正处于类似的早期阶段。香山的IPC已经接近ARM A76(约2018–2019年的商业水平),相当于Linux在2000年代初期的状态——性能已经"够用"但还不够优秀。如果RISC-V处理器的性能每2年提升一代(这与当前的发展速度一致),预计到2027–2028年可以追平ARM的最新核心水平。届时,RISC-V的开放性和可定制性将成为其相对于ARM的差异化竞争优势——正如Linux的开放性最终成为其相对于商业UNIX的核心优势一样。

这一类比的局限在于:处理器设计的门槛远高于操作系统开发。Linux的贡献者只需要一台PC就可以编写和测试代码,而处理器设计需要昂贵的EDA工具和流片费用。但FPGA原型验证(如在Xilinx VU19P上运行完整的香山RTL)和云端EDA工具的普及正在降低这一门槛。

RISC-V高性能处理器的关键里程碑预测。基于当前的发展轨迹,以下是对未来关键里程碑的预测:

  • 2025–2026年:香山第三代(南湖?)在先进工艺(如7nm/5nm)上流片,目标IPC达到ARM Cortex-A78/X1级别。多核版本(4–8核)支持完整的Cache一致性。

  • 2026–2027年:首款RISC-V处理器进入SPEC CPU 2017 Top-10排名(可能通过Ventana或SiFive的商业产品)。RISC-V编译器优化达到ARM GCC 10.x的成熟度。

  • 2027–2028年:RISC-V高性能核心的IPC追平ARM Cortex-X3/X4级别。首个基于RISC-V的超级计算机进入Top500排名(可能基于中国大陆的自主设计)。

  • 2028–2030年:RISC-V在特定领域(AI推理、密码学、网络处理)的处理器中通过自定义指令扩展取得对ARM/x86的性能优势,证明开放ISA可定制性的价值。

挑战与机遇

RISC-V高性能处理器面临的主要挑战包括:

  1. 软件生态:与x86和ARM相比,RISC-V的软件生态仍然不够成熟。高性能计算、数据库、AI框架等关键应用的RISC-V优化仍在进行中。

  2. 验证成熟度:商业处理器公司拥有数十年积累的验证基础设施和测试向量。RISC-V处理器项目需要时间来建立同等水平的验证能力。

  3. 物理实现经验:先进工艺下的物理设计(时钟树、电源网络、信号完整性等)需要丰富的经验和专有的EDA工具。这方面的知识积累无法通过开源来加速。

  4. 内存和I/O的生态:高性能处理器需要与DDR5/HBM内存、PCIe Gen5/CXL互连等高速I/O标准配合。这些接口的PHY设计和控制器开发需要大量的投入。

同时,RISC-V高性能处理器也拥有独特的机遇:

  • 领域特定优化:RISC-V ISA的可扩展性允许为特定领域(AI推理、密码学、信号处理)添加自定义指令扩展,这是封闭ISA无法提供的竞争优势。

  • 异构集成:RISC-V核心可以与GPU、NPU、FPGA等加速器在同一SoC中异构集成,通过自定义指令实现处理器核心与加速器之间的高效协作。

  • 自主可控:对于部分国家和地区,RISC-V提供了不受单一供应商控制的ISA选择,是实现半导体供应链自主可控的重要路径。

案例研究 5 — RISC-V自定义指令扩展的竞争优势:以AI推理为例

RISC-V ISA的可定制性是其相对于ARM和x86的最独特竞争优势。RISC-V规范预留了大量的自定义指令编码空间(Custom-0到Custom-3,共4个32位操作码组),允许处理器设计者添加领域特定的硬件指令而不影响标准指令的兼容性。

案例:INT8矩阵乘法加速。AI推理中最常见的操作是INT8精度的矩阵乘法。标准的RVV指令可以实现向量化的INT8乘累加,但每次操作只处理VLEN/8个元素。通过自定义指令扩展,可以添加一条专用的矩阵乘法指令——例如mmaqa.b rd, rs1, rs2,一次完成一个4×44 \times 4的INT8矩阵乘法块。

性能对比

方法每周期INT8 MACs面积@N5能效(MACs/mW)
标量(mul+add)1基线基线
RVV (VLEN=128)16+0.1mm2^214×14\times
自定义矩阵指令64+0.3mm2^240×40\times

自定义矩阵指令的吞吐量是RVV的4倍,面积仅增加0.2mm2^2,能效是标量的40倍。这种领域特定的优化在ARM和x86的封闭ISA中无法实现——ARM的SME(Scalable Matrix Extension)虽然提供了类似功能,但所有ARM实现都必须遵循相同的SME规范,无法针对特定应用场景进行定制。

编译器集成。自定义指令的软件支持通过GCC/LLVM的内建函数(built-in/intrinsic)机制实现。处理器设计者为每条自定义指令定义一个C内建函数(如__riscv_mmaqa_b()),编译器直接将该函数映射为对应的机器指令。这种方法不需要修改编译器的核心代码——只需添加目标无关的intrinsic定义。

挑战。自定义指令的主要挑战是软件碎片化——不同厂商的自定义指令互不兼容,使用自定义指令编写的代码只能在特定的处理器上运行。RISC-V社区通过Profile标准和扩展标准化流程来缓解这一问题——一旦某个自定义扩展被证明有广泛的应用价值,就可以被提交为RISC-V标准扩展,供所有厂商实现。

案例研究 6 — RISC-V高性能核心的设计方法论对比

本章讨论的RISC-V高性能核心在设计方法论上呈现出有趣的多样性:

  • 香山:使用Chisel(基于Scala的硬件描述语言),强调参数化和高级抽象。Chisel允许使用面向对象编程和函数式编程的范式来描述硬件,生成器(generator)模式使得复杂的参数化结构更易于表达。代价是Chisel生成的Verilog可读性较差,且Chisel工具链的成熟度不及传统EDA工具。

  • 平头哥C910:使用传统的Verilog HDL,与工业界主流的设计流程完全兼容。Verilog的优势是工具链成熟、工程师群体庞大、物理实现工具支持良好。代价是参数化和抽象能力不如Chisel,大规模的微架构探索需要更多的手动修改。

  • BOOM:同样使用Chisel,但更强调作为研究平台的灵活性。BOOM的Chisel代码中大量使用了Scala的类型系统和模式匹配来实现模块的可插拔替换。

  • SiFive P系列:使用SiFive内部的Chisel变体和自动化工具链。SiFive作为Chisel的主要推动者,其工具链的成熟度在业界领先。

  • Ventana Veyron:据信使用传统的RTL设计方法(SystemVerilog),符合大型处理器设计团队的传统工作流程。

这种方法论的多样性反映了处理器设计行业正在经历的一场变革:传统的手写RTL方法论在面对越来越复杂的微架构时遇到了生产力瓶颈,而以Chisel/SpinalHDL为代表的高级硬件描述语言正在提供新的解决方案。但高级HDL的工具链成熟度和工程师接受度仍然是其广泛采用的障碍。

香山昆明湖的向量扩展实现细节

昆明湖的RVV 1.0向量扩展实现是其微架构设计中最复杂的部分之一。以下深入分析其实现的几个关键方面。

向量寄存器重命名。RVV 1.0定义了32个向量寄存器(v0–v31),每个寄存器的宽度由VLEN参数决定(昆明湖为128位)。与标量寄存器重命名类似,向量寄存器也需要通过物理向量寄存器文件(Vector PRF)进行重命名以支持乱序执行。

RVV的LMUL(Vector Register Grouping)机制增加了重命名的复杂度。当LMUL >1> 1时,多个逻辑向量寄存器被"分组"为一个更宽的向量操作数。例如,LMUL=2时,v0和v1被视为一个256位的操作数,LMUL=4时v0–v3被视为一个512位的操作数。在物理寄存器文件中,这需要分配连续的多个128位物理寄存器。

昆明湖的向量PRF约有64–96个128位物理寄存器。当LMUL=1时,每个向量μ\muop分配1个物理寄存器;LMUL=2时分配2个连续物理寄存器。物理寄存器的连续性约束增加了分配逻辑的复杂度——空闲列表需要支持"连续分配"(allocate NN contiguous registers)操作,这比标量PRF的单项分配复杂得多。

向量访存的实现。RVV支持三种向量访存模式:unit-stride(连续访问)、strided(固定步长访问)和indexed(间接访问/gather-scatter)。三种模式的硬件实现复杂度差异巨大:

  • Unit-stride(如vle32.v):一次加载连续的VLEN/SEW\text{VLEN}/\text{SEW}个元素(SEW为元素宽度)。在VLEN=128、SEW=32的配置下,每次加载4个32位元素,总宽度128位。这可以映射为一次128位的DCache读取,与标量的128位load类似,实现简单。

  • Strided(如vlse32.v):以固定步长访问不连续的元素。在VLEN=128、SEW=32、stride=16字节的配置下,需要4次独立的32位DCache读取(每次读取一个元素)。这需要向量访存单元在4个周期内生成4个地址并依次访问DCache——吞吐量比unit-stride低4倍。

  • Indexed/Gather(如vluxei32.v):使用另一个向量寄存器中的元素作为索引/偏移地址。这是最慢的模式——每个元素的地址完全独立,需要VLEN/SEW\text{VLEN}/\text{SEW}次独立的DCache访问。在VLEN=128、SEW=32下需要4次独立访问。

昆明湖的向量访存单元复用了标量MemBlock的load/store端口。一条向量load指令被分解为多条标量load μ\muops,每条μ\muop通过一个load端口执行。对于unit-stride模式,由于地址连续,可以发起一次宽load(128位)而非多次窄load,效率更高。

性能分析 7 — 向量扩展对昆明湖IPC的贡献

RVV向量扩展在不同工作负载中对IPC的贡献差异很大:

  • 矩阵乘法(DGEMM):RVV可以将每周期的浮点操作数从2(标量FMA的2个操作数)提升到2×VLEN/64=2×2=42 \times \text{VLEN}/64 = 2 \times 2 = 4(128位向量FMA的4个双精度操作数)。理论吞吐量翻倍。实际中由于访存带宽限制和寄存器pressure,RVV在DGEMM上的加速比约为1.5–1.8倍。

  • 图像处理(如高斯模糊):RVV可以同时处理128/8=16128/8 = 16个8位像素(unit-stride load + 向量ALU),相比标量的每次1个像素,理论加速比为16倍。实际中受限于内存带宽和指令开销,加速比约为8–12倍。

  • 编译器自动向量化:GCC和LLVM对RISC-V RVV的自动向量化支持仍在快速迭代中。在SPEC CPU 2017的部分浮点基准上,自动向量化可以带来20%–80%的性能提升(vs纯标量代码)。手动向量化的intrinsics通常比自动向量化多获得20%–50%的额外性能。

RISC-V处理器的验证方法学

高性能RISC-V处理器的设计验证是一个核心挑战。商业处理器公司(如Intel、AMD、ARM)拥有数十年积累的验证基础设施和专有测试向量,而RISC-V开源项目需要从零开始建立验证能力。

DiffTest方法论。香山项目开发的DiffTest(Differential Testing)方法论已成为开源RISC-V处理器验证的事实标准。DiffTest的核心思想是:

  1. 将被测试的处理器RTL(DUT,Device Under Test)与一个经过充分验证的参考模拟器(REF,如NEMU)进行逐条指令对比

  2. DUT和REF从相同的初始状态开始执行同一段程序。每当DUT提交(commit)一条指令时,REF也执行该指令,然后比较两者的体系结构状态(所有通用寄存器、PC、CSR等)是否一致。

  3. 如果检测到不一致,DiffTest立即报告并输出导致不一致的指令和上下文信息,帮助设计者定位bug。

DiffTest的效率远高于传统的ISA级随机测试(如riscv-tests、riscv-torture):

  • ISA级测试只验证单条指令的正确性,无法覆盖指令之间的微架构交互(如Store-to-Load Forwarding、分支误预测恢复、中断处理中的指令重排序等)。

  • DiffTest对每一条指令都进行全状态比较,可以发现任何导致体系结构状态偏差的bug——包括极其罕见的边界条件(如同时发生的中断和异常、特定的Cache替换导致的一致性问题等)。

香山DiffTest验证框架的工作原理。DUT(待测设计)和REF(参考模拟器)执行同一程序,每提交一条指令就比较全部体系结构状态。任何不一致立即报告。
香山DiffTest验证框架的工作原理。DUT(待测设计)和REF(参考模拟器)执行同一程序,每提交一条指令就比较全部体系结构状态。任何不一致立即报告。

DiffTest的工程实践。在香山的实际开发中,DiffTest已经发现了超过200个微架构bug,涵盖以下类别:

  • 分支预测恢复(约30%):误预测恢复后体系结构状态不正确——如RAT快照恢复遗漏了某些寄存器、冲刷范围不完整等。

  • 存储子系统(约25%):Store-to-Load转发的地址匹配逻辑错误、Store Buffer的提交顺序错误、DCache的一致性违规等。

  • 特权级切换(约20%):异常/中断的处理逻辑错误——如M模式到S模式的切换中CSR更新不完整、中断优先级的判定逻辑错误等。

  • 向量扩展(约15%):RVV指令的掩码处理错误、LMUL>>1时的寄存器分组边界条件、向量异常的精确性等。

  • 其他(约10%):时钟中断的计数误差、浮点异常的处理顺序等。

这些bug的分布反映了高性能处理器设计中的复杂性层次:分支预测恢复和存储子系统的交互是最容易出错的区域,因为它们涉及推测执行和顺序恢复的微妙交互——这些交互在简单的单元测试中很难覆盖,但DiffTest通过运行完整的操作系统和应用可以自然地触发。

DiffTest的局限是它只能发现功能性bug(导致错误结果的bug),不能发现性能bug(不影响正确性但影响性能的bug,如预取器不工作、调度器饿死某类指令等)和功耗bug(不必要的功耗消耗)。性能和功耗的验证需要额外的方法学——如性能计数器分析、功耗仿真等。

系统级验证。除了DiffTest外,高性能RISC-V处理器的验证还需要系统级测试——在处理器上运行完整的操作系统和应用程序。香山和C910都通过在RTL仿真(或FPGA原型)上运行Linux内核启动和SPEC基准测试来验证系统级的正确性。这种验证可以发现DiffTest无法覆盖的bug——如内存映射I/O(MMIO)的处理错误、中断控制器(PLIC/CLINT)的交互问题、页表遍历的边界条件等。

性能分析 8 — 香山昆明湖 vs Cortex-A76:IPC分解对比

将香山昆明湖与ARM Cortex-A76在SPEC CPU 2006整数基准上的IPC进行分解对比,可以精确定位两者的性能差异来源。

步骤1:基本微架构参数对比

参数昆明湖Cortex-A76
解码宽度6-wide4-wide
ROB256+128
整数ALU43
Load端口22
L1 ICache64KB64KB
L1 DCache64KB64KB
L2 Cache1MB256–512KB
分支MPKI\sim3.0\sim3.5

步骤2:IPC贡献因子分解

假设一个理想的无限资源处理器的IPCideal=8\text{IPC}_{\text{ideal}} = 8(受限于真正的数据依赖链),各资源限制对IPC的"折扣"如下:

IPC限制因子昆明湖A76
解码宽度限制×0.75\times 0.75×0.50\times 0.50
分支误预测惩罚×0.90\times 0.90×0.88\times 0.88
Cache miss惩罚×0.92\times 0.92×0.88\times 0.88
数据依赖链×0.85\times 0.85×0.85\times 0.85
发射/执行资源×0.90\times 0.90×0.88\times 0.88
综合IPC3.8\textbf{3.8}2.6\textbf{2.6}

步骤3:分析差异来源

昆明湖的IPC领先A76约46%。主要贡献因素:

  • 解码宽度(6 vs 4):贡献约50%的IPC差距。6-wide解码在平均基本块长度5–7条指令的RISC-V代码中,有效利用率约为75%,而4-wide约为50%(更容易被分支截断)。

  • ROB深度(256+ vs 128):贡献约20%的差距。更大的ROB允许更好地隐藏L2 miss延迟。

  • L2 Cache(1MB vs 256–512KB):贡献约15%的差距。更大的L2直接降低了L2 miss率。

  • 分支预测精度(MPKI 3.0 vs 3.5):贡献约10%的差距。

  • 其他优化:贡献约5%。

步骤4:工艺归一化后的结论

A76在7nm工艺下可达2.8GHz,昆明湖在14nm下约2.0GHz。综合性能(IPC×\times频率):昆明湖约3.8×2.0=7.63.8 \times 2.0 = 7.6分,A76约2.6×2.8=7.32.6 \times 2.8 = 7.3分。两者基本持平——说明昆明湖通过更高的IPC弥补了工艺的差距。如果昆明湖在7nm下实现,频率有望达到2.8–3.0GHz,综合性能将超越A76约30%–40%。

RVV实现策略的深度分析

RISC-V向量扩展(RVV 1.0)的硬件实现存在两种根本不同的策略:时间复用(窄数据通路,多周期执行同一向量操作)和空间展开(宽数据通路,单周期完成)。不同的策略对寄存器重命名、发射队列和执行单元的设计产生了深远影响。

设计权衡 4 — RVV的时间复用 vs 空间展开

时间复用方案(如昆明湖的VLEN=128实现):

在VLEN=128的实现中,处理一个LMUL=4的向量操作(等效512位)需要将操作分解为4条128位μ\muops,每条依次在128位宽的向量执行通道中执行。

  • 优势:向量执行通道与标量NEON/FP通道完全共享,无额外面积。向量寄存器文件紧凑(32×128=409632 \times 128 = 4096位 = 512字节)。重命名逻辑与标量相似,只需为128位寄存器分配物理寄存器。

  • 劣势:LMUL=4时一条向量指令占用4个μ\muop槽位,有效降低了6-wide解码的利用率。向量吞吐量受限于标量执行通道的带宽。

  • 对发射队列的影响:每条向量指令被分解为LMUL条μ\muops,每条μ\muop独立占用一个发射队列条目。LMUL=4时,一条向量指令占用4个发射队列条目——在12–16项的发射队列中,这可能导致队列快速填满。

空间展开方案(如假设的VLEN=512实现):

在VLEN=512的实现中,每条向量指令直接在512位宽的执行通道中单周期完成。

  • 优势:向量吞吐量最大化。LMUL=1时每条指令处理512位数据。发射队列不会因向量μ\muop分解而快速填满。

  • 劣势:512位执行通道的面积约为128位的4倍(约0.3  mm20.3\;\text{mm}^2 @N5工艺)。向量寄存器文件面积为128位的4倍(32×512=1638432 \times 512 = 16384位)。需要512位的旁路网络和结果总线——旁路网络的面积与宽度的平方成正比。

  • 对寄存器重命名的影响:物理向量寄存器从128位扩展到512位,但数量不变。重命名逻辑的复杂度与寄存器数量而非宽度相关,因此重命名逻辑本身不会更复杂。但空闲列表和检查点的存储量增加(每个物理寄存器的数据更大)。

量化对比:以DGEMM(双精度矩阵乘法)为例,假设内核的向量化率为80%:

指标VLEN=128VLEN=512
每条向量指令的μ\muops1(LMUL=1)/ 4(LMUL=4)1
向量吞吐(双精度元素/周期)2(LMUL=1)/ 8(LMUL=4)8
面积(向量单元,@N5)\sim0.1 mm2^2\sim0.4 mm2^2
DGEMM加速比(vs标量)1.5×1.5\times3.5×3.5\times6×6\times7×7\times

结论:空间展开方案在向量密集型工作负载中性能约为时间复用方案的2倍,但面积增加约4倍。对于移动处理器(向量指令占比<<10%),时间复用方案的面积效率远优于空间展开。对于HPC处理器(向量指令占比>>50%),空间展开方案更优。昆明湖选择VLEN=128的时间复用方案,是其"通用高性能核心"定位的合理选择。

本章小结

本章深入分析了当前最具代表性的高性能RISC-V处理器设计。主要结论如下:

  1. 香山处理器的微架构创新:香山以其完全开源的策略和不断提升的性能,成为RISC-V高性能核心的重要标杆。其FTQ解耦前端设计(44.1.3 节)、TAGE-SC-L分支预测器(与第 15.0 章中讨论的算法直接对应)和MemBlock双端口存储设计都是值得深入研究的微架构创新。昆明湖的IPC已接近ARM Cortex-A76级别,验证了RISC-V ISA在高性能领域的可行性。

  2. RISC-V ISA对微架构的独特影响:无条件标志寄存器消除了flags的假依赖(44.4.4 节),32个通用寄存器减少了溢出,弱内存模型(RVWMO)为存储子系统的激进优化提供了更大的自由度。但RVC变长指令增加了前端复杂度,RISC-V的分支指令需要内置比较器增加了分支单元延迟。

  3. 商业与开源的互补:SiFive P系列展示了商业RISC-V核心IP如何在可配置性与性能之间取得平衡(44.2 节)。Ventana Veyron的chiplet架构代表了RISC-V进入服务器市场的大胆尝试(44.3.1 节)。平头哥C910/C920展示了从中等性能核心向高性能核心持续演进的路径。BOOM作为学术研究平台,为微架构创新提供了不可替代的基础设施。

  4. RVV向量扩展的实现策略:时间复用(VLEN=128)和空间展开(VLEN=512+)的权衡取决于目标工作负载的向量化比例。移动/通用处理器适合窄VLEN+LMUL的时间复用方案,HPC处理器适合宽VLEN的空间展开方案。

  5. 开源验证方法学:DiffTest(差分测试)方法论已成为开源RISC-V处理器验证的事实标准(44.5.5 节),通过与参考模拟器的逐条指令对比实现了高效的功能验证。

设计提示

本章分析了单线程RISC-V高性能处理器的微架构设计。一个自然的问题是:RISC-V处理器是否需要SMT(同时多线程)?在第 43.0 章中,我们看到ARM Cortex-X系列和Apple M系列均不支持SMT,而Intel从2002年的Pentium 4开始就广泛使用SMT。SiFive P870是少数支持SMT的RISC-V核心之一。

SMT的核心价值在于提高执行资源的利用率——当单线程的ILP不足以充分利用所有执行端口时,第二个线程可以填补空闲的执行槽位。但SMT也带来了安全风险(如Spectre-SMT变体利用共享缓存和执行端口进行侧信道攻击)和单线程性能的下降(ROB和Cache被两个线程分享)。

下一章(第 45.0 章)将深入分析SMT的微架构实现,包括资源分区策略、线程调度策略和安全缓解措施。读者将看到,RISC-V ISA的某些特征(如更多的通用寄存器、更简单的特权级模型)使得RISC-V上的SMT实现比x86更加直接——但同样面临安全与性能的根本权衡。

RISC-V向量扩展与ARM SVE/x86 AVX的对比

RISC-V的向量扩展(RVV)与ARM SVE和x86 AVX在设计理念上存在根本性差异。这些差异对处理器微架构的实现有深远影响。

特性RVV 1.0ARM SVE/SVE2x86 AVX-512
向量长度可变(128–65536b)可变(128–2048b)固定512b
长度设置vsetvli指令硬件VLEN编译时固定
寄存器组合LMUL=1/2/4/8
掩码寄存器v0 + 8b掩码P0–P15k0–k7
逻辑寄存器数323232
Gather/Scatter支持支持支持

RVV、SVE/SVE2与AVX-512的ISA级对比

RVV的LMUL机制。RVV独有的LMUL(Register Grouping Multiplier)机制允许将多个逻辑向量寄存器组合为一个更大的向量操作数。例如LMUL=4时,v0–v3被视为一个4×VLEN4 \times \text{VLEN}位的向量。这一机制在VLEN较小的实现上(如128位)尤为有用——通过LMUL=4,128位的硬件可以处理等效512位的向量操作。

但LMUL对微架构的影响是复杂的。LMUL=4意味着一条向量指令消耗4个逻辑寄存器(v0–v3),可用的逻辑向量寄存器从32个减少到32/4=832/4 = 8个组。在寄存器pressure较高的代码中,LMUL=4可能导致严重的寄存器溢出(spill),反而降低性能。编译器需要在LMUL值和寄存器pressure之间做出平衡——这增加了自动向量化的难度。

对微架构实现的影响对比。从处理器设计的角度看:

  • 解码复杂度:AVX-512的固定512位操作使解码器只需处理一种向量宽度。RVV和SVE的可变长度需要解码器根据vtype/VL动态调整操作行为,增加了控制逻辑的复杂度。

  • 寄存器文件面积:AVX-512需要32个512位寄存器(2048字节)。RVV在VLEN=128时只需要32个128位寄存器(512字节),面积仅为AVX-512的25%。但LMUL机制要求每条指令可能写入多个物理寄存器,增加了重命名逻辑的复杂度。

  • 执行单元设计:AVX-512要求原生的512位数据通路(或256位double-pump),面积和功耗最大。RVV在VLEN=128时只需128位通路,面积最小。SVE在VLEN=128时与RVV相当,但SVE的最大VLEN可达2048位,需要更宽的物理通路支持。

  • 编译器支持:AVX-512的固定宽度最容易被编译器利用(intrinsics映射直接)。RVV和SVE的VLA模型更灵活但自动向量化更困难——编译器需要生成与向量长度无关的代码,使用谓词掩码处理尾部元素。

设计提示

RVV的设计理念是"一次编写,处处运行"——同一段向量代码可以在VLEN=128的嵌入式处理器和VLEN=1024的HPC处理器上都正确运行并获得合理的性能。这种可移植性在x86的固定宽度SIMD世界中是不存在的——为AVX-512优化的代码不能在只支持AVX2的处理器上运行,需要显式的代码分支(runtime dispatch)。RVV的VLA模型通过vsetvli指令在运行时查询硬件的VLEN,消除了这种代码分支需求。对于RISC-V生态的碎片化现状(不同厂商的VLEN可能不同),VLA模型是确保软件可移植性的关键设计选择。

正文与图片:CC BY-NC-SA 4.0 · 本仓库少量站点配置代码:MIT