乘法器实验

注释:这段乘法器代码是自己学习特权老师后的一个总结,由于特权的视频很短(视频和具体代码可到网站上下载),讲得也不是很详细,特别是实现乘法的那段代码,经过学习,自己用左移的方式实现了乘法器,但是并不是很好。还有,由于自己初学FPGA,所以希望更多的指点和建议。

乘法器

相关理论:

乘法器是众多数字系统中的基本模块,从原理上说它是属于组合逻辑电路的范畴,但从工程实际设计上来说,它往往会利用时序逻辑设计的方法来实现,属于时序逻辑的范畴。

通过这个实验使大家能够掌握利用FPGA/CPLD设计乘法器的思想,并且能够将我们设计的乘法器应用到实际工程中,乘法器的设计方法有两种,组合逻辑设计方法和时序设计方法,采用组合逻辑设计方法电路事先将所有的乘积项全部计算出来,最后加法运算,采用时序逻辑设计方法,电路将已经得到的乘积项右移,然后与乘积项相加并保存和值,反复迭代上述步骤直至计算出最终结果。

在该实验中就是要利用时序;逻辑设计方法来设计一个16位乘法器,则就会利用时钟信号控制乘法运算。用时序逻辑设计方法与组合逻辑设计方法比较,它有什么好处呢?利用时序逻辑设计方法可以使整体设计具备流水线结构特征,能适用在各种实际设计中。

在提及乘法器的速度时,可以先了解一下数据吞吐量的概念,数据吞吐量使指芯片在一定时钟频率条件下所能处理的有效数据量。假设本实验设计的芯片时钟频率可达到300MH,那么该芯片的数据吞吐量是多少呢?

由于芯片完成一次乘法运算需要一个以上的时钟周期,因此,即使芯片采用300MHz的时钟频率,它每秒钟所能处理的有效数据吞吐量也一定小于300M,对于16位乘法器而言,ain和bin均为0xFFFF时,芯片的运算量最大,计算所需的时间也最长,这种情况才能作为我们计算数据吞吐量的依据。

假设芯片在200MHz的条件下ain和bin均为0xFFFF时需要16个时钟周期才能得到乘法结果,那么芯片在200MHz的条件下的数据吞吐量就为200M/16=12.5M。

信号名称

方向

描述

clk

Input

时钟信号,50MHz

rst_n

input

复位信号,低电平有效

start

input

使能信号,为0表示信号无效;为1表示读入乘数和被乘数,该信号由0到1变化后,会进行一次当前ain和bin的乘法运算,进行下一次运算则需从新拉低后在拉高。

ain

Input

输入被乘数,位宽16位

bin

Input

输入乘数,位宽16位

yout

output

乘积输出,位宽32位

done

output

输出有效标志位,有效时,保持一个时钟周期高脉冲,为1表示乘法运算完成,yout端的数据稳定,得到最终的乘积;为0表示乘法运算未完成,yout端的数据不稳定。

 

设计思想:

采用时序逻辑设计方法,电路将已经得到的乘积项右移(最后一次计算不移位),然后与乘积项相加并保存和值,反复迭代上述步骤直至计算出最终结果。例如:两个四位的数相乘,产生一个八位的数。

设计代码

              if(areg[i-1])

