Xilinx FPGA资源解析与使用系列——DSP48E使用实例(三)
)
前言
前面我们介绍了DSP48E1资源可以非常的灵活的使用,而且也给出了它的原语模型,现在我们用一个例子来说明它的妙用。
实现功能
在某些扩频信号解调中,例如GPS信号,我们一般需要进行相干累加,例如将接收的基带信号与本地PN码进行相乘并且累加,每隔1ms截取结果,并将累加器清0,然后重新累加。
用公式描述就是:
P1 = P1+ B1 *C1
P2 = P2+ B2 *C1
C1 = -1 OR 1
(一个乘法器实现2个24bit的累加器)
一般写法
这里给出这个相关器的代码
module corr_module (
input i_clk ,
input i_corr_en ,
input i_pn_code ,
input i_dump ,
output o_dump_en,
input [4 :0] i_base_i ,
input [4 :0] i_base_q ,
output [15:0] o_accu_i ,
output [15:0] o_accu_q
);
reg [17:0] r_accu_i_temp = 0;
reg [17:0] r_accu_q_temp = 0;
assign o_dump_en = i_dump;
assign o_accu_i = r_accu_i_temp[17:2];
assign o_accu_q = r_accu_q_temp[17:2];
always @(posedge i_clk)
begin
if(i_corr_en == 0 || i_dump)
begin
r_accu_i_temp <= 0;
r_accu_q_temp <= 0;
end
else
begin
if(i_pn_code == 0)
begin
r_accu_i_temp <= r_accu_i_temp + {{13{i_base_i[4]}},i_base_i};
r_accu_q_temp <= r_accu_q_temp + {{13{i_base_q[4]}},i_base_q};
end
else
begin
r_accu_i_temp <= r_accu_i_temp - {{13{i_base_i[4]}},i_base_i};
r_accu_q_temp <= r_accu_q_temp - {{13{i_base_q[4]}},i_base_q};
end
end
end
endmodule
想必大家看这个应该问题不大
用DSP48E1实现
module corr_module_dsp(
input i_clk ,
input i_corr_en ,
input i_pn_code ,
input i_dump ,
output o_dump_en,
input [4 :0] i_base_i ,
input [4 :0] i_base_q ,
output [15:0] o_accu_i ,
output [15:0] o_accu_q
);
wire [47:0]acc_in;
wire [23:0]i_acc_in;
wire [23:0]q_acc_in;
wire [3:0]ALUMODE;
wire [47:0]acc_result;
wire [47:0]accu_i_temp_dsp;
wire [47:0]accu_q_temp_dsp;
assign ALUMODE = (i_pn_code == 0) ? 4'b0000 : 4'b0011; //加减控制代码
assign o_dump_en = i_dump;
assign i_acc_in = {{19{i_base_i[4]}},i_base_i};
assign q_acc_in = {{19{i_base_q[4]}},i_base_q};
assign acc_in = {i_acc_in,q_acc_in} ;
assign accu_i_temp_dsp = acc_result[47:24];
assign accu_q_temp_dsp = acc_result[23:0];
assign o_accu_i = accu_i_temp_dsp[17:2];
assign o_accu_q = accu_q_temp_dsp[17:2];
DSP48E1 #(
// Feature Control Attributes: Data Path Selection
.A_INPUT("DIRECT"), // Selects A input source, "DIRECT" (A port) or "CASCADE" (ACIN port)
.B_INPUT("DIRECT"), // Selects B input source, "DIRECT" (B port) or "CASCADE" (BCIN port)
.USE_DPORT("FALSE"), // Select D port usage (TRUE or FALSE)
.USE_MULT("NONE"), // Select multiplier usage ("MULTIPLY", "DYNAMIC", or "NONE")
.USE_SIMD("TWO24"), // SIMD selection ("ONE48", "TWO24", "FOUR12")
// Pattern Detector Attributes: Pattern Detection Configuration
.AUTORESET_PATDET("NO_RESET"), // "NO_RESET", "RESET_MATCH", "RESET_NOT_MATCH"
.MASK(48'h3fffffffffff), // 48-bit mask value for pattern detect (1=ignore)
.PATTERN(48'h000000000000), // 48-bit pattern match for pattern detect
.SEL_MASK("MASK"), // "C", "MASK", "ROUNDING_MODE1", "ROUNDING_MODE2"
.SEL_PATTERN("PATTERN"), // Select pattern value ("PATTERN" or "C")
.USE_PATTERN_DETECT("NO_PATDET"), // Enable pattern detect ("PATDET" or "NO_PATDET")
// Register Control Attributes: Pipeline Register Configuration
.ACASCREG(0), // Number of pipeline stages between A/ACIN and ACOUT (0, 1 or 2)
.ADREG(1), // Number of pipeline stages for pre-adder (0 or 1)
.ALUMODEREG(0), // Number of pipeline stages for ALUMODE (0 or 1)
.AREG(0), // Number of pipeline stages for A (0, 1 or 2)
.BCASCREG(0), // Number of pipeline stages between B/BCIN and BCOUT (0, 1 or 2)
.BREG(0), // Number of pipeline stages for B (0, 1 or 2)
.CARRYINREG(1), // Number of pipeline stages for CARRYIN (0 or 1)
.CARRYINSELREG(1), // Number of pipeline stages for CARRYINSEL (0 or 1)
.CREG(0), // Number of pipeline stages for C (0 or 1)
.DREG(0), // Number of pipeline stages for D (0 or 1)
.INMODEREG(1), // Number of pipeline stages for INMODE (0 or 1)
.MREG(0), // Number of multiplier pipeline stages (0 or 1)
.OPMODEREG(1), // Number of pipeline stages for OPMODE (0 or 1)
.PREG(1) // Number of pipeline stages for P (0 or 1)
)
DSP48E1_inst (
// Cascade: 30-bit (each) output: Cascade Ports
.ACOUT(), // 30-bit output: A port cascade output
.BCOUT(), // 18-bit output: B port cascade output
.CARRYCASCOUT(), // 1-bit output: Cascade carry output
.MULTSIGNOUT(), // 1-bit output: Multiplier sign cascade output
.PCOUT(), // 48-bit output: Cascade output
// Control: 1-bit (each) output: Control Inputs/Status Bits
.OVERFLOW(), // 1-bit output: Overflow in add/acc output
.PATTERNBDETECT(), // 1-bit output: Pattern bar detect output
.PATTERNDETECT(), // 1-bit output: Pattern detect output
.UNDERFLOW(), // 1-bit output: Underflow in add/acc output
// Data: 4-bit (each) output: Data Ports
.CARRYOUT(), // 4-bit output: Carry output
.P(acc_result), // 48-bit output: Primary data output
// Cascade: 30-bit (each) input: Cascade Ports
.ACIN(30'd0), // 30-bit input: A cascade data input
.BCIN(18'd0), // 18-bit input: B cascade input
.CARRYCASCIN(1'b0), // 1-bit input: Cascade carry input
.MULTSIGNIN(1'b0), // 1-bit input: Multiplier sign input
.PCIN(48'b0), // 48-bit input: P cascade input
// Control: 4-bit (each) input: Control Inputs/Status Bits
.ALUMODE(ALUMODE), // 4-bit input: ALU control input
.CARRYINSEL(3'b000), // 3-bit input: Carry select input
.CLK(i_clk), // 1-bit input: Clock input
.INMODE(5'b00000), // 5-bit input: INMODE control input
.OPMODE(7'b010_1100), // 7-bit input: Operation mode input
// Data: 30-bit (each) input: Data Ports
.A(0), // 30-bit input: A data input
.B(0), // 18-bit input: B data input
.C(acc_in), // 48-bit input: C data input
.CARRYIN(1'b0), // 1-bit input: Carry input signal
.D(0), // 25-bit input: D data input
// Reset/Clock Enable: 1-bit (each) input: Reset/Clock Enable Inputs
.CEA1(1'b1), // 1-bit input: Clock enable input for 1st stage AREG
.CEA2(1'b1), // 1-bit input: Clock enable input for 2nd stage AREG
.CEAD(1'b0), // 1-bit input: Clock enable input for ADREG
.CEALUMODE(1'b1), // 1-bit input: Clock enable input for ALUMODE
.CEB1(1'b1), // 1-bit input: Clock enable input for 1st stage BREG
.CEB2(1'b1), // 1-bit input: Clock enable input for 2nd stage BREG
.CEC(1'b1), // 1-bit input: Clock enable input for CREG
.CECARRYIN(1'b0), // 1-bit input: Clock enable input for CARRYINREG
.CECTRL(1'b1), // 1-bit input: Clock enable input for OPMODEREG and CARRYINSELREG
.CED(1'b0), // 1-bit input: Clock enable input for DREG
.CEINMODE(1'b1), // 1-bit input: Clock enable input for INMODEREG
.CEM(1'b1), // 1-bit input: Clock enable input for MREG
.CEP(1'b1), // 1-bit input: Clock enable input for PREG
.RSTA(1'b0), // 1-bit input: Reset input for AREG
.RSTALLCARRYIN(1'b0), // 1-bit input: Reset input for CARRYINREG
.RSTALUMODE(1'b0), // 1-bit input: Reset input for ALUMODEREG
.RSTB(1'b0), // 1-bit input: Reset input for BREG
.RSTC(1'b0), // 1-bit input: Reset input for CREG
.RSTCTRL(1'b0), // 1-bit input: Reset input for OPMODEREG and CARRYINSELREG
.RSTD(1'b0), // 1-bit input: Reset input for DREG and ADREG
.RSTINMODE(1'b0), // 1-bit input: Reset input for INMODEREG
.RSTM(1'b0), // 1-bit input: Reset input for MREG
.RSTP(i_corr_en == 0 || i_dump) // 1-bit input: Reset input for PREG
);
endmodule
注意上面代码中重点地方:
assign ALUMODE = (pn_code == 0) ? 4'b0000 : 4'b0011; //加减控制代码
.USE_SIMD("TWO24"), // SIMD selection ("ONE48", "TWO24", "FOUR12")
.USE_MULT("NONE"), // Select multiplier usage ("MULTIPLY", "DYNAMIC", or "NONE")
.MREG(0), // Number of multiplier pipeline stages (0 or 1)
.PREG(1) // Number of pipeline stages for P (0 or 1)
.P(acc_result), // 48-bit output: Primary data output
.ALUMODE(ALUMODE), // 4-bit input: ALU control input
.CARRYINSEL(3'b000), // 3-bit input: Carry select input
.CLK(i_clk), // 1-bit input: Clock input
.INMODE(5'b00000), // 5-bit input: INMODE control input
.OPMODE(7'b010_1100), // 7-bit input: Operation mode input
.C(acc_in), // 48-bit input: C data input
.RSTP(i_corr_en == 0 || i_dump) // 1-bit input: Reset input for PREG
简单说明下:
1、MREG must be set to 0 when the multiplier is not used. MREG必须设置为0,当乘法器不使用
2、ALUMODE: 4’b0000 : Z+X+Y+CIN 实现加法器
4’b0011 : Z-(X+Y+CIN) 实现减法
3、OPMODE: 7’b011_0011 : X = A:B, Y = 0,Z = C
7’b010_0011 : X = A:B, Y = 0,Z = P OPMODE[6:4] = 010 MUST SELECT WITH PREG = 1
7’b010_1100 X = 0 , Y = C,Z = P 也能完成累加
如果看不明白,参考前面的博文
功能对比分析
这两个模块功能上是等效的。
这是仿真结果:
r_accu_i1 、r_accu_q1 、r_accu_i2 、r_accu_q2 这些信号就是两个模块输出的锁存结果,锁存指示为o_dump_en。可以看出,两个模块的输出是一模一样的。
资源对比分析
看看两种实现方式的资源占用情况:
使用dsp48E1完全实现了两个相关器(P = P +A*B)(A=-1 OR 1) 的功能,没占用一点逻辑资源。
相当于1个DSP 48E置换了 37个LUT+36个FF。这个片子有400个DSP,全部置换相当于LUT逻辑资源增加了20%!
时序性能分析
corr_module 的综合结果:
corr_module 的逻辑级数:
corr_module_dsp 的综合结果:
corr_module_dsp 的逻辑级数分析:
时序性能的好坏立竿见影!!!!
总结
本章阐述了用一个DSP48E1实现了两个相关器的功能,充分利用了DSP48E1的特点:
1、单指令多数据 (SIMD) 算术单元,可以用作两个24bit/四个12bit 加法器或者减法器或者累加器
2、加减法动态配置
这两个特点,实现了 DSP48E1和 LUT+FF的资源置换,对于FPGA资源的高效利用非常有意义。
写在最后
各位看官,如果觉得对你有用,麻烦点个赞,干货啊,估计CSDN全网都找不到第2篇这样的。写一篇这样的文章花了我3-4个小时,全都是下班业余的时间,大家都只看不点赞不收藏,我刷抖音去不香么!谢谢各位支持。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 百万级群聊的设计实践
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 全网最简单!3分钟用满血DeepSeek R1开发一款AI智能客服,零代码轻松接入微信、公众号、小程
· .NET 10 首个预览版发布,跨平台开发与性能全面提升
· 《HelloGitHub》第 107 期