除法器 — Divider

恢复余数除法:8位无符号硬件除法

📖 恢复余数除法原理

恢复余数除法(Restoring Division)是最直观的硬件除法算法:

恢复余数除法步骤 (8位): 初始化: A=0, Q=被除数, M=除数 重复8次: 1. 左移 A:Q (A←A<<1|Q[7], Q←Q<<1) 2. A = A - M (试减) 3. 如果 A < 0 (不够减): → 恢复: A = A + M (加回来) → Q[0] = 0 (商位=0) 4. 如果 A >= 0 (够减): → Q[0] = 1 (商位=1) 结果: Q = 商, A = 余数 示例: 10 ÷ 3 A=00000000 Q=00001010 M=00000011 Iter 1: A:Q左移 → A=0,Q=00010100 A-M=0-3=-3 < 0 → 恢复, Q[0]=0 Iter 2: A:Q左移 → ... ... 最终: Q=00000011 (3), A=00000001 (1)
除法算法步骤数每步操作特点
恢复余数N减法+条件恢复简单但慢(恢复步浪费)
非恢复余数N加法或减法无需恢复,但加减交替
SRT除法基于冗余表示更快,但实现复杂
Newton-Raphsonlog(N)迭代逼近适合浮点
非恢复余数除法(Non-Restoring)消除了恢复步:当A<0时不恢复,而是下一步加M(而不是减M)。这样每步固定一次加减运算,比恢复余数更快。

🖥️ Verilog实现:恢复余数除法器

