PSoC5LP Prototyping Kit (11) --- DFB と Filter (3)

DFB (Digital Filter Block) で扱うデータは 24 ビット幅ですが、DFB は 8 ビット MCU の 8051 を搭載した PSoC3 にも内蔵されています。
当然、8 ビット MPU の 8051 では 24 ビット・データを一度に扱うことはできません。 8 ビットの読み書きを 3 回行なう必要があります。 そのため、DFB のデータ入力部には「ステージング・レジスタ」(staging register) と称するれレジスタが設けられており、24 ビット・データが「完成」するまで保持しておく機能を持たせています。
DFB 自体は PSoC3 でも PSoC5LP でも共通なので、8 ビット対応機能は PSoC5LP 内蔵の DFB にも含まれています。
DFB のデータ・パスの簡略化したブロック図を下に再掲します。

出力には「ホールディング・レジスタ」(holding register) があって出力データを保持しています。 
ステージング・レジスタとホールディング・レジスタとは A と B の 2 組あって、DFB を擬似的に 2 チャネルあるように見せることができます。
DFB の実体は「ミニ DSP」なので、DSP のプログラム次第ですが、

  • ステージング・レジスタ A にデータが入ると A 側の設定で計算を行いホールディング・レジスタ A に出力
  • ステージング・レジスタ B にデータが入ると B 側の設定で計算を行いホールディング・レジスタ B に出力

するような動作をさせることができます。
Filter コンポーネントでチャネル A/B 両方をイネーブルして作成したフィルタの DSP プログラムでは、実際にそのような作りになっています。
もちろん、演算ハードウェアは 1 組しかないので、2 チャネル分の処理は逐次的に行なわれます。 A / B 同時に入力されても、まず A / B どちらかのチャネルの処理を行って出力し、それから残りのチャネルの処理に取り掛かります。
また、ステージング/ホールディング・レジスタの A/B の区別と、データパスの RAM A/B、MAC の A/B 入力とは直接の関係はありません。 ステージング・レジスタ A から RAM B への書き込みも、ステージング・レジスタ B からの RAM A への書きこみも自由に行なえます。
ステージング・レジスタもホールディング・レジスタも、単に

  • HIGH : b23..b16 (STAGEAH / STAGEBH / HOLDAH / HOLDBH)
  • MID : b15..b8 (STAGEAM / STAGEBM / HOLDAM / HOLDBM)
  • LOW : b7..b0 (STAGEA / STAGEB / HOLDA / HOLDB)

の 3 つのバイト単位でアクセス可能な 24 ビット・レジスタというだけで、特別なものではありません。
キーとなるのは「コヒーレンシ」(coherency、一貫性) という考え方です。
24 ビット・データを一度に書き換えるのではなく、たとえば、8 ビットずつの 3 回の書き込みで書き換える場合に、一時的には前回のサンプルのデータと今回のサンプルのデータとが混在することになります。

  • 1 バイト目の書き込み後 — 前回のデータの残骸 2 バイト、今回のデータ 1 バイト
  • 2 バイト目の書き込み後 — 前回のデータの残骸 1 バイト、今回のデータ 2 バイト
  • 3 バイト目の書き込み後 — 3 バイトすべてが今回のデータ

前回のデータと今回のデータが混在する、途中の段階で計算が始まってしまうと正しい結果が得られないことになります。
ここで言う「コヒーレンシ」とは、どのレジスタへのアクセスをもって今回のデータが「完成」したと見なすかという「お約束」の設定のことです。
入力に対しては、ハードウェアはこの書き込みを検出して「入力データあり」のステータスを立てます。 出力データに関しては、ホールディング・レジスタに対するこの読み込みを検出して、(必要な分の) すべてのデータが読み出されたということで、レベル出力していた DMA リクエストのフラグを下げます。
32 ビット MCU である Cortex-M3 を搭載する PSoC5LP では、16 ビット・ワードアクセスも可能ですが、その場合には、その 16 ビット・ワード内に指定のレジスタが含まれていれば「完成」と見なされます。
デフォルトでは入力コヒーレンシ指定は HIGH になっています。
つまり、

  • 入力データの有効ビット幅が 8 ビット以下ならば、そのデータをレジスタの HIGH バイトに書きさえすればデータは完成
  • 入力データのビット有効幅が 16 ビット以下ならば、バイト・アクセスでは、MID バイト → HIGH バイトの順で書き込めば完成
  • 入力データのビット有効幅が 24 ビット以下ならば、バイト・アクセスでは、LOW バイト → MID バイト → HIGH バイトの順で書き込めば完成

となります。
UDB (Universal Digital Block) では、CPU / DMA とは PHUB (Perpheral HUB) の 16 ビットのスポークを介して接続されています。 UDB との間で入出力されるデータが 16 ビット幅以下ならば、1 回の DMA バーストで転送可能です。
ところが、ステージング/ホールディング・レジスタの MID バイトは奇数アドレスに配置されているので、16 ビット転送に対してはミス・アラインとなり、8 ビット転送 2 回で 16 ビット分のデータが転送されることになり、効率が落ちます。
これを 16 ビット・ワード境界にアラインした 1 回の DMA 転送で転送可能とするための「データ・アライン」(data align) 機能が用意されています。 (デフォルトはデータ・アラインなし)
これは、ステージング/ホールディング・レジスタで、データパス内部に対しては MID レジスタを HIGH レジスタのように、LOW レジスタを MID レジスタのように「見せかける」機能です。

入力データ・アラインを指定しておくと、DMA により、同一の 16 ビット・ワード境界内の STAGEM と STAGEA に書き込んだデータは、データパス内部ではあたかも STAGEH と STAGEM に対するデータのように扱われます。
同様に、出力データ・アラインを指定しておくと、本来は HIGH バイトに対する出力データが HOLDAM レジスタに現れ、MID バイトに対する出力データが HOLDA レジスタに現れます。
データ・アライン機能は外部インターフェースであるステージング/ホールディング・レジスタと、データパス内部との間の機能なので、CPU / DMA から見たレジスタに対するコヒーレンシ指定に変更はありません。
上の図の例では、コヒーレンシは MID レジスタに対して設定する必要があります。
コヒーレンシおよびデータ・アラインの設定は、STAGEA / STAGEB / HOLDA / HOLDB の 4 つの入出力レジスタそれぞれ独立に設定可能です。
Filter コンポーネントAPI 関数では、CPU からソフト的に DFB の入出力にアクセスする関数として、

 uint8 Filter_Read8(uint8 channel)
 uint16 Filter_Read16(uint8 channel)
 uint32 Filter_Read24(uint8 channel)
 void Filter_Write8(uint8 channel, uint8 sample)
 void Filter_Write16(uint8 channel, uint16 sample)
 void Filter_Write24(uint8 channel, uint32 sample)

が用意されています。
これらはデフォルトの設定 (コヒーレンシ HIGH、データ・アラインなし) で正しく動作するようになっています。 デフォルト設定から変更すると誤動作の可能性があります。
コヒーレンシの設定に関しては次の API 関数があります。

 void Filter_SetCoherency(uint8 channel, unit8 byte_select)

この関数では入出力ともに同じコヒーレンシに設定されます。
入出力どちらかを DMA で行なうために入出力のコヒーレンシを異なる値に設定する必要がある場合には、DFB のレジスタに直接設定します。
データ・アラインについては、設定のための API 関数は用意されていないので、使う必要がある場合はレジスタに直接設定します。
(2015/11/05 追記)
以上の説明は PSoC Creator 3.0 に含まれる Filter v3.10 コンポーネントの場合です。
PSoC Creator 3.1 以降に含まれる Filter v3.20 コンポーネントの場合には下のふたつの API 関数が追加されていて、入出力のコヒーレンシを独立に変えることや、データ・アラインの設定ができます。

void Filter_SetCoherencyEx(uint8 regSelect, uint8 key)
void Filter_SetDalign(uint8 regSelect, uint8 state)