乘法器在数学运算中应用及其广泛,在当代的计算机系统,数字信号处理系统等都是必备处理部件。因此本节内容介绍二进制乘法器的原理及设计实现。
1.一位乘法器设计
一位乘法器P=a * b; a,b都是1 bit 变量,P是1 bit的运算结果。真值表如表1:
表1:
| 输入变量 | 积 | |
| a | b | P |
| 0 | 0 | 0 |
| 0 | 1 | 0 |
| 1 | 0 | 0 |
| 1 | 1 | 1 |
一位乘法器的逻辑表达式为: P = ab; 或写为P = a & b;
从表达式的可以看出一位乘法器非常简单,其实就是两个变量相与。那么多位乘法器的结果又如何呢?
2.多位乘法器设计
- 多位乘一位的乘法器
多位乘以一位的乘法器也很简单,只要被乘数的各个位与乘数相“与”,然后拼接起来就可以得到乘法结果。
例如4 * 1的乘法器,P = abcd * m = {a & m, b & m, c & m, d & m};
注意这里的花括号{}是Verilog语法中的位拼接运算符。
- 多位乘以多位的乘法器
多位乘以多位的乘法器中,由于乘数的不同位的权值不同,因此是带权值的一位乘多位的相乘相加过程。
以4 * 4的乘法器为例介绍运算过程。P = abcd * efgh.
P = abcd x h x 20 + abcd x g x 21 + abcd x f x 22 + abcd x e x 23
注:x 表示乘法
由于任何数乘以2的幂相当于该数左移 幂的值,因此上式又可以改写如下:
P = {abcd x h } + {abcd x g, 1’b0} + {abcd x f, 2’b00} +{ abcd x e, 3’b000}; 展开后的结果如下,
P={a &h, b & h, c & h, d & h}+{a & g, b & g, c & g, d & g, 1’b0}+{a & f, b & f, c & f, d & f, 2’b00}+{a & e, b & e, c & e, d & e, 3’b000}; (1)
式(1)用竖式表达如下:
竖式中在相加的过程中要考虑每次相加产生的进位问题。
在Verilog的语法中参加运算的变量会自动找出位宽最宽的变量,按照该变量的位宽对参与运算的变量进行左扩展(在左边补相应位数的0),而且进位位运算自动实现。
例1 :用Verilog HDL 描述4 * 4的乘法器
实体描述
module mul4x4
(
input [3:0] a,
input [3:0] b,
output [7:0] P
);
assign P = ({a & {4{b[3]}}, 3'b0}) + ({a & {4{b[2]}}, 2'b0}) + ({3'b0,a & {4{b[1]}}, 1'b0}) + (a & {4{b[0]}}) ;
endmodule
上式中,乘积项({a & {4{b[2]}}, 2’b0})与 (a & {4{b[0]}})虽然位数不足8位,但由于表达式的运算结果在参与下一级运算时会自动位扩展(左扩展),因此左边扩展的0可以省略,但右边需要补的零不能省略,如:
式({a & {4{b[3]}}, 3’b0}),式({a & {4{b[2]}}, 2’b0}) 及 ({3’b0,a & {4{b[1]}}, 1’b0})右边的零表示移位,因此不能省略。
仿真程序tb.v
module tb( );
reg [3:0] ta, tb;
wire [7:0] tp;
integer i, j;
initial
begin
ta = 0;
tb = 0;
i = 0;
j = 0;
#10
for(i = 0; i < 16; i = i + 1) begin
for(j = 0; j < 16; j = j + 1)
#10 tb = tb + 1;
ta = ta + 1;
end
end
mul4x4 mul4x4_inst
(
.a (ta),
.b (tb),
.P (tp)
);
endmodule
Modelsim的仿真结果如图1:

图1
-
- 注意事项:
- 在上面的描述中混合使用的Verilog的”与”运算符&(如:a&b)和数字电路的”与”(如:ab),请根据上下文的内容加以区别。
- 在做加法运算中混合使用了多项加法,位扩展等运算符,并使用了赋值语句,因此在此处一定要注意:加法之间的各项要用括号()隔离,同时要把各个表达式中位宽最宽的表达式放在最左边(({a & {4{b[3]}},3’b0})),依次是较低位宽项。如:
- 注意事项:
assign P=({a & {4{b[3]}},3’b0})+({a & {4{b[2]}},2’b0})+({a & {4{b[1]}},1’b0})+ (a & {4{b[0]}}) ;
如果没有()隔离,仿真结果将会出现错误,这一点在Modelsim以及Vivado 自带的仿真工具都得到验证。对于赋值语句:
assign P={a & {4{b[3]}},3’b0}+{a & {4{b[2]}},2’b0}+{a & {4{b[1]}},1’b0}+ a & {4{b[0]}} ;
以及赋值语句:
assign P= (a & {4{b[0]}})+({3’b0,a & {4{b[1]}},1’b0})+({a & {4{b[2]}},2’b0})+ ({a & {4{b[3]}},3’b0});
都会得到错误的仿真结果,错误的仿真结果如图2:

图2
从图2可以看出,当ta==14, tb==2时,应该是28,然而运算结果tp=0;这显然与实际期望的运算结果不符,因此在使用时应特别注意。
- 上面的问题还可以用定义临时变量的解决办法,因为纯的的变量或向量可以自动实现位扩展。
例2:
module mul4x4
(
input [3:0] a,
input [3:0] b,
output [7:0] P
);
//先将运算表达式赋值给中间变量 pa, pb, pc ,pd
wire [3:0] pa = a & {4{b[0]}};
wire [4:0] pb = {a & {4{b[1]}}, 1'b0};
wire [5:0] pc = {a & {4{b[2]}}, 2'b0};
wire [6:0] pd = {a & {4{b[3]}}, 3'b0};
assign P = pa + pb + pc + pd;
endmodule
例2的仿真结果如图3:

图3
从图3中可以看出,采用例2的写法,仿真结果也是正确的。
- 多位乘法器可以直接利用乘法运算符*,由于*是可综合的运算符,综合工具可以把乘法表达式变成实现RTL或门级实现。
例4:
module mul4x4
(
input [3:0] a,
input [3:0] b,
output [7:0] P
);
assign P = a * b;
endmodule
乘法器的应用一般都在时序电路使用,因此位数很宽乘法会影响时序收敛,这部分内容在讲解完时序电路设计后会继续探讨,如何采用流水线提高乘法器运算的性能,请参见”如何利用流水线提高系统性能”
对应视频:
