dsPIC33FJ64GP802 (13) --- IIR フィルタ関数と Yデータ・メモリ・アクセス (2)

 IIR フィルタ関数が範囲外の Y データ・メモリをアクセスすることで発生するドレス・エラーを防ぐ方法には、大きく分けて次の 2 種類があります。

  • 「範囲外」アクセスの対象が RAM 非実装領域とならないように、RAM 中にダミーの変数を定義して「範囲外」アクセスがその「実在」する RAM に対して行われるようにする。
  • IIR フィルタ関数自体を「改造」して、範囲外アクセスが発生しないようにする。

 後者のほうが本質的な解決策ですが、場合によっては性能低下を招く可能性もあり、ここでは、主に前者の方法を示します。
 IIR フィルタ関数の範囲外アクセスは、必要な遅延要素配列の最後の要素の次の 1 ワードを読み込むだけなので、RAM 領域の最後の 1 ワード (dsPIC33FJ64GP802 では 0x47FE ~ 0x47FF) にダミー変数を配置できればアドレス・エラーを阻止できます。
 その方法として、C 言語ソースの記述だけで対策できるものをあげると、次のようになります。

(1) 対象の遅延要素配列の配列要素数を 1 増やす。


(2) DMA 属性を持つ変数を定義する。  (DMA 機能を持つデバイスのみ)


(3) 遅延要素の delay1 と delay2 を別々に定義せず、ひとつの配列として定義する。 (IIRTransposed() 関数のみ)

 (1) では、ひとつ増やした配列要素が「ガード」となって、アドレス・エラーを防ぎます。 「N_sect」を 2 次セクション数として、たとえば、次のように記述します。

// delay element in Y-DATA memory
// for IIRCanonic()
fractional _YBSS(2) d_880[2*N_sect+1];

// for IIRTransposed()
fractional _YBSS(2) d1_880[N_sect+1];
fractional _YBSS(2) d2_880[N_sect];

 複数の IIR フィルタを使用する場合、この対策を施すのは、RAM の最後尾に配置されるフィルタに対してだけ行えばよいのですが、問題は、どの遅延要素配列が最後尾に配置されるかというルールが不明なことです。
 あるバージョンの XC16 コンパイラでは最後尾に配置されていた遅延要素配列が、コンパイラのバージョンが変わったら、配置の順番も変わってしまうことも十分考えられます。
 その場合には、ソースの記述に手を入れなければなりません。
 (2) は DMA を持つデバイスにしか有効ではありませんが、確実な方法です。
 DMA を持つデバイスでは、SRAM の後ろの方、Y メモリ領域内に DMA 用のデュアルポート・メモリが実装されていて、CPU と DMA コントローラが、お互いに干渉せずにメモリ・アクセスができるようになっています。 
 dsPIC33FJ64GP802 では、この DPSRAM (Dual Port SRAM) の容量は 2 K バイトで、0x4000 ~ 0x47FF に配置されています。
 「dma」属性を持つ変数が宣言されると、DMA RAM 領域の後ろから割り当てられていき、すべて割り当て終えたあとで「ymemory」属性を持つ変数の配置に移ります。
 つまり、ひとつでも「dma」属性を持つ変数が宣言されると、確実にそれ (ら) はデータ・メモリの最後尾に配置され、他の Y データ・メモリに配置されるべき変数は「最後尾」ではないことが保証されます。
 すでにプログラムで DMA を使っているなら、特にすべきことはなく、DMA を使っていないプログラムでは、たとえば次のようにしてダミー変数を定義します。

// dummy variable at bottom of Y-memory
fractional __attribute__((space(dma))) dummy;

この指定をした場合のリンク結果の .map ファイルの例を下に示します。

Microchip Technology Inc, v1.32 (B)

External Symbols in Data Memory (by address):

                    0x0800                  _ph_acc
                    0x0804                  _ph_inc

         . . . . . . <中略> . . . . . .					
                    0x4626                  _d_880
                    0x4632                  _d1_880
                    0x4638                  _d2_880
                    0x463e                  _d1_932
                    0x4644                  _d2_932
                    0x47fe                  _dummy

 「_dummy」以外の「_d」で始まるシンボルは IIR フィルタの遅延要素配列です。
 _dummy が他の遅延要素配列に優先して RAM の最後尾に配置されているのが分かります。
 方法の (3) は、IIRTransposed() 関数では、delay1 側のアクセスで問題を起こすので、delay1 側の遅延要素配列が「単独」で RAM の最後尾に配置されないように、delay1 と delay2 とを、この順番で連結してひとつの配列として宣言するものです。
 下に例を示します。

// for IIRTransposed()
fractional _YBSS(2) d12_880[2*N_sect];

IIRTransposedStruct IIRT_prof_880;

         . . . . . . <中略> . . . . . .					
  IIRT_prof_880.delayBase1 = &d12_880[0];
  IIRT_prof_880.delayBase2 = &d12_880[N_sect];

 delay2 側の配列が「ガード」となってアドレス・エラーを防ぎます。