PSoC5LP Prototyping Kit (16) --- DFB と Filter (8)

N タップ FIR (Finite Impulse Response) フィルタの構成を下の図に示します。 フィルタの「次数」で言うなら (N-1) 次のフィルタです。
ここで、角括弧 を伴った量は、時間方向に離散化 (discretization) されたサンプル値であるとします。
h[・] は「インパルス応答」の値で、h[0] が最初に現れる値、h[N-1] が最後に現れる値です。
x[k] が「現在」の (サンプルされた) 入力信号を表すものとし、x[k-N+1] が (N-1) サンプル前の値とします。
[f:id:pcm1723:20151110161155p:image]
フィルタを作用させるということは、入力サンプル値系列とインパルス応答系列とを「たたみ込む」ことなので、ふたつの系列の時間方向の順序を逆にしたもの同士を掛け合わせることになります。
たとえば、最後のインパルス応答 h[N-1] と 最新の入力サンプル x[k] を掛け、最初のインパルス応答 h[0] に (N-1) サンプル前の x[k-N+1] を掛けます。
そして、次のサンプル x[k+1] が到着したら、「遅延メモリ」全体を上の図で言えば右シフトして更新したものについて計算を行い、新たな出力値とします。
Filter コンポーネントで作成される実際の FIR フィルタ計算プログラムでは、サンプル・メモリの内容のシフトは全く行なわず、その代わりに新しいサンプルを格納する RAM アドレスを変化させ、サンプル・メモリから読み出す系列の最初のアドレスを変化させることで実現しています。
一方、IIR バイクアッド・フィルタでは、遅延メモリ間で実際にデータを移動させています。
FIR フィルタ計算のようすを次に示します。
ここで、 A[・] は入力サンプル系列が格納される RAM A を表すものとします。 角括弧
内の数値は、上の図とは違って、「RAM アドレス」を表現しています。
B[] はインパルス応答列が格納される RAM B を表しています。 逆順 (B[0] が最後の値、B[N-1] が最初の値) で格納されています。
簡単のため、RAM のアドレスは、N タップ・フィルタに対して 0 番地から (N-1) 番地まで割り付けられるものとします。 これは、チャネル A で目的の FIR フィルタを定義するか、またはチャネル A は無効でチャネル B で目的の FIR フィルタを定義する場合に、そのような割り付けになります。
上の図では、現在の入力サンプル x[k] に対する最初の計算では遅延メモリにストアされないような表現になっていますが、実際の FIR フィルタ・プログラムでは、現在の入力サンプルをまずサンプル・メモリ中にストアしてから計算を始めています。
また、アキュムレータはゼロクリアされた状態からたたみ込み演算を開始するものとします。
まず、1 番目のサンプルに対する計算です。

A[0] = new_sample
Acc += B[0] * A[0]
Acc += B[1] * A[1] 
Acc += B[2] * A[2]
    …
Acc += B[N-2] * A[N-2]
Acc += B[N-1] * A[N-1]

入力サンプルは最も若い番地の A[0] にストアされ、その A[0] からたたみ込み演算を開始します。
この段階では、A[0] のみ入力サンプルが入っており、他のサンプル・メモリには初期値 0 が格納されています。
2 番目のサンプルが到着すると下のような計算を行います。

A[N-1] = new_sample
Acc += B[0] * A[N-1]
Acc += B[1] * A[0] 
Acc += B[2] * A[1]
    …
Acc += B[N-2] * A[N-3]
Acc += B[N-1] * A[N-2]

2 番目のサンプルは A[N-1] に格納され、その A[N-1] からたたみ込み演算を開始します。 インパルス応答である B[] については B[0] から読み出されており、前回とは変化ありません。
最初のサンプルに対する計算では (B[1] * A[1]) だったものが (B[1] * A[0]) となり、1 個ズレた組み合わせとなっています。 以下、すべての組み合わせが前回の計算とは 1 個ズレた形となります。
3 番目のサンプルに対しては下のようになります。

A[N-2] = new_sample
Acc += B[0] * A[N-2]
Acc += B[1] * A[N-1] 
Acc += B[2] * A[0]
    …
Acc += B[N-2] * A[N-4]
Acc += B[N-1] * A[N-3]

3 番目のサンプルは A[N-2] に格納され、その A[N-2] からたたみ込み演算を開始します。 以下、最初の入力サンプルに対する計算とは 2 個ズレた組み合わせとなっています。
N 番目のサンプルに対しては下のようになります。

A[1] = new_sample
Acc += B[0] * A[1]
Acc += B[1] * A[2] 
Acc += B[2] * A[3]
    …
Acc += B[N-2] * A[N-1]
Acc += B[N-1] * A[0]

N 番目のサンプルは A[1] に格納され、その A[1] からたたみ込み演算を開始します。
一番最初の入力サンプルは A[0] に格納されていますが、これは次回 (N+1) 番目のサンプルに対する計算の時に新しいサンプルの値に書き換えられて消えてしまいます。
その次回の (N+1) 番目のサンプルに対する計算は、一番最初のサンプルに対する計算と同じ形になります。
あとは、この繰り返しです。
このたたみ込み演算部分を下のような 1 行のプログラムで実現しています。 (入力サンプル値のストアは初期化部で行なわれる)

ChA_fir:
  acu(incr,incr) dmux(sra, srm) alu(setb) mac(macc) jmpl(eob,acubeq, ChA_firFinish)