1. 项目概述从手册到实战拆解PowerPC 601的硬核内功如果你和我一样是从那个奔腾、K6处理器风靡的年代过来的硬件爱好者或者现在正从事嵌入式、游戏机模拟器开发那么“PowerPC”这个名字一定不会陌生。它不像x86那样无处不在却深深烙印在苹果Power Macintosh、任天堂GameCube、索尼PS3等经典产品的灵魂里。今天我们不谈那些宏大的产品故事而是把目光聚焦到这一切的基石之一——PowerPC 601处理器。手头正好有一份当年的官方用户手册里面密密麻麻的寄存器表、指令分类和缓存框图对新手来说可能像天书但对想深入理解RISC架构、甚至动手写点底层代码比如模拟器或者Bootloader的朋友来说这就是一份宝藏地图。这份手册的节选主要勾勒了PowerPC 601的三个核心硬件模块寄存器模型、指令集架构和缓存与内存管理单元。简单来说寄存器是CPU的“工作台”指令集是它听得懂的“语言”而缓存则是连接它与慢速内存的“高速缓冲区”。601作为PowerPC家族的第一代量产成员承上启下它继承了IBM POWER架构的一些特性以保持兼容又确立了未来PowerPC架构的基本规范。理解它你就能理解很多RISC设计的精髓比如为什么指令长度固定为32位、为什么要有用户和特权模式之分、缓存一致性协议MESI到底在忙活什么。接下来的内容我会带你像读一份“产品拆解报告”一样把这些手册里的术语和框图还原成工程师设计时的考量和我们编程时需要注意的细节。无论你是计算机体系结构的学生还是对复古硬件、底层开发感兴趣的极客相信都能从中找到乐趣和干货。我们不仅要知道601有什么更要明白它为什么这么设计以及在实际中如何与之打交道。2. 核心细节解析601的寄存器世界——权限、角色与访问之道手册的图1-3是一张经典的“编程模型”图但它不仅仅是寄存器的罗列更是一张清晰的权限和功能地图。理解这张图是理解601如何工作的第一步。2.1 用户与特权模式一道关键的安全鸿沟PowerPC 601和大多数现代处理器一样通过机器状态寄存器中的特权级位来划分两个世界用户模式和特权模式。这不仅仅是权限高低的问题更是系统稳定性的基石。用户模式普通应用程序运行的状态。在此模式下程序只能访问一部分资源主要是通用寄存器、浮点寄存器、链接寄存器、计数寄存器等。它的指令集也被限制无法执行那些会直接影响硬件状态如管理缓存、进行地址转换的敏感指令。这种设计有效防止了用户程序因bug或恶意行为而破坏整个系统。特权模式操作系统内核、驱动等系统级软件运行的状态。在此模式下软件可以访问所有寄存器包括那些用于内存管理、异常处理、调试和处理器控制的特殊功能寄存器。例如写实时时钟寄存器、修改块地址转换寄存器、访问硬件实现寄存器等操作都必须在特权模式下进行。注意在编写系统软件如操作系统内核时必须时刻注意当前所处的模式。在用户模式下尝试执行mtspr指令去写一个特权SPR会直接触发一个“特权指令”异常。这是系统实现保护机制的关键。2.2 特殊功能寄存器详解601的独特性与兼容性手册中特别强调了一些“601独有”或“实现特定”的寄存器这些是理解601区别于其他PowerPC处理器的关键。MQ寄存器这是一个典型的兼容性包袱。MQ是一个32位寄存器用于辅助乘除法和长位移/循环移位指令。例如在执行32位乘法时64位的结果的高32位可能会存放在MQ中在做64位移位时MQ会和某个GPR配对使用。它的存在主要是为了与更早的IBM POWER架构的指令集保持二进制兼容。在纯粹的PowerPC架构中这类操作有更优雅的解决方式比如使用多个GPR。在编程时如果你在处理遗留的POWER架构代码就需要特别注意MQ的用法。实时时钟寄存器RTCU和RTCL这对寄存器提供了一个64位的实时时钟计数器。手册里一个非常精妙的设计细节是读和写操作使用了不同的SPR编号。用户程序可以用一组编号SPR4, SPR5来读取时钟值但无法写入。只有特权程序用另一组编号SPR20, SPR21才能写入。这种硬件级的保护防止了用户程序随意篡改系统时间基准确保了计时服务的可靠性。块地址转换寄存器BAT寄存器是一种高效的地址翻译机制用于将一大段连续的逻辑地址直接映射到物理地址无需经过复杂的页表查询。601有4对IBAT指令BAT而完整的PowerPC架构定义了8对IBAT和8对DBAT数据BAT。601将其IBAT当作统一的BAT使用这意味着它用同一套BAT条目来翻译指令和数据的访问。这在设计简单、对性能要求不极致的场景下是可行的但也意味着软件无法为指令和数据设置不同的块映射属性如只执行、只读灵活性有所降低。硬件实现寄存器HID0-HID2, HID5, HID15这些寄存器是给硬件调试和系统设计人员准备的“后门”。例如HID0包含使能检查停止、控制缓存行为如禁用缓存、使能写分配的位。HID2在601中用作指令地址断点寄存器用于调试。HID15存放处理器标识这在多处理器系统中至关重要用于区分不同的CPU核心。 手册明确警告这些寄存器的定义在不同PowerPC处理器间可能不一致。这意味着为601写的底层代码如果严重依赖HID寄存器移植到其他PowerPC芯片如603、740、750时可能需要修改。这是编写可移植性高的固件时需要警惕的。2.3 寄存器访问方式显式与隐式访问这些寄存器主要有两种方式显式访问使用专门的指令如mtspr和mfspr。你需要知道目标寄存器的SPR编号如图1-3左侧所示。这是最直接、最常用的方式。隐式访问作为指令执行的一部分自动发生。例如执行bl分支并链接指令时返回地址会自动存入链接寄存器执行比较指令时结果会设置条件寄存器的相应位进行乘除运算时MQ寄存器可能被自动使用。理解这两种方式有助于你在阅读汇编代码或调试时清楚寄存器的值是如何变化的。3. 指令集与寻址模式RISC哲学的具象化PowerPC是经典的RISC设计其指令集设计充分体现了RISC的核心理念简单、规整、利于流水线和超标量执行。3.1 指令格式与分类为何固定32位所有PowerPC指令都是32位长且字对齐。这个设计带来了几个巨大的优势简化取指取指令单元每次固定取4个字节无需判断指令边界硬件设计简单。并行解码因为格式规整操作码、寄存器域、立即数域的位置相对固定解码器可以更容易地进行并行解码为后续的多发射超标量做好准备。简化流水线在流水线的各个阶段对指令长度的处理是统一的减少了控制逻辑的复杂性。手册将指令分为几大类这个分类主要是功能性的并不严格对应执行单元整数指令包括算术、比较、逻辑、移位/循环。这是所有计算的基础。浮点指令包括算术、乘加、舍入转换、比较和状态控制。601的浮点单元性能在当时相当强劲。加载/存储指令这是RISC架构的关键特征——只有load和store指令可以访问内存所有计算都在寄存器间进行。这种“加载-存储”架构使得指令功能纯粹流水线更容易调度。手册特别提到了lwarx和stwcx.这对指令它们是实现原子操作如锁、信号量的基石通过“加载保留-条件存储”的机制能在多处理器环境下安全地更新共享内存。流控制指令包括分支、陷阱、条件寄存器操作。它们改变了指令执行的顺序。处理器控制指令用于同步内存访问sync,isync、管理特殊寄存器mtmsr,mfmsr、管理TLB等。是操作系统内核的利器。内存控制指令用于管理缓存、段寄存器。例如dcbst数据缓存块存储用于将修改过的缓存数据写回内存icbi指令缓存块无效用于使指令缓存失效在多处理器一致性维护中至关重要。3.2 寻址模式极简主义的效率PowerPC的寻址模式只有两种简单到令人发指EA (rA|0) offset基址寄存器rA如果为0则代表值0加上一个16位的有符号立即数偏移量。EA (rA|0) rB基址寄存器加上另一个索引寄存器rB的值。这种极简设计减少了地址计算单元的复杂度使得有效地址计算能在一个时钟周期内完成对于保持流水线畅通至关重要。所有的复杂地址计算比如数组索引、结构体成员访问都通过编译器在生成代码时利用多条简单的算术指令和这两种寻址模式组合来完成。3.3 601的指令集特色继承、扩展与差异601的指令集是PowerPC架构的一个子集并做了一些扩展和调整未实现的指令附录C列出了一些PowerPC架构定义但601未实现的指令这些通常被定义为“可选”指令。在601上执行它们会触发“非法指令”异常。POWER兼容指令为了运行旧的POWER架构二进制代码601实现了一部分POWER指令附录B这主要依赖于前面提到的MQ寄存器等兼容性设计。特定指令行为差异一些缓存控制指令在601上可能功能不全或是空操作。这是因为601采用统一缓存而PowerPC架构的某些指令是为分离的指令/数据缓存设计的。例如一个旨在只无效指令缓存的指令在601上可能会无效整个统一缓存或者干脆什么都不做。编写可移植的系统代码时必须查阅具体的处理器手册。4. 缓存实现32KB统一缓存与MESI协议缓存是弥补CPU与内存速度差距的关键。601的缓存设计体现了早期高性能RISC处理器的典型思路。4.1 缓存组织结构八路组相联的奥秘601有一个32KB的统一缓存。所谓统一是指指令和数据共享这个缓存空间。这与哈佛架构分离的指令/数据缓存不同。统一的优点是空间利用率更灵活动态平衡指令和数据缺点是可能存在指令和数据争用缓存行的情况。它是八路组相联的。我们可以这样理解整个缓存被分成64条线。每条线包含2个扇区每个扇区有8个字。所以一条线总共是16个连续的字64字节并且总是从16字对齐的地址开始加载。这64条线又被组织成8个组。每个组里有8条线这就是“八路”。当一个内存地址需要被缓存时用地址的某些位索引位决定它属于哪个组。然后这个地址的标签tag会与组内8条线中每一条的标签进行比较。如果匹配且状态有效就是缓存命中如果不匹配或无效就是缓存缺失需要从内存加载。八路组相联是组相联度路数的一个折中选择。路数越高缓存冲突多个热门地址映射到同一组的概率越低命中率可能越高但比较电路也更复杂、速度更慢。601选择八路是在当时工艺条件下对性能和复杂度的一个平衡。4.2 MESI协议多处理器缓存一致性的守护者MESI协议是601支持多处理器系统的核心。每个缓存行在601中是扇区都有一个2位的状态标记对应四种状态修改该缓存行中的数据已被修改与主内存不同且是唯一有效的副本。在它被替换出去之前必须写回内存。独占该缓存行中的数据与主内存一致且是当前系统中唯一的缓存副本。处理器可以安静地读写它无需通知其他处理器。共享该缓存行中的数据与主内存一致但系统中至少还有一个其他处理器也缓存了它。任何处理器要修改它必须先通过总线广播一个请求将其他副本置为无效然后自己才能进入“修改”状态。无效该缓存行中的数据是无效的不能使用。601通过总线侦听逻辑来维护这个协议。当其他处理器在总线上发起内存事务时601的缓存控制器会“偷听”这个事务。如果发现事务涉及的地址正好在自己缓存中并且状态是“共享”或“独占”而对方是要写入那么601就需要将自己对应的缓存行置为“无效”。这个过程对处理器核心访问缓存的影响很小因为侦听有独立的端口。实操心得在编写多核或多处理器系统的底层驱动或并发程序时理解MESI协议至关重要。不当的内存访问模式如频繁修改被多个核心共享的变量会导致大量的缓存行在“共享”和“无效”状态间震荡产生大量的总线通信严重损害性能这就是所谓的“缓存伪共享”问题。通常的解决方法是让每个核心操作独立的内存地址通过内存对齐和填充。4.3 缓存加载策略关键字优先与预取601的缓存加载策略也很有讲究关键字符先当发生缓存缺失时总线事务会首先传输处理器请求的那个特定的“四字”。这个四字会被立刻送给执行单元让等待它的指令得以继续执行而不必等到整个扇区8个字都加载完。这显著减少了缓存缺失带来的停顿。可选扇区预取在加载了缺失的扇区后如果同一缓存线中的另一个扇区是无效状态601可以尝试低优先级地更新那个扇区。这实际上是一种简单的硬件预取利用了空间局部性原理程序很可能很快会访问相邻地址。系统软件可以禁用这个功能。5. 异常模型当意外发生时异常是处理器响应内部或外部事件如除零错误、外部中断、页错误的机制。601的异常处理设计得非常精细。5.1 异常的分类与处理手册将异常按两个维度分类同步/异步精确/不精确。同步异常由正在执行的指令直接导致如非法指令、对齐错误、浮点异常。异步异常由外部事件导致与当前指令流无关如外部中断、递减器中断。精确异常异常发生时处理器的状态是完全确定的。异常处理程序可以精确知道是哪条指令导致的异常并且该指令之前的所有指令都已完成之后的指令都未执行。这使得异常可以完全恢复。不精确异常异常发生时处理器的状态可能不确定。可能有些在故障指令之后的指令已经执行了。这通常难以恢复。601将所有异常都作为精确异常来处理即使是PowerPC架构定义为“不精确”的浮点异常模式。这简化了异常处理程序的编写提高了系统的可靠性。当异常发生时处理器将下一条指令的地址和当前的机器状态分别保存到SRR0和SRR1寄存器。根据异常类型跳转到固定的异常向量地址如表1-2中的偏移量如0x00300对应数据访问异常。处理器切换到特权模式开始执行异常处理程序。5.2 关键的异常类型解析表1-2是一个异常速查表对系统程序员极其重要。这里挑几个典型的说说数据访问异常最常见的原因就是页错误。当程序访问一个虚拟地址而MMU在页表或BAT中找不到有效的映射时就会触发此异常。操作系统需要分配物理页建立映射然后重新执行那条导致异常的指令。DSISR寄存器提供了详细的错误原因位。对齐异常当尝试访问未对齐的内存时触发。例如一个lwz指令要求加载一个字但给出的地址不是4字节对齐的。601硬件不支持非对齐访问必须由软件异常处理程序通过多次对齐访问来模拟这会导致性能损失。因此好的编译器会尽量保证数据对齐。程序异常这是一个大类包含浮点异常、非法指令、特权指令违规、陷阱指令触发等。陷阱指令是一种非常有用的机制它允许软件设置条件如某个寄存器大于另一个当条件满足时自动触发异常常用于实现调试断点或系统调用模拟。运行模式/跟踪异常这是一个601特有的、用于调试的异常。通过设置HID1寄存器和MSR[SE]位可以让处理器进入单步执行模式或者在执行到特定指令地址时中断。这对于开发底层固件和调试操作系统内核至关重要。6. 内存管理单元从虚拟地址到物理地址MMU负责将程序看到的虚拟地址转换成实际访问内存的物理地址并提供内存保护。6.1 地址转换流程TLB、BAT与页表601使用段页式内存管理。一个32位的有效地址被分成段号、页号和页内偏移。首先查BATMMU先用地址的高位去匹配四个块地址转换寄存器。如果命中说明这个地址属于一个被BAT映射的大块内存128KB-8MB直接使用BAT中的物理地址速度快无需查页表。BAT通常用于映射操作系统内核、关键设备寄存器等固定区域。其次查TLB如果BAT未命中则查找统一TLB。UTLB是一个256条目、两路组相联的缓存里面存放了最近使用过的页表项。如果命中同样快速获得物理地址。最后查页表如果UTLB也未命中就发生“TLB缺失”。这时硬件会启动一个页表搜索过程。它使用一个哈希函数根据虚拟页号计算出两个可能的页表项组地址然后从内存中读取这些PTEG查找匹配的页表项。找到后不仅用于本次访问还会将其加载到UTLB中以备下次使用。这个过程由硬件自动完成但速度比TLB命中慢得多。指令TLB为了加速取指601还有一个独立的、4条目的ITLB专门缓存指令地址的翻译结果。它由硬件自动维护与UTLB保持一致。6.2 软件职责与性能考量虽然硬件提供了TLB和自动的页表搜索但软件操作系统仍有重要职责维护页表操作系统负责在内存中建立和维护哈希页表这个数据结构。处理页错误当页表搜索也失败时即所需的页不在内存中会触发数据或指令访问异常操作系统需要从磁盘换入页面。TLB一致性当操作系统修改了页表例如将一个页面换出它必须负责无效化UTLB中对应的旧条目。601提供了tlbie等指令来完成这个操作。在多处理器系统中这个无效化操作可能需要广播到所有处理器。避坑技巧在编写对性能要求极高的代码如游戏引擎、数字信号处理内核时应尽量利用BAT或大页来减少TLB缺失。因为TLB条目有限如果代码或数据访问模式非常随机跨越大量页面会导致频繁的TLB缺失和页表搜索严重拖慢速度。有时通过调整数据布局提高空间局部性可以显著提升TLB命中率。7. 指令时序与流水线超标量执行的早期实践手册最后简要提到了601的流水线这是它实现高性能的关键。7.1 三发射超标量流水线601是一个三发射超标量处理器这意味着它内部有三个独立的执行单元理论上每个周期可以同时开始执行三条指令整数单元执行整数算术、逻辑、移位、加载/存储地址计算等指令。浮点单元执行所有浮点指令。分支处理单元执行分支指令并负责指令预取。图1-5的流水线图展示了指令从取指到完成的旅程。指令先被取到指令队列中。分发单元会查看队列底部的几条指令并尝试将它们分派到空闲的执行单元。这里有个关键限制整数指令只能从IQ0位置分派这保证了整数指令它们经常有数据依赖能按程序顺序完成简化了依赖检查和结果写回的顺序控制。而浮点和分支指令可以从IQ0-IQ3分派灵活性更高。7.2 理解性能瓶颈要写出高效的601汇编代码需要理解其流水线特性数据冒险后一条指令需要前一条指令的结果。601的整数流水线有多个阶段如果一条指令刚算出结果下一条指令立刻就要用可能会产生停顿。编译器或汇编程序员可以通过指令调度在两条相关指令之间插入不相关的指令来填充这个空档。控制冒险分支指令会导致预取的后续指令可能无效。601的BPU会尝试进行简单的静态分支预测。对于无法预测的分支流水线会被清空一部分带来惩罚。结构冒险多个指令争用同一资源。例如虽然IU和FPU可以并行工作但它们都可能需要访问统一的缓存或系统总线。如果同时发生缓存缺失就会产生争用。手册中提到的“标记”机制很有趣。当浮点或分支指令被分派时系统会用“标签”将它们与流水线中前一个整数指令关联起来。在整数完成阶段逻辑单元根据这些标签重新排序所有执行单元的结果确保它们按程序顺序提交从而维持一个精确的异常模型。即使浮点运算先算完也必须等到它之前的所有整数指令都完成后其结果才能被正式写回寄存器。
深入解析PowerPC 601:RISC架构、缓存与MMU设计实战
1. 项目概述从手册到实战拆解PowerPC 601的硬核内功如果你和我一样是从那个奔腾、K6处理器风靡的年代过来的硬件爱好者或者现在正从事嵌入式、游戏机模拟器开发那么“PowerPC”这个名字一定不会陌生。它不像x86那样无处不在却深深烙印在苹果Power Macintosh、任天堂GameCube、索尼PS3等经典产品的灵魂里。今天我们不谈那些宏大的产品故事而是把目光聚焦到这一切的基石之一——PowerPC 601处理器。手头正好有一份当年的官方用户手册里面密密麻麻的寄存器表、指令分类和缓存框图对新手来说可能像天书但对想深入理解RISC架构、甚至动手写点底层代码比如模拟器或者Bootloader的朋友来说这就是一份宝藏地图。这份手册的节选主要勾勒了PowerPC 601的三个核心硬件模块寄存器模型、指令集架构和缓存与内存管理单元。简单来说寄存器是CPU的“工作台”指令集是它听得懂的“语言”而缓存则是连接它与慢速内存的“高速缓冲区”。601作为PowerPC家族的第一代量产成员承上启下它继承了IBM POWER架构的一些特性以保持兼容又确立了未来PowerPC架构的基本规范。理解它你就能理解很多RISC设计的精髓比如为什么指令长度固定为32位、为什么要有用户和特权模式之分、缓存一致性协议MESI到底在忙活什么。接下来的内容我会带你像读一份“产品拆解报告”一样把这些手册里的术语和框图还原成工程师设计时的考量和我们编程时需要注意的细节。无论你是计算机体系结构的学生还是对复古硬件、底层开发感兴趣的极客相信都能从中找到乐趣和干货。我们不仅要知道601有什么更要明白它为什么这么设计以及在实际中如何与之打交道。2. 核心细节解析601的寄存器世界——权限、角色与访问之道手册的图1-3是一张经典的“编程模型”图但它不仅仅是寄存器的罗列更是一张清晰的权限和功能地图。理解这张图是理解601如何工作的第一步。2.1 用户与特权模式一道关键的安全鸿沟PowerPC 601和大多数现代处理器一样通过机器状态寄存器中的特权级位来划分两个世界用户模式和特权模式。这不仅仅是权限高低的问题更是系统稳定性的基石。用户模式普通应用程序运行的状态。在此模式下程序只能访问一部分资源主要是通用寄存器、浮点寄存器、链接寄存器、计数寄存器等。它的指令集也被限制无法执行那些会直接影响硬件状态如管理缓存、进行地址转换的敏感指令。这种设计有效防止了用户程序因bug或恶意行为而破坏整个系统。特权模式操作系统内核、驱动等系统级软件运行的状态。在此模式下软件可以访问所有寄存器包括那些用于内存管理、异常处理、调试和处理器控制的特殊功能寄存器。例如写实时时钟寄存器、修改块地址转换寄存器、访问硬件实现寄存器等操作都必须在特权模式下进行。注意在编写系统软件如操作系统内核时必须时刻注意当前所处的模式。在用户模式下尝试执行mtspr指令去写一个特权SPR会直接触发一个“特权指令”异常。这是系统实现保护机制的关键。2.2 特殊功能寄存器详解601的独特性与兼容性手册中特别强调了一些“601独有”或“实现特定”的寄存器这些是理解601区别于其他PowerPC处理器的关键。MQ寄存器这是一个典型的兼容性包袱。MQ是一个32位寄存器用于辅助乘除法和长位移/循环移位指令。例如在执行32位乘法时64位的结果的高32位可能会存放在MQ中在做64位移位时MQ会和某个GPR配对使用。它的存在主要是为了与更早的IBM POWER架构的指令集保持二进制兼容。在纯粹的PowerPC架构中这类操作有更优雅的解决方式比如使用多个GPR。在编程时如果你在处理遗留的POWER架构代码就需要特别注意MQ的用法。实时时钟寄存器RTCU和RTCL这对寄存器提供了一个64位的实时时钟计数器。手册里一个非常精妙的设计细节是读和写操作使用了不同的SPR编号。用户程序可以用一组编号SPR4, SPR5来读取时钟值但无法写入。只有特权程序用另一组编号SPR20, SPR21才能写入。这种硬件级的保护防止了用户程序随意篡改系统时间基准确保了计时服务的可靠性。块地址转换寄存器BAT寄存器是一种高效的地址翻译机制用于将一大段连续的逻辑地址直接映射到物理地址无需经过复杂的页表查询。601有4对IBAT指令BAT而完整的PowerPC架构定义了8对IBAT和8对DBAT数据BAT。601将其IBAT当作统一的BAT使用这意味着它用同一套BAT条目来翻译指令和数据的访问。这在设计简单、对性能要求不极致的场景下是可行的但也意味着软件无法为指令和数据设置不同的块映射属性如只执行、只读灵活性有所降低。硬件实现寄存器HID0-HID2, HID5, HID15这些寄存器是给硬件调试和系统设计人员准备的“后门”。例如HID0包含使能检查停止、控制缓存行为如禁用缓存、使能写分配的位。HID2在601中用作指令地址断点寄存器用于调试。HID15存放处理器标识这在多处理器系统中至关重要用于区分不同的CPU核心。 手册明确警告这些寄存器的定义在不同PowerPC处理器间可能不一致。这意味着为601写的底层代码如果严重依赖HID寄存器移植到其他PowerPC芯片如603、740、750时可能需要修改。这是编写可移植性高的固件时需要警惕的。2.3 寄存器访问方式显式与隐式访问这些寄存器主要有两种方式显式访问使用专门的指令如mtspr和mfspr。你需要知道目标寄存器的SPR编号如图1-3左侧所示。这是最直接、最常用的方式。隐式访问作为指令执行的一部分自动发生。例如执行bl分支并链接指令时返回地址会自动存入链接寄存器执行比较指令时结果会设置条件寄存器的相应位进行乘除运算时MQ寄存器可能被自动使用。理解这两种方式有助于你在阅读汇编代码或调试时清楚寄存器的值是如何变化的。3. 指令集与寻址模式RISC哲学的具象化PowerPC是经典的RISC设计其指令集设计充分体现了RISC的核心理念简单、规整、利于流水线和超标量执行。3.1 指令格式与分类为何固定32位所有PowerPC指令都是32位长且字对齐。这个设计带来了几个巨大的优势简化取指取指令单元每次固定取4个字节无需判断指令边界硬件设计简单。并行解码因为格式规整操作码、寄存器域、立即数域的位置相对固定解码器可以更容易地进行并行解码为后续的多发射超标量做好准备。简化流水线在流水线的各个阶段对指令长度的处理是统一的减少了控制逻辑的复杂性。手册将指令分为几大类这个分类主要是功能性的并不严格对应执行单元整数指令包括算术、比较、逻辑、移位/循环。这是所有计算的基础。浮点指令包括算术、乘加、舍入转换、比较和状态控制。601的浮点单元性能在当时相当强劲。加载/存储指令这是RISC架构的关键特征——只有load和store指令可以访问内存所有计算都在寄存器间进行。这种“加载-存储”架构使得指令功能纯粹流水线更容易调度。手册特别提到了lwarx和stwcx.这对指令它们是实现原子操作如锁、信号量的基石通过“加载保留-条件存储”的机制能在多处理器环境下安全地更新共享内存。流控制指令包括分支、陷阱、条件寄存器操作。它们改变了指令执行的顺序。处理器控制指令用于同步内存访问sync,isync、管理特殊寄存器mtmsr,mfmsr、管理TLB等。是操作系统内核的利器。内存控制指令用于管理缓存、段寄存器。例如dcbst数据缓存块存储用于将修改过的缓存数据写回内存icbi指令缓存块无效用于使指令缓存失效在多处理器一致性维护中至关重要。3.2 寻址模式极简主义的效率PowerPC的寻址模式只有两种简单到令人发指EA (rA|0) offset基址寄存器rA如果为0则代表值0加上一个16位的有符号立即数偏移量。EA (rA|0) rB基址寄存器加上另一个索引寄存器rB的值。这种极简设计减少了地址计算单元的复杂度使得有效地址计算能在一个时钟周期内完成对于保持流水线畅通至关重要。所有的复杂地址计算比如数组索引、结构体成员访问都通过编译器在生成代码时利用多条简单的算术指令和这两种寻址模式组合来完成。3.3 601的指令集特色继承、扩展与差异601的指令集是PowerPC架构的一个子集并做了一些扩展和调整未实现的指令附录C列出了一些PowerPC架构定义但601未实现的指令这些通常被定义为“可选”指令。在601上执行它们会触发“非法指令”异常。POWER兼容指令为了运行旧的POWER架构二进制代码601实现了一部分POWER指令附录B这主要依赖于前面提到的MQ寄存器等兼容性设计。特定指令行为差异一些缓存控制指令在601上可能功能不全或是空操作。这是因为601采用统一缓存而PowerPC架构的某些指令是为分离的指令/数据缓存设计的。例如一个旨在只无效指令缓存的指令在601上可能会无效整个统一缓存或者干脆什么都不做。编写可移植的系统代码时必须查阅具体的处理器手册。4. 缓存实现32KB统一缓存与MESI协议缓存是弥补CPU与内存速度差距的关键。601的缓存设计体现了早期高性能RISC处理器的典型思路。4.1 缓存组织结构八路组相联的奥秘601有一个32KB的统一缓存。所谓统一是指指令和数据共享这个缓存空间。这与哈佛架构分离的指令/数据缓存不同。统一的优点是空间利用率更灵活动态平衡指令和数据缺点是可能存在指令和数据争用缓存行的情况。它是八路组相联的。我们可以这样理解整个缓存被分成64条线。每条线包含2个扇区每个扇区有8个字。所以一条线总共是16个连续的字64字节并且总是从16字对齐的地址开始加载。这64条线又被组织成8个组。每个组里有8条线这就是“八路”。当一个内存地址需要被缓存时用地址的某些位索引位决定它属于哪个组。然后这个地址的标签tag会与组内8条线中每一条的标签进行比较。如果匹配且状态有效就是缓存命中如果不匹配或无效就是缓存缺失需要从内存加载。八路组相联是组相联度路数的一个折中选择。路数越高缓存冲突多个热门地址映射到同一组的概率越低命中率可能越高但比较电路也更复杂、速度更慢。601选择八路是在当时工艺条件下对性能和复杂度的一个平衡。4.2 MESI协议多处理器缓存一致性的守护者MESI协议是601支持多处理器系统的核心。每个缓存行在601中是扇区都有一个2位的状态标记对应四种状态修改该缓存行中的数据已被修改与主内存不同且是唯一有效的副本。在它被替换出去之前必须写回内存。独占该缓存行中的数据与主内存一致且是当前系统中唯一的缓存副本。处理器可以安静地读写它无需通知其他处理器。共享该缓存行中的数据与主内存一致但系统中至少还有一个其他处理器也缓存了它。任何处理器要修改它必须先通过总线广播一个请求将其他副本置为无效然后自己才能进入“修改”状态。无效该缓存行中的数据是无效的不能使用。601通过总线侦听逻辑来维护这个协议。当其他处理器在总线上发起内存事务时601的缓存控制器会“偷听”这个事务。如果发现事务涉及的地址正好在自己缓存中并且状态是“共享”或“独占”而对方是要写入那么601就需要将自己对应的缓存行置为“无效”。这个过程对处理器核心访问缓存的影响很小因为侦听有独立的端口。实操心得在编写多核或多处理器系统的底层驱动或并发程序时理解MESI协议至关重要。不当的内存访问模式如频繁修改被多个核心共享的变量会导致大量的缓存行在“共享”和“无效”状态间震荡产生大量的总线通信严重损害性能这就是所谓的“缓存伪共享”问题。通常的解决方法是让每个核心操作独立的内存地址通过内存对齐和填充。4.3 缓存加载策略关键字优先与预取601的缓存加载策略也很有讲究关键字符先当发生缓存缺失时总线事务会首先传输处理器请求的那个特定的“四字”。这个四字会被立刻送给执行单元让等待它的指令得以继续执行而不必等到整个扇区8个字都加载完。这显著减少了缓存缺失带来的停顿。可选扇区预取在加载了缺失的扇区后如果同一缓存线中的另一个扇区是无效状态601可以尝试低优先级地更新那个扇区。这实际上是一种简单的硬件预取利用了空间局部性原理程序很可能很快会访问相邻地址。系统软件可以禁用这个功能。5. 异常模型当意外发生时异常是处理器响应内部或外部事件如除零错误、外部中断、页错误的机制。601的异常处理设计得非常精细。5.1 异常的分类与处理手册将异常按两个维度分类同步/异步精确/不精确。同步异常由正在执行的指令直接导致如非法指令、对齐错误、浮点异常。异步异常由外部事件导致与当前指令流无关如外部中断、递减器中断。精确异常异常发生时处理器的状态是完全确定的。异常处理程序可以精确知道是哪条指令导致的异常并且该指令之前的所有指令都已完成之后的指令都未执行。这使得异常可以完全恢复。不精确异常异常发生时处理器的状态可能不确定。可能有些在故障指令之后的指令已经执行了。这通常难以恢复。601将所有异常都作为精确异常来处理即使是PowerPC架构定义为“不精确”的浮点异常模式。这简化了异常处理程序的编写提高了系统的可靠性。当异常发生时处理器将下一条指令的地址和当前的机器状态分别保存到SRR0和SRR1寄存器。根据异常类型跳转到固定的异常向量地址如表1-2中的偏移量如0x00300对应数据访问异常。处理器切换到特权模式开始执行异常处理程序。5.2 关键的异常类型解析表1-2是一个异常速查表对系统程序员极其重要。这里挑几个典型的说说数据访问异常最常见的原因就是页错误。当程序访问一个虚拟地址而MMU在页表或BAT中找不到有效的映射时就会触发此异常。操作系统需要分配物理页建立映射然后重新执行那条导致异常的指令。DSISR寄存器提供了详细的错误原因位。对齐异常当尝试访问未对齐的内存时触发。例如一个lwz指令要求加载一个字但给出的地址不是4字节对齐的。601硬件不支持非对齐访问必须由软件异常处理程序通过多次对齐访问来模拟这会导致性能损失。因此好的编译器会尽量保证数据对齐。程序异常这是一个大类包含浮点异常、非法指令、特权指令违规、陷阱指令触发等。陷阱指令是一种非常有用的机制它允许软件设置条件如某个寄存器大于另一个当条件满足时自动触发异常常用于实现调试断点或系统调用模拟。运行模式/跟踪异常这是一个601特有的、用于调试的异常。通过设置HID1寄存器和MSR[SE]位可以让处理器进入单步执行模式或者在执行到特定指令地址时中断。这对于开发底层固件和调试操作系统内核至关重要。6. 内存管理单元从虚拟地址到物理地址MMU负责将程序看到的虚拟地址转换成实际访问内存的物理地址并提供内存保护。6.1 地址转换流程TLB、BAT与页表601使用段页式内存管理。一个32位的有效地址被分成段号、页号和页内偏移。首先查BATMMU先用地址的高位去匹配四个块地址转换寄存器。如果命中说明这个地址属于一个被BAT映射的大块内存128KB-8MB直接使用BAT中的物理地址速度快无需查页表。BAT通常用于映射操作系统内核、关键设备寄存器等固定区域。其次查TLB如果BAT未命中则查找统一TLB。UTLB是一个256条目、两路组相联的缓存里面存放了最近使用过的页表项。如果命中同样快速获得物理地址。最后查页表如果UTLB也未命中就发生“TLB缺失”。这时硬件会启动一个页表搜索过程。它使用一个哈希函数根据虚拟页号计算出两个可能的页表项组地址然后从内存中读取这些PTEG查找匹配的页表项。找到后不仅用于本次访问还会将其加载到UTLB中以备下次使用。这个过程由硬件自动完成但速度比TLB命中慢得多。指令TLB为了加速取指601还有一个独立的、4条目的ITLB专门缓存指令地址的翻译结果。它由硬件自动维护与UTLB保持一致。6.2 软件职责与性能考量虽然硬件提供了TLB和自动的页表搜索但软件操作系统仍有重要职责维护页表操作系统负责在内存中建立和维护哈希页表这个数据结构。处理页错误当页表搜索也失败时即所需的页不在内存中会触发数据或指令访问异常操作系统需要从磁盘换入页面。TLB一致性当操作系统修改了页表例如将一个页面换出它必须负责无效化UTLB中对应的旧条目。601提供了tlbie等指令来完成这个操作。在多处理器系统中这个无效化操作可能需要广播到所有处理器。避坑技巧在编写对性能要求极高的代码如游戏引擎、数字信号处理内核时应尽量利用BAT或大页来减少TLB缺失。因为TLB条目有限如果代码或数据访问模式非常随机跨越大量页面会导致频繁的TLB缺失和页表搜索严重拖慢速度。有时通过调整数据布局提高空间局部性可以显著提升TLB命中率。7. 指令时序与流水线超标量执行的早期实践手册最后简要提到了601的流水线这是它实现高性能的关键。7.1 三发射超标量流水线601是一个三发射超标量处理器这意味着它内部有三个独立的执行单元理论上每个周期可以同时开始执行三条指令整数单元执行整数算术、逻辑、移位、加载/存储地址计算等指令。浮点单元执行所有浮点指令。分支处理单元执行分支指令并负责指令预取。图1-5的流水线图展示了指令从取指到完成的旅程。指令先被取到指令队列中。分发单元会查看队列底部的几条指令并尝试将它们分派到空闲的执行单元。这里有个关键限制整数指令只能从IQ0位置分派这保证了整数指令它们经常有数据依赖能按程序顺序完成简化了依赖检查和结果写回的顺序控制。而浮点和分支指令可以从IQ0-IQ3分派灵活性更高。7.2 理解性能瓶颈要写出高效的601汇编代码需要理解其流水线特性数据冒险后一条指令需要前一条指令的结果。601的整数流水线有多个阶段如果一条指令刚算出结果下一条指令立刻就要用可能会产生停顿。编译器或汇编程序员可以通过指令调度在两条相关指令之间插入不相关的指令来填充这个空档。控制冒险分支指令会导致预取的后续指令可能无效。601的BPU会尝试进行简单的静态分支预测。对于无法预测的分支流水线会被清空一部分带来惩罚。结构冒险多个指令争用同一资源。例如虽然IU和FPU可以并行工作但它们都可能需要访问统一的缓存或系统总线。如果同时发生缓存缺失就会产生争用。手册中提到的“标记”机制很有趣。当浮点或分支指令被分派时系统会用“标签”将它们与流水线中前一个整数指令关联起来。在整数完成阶段逻辑单元根据这些标签重新排序所有执行单元的结果确保它们按程序顺序提交从而维持一个精确的异常模型。即使浮点运算先算完也必须等到它之前的所有整数指令都完成后其结果才能被正式写回寄存器。