一直很想学FPGA,去年11月份买了一套FPGA的开发板,玩了没两天,换了新的工作,那套开发板也束之高阁了。最近公司做了一个《时间管理》的培训,发现自己这一年总是在浪费时间没有什么进步,很后悔。然后决定好好学习一下特权同学的《深入浅出玩转FPGA》那套视频,因为还有些基础在,就直接跳到lesson12—乘法器设计实验。
看特权同学的视频,一直没有弄明白最关键的乘法操作的两句话(红色字体)。
else if (i > 5'd0 && i < 5'd16)
begin
if(areg[i-1]) yout_r <= {1'b0,yout_r[30:15]+breg,yout_r[14:1]};
else yout_r <= yout_r >> 1;
end
else if (i == 5'd16 && areg[15]) yout_r = yout_r[31:16] + breg;
然后自己在在草稿纸上慢慢演算,弄明白了二进制乘法是怎么相乘的。
我们以四位数乘以四位数举例:用4位二进制乘数的最高位乘以被乘数时,得出的结果最高只可能出现在第7位上(从广义上说就是结果的次高位上),所以第八位(广义上说就是最高位)只是用来准备存放进位的。
1010(被乘数)
X1011(乘数)
---------------------------
乘数的最低位为1 得到0101 0000(右移累加)
乘数的次低位为1 得到0111 1000(右移累加)
乘数的第三位为0 得到0011 1100(右移)
乘数的最高位为1 得到0110 1110(右移累加)
我总结的规律就是乘数位上为1,结果先右移一位,次高位开始累加乘数。
乘数位上为0,结果就只右移一位。
我感觉我总结的规律与特权同学的不一样,我先验证我所得到规律是否正确。
我写的乘法器的源码:
module ex(
clk,rst_n,
ain,bin,start,yout,done
);
input clk,rst_n,start;
input[15:0] ain;
input[15:0] bin;
output done;
output[31:0]yout;
reg[15:0]areg;
reg[15:0]breg;
reg[31:0]yout_r;
reg done_r;
reg[4:0]i;
always @(posedge clk or negedge rst_n)
if(!rst_n) i <= 5'd0;
else if(start == 1 && i<=5'd16) i <= i + 1'b1;
else if(!start) i <= 5'd0;
always @(posedge clk or negedge rst_n)
if(!rst_n) done_r <= 1'b0;
else if(i == 5'd16) done_r <= 1'b1;
else if(i == 5'd17) done_r <= 1'b0;
assign done = done_r;
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
areg <= 16'h0000;
breg <= 16'h0000;
yout_r <= 32'h00000000;
end
else if(start) begin
if(i == 5'd0) begin
areg <= ain;
breg <= bin;
end
else if(i > 5'd0 && i < 5'd17) begin
if(areg[i-1]) yout_r = {1'b0,yout[31:16]+breg,yout_r[15:1]};
else yout_r <= yout_r>>1;
end
end
end
assign yout = yout_r;
endmodule
testbench:
module ex_tb;
reg clk;
reg rst_n;
reg start;
reg[15:0] ain;
reg[15:0] bin;
wire[31:0] yout;
wire done;
ex i_ex
(
.clk (clk),
.rst_n (rst_n),
.ain (ain),
.bin (bin),
.start (start),
.yout (yout),
.done (done)
);
initial begin
clk = 0;
forever
#10 clk = ~clk;
end
initial begin
rst_n = 1'b0;
start = 1'b0;
ain = 16'd0;
bin = 16'd0;
#1000;
rst_n = 1'b1;
#1000;
ain = 16'd65535;
bin = 16'd32768;
#100;
start = 1'b1;
#4500;
start = 1'b0;
#1000_000;
$stop;
end
endmodule
modelsim跑出来的结果:
计算器所得的结果
说明我的代码应该完全正确。
特权同学代码仿真出来的结果:
发现是错误的。
特权同学的代码错在哪儿?以下是网友的分析:
细心的读者估计已经发现{1'b0,yout[30:15]+breg,yout_r[14:1]}这条语句中位数怎么算都是31位啊,明明应该是32位的嘛(视频里也说是32位的啊)为什么会出现这个bug呢?
这里,有些网友是这样解释的yout[30:15]+breg这两个16位数相加如果足够大,肯定是会产生进位的,所以就成了17位。呵呵,现在刚好17+14+1=32位了吧。
但是这个解释有一个很明显的bug:那要是没有进位呢?不还是16位的吗?
笔者认真考虑了一个晚上想了很多解释最终觉得以下两个解释,虽然自己也不是很有信心,但勉强可以支撑一下这个算法:
1,对于verilog语言“+”会产生一个一个加法器,而加法器都是会带进位位的,所以yout[30:15]+breg会自动产生一个进位位,变成17位。
2,即使上面一个说法不成立,那么当{...}中不足32位时应该会在最高位自动补0或者X吧,这样最终依然是32位。并且不影响后面的计算结果。
对于这样两种猜想,由于本人能力有限,且刚刚接触FPGA所以不敢保证不是谬论,还希望高手们要多多指教。
上面啰嗦了这么多,其实只是对特权大哥代码的一个解读,真正有问题的是下一句:
根据上面总结出的那个规律,最高位只是用来进位用的那么这句代码:
yout_r[31:16]<=yout_r[31:16]+breg;
就明显有问题了,如果两个加数相加产生进位该怎么办呢?
暂时就写这么多,有什么不对的地方欢迎大家批评指正!!!
用户1840013 2015-10-8 20:47
wiliamzhou_446210705 2014-8-27 22:42
用户1774260 2014-8-20 14:18
ilove314_323192455 2014-8-13 19:22
用户1711475 2014-8-13 11:15