版权声明:本文为CSDN博主「掌阅读书」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_30767945/article/details/112576775
抄一个Verilog大作业。
题目描述:用VerilogHDL设计实现64bit二进制整数乘法器
基本功能:
1.底层乘法器使用16*16\8*8\8*32\8*16小位宽乘法器来实现,底层乘法器可以使用FPGA内部IP实现;
2.基于modelsim仿真软件对电路进行功能验证;
3.基于Quartus平台对代码进行综合及综合后仿真,芯片型号不限;
4.电路综合后的工作频率不低于50MHz。
第 1 章 电路的设计思想
1.1 不作优化的乘法器结构
对于一个乘法来说,它的计算步骤是用乘数的每一位以从低到高的顺序与被乘数相乘再错位相加。那么,如果把64位因数与64位因数相乘的结果展开以后会发现,错位相加的那些加数是可以用16×16位乘法器或者8×64位乘法器的错位相加的加数来凑一起的。我可以通过把四个3×3位乘法器的运算结果进行移位和相加以后,可以实现一个6×6位乘法器的运算结果。在上图中,因为乘法器是要调用3×3位乘法器,所以需要把两个因数分别切割成两个3位来调用乘法器。所以需要2×2=4个乘法器。根据题目所给出的允许调用的乘法器里,为了使调用的乘法器个数最小,我的子乘法器选择的范围将聚焦在16×16位乘法器和8×32位乘法器。不论调用这里面那个乘法器,最终都是需要16个乘法器的。16个乘法器并行运算同时送出数据。
若调用16位乘法器设计电路,无优化的情况下需要调用16个乘法器,8×32位乘法器同理。此外,64位电路的设计的移位设计有着极强的规律,在电路的设计中,这种规律性会体现在电路结构的相似性。
调用8×32位乘法器的设计的分析思路与方法与上文类似。
1.2 乘法器结构的折叠优化
不论采用8×32位还是16位的子乘法器来设计64位乘法器,如果用十六个乘法器并行运算,然后再对结果进行移位和相加。为了保证并行计算的延续性在电路的右端还需要放置至少16个寄存器以及15个加法器。这些模块凑一起将会导致电路的规模巨大。但是,在并形64位乘法器的设计里,电路的相乘以及移位的步骤是一样的,在设计出来的电路中这些16个乘法器以及后面的移位的单元的结构都是一致的。因此我们可以对这一部分采用时分复用的方式进行优化。这种反复使用某个结构(部件)的过程也被称作折叠。[2]折叠可以使得相同的N个结构优化到一个结构,代价是本来一个时钟周期可以处理的过程需要在若干个(N个左右)时钟周期完成。因此电路的吞吐率会下降到以下。
由于本次作业要求所设计的乘法器的面积与性能等指标的均衡,采用结构折叠可中和16个16位乘法所造成的很好的性能与很差的面积之间的矛盾。
调用8×32位乘法器的电路的设计与优化与上文雷同。
1.3 64位乘法器的多种设计思路
1.3.1 16个子乘法器的全并行思路
以调用16位子乘法器的设计为例。根据1.1节的结论,16个16位子乘法器先并行计算,然后按照(1.3)式来分配每一个乘法器后边的128位移位寄存器,再把128位移位寄存器移位后存贮的数值加起来输出。
为了减少加法器的延时,可把加法器改为树形结构,即采用四级8421的加法器的结构。
其次,再乘法器设计里的加法器模块,因为位宽至少也是16位的,所以加法器可采用进位旁路加法器来减少管子的数量。无论怎么,加法器电路的管子数量是随着位宽的增加而增加的。如果寄存器直接采用128位,则加法器的位宽也应该是128位的。所以减小加法器的位宽可以从先减少寄存器的位宽再减小加法器的位宽入手。比如,第一级加法器的输入采用32位寄存器,使得有移位要求的数据可以移位。第二级加法器的输入增加4个64寄存器,第三级和第四级一共增加3个128寄存器。加法器的位宽四级下来也会减小。这样优化下来,寄存器的总面积和加法器的总面积都会减小。
如1.1节所示,这种设计思路即使优化后仍然存在性能显著与面积冗余的矛盾。因此,在设计中会继续采用1.2节的思路对其优化。
1.3.2 1个子乘法器的全串行思路
现在,考虑采用1.2节的优化方法下最极端的思路,即仅采用一个子乘法器进行设计。电路框图如下:
如上图,将两个64位的因数分别切成16位数据,由时钟驱动选择器在不同时刻选择不同的数据组合并在乘法器中相乘。然后把相乘的结果移位以后并与寄存器里的数据相加,然后当累加次数够了以后把累加结果作为64位乘法器的结果输出。关于电路还有如下几点说明。
时钟驱动选择器选择数据是通过计数器实现的,比如时钟要驱动一个四选一的多路选择器,那么需要在时钟和选择器之间加上一个四进制计数器,然后时钟每跳一下计数器加一位。当计数器的计数结果连接到选择器的使能端上时,可实现乘法器的乘数随着时钟的跳变而转换。
选择器MUX1在跳转时,要固定选择器MUX2里一个数保持不变,直至MUX1里所有的数都循环结束。因此MUX1的时钟信号的频率应该是MUX2频率的4倍,同样道理,因为最终的结果需要移位累加4次才能出结果,所以最后一个寄存器的时钟频率应当是MUX2频率的0.25。
如果采用多个时钟信号对于这种小模块来说是很浪费的,那么可以考虑给控制选择使能端的两个计数器添加进位输出并级联。这样可以使用一个全局时钟。
为了提高数据的吞吐率,电路中多加了几级流水线。
采用8×32位寄存器设计与上述思路雷同,无非是选择器分别使用二选一选择器与四选一选择器。时钟的进位也不一样。
1.4 最终的乘法器结构设计方案
1.4.1 X个(1
在1.3节我分别列出来了两种极端情况下的电路设计情况,为了协调电路面积、性能、功耗三者的关系,我采用两种措施来保证电路的设计。首先是采用串并混合的电路,根据作者本人的粗略估计,并行的乘法器为四个16位乘法器的时候电路的面积和性能会有一个均衡。其次是为保证在前者情况下功耗的最优,电路设计的顺序结构应为先并行再串行。如果采用先串行再并行,那么在串行的时候电路里有不产生结果的时钟周期,而这种时钟周期在并行以后依然不产生结果,会造成时钟的浪费。对于有把握的地方,可分割的组合逻辑模块可在中间插入流水线分割组合逻辑电路来提高性能,但是这样会降低芯片的响应速度。如果假设乘法器的应用场景为大串的乘法连续计算,那就尽量加上。最后要算好延时等时序问题,保证在正确的时刻把正确的积进行输出 (由计数器控制)。
第 2 章 电路的实现
4个16×16位乘法器实现64位乘法器所组成的64位乘法器的电路示意图如下:
其中,16×64位乘法器的设计参考1.1节的全并行乘法器设计;64×64位乘法器的设计调用16×64位的乘法器并参考1.2节的串行乘法器的设计。下面将分别给出16×64位乘法器和64×64位乘法器的设计图并详细说明。
2.1 64位乘法器流程图
图2.1 16×64位乘法器电路示意图
图2.2 64×64位乘法器电路示意图
2.2 64位乘法器设计细节
2.2.1 端口的wire连接
为了规避语法问题带来的编译通不过,我将所有的module的端口都定义为wire类型。而调用的IP的乘法器的端口也是wire类型,这样实例化这些模块以后的连接就比较方便。
其中可能会存在需要在敏感事件列表里对端口进行赋值,这时候便定义reg类型的中间变量来参与赋值和运算,最终用assign语句将它和端口进行连接。
2.2.2 选择器的时钟控制
利用1.3.2节的思路,用时钟驱动计数器工作,再用计数器的输出作为选择器的输入,来选择数据,从而实现时钟驱动数据的跳变。
2.2.3 流水线插入
为了提高电路的工作频率,我插入了一些流水线,诸如在两级乘法器的移位累加过程中,多级求和过程中。结果的输出过程等。这样有利于把长路径的组合逻辑电路进行分割,以减小组合逻辑的延迟,提高频率。
流水线没有全加,因为加的多了面积也会增大,而且,结果的响应时间也会拉长。
2.2.4 移位累加过程[3]
按照传统的思维,错位相加应该是先把低位来的数据放在寄存器低位,然后寄存器的数据左移,再和次低位来的数据相加,依次循环到最高位的结果。我在代码里的移位累加过程刚刚反过来。即先把低位来的数据放在最高位,然后寄存器数据右移后与次高位来的数据相加,最终的效果差不多。
最终的操作步骤是,先把寄存器清零,然后寄存器右移16位,和低位来的数据放最高位以后的数据相加并放回寄存器,然后一直重复右移,累加,放值的过程
移位累加过程如果在移位和累加过程中间加一层流水线的话延时可能会显著降低,但是考虑到这里的寄存器数量太多,于是放弃。
2.2.5 移位累加过程的控制
每一个计算周期每一步都会通过计数器进行标明。所以可以根据计数结果来控制移位累加的过程。
移位累加的过程需要注意到两个问题,一个是一个计算周期开始的时候,累加的寄存器内数据需要归零并开始计算。如果不归零那寄存器会累加上之前计算的结果。所以我们需要推算出第一个16×64位乘法的结果在哪个周期首先产生,那在这个周期之后的第一个上升沿就会发生程序的第一次移位累加,往前推导一下,在这个周期寄存器里存放的恰好是上一个计算周期的结果,所以,在下一个上升沿还需要把寄存器里的数据赋出去。
事实上,对于电路时序的确认我的推算还是有一些误差,最终在跑仿真的时候检查发现有错位便再进行这部分的修改,即对if语句的参数的修改。
2.3 电路的Verilog HDL关键代码及解释
2.3.1 16×64代码及解释
//16×64子乘法器模块module mul1664 (clk,reset,a, b, result); input clk; input [15:0] a;//a为16位 input [63:0] b;//b为64位 output [79:0] result; reg [31:0] temp_result1; reg [31:0] temp_result2; reg [31:0] temp_result3; reg [31:0] temp_result4; reg [47:0] temp_sum1; reg [47:0] temp_sum2; reg [79:0] temp_result; //非阻塞赋值,使得程序自带流水线 initial always@(posedge clk) if(!reset) begin temp_sum1<=0; temp_sum2<=0; temp_result<=0; end else begin //第一个时钟上升沿:调用四个乘法器同时运算 mul64 submul1(.dataa(a),.datab(b[15:0]),.result(temp_result1) ); mul64 submul2(.dataa(a),.datab(b[31:16]),.result(temp_result2) ); mul64 submul3(.dataa(a),.datab(b[47:32]),.result(temp_result3) ); mul64 submul4(.dataa(a),.datab(b[63:48]),.result(temp_result4) ); //第二个时钟上升沿:第一层加法同时运算,错位相加 temp_sum1<={temp_result2,16'b0}+{16'b0+temp_result1}; temp_sum2<={temp_result4,16'b0}+{16'b0+temp_result3}; //第三个时钟上升沿:第二层加法同时运算,错位相加 temp_result<={temp_sum1,32'b0}+{temp_sum2,32'b0}; end assign result=temp_result;endmodule//16×16bit乘法器//由IP生成,即mul64
2.3.1 64×64代码及解释
//64×64位乘法器module multiply64(clk,reset,a,b,result); //输入信号:时钟信号和复位信号 //时钟控制计数器、乘法器的流水线工作以及一个计算周期内最终信号的移位累加过程的进行 //复位信号控制计数器、乘法器的复位 input clk; input reset; //输入信号:两个64位的乘数 input [63:0] a; input [63:0] b; //输出信号:64位乘法器的运算结果 output [127:0] result; //enable使能端,两个作用: //第一,控制4选一多路选择器选择一个计算周期每个时刻下正确的复用信号 //第二,控制实现最终移位累加的128位寄存器temp1_result实现正确的复位,赋初值以及把正确结果送到寄存器temp_result wire [1:0] enable; //选择器选择的结果,作为16×64位乘法器的乘数 wire [15:0] temp16; //作为复用的16×64位乘法器的运算结果 wire [79:0] temp1664; //实现复用乘法器结果temp1664的移位和累加 reg [127:0] temp1_result; //存放一个计算周期结束后的64位乘法器的计算结果 reg [127:0] temp_result; //用时钟驱动计数器计数,每四个周期是一个循环,无进位 //因为乘法器是复用了四次,所以只要选择合适的时序以及恰当的时间点,那么完全可以实现一个周期四个时钟,不会再多 //如果不对这一块作优化而是无脑堆流水线的话则需要五、六个周期甚至更多 count sel(.clk(clk), .reset(reset), .control_cnt(enable) ); //用选择器选择是哪16位参与运算 //我们调用16×64位乘法器,当64位的数确定以后,我们需要把另一个64位的数每隔16位进行切割 //然后通过由时钟所驱动的计数器来控制某一个时刻下,我选择哪16位参与我们所复用的16×64位乘法器的运算 //使能端由计数器的计数结果进行控制 mux4in1 choice(.a(a[15:0]), .b(a[31:16]), .c(a[47:32]), .d(a[63:48]), .en(enable), .mux_out(temp16) ); //调用16×64位乘法器来进行运算 mul1664 submul1664( .clk(clk), .reset(reset), .a(temp16), .b(b), .result(temp1664) ); always@(posedge clk) //把”计数器为2“设置为计算周期的开始标志位 //通过对时序的推算可知,在全部时钟周期里,16×64位计数器第一次出结果的时候,计数器为1 //这样,如果再经过三次时钟周期,那么便可以完成一次16×64位乘法器的复用,即四次以后应该出移位累加寄存器temp1_result的结果 //此时,由于四进制计数器的缘故,此时的计数结果仍然是1 //但是,从16×64位乘法器到temp1_result之间有一层流水,所以,temp1_result的数据有一个延时 //因此选择在enable==2'b10时进行temp1_result初始化与result赋值 if(enable==2'b10) begin //每一个计算周期开始以后,赋初值 temp1_result<={temp1664,48'b0}; //当一个运算周期开始以后,由寄存器输出上一个运算周期结果 temp_result<=temp1_result; end //完成移位相加的过程,并使寄存器锁存数据 //移位累加过程: //每个计算周期内,乘法器复用时采用的乘数分别是一个64位乘数的低16位到高16位 //那么就需要把这四次的结果按照时间先后顺序从低位到高位依次左移16位并相加 //为了串行运算的电路设计,我采用设计一般乘法器的一个电路设计 //便是每次的结果都是以高位存放,然后和右移16位的累加寄存器的结果相加并把结果存放在累加寄存器temp1_result else begin //其他时刻寄存器内数值保持不变 temp_result<=temp_result; //对乘法器的计算结果进行16位移位并与此时16×64位乘法器的运算结果累加 temp1_result<={16'b0,temp1_result[127:16]}+{temp1664,48'b0}; end //把寄存器的计算结果连接到输出 assign result=temp_result; endmodule//四选一选择器//控制选择一个计算周期每个时刻下正确的复用信号module mux4in1(a,b,c,d,en,mux_out); //输入信号:四个备选信号 input[15:0] a; input[15:0] b; input[15:0] c; input[15:0] d; //输入信号:使能端 input[1:0] en; //输出信号:选择器选择的结果 output[15:0] mux_out; //输出信号的中间变量 reg[15:0] temp_out; //组合逻辑电路 always@(a or b or c or d or en) //case语句用来做数据选择 case(en) 2'b00:temp_out =a; 2'b01:temp_out =b; 2'b10:temp_out =c; 2'b11:temp_out =d; //防止latch出现 default:temp_out=15'b0; endcase //输出信号赋值 assign mux_out=temp_out;endmodule//控制要用的计数器模块,每四个时钟周期进一位,但是无进位信号//也是每四个时钟周期完成一次64×64位乘法器的运算 //计数器得到的计数结果最主要的目的在于:在每一个计算周期内标注好了我每个时钟周期 //这样我就可以知道某个周期电路应该做什么,并通过使计数结果作使能端来控制电路的行为module count(clk,reset,control_cnt); //输入信号:时钟信号和复位信号时钟和复位信号 input clk; input reset; //输出信号:计数器的计数结果 output [1:0] control_cnt; //存放计数结果的寄存器 //为了我在宏观module里线路的连接便捷,我基本都把input信号以及output信号定义成了默认的wire类型信号 //但是,在这些module的内部,敏感事件列表里往往会对输出信号进行赋值,这里我便用一个临时的reg类型信号来做输出的临时信号 //注:本文件中所有命名带有“temp”字符串的变量均为临时变量,起规避语法错误(syntax Error)的作用或者做其它中间变量 reg [1:0] temp_cnt; //每遇到一次时钟上升沿计数器加1 always@(posedge clk or negedge reset) //复位计数结果清零 if(!reset) temp_cnt<=2'b00; //遇三计数结果清零重新计数 else if(temp_cnt==2'b11) temp_cnt<=2'b00; //计数 else temp_cnt<=temp_cnt+2'b01; //输出信号赋值 assign control_cnt=temp_cnt;endmodule
————————————————
版权声明:本文为CSDN博主「掌阅读书」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_30767945/article/details/112576775