// Lesson 14: Restoring Division — Unsigned 8-bit Divider
module divider(
    input  wire        clk, rst_n,
    input  wire        start,
    input  wire [7:0]  dividend, divisor,
    output reg  [7:0]  quotient, remainder,
    output reg         done,
    output reg         div_by_zero
);
    reg [7:0] Q, A, M; reg [3:0] step; reg [2:0] bit_cnt;
    always @(posedge clk or negedge rst_n) begin
        if(!rst_n) begin quotient<=0; remainder<=0; done<=0;
            div_by_zero<=0; step<=0; Q<=0; A<=0; M<=0; bit_cnt<=0; end
        else if(start && step==0) begin
            if(divisor==0) begin div_by_zero<=1; quotient<=8'hFF;
                remainder<=8'hFF; done<=1; end
            else begin M<=divisor; Q<=dividend; A<=0;
                step<=1; done<=0; div_by_zero<=0; bit_cnt<=0; end
        end else if(step==1) begin
            A<={A[6:0],Q[7]}; Q<={Q[6:0],1'b0}; step<=2;
        end else if(step==2) begin
            if(A>=M) begin A<=A-M; Q[0]<=1; end
            else Q[0]<=0;
            if(bit_cnt==7) begin quotient<=Q; remainder<=A; done<=1; step<=0; end
            else begin bit_cnt<=bit_cnt+1; step<=1; end
        end
    end
endmodule
Verilator仿真验证通过 — 恢复余数除法正确

代码解析

📊 处理器中的除法实现

处理器除法延迟除法方法面积
Rocket Core25-37周期迭代除法
BOOM25-37周期迭代除法
ARM Cortex-A728-28周期SRT基数4
Apple M1~10周期高基数SRT
Intel Alder Lake15-25周期基数16 SRT
RISC-V M扩展除法指令: DIV rd, rs1, rs2 // 有符号除法 DIVU rd, rs1, rs2 // 无符号除法 REM rd, rs1, rs2 // 有符号取余 REMU rd, rs1, rs2 // 无符号取余 特殊情况: 除数为0: 结果=-1 (有符号) 或 0xFFFFFFFF 有符号最小值/-1: 结果=有符号最小值 (溢出)

🧪 实验练习

  1. 实现非恢复余数除法:消除恢复步,减少周期数
  2. 扩展到32位除法:支持RISC-V M扩展的DIV/DIVU/REM/REMU
  3. 添加有符号除法:将操作数转为绝对值,除完后修正符号
  4. 实现流水线除法:支持多条除法指令重叠执行
恢复余数除法正确
思考题:恢复余数除法中,如果被除数是255、除数是1,需要多少个周期?最坏情况下需要多少次恢复操作?
参考资料:Computer Architecture §3.4 | 恢复/非恢复除法 | RISC-V M扩展规范

🔗 除法器在RISC-V M扩展中的实现

RISC-V M扩展定义了4条除法指令:

指令操作特殊情况
DIVx[rd] = x[rs1] / x[rs2] (有符号)÷0→-1, MIN/-1→MIN
DIVUx[rd] = x[rs1] / x[rs2] (无符号)÷0→2^W-1
REMx[rd] = x[rs1] % x[rs2] (有符号)÷0→rs1, MIN/-1→0
REMUx[rd] = x[rs1] % x[rs2] (无符号)÷0→rs1

除法指令的特殊情况

RISC-V除法特殊规则: 1. 除数为0: DIV: 结果 = -1 (全1) DIVU: 结果 = 2^W-1 (全1) REM: 结果 = rs1 (被除数) REMU: 结果 = rs1 2. 有符号最小值 ÷ (-1): DIV: 结果 = 有符号最小值 (溢出!) REM: 结果 = 0 这些特殊值不是异常——不触发trap! 这与x86/ARM不同(它们触发异常)。 设计哲学: 让软件处理特殊情况, 硬件不产生异常, 减少控制逻辑。

高性能除法的替代方案

恢复余数除法是最基础的实现。现代处理器使用更快的算法:

Apple M1的除法器使用高基数SRT,整数除法约10个周期。而Rocket的迭代除法需要25-37个周期。

🎯 本课与整体课程的关系

除法器知识图谱: 13 Booth乘法器 → 14 除法器 (本课) ↓ 除法是最慢的算术运算 ↓ 15 乱序执行 — 除法延迟是关键瓶颈 23 RVM — M扩展包含乘除法 25 RVF/RVD — 浮点除法更复杂

📚 延伸阅读与参考资料

资料内容链接
RISC-V特权规范CSR、Trap、中断完整定义riscv.org/specifications
RISC-V手册中文版免费教材crva.ict.ac.cn
OpenSBI源码M-mode固件参考实现github.com/riscv/opensbi
Linux RISC-V内核移植与驱动kernel.org
BOOM处理器UC Berkeley开源OoO核心github.com/riscv-boom/riscv-boom
香山处理器中科院开源高性能核心github.com/OpenXiangShan

相关课程

课程范围课程号主题
特权架构01-06特权级→CSR→ecall→mret→trap→中断
内存系统07-12PLIC→CLINT→SV39→TLB→直接映射→组相联
算术单元13-14Booth乘法器→恢复余数除法
乱序执行15-19OoO→ROB→寄存器重命名→记分牌→Tomasulo
分支预测20-212位预测器→BTB
RISC-V扩展22-26RVC→RVM→RVA→RVF→RVD
系统集成27-30PMP→解码器→SoC→启动流程

实验环境搭建

建议使用以下环境进行实验:

📊 除法器延迟与面积权衡

方法32位延迟面积(K gates)精度
恢复余数64 cycles3精确
非恢复余数32 cycles4精确
SRT-416 cycles8精确
SRT-811 cycles15精确
Newton-Raphson6-8 cycles20+近似(需要修正)
查表+迭代4-6 cycles30+近似(需修正)

SRT除法器

SRT除法器以Sweeney、Robertson、Tocher三人命名,是目前最快的硬件除法算法:

SRT-4 除法原理: 每步选择商位: q_i ∈ {-2, -1, 0, 1, 2} 选择规则: 根据部分余数的前几位和除数的前几位 优点: • 冗余商表示: 允许后续修正 • 每步多位: SRT-4每步2位, SRT-8每步3位 • 部分余数有界: |P| < 2/3 × D 缺点: • 查找表(商选择函数): 需要ROM或组合逻辑 • Intel Pentium的FDIV bug: 查找表有5个错误项, 导致某些除法结果错误 1994年, 损失4.75亿美元

Intel Pentium FDIV bug是计算机史上最著名的硬件bug之一,正是因为SRT除法器的查找表设计错误导致的。此后,硬件除法器的形式化验证变得至关重要。

🔬 非恢复余数除法详解

非恢复余数除法消除了恢复步骤,每步固定一次加减运算:

非恢复余数除法算法: 初始化: A=0, Q=被除数, M=除数 每步: if A >= 0: // 上一步够减 A = (A << 1 | Q[msb]) - M // 左移后减 Q[0] = (A >= 0) ? 1 : 0 else: // 上一步不够减 A = (A << 1 | Q[msb]) + M // 左移后加! Q[0] = (A >= 0) ? 1 : 0 最后: 如果A < 0, A += M (恢复余数) 10 ÷ 3 = ? A=0 Q=1010 M=0011 Step1: A≥0, A=(00000<<1|1)-0011=00001-0011=-0010, A<0, Q[0]=0 Step2: A<0, A=(-0010<<1|0)+0011=-0100+0011=-0001, A<0, Q[0]=0 Step3: A<0, A=(-0001<<1|1)+0011=-0001+0011=0010, A≥0, Q[0]=1 Step4: A≥0, A=(0010<<1|0)-0011=0100-0011=0001, A≥0, Q[0]=1 Q=0011=3, A=0001=1 ✓ 余数无需恢复(A≥0)

非恢复余数除法比恢复余数除法每步少一次条件加法,性能提升约30%。这是硬件除法器最常用的基础算法。