Menu Close

Verilog 乘法器的设计及仿真注意事项

乘法器在数学运算中应用及其广泛,在当代的计算机系统,数字信号处理系统等都是必备处理部件。因此本节内容介绍二进制乘法器的原理及设计实现。

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     h   x   20 + abcd    g    21 + abcd   x   f   x   22 + abcd   x   e   23

注:x 表示乘法

由于任何数乘以2的幂相当于该数左移 幂的值,因此上式又可以改写如下:

P = {abcd  h } + {abcd  g, 1’b0} + {abcd  f, 2’b00} +{ abcd  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)用竖式表达如下:

%title插图%num

竖式中在相加的过程中要考虑每次相加产生的进位问题。

在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:

%title插图%num

图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:

%title插图%num

图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:

%title插图%num

图3

从图3中可以看出,采用例2的写法,仿真结果也是正确的。

  • 多位乘法器可以直接利用乘法运算符*,由于*是可综合的运算符,综合工具可以把乘法表达式变成实现RTL或门级实现。

例4:

module mul4x4 
( 
    input  [3:0] a,
    input  [3:0] b, 
    output [7:0] P
); 
 
assign P = a * b;
 
endmodule

 

乘法器的应用一般都在时序电路使用,因此位数很宽乘法会影响时序收敛,这部分内容在讲解完时序电路设计后会继续探讨,如何采用流水线提高乘法器运算的性能,请参见”如何利用流水线提高系统性能

 

 

 

Posted in FPGA, FPGA, IC, Quartus II, Verilog, Verilog, Vivado, 教材与教案, 文章

发表评论

相关链接