硬件乘法专题-Booth-Wallace乘法

硬件乘法专题-Booth-Wallace乘法

本文提到的算法在 Github 开源:https://github.com/devindang/openip-hdl
该算法的设计文件也在此CPU设计中应用:https://github.com/devindang/dv-cpu-rv

有符号乘法

考虑一个8比特乘8比特的乘法,有符号乘法的结果可以表示为:

\([𝑋\cdot 𝑌]_{2𝑐} =−𝑋\cdot 𝑦_7 ⋅2^7 +𝑋\cdot 𝑦_6\cdot 2^6+\cdots+𝑋\cdot 𝑦_1\cdot 2^1 +𝑋\cdot 𝑦_0\cdot 2^0=[𝑋]_{2𝑐}\cdot (−𝑦_7\cdot 2^7+𝑦_6\cdot 2^6+\cdots+𝑦_1\cdot 2^1+𝑦_0\cdot 2^0)\)

其中,2c意为“2的补码”,在后续的表达式中,我将省略该符号,此处只讨论有符号乘法的运算。

这是最一般的二进制乘法的形式。假设两个数的位宽均为N,这种乘法需要计算的全位宽(N)加法的次数为N。

考虑以下例子,-13*6=-78,其中-13是被乘数(multiplicand),6是乘数(multiplier)。最后一个部分积是全0,在图中被省略。

image

下面的例子是,13*-6=-78,与上面的例子具有相同的结果。

image

与无符号乘法不同的地方有:

1). 每个部分积均需符号扩展;
2). 最后一个部分积由减法运算产生,而非加法。

基2 Booth乘法

Booth乘法器对上式做变换,得到以下这种形式:

\([𝑋⋅𝑌]_{2𝑐} =[𝑋]_{2𝑐}\cdot(−𝑦_7\cdot 2^7+𝑦_6\cdot 2^6+\cdots +𝑦_1\cdot 2^1+𝑦_0\cdot 2^0)\)

\(=−𝑦_7\cdot 2^7+𝑦_6\cdot 2^6−𝑦_6\cdot 2^6+\cdots +𝑦_1\cdot 2^2−𝑦_1\cdot 2^1+𝑦_0\cdot 2^1−𝑦_0\cdot 2^0\)

\(=(𝑦_6 −𝑦_7)\cdot 2^7 +(𝑦_5 −𝑦_6)\cdot 2^6 +\cdots +(𝑦_0 −𝑦_1)\cdot 2^1 +(𝑦_{−1} −𝑦_0)\cdot 2^0\)

其中, \(𝑦_{−1} = 0\) . 通过变换, 有符号乘法运算的规律性被显现出来, 最后一个部分积的产生无需再乘以-1(减法). 这种算法称为基2 Booth算法,这是由于我们使用2比特产生了对应的1比特的“乘数”来产生部分积。

部分积的产生由 \(y_i y_{i-1}\) 共同确定:

\(y_i\) \(y_{i-1}\) \(z_i\) 注释 z neg
0 0 0 无需操作 0 0
0 1 1 +X 1 0
1 0 -1 -X (2的补码) 1 1
1 1 0 无需操作 0 0

理论枯燥,考虑下面的例子,计算13x6:

image

注意灰色的"1"并非代表真实的进位,而是因为我省略了部分积中的部分符号位扩展(第二个部分积),计算结果一定是正数。

基4 Booth算法

让我们来讨论基 4 Booth 算法, 它是在现代 CPU 设计和 DSP 设计中最常用到的一种实现方式。

首先看基4 Booth算法中对于计算公式的变形:

\([𝑋\cdot 𝑌]_{2𝑐} =(𝑦_5 +𝑦_6 −2𝑦_7)\cdot 2^6 +(𝑦_3 +𝑦_4 −2𝑦_5)\cdot 2^4 +\cdots+(𝑦_{−1} +𝑦_0 −2𝑦_1)⋅2^0\)

Radix-4 Booth 算法不是将 2 个部分积划分到一起,而是将每 3 个部分积划分到一起,并每 2 位执行求和,或者说,乘数移位 2 位。 基4意味着,它将乘数设置为一组数字 0-3,而不仅仅是 0,1(基本阵列乘法,或基2 Booth 算法)。 这会将部分积的数量减少一半,因为您一次乘以两个二进制位。 然而,它需要乘以3,这是困难的。 (乘以 0、1 或 2 是微不足道的,因为它们只涉及简单的移,但是乘 3 是困难的)为了避免乘以 3,我们将 Booth 数字集重新编码为 2, 1, 0, ‐1 和‐2。

\(y_{i+1}\) \(y_i\) \(y_{i-1}\) \(z_i\) 注释 z0 z1 neg
0 0 0 0 无需操作 0 0 0
0 0 1 1 +X 1 0 0
0 1 0 1 +X 1 0 0
0 1 1 2 +2X 0 1 0
1 0 0 -2 -2X (2的补码) 0 1 1
1 0 1 -1 -X (2的补码) 1 0 1
1 1 0 -1 -X (2的补码) 1 0 1
1 1 1 0 无需操作 0 0 0

z0, z1, neg 是在本设计中使用到的信号,其中z0意味着需要做1比特的移位,z1意味着需要做2比特的移位,neg意味着需要做减法运算(比特取反再+1)

