《RISC-V开放架构设计之道》读书笔记:“硬件实现友好”与“简洁即高效”设计原则

发布时间:2026/8/15 20:35:31
《RISC-V开放架构设计之道》读书笔记:“硬件实现友好”与“简洁即高效”设计原则 ps这几篇RISC-V的笔记之前整理了一直放在草稿箱里忘了发了捂脸目录0 相关内容1 短立即数和长立即数来源ds1.1 什么是立即数1. 短立即数Short Immediate定义编码与使用特点为什么是 12 位2. 长立即数Long Immediate定义编码与使用特点为什么需要长立即数3. 短立即数与长立即数的对比4. 在伪指令 li 中的应用5. 实际编程中的注意事项总结2 读书笔记 RISC-V 在设计上对硬件实现友好的几个特点1. 基础术语解释源操作数Source Operand vs. 目的操作数Destination Operand时序Timing与译码路径Decode Path2. 逐句解析原文第一句第二句第三句第四句第五句3. 总结这段话的核心思想3 读书笔记 RISC-V 的“简洁即高效”原则在具体实现中的体现1. 基础术语解释指令编码空间操作码Opcode与位域Bit Field数据通路Datapath与控制逻辑信号扇出Signal Fanout2. 逐句解析原文第一句第二句第三句第四句3. 综合理解这段话的硬件意义4 RISC-V 寄存器5 读书笔记RISC-V 通过分离“运算宽度”和“访存宽度”在硬件简单性和能耗效率之间取得了巧妙平衡。1.什么是访存指令2.什么是短数据访存3.结合上下文的补充理解0 相关内容【权威书籍】RISC-V开放架构设计之道【外部链接】RISC-V 入门笔记新手必看 - 知乎【外部链接】RISC-V入门资料_riscv开源官方网站-CSDN博客【外部链接】03 计算机组成原理-CPU的构成 - 知乎1 短立即数和长立即数来源ds1.1 什么是立即数在 RISC-V 架构中立即数immediate是指直接编码在指令中的常数用于算术运算、地址计算、加载/存储偏移量等操作。根据立即数的位宽和编码方式可以分为短立即数和长立即数。理解它们的区别对于编写高效汇编代码、理解编译器行为以及底层调试都至关重要。1. 短立即数Short Immediate定义短立即数通常指12位有符号整数范围是-2048 到 20472^11 个负数 2^11 个非负数包括0。它直接嵌入在I-type、S-type和B-type指令中。编码与使用I-type 指令如addi、slti、lw立即数占 12 位作为操作数参与运算。S-type 指令如sw立即数被拆分成两个部分低 5 位和高 7 位但合并后仍是 12 位有符号偏移。B-type 指令如beq立即数也是 12 位有符号用于条件分支跳转但编码时省略了最低位总是偶数地址。特点单条指令即可完成操作因为 12 位立即数直接编码在指令中执行时无需额外指令加载。有符号扩展所有短立即数在参与运算前都会被有符号扩展到 32 位RV32或 64 位RV64以保持数值的正确性。常见例子addi a0, a1, 100 # a0 a1 100 (100 是短立即数) lw a0, 16(sp) # 加载地址 sp16 的字16 是短立即数偏移为什么是 12 位这是 RISC-V 指令集设计时的一个权衡在保持指令长度固定为 32 位的前提下留出足够的位给寄存器编号5位 × 3个 15位和操作码同时保证立即数范围能覆盖常见的小常数和访存偏移。对于大多数程序来说12 位有符号数足以应对常见的偏移量和小常数。2. 长立即数Long Immediate定义长立即数通常指20 位无符号整数范围 0 到 1048575或者需要组合两个立即数才能表示的 32 位/64 位常数。它出现在U-type指令lui、auipc中这些指令用于加载立即数的高 20 位。编码与使用U-type 指令立即数占 20 位存放于指令的高 20 位低 12 位固定为 0。执行时这 20 位被左移 12 位即乘以 2^12后存入目标寄存器低 12 位补 0。lui rd, imm20rd imm20 12auipc rd, imm20rd PC (imm20 12)32 位/64 位任意常数若要加载一个完整的 32 位或 64 位立即数通常需要组合lui和addi或其他指令例如lui a0, 0x12345 # a0 0x12345000 addi a0, a0, 0x678 # a0 0x12345678注意addi会对 12 位立即数进行符号扩展所以当低 12 位的最高位第 11 位为 1 时需要特殊处理调整高 20 位并改用addi加载负数但汇编器如li伪指令会自动处理这种细节。特点需要多条指令组合单个长立即数无法直接用于算术运算必须通过lui/auipc加载高 20 位再配合addi等指令加上低 12 位。无符号但组合后可表示有符号数lui的 20 位本身是无符号的但与addi的有符号扩展组合后可以表示完整的 32 位有符号数范围。常用于地址加载和大常数li a0, 0xdeadbeef # 伪指令展开为 lui addi 序列 la a0, large_array # 获取数组地址可能超过 12 位偏移为什么需要长立即数RISC-V 指令长度固定为 32 位无法将 32 位立即数直接编码在一条指令中那样会占满所有位无法指定寄存器和操作。因此通过lui提供高 20 位再配合addi提供低 12 位就可以构造任意 32 位常数。这是典型的 RISC 设计思路用两条指令完成复杂操作保持指令格式规整。3. 短立即数与长立即数的对比特性短立即数长立即数位宽12 位20 位U-type或 32/64 位组合范围-2048 ~ 20470 ~ 104857520位无符号编码指令类型I-type, S-type, B-typeU-type (lui,auipc)扩展方式有符号扩展到 32/64 位左移 12 位低 12 位补 0单条指令完成是否除单纯高 20 位加载外典型用途小常数、偏移量、条件分支大常数、地址高 20 位、PC 相对地址例子addi x1, x2, 100lui x1, 0x123454. 在伪指令li中的应用伪指令li rd, constant会根据常数的大小自动选择最有效的展开方式如果常数在 -2048 ~ 2047 之间短立即数范围展开为addi rd, x0, constant1 条指令。如果常数超过短立即数范围但 ≤ 32 位拆分为luiaddi两条指令。例如li a0, 0x12345678assemblylui a0, 0x12345 # 高 20 位 (0x12345 12 0x12345000) addi a0, a0, 0x678 # 低 12 位 (0x678) 需注意符号扩展如果低 12 位的最高位为 1汇编器会调整高 20 位并改用addi加载一个负数以正确处理符号扩展。如果常数是 64 位RV64可能需要更多指令如luiaddislliadd等但li伪指令会妥善处理。这种灵活性让程序员无需关心常数范围直接使用li即可。5. 实际编程中的注意事项代码密度短立即数指令只占 4 字节而长立即数组合需要 8 字节甚至更多。在内存受限的嵌入式系统中尽量使用短立即数有助于减小代码体积。性能两条指令比一条指令多占用一个周期在大多数流水线 CPU 中因此频繁使用大常数的地方应考虑能否用其他方式替代如从内存加载。地址计算la伪指令本质上也是利用auipc长立即数和addi组合来生成 PC 相对地址适用于获取全局变量或标签的地址。调试时反汇编代码时看到luiaddi序列应意识到它们共同构成一个完整的长立即数。总结短立即数是 RISC-V 指令中直接编码的 12 位有符号常数用于单条指令的快速操作。长立即数指 20 位无符号立即数lui/auipc或需要组合多条指令才能表示的 32/64 位常数。这种划分是 RISC-V 指令集设计的基础既保证了指令格式的简洁又通过伪指令和编译器支持提供了编程便利性。2 读书笔记 RISC-V 在设计上对硬件实现友好的几个特点“其次RISC-V指令支持3个 寄存器操作数而不像x86-32那样让源操作数和目的操作数共享一个字段。当一个 操作本身具有3个不同的操作数而ISA的指令只支持两个操作数时编译器或汇编语言程序员需要额外使用一条传送move指令避免其中一个源操作数被破坏。 再者在所有RISC-V指令中源寄存器和目的寄存器始终位于同一字段这意味着 可在指令译码前开始访问寄存器。在许多其他ISA中如ARM-32和MIPS-32某 些字段在一部分指令中作为源操作数在另一部分指令中又作为目的操作数。为选出 正确的字段不得不在时序本就紧张的译码路径上额外添加逻辑。最后这些指令格 式的立即数字段总是进行符号扩展其符号位总是位于指令的最高位。此设计方案可 将立即数符号扩展提前到指令译码前进行从而缓解紧张的时序。 ” ——《RISC-V开放架构设计之道》14页1. 基础术语解释源操作数Source Operand vs. 目的操作数Destination Operand源操作数指令执行时需要读取的输入数据。例如add a0, a1, a2中a1和a2就是源操作数它们的值被加法器使用。目的操作数指令执行后结果存放的位置。上例中的a0就是目的操作数运算结果会写入它。有些指令集如 x86为了节省指令位宽会将目的操作数与其中一个源操作数合并例如add eax, ebx表示eax eax ebx此时eax既是源又是目的。这会导致原始eax的值被覆盖如果后面还需要它就必须先用一条mov指令保存。RISC-V 则采用三操作数格式如add rd, rs1, rs2目的和源完全分开不会意外破坏源数据。时序Timing与译码路径Decode Path时序在数字电路中指令的执行受时钟周期控制。一个时钟周期内信号必须从寄存器出发经过组合逻辑最终稳定到达下一个寄存器的输入端。这个过程中的最大延迟决定了时钟频率的上限。译码路径指令从内存取出后需要经过译码器解析出操作码、寄存器编号、立即数等字段。译码逻辑是组合电路它的延迟是时序路径的一部分。如果译码逻辑复杂就会增加延迟可能迫使时钟频率降低或需要插入更多流水线级。2. 逐句解析原文第一句“其次RISC-V指令支持3个寄存器操作数而不像x86-32那样让源操作数和目的操作数共享一个字段。”意思RISC-V 的算术指令如加法显式指定三个独立的寄存器两个源、一个目的。而 x86-32 的典型双操作数指令如add eax, ebx中第一个操作数既是源又是目的共享同一个字段。好处三操作数避免了对原始数据的破坏减少了额外的mov指令。例如在 RISC-V 中要计算c a b且保留a和b可以直接写add c, a, b。而在 x86 中如果只能用两个操作数可能需要先mov c, a再add c, b多了一条指令。第二句“当一个操作本身具有3个不同的操作数而ISA的指令只支持两个操作数时编译器或汇编语言程序员需要额外使用一条传送move指令避免其中一个源操作数被破坏。”补充解释这正好印证了上一点。如果算法需要三个不同的寄存器比如a b存到c而 ISA 只支持两个操作数比如add a, b会覆盖a那么就必须用mov指令先把a复制到别处或者先计算结果再恢复。这不仅增加代码量还可能降低性能。第三句“再者在所有RISC-V指令中源寄存器和目的寄存器始终位于同一字段这意味着可在指令译码前开始访问寄存器。”意思RISC-V 指令格式非常规整所有指令中源寄存器 rs1、rs2 和目的寄存器 rd 都固定在相同的比特位置比如 rs1 在 19-15 位rs2 在 24-20 位rd 在 11-7 位。这样译码器不需要先判断指令类型就能直接从这些位置提取寄存器编号并提前开始读取寄存器文件。硬件意义寄存器文件读取通常需要一定时间地址译码、读取存储单元。如果能提前开始读取就能与译码并行进行从而缩短整个执行路径的延迟改善时序。第四句“在许多其他ISA中如ARM-32和MIPS-32某些字段在一部分指令中作为源操作数在另一部分指令中又作为目的操作数。为选出正确的字段不得不在时序本就紧张的译码路径上额外添加逻辑。”对比以 ARM-32 为例它的指令格式中同一个比特位在不同指令中可能代表不同含义比如有时是源寄存器有时是目的寄存器。译码器必须先解析操作码确定指令类型然后才知道哪个字段是源、哪个是目的。这需要多路选择器根据操作码来切换字段含义这个多路选择器就增加了译码路径的延迟即“额外添加逻辑”。结果这种延迟会挤占本就紧张的时序预算可能导致需要降低时钟频率或增加流水级数。第五句“最后这些指令格式的立即数字段总是进行符号扩展其符号位总是位于指令的最高位。此设计方案可将立即数符号扩展提前到指令译码前进行从而缓解紧张的时序。”意思RISC-V 的立即数在指令中总是有符号数并且符号位即最高位位于指令的固定最高位例如 31 位。这样在指令被取出后可以立即开始符号扩展而不必等译码完成才知道是否需要扩展。硬件意义符号扩展电路比较简单但若需要等译码才知道是否是有符号扩展就会增加延迟。RISC-V 的设计让符号扩展可以与指令缓存输出同时进行同样提前了操作缓解了关键路径的压力。3. 总结这段话的核心思想这段话其实是在强调 RISC-V 在设计上对硬件实现友好的几个特点三操作数减少对额外指令的需求提升代码密度和效率。固定字段位置允许提前读取寄存器与译码并行降低延迟。立即数符号位固定允许提前进行符号扩展同样优化时序。3 读书笔记 RISC-V 的“简洁即高效”原则在具体实现中的体现“为了给ISA扩展预留充足空间RV32I基础指令集只使用32位指令编码空间的 不到1/8。同时架构师还精心挑选 RV32I 的操作码使数据通路相同的指令尽可能共 享操作码的位域从而简化控制逻辑。我们将看到B型分支指令和J型跳转指令的 地址需要左移1位来乘以2增大了二者的跳转范围。RISC-V调整了指令的立即数 排布使指令信号扇出和立即数选择成本降低近一半再次简化低端处理器的数据通 路逻辑。” ——《RISC-V开放架构设计之道》17页1. 基础术语解释指令编码空间32位指令共有 2^32 约 43 亿种可能的编码组合。这些组合被划分为不同的操作码区域分配给各种指令。预留空间意味着故意留出大量未使用的编码供未来扩展指令集使用如向量扩展、加密扩展等。操作码Opcode与位域Bit Field操作码指令中用于标识指令类型如加法、分支的二进制编码。位域指令中特定范围的比特位用于存放寄存器编号、立即数等。不同指令可能对同一比特位有不同的解读。数据通路Datapath与控制逻辑数据通路处理器中执行数据运算的硬件路径包括算术逻辑单元ALU、寄存器文件、多路选择器等。控制逻辑根据指令的操作码生成控制信号来指挥数据通路工作的电路。信号扇出Signal Fanout指一个信号需要驱动的负载数量。在电路中一个信号如果连接到很多地方即扇出大会导致传输延迟增加因为需要更大的驱动能力。降低扇出可以改善时序。2. 逐句解析原文第一句“为了给ISA扩展预留充足空间RV32I基础指令集只使用32位指令编码空间的不到1/8。”意思RV32IRISC-V 32位整数基础指令集只使用了所有可能32位指令编码的不到1/8即不到 12.5%。这意味着绝大部分编码空间是空闲的。为什么这样做前瞻性为未来的指令集扩展如乘除法扩展M、原子操作扩展A、浮点扩展F/D、压缩指令扩展C等预留足够的编码空间。简化解码操作码的分布可以有规律便于硬件解码器的设计。对比x86和ARM由于历史包袱编码空间非常拥挤新扩展往往需要采用复杂的编码技巧如使用前缀字节增加了解码复杂度。第二句“同时架构师还精心挑选 RV32I 的操作码使数据通路相同的指令尽可能共享操作码的位域从而简化控制逻辑。”意思在设计操作码时有意让执行时所需硬件资源相似的指令其操作码的某些比特位具有相同含义。例子所有寄存器-寄存器算术运算如 ADD、SUB、AND、OR可能共享大部分操作码位只有一小部分用于区分具体功能。这样控制逻辑只需要根据这些共享位域就能提前确定需要激活哪些数据通路组件如 ALU、寄存器读取而不必完全解码整个操作码。好处控制逻辑变得更简单、更快因为部分解码工作可以提前进行或者用更少的逻辑门实现。第三句“我们将看到B型分支指令和J型跳转指令的地址需要左移1位来乘以2增大了二者的跳转范围。”意思RISC-V 的分支B型和跳转J型指令中编码的立即数实际上是半字偏移量以2字节为单位。在执行时硬件会将其左移1位相当于乘以2后再与当前PC相加。为什么左移RISC-V 指令必须是2字节对齐压缩指令或4字节对齐基础指令。因此跳转目标的地址最低位总是0。通过左移1位原本可以表示 -4K 到 4K 的半字偏移实际上变成了 -8K 到 8K 的字节偏移相当于跳转范围翻倍而不需要增加立即数的位数。硬件意义左移1位在硬件上只需简单地将立即数的比特位连接wiring到更高位几乎不消耗逻辑资源。第四句“RISC-V调整了指令的立即数排布使指令信号扇出和立即数选择成本降低近一半再次简化低端处理器的数据通路逻辑。”意思RISC-V 精心设计了立即数在指令中的物理位置使得提取立即数不同部分的多路选择器更简单且信号扇出更小。具体设计在所有指令格式中立即数的某些部分如符号位始终位于固定位置。不同类型的指令I型、S型、B型、U型、J型的立即数其比特位在指令中的排布经过了优化使得从指令字中提取立即数的硬件可以用更少的多路选择器和连线实现。“扇出降低近一半”如果立即数比特位分散在各处那么每个比特位可能需要驱动多个不同的立即数提取电路导致扇出大。RISC-V 的排布让每个比特位尽可能只服务于一种立即数类型从而减少每个信号需要驱动的负载降低扇出改善时序。“选择成本降低”指用于从指令字中挑选出立即数各个部分的组合逻辑多路选择器变得更简单门延迟更小面积更省。3. 综合理解这段话的硬件意义这段文字实际上是在描述 RISC-V 设计哲学中的“简洁即高效”原则在具体实现中的体现预留空间→易于扩展为新功能留出编码空间避免未来因编码不足而被迫采用复杂的解码方案。共享位域→解码简化让相似指令的操作码有规律减少控制逻辑的复杂度和延迟。地址左移→范围翻倍用极小的硬件代价硬连线左移换取更大的跳转范围。立即数排布优化→逻辑简化、时序优化通过精心安排立即数比特位降低信号扇出和选择逻辑的复杂度这对低端处理器如嵌入式微控制器尤其重要因为它们希望用最少的逻辑门实现最高性能。总的来说RISC-V 的设计者们在指令编码阶段就深入考虑了硬件实现的物理约束时序、扇出、逻辑复杂度从而让 RISC-V 处理器可以设计得更简单、更快、更省电。这对于你作为嵌入式软件工程师的意义在于理解这些设计可以帮助你写出更高效的代码比如知道跳转范围为什么是那样的以及理解编译器在生成代码时的某些优化策略。4 RISC-V 寄存器寄存器介绍【外部链接】RISV-V架构的寄存器介绍_riscv寄存器-CSDN博客【外部链接】RISC-V 入门笔记新手必看 - 知乎注在 RISC-V 架构中CSR 寄存器Control and Status Register控制与状态寄存器确实通常被称为系统寄存器以区别于程序员经常操作的通用寄存器General-Purpose Registers, GPRs如x0到x31或别名a0、sp、ra等。5 读书笔记RISC-V 通过分离“运算宽度”和“访存宽度”在硬件简单性和能耗效率之间取得了巧妙平衡。“首先RV32I中没有字节或半字的整数计算操作所有操作的位宽均 与寄存器位宽相同。内存访问的能耗比算术运算高几个数量级因此短数据访存可 大幅降低能耗但短数据运算不会。此外ARM-32可在大多数算术和逻辑操作中对 其中一个操作数进行移位此特殊功能使数据通路更复杂但很少使用。与之相对 RV32I 提供独立的移位指令。” ——《RISC-V开放架构设计之道》20页1.什么是访存指令访存指令Memory Access Instructions是指处理器用来与内存或I/O交换数据的指令主要包括两类加载指令Load将数据从内存地址复制到寄存器。例如 RISC-V 中的lw加载字、lh加载半字、lb加载字节。存储指令Store将数据从寄存器写回内存地址。例如sw存储字、sh存储半字、sb存储字节。访存指令是程序访问数据的主要途径任何涉及全局变量、数组、指针、堆栈的操作最终都会转化为这类指令。2.什么是短数据访存短数据访存指访问数据宽度小于处理器通用寄存器位宽的内存操作。在 RV32I 中寄存器是 32 位因此字Word32 位对应lw/sw。半字Half Word16 位对应lh/sh。字节Byte8 位对应lb/sb。这里的“短数据”就是指半字和字节。之所以强调“短数据访存”是因为能耗优势内存访问的能耗远高于算术运算可能高几个数量级。如果只需读取一个字节却用lw加载整个字就会多传输 24 位无用数据浪费能量和带宽。因此提供短数据访存指令允许程序按需只访问实际需要的字节数显著降低能耗——这正是原文“短数据访存可大幅降低能耗”的含义。数据宽度匹配许多数据结构如字符数组、网络包、文件头包含大量 8 位或 16 位字段。直接支持这些宽度的访存指令避免了额外移位和掩码操作简化代码并提升效率。3.结合上下文的补充理解原文还提到“RV32I中没有字节或半字的整数计算操作所有操作的位宽均与寄存器位宽相同。”这意味着算术运算如加法、减法总是以 32 位为单位但内存访问却可以精细到字节/半字。这种设计背后的理念是算术运算能耗相对低即使操作的是 8 位数据用 32 位 ALU 计算也不会有太大浪费反而简化了硬件无需支持多种运算宽度。内存访问能耗高因此必须提供精细粒度的访存指令来节省能耗和带宽。所以RISC-V 通过分离“运算宽度”和“访存宽度”在硬件简单性和能耗效率之间取得了巧妙平衡。