FPGA计算加速 - 双乘法器

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

FPGA因其灵灵活性、高并行性和可定制性,在卷积神经网络的加速中表现出良好的性能。实践中通常会将卷积的乘加运算交付给FPGA的DSP块,因此DSP的使用效率会直接影响加速器的性能。将两个乘法操作封装到一个DSP块可以同时提高DSP资源的利用率和卷积运算的速度。基于
双乘法器的卷积算子可以使运算的吞吐量和 DSP资源的利用率同时提高一倍。


一、FPGA中的DSP

目前FPGA中的DSP资源支持最多25bits×18bits的有符号乘法运算,同时拥有25bits的预加器和48bits的累加器。DSP块是FPGA中承担卷积运算的主要运算硬件单元。

在使用FPGA做卷积神经网络的硬件加速器设计时,由于其硬件特性,无法高效实现大量浮点运算,因此一般采取定点量化的方式。相关实验表明,量化位宽在8bit及以上时,各网络的损失基本在1%以内。因此,FPGA神经网络加速器往往使用8bit的量化方式来进行卷积运算。这样位宽为我们设计双乘法器提供了可能。

二、双乘法器

DSP块是FPGA中承担卷积运算的主要运算硬件单元。但是经过定点量化的卷积运算通常只需要进行8bits×8bits的有符号乘法运算,这无疑会带来很大的位宽浪费。
Nguyen提出了双乘法封装的方法[3],即将两个有相同乘数的乘法拼接在一起交给 DSP模块进行运算。如图a所示的两个乘法 A×C和 B×C,其中 A、B、C都为nbits无符号数,将 A和B进行拼接,由于nbits×nbits的结果为2nbits,故需要在拼接数中间插入(n+1)bits的0以保证A×C和B×C互不影响。

图a

2.1双乘法器(无符号数)

对于无符号的双乘法器十分简单,不需要考虑符号问题,只是数据的简单拼接。
下图以 100 * 101 (4*5)为例 :
在这里插入图片描述
注意上图乘数的位数 100:3bit ;101: 3bit ;组合后 100_0_000_101; 110 : 3bit
计算结束后,按照位数对结果进行数据分割 将后位6bit拿出,之后拿掉间隔的一个0位,剩下的前面几位为 100 * 110的值。

2.2双乘法器(有符号数)

当处理有符号数时,需要考虑符号位的影响。经过位拼接后,会将低位乘数的符号位误作为运
算位进行处理,这种情况下会产生误差导致运算结果错误。
计算如下图所示:

在这里插入图片描述

有符号数被当成无符号数进行计算 并且最终输出错误结果,这就是我们需要解决的问题。

2.2.1有符号乘法解决方案

对于一个有符号数nbits有符号数A=anan-2…a1a0,对于其补码表示存在如下公式:

在这里插入图片描述
对此公式进行化简:
在这里插入图片描述
因此,我们可以把n位有符号的乘数看作是其补码所对应的n位无符号数以及这位有符号乘数最高位对应2的n次方的差。
对应的我们可以把有符号的乘法进行变换
在这里插入图片描述
这里的C为有符号数。这样就化为了一个 无符号数*有符号数 之后做差的运算。

因此, -5*-6 = 1011 * 1010 ==>
1011 ×1010 - 1010 0000 (此时1010不是-5 而是11)

= (11×-6)10-1010 0000 = (-66)10 - 1010 0000

=1011 1110 - 1010 0000 = 0001 1110 = (30)10

3双乘法器设计

首先对输入的3个数据 A、B、C进行预处理。通过比较器对数据C的符号进行判断,产生选择器的控制信号,之后3个二选一选择器使用该信号分别完成对数据 A 与-A、B与-B、C与-C的选择处理,得到预处理值 ~A、 ~B和 ~C。然后将 ~A、 ~B与(n+1)bits 0 进行位拼接,将该值与 ~C输入到DSP单元完成乘法运算。最后使用移位器将 ~C左移n位,再将该值与乘法运算结果一同输入到减法器中,得到最终的校验结果。其中,高2nbits为B×C的准确值,低2nbits为 A×C的准确值

在这里插入图片描述

1代码设计

代码如下(示例):

