Xilinx FIR compiler 實現pulse-shaping濾波器,並利用多通道和插值適配RFdc


在數字通信中,很重要的一步是做pulse-shaping(脈沖成形)。通常使用FIR濾波器實現成形濾波器。本文首先利用Matlab filterBuilder工具計算濾波器參數,之后利用Xilinx FIR compiler實現了濾波器,最后,通過配置FIR compiler的Parallel Channel 和 Interpolation 實現了對RF data converter適配,從而降低了總線速率。

1 生成濾波器系數

考慮到總線時鍾速率,碼速率等因素,設計成形濾波器,輸入為類型 平方根升余弦,SPS(samples per symbol) = 5,interpolation = 5,滾降系數 = 0.5,指定FIR階數 28。

image

工具即可計算出FIR參數。同時可以看到濾波器的特性:如頻率特性
image

單位沖擊響應,(沖擊響應左側第一個點越接近0 效果越好)
image

單位階躍響應
image

生成的濾波器系數,可以寫到文本文件,以方便下一步輸入到Xilinx FIR compiler

csvwrite('myFile.txt', Hps4.Numerator)

2 Xilinx FIR compiler

這個IP使用的方法不難,網上介紹的文章很多。這里只對關鍵步驟進行說明。

2.1 輸入濾波器參數

Xilinx的IP可以提供自動的參數量化的功能,因此不需要進行在Matlab中進行量化,直接將參數輸入即可。
image

輸入參數向量以后,IP核會自動計算量化,並在左側Freq. Response中給出濾波器頻響曲線;之后在下方改變濾波器類型為“Interpolation”插值型,插值系數為5.

2.2 並行通道與硬件過采樣設置

image

這里將通道數設置為2,(RFdc采用IQ輸出,因此兩個並行的通道對應IQ數據,兩通道相互獨立)。
硬件過采樣按圖設置,即不進行過采樣。在此頁面的最下方給出了結果。

每個時鍾輸入一次數據
每個時鍾輸出一次數據
每次並行輸入一組數據
每次並行輸出五組數據 (對應插值系數5)

2.3 輸出位數設置

濾波器進行了乘法操作,數據位數理論上會變寬,即輸入16位數據 輸出數據位寬大於16位,但是RFdc的量化精度為16位,因此直接取輸出結果的高16位即可。
image
這里不改變參數量化的設置,保持默認即可(圖中參數含義:參數量化為16位二進制數,其中小數位數17位)。下方的輸出配置為舍棄低位(Truncate LSBs),輸出位寬為16位。

左側Implementation Details中會給出輸入輸出的數據格式。

  • 輸入
    • 高16位為通道1 數據格式為sfix16_0
    • 低16位為通道0 數據格式為sfix16_0
  • 輸出
    • [159:144] 16位為通道1的第4個采樣點 數據格式為sfix16_0
    • [143:128] 16位為通道0的第4個采樣點 數據格式為sfix16_0
    • [127:112] 16位為通道1的第3個采樣點 數據格式為sfix16_0
    • [111: 96] 16位為通道0的第3個采樣點 數據格式為sfix16_0
    • [95 : 80] 16位為通道1的第2個采樣點 數據格式為sfix16_0
    • [79 : 64] 16位為通道0的第2個采樣點 數據格式為sfix16_0
    • [63 : 48] 16位為通道1的第1個采樣點 數據格式為sfix16_0
    • [47 : 32] 16位為通道0的第1個采樣點 數據格式為sfix16_0
    • [31 : 16] 16位為通道1的第0個采樣點 數據格式為sfix16_0
    • [15 : 0] 16位為通道0的第0個采樣點 數據格式為sfix16_0

務必注意輸出格式。

2.4 仿真分析

對上述設置進行仿真,下面給出一個簡單的testbench

