基于FPGA的高效乘法器

时间:2025-04-28  作者:Diven  阅读:0

01 设计思路   二进制的乘法运算与十进制的乘法运算相似,如下图所示,二进制数据6’b110010乘以二进制数据4’b1011,得到乘积结果10’b1000100110。

基于FPGA的高效乘法器

图1 二进制乘法运算 仔细观察上图发现,乘数最低位为1(上图紫色数据位),则得到紫色数据,乘数第1位为1,将被乘数左移1位,得到橙色数据,然后乘数的第2位是0,0乘以被乘数为0,则舍弃。乘数的第3位为1,则将被乘数左移3位,得到红色数据。然后将紫色、橙色、红色数据相加,得到乘积。 这就是二进制乘法运算思路,乘法的运算时间与乘数的位宽有关。乘数为1时需要左移的位数与数据位的权重其实有关,但是FPGA实现这样的运算并不算特别简单,还能不能简化? 当乘数或者被乘数为0时,直接输出0即可,不需要运算。 当乘数和被乘数均不等于0时,乘积的初始值为0,每个时钟周期把乘数右移一位,被乘数左移一位,如果乘数最低位为1,则乘积等于乘积加上此时被乘数的值,当乘数为1时,计算完成,输出乘积的运算结果。 计算流程如下图所示,其实就是将图1的运算拆分,每次只需要判断乘数的最低位是否为1,从而确定乘积是否需要加上被乘数,乘数每右移一次,被乘数就必须左移一次,这样能保证乘积不变。当乘数变为1时,移位结束,此时乘数最低位为1,被乘数加上乘积后作为运算结果,完成运算。

图2 简化的移位相加运算

02 代码设计   由此,就可以编写FPGA代码了,为了模块通用,位宽全部进行参数化设计,增加开始计算信号和模块忙闲指示信号,以及乘积计算完成的有效指示信号。 端口信号如下表所示:

表1 端口信号列表

 

信号I/O位宽含义
clkI1系统时钟。
rst_nI1系统复位,低电平有效。
startI1开始运算,高电平有效。
multiplICandIMULT_D被乘数。
multiplierIMULT_R乘数。
productOMULT_D
+ MULT_R
乘积。
product_vldO1乘积有效指示信号,高电平有效
rdyO1模块空闲指示信号,高电平有效。

 

当开始计算信号有效且乘数与被乘数均不等于0且模块不处于运算状态时,把开始计算信号start_f拉高,运算状态标志信号flag初始值为0,当检测到开始运算start_f有效时拉高,当乘数为1时结束运算,flag信号拉低,对应代码如下所示:

 

 

 //开始计算信号有效且乘数和被乘数均不等于0; assign start_f = (~flag) && (start && (multiplICand != 0) && (multiplier != 0)); //运算标志信号, always@(posedge clk or negedge rst_n)begin if(rst_n==1'b0)begin//初始值为0; flag <= 1'b0; end else if(start_f)begin//开始运算时拉高 flag <= 1'b1; end else if(multiplier_r == 1)begin//运算结束时拉低; flag <= 1'b0; end end

 

 

