基本算法
移位相加即是日常我们使用的手算算法,移位相加的描述如下
- 设置积的初值为0
- 若乘数的最低位为0,则积不变,否则累加被乘数
- 若乘数的第一位为0,则积不变,否则累加向左移位一位的被乘数
- ...
- 若乘数的第n位(最高位)为0,则积不变,否则累加向左移位n位的被乘数
RTL代码
module serial_shiftadder_multipcation # (
parameter WIDTH = 4
)(
input clk, // Clock
input rst_n, // Asynchronous reset active low
input multiplier_valid,
input [WIDTH - 1:0]multiplier1,
input [WIDTH - 1:0]multiplier2,
output reg product_valid,
output reg [2 * WIDTH - 1:0]product
);
接口定义部分,采用参数化设计,WIDTH为乘数/被乘数的位宽,multiplier_valid拉高时表示输入有效,并开始计算。product_valid被拉高时表示计算完成,当前的输出是有效的
/*****************buffer and shift*******************/
reg [WIDTH - 1:0]min_mult;
reg [2 * WIDTH - 1:0]max_mult;
always @ (posedge clk or negedge rst_n) begin
if(~rst_n) begin
{max_mult,min_mult} <= 'b0;
end else if(multiplier_valid == 1'b1) begin
if(multiplier1 > multiplier2) begin
max_mult <= '{multiplier1};
min_mult <= multiplier2;
end else begin
max_mult <= '{multiplier2};
min_mult <= multiplier1;
end
end else if(min_mult != 'b0) begin
max_mult <= max_mult << 1;
min_mult <= min_mult >> 1;
end else begin
max_mult <= max_mult;
min_mult <= min_mult;
end
end
移位部分,有输入时比较两个输入的大小,使用小的数控制迭代数量,减小时间消耗。若较小的乘数不为0,则将较大的数向左移位,较小的的数向右位移。
/******************adder********************/
always @ (posedge clk or negedge rst_n) begin
if(~rst_n) begin
{product_valid,product} <= 'b0;
end else if(min_mult[0] == 1'b1) begin
product <= product + max_mult;
product_valid <= 1'b0;
end else if(min_mult != 'b0) begin
product <= product;
product_valid <= 1'b0;
end else if(multiplier_valid == 1'b1) begin
product <= 'b0;
product_valid <= 1'b0;
end else begin
product <= product;
product_valid <= 1'b1;
end
end
endmodule
累加部分,若较小的乘数最低位为0,保持积不变,否则累加当前的大乘数,当小乘数为0是,表示运算已经结束,输出有效拉高。
测试
使用自动化测试,使用高层次方法计算积,再与输出比较看是否相等
module mult_tb (
);
parameter WIDTH = 4;
logic clk,rst_n;
logic multiplier_valid;
logic [WIDTH - 1:0]multiplier1;
logic [WIDTH - 1:0]multiplier2;
logic product_valid;
logic [2 * WIDTH - 1:0]product;
serial_shiftadder_multipcation # (
.WIDTH(WIDTH)
) dut (
.clk(clk), // Clock
.rst_n(rst_n), // Asynchronous reset active low
.multiplier_valid(multiplier_valid),
.multiplier1(multiplier1),
.multiplier2(multiplier2),
.product_valid(product_valid),
.product(product)
);
initial begin
clk = 1'b0;
forever begin
#50 clk = ~clk;
end
end
initial begin
rst_n = 1'b1;
#5 rst_n = 1'b0;
#10 rst_n = 1'b1;
end
initial begin
{multiplier_valid,multiplier1,multiplier2} = 'b0;
forever begin
@(negedge clk);
if(product_valid == 1'b1) begin
multiplier1 = (WIDTH)'($urandom_range(0,2 ** WIDTH));
multiplier2 = (WIDTH)'($urandom_range(0,2 ** WIDTH));
multiplier_valid = 1'b1;
end else begin
multiplier_valid = 1'b0;
end
end
end
logic [2 * WIDTH - 1:0]exp;
initial begin
forever begin
@(posedge product_valid);
exp = multiplier1 * multiplier2;
if(exp == product) begin
$display("successfully, mult1=%d mult2=%d product=%d",multiplier1,multiplier2,product);
end else begin
$display("failed,mult1=%d mult2=%d product=%d exp=%d",multiplier1,multiplier2,product,exp);
end
end
end
endmodule