这篇会来教大家写一个乘法器,那麽你可能会想:为什麽会需要乘法器呢?像我在 quartus 或 Vivado 里打乘号也可以有乘法器用啊
那是因为在 EDA tool 里面已经帮你在你使用乘号时合成出了他们已经写好的乘法器了,那为什麽有这个不用还要另外设计呢?因为乘法器的种类有很多种,每一种也都有着不同的优缺点,所以通常会根据自己的需求来去设计一个最适合的乘法器,就例如 pipelined 乘法器,虽然把乘法拆成了数个步骤算,但是却可以增加 throughput。
那麽我们先来看看 Booth 这个演算法
例如:nbit X nbit(P = a X b):
那麽第一步先来宣告状态吧
定义状态
/*---------parameter----------*/
localparam IDLE = 2'd0;
localparam CAL = 2'd1;
localparam SHIFT = 2'd2;
localparam FINISH = 2'd3;
定义输入/输出
输入:
输出:
module booth_multiplier(
clkSys,
rst_n,
en,
a,
b,
product,
done
);
/*---------ports declarations----------*/
input clkSys;
input rst_n;
input en;
input [31:0] a;
input [31:0] b;
output [63:0] product;
output done;
reg [63:0] product;
reg done;
宣告变数
状态逻辑
/*---------state----------*/
always@(posedge clkSys or negedge rst_n)begin
if(!rst_n)fstate <= IDLE;
else begin
case(fstate)
IDLE:begin
if(en)fstate <= CAL;
else fstate <= IDLE;
end
CAL:begin
fstate <= SHIFT;
end
SHIFT:begin
if(times == 6'd32)fstate <= FINISH;
else fstate <= CAL;
end
FINISH: fstate <= IDLE;
default:fstate <= IDLE;
endcase
end
end
输出逻辑
注:这里我是写 32bit X 32bit 的乘法器
/*---------output----------*/
always@(posedge clkSys or negedge rst_n)begin
if(!rst_n)begin
aReg <= 32'd0;
sReg <= 32'd0;
pReg <= 65'd0;
times <= 6'd0;
done <= 1'b0;
product <= 64'd0;
end
else begin
case(fstate)
IDLE:begin
aReg <= a;
sReg <= (~a + 32'd1);
pReg <= {32'd0, b, 1'b0};
done <= 1'b0;
times <= 6'd0;
end
CAL:begin
if(pReg[1:0] == 2'b01) pReg <= {pReg[64:33]+aReg ,pReg[32:0]};//20 bit of MSB add aReg
else if(pReg[1:0] == 2'b10)pReg <= {pReg[64:33]+sReg ,pReg[32:0]};//20 bit of MSB minus aReg
else pReg <= pReg;
times <= times + 6'd1;
end
SHIFT:begin
pReg <= {pReg[64],pReg[64:1]};
end
FINISH:begin
done <= 1'b1;
product <= pReg[64:1];
end
default:begin
aReg <= 32'd0;
sReg <= 32'd0;
pReg <= 65'd0;
times <= 6'd0;
done <= 1'b0;
product <= 64'd0;
end
endcase
end
end
endmodule
`timescale 10ns/10ns
module tb_booth();
reg clk;
reg rst_n;
reg signed [31:0] a;
reg signed [31:0] b;
reg en;
wire done_sig;
wire signed [63:0] product;
booth_multiplier UUT(
.clkSys(clk),
.rst_n(rst_n),
.en(en),
.a(a),
.b(b),
.product(product),
.done(done_sig)
);
initial begin
clk = 0;
rst_n = 0;
a = 0;
b = 0;
en = 0;
repeat(5)@(posedge clk)rst_n = 0;
#10 rst_n = 1;
test(32'd1, -32'd1);
test(-32'd2, -32'd2);
test(32'd3, -32'd3);
test(32'd4, -32'd4);
test(32'd5, -32'd5);
test(32'd6, 32'd6);
test(-32'd7, -32'd7);
test(32'd8, -32'd8);
test(32'd9, -32'd9);
test(32'd10,-32'd10);
test(32'd101, -32'd110);
test(-32'd299, -32'd289);
test(32'd330, -32'd3);
test(32'd4986, -32'd4452);
test(32'd5243, -32'd575);
test(32'd2, 32'd67896);
test(-32'd7, -32'd1334);
test(32'd812, -32'd802);
test(32'd0, -32'd9);
test(32'd1000,-32'd123);
#1000 $stop;
end
always #10 clk = ~clk;
task test;
input signed [19:0] in1;
input signed [19:0] in2;
begin
a = in1;
b = in2;
#40 en = 1;
#20 en = 0;
wait (done_sig == 1);
end
endtask
endmodule
像这边在写 TestBench 时有很重复性动作时就可以写成一个 task,可以大大提升效率,而 task 之间总要等电路计算完才给下一笔测试资料嘛,所以在这边也有用到 wait 语法,让每一个 task 都收到模组的 done 时才继续往下。
Wave
可以看到计算结果都完全正确,那麽今天的教学就到这边~
<<: 用 Python 畅玩 Line bot - 07:Audio message
>>: 找LeetCode上简单的题目来撑过30天啦(DAY26)
资安领域辽阔,依事件处理阶段粗分为: 事前:预防 事中:识别与监控 事後:调查与预防同问题再发生 D...
为什麽要选择laravel 我们常说:「不以规矩不能成方圆。」对一个程序初心者来说,写程序最容易遇到...
这是今天要介绍的详细一点的数据类型: 整型(int/integer) 浮点型(float) 布尔型(...
今天终於要进入测试API的阶段啦~~~ 不过在进入测试阶段之前,还是要来介绍一下我们的工具要怎麽使用...
昨天我们介绍了如何用 KDoc 语法标记程序码并用 Dokka 来产生 API 文件,今天我们要将产...