yout_r = {1'b0,yout[6:3]+breg,yout_r[2:1]}; //累加并移位

                                   else yout_r <= yout_r>>1; //移位不累加

                            end

                     else if(i == 5'd8 && areg[7]) yout_r[7:4] <= yout_r[7:4]+breg;      //累加不移位

assign yout=yout_r;

由代码可知:如果我们用4位乘4位,则在进行前3次运算的时候,都要移位,实际进行了三次移位(最后一次没移位),我们每次加的时候都是取结果的第四位到第七位与被乘数相加,将2,3位作为新和的最低两位,最高位补0,实现了先加,再将加合移位的目的,为什么是取结果的第四位到第七位与乘数相加呢?这个我也想了很久才想通。看例:

代码解释:(我用的四位乘得八位的来做的)

   1 1 1 1(被乘数)

X     1 1 0 1(乘数)

由于4位相乘得到的是八位的积:所以我们初始化结果的时候是:

(位)编号:7654 3210

乘积:   0000 0000(yout_r)

由代码可知,第一次运算结果是:

0011 1100(yout_r)   为什么不是0111 1000呢?因为两个四位数的相加的结果是5位,只是平时我们把最高位的0省略了,但是这里不能省略,否则就会出错。开始我就是这里出错了,害得自己纠结了好久。

第二次计算:由于乘数的第二位是0,所以移位不相加。结果:0001 1110

第三次计算:

                           6543(四位到第七位与被乘数相加)

           0011

                         +1111

      -----------------

                       10010

相加后最高位补0,最低两位是上次运算结果的2,3位。

拼接结果:1001 011(没移位补0)

由于下次是最后一次计算,所以不移位。

第四次计算:1001 011(只有七位,因为没移位补0)

+1111

                            ----------------------

                               1100 0011   

经过对这种乘法器实现方式的学习之后,自己编写了一个用左移的方式来实现乘法器的方式。这里我们还是用两个四位相乘的运算来作为例子。

例:

                            1111(被乘数)

                     X     1101(乘数)

              ----------------------

                         1111

                        0000

                       1111

                     1111

              --------------------

                 11000011  

由计算,我是对被乘数进行移位在与上次计算的结果相加,但是,从计算看出,第一次不移位。后面三次都是移位了的。

第一次计算结果:00001111

被乘数移位得:00011110

  00001111

+00000000

第二次计算结果:    00001111

被乘数移位得:00111100

  00001111

+   00111100

第三次次计算结果:  01001011

被乘数移位得:01111000

  01001011

+   01111000

第四次次计算结果:  11000011

其实这种方法更容易理解些,我最开始知道的也就是这种方法,今天通过过对第一种方法的学习,自己后来用FPGA把这种也实现了。

关键代码:这里我采用的是阻塞赋值语句的方式,开始采用的是非阻塞的方式,但是得不到自己想要的结果,但是经过自己多次检查修改,在逻辑上没有任何错误,后来改成阻塞的方式,结果就正确了。不过自己对什么时候用非阻塞,什么时候用非阻塞还是不明白。需多看书练习。这段代码繁琐了点,但是简单易懂。但是第一种方式的实现更好,资源利用得更少。这个代码还需在优化才行。

always @ (posedge clk or negedge rst_n)

       begin

              if(!rst_n) begin

                     muxa_r = 4'h0;

                     muxb_r = 4'h0;

                     muxout_r = 8'h00;

                     end

              else if(i==3'd0) begin

                     muxa_r = {{4{1'b0}},muxa[3:0]};

                     muxb_r = muxb;

                     end

              else if(i > 3'd0 && i < 3'd5)

                      begin

              if(muxb_r == 0)

                     muxout_r = 1'b0;       //乘数为0则不用计算了

              else

                     begin

                     if(i == 1)

                     begin//第一次运算不移位

                     if(muxb_r[0])

                            muxout_r = muxa_r;  //乘数的第一位为1,则输出等于被乘数

                     else muxout_r =1'b0; //否则输出为0

                     end

                     else if(muxb_r[i-1]) //计算后三位

                     begin

                            muxa_r = muxa_r << 1'b1;

                            muxout_r= muxa_r + muxout;//移位后与上一次的结果相加。

                     end

                     else

                            muxa_r = muxa_r << 1'b1;//若乘数为第i位为0则被乘数移位不加上次结果,上次结果变。                  

       end

       end

       end       

assign muxout = muxout_r;

仿真,为了较好的比较两种代码,我将自己的代码也改成了16*16的,

仿真时间最大延迟336959ps=336.959ns。

对于第一种延时  301672ps=301.672ns。

从仿真中看出这两种代码延迟差不多。

但是自己设计的这个代码并不好,因为在时序电路设计中应尽量使用非阻塞赋值语句,我本来想用非阻塞赋值的,但是没成功,所以希望那个指点哈。

 

posted @ 2012-02-10 11:37  慢慢来,别慌  阅读(1308)  评论(0编辑  收藏  举报