流水线乘法器的原理及verilog代码

流水线乘法器的原理及verilog代码1 乘法原理 二进制数乘法的显著特点就是可以将乘法转换为移位 乘 2 就是左移一位 乘 2 n 就是左移 n 位 而一个二进制数又可以看成是由若干个 2 的 i 次方的和 设被乘数和乘数分别为 M N 且都是 32 位的二进制数 乘积结果为 64 位 的向量 CO 则 所以乘法可以由移位电路和加法器完成

大家好,我是讯享网,很高兴认识大家。

1.乘法原理

二进制数乘法的显著特点就是可以将乘法转换为移位,乘2就是左移一位,乘2^n就是左移n位。而一个二进制数又可以看成是由若干个2的i次方的和。

设被乘数和乘数分别为M、N,且都是32位的二进制数,乘积结果为64位 的向量CO则

所以乘法可以由移位电路和加法器完成。计算有两种方式:串行和并行。

串行计算是每进行一次移位,将结果相加,计算一次乘法总共需要n+1个时钟周期,n次移位和n次加法。

而并行则是需要两个时钟周期,n个移位电路分别移位之后,将n个结果相加。而第二个周期的n个数相加这一步会需要非常长的计算延时,导致电路时序(建立时间、保持时间)很难满足要求,且风险很高

流水线乘法器则是在串行计算的基础上将每次移位和加法的结果暂存,并且第一级电路计算完成之后可以直接开始下一组数据的乘法计算。对于4位的数据乘法,在延迟后电路会源源不断地输出数据,而普通的并行方式的乘法器则是每两个周期计算一次乘法。

2.模块编写

2.1 底层模块multi_cell

首先我们需要设计一个底层模块用于输出移位和相加结果,并且加上valid和ready信号作为握手协议,valid拉高时输入数据有效

算法中每次被乘数M的移位都是递增,可以将4位的M先拓展成8位的{4'b0,M}作为multi_cell的输入,每级移位一次。对于N,虽然我们可以将N的每一位作为multi_cell的输入,但是这设计时就固定了电路适合的位数。比如4位的代码无法进行8位的计算,需要修改。

所以为了增加设计的通用性,我们将乘数N进行右移,每级移位一次并全部取第0位。当乘法器位宽改变时,只需要修改参数M、N即可。

需要多少级multi_cell由乘数N的位数决定, 对于第i级模块,输入为移位了i次的M,对应的是移位了i次的N,此时第0位即为原N的第i位。在计算加法时,如果移位后的N第0位是1,则上一级和与当前M相加,否则加0。(级数由0开始)

下面是multi_cell的verilog代码

module multi_cell # ( parameter M = 4, parameter N = 4 ) ( //input clk, rst_n, multi_M, multi_N, multi_acci, valid, //output multi_shift_M, multi_shift_N, multi_acco, ready ); input wire clk; input wire rst_n; input wire [M+N-1:0] multi_M; input wire [N-1:0] multi_N; input wire [N+M-1:0] multi_acci; output reg [M+N-1:0] multi_shift_M; output reg [N-1:0] multi_shift_N; output reg [M+N-1:0] multi_acco; //protocal input wire valid; output reg ready; always @(posedge clk or negedge rst_n) begin if(!rst_n)begin ready <= 1'b0; multi_shift_M <= {(M+N){1'b0}}; multi_shift_N <= {(N){1'b0}}; multi_acco <= {(M+N){1'b0}}; end else if(valid)begin ready <= 1'b1; multi_shift_M <= (multi_M << 1); multi_shift_N <= (multi_N >> 1); if(multi_N[0]) multi_acco <= multi_acci + multi_M; else multi_acco <= multi_acci; end else begin ready <= 1'b0; multi_shift_M <= {(M+N){1'b0}}; multi_shift_N <= {(N){1'b0}}; multi_acco <= {(M+N){1'b0}}; end end endmodule
讯享网

2.2 顶层模块设计

顶层模块将multi_cell级联即可,将每一级输出的移位之后M的结果作为下一级被乘数的输入,N同理。acco作为下一级的acci。

需要注意的是第0级的acci为0.同时需要消耗寄存器储存每一次的输出(移位输出和acco)。并且由于握手协议的存在,每一级的ready信号作为下一级的valid,并且需要逐级储存,第一级的valid由输入决定,最后一级的ready和累加和连接到输出。

下面是multiplier的顶层代码

讯享网`timescale 1ns/1ns module multi_pipe#( parameter M = 4, parameter N = 4 )( //input clk, rst_n, multi_M, multi_N, valid, //output multi_o, ready ); input wire clk; input wire rst_n; input wire [M-1:0] multi_M; input wire [N-1:0] multi_N; input wire valid; output wire [N+M-1:0] multi_o; output wire ready; wire [N+M-1:0] multi_shift_M [N-1:0]; wire [N+M-1:0] multi_shift_N [N-1:0]; wire [N+M-1:0] multi_acco [N-1:0]; wire [N-1:0] valid_array; multi_cell#( .M(M), .N(N) ) multi_cell_0( .clk (clk ), .rst_n (rst_n ), .multi_M ({ 
  
    
  {N{1'b0}},multi_M}), .multi_N (multi_N ), .multi_acci ({(M+N){1'b0}} ), .valid (valid ), .multi_shift_M (multi_shift_M[0] ), .multi_shift_N (multi_shift_N[0] ), .multi_acco (multi_acco[0] ), .ready (valid_array[0] ) ); genvar i; generate for(i=1;i<N;i=i+1)begin:multi_cell_x multi_cell#( .M(M), .N(N) ) multi_cell_x( .clk (clk ), .rst_n (rst_n ), .multi_M (multi_shift_M[i-1] ), .multi_N (multi_shift_N[i-1] ), .multi_acci (multi_acco[i-1] ), .valid (valid_array[i-1] ), .multi_shift_M (multi_shift_M[i] ), .multi_shift_N (multi_shift_N[i] ), .multi_acco (multi_acco[i] ), .ready (valid_array[i] ) ); end endgenerate assign multi_o = multi_acco[N-1]; assign ready = valid_array[N-1]; endmodule

3.testbench验证

testbench如下所示,经过延迟后,持续输出数据

小讯
上一篇 2025-01-13 21:21
下一篇 2025-04-07 16:31

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/63709.html