`timescale 1ns / 1ps
module fir_tb();

reg clk;
reg [15:0] in_data_i, in_data_q;
reg in_tvalid;
wire in_tready;
wire [15:0] out_data0_i, out_data1_i, out_data2_i, out_data3_i, out_data4_i;
wire [15:0] out_data0_q, out_data1_q, out_data2_q, out_data3_q, out_data4_q;
wire out_tvalid;
initial clk = 0;
always #4 clk=~clk;


fir_compiler_0 your_instance_name (
  .aclk(clk),                              // input wire aclk
  .s_axis_data_tvalid(in_tvalid),  // input wire s_axis_data_tvalid
  .s_axis_data_tready(in_tready),  // output wire s_axis_data_tready
  .s_axis_data_tdata({in_data_i, in_data_q}),    // input wire [31 : 0] s_axis_data_tdata
  .m_axis_data_tvalid(out_tvalid),  // output wire m_axis_data_tvalid
  .m_axis_data_tdata({{out_data4_i, out_data4_q}, {out_data3_i, out_data3_q}, {out_data2_i, out_data2_q}, {out_data1_i, out_data1_q}, {out_data0_i, out_data0_q}})    // output wire [159 : 0] m_axis_data_tdata
);
integer i;
integer r;
// save to file
integer out0, out1, out2, out3, out4;
integer out0_q, out1_q, out2_q, out3_q, out4_q;
initial begin
    in_tvalid = 1;
    r = $random;
    in_data_i = (r>0) ? 16'h6665 : 16'h999b;
    in_data_q = (r<=0) ? 16'h6665 : 16'h999b;
    for (i=0; i<200; i=i+1)begin
        #40  // sps=5
        r = $random;
        in_data_i = (r>0) ? 16'h6665 : 16'h999b;
        in_data_q = (r<=0) ? 16'h6665 : 16'h999b;
    end
    $fclose(out0);
    $fclose(out1);
    $fclose(out2);
    $fclose(out3);
    $fclose(out4);
    
    $fclose(out0_q);
    $fclose(out1_q);
    $fclose(out2_q);
    $fclose(out3_q);
    $fclose(out4_q);
    $stop;
end


initial begin
    out0 = $fopen("out0.txt", "w");
    out1 = $fopen("out1.txt", "w");
    out2 = $fopen("out2.txt", "w");
    out3 = $fopen("out3.txt", "w");
    out4 = $fopen("out4.txt", "w");
    
    out0_q = $fopen("out0q.txt", "w");
    out1_q = $fopen("out1q.txt", "w");
    out2_q = $fopen("out2q.txt", "w");
    out3_q = $fopen("out3q.txt", "w");
    out4_q = $fopen("out4q.txt", "w");
end

always @(posedge clk) begin
    if (out_tvalid) begin
        $fwrite(out0, "%x\n", out_data0_i);
        $fwrite(out1, "%x\n", out_data1_i);
        $fwrite(out2, "%x\n", out_data2_i);
        $fwrite(out3, "%x\n", out_data3_i);
        $fwrite(out4, "%x\n", out_data4_i);
        
        $fwrite(out0_q, "%x\n", out_data0_q);
        $fwrite(out1_q, "%x\n", out_data1_q);
        $fwrite(out2_q, "%x\n", out_data2_q);
        $fwrite(out3_q, "%x\n", out_data3_q);
        $fwrite(out4_q, "%x\n", out_data4_q);
    end
end

endmodule

因為是輸出是並行輸出,在仿真器中不容易觀察波形,因此在testbench中,將數據分別保持到文件中,並寫了個簡單的python腳本進行繪圖。

from bitstring import BitArray
import matplotlib.pyplot as plt

f0 = open('./out0.txt')
f1 = open('./out1.txt')
f2 = open('./out2.txt')
f3 = open('./out3.txt')
f4 = open('./out4.txt')

data_int = []

while True:
    s0 = f0.readline()
    s1 = f1.readline()
    s2 = f2.readline()
    s3 = f3.readline()
    s4 = f4.readline()
    if len(s0) == 0:
        break
    if s0[0] == 'x':
        continue

    data_int.append(BitArray(hex=s0).int)
    data_int.append(BitArray(hex=s1).int)
    data_int.append(BitArray(hex=s2).int)
    data_int.append(BitArray(hex=s3).int)
    data_int.append(BitArray(hex=s4).int)

plt.plot(data_int)
plt.show()

f0.close()
f1.close()
f2.close()
f3.close()
f4.close()

image
上圖為數據10001100111111...的成形后波形。

3 RF data converter

說一點RFdc的配置,首先設置DAC的每個時鍾周期的采樣點數為10點(I, Q * 5 = 10)。
image
之后在手冊中可以查知,總線上高位為Q,低位為I,高位為“后”采樣點,低位為“先”采樣點。正好與FIR濾波器的並行輸出的位數一致,因此可以直接連接。
image

4 測試結果

image
從FIR濾波器的圖中,可以看出,第一個0點對應的歸一化頻率是0.32,
這里對應的是采樣率。因此可以計算半帶寬為5(sps) * 10Mbps(碼速率)*0.32 = 16MHz
圖中M1 delta頻率為15.85MHz。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM