使用FPGA进行算法处理时加减乘除是非常普遍的操作。
正常的加减处理直接使用+ -符号即可,但是对于有符号数的加减要注意位宽的扩展,如下图,计算a+b时首先要对a,b进行位宽扩展,一般原则是每两个数据的累加扩1位。比如2数据累加扩1bit,四个数据累加扩2bit,以此类推。
但是随着数据位宽的不断增加,较大位宽的加法在高速场景中的时序收敛是非常难的,因此要对大位宽的加法进行优化,优化思路就是FPGA通用的原则:用面积换速度,插入流水线。
代码如下,代码中将大位宽的信号分为两半,分别做减法后打一拍,然后拼接为最终的输出结果,其将大位宽的加法划分为两个小位宽的加法。
// ============================================================// File Name: cm_add// VERSION : V1.0// DATA : 2022/9/28// Author : FPGA干货分享// ============================================================// 功能:大位宽累加器 // delay 2clk// ============================================================ `timescale 1ns/1psmodule cm_add #( parameter C_DATA_WITH = 32 ) // ( input wire i_sys_clk , // 输入时钟 input wire [C_DATA_WITH-1:0] i_data_ina , // 输入信号 input wire [C_DATA_WITH-1:0] i_data_inb , // 输入信号 output reg [C_DATA_WITH :0] o_data_out );// 输出信号位宽加1 // ============================================================// 内部参数// ============================================================localparam C_DATA_WITH_HALF = C_DATA_WITH/2 ;// ============================================================// 变量// ============================================================reg [C_DATA_WITH_HALF:0] s_add_pre1 ;reg [C_DATA_WITH:C_DATA_WITH_HALF] s_add_pre2 ;wire [C_DATA_WITH:C_DATA_WITH_HALF] s_add_pre2_d ;// ============================================================// main code// ============================================================ always @(posedge i_sys_clk) begin s_add_pre1 <= {1'b0,i_data_ina[C_DATA_WITH_HALF-1:0]} + {1'b0,i_data_inb[C_DATA_WITH_HALF-1:0]}; s_add_pre2 <= {i_data_ina[C_DATA_WITH-1],i_data_ina[C_DATA_WITH-1:C_DATA_WITH_HALF]} + {i_data_inb[C_DATA_WITH-1],i_data_inb[C_DATA_WITH-1:C_DATA_WITH_HALF]} ; end assign s_add_pre2_d = s_add_pre2 + s_add_pre1[C_DATA_WITH_HALF]; always @(posedge i_sys_clk) o_data_out <= {s_add_pre2_d,s_add_pre1[C_DATA_WITH_HALF-1:0]}; endmodule
实现如下:
对于更大位宽的加法,可以将其拆分为更多个小位宽的加法,思路不变。
由于数据使用补码的原因,FPGA减法的操作与加法相同,注意数据位宽扩展防止溢出即可。