在數字通信中,很重要的一步是做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。
工具即可計算出FIR參數。同時可以看到濾波器的特性:如頻率特性
單位沖擊響應,(沖擊響應左側第一個點越接近0 效果越好)
單位階躍響應
生成的濾波器系數,可以寫到文本文件,以方便下一步輸入到Xilinx FIR compiler
csvwrite('myFile.txt', Hps4.Numerator)
2 Xilinx FIR compiler
這個IP使用的方法不難,網上介紹的文章很多。這里只對關鍵步驟進行說明。
2.1 輸入濾波器參數
Xilinx的IP可以提供自動的參數量化的功能,因此不需要進行在Matlab中進行量化,直接將參數輸入即可。
輸入參數向量以后,IP核會自動計算量化,並在左側Freq. Response中給出濾波器頻響曲線;之后在下方改變濾波器類型為“Interpolation”插值型,插值系數為5.
2.2 並行通道與硬件過采樣設置
這里將通道數設置為2,(RFdc采用IQ輸出,因此兩個並行的通道對應IQ數據,兩通道相互獨立)。
硬件過采樣按圖設置,即不進行過采樣。在此頁面的最下方給出了結果。
每個時鍾輸入一次數據
每個時鍾輸出一次數據
每次並行輸入一組數據
每次並行輸出五組數據 (對應插值系數5)
2.3 輸出位數設置
濾波器進行了乘法操作,數據位數理論上會變寬,即輸入16位數據 輸出數據位寬大於16位,但是RFdc的量化精度為16位,因此直接取輸出結果的高16位即可。
這里不改變參數量化的設置,保持默認即可(圖中參數含義:參數量化為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()
上圖為數據10001100111111...的成形后波形。
3 RF data converter
說一點RFdc的配置,首先設置DAC的每個時鍾周期的采樣點數為10點(I, Q * 5 = 10)。
之后在手冊中可以查知,總線上高位為Q,低位為I,高位為“后”采樣點,低位為“先”采樣點。正好與FIR濾波器的並行輸出的位數一致,因此可以直接連接。
4 測試結果
從FIR濾波器的圖中,可以看出,第一個0點對應的歸一化頻率是0.32,
這里對應的是采樣率。因此可以計算半帶寬為5(sps) * 10Mbps(碼速率)*0.32 = 16MHz
圖中M1 delta頻率為15.85MHz。