在Verilog编程中常常会遇到数据延时问题,进行计算的时候就需要将数据的时序对齐。本文以内值滤波器的实现为例,记录一下编程需要考虑的延时和数据截断问题。
插值滤波器的计算公式为
f
1
=
0.5
x
(
m
)
−
0.5
x
(
m
−
1
)
−
0.5
x
(
m
−
2
)
+
0.5
x
(
m
−
3
)
f
2
=
1.5
x
(
m
−
1
)
−
0.5
x
(
m
)
−
0.5
x
(
m
−
2
)
−
0.5
x
(
m
−
3
)
f
3
=
x
(
m
−
2
)
y
I
(
k
)
=
f
1
u
(
k
)
u
(
k
)
+
f
2
u
(
k
)
+
f
3
\begin{aligned} &f_1 = 0.5x(m)-0.5x(m-1)-0.5x(m-2)+0.5x(m-3)\\ &f_2=1.5x(m-1)-0.5x(m)-0.5x(m-2)-0.5x(m-3)\\ &f_3=x(m-2)\\ &y_I(k) = f_1u(k)u(k)+f_2u(k)+f_3 \end{aligned}
f1=0.5x(m)−0.5x(m−1)−0.5x(m−2)+0.5x(m−3)f2=1.5x(m−1)−0.5x(m)−0.5x(m−2)−0.5x(m−3)f3=x(m−2)yI(k)=f1u(k)u(k)+f2u(k)+f3
模块的输入输出端口定义,及输入数据移位缓存。定义4个reg变量用于存储输入数据。
module InterpolateFilter(
input rst_n,
input clk,
input signed[15:0] din,
input signed[15:0] uk,
output signed[17:0] dout
);
//输入数据缓存
reg signed[15:0] din_1, din_2, din_3, din_4;
always@(posedge clk or negedge rst_n) begin
if(!rst_n) begin
din_1 <= 16'd0;
din_2 <= 16'd0;
din_3 <= 16'd0;
din_4 <= 16'd0;
end
else begin
din_1 <= din;
din_2 <= din_1;
din_3 <= din_2;
din_4 <= din_3;
end
end
//输入数据缓存
reg signed[15:0] uk_1, uk_2;
always@(posedge clk or negedge rst_n) begin
if(!rst_n) begin
uk_1 <= 16'd0;
uk_2 <= 16'd0;
end
else begin
uk_1 <= uk;
uk_2 <= uk_1;
end
end
计算
f
1
、
f
2
、
f
3
f1、f2、f3
f1、f2、f3,其中din_1代表
x
(
m
)
x(m)
x(m),din_2代表
x
(
m
−
1
)
x(m-1)
x(m−1),以此类推。采用移位的方法代替乘数0.5,同时注意符号位。
4个16位有符号数相加,其结果有效位数应增加至18位。
wire signed[17:0] f1, f2, f3;//4个16位有符号数相加,拓展2位,即18位
//有符号数,右移时,符号位保持
//f1 = 0.5x(m)-0.5x(m-1)-0.5\x(m-2)+0.5x(m-3)
assign f1 = {{3{din_1[15]}},din_1[15:1]}
- {{3{din_2[15]}},din_2[15:1]}
- {{3{din_3[15]}},din_3[15:1]}
+ {{3{din_4[15]}},din_4[15:1]};
//f2 = 1.5x(m-1)-0.5x(m)-0.5x(m-2)-0.5x(m-3)
assign f2 = {{2{din_2[15]}},din_2} + {{3{din_2[15]}},din_2[15:1]}
- {{3{din_1[15]}},din_1[15:1]}
- {{3{din_3[15]}},din_3[15:1]}
- {{3{din_4[15]}},din_4[15:1]};
//f3 = x(m-2)
assign f3 = {{2{din_3[15]}},din_3};
接下来调用Vivado中的乘法器IP核,计算 f 1 u ( k ) 、 f 2 u ( k ) 、 f 1 u ( k ) u ( k ) f_1u(k)、f_2u(k)、f_1u(k)u(k) f1u(k)、f2u(k)、f1u(k)u(k),其中uk_1是输入数据uk的缓存,计算 f 1 u ( k ) u ( k ) f_1u(k)u(k) f1u(k)u(k)时需等待 f 1 u ( k ) f_1u(k) f1u(k)的结果出来,同时对其截位至18位(最高位符号位+低17位),并且 f 1 u ( k ) u ( k ) f_1u(k)u(k) f1u(k)u(k)落后了1个时钟周期。另外此时的输入数据uk已经更新,所以也需要提前对其缓存,便有uk_1、uk_2。
wire signed[33:0] f1_u, f2_u, f1_u_u;
mult18_16 u_mult1(
.CLK(clk), // input wire CLK
.A(f1), // input wire [17 : 0] A
.B(uk_1), // input wire [15 : 0] B
.P(f1_u) // output wire [33 : 0] P
);
//f2_u = f2*uk
mult18_16 u_mult2(
.CLK(clk), // input wire CLK
.A(f2), // input wire [17 : 0] A
.B(uk_1), // input wire [15 : 0] B
.P(f2_u) // output wire [33 : 0] P
);
//f1_u_u = f1*uk*uk 此处结果落后1个周期
mult18_16 u_mult3(
.CLK(clk),
.A({f1_u[33],f1_u[16:0]}),//取18位有效数据,
.B(uk_2),
.P(f1_u_u)
);
注意到 f 3 f_3 f3已经超前 f 1 u ( k ) u ( k ) f_1u(k)u(k) f1u(k)u(k) 2个时钟周期, f 2 u ( k ) f_2u(k) f2u(k)超前 f 1 u ( k ) u ( k ) f_1u(k)u(k) f1u(k)u(k) 1个时钟周期,于是乎需要提前缓存两者的数据。
//延时 与f1_u_u 对齐
reg signed[33:0] f2_u_1,f3_1, f3_2;
always@(posedge clk or negedge rst_n) begin
if(!rst_n) begin
f2_u_1 <= 34'd0;
f3_1 <= 34'd0;
f3_2 <= 34'd0;
end
else begin
f2_u_1 <= f2_u;
f3_1 <= f3;
f3_2 <= f3_1;
end
end
最后计算 y 1 ( k ) = f 1 u ( k ) u ( k ) + f 2 u ( k ) + f 3 y_1(k) = f_1u(k)u(k)+f_2u(k)+f_3 y1(k)=f1u(k)u(k)+f2u(k)+f3,以f1_u_u的时序为准对齐即可,此处也需要注意数据截断问题。
reg signed[18:0] dout_reg;
always@(posedge clk or negedge rst_n) begin
if(!rst_n) begin
dout_reg <= 19'd0;
end
else begin
dout_reg <= {f1_u_u[33],f1_u_u[17:0]} + {f2_u_1[33],f2_u_1[17:0]} + {f3_2[17],f3_2};
end
end
assign dout = {dout_reg[18],dout_reg[16:0]};
最后完整的仿真时序图如下。
验证:
x
(
m
)
=
2
,
x
(
m
−
1
)
=
6
,
x
(
m
−
2
)
=
4
,
x
(
m
−
4
)
=
2
f
1
=
1
−
3
−
2
+
1
=
−
3
f
2
=
9
−
1
−
2
−
1
=
5
f
3
=
4
u
(
k
)
=
2
f
1
u
(
k
)
u
(
k
)
=
−
12
f
2
u
(
k
)
=
10
y
I
(
k
)
=
−
12
+
10
+
4
=
2
,
即
如
图
d
o
u
t
=
2
\begin{aligned} &x(m)=2,x(m-1)=6,x(m-2)=4,x(m-4)=2\\ &f_1 =1-3-2+1=-3\\ &f_2=9-1-2-1=5\\ &f_3=4\\ &u(k)=2\\ &f_1u(k)u(k)=-12\\ &f_2u(k)=10\\ &y_I(k)=-12+10+4=2,即如图dout=2 \end{aligned}
x(m)=2,x(m−1)=6,x(m−2)=4,x(m−4)=2f1=1−3−2+1=−3f2=9−1−2−1=5f3=4u(k)=2f1u(k)u(k)=−12f2u(k)=10yI(k)=−12+10+4=2,即如图dout=2