FIR濾波器理論與實現程式碼(VHDL)
推薦使用電腦瀏覽。
數字濾波器
是一個作用範圍很廣的功能模組,當然FPGA發展到現在,有了很多很實用的濾波器IP核,方便開發者進行專案的開發,IP核的使用不在文章的探討範圍n。
在這裡先推薦一下matlab中的fda(filter design & analysis)工具,對於濾波器的設計和理解會有很大的幫助。
本文主要講FIR數字濾波器的基礎理論,自己的一些理解和程式碼示例。
FIR是
有限脈衝響應
(Finite Impulse Response),即訊號輸入FIR濾波器的響應最終會趨於0,通俗點講就是在每個取樣時刻累加次數是有限的,這個可以結合下面的公式進行理解。FIR因為其具有很好的
相位線性的特性
(
線性相位
通俗講就是很多通帶訊號同時輸入到FIR濾波器中,經過一定時間後又同時輸出)使其在工程中得到了廣泛的使用。
FIR濾波器本質上就是有限線性卷積的過程,對於係數f[n]有(M+1)個的FIR輸出和輸入時間序列x[n]的關係公式可以表示成:
時域上是
卷積過程
(這裡*號是卷積,
翻轉滑動相乘累加
),這裡根據公式對FPGA中濾波器
輸出最大不溢位位寬
的計算做個介紹,首先假設輸入x[n-k]是
幅值
為1,符號與f[k]相同的資料,則
。此時y[n]就是輸出與輸入間幅值差最大的時候,將其換算成
最小位寬差
為
,輸出位寬
最小
為輸入訊號位寬加W(向上取整)。對於時域卷積公式對於理解FIR濾波不是很好,現在將公式進行z變換(卷積z變換後就是相乘)後再理解:
變換到z域(這裡可以理解為
頻域
)後理解起來就很簡單了,畫張頻域圖來幫助理解。
這就是理論上(係數取值從負無窮到正無窮)濾波器濾波的原理。因為FIR濾波器係數實際工程中無法選擇無數個,所以導致工程濾波器存在過渡帶,通帶會有抖動,
阻帶
是按照一定比例衰減等等為問題。而工程FIR濾波器設計就是透過選擇不同的係數和其長度來平衡這些問題達到設計需求。
現在我們有了公式,要做的就是將公式轉化為工程,這個轉化過程是多種多樣的,這裡只做其中直接形和轉置形介紹。首先將上述卷積公式進行展開:
為了方便畫圖和理解,我們這裡假設M=2,公式變成:
將上面等式的結構畫出來是:
Unit Delay代表這裡有一個延遲。這裡假設三個係數分別是{-2,4,-2},公式和結構都非常清晰和簡單,根據上面的結構圖將其轉化成VHDL語言程式碼:
——工程名:直接形FIR濾波器
——濾波係數:f[0-2]={-2,4,-2}
library
ieee
;
use
ieee。std_logic_1164。
all
;
use
ieee。std_logic_arith。
all
;
use
ieee。std_logic_unsigned。
all
;
entity
fir_Direct
is
generic
(
w0
:
integer
:=
11
;
——濾波器輸出位寬
w1
:
integer
:=
13
——中間變數的位寬
);
port
(
clk
:
in
std_logic
;
rstn
:
in
std_logic
;
data_In
:
in
std_logic_vector
(
7
downto
0
);
data_Out
:
out
std_logic_vector
(
w0
-
1
downto
0
)
);
end
fir_Direct
;
architecture
beha
of
fir_Direct
is
type
array_3x8
is
array
(
0
to
2
)
of
std_logic_vector
(
7
downto
0
);
——定義一個數組
type
array_2x4
is
array
(
integer
range
<>
)
of
std_logic_vector
(
3
downto
0
);
signal
tap
:
array_3x8
;
signal
tap_Add
:
std_logic_vector
(
8
downto
0
);
signal
tap_Delay
:
std_logic_vector
(
8
downto
0
);
signal
c_t_mul1
,
c_t_mul2
,
data_OTemp
:
std_logic_vector
(
w1
-
1
downto
0
);
signal
coef
:
array_2x4
(
0
to
1
);
begin
coef
(
0
)
<=
conv_std_logic_vector
(
-
2
,
4
);
coef
(
1
)
<=
conv_std_logic_vector
(
4
,
4
);
process
(
clk
,
rstn
)
begin
if
rstn
=
‘0’
then
for
i
in
0
to
2
loop
——loop 是在一個時鐘內迴圈一遍
tap
(
i
)
<=
(
others
=>
‘0’
);
end
loop
;
tap_Add
<=
(
others
=>
‘0’
);
tap_Delay
<=
(
others
=>
‘0’
);
elsif
rising_edge
(
clk
)
then
tap
(
0
)
<=
data_In
;
tap
(
1
to
2
)
<=
tap
(
0
to
1
);
——tap(0)對應x[n],tap(1)對應x[n-1],後面依次對應
—— data_OTemp <= tap(0)*(-2) + tap(1)*4 + tap(2)*(-2)
——上面的寫法資源佔用較多,執行速度也比較慢
——係數對稱節約乘法器
tap_Add
<=
(
tap
(
0
)(
7
)
&
tap
(
0
))
+
(
tap
(
2
)(
7
)
&
tap
(
2
));
tap_Delay
<=
tap
(
1
)(
7
)
&
tap
(
1
);
c_t_mul1
<=
signed
(
tap_Add
)
*
signed
(
coef
(
0
));
c_t_mul2
<=
signed
(
tap_Delay
)
*
signed
(
coef
(
1
));
data_OTemp
<=
c_t_mul1
+
c_t_mul2
;
data_Out
<=
data_OTemp
(
w0
-
1
downto
0
);
end
if
;
end
process
;
end
beha
;
下面介紹一下轉置型別的FIR濾波器,轉置型別FIR濾波器相當於交換輸入輸出,顛倒訊號流方向,將加法器移動到延遲後面,
轉置型別FI
R結構圖如下:
很容易看出轉置結構最終轉化為公式和直接形是等效的,所不同的是輸入不需要額外的移位暫存器,為FPGA工程提供了一種新的結構,這種結構程式碼效率和資源量都要優於直接形。根據新的結構圖,將其轉化為VHDL程式碼:
——工程名:轉置形FIR濾波器
——濾波係數:f[0-2]={-2,4,-2}
library
ieee
;
use
ieee。std_logic_1164。
all
;
use
ieee。std_logic_arith。
all
;
use
ieee。std_logic_unsigned。
all
;
entity
fir_Transposed
is
generic
(
w0
:
integer
:=
11
;
——濾波器輸出位寬
w1
:
integer
:=
12
——中間變數的位寬
);
port
(
clk
:
in
std_logic
;
rstn
:
in
std_logic
;
data_In
:
in
std_logic_vector
(
7
downto
0
);
data_Out
:
out
std_logic_vector
(
w0
-
1
downto
0
)
);
end
fir_Transposed
;
architecture
beha
of
fir_Transposed
is
type
array_3x8
is
array
(
0
to
2
)
of
std_logic_vector
(
7
downto
0
);
type
array_3xx
is
array
(
0
to
2
)
of
std_logic_vector
(
w1
-
1
downto
0
);
signal
sum
,
c_t_mul
:
array_3xx
;
signal
tap
:
array_3x8
;
begin
c_t_mul
(
0
)
<=
signed
(
tap
(
0
))
*
signed
(
conv_std_logic_vector
(
-
2
,
4
));
c_t_mul
(
1
)
<=
signed
(
tap
(
0
))
*
signed
(
conv_std_logic_vector
(
4
,
4
));
c_t_mul
(
2
)
<=
c_t_mul
(
0
);
process
(
clk
,
rstn
)
begin
if
rstn
=
‘0’
then
tap
(
0
)
<=
(
others
=>
‘0’
);
for
i
in
0
to
2
loop
sum
(
i
)
<=
(
others
=>
‘0’
);
end
loop
;
elsif
rising_edge
(
clk
)
then
tap
(
0
)
<=
data_In
;
sum
(
0
)
<=
c_t_mul
(
2
);
for
i
in
1
to
2
loop
sum
(
i
)
<=
sum
(
i
-
1
)
+
c_t_mul
(
2
-
i
);
end
loop
;
data_Out
<=
sum
(
2
)(
w0
-
1
downto
0
);
end
if
;
end
process
;
end
beha
;