简介:这是一份基于 Verilog 的无符号 Radix-2 SRT 除法器设计资源,适合数字逻辑、FPGA 或 IC 设计初学者用于理解基2 SRT 算法的硬件实现与仿真验证。压缩包共 6 个文件,主体为 3 个 Verilog 源码文件,分别承担 RTL 除法核心、辅助子模块与测试台顶层,另附 README 说明、Matlab 仿真对比脚本及工程配置信息,整体仅 5KB,结构紧凑、便于精读与快速上手。已有 1292 人学习下载。读者可借助 RTL 主文件与测试台搭建的仿真环境,观察无符号基2 SRT 除法的商数生成、余数更新和移位控制流程;配套 Matlab 脚本还能辅助进行算法级与 RTL 级结果对照,适合作为计算机组成原理或数字设计课程中除法器章节的课下练习资料。 做硬件除法器这件事,说大不大,说小不小。前阵子在调试一个RISC-V教学核,指令集里要补一条无符号除法指令。一开始想直接调IP核,但IP核延迟、资源、时序都不是自己控制的,仿真时看不到内部状态,出了问题根本没法排。于是干脆自己写一个无符号Radix-2 SRT除法器。做完之后把整个设计思路、商选择表的产生方式、还有几个踩过的坑整理出来,希望对同样在折腾除法器的人有点帮助。
这篇内容适合谁看?如果你在做FPGA信号处理、在给CPU写除法指令、或者只是想把“除法器”这个黑盒变成自己可控的模块,都可以参考。我假设读者对二进制补码、状态机、Verilog基础语法有基本了解,但不需要你提前研究过除法器的各种论文。
1. 为什么选Radix-2 SRT:从慢速除法到快速除法
1.1 除法器的真实痛点
除法和加、减、乘不一样。加法器可以并行算完所有位的进位,乘法器可以把部分积展开成树形结构并行压缩。但除法本质上是一个“串行试商”的过程:这一拍的商选错了,会直接影响下一拍的部分余数,后面所有结果都得跟着错。所以除法器的速度瓶颈不在加减法本身,而在“每一步的商怎么选”。
传统教科书里教的恢复余数除法,每拍要做一次全宽度的减法,然后看结果符号决定商位是0还是1。如果余数为负,还要把除数加回来“恢复”余数。这一步不仅多了一次加法,而且全宽度比较器的延迟会直接落在关键路径上。不恢复余数除法省掉了恢复操作,但仍然需要一个N位加法器出结果后才能判断商的符号,周期照样快不了。
SRT算法是另一条路,它允许商位取值变成{-1, 0, +1}。这个“允许负商位”的改动看上去只是数字集合变了,实际上把“每拍必须精确判断”变成了“每拍只需要大致判断”,硬件上正是靠这个自由度做出速度的。
1.2 SRT把“精确判断”换成“容错迭代”
Radix-2 SRT除法的迭代核心是这一条:
w[j+1] = 2 * (w[j] - q[j] * D)
其中w[j]是第j拍的部分余数,D是除数,q[j]是第j拍选出的商位,只能取-1、0、+1三种值。整体约束是让部分余数始终被限制在一个安全区间内,一般要求|w|不超过一个跟D有关的常数。
这个公式真正有意思的地方在于:它不要求你把2w和D的完整大小关系比出来,只要判定2w落在哪个区域就行。判定可以基于截断的高位来实现,不必用全宽度比较器。
打个比方,传统除法像是称体重,必须精确到克才知道该往哪边调整;SRT像是看一眼指针大致在哪个区间就敢动手调,就算调过头了,下一拍还能拉回来。
1.3 常见除法器方案对比
| 方案 | 每拍产出商位数 | 商位集合 | 关键路径 | 典型用途 |
|---|---|---|---|---|
| 恢复余数除法 | 1位 | 0/1 | 全宽减法+比较 | 教学、低复杂度场景 |
| 不恢复余数除法 | 1位 | 0/1 | 全宽加法器 | 软核CPU、简单硬件除法 |
| Radix-2 SRT | 1位 | -1/0/+1 | 截断比较+加减D | 浮点尾数除法、通用除法器 |
| Radix-4 SRT | 2位 | -2/-1/0/+1/+2 | 高位比较+多路选择 | 高性能处理器 |
这张表能看出来,基2 SRT的每拍产出商位数和普通除法一样都是1位,但它把关键路径从“全宽度比较+全宽度减法”缩短成了“高位截断比较+加减D选择”。这个周期优势才是它被广泛用在浮点运算单元里的原因。
2. 关键电路拆解:商选择、数据通路与位宽设计
2.1 商选择表是怎么算出来的
整个SRT除法器最核心的模块就是商选择逻辑,它决定一拍要选哪个q。设计目标很简单:保证w[j+1]不会逃出安全范围。
假设我们约定每次迭代后|w| ≤ 2D(这个边界不同实现不一样,但思想相同)。那么对于当前w,如果2w比D大不少,选q=1是合理的;如果2w比负D小不少,选q=-1;如果2w就在中间,选q=0最安全。
工程上不可能每个周期都把w和D做全宽度比较,所以办法是只看w的高几位和D的高几位。例如,w取高5位转成4位比较量,D取高3位,然后查一张很小的表。表里的判断条件可以用Verilog的case语句实现,也可以写成一段组合逻辑判断。
这里有一个特别容易搞错的地方:公式里比较的是2w,不是w。如果你的部分余数寄存器存的是w,那做比较时要么把w左移一位再截断,要么取w的高5位然后用前4位参与判表。不少人第一次写的时候直接拿w的高4位去判,结果边界偏移了一位,仿真就会出现偶发错误。
商选择表中的阈值不一定唯一,不同的阈值得到不同的表,只要满足不收敛条件就行。我自己的做法是写一个Python脚本,把所有可能的w截断值和D截断值枚举一遍,检查在当前选择下w'是否仍然落在安全范围,不满足的组合直接标记出来。这样生成的表,比手推边界可靠得多。
2.2 部分余数寄存器与位宽选择
部分余数寄存器w的位宽是整个设计的根基。因为它可能为负,必须用补码表示,而且因为每拍要乘2,还要留出足够的进位保护位。
我通常会设置w的位宽为N+4,其中N是除数D的位宽。比N多出来的4位里,2位用来扩展符号位和整数部分,2位用来降低截断误差。太少的位置会压缩商选择表的判断区间,导致表设计非常别扭;位置太多又浪费寄存器资源。这个值是我在32位除法器里实测比较舒服的一个折中。
D的位宽可以保持N位,但如果要做规格化(后面会讲),实际参与迭代的D要左移过,所以D寄存器本身也要能容纳左移后的值。最稳妥的做法是D和w统一用N+4位宽度,减少位宽对齐的思考负担。
2.3 商重建:为什么不能直接把q塞进商寄存器
很多新手做完SRT迭代后,发现商不对,根本原因是没有理解“商位q是冗余编码”这件事。每拍选出的q是-1或0或1,它并不是最终的二进制商位,不能直接塞进一个普通移位寄存器里。
正确的做法是维护两个寄存器:一个记录“出现过+1的位置”,另一个记录“出现过-1的位置”。比如第3拍选了q=+1,那pos寄存器的第3位写1,neg寄存器第3位写0;如果某拍选了-1,就反过来。迭代结束后,真正的商等于pos减neg。
这个减法需要在最后额外做一次,但它的成本很低,不在关键路径上,完全不影响每拍的周期。如果你在做的是高基数SRT,比如基4的q集合包含±2,那么商重建会更复杂,需要多个位的编码。基2的好处就在这里,商重建只需要两个移位寄存器和一个减法器。
3. 可综合实现:Verilog框架与迭代状态机
3.1 顶层模块和端口定义
我习惯把除法器做成一个独立的模块,方便在其他地方实例化。顶层端口大概是这样的:
module div_radix2_srt #( parameter N = 32 )( input wire clk, input wire rst_n, input wire start, input wire [N-1:0] dividend, input wire [N-1:0] divisor, output reg done, output reg [N-1:0] quotient, output reg [N-1:0] remainder );start信号拉高一拍后开始运算,done信号拉高表示结果有效。这种接口在总线模块里接起来很方便,无论挂在CPU总线上还是FPGA的AXI-Lite上都能直接适配。
内部模块我分成三块:规格化预处理、迭代核心、商重建和余数调整。规格化预处理不一定在第一次做的时候就能意识到要加,但它恰恰是整数除法能不能直接用SRT的关键。
3.2 迭代状态机与规格化预处理
SRT算法天然是“小数除法”的思路,它要求被除数和除数都落在比较接近的范围内,然后迭代出一串小数商位。而这里做的是无符号整数除法,被除数A完全可能比除数D大几十倍,所以不能直接送进迭代核心。
我的做法是:
- 对D做前导零计数clz_d,把D左移clz_d位,让D的MSB为1,这样D就落在[1,2)这个归一化区间。
- 对被除数A也左移clz_d位,让A落在同一数量级下。
- 如果A仍然大于等于D,说明商的整数部分不为0。这种情况要处理:要么预先把A右移一位,并在最终商的高位补1;要么在状态机中多做一步处理。
这一步是最容易埋坑的。因为A左移clz_d之后,它的位宽可能超过了N,需要把A和D都扩宽到N+4甚至N+5位再进迭代,否则最高位被截掉,结果差一截。
迭代控制相对简单:计数器从0跑到N,每拍更新一次w。状态机有idle、compute、finish三个状态。compute阶段每个周期做一次w更新和q记录,finish阶段做商重建和余数调整。之所以要把finish单独一个状态,是因为最后的Q=pos-neg减法需要一拍,不能跟最后一步迭代挤在同一拍里。
3.3 商选择模块的可综合写法
商选择逻辑最直观的写法是一大段case语句。以4位截断余数加3位截断除数为示例,表的基本形式可以写成:
always @(*) begin case ({twoR_est, d_est}) // 2w明显大于0区间,选+1 4'b0101, 4'b0110, 4'b0111: q = 2'b01; // 2w明显小于0区间,选-1 4'b1001, 4'b1010, 4'b1011: q = 2'b11; // 中间区域选0 default: q = 2'b00; endcase end这里q我用了2bit编码,方便后续处理:2’b01表示+1,2’b11表示-1(也就是补码-1),2’b00表示0。用补码表示负q,后面做w更新和商重建都统一。
上面这个case只是为了展示写法,实际阈值不能照抄。不同位宽、不同安全区间的表项差异很大,需要用脚本生成并验证。这里强烈建议做一次全边界穷举验证,不要只靠手工挑选几个典型值。
3.4 关键路径与时序优化思路
基础版SRT每拍的关键路径是:截断比较器 → 选择器(选±D或0)→ 加法器 → 寄存器。这条路径比全宽减法短很多,因为截断比较器的逻辑深度很小,选择器和加法器才是大头。
如果时序还是不满足,可以考虑把部分余数w用进位保存加法器(CSA)来表示,也就是把w拆成sum和carry两个寄存器,每次迭代不再做完整加法,只是把新的部分积压进去。这样每拍的关键路径就缩短成一个CSA压缩器加比较器。代价是商选择逻辑要能处理两个寄存器表示的冗余值,复杂度会明显上升。
我的建议是:第一次实现先用普通补码加非冗余w,先把功能跑通,再用随机测试验边界。确认算法没有任何问题之后,再考虑用CSA做时序优化。一上来就上CSA,出错了很难定位是算法问题还是电路问题。
4. 调试实录:仿真验证与常见坑
4.1 商选择表边界不收敛
这个问题的症状很典型:仿真跑到一半,部分余数突然跳到特别大的值,之后所有商位都乱了,最终结果差得很离谱。
排查思路是打印每一拍的w、2w、q和D,盯着w有没有超出安全区间。一旦发现w超出,就说明当前这一拍商选错了。然后拿着这一拍的2w截断值和D截断值去对照商选择表,看表里是不是给了个错误的选择。
我之前遇到过一次,原因是生成商选择表时没有考虑D的高位。只看2w的高位就选q,在某些D比较小的边界上会选错。后来改成“2w高4位+D高3位联合查表”,边界情况才稳定下来。
4.2 商重建值差一个LSB
如果迭代过程完全没有越界,但最终商总是比期望值小1或大1,问题基本出在商重建的减法上。
pos和neg两个寄存器都表示的是加权和,它们的位权重关系取决于你把哪一拍当作二进制点后的第一位。如果迭代了N拍,最后减出来还要考虑截断方向。另外,如果某几拍选了q=0,pos和neg在该位都是0,这些位置不能遗漏,否则商的权重就错了。
调试办法很简单,写一个小的参考模型,所有中间信号都导出,比对pos和neg在每一拍的值。找到第一拍发生偏差的位置,再去查那一步的q是否与参考模型一致。
4.3 规格化移位后的输出换算混乱
这个坑几乎每个做整数SRT的人都会踩一次。
比如D的前导零个数是ld,A左移了ld位参与迭代,最后迭代余数w_final对应的是“归一化后的被除数”的余数,不是原始A的余数。要想得到真实余数,得把w_final右移ld位,而且要考虑负数余数时的符号扩展。
商的换算更隐蔽。因为它本质上是在一个缩放过的小数域里计算,最后的结果可能还要根据A是否大于D做修正。我踩过最痛的一次是32位里算“5除以3”,因为D的前导零很多,迭代完余数寄存器里是一个2的30次方量级的大数,代码写错了右移量,余数输出完全不对。
解决这类问题的最好办法,是在状态机里专门记录预处理的移位量,finish阶段再根据这个移位量做一次统一的商和余数校准,不要在外围总线模块里散落着各种移位操作。
4.4 验证用例与仿真脚本建议
SRT除法器的验证不能只靠几组手算用例,我建议分三层来测。
第一层是边界用例:被除数为0、除数为0(需要单独判断,否则迭代不收敛)、被除数等于除数、被除数是除数的两倍、除数最高位为1、被除数最高位为1。
第二层是随机测试:用Python或者SystemVerilog的随机数生成,跑上万组随机无符号数,和C语言或者Python里的整数除法结果逐组比对。这一步能覆盖大部分边界组合。
第三层是定向压力测试:故意构造让商选择表处于临界状态的用例。怎么构造?让被除数和除数的前几位相同,或者让部分余数w恰好卡在滞回窗口附近。这些用例往往是随机测试覆盖不到的。
我的验证脚本习惯是把所有失败用例的输入、期望输出、实际输出打到一个日志文件里,然后写一个Python脚本解析,自动算出差值分布。如果所有失败都是同一个差值,那多半是商重建或者寄存器位宽的问题,很快就能定位。
最后说一个个人习惯:每次修改商选择表或者位宽之后,第一件事不是跑完整随机测试,而是先跑一遍边界用例和上一次失败的那几个用例。因为SRT的商选择表对边界极其敏感,可能你只是把截断位宽从4位改成5位,之前验证通过的某些边界又变成不收敛了。保留一组历史回归用例,比什么都管用。
本文还有配套的精品资源,点击获取