然后就是对乘数和被乘数信号的处理,如下所示。初始值均为0,当开始运算时,将输入的乘数和被乘数保存到相应寄存器中,如果flag信号有效,则每个时钟周期把乘数右移1位,把被乘数左移1位。

 

 

 always@(posedge clk or negedge rst_n)begin if(rst_n==1'b0)begin//初始值为0; multiplicand_r <= {{MULT_D + MULT_R}{1'b0}}; multiplier_r <= {{MULT_R}{1'b0}}; end else if(start_f)begin//当计算开始时; multiplicand_r <= multiplicand;//将被乘数加载到被乘数寄存器中。 multiplier_r <= multiplier;//将乘数加载到乘积寄存器中。 end else if(flag)begin//正常计算标志信号有效时,被乘数左移一位,乘数右移一位。 multiplicand_r <= multiplicand_r << 1; multiplier_r <= multiplier_r >> 1; end end

 

 

之后就是乘积的运算,初始值为0,当开始信号有效时,不管乘数和被乘数的状态是什么,将乘积寄存器设置为0。在之后的运算中,如果flag有效并且乘数最低位为1,则把乘积寄存器的值与被乘数寄存器的值相加,得到乘积寄存器数据。

 

 

 //计算乘法运算结果,开始信号有效时,将乘积清零。 //当乘数寄存器最低位为1时,加上此时被乘数的值。 always@(posedge clk or negedge rst_n)begin if(rst_n==1'b0)begin//初始值为0; product_r <= {{MULT_D + MULT_R}{1'b0}}; end else if(start)//当乘数或者被乘数为0时,乘积输出0. product_r <= {{MULT_D + MULT_R}{1'b0}}; else if(flag && multiplier_r[0])begin//如果乘积的最低位为1,则把乘积的高位数据与被乘数相加。 product_r <= product_r + multiplicand_r; end end

 

 

最后就是乘积运算的输出,如果开始信号有效时,乘数和被乘数其中一个为0,则乘积输出0,拉高乘积有效指示信号。如果在计算乘积的过程中(flag为高电平)且乘数等于1,则表示计算完成,把乘积寄存器值加上此时被乘数的值作为乘积输出,并且把乘积有效指示信号拉高一个时钟周期。乘积有效指示信号在其余时间均为0。

 

 

 //输出乘积和乘积有效指示信号; always@(posedge clk or negedge rst_n)begin if(rst_n==1'b0)begin//初始值为0; product <= {{MULT_D + MULT_R}{1'b0}}; product_vld <= 1'b0; end else if((~flag) && (start && ((multiplicand == 0) || (multiplier == 0))))begin product <= {{MULT_D + MULT_R}{1'b0}};//如果开始计算时,乘数或者被乘数为0,则直接输出0; product_vld <= 1'b1; end else if(flag && (multiplier_r == 1))begin//计算完成时,把计算结果输出,且乘积有效指示信号拉高; product <= product_r + multiplicand_r; product_vld <= 1'b1; end else begin//其余时间把有效指示信号拉低; product_vld <= 1'b0; end end

 

 

最后就是模块忙闲指示信号,当开始信号有效或者模块处于计算状态时拉低,其余时间拉高,上游模块检测到该信号后就可以拉高start信号,开始下一次运算。注意该信号只能使用组合逻辑电路生成,并且上游只能通过时序电路检测该信号状态。

 

 

 //生成模块忙闲指示信号; always@(*)begin//当开始信号有效或者标志信号有效时,模块处于工作状态; if(start || flag) rdy = 1'b0; else//否则模块处于空闲状态; rdy = 1'b1; end

 

 

代码就这么多,相对比较简单,参考代码如下:

 

 

Module mult #( parameter MULT_D = 8 ,//被乘数位宽; parameter MULT_R = 4 //乘数位宽;)( input clk ,//系统时钟信号; input rst_n ,//系统复位信号,低电平有效; input start ,//开始运算信号,高电平有效; input [MULT_D - 1 : 0] multiplicand ,//被乘数; input [MULT_R - 1 : 0] multiplier ,//乘数; output reg [MULT_D + MULT_R - 1 : 0] product ,//乘积输出; output reg product_vld ,//乘积有效指示信号,高电平有效; output reg rdy //模块忙闲指示信号,高电平表示空闲;); reg flag ; reg [MULT_D - 1 : 0] multiplier_r ;//乘数的寄存器 reg [MULT_D + MULT_R - 1 : 0] multiplicand_r ;//被乘数的寄存器。 reg [MULT_D + MULT_R - 1 : 0] product_r ;//乘积寄存器; wire start_f ; //开始计算信号有效且乘数和被乘数均不等于0; assign start_f = (~flag) && (start && (multiplicand != 0) && (multiplier != 0)); //运算标志信号, always@(posedge clk or negedge rst_n)begin if(rst_n==1'b0)begin//初始值为0; flag <= 1'b0; end else if(start_f)begin//开始运算时拉高 flag <= 1'b1; end else if(multiplier_r == 1)begin//运算结束时拉低; flag <= 1'b0; end end always@(posedge clk or negedge rst_n)begin if(rst_n==1'b0)begin//初始值为0; multiplicand_r <= {{MULT_D + MULT_R}{1'b0}}; multiplier_r <= {{MULT_R}{1'b0}}; end else if(start_f)begin//当计算开始时; multiplicand_r <= multiplicand;//将被乘数加载到被乘数寄存器中。 multiplier_r <= multiplier;//将乘数加载到乘积寄存器中。 end else if(flag)begin//正常计算标志信号有效时,被乘数左移一位,乘数右移一位。 multiplicand_r <= multiplicand_r << 1; multiplier_r <= multiplier_r >> 1; end end //计算乘法运算结果,开始信号有效时,将乘积清零。 //当乘数寄存器最低位为1时,加上此时被乘数的值。 always@(posedge clk or negedge rst_n)begin if(rst_n==1'b0)begin//初始值为0; product_r <= {{MULT_D + MULT_R}{1'b0}}; end else if(start)//当乘数或者被乘数为0时,乘积输出0. product_r <= {{MULT_D + MULT_R}{1'b0}}; else if(flag && multiplier_r[0])begin//如果乘积的最低位为1,则把乘积的高位数据与被乘数相加。 product_r <= product_r + multiplicand_r; end end //输出乘积和乘积有效指示信号; always@(posedge clk or negedge rst_n)begin if(rst_n==1'b0)begin//初始值为0; product <= {{MULT_D + MULT_R}{1'b0}}; product_vld <= 1'b0; end else if((~flag) && (start && ((multiplicand == 0) || (multiplier == 0))))begin product <= {{MULT_D + MULT_R}{1'b0}};//如果开始计算时,乘数或者被乘数为0,则直接输出0; product_vld <= 1'b1; end else if(flag && (multiplier_r == 1))begin//计算完成时,把计算结果输出,且乘积有效指示信号拉高; product <= product_r + multiplicand_r; product_vld <= 1'b1; end else begin//其余时间把有效指示信号拉低; product_vld <= 1'b0; end end //生成模块忙闲指示信号; always@(*)begin//当开始信号有效或者标志信号有效时,模块处于工作状态; if(start || flag) rdy = 1'b0; else//否则模块处于空闲状态; rdy = 1'b1; endendModule

 

 

03 模块仿真

对应的TestBench如下所示:

 

 

`timescale 1 ns/1 nsmodule test(); localparam CYCLE = 10 ;//系统时钟周期,单位ns,默认10ns; localparam RST_TIME = 10 ;//系统复位持续时间,默认10个系统时钟周期; localparam MULT_D = 8 ;//被乘数位宽; localparam MULT_R = 4 ;//乘数位宽; reg clk ;//系统时钟,默认100MHz; reg rst_n ;//系统复位,默认低电平有效; reg start ;//开始运算信号,高电平有效; reg [MULT_D - 1 : 0] multiplicand;//被乘数; reg [MULT_R - 1 : 0] multiplier ;//乘数; wire [MULT_D + MULT_R - 1 : 0] product ;//乘积输出; wire product_vld ;//乘积有效指示信号,高电平有效; wire rdy ;//模块忙闲指示信号,高电平表示空闲; //例化需要仿真的模块; mult #( .MULT_D ( MULT_D ),//被乘数位宽; .MULT_R ( MULT_R ) //乘数位宽; ) u_mult ( .clk ( clk ),//系统时钟,默认100MHz; .rst_n ( rst_n ),//系统复位,默认低电平有效; .start ( start ),//开始运算信号,高电平有效; .multiplicand ( multiplicand ),//被乘数; .multiplier ( multiplier ),//乘数; .product ( product ),//乘积输出; .product_vld ( product_vld ),//乘积有效指示信号,高电平有效; .rdy ( rdy ) //模块忙闲指示信号,高电平表示空闲; ); //生成周期为CYCLE数值的系统时钟; initial begin clk = 0; forever #(CYCLE/2) clk = ~clk; end //生成复位信号; initial begin rst_n = 1;start = 0;multiplicand = 0; multiplier = 0; #2; rst_n = 0;//开始时复位10个时钟; #(RST_TIME*CYCLE); rst_n = 1; #(5*CYCLE); multiplicand = 4; multiplier = 15; start = 1'b1; #(CYCLE); start = 1'b0; #(CYCLE); repeat(30)begin//产生30组随机数据进行测试; @(posedge rdy); #(8*CYCLE); #1; multiplicand = {$random};//产生随机数据,作为被乘数; multiplier = {$random};//产生随机数据,作为乘数; start = 1'b1; #(CYCLE); start = 1'b0; end @(posedge rdy); #(8*CYCLE); $stop;//停止仿真; endendmodule
简要截取仿真的一段数据进行查看,如下所示,start信号有效时,乘数为13,被乘数为92。之后被相应的寄存器暂存,然后flag信号为高电平时,每个时钟周期乘数寄存器右移一位,被乘数寄存器数据左移一位。 如果乘数最低位为1,则乘积寄存器的值就会与被乘数的值相加,得到新的乘积寄存器值,最后当乘数为1时,蓝色的乘积信号就会把乘积寄存器的值460与被乘数的值736相加得到1196作为输出,完成乘法运算。

 

 

图3 仿真截图 至此,该模块的设计到此结束,该模块的位宽全部进行了参数化处理,需要修改乘数和被乘数的位宽时,只需要修改位宽的参数即可,代码不需要做任何修改。 该模块在后续设计中可能作为子模块出现,因为这种靠移位和加法的运算,在面对较大位宽的乘法运算时,可以得到更高的时钟频率。 审核编辑:黄飞

 

猜您喜欢

0201贴片电阻,指的是尺寸为0.2mm x 0.1mm的表面贴装电阻,是目前市场上最小的贴片电阻之一。它的小巧身材使其成为电子产品小型化和轻量化趋势下的理想选...
2024-11-26 11:29:47

雪崩二极管是特殊的半导体器件,应用于电子电路中。与普通二极管相比,雪崩二极管在反向偏置下具有独特的击穿特性,这使得在许多应用中,尤其是在高压和高频电路中,显示出...
2025-04-08 03:31:07

旋具头是现代工具中不可少的组成部分,应用于各种机械和电子设备的维护与修理。设计旨在提供高效的扭力传递,使得操作更加轻松。旋具头通常采用高强度合金钢材料,耐磨损、...
2011-08-09 00:00:00

热熔胶枪是常见的手工工具,应用于家庭、工艺制作及工业领域。通过加热热熔胶棒,使其融化后,通过喷嘴将熔化的胶水均匀地涂抹在所需粘接的物体表面。热熔胶的特点是干燥迅...
2013-05-01 00:00:00

十字槽盘头螺钉和弹垫组合在现代机械和建筑领域中受到青睐,其主要优势体现在以下几个方面。十字槽设计使得螺钉在安装和拆卸时更加方便,避免了滑丝的风险,提高了工作效率...
2015-06-19 00:00:00


防护面屏规格尺寸是多少?在选择防护面屏时,了解其规格尺寸非常重要。防护面屏的宽度通常在20到30厘米之间,高度则在30到40厘米之间,这样的尺寸设计可以有效覆盖...
2018-10-26 00:00:00

现代电子设备中,连接器起着非常重要的作用。不仅保证了电气信号的传输,还在设备的设计和功能上提供了灵活性。本文将重点介绍“CONN_11.4X5.25MM_SM”...
2025-04-22 14:31:14

电解电容是电子设备中重要组成部分。在电路中起到关键作用。很多人对了解不多。本文将介绍电解电容的用途和作用。储能功能电解电容的主要功能是储能。可以存储电能,并在需...
2025-03-22 21:01:39

电子发烧友网报道(文/李宁远)作为导航测量系列的基础元器件之一,加速度计是测量运载体加速度的器件。它被广泛应用于消费电子、工业自动化、汽车、航空航天等各个领域,...
2024-05-12 08:02:00