乘法器在数学运算中应用及其广泛,在当代的计算机系统,数字信号处理系统等都是必备处理部件。因此本节内容介绍二进制乘法器的原理及设计实现。
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
乘法器的应用一般都在时序电路使用,因此位数很宽乘法会影响时序收敛,这部分内容在讲解完时序电路设计后会继续探讨,如何采用流水线提高乘法器运算的性能,请参见”如何利用流水线提高系统性能”
for(i = 0; i < 16; i = i + 1) begin
for(j = 0; j < 16; j = j + 1)
#10 tb = tb + 1;
ta = ta + 1;
老师这条语句的执行顺序是怎么样的,针对这两条for循环语句的,我看for循环语句那章也没有细讲这个,谢谢老师帮忙解答下