FPGA 校正算法的Verilog HDL实现
上一节讲了基于LMS算法的通道失配校正模型的MATLAB仿真MATLAB 基于LMS算法的通道失配校正算法仿真_小小低头哥的博客-优快云博客,这一节介绍如何使用FPGA实现此校正算法。
1.确定运算字长及数据截位方法
在编写Verilog HDL程序前,先讨论一下字长问题。根据定点数运算的规则,在同一个系统中各定点数的小数点必须确保一致才能进行正确的运算。 同时,为保证运算的精确性,还必须尽量保证更多的有效数据位参与运算。在本例中,由于涉及乘法运算,系统为闭环系统,数据截位操作是无法避免的。根据上一节的MATLAB仿真结果可知,当输入数据范围为[-1 1]时,权值数据范围也在[-1 1]范围内。为了便于处理,通常将运算的小数点位定在符号位与次高位之间,即将数据范围限制在[-1 1]内(最高位为符号位,只剩下小数位,所以就在-1到1之间)。
要实现的数学表达式如下:
y
(
n
)
=
W
H
(
n
)
X
(
n
)
(1)
y(n)=W^H(n)X(n) \tag{1}
y(n)=WH(n)X(n)(1)
e ( n ) = d ( n ) − y ( n ) (2) e(n)=d(n)-y(n) \tag{2} e(n)=d(n)−y(n)(2)
Δ W ( n ) = 2 μ X ( n ) e ∗ ( n ) (3) \Delta W(n)=2\mu X(n)e^*(n) \tag{3} ΔW(n)=2μX(n)e∗(n)(3)
W ( n + 1 ) = W ( n ) + Δ W ( n ) (4) W(n+1)=W(n)+\Delta W(n)\tag{4} W(n+1)=W(n)+ΔW(n)(4)
整个通道失配校正系统其实就是要求在一个数据抽样周期内完成上面所列出的4个运算。需要注意的是,上式中所有数据均为复数 。先看式(1), W ( n ) W(n) W(n) 与 X ( n ) X(n) X(n) 的数据位数均为16比特,由于均在[-1 1]之间,因此相乘后的数据并不会溢出 。FPGA实现复数乘法运算时最好采用复数乘法器IP核的方式,保持全精度运算,则两个16比特数据的乘法输出为32比特。由于在求取误差信号时,参考信号 d ( n ) d(n) d(n)为16比特,因此在求取 e ( n ) e(n) e(n)时,必须对32比特的乘法结果进行截位处理。图1所示为定点数乘法运算中的小数点位置关系。
由图1可知,为了使 y ( n ) y(n) y(n)的小数点位置与x(n)相同,32比特的乘法结果数据需要截取16比特的数据,以获得 y ( n ) y(n) y(n)数据。复数P核的输出数据为33比特。由于输入数据均为16比特的定点数,且小数点位定在符号位与次高位之间,因此两个16比特数据相乘后符号位扩展为2比特,小数位扩展为30比特,共32比特数据,其中小数点位置在比特30与比特29之间。根据复数乘法运算规则(a+bi)×(c +di)=(ac-bd)+(ad + bc)i,复数乘法运算的最终结果为两个定点数乘法运算结果的和或差,为确保数据不溢出,采用33比特存储复数的实部及虚部数据。因此,16比特的yn)数据从复数乘法结果的30~15比特数据截取。
由于 d ( n ) d(n) d(n)与截位后的八n)均是16比特的定点数,且小数点位置均在符号位与次高位之间,因此两者直接相减,即可得到误差信号 e ( n ) e(n) e(n)。接下来需要求取权值更新值VW(n),这一步运算也是整个通道失配校正系统的关键步骤。首先需要注意的是,步长因子u=1/64,则2u=1/32,可通过右移5位来近似实现与2u的乘法运算。图2为计算 Δ W ( n ) \Delta W(n) ΔW(n)的定点数的小数点位置关系。与计算 y ( n ) y(n) y(n)不同的是,需要对乘法结果右移5位,实现与步长因子的乘法运算,也就是最高位再扩展5位符号位而已。因此, Δ W ( n ) \Delta W(n) ΔW(n)的截位方法为,取32位乘法结果数据的32~20比特,并扩展3位符号位作为最终的 Δ W ( n ) \Delta W(n) ΔW(n)。同样,根据复数乘法运算规则及其P核输出数据位数,33比特的数据已包含了扩展的2位符号位,因此再扩展3位符号位,即可实现乘以1/32的运算。第4个式子就比较简单了,直接将两个16比特的定点数相加即可。
2.算法的Verilog HDL实现
//通道失配校正算法
module MisMatch(
input rst, //复位信号
input clk, //时钟信号
input signed[15:0] rr, //参考数据实部
input signed[15:0] ri, //参考信号虚部
input signed[15:0] xr, //输入数据实部
input signed[15:0] xi, //输入数据虚部
output clkdv, //输出数据同步时钟,为系统时钟的1/4
output signed[15:0] yr, //输出数据实部
output signed[15:0] yi, //输出数据虚部
output signed [15:0] wr, //权值数据实部
output signed [15:0] wi, //权值数据虚部
output signed [15:0] er, //误差数据实部
output signed [15:0] ei //误差数据虚部
);
//3位计数器,计数周期为4,为输入数据的速率
reg [2:0] count;
reg clk_dv;
always @(posedge clk or posedge rst)
if(rst) begin
count <= 3'd0;
clk_dv <= 1'b0;
end
else if(count == 3'd3)begin //count从0计数到3
count <= 3'd0;
clk_dv <= 1'd1;
end
else begin
count <= count + 1'd1;
clk_dv <= 1'd0;
end
assign clkdv =clk_dv; //此信号作为数据有效输出信号 高电平有效
//计算输出数据 yn
reg signed [15:0] wit;
reg signed [15:0] wrt;
reg signed [32:0] yit;
reg signed [32:0] yrt;
reg signed [15:0] xit;
reg signed [15:0] xrt;
always @(posedge clk or posedge rst)
if(rst) begin
yit <= 16'd0;
yrt <= 16'd0;
end
else begin //第二步 耗费一个时钟
yit <= xit * -wit; //共轭 所以取了负号
yrt <= xrt * wrt;
end
//计算误差信号
reg signed [16:0] ehrt;
reg signed [16:0] ehit;
reg signed [15:0] rit;
reg signed [15:0] rrt;
always @(posedge clk or posedge rst)
if(rst) begin
ehrt <= 17'd0;
ehit <= 17'd0;
end //第三步 耗费一个时钟
else begin //注意 这里虚部取了共轭 本来是{rit[15],rit} - yit[31:15]
ehrt <= {rrt[15],rrt} - yrt[31:15];
ehit <= yit[31:15] - {rit[15],rit}; //yit[30:15] 去除两位符号位 并保留后15位小数位数据 再扩展一位符号位成17位 所以就是yit[31:15]
end
//将计算出的yn截位后输出
assign yr = yrt[30:15]; //保留了一位符号位 后15位为小数位
assign yi = yit[30:15];
//计算权值更新变量
reg signed [32:0] dwrt;
reg signed [32:0] dwit;
always @(posedge clk or posedge rst)
if(rst) begin
dwit <= 33'd0;
dwrt <= 33'd0;
end
else begin
dwit <= xit * ehit ; //第四步 耗费一个时钟 做乘法运算的时候小数位要对其
dwrt <= xrt * ehrt ; //可能由于ehrt小数位是第14-15位 最前面的两位均为符号位 这里运算的时候就去掉了一位符号位
end
//对权值进行更新 及锁存数据
reg clk_d;
always @(posedge clk or posedge rst)
if(rst) begin
xit <= 16'd0;
xrt <= 16'd0;
rit <= 16'd0;
rrt <= 16'd0;
wit <= 16'd0;
wrt <= 16'b0111111111111111;
end
else if(count==3'd3) begin //第一步 耗费了第一个时钟 count
//更新权值
wit <= wit + {{3{dwit[32]}},dwit[32:20]};//本来是取dwit[30:15],但是左移五位 就成了dwit[30:20] 再扩展成16位 加五位符号位
wrt <= wrt + {{3{dwrt[32]}},dwrt[32:20]};
//锁存数据
xit <= xi;
xrt <= xr;
rit <= ri;
rrt <= rr;
end
assign wr = wrt;
assign wi = wit;
assign er = ehrt;
assign ei = ehit;
endmodule
代码中的复数乘法器最好用IP核,更精确,由于我没有破解此IP核,所以直接用的*号计算。
3.FPGA实现后的仿真测试
将Modelsim中仿真输出的wr wi数据用MATLAB画出来。如图3所示
MATLAB仿真的收敛曲线如图4所示
比较图3、图4可以看出,MATLAB方针的收敛曲线与FPGA实现后的算法收敛曲线应该还算相同,说明FPG实现的结果接近理论值,满足设计需求。