z0, z1, neg 的产生很容易,可以通过简单的组合逻辑来实现,最简单的方式是从真值表来推断,其中:

\(z0=y_i \oplus y_{i-1}\)

\(z1 = (y_{i+1} \overline{y_i}) \overline{y_{i-1}} + (\overline{y_{i+1} y_i y_{i-1}})\)

\(neg=y_{i+1} \cdot \overline{(y_i \cdot y_{i-1})}\)

booth_encoder 模块用于产生这三个信号。当我们获得了这三个信号后,下一步操作是执行加法,减法,或移位操作来产生部分积送给 Wallace 树。为了实现这一逻辑, booth_selector 模块根据这三个信号来产生单比特的部分积。

单比特的部分积这样产生:

  1. 如果z1为1,则使用低1位的数字来产生部分积(等效于乘以2);
  2. 如果z0为1,则使用当前数字产生部分积;
  3. 如果neg为1,则仅对每一位取反,否则不做变换;
  4. 在每一级的N个比特部分积产生完成后,加上neg作为最终部分积。

下面我们看一个例子,这个例子是 13x6=78:

image

第一步:给乘数尾部添0,得到 00000110(0);
第二步:对100进行Booth编码,图中,每一个yi均使用下划线标示出了,100意味着-2X,z0=0, z1=1, neg=1;
第三步:产生第一个部分积,被乘数需要左移移位,换句话说,当前比特的部分积使用低1位的数字(这也是在本设计中实现的方法),得到00011010,因为neg是1,所以所有比特需要反转,得到11100101,同时需要加上1(也就是neg的值),neg的值被存入了一个叫做n的寄存器中,送往一个行波进位加法器来产生最终的部分积。

        RCA #(65) u_rca(
            .a    (pp[u]),
            .b    ({64'd0,n[u]}),
            .cin  (1'b0),
            .sum  (pp2c[u]),
            .cout ()
        );

第四步:检查加下来的3比特,011意味着+2X,无需做2的补码变换。
第五步:继续操作,接下来两个部分乘数由000和000产生,意味着无需操作。
第六步,将所有部分积加起来,得到01001110(78).

你应当注意到了 0000 和 00 之间的实线,我们会在下一节讨论,这是与 Wallace 树相关的。

Booth编码将总的加法操作次数从N减少到了N/2,一半的加法运算被简单的移位和NOP(无操作)给替代了。

Wallace 树

回到上一节中的示例,实线是为 Wallace 树乘法放置的。 Wallace树将每3个Booth算法的部分积作为输入,并为下一阶段输出2个结果。下图示出了一种32位Wallace树,继续以这个例子为例,11100101被发送到最右边的CSA,同样,01101000和00000000(第三个部分积)、00000000(实线下的第四个部分积) 被送到右侧第 2 个 CSA 单元。

CSA单元:
Carray Save Adder,进位保留加法器,这是Wallace树的基本结构,他不产生进位结果,而是将每一位的进位直接输出,例如我们做一个64+64的加法,如果是行波进位加法器(RCA, Ripple Carry Adder),每一比特产生的进位都需要传递给下一位,从而至少64个加法器周期的延迟是必须的。而如果采用CSA,每一位的进位都直接输出,仅仅有一个加法器周期的延迟。这对时序是非常友好的。

Wallace树的结构可以通过下面的表进行推理,假设所有的操作数都是经过booth编码后的。

级数 独立输入 保留 输入 输出
0 0 2 10*3 10 *2
1 1 1 7*3 7 *2
2 1 0 5*3 5 *2
3 0 1 3*3 3 *2
4 0 1 2*3 2 *2
5 0 2 1*3 1 *2
6 1 1 1*3 1 *2
7 1 0 1*3 1 *2

用于乘法运算的 Wallace 树的基本加法器单元有 3 个输入 a、b、cin 和 2 个输出 sum、cout。 它使用 CSA(进位保留加法器)而不是 RCA(行波进位加法器),其中保存进位以供进一步加法,而不是行波到较高位,从而节省了延迟。

对于 stage 0,总输入个数32,有30个被送入10个CSA,剩下两个进行保留,在后面的CSA中进行运算;
对于 stage 1,总输入个数包括2个保留的内容,以及102个上一级的输出,共22个,因此有21个参与运算,使用2个保留的部分积中的1个作为独立输入,此时保留2个,共73个被送入CSA。
以此类推,可以画出下面的 Wallace 树。

image

可以在适当的地方插入寄存器使得整个乘法器进行流水计算,比如在第二级,第五级,第八级插入寄存器,为Booth编码提供一定的建立时间裕量,如果时序不满足,可以增大流水线级数。

写在后面

完整的代码均在 Github 开源,包括完整的 testbench 文件,以及 modelsim 或者 vcs+verdi 的脚本,您可以一键运行仿真来学习 booth-wallace 算法,为中国的芯片行业以及开源事业贡献一份绵薄的力量!

参考资料

[1] 胡伟武. 计算机体系结构基础. 第3版, 机械工业出版社, 2021. Link

posted @ 2023-08-16 11:25  devindd  阅读(1092)  评论(0编辑  收藏  举报