
对应原书第 6 章6.3 编程6.4 机器语言摘要上一篇笔记认识了寄存器、立即数和内存操作数。本篇只保留第七章 RTL 设计直接需要的程序流程、分支/跳转、word 访存和机器码字段高级语言控制结构、ABI 和字符串等软件内容不再展开。我不需要把重点放在写复杂软件上而要能从一条指令判断它读哪些寄存器、立即数怎样生成、会不会改写 PC、最终允许哪些体系结构状态发生变化预备概念控制流、PC、标签和位字段程序计数器 PC程序计数器Program CounterPC是保存当前指令字节地址的 32 位体系结构状态。RV32I 基础指令长 32 bit即 4 byte所以正常顺序执行时PCnextPC4 PC_{next}PC4PCnextPC4PC4是组合逻辑算出的候选值PC 寄存器只在时钟沿更新。遇到分支或跳转时控制逻辑会选择另一个候选地址写入 PC。控制流控制流control flow是指令地址随程序执行而变化的顺序顺序执行下一条指令地址为PC4条件分支比较条件成立时转到目标地址否则仍为PC4无条件跳转直接转到目标地址jal跳转的同时把PC4写入目的寄存器。标签、目标地址和偏移量汇编中的标签label是某条指令地址的可读名称。汇编器会将标签转换为数值地址或相对当前 PC 的有符号偏移量。loop: addi s0, s0, 1 bne s0, s1, loop标签不是硬件状态机器码只保存编码后的分支偏移量。位字段和掩码位字段bit field是一个数据字中具有特定含义的一段连续 bit。掩码mask通过0和1选择要清除、保留或置位的 bit。例如与0xFF做 AND只保留最低 8 bit。6.3 编程6.3.1 程序流程指令和数据一样存放在内存中。原书示例中三条连续指令的地址为0x538 addi s1, s2, s3 0x53C lw t2, 8(s1) 0x540 sw s3, 3(t6)每条指令占 4 byte所以地址依次增加 40x53840x53C,0x53C40x540 0x53840x53C, \qquad 0x53C40x5400x53840x53C,0x53C40x540从硬件看取指阶段至少需要PC_q ─────────────→ 指令存储器地址 │ └→ 32 位加法器计算 PC_q 4 │ 分支/跳转目标 ───────────┤ ↓ PC 下一值 MUX → PC_d下面预设一个单周期控制流情景指令存储器组合读当前 PC 稳定时指令字段、寄存器读数据、立即数、比较结果和下一 PC 都在本周期内组合产生PC 与寄存器堆只在上升沿更新。该模型只用于理解顺序执行、beq和jal的状态变化不等同于完整处理器实现。**实际时序情景**复位释放后的某一周期当前 PC 为0x0000_1000指令 RAM 输出0x0100_00EF即jal x1,16。时刻组合/时序结果上升沿后至下一上升沿前取指后识别为jalJ-type 偏移量重排为0x10跳转目标为0x1010同时形成返回地址0x1004和对x1的写使能。下一上升沿PC 更新为0x1010同时x1更新为0x1004。因此jal不是“先写返回地址、再跳转”的两步操作而是在同一个上升沿向两个不同体系结构状态提交结果。这个示例只适用于组合读指令存储器若 FPGA Block RAM 配置为同步读取指会多出一个寄存器周期应改用多周期或流水线结构。需要区分PC_q是时序逻辑保存的当前状态PC4、分支目标和跳转目标是组合逻辑产生的候选值PC 选择信号错误会改变后续全部指令流不能当作普通数据错误处理。6.3.2 逻辑与移位指令1. 逻辑指令RISC-V 的and、or、xor对两个 32 位源寄存器逐位运算并把结果写入目的寄存器。andi、ori、xori则用一个寄存器和一个经符号扩展的 12 位立即数作为输入。指令典型用途某一位的结果and/andi清零、提取字段两个输入都为 1 才为 1or/ori合并字段、置位任一输入为 1 就为 1xor/xori翻转、比较差异两个输入不同时为 1例如提取s732h1234_ABCD的s7[15:8]srli s6, s7, 8 # s6 0x0012_34AB andi s6, s6, 0xFF # s6 0x0000_00AB位级计算是0x1234_ABCD 8 0x0012_34AB 0x0012_34AB 0x0000_00FF 0x0000_00ABxori rd, rs1, -1可以实现按位取反因为 12 位的-1编码为12hFFF符号扩展后为32hFFFF_FFFF。slt和sltu都把比较结果写成 32 bit 的0或1但前者按有符号数解释后者按无符号数解释。它们和后面的blt/bltu复用了“比较”的概念却有不同的体系结构副作用slt写rd分支只可能改写 PC。在完整单周期数据通路中比较器输出也是本周期的组合写回候选值只有该指令被译码为slt/sltu时控制器才会在下一个上升沿使能对应rd的写入。不能把比较表达式本身误认为一次独立的“软件赋值”。2. 移位指令指令操作空位补什么sll/slli逻辑左移低位补 0srl/srli逻辑右移高位补 0sra/srai算术右移高位复制原符号位RV32 的立即数移位量只需 5 bit因为它只能选择0312532 2^5322532左移nnn位在不发生有效位截断时相当于乘以2n2^n2n。算术右移适合保持补码符号逻辑右移则把输入当作无符号位串。移出 32 位边界的 bit 会被直接丢弃因此不能把移位与数学乘除完全等同。硬件中移位类指令通常由桶形移位器完成。若把它并入 ALU组合路径大致为寄存器数据 5 位 shamt → 移位器 → 写回 MUX → 寄存器堆6.3.3 分支分支通过修改 PC 改变程序流程。条件成立称为分支成功taken条件不成立称为分支失败not taken。1. 条件分支指令条件数值解释beq rs1, rs2, labelrs1 rs2位模式相等bne rs1, rs2, labelrs1 ! rs2位模式不等blt rs1, rs2, labelrs1 rs2有符号bge rs1, rs2, labelrs1 rs2有符号bltu rs1, rs2, labelrs1 rs2无符号bgeu rs1, rs2, labelrs1 rs2无符号例如beq s0, s1, target对应的 PC 选择关系是PCnext{PCsext(branch_imm),s0s1PC4,s0≠s1 PC_{next} \begin{cases} PCsext(branch\_imm), s0s1 \\ PC4, s0\ne s1 \end{cases}PCnext{PCsext(branch_imm),PC4,s0s1s0s1blt与bltu的输入 bit 可以完全相同但比较结果可能不同。因此译码器必须把有符号/无符号比较模式传给比较器或 ALU。2. 跳转j label无条件跳转到标签处。它是伪指令实际可展开为jal x0, label因为结果写向x0会被丢弃所以只改变 PC不保存返回地址。jal和jalr还会在函数调用中保存返回地址稍后在 6.3.7 讨论。分支的硬件理解条件分支至少包含三部分组合逻辑rs1、rs2 → 比较器 ─────────────┐ PC、B-type 立即数 → 目标加法器 ├→ PC MUX PC → PC4 加法器 ──────────────┘条件分支不应写寄存器也不应写数据存储器。验证时需要同时检查“PC 是否选对”和“无关写使能是否保持为 0”。按6.3.1的时序模型beq在组合阶段比较两个源寄存器条件成立时选择PCB_imm否则选择PC4。该指令没有普通寄存器写使能因此下一上升沿只更新 PC不会写寄存器。6.3.6 数组数组元素连续存放在内存中。若数组基址为base索引为i每个元素大小为element_sizebyte则element_addressbasei×element_size element\_addressbasei\times element\_sizeelement_addressbasei×element_size1. 32 位整数数组RV32I 中一个int通常按 32 位 word 处理占 4 byteaddress(scores[i])base(i≪2) address(scores[i])base(i\ll2)address(scores[i])base(i≪2)原书“每个分数加 10”示例的核心地址计算为slli t0, s1, 2 # t0 i * 4 add t0, t0, s0 # t0 scores[i] lw t1, 0(t0) addi t1, t1, 10 sw t1, 0(t0)这里左移 2 位只是把 word 索引转换为字节偏移本篇只关注 32 bit word 访问。这五条汇编指令对应五个连续的指令周期而不是一个组合数据流先由slli在上升沿写回缩放后的索引再由add写回元素地址随后lw读出元素并在上升沿写入t1addi再写回t110最后sw在存储器写入沿提交数据。它们复用同一套寄存器堆、ALU 和存储器接口。若数据 RAM 为同步读lw的读数据会额外跨越一个时钟边界不能继续套用这里的单周期假设。6.3.7 函数调用第七章直接用到的是jal的硬件语义rd ← PC 4 PC ← PC J_imm这要求写回 MUX 能选择PC4PC MUX 能选择跳转目标。按6.3.1的时序模型jal在组合阶段同时生成PCJ_imm与PC4下一个上升沿把前者提交到 PC、把后者提交到 J-type 的rd。上面的jal x1,16情景中这两个提交结果分别是0x1010与0x1004。6.3.8 伪指令伪指令在进入处理器前已被汇编器展开译码器只识别真实 opcode不为j、nop或ret增加专用硬件。6.4 机器语言汇编器把助记符、寄存器别名和标签转换成 32 位机器指令。RV32I 实际有 R、I、S、B、U、J 六种格式原书根据字段相似性把它们归为 R-type、I-type、S/B-type 和 U/J-type 四组。所有格式都满足两个重要规律opcode固定放在instr[6:0]rd、rs1、rs2和立即数字段尽量占据相同位置。这种折中让汇编编码看起来不够直观但能减少译码与立即数选择硬件。这正对应原书的设计原则 4好的设计需要好的折中。RISC-V 没有强迫所有指令只用一种格式也没有为每条指令设计完全不同的格式。各类指令共享的字段在取指后由组合连线一次性切出opcode做主译码rd/rs1/rs2连接寄存器堆端口funct3细分具体操作。这些都是组合结果只有进入流水线设计时才需要将字段和控制信号锁存到流水级寄存器。6.4.1 R-type 指令R-type 用两个源寄存器和一个目的寄存器31 25 24 20 19 15 14 12 11 7 6 0 ----------------------------------------------------- | funct7 | rs2 | rs1 |funct3| rd | opcode | ----------------------------------------------------- 7 bit 5 bit 5 bit 3 bit 5 bit 7 bitrs1、rs2寄存器堆两个读地址rd写地址opcode、funct3、funct7共同决定 ALU 操作和控制信号。6.4.2 I-type 指令I-type 用一个源寄存器、一个目的寄存器和一个 12 位立即数31 20 19 15 14 12 11 7 6 0 ----------------------------------------------------- | imm[11:0] | rs1 |funct3| rd | opcode | ----------------------------------------------------- 12 bit 5 bit 3 bit 5 bit 7 bit它用于addi、andi、ori、xori、Load 和jalr等。除立即数移位指令外imm[11:0]按 12 位补码解释并符号扩展到 32 位。对slli、srli和sraiimm[4:0]是 5 位无符号移位量高 7 位不再是普通数值其中srai通过instr[30]1与srli区分。译码时不能把整个 12 位字段都送入移位量端口。对lw rd,-36(rs1)I-type 的imm[11:0]12hFDC会符号扩展为32hFFFF_FFDC再由地址 ALU 计算effective_addressrs1(−36) effective\_addressrs1(-36)effective_addressrs1(−36)这里需要掌握的是“12 bit 字段 → 32 bit 有符号操作数”的路径而非手算整条机器码。6.4.3 S/B-type 指令S-type 和 B-type 都没有rd因为 Store 写内存、Branch 写 PC不需要普通寄存器写回。二者共同保留rs1、rs2、funct3和opcode但立即数拼接方式不同。1. S-type31 25 24 20 19 15 14 12 11 7 6 0 -------------------------------------------------------- | imm[11:5] | rs2 | rs1 |funct3| imm[4:0] | opcode | --------------------------------------------------------对sw rs2, imm(rs1)rs1提供基址rs2提供写数据imm经符号扩展后与基址相加立即数被拆成高 7 位与低 5 位放入指令。硬件必须把两段重新拼回S_imm sext({instr[31:25], instr[11:7]})Store 的控制不变量是RegWrite0。如果译码器误把instr[11:7]当作rd就可能产生错误寄存器副作用。2. B-typeB-type 的 13 位有符号偏移量最低位固定为 0指令只保存其余 12 位B_imm sext({instr[31], instr[7], instr[30:25], instr[11:8], 1b0}) branch_target PC B_imm最低位不编码的原因是分支目标至少按 2 byte 对齐所以偏移量必为偶数。这里编码的是字节偏移量不是“跨过几条指令”。6.4.4 U/J-type 指令1. U-type31 12 11 7 6 0 ----------------------------------------------------------- | imm[31:12] | rd | opcode | ----------------------------------------------------------- 20 bit 5 bit 7 bitlui rd, imm20把指令中的 20 位立即数放到结果的高 20 位低 12 位补 0U_imm {instr[31:12], 12b0}这与上一篇构造大常数时的lui行为相同。2. J-typejal使用 21 位有符号 PC 相对偏移量最低位同样固定为 0J_imm sext({instr[31], instr[19:12], instr[20], instr[30:21], 1b0}) jump_target PC J_imm rd PC 4RTL 只需保证 J-type 立即数按该位序重排并同时让 PC 选择jump_target、写回 MUX 选择PC4无需手算其整条机器码。6.4.5 立即数编码六种格式的立即数生成关系可以集中写成I: sext(instr[31:20]) S: sext({instr[31:25], instr[11:7]}) B: sext({instr[31], instr[7], instr[30:25], instr[11:8], 1b0}) U: {instr[31:12], 12b0} J: sext({instr[31], instr[19:12], instr[20], instr[30:21], 1b0})R-type 不需要立即数。为什么字段看起来被打乱RISC-V 尽量让相同含义的立即数 bit 落在相同指令位置并让instr[31]作为有符号立即数的符号来源。这样可以减少立即数生成器中的 MUX 输入和布线交叉。硬件可以把它理解为instr[31:7] ↓ 按 ImmSrc 选择并重排 ↓ 符号扩展 / 低位补 0 ↓ 32 位 ImmExt立即数生成器是组合逻辑不应保存状态。验证时至少覆盖I/S-type 的最大正数2047与最小负数-2048B/J-type 的正向与负向偏移B/J-type 生成结果 bit 0 恒为 0U-type 低 12 位恒为 0不同格式下instr[31]的符号扩展。真正实现时应把这些拼接式放在同一组合译码区B/J 型的目标地址计算已在6.3.1的时序情景中说明。I/S/U 型遵循同样原则字段重排和符号扩展在当前周期完成产生的值只在驱动 PC 或写回使能的下一个上升沿改变体系结构状态。6.4.6 寻址方式寻址方式说明指令从哪里取得操作数或怎样产生新 PC。寻址方式例子地址/操作数来源硬件路径寄存器寻址add s0,s1,s2rs1、rs2寄存器堆 → ALU立即数寻址addi s0,s1,4rs1、立即数寄存器堆/立即数 MUX → ALU基址寻址lw t0,8(s1)rs1sext(imm)ALU 算有效地址 → 数据存储器PC 相对寻址beq、jalPCsext(offset)PC/立即数 → 目标加法器同一个加法运算在不同寻址方式下具有不同输入来源因此数据通路需要 MUX不能只看见“都是加法”就忽略控制信号。6.4.7 解释机器语言代码译码器的稳定顺序是用instr[6:0]的opcode选择主控制信号和指令格式并行切出rd、rs1、rs2、funct3、instr[30]由功能字段选择 ALU/比较操作并由格式选择立即数重排只将明确需要的字段送入寄存器堆、ALU、存储器接口或 PC 选择逻辑。重点是“字段→控制/数据通路”的连接反汇编或 ABI 名称转换不是学习的重点。6.4.8 存储程序存储程序的核心是机器指令和普通数据一样以二进制形式存储在内存中。更换程序只需改变内存中的指令内容不需要重新连接硬件。原书示例从地址0x0000_0830开始保存 4 条指令0x0000_0830 0x0149_8933 0x0000_0834 0x4073_02B3 0x0000_0838 0xFF23_0913 0x0000_083C 0xFFA9_A383PC 初始化为0x0000_0830后处理器重复执行按 PC 取指 → 译码 → 读取操作数 → 执行 → 更新体系结构状态 → 选择下一 PC取指数据流已经在6.3.1首次出现 PC 时给出。这里只需记住PC 的体系结构含义始终是字节地址pc_q[31:2]仅在连接 32 bit 指令 RAM 时转换为 word 索引并不改变 PC 本身的单位。在本章范围内RV32I 的主要体系结构状态包括PC32 个整数寄存器的可见内容内存中的可见数据。组合逻辑的中间节点、流水线寄存器和分支预测状态不属于这里的软件可见体系结构状态。它们属于具体微体系结构下一章才会展开。本篇硬件视角总结从指令语义到硬件资源书中概念主要硬件资源或行为顺序执行PC 寄存器、PC4加法器条件分支两端口寄存器读取、比较器、目标加法器、PC MUX逻辑/移位ALU 逻辑单元、桶形移位器数组访问索引缩放、地址加法、Load/Store 接口jalPC4写回、跳转目标写 PC指令格式opcode 译码器、寄存器地址字段、立即数生成器存储程序指令存储器、PC 驱动的取指路径常见指令的副作用指令类别写rd读内存写内存改变 PC 的方式R/I-type ALU是否否PC4Load是是否PC4Store否否是PC4Branch否否否PC4或分支目标jal是写入PC4否否跳转目标这张表适合直接转化为后续控制器的检查清单。尤其要防止 Store、Branch 对寄存器产生非预期写回也要防止分支失败时仍选择目标 PC。后续小节内容均偏向于软件不再进行过于深度的学习后续直接开始第七章 微体系结构 的学习。