在硬件设计中,乘法器是非常重要的一个器件,乘法器的种类繁多,常见的有并行乘法器、移位相加乘法器和查找表乘法器,并行乘法器的实现非常简单,在Verilog中只需要通过assign dout=a*b实现即可,若要进行有符号的乘法,只需在变量前加上$signed。查找表乘法器实际上是先将乘法的计算结果提前算好,这样就可以在计算时通过查表的方式直接得到结果,一般用于位宽较小的情况。移位相加乘法器是一种耗费较少资源的算法,其思想是将乘法转化为加法和移位运算,不足之处是计算过程的延时较长,一般用于对性能要求不高的场合。
无符号乘法器
移位相加乘法器的原理:
以a=1011和b=1001相乘为例(均为无符号数)
1.b的第0位为1,所以
result=result+a<<0;
2.b的第二位为0,result保持不变
3.b的第三位也为0,保持不变
4.b的第四位为1,则
result=result+a<<3,得到最终的计算结果
可以看到,W位宽的无符号乘法器,需要W个周期才能计算完成。
下面是无符号乘法的Verilog实现:
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2022/03/18 21:37:17
// Design Name:
// Module Name: demo
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module demo
(
input logic clk,
input logic rst_n,
input logic vld,
input logic [7:0] dina,
input logic [7:0] dinb,
output logic [15:0] dout,
output logic ovld
);
logic [7:0] reg_a; //被乘数
logic [7:0] reg_b; //乘数
logic [15:0] psum;
logic [15:0] T;
logic busy;
logic one_bit;
logic [3:0] cnt;
//
always_ff@(posedge clk,negedge rst_n)
if(~rst_n)
begin
reg_a<=0;
end
else if(vld)
begin
reg_a<=dina;
end
always_ff@(posedge clk)
if(vld)
psum<=0;
else if(busy&&one_bit) //
psum<=psum+T;
else
psum<=psum;
//T
always_ff@(posedge clk)
if(vld)
T<=dina;
else if(busy)
T<=(T<<1);
//reg_b
always_ff@(posedge clk)
if(vld)
reg_b<=dinb;
else if(busy)
reg_b<={1'b0,reg_b[7:1]};
//one_bit
assign one_bit=reg_b[0];
//busy
always_ff@(posedge clk)
if(vld)
busy<=1;
else if(busy&&cnt==7)
busy<=0;
//cnt
always_ff@(posedge clk)
if(vld)
cnt<=0;
else if(busy)
cnt<=cnt+1;
//dout
assign dout=psum;
//ovld
always_ff@(posedge clk,negedge rst_n)
if(~rst_n)
ovld<=0;
else if(busy&&cnt==7)
ovld<=1;
else
ovld<=0;
endmodule
经过仿真,发现上述代码功能正确。
有符号乘法器
我们通过调用无符号乘法器,来实现有符号乘法。具体方法为
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2022/03/23 13:08:23
// Design Name:
// Module Name: signed_mult
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module signed_mult(
input logic clk,
input logic rst_n,
input logic vld,
input logic [7:0] dina,
input logic [7:0] dinb,
output logic [15:0] dout,
output logic ovld
);
logic sign_a;
logic sign_b;
logic sign_o;
logic [7:0] unsigned_a;
logic [7:0] unsigned_b;
logic [15:0] unsigned_o;
assign sign_a=dina[7];
assign sign_b=dinb[7];
assign sign_o=sign_a^sign_b; //1为负,0为正
assign unsigned_a=(sign_a==1'b1)?~dina+1:dina;
assign unsigned_b=(sign_b==1'b1)?~dinb+1:dinb;
assign dout=(sign_o==1'b1)?~unsigned_o+1:unsigned_o;
//
demo unsigned_mult
(
.clk(clk),
.rst_n(rst_n),
.vld(vld),
.dina(unsigned_a),
.dinb(unsigned_b),
.dout(unsigned_o),
.ovld(ovld)
);
endmodule
以下是仿真波形,测试了a,b均为正,均为负数,一正一负四种情况,结果均正确: