2023年11月27日月曜日

BlackFinを使ってみるなり(28) FIR filterライブラリ

ADが提供する演算ライブラリがいろいろあります.マジで便利そう.使えればですが.

さしあたってやりたいのが、32ksps→1kspsぐらいに間引きたい.単純に1/32に間引くとエイリアシングが出てしまうので、500HzのLPFを通しておいてから1/32に間引くというのが作法です.

LPFはFIR filterで構成します.FIR filterはhardwareでやるにせよ、softwareでやるにせよ、大きな計算量が必要です.結局のところ、間引き(decimation)というのはFIR filterが演算量の大部分を占めるってことになります.

ADのccesのインストフォルダを探すとfilter.hというのがあってその中にFIRやIIRのプロトが書かれています.

typedef struct{
  fract32 *h; // coefficients
  fract32 *d; // start of delay line
  fract32 *p; // read/write pointer
  int k; // number of coefficients
  int l; // interpolation/decimation index
} _fir_fr32_state;

void fir_fr32 (
   const fract32 _input[],
   fract32 _output[],
   int _length,
   fir_state_fr32 *_filter_state);

あーそんな感じねと察します.でもsample programが無いとちょっとかったるい.delay lineの目的が不明です.検索したけど見当たりませんでした.

自力でやるのかくそう・・・・

ーーーー
それでやってみたのですが、意外と簡単にうごかせたかな.

1.FIRのタップ係数を決める

ライブラリとは別なのでこれを最初に説明するってのもどうかと思いますが、こちらのサイトが便利です.好みのfilter特性を入力すると、タップ係数を出力してくれます.
自力で逆フーリエ変換してもタップ係数を算出できますけど、めんどくさいので自力計算はやめときました.

やたらと厳しいfilter特性を指定するとタップ係数が長くなります.タップ係数が長くなると計算時間がかかってDSPが処理できなくなります.BF706で500タップを実装したらhung-upしちゃいました.200タップなら動きました.

同サイトはタップ係数定義を吐き出すので、source codeにコピペしましょう.
キモは、
・32bit FIRならば、、、
・32bit intで係数出力させる(ライブラリ内ではfract32と型表記)
・係数のgain調整はしなかった

#define FILTER_TAP_NUM 85

static int filter_taps[FILTER_TAP_NUM] = {
  -11666725,
  -4238744,
  -4883710,
  -5507981,
  -6093541,
  -6612955,  以下略

2.FIR filterにタップ係数などを設定する

blackfinのライブラリを使います.定義はfilter.hに書かれてる.

_fir_fr32_state firparam;   ←FIRのパラメータ構造体

構造体にFIRのパラメータを設定する.
fract32 delay[2*FILTER_TAP_NUM];
fir_init( firparam, filter_taps, delay, FILTER_TAP_NUM, 1 );

引数の意味はなんとなくわかるけど、、、
最凶に判らないのがdelayというもの.タップ数の2倍長のwork areaを与えてあげると正しく動く.→こちらが参考
末尾引数1は、間引き数、補間数なのでここでは1でいいかんじ.0じゃない.

3.FIR filterをうごかす

わたしは初めてDSPを使っているのですが、どーゆー風に実装するのかなぁ?とモヤモヤしていた事があります.

というのは、、、verilogでhardware FIRを設計する場合は毎サンプルに1つデータが入って来ます.それをガシャンと演算します.FIR pipelineから1つのデータが出力されます.これは判りやすい.設計経験もあります.

しかしDSPでは、ADCから入ってくるデータはFIFOに格納され、数~数10サンプルのブロックでデータが到来します.これをFIRにガガガッと演算させ、ブロックで出力される.ガガガッの最後には仕掛データが在庫のようにFIR内部に溜まっている.こんなの実装するのどうするのかな、かったるい、そんな風に思っていました.未体験ゾーンだし.

だがしかし、blackfinライブラリを使ったらすんなり動いたので中身がどうなっているのかは判らずじまいでスルーします.

それでFIR処理のdata flowはこんなです.
・hardwareは、BF706のI2Sに32bit ADCと32bit DACがついてる
・起動時にI2Sに「ADC FIFOが満タンになったらcallbackしてね」と設定しておく
・参考までにADC FIFO長は64サンプルにしてる(32bit 2ch 64sample=512BYTE)
・callbackが来たら以下の処理を行う
 1)FIFOからdataを取り出し、fract32の64個配列に格納
 2)同配列をFIRライブラリへ突っ込む
 3)FIR出力が出てくる (fract32 64個配列)
 4)出力配列をDACのFIFOにコピー
 5)DAC FIFOをI2S DMAにおまかせする

fract32 input[64], output[64];
for(int i=0; i<64; i++){
 input[i] = *pBufRx;  ←ADC FIFO  (1)
 pBufRx += 2;
 }
 fir_fr32 ( input, output, 64&firparam );(2)(3)
 for(int i=0; i<64; i++){
  *pBufTx = output[i];  ←DAC FIFO (4)
  pBufTx += 2;
 }

以上でさらっとおしまい.

#ライブラリにはdecimationもあるのでそれも試します.

すいませんが、project folder詰め合わせのupは諸般の事情でやめときます.ボケた写真でも載せておこう.右下がBF706です.

27へ    29へ

かしこ

12 件のコメント:

  1. >こちらのサイトが便利です
    あ、こんなサイトがあったんだ。スゴイです。
    ※某大学の研究室でも、係数計算してくれるサイトが公開されてたのは知ってましたが、
    ・グラフ付き
    は、良いですね。いかにもイマドキって感じ。

    >どーゆー風に実装するのかなぁ?
    ソフトなら、
    ・一定期間のサンプリングデータの配列(=FIFO)を「ごにょごにょ」するだけ
    ですが、「verilogでhardware FIRを設計」の場合は、
    ・係数レジスタ+「シフト可能なレジスタファイル」(1bitじゃないから「シフトレジスタ」じゃないですね)
    を使って、最後の係数を計算して直近のサンプルに掛け算する(同時に、ウラでは、レジスタファイルに、直近のサンプルを取り込む)
    と言う感じでしょうか。
    ※どっちが分かりやすいかは・・・難しいですね。
    verilogだと、いわゆる「ブロック図」をそのまま実現していますが、
    ソフトだと、処理はブロック図とは「一対一では対応しない」(抽象化されてるから)ので、分かりにくいと言えば分かりにくいですが、
    「(数学の)関数表現」(Σ記号でかける奴)に近い感じもします。
    ※まぁ、いずれにしても「ややこしい」(言葉で表現しずらい・・・)

    返信削除
    返信
    1. >「シフト可能なレジスタファイル」
      ↑で、「FIR pipeline」って言ってるやつですね。
      ※よく知らないんですが、verilogって、「パイプラインのマクロ」とかもあるんですね。(まぁ、そうじゃなきゃやってられんだろうけど)なんかスゴイと思いました。

      削除
    2. verilogだとなんでも並列演算って感じです。
      20タップのFIRなら乗算器20ヶ生成。加算器20ヶ生成。必要な演算は全部パラに存在するよな作りにします。

      softだと結局シーケンシャルなのであれこれと制約で「慣れぬ」とか「hardwareは楽ちん」とか思いぱしってしまいます。

      hardwareでもシーケンシャルにするのは可能ですけど、スピード重視で全パラ。

      削除
    3. パイプラインマクロは無いと言えるんじゃないかと思います。
      演算→レジスタ→演算→レジスタ の流れを自分で書くイメージ。

      削除
    4. >パイプラインマクロは無い
      有りそうで無いんですかね・・・
      ※よく使いそうな機能と思いますが「ベンダーマクロ(モジュール)」(勿論、仕事で使う有償の奴)とかにはあるんだろうな・・・
      ※私もそろそろ本気で、HDL始めなくては。手が動くうちに。

      削除
    5. お、HDLにも進出ですか。
      実りある2024になりますぞー

      ぼくちんは引き続きDSPを使ってみるなり

      削除
    6. ご存じかも知れませんが、この人のブログ
      https://msyksphinz.hatenablog.com/
      FPGA開発日記
      とか、面白そうです。
      ※この人も「もはや RISC-V は、必須科目になった」って、言ってますね。
      つか、「自分でCPU作ってる」とか、スゴイです。

      削除
    7. 最新の記事では、もはやセキュリティは、「ソフト」では無くて、
      ・IP(勿論、Intellectual Property のほう)で担保する時代
      とか言ってるし。こういうのを、理解したいですね。

      削除
    8. 知らないブログでした.
      わたしも4bit CPUぐらいなら作れるかな.
      てか、むかしむかし、SN74シリーズに4bit CPUってありましたよね.いやALUだったか...

      削除
    9. >SN74シリーズに4bit CPU
      「SN74181」ですね。
      https://ja.wikipedia.org/wiki/74181
      ※VAX-11/780 のALUがこれ使ってるのは、有名な話
      確か私も、1個持ってたハズ。後で探してみます。

      削除
    10. ちょっと前までは、FPGAにも、こういう機能ブロック(ALU とか レジスタファイルとか・・・)が、ハードマクロとして提供されていた気がするのですが、最近は、集積度が上がってきたので、
      ・必要な分だけ、自分で作り込む(Or ソフトマクロで提供)
      のほうが、普通になってきたのでしょうか?
      >演算→レジスタ→演算→レジスタ の流れを自分で書くイメージ
      って、そういう意味ですよね?
      ※固定小数点なら「必要なだけ」その場で「加算回路がジェネレート出来る」とか、スゴイ時代だな。
      昔は「単純な加算回路」でも、一つのICとして売ってたのに・・・
      「7483」という、4-bit binary full adder があって、これで遊んでたことがあります。

      削除
    11. >VAX-11/780 のALUがこれ

      有名機がこんな素朴なのを使ってたとは.

      わたしが就職した1987年はまだ現場に74LSと74HCがざっくざくと使われていました.

      削除