作者:桂。
時間:2018-02-06 17:52:38
鏈接:http://www.cnblogs.com/xingshansi/p/8423457.html
前言
到目前為止,本文沒有對濾波器實現進行梳理,FIR仿真驗證的平台(基於FPGA實現)包括HLS、Systemgenerator,至於*.v 與*.sv可通過程序(如python實現)完成轉化,FIR的零散記錄到本篇告一段落,本文重點記錄DSP48E的使用
一、DSP48E
A-基本結構
主要參考UG479.pdf,DSP48E1結構:
可以看出主要功能為:P = (A±D)×B±C。具體功能可參考IP核:
slice結構及位寬關系:
DSP48E在Xilinx內部的布局:
常用器件DSP48E資源:
B-原語調用
原語類似C語言的匯編,直接關聯器件的底層結構,因此通常時序可以做的更好。
DSP48E支持原語調用,記錄兩個例子:
Ex1:
`timescale 1ns / 1ps // m = b * (a + d) // p = c+m or p+m module dsp48_wrap_f ( input clock, input ce1, input ce2, input cem, input cep, input signed [24:0] a, input signed [17:0] b, input signed [47:0] c, input signed [24:0] d, // this has two fewer pipe stages // X+Y is usually the multiplier output (M) // Z is either P, PCIN or C // bit 1:0: 0: Z+X+Y 3:Z-(X+Y) 1: -Z + (X+Y) 2: -1*(Z+X+Y+1) // bits 3:2, 0: Z=0, 1: Z=PCIN, 2: Z=P, 3: Z = C // bit 4: sub in pre add input [4:0] mode, input signed [47:0] pcin, output signed [47:0] pcout, output signed [47-S:0] p); parameter S = 0; parameter USE_DPORT = "FALSE"; // enabling adds 1 reg to A path parameter AREG = 1; parameter BREG = 1; // 0 - 2 wire signed [47:0] dsp_p; assign p = dsp_p[47:S]; DSP48E1 #( .A_INPUT("DIRECT"), // "DIRECT" "CASCADE" .B_INPUT("DIRECT"), // "DIRECT" "CASCADE" .USE_DPORT(USE_DPORT), .USE_MULT("MULTIPLY"),// "MULTIPLY" "DYNAMIC" "NONE" .USE_SIMD("ONE48"), // "ONE48" "TWO24" "FOUR12" // pattern detector - not used .AUTORESET_PATDET("NO_RESET"), .MASK(48'h3fffffffffff), .PATTERN(48'h000000000000), .SEL_MASK("MASK"), .SEL_PATTERN("PATTERN"), .USE_PATTERN_DETECT("NO_PATDET"), // register enables .ACASCREG(1), // pipeline stages between A/ACIN and ACOUT (0, 1 or 2) .ADREG(1), // pipeline stages for pre-adder (0 or 1) .ALUMODEREG(1), // pipeline stages for ALUMODE (0 or 1) .AREG(AREG), // pipeline stages for A (0, 1 or 2) .BCASCREG(1), // pipeline stages between B/BCIN and BCOUT (0, 1 or 2) .BREG(BREG), // pipeline stages for B (0, 1 or 2) .CARRYINREG(1), // this and below are 0 or 1 .CARRYINSELREG(1), .CREG(1), .DREG(1), .INMODEREG(1), .MREG(1), .OPMODEREG(1), .PREG(1)) dsp48_i ( // status .OVERFLOW(), .PATTERNDETECT(), .PATTERNBDETECT(), .UNDERFLOW(), // outs .CARRYOUT(), .P(dsp_p), // control .ALUMODE({2'd0, mode[1:0]}), .CARRYINSEL(3'd0), .CLK(clock), .INMODE({1'b0,mode[4],3'b100}), .OPMODE({1'b0,mode[3:2],4'b0101}), // signal inputs .A({5'd0,a}), // 30 .B(b), // 18 .C(c), // 48 .CARRYIN(1'b0), .D(d), // 25 // cascade ports .ACOUT(), .BCOUT(), .CARRYCASCOUT(), .MULTSIGNOUT(), .PCOUT(pcout), .ACIN(30'h0), .BCIN(18'h0), .CARRYCASCIN(1'b0), .MULTSIGNIN(1'b0), .PCIN(pcin), // clock enables .CEA1(ce1), .CEA2(ce2), .CEAD(1'b1), .CEALUMODE(1'b1), .CEB1(ce1), .CEB2(ce2), .CEC(1'b1), .CECARRYIN(1'b1), .CECTRL(1'b1), // opmode .CED(1'b1), .CEINMODE(1'b1), .CEM(cem), .CEP(cep), .RSTA(1'b0), .RSTALLCARRYIN(1'b0), .RSTALUMODE(1'b0), .RSTB(1'b0), .RSTC(1'b0), .RSTCTRL(1'b0), .RSTD(1'b0), .RSTINMODE(1'b0), .RSTM(1'b0), .RSTP(1'b0) ); endmodule // dsp48_wrap_f
Ex2:
// p = c + b * a 3 cycles if r else p = p + b * a module macc ( input clock, input [2:0] ce, // bit 0 = a, 1 = b , 2 = c input r, // reset accumulator to c + a*b input signed [24:0] a, input signed [17:0] b, input signed [47:0] c, output signed [47-S:0] p); parameter S = 0; parameter AREG = 1; // 0 - 2 parameter BREG = 1; // 0 - 2 wire signed [47:0] dsp_p; assign p = dsp_p[47:S]; // X+Y is usually the multiplier output (M) // Z is either P, PCIN or C // bit 1:0: 0: Z+X+Y 3:Z-(X+Y) 1: -Z + (X+Y) 2: -1*(Z+X+Y+1) // bits 3:2, 0: Z=0, 1: Z=PCIN, 2: Z=P, 3: Z = C // bit 4: sub in pre add wire [4:0] mode = {1'b0, r ? 2'b11 : 2'b10, 2'b00}; DSP48E1 #( .A_INPUT("DIRECT"), // "DIRECT" "CASCADE" .B_INPUT("DIRECT"), // "DIRECT" "CASCADE" .USE_DPORT("FALSE"), .USE_MULT("MULTIPLY"),// "MULTIPLY" "DYNAMIC" "NONE" .USE_SIMD("ONE48"), // "ONE48" "TWO24" "FOUR12" // pattern detector - not used .AUTORESET_PATDET("NO_RESET"), .MASK(48'h3fffffffffff), .PATTERN(48'h000000000000), .SEL_MASK("MASK"), .SEL_PATTERN("PATTERN"), .USE_PATTERN_DETECT("NO_PATDET"), // register enables .ACASCREG(1), // pipeline stages between A/ACIN and ACOUT (0, 1 or 2) .ADREG(1), // pipeline stages for pre-adder (0 or 1) .ALUMODEREG(1), // pipeline stages for ALUMODE (0 or 1) .AREG(AREG), // pipeline stages for A (0, 1 or 2) .BCASCREG(1), // pipeline stages between B/BCIN and BCOUT (0, 1 or 2) .BREG(BREG), // pipeline stages for B (0, 1 or 2) .CARRYINREG(1), // this and below are 0 or 1 .CARRYINSELREG(1), .CREG(1), .DREG(1), .INMODEREG(1), .MREG(1), .OPMODEREG(1), .PREG(1)) dsp48_i ( // status .OVERFLOW(), .PATTERNDETECT(), .PATTERNBDETECT(), .UNDERFLOW(), // outs .CARRYOUT(), .P(dsp_p), // control .ALUMODE({2'd0, mode[1:0]}), .CARRYINSEL(3'd0), .CLK(clock), .INMODE({1'b0,mode[4],3'b100}), .OPMODE({1'b0,mode[3:2],4'b0101}), // signal inputs .A({5'd0,a}), // 30 .B(b), // 18 .C(c), // 48 .CARRYIN(1'b0), .D(25'd0), // 25 // cascade ports .ACOUT(), .BCOUT(), .CARRYCASCOUT(), .MULTSIGNOUT(), .PCOUT(), .ACIN(30'h0), .BCIN(18'h0), .CARRYCASCIN(1'b0), .MULTSIGNIN(1'b0), .PCIN(48'h0), // clock enables .CEA1(1'b1), .CEA2(ce[0]), .CEAD(1'b1), .CEALUMODE(1'b1), .CEB1(1'b1), .CEB2(ce[1]), .CEC(ce[2]), .CECARRYIN(1'b1), .CECTRL(1'b1), // opmode .CED(1'b1), .CEINMODE(1'b1), .CEM(1'b1), .CEP(1'b1), .RSTA(1'b0), .RSTALLCARRYIN(1'b0), .RSTALUMODE(1'b0), .RSTB(1'b0), .RSTC(1'b0), .RSTCTRL(1'b0), .RSTD(1'b0), .RSTINMODE(1'b0), .RSTM(1'b0), .RSTP(1'b0) ); endmodule
二、FIR實現思路
考慮到調用DSP48E,首先分析DSP48E乘法/乘加的時序特性:
可以看出輸出相比輸入,延遲4拍,仿真3*5,結果與理論一致:
以N-1(不失一般性,N=6)階FIR為例,由於乘法可支持25*18,假設數據18(bit),濾波器系數25(bit)。濾波器系數個數為6:
因此可得FIR實現的基本流程:
- Step1:對於t時刻,輸入數據與濾波器系數相乘,得到y(t)[N-1:0]
- Step2:更新數據流:data_chain(t) = y(t)[N-1:0] + [data_chain(t-1) [N-2:0],0]
- Step3:輸出濾波結果:output = data_chain(t) [N-1]
根據算法流程,設計FPGA數據流:
1)參數位寬定義
- 輸入數據:parameter indatwidth = 18;
- 濾波器系數:parameter coefwidth = 25;
- DSP48核輸出位寬:localparam multoutwidth = coefwidth + indatwidth;
- 輸出數據(自定義):parameter outdatwidth = 18;
- 數據流(截斷位寬自定義):這里 localparam chainwidth 用multoutwidth替代;
2)數據運算拆解
結合上文Step2的特性,細節上:a)可針對coef0單獨用乘法運算、其他coef利用乘加運算,b)也可以對datachain補零,這里采用后一種思路。
- 輸入輸出
input [indatwidth-1:0] datin;
input [5:0][coefwidth-1:0] coef;
input clk,rst;
output signed [outdatwidth-1:0] datout;
- DSP48的乘加操作
genvar ii;
generate
for(ii = 0; ii < N; ii++)
begin
multiplus mpu(
.CLK(clk),
.A(coef[ii]),
.B(datin),
.C(dti[ii]),
.P(mres[ii])
);
end
endgenerate
- 關於截位
對數據進行截位,例如對x截位,通常不是直接舍去其他位數,而是對x進行4舍5入,轉化到FPGA就是:
x1 <= x[起始位置 -: 有效位數] + 1;
result <= (x1>>>1);
這里僅論證實現思路,截位的細節操作不再添加。
- 乘法器的延拍
genvar ii;
generate
for(ii = 1; ii < N; ii++)
begin
always @(posedge clk) begin
dtchain[ii][fixdelay-1:1] <= dtchain[ii][fixdelay-2:0];
dtchain[ii][0] <= mres[ii-1][multoutwidth-1:0];
end
end
endgenerate
三、仿真驗證
首先MATLAB仿真驗證上述步驟的有效性:
%FIR功能驗證 clc;clear all;close all; coef = [-15,19,123,123,19,-15]; datin = [3,13,17,21,24,28,31]; %main %不考慮延拍,datachain不必引入 N = 6; mres = zeros(1,N); dto = zeros(1,N); result = []; for i = 1:length(datin) dto(2:N) = mres(1:N-1); mres = datin(i)*coef + dto; result = [result,mres(N)]; end %compare conv_res = conv(datin,coef); [result;conv_res(1:length(datin))]
算法運算結果與理論一致:
編寫測試模塊及testbench:
winfilter.sv
`timescale 1ns / 1ps module winfilter(coef, datin, clk, rst, datout); //parameter parameter indatwidth = 18; parameter outdatwidth = 18; parameter coefwidth = 25; localparam multoutwidth = coefwidth + indatwidth; localparam N = 6; localparam fixdelay = 4;//smultplus delay //port input [indatwidth-1:0] datin; input [N-1:0][coefwidth-1:0] coef; input clk,rst; output [outdatwidth-1:0] datout; //define reg signed [outdatwidth-1:0] datout; reg [N-1:0][fixdelay-1:0][multoutwidth-1:0] dtchain; wire [N-1:0][multoutwidth:0] mres; //initial initial begin dtchain <= 0; datout <= 0; end //main genvar ii; generate for(ii = 1; ii < N; ii++) begin always @(posedge clk) begin dtchain[ii][fixdelay-1:1] <= dtchain[ii][fixdelay-2:0]; dtchain[ii][0] <= mres[ii-1][multoutwidth-1:0]; end end endgenerate generate for(ii = 0; ii < N; ii++) begin multiplus multp_inst( .CLK(clk), .A(coef[ii]), .B(datin), .C(dtchain[ii][fixdelay-1]), .P(mres[ii]) ); end endgenerate //output always @(posedge clk) begin if(rst) begin datout <= 0; end else begin datout <= mres[N-1][multoutwidth-19 -: outdatwidth]; //datout <= mres[N-1][multoutwidth-2 -: outdatwidth]; end end endmodule
tb
`timescale 1ns / 1ps module tb(); logic [17:0] datin; logic clk,rst; logic [5:0][24:0] coef; logic [17:0] datout; //-------------------------------------// parameter data_num = 32'd1024; reg [17:0] data_men[1:data_num]; initial begin $readmemb("D:/PRJ/vivado/simulation_ding/009_lpf6tap/matlab/sin_data.txt",data_men); end integer i = 1; always @(posedge clk) begin datin <= data_men[i]; i <= i + 8'd1; end initial begin clk <= 0; rst <= 0; datin <= 0; coef <= 0; #4 coef <= {-25'd15,25'd19,25'd123,25'd123,25'd19,-25'd15}; #6000 $stop; end always #2 clk = ~clk; winfilter wininst( .coef(coef), .datin(datin), .clk(clk), .rst(rst), .datout(datout) ); endmodule
其中dsp48參數設置:
仿真結果: