热度 29
2014-8-10 10:44
2215 次阅读|
5 个评论
一直很想学FPGA,去年11月份买了一套FPGA的开发板,玩了没两天,换了新的工作,那套开发板也束之高阁了。最近公司做了一个《时间管理》的培训,发现自己这一年总是在浪费时间没有什么进步,很后悔。然后决定好好学习一下特权同学的《深入浅出玩转FPGA》那套视频,因为还有些基础在,就直接跳到lesson12—乘法器设计实验。 看特权同学的视频,一直没有弄明白最关键的乘法操作的两句话(红色字体)。 else if (i 5'd0 i 5'd16) begin if(areg ) yout_r = {1'b0,yout_r +breg,yout_r }; else yout_r = yout_r 1; end else if (i == 5'd16 areg ) yout_r = yout_r + 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 ain; input bin; output done; output yout; reg areg; reg breg; reg yout_r; reg done_r; reg 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 ) yout_r = {1'b0,yout +breg,yout_r }; else yout_r = yout_r1; end end end assign yout = yout_r; endmodule testbench: module ex_tb; reg clk; reg rst_n; reg start; reg ain; reg bin; wire 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 +breg,yout_r }这条语句中位数怎么算都是31位啊,明明应该是32位的嘛(视频里也说是32位的啊)为什么会出现这个bug呢? 这里,有些网友是这样解释的yout +breg这两个16位数相加如果足够大,肯定是会产生进位的,所以就成了17位。呵呵,现在刚好17+14+1=32位了吧。 但是这个解释有一个很明显的bug:那要是没有进位呢?不还是16位的吗? 笔者认真考虑了一个晚上想了很多解释最终觉得以下两个解释,虽然自己也不是很有信心,但勉强可以支撑一下这个算法: 1,对于verilog语言“+”会产生一个一个加法器,而加法器都是会带进位位的,所以yout +breg会自动产生一个进位位,变成17位。 2,即使上面一个说法不成立,那么当{...}中不足32位时应该会在最高位自动补0或者X吧,这样最终依然是32位。并且不影响后面的计算结果。 对于这样两种猜想,由于本人能力有限,且刚刚接触FPGA所以不敢保证不是谬论,还希望高手们要多多指教。 上面啰嗦了这么多,其实只是对特权大哥代码的一个解读,真正有问题的是下一句: 根据上面总结出的那个规律,最高位只是用来进位用的那么这句代码: yout_r =yout_r +breg; 就明显有问题了,如果两个加数相加产生进位该怎么办呢? 暂时就写这么多,有什么不对的地方欢迎大家批评指正!!!