module mult2(
	input			sys_clk     ,
	input			rst_n		,
	input     		[7:0] data_A,
	input     		[7:0] data_B,//-7-7
	input   signed  [7:0] data_C, 
								
	//8+8+1                     ,
	output  reg  		[15:0] outA  ,
	output   reg  		[15:0] outB  

    );
	//填入dsp预处理数据
	reg  [7:0] A_r,B_r;
	reg  signed [7:0] C_r;
	reg  [24:0] in_r;
	//reg  [24:0] in_r_1;
	reg  [32:0]out,out_r,out_r_1;
	reg  [32:0] C_x;  //做差分使用 
	//其各自的相反数
	wire [7:0] A_n ;
	wire [7:0] B_n ;
	wire signed [7:0] C_n ;
	
	assign A_n = ~data_A + 1'b1;
	assign B_n = ~data_B + 1'b1;
	assign C_n = ~data_C + 1'd1;
	
	//预选择参数
	always @(*)begin
	
		if(!rst_n)begin
			A_r = 8'd0;
			B_r = 8'd0;
			C_r = 8'd0;
		end
		else if(data_C[7]==1)begin 
			A_r = A_n;
			B_r = B_n;
			C_r = C_n;
		
		end
		
		else if(data_C[7] == 0)begin 
			A_r = data_A;
			B_r = data_B;
			C_r = data_C;
			
		end
		
	end
	//组合预选参数 
	always@(*)begin
		if(!rst_n)begin
			
			in_r = 25'd0;
		end
		else
			in_r  = {A_r,{9{1'b0}},B_r};
	end
	
	//参数计算 
	always@(*)begin
		if(!rst_n)begin
			out_r = 33'd0;
			out_r_1 = 33'd0;
		end
		else begin 
			out_r = $signed({in_r}) * C_r;
			out_r_1 = $signed(in_r[24:0]) * C_r;
		end	
	end
	
	//相减处理
	always@(*)begin
		if(!rst_n)begin
			C_x = 33'd0;
			out = 33'd0;
		end
		else begin
			if(B_r[7] == 1&& A_r[7] ==0 )begin
				outA = out_r[32:17] ;
				outB = out_r[15:0] - {C_r,{8{1'b0}}}; 
				
			end
			else if(B_r[7] == 0&& A_r[7] ==1 )begin
				outA = out_r[32:17] ;
				outB = out_r[15:0] ; 
				
			end
			else if(B_r[7] == 1&& A_r[7] ==1 )begin
				outA = out_r[32:17] ;
				outB = out_r[15:0] - {C_r,{8{1'b0}}}; 
				
			end
			
			else if(B_r[7] ==0 && A_r[7]==0)begin
				outA = out_r[32:17] ;
				outB = out_r[ 15:0] ;
				
			end
			
		end
	end
	
endmodule

2.激励文件

代码如下(示例):

module mult2_tb();


 reg   sys_clk; 
 reg   rst_n;	
 reg     [7:0]data_A  ;
 reg     [7:0]data_B  ;
 reg   signed  [7:0]data_C  ;
 wire    [15:0]outA    ;
 wire    [15:0]outB    ;
 reg     [7:0]test;
 reg signed [16:0] test1;
 reg   signed  [3:0]data_B_s  ;
initial begin
	sys_clk = 1;
	rst_n   = 0;
	data_A =  8'd00000010;
	//data_A = 4'b0001;
	data_B = 8'b00001110;  //-6
	//data_B = 4'b0000;
	data_B_s = 4'b1111;  //-6
	//data_C = 4'b1011;//-5
	
	data_C = 8'b11111011;
	#100;
	rst_n  = 1;
	test = data_C*$signed({0,data_B});
	test1 = $unsigned({{4{1'b1}},data_A,1'b0,{4{1'b0}},data_B})*$signed(data_C);
	
	#200;
	data_A =  8'd1;   //1
	data_B = 8'd0; //0
	data_C = 8'b11111011;//-5
	
	#200
	data_A = 8'd2;   //2
	data_B = 8'b00000001; //1
	data_C = 8'b11111011;//-5
	#200
	data_A = 8'd11111001;   //-7
	data_B = 8'b00000001; //1
	data_C = 8'b00000011;//3
	#200
	data_A = 8'd11111001;   //-7
	data_B = 8'b01111111; //1
	data_C = 8'b00000011;//3
	

end

always #10 sys_clk = ~sys_clk;

mult2 u_mult(
	.sys_clk    (sys_clk),
	.rst_n		(rst_n)  ,
	.data_A     (data_A) ,
	.data_B     (data_B) , //-3~+3
	.data_C     (data_C) , 
	.outA       (outA)   ,
	.outB       (outB)

    );
endmodule


3实验结果

在这里插入图片描述
黄色信号为输入,蓝色信号为输出。 可以看到使用双乘法器进行有符号数的乘法操作成功实现。


总结

本次设计是根据阅读论文《基于FPGA的双乘法器卷积加速算子的封装方法》来进行的。如果想要有更深的了解,可以去阅读这篇论文。 学识浅薄,叙述中如有不妥之处,还望大家多多批评指正。

  • 2
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
FPGA(可编程逻辑门阵列)可以用于实现浮点乘法的加速。下面是一些步骤来实现这个加速过程: 1. 设计浮点乘法器:首先,你需要设计一个浮点乘法器,可以使用硬件描述语言(如Verilog或VHDL)来描述这个乘法器的功能。这个乘法器可以使用乘法算法(如Booth算法)来实现高效的浮点乘法运算。 2. 将浮点乘法器实例化为FPGA:通过使用FPGA开发工具,将你设计的浮点乘法器实例化为FPGA的逻辑电路。这将把你的设计编译成可在FPGA上运行的位流文件。 3. 连接输入和输出接口:将输入信号(包括浮点数和控制信号)和输出信号(乘积)连接到FPGA芯片上的输入和输出引脚。这可以通过FPGA开发板上的引脚映射工具完成。 4. 配置FPGA:将编译生成的位流文件加载到FPGA芯片上,并配置FPGA以执行你的浮点乘法器设计。这可以通过FPGA开发工具中的烧录操作完成。 5. 进行浮点乘法加速:一旦FPGA配置完成,你可以使用输入接口将浮点数传递给FPGA,并使用输出接口读取FPGA计算的浮点乘积。由于FPGA是并行处理的,所以可以获得比传统软件实现更高的计算性能。 需要注意的是,FPGA开发对硬件设计和编程有一定的要求,需要具备相关的知识和技能。另外,为了实现更高效的浮点乘法加速,还可以使用流水线技术、并行计算乘法器阵列等方法来优化设计。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值