TK80/BSの
シンセシステムを復活/拡張したい〜
その28
デジタルラダーフィルタと
EGの実験
2018年
9月16
HLSのC++の機能で、ap_fixed.hをインクルードすると、任意精度の固
定少数点が使えるようになります。<全体ビット数、整数部ビット数>で定義します。素のCで固定少数点を使う場合には自分で少数点の位置を管理し、乗算、除算はシフト処理が必要です。それに比べると非常に楽になります。特に変数のビット長が40や36など半端な長さが使えるようになり、リソースの節約になります。下の上ウインド
は素のCから移行したソースで、ダミーデータの入力データはlong
longの64ビット長(整数部48、少数部16)になっています。下ウインドは任意精度固定少数点の40ビット(整数部24、少数部16)を使っています。任意精度固定少数点は新しい型が増えたのと同じで、直接1や−1などを直接代入したり、四則演算もそのままできるようになります。注意点として、定数と演算する時は次の例のようにキャストしないとエラーが出ます。例:Y=(fix24_16)0.3*X;

以前はテストベンチ側で1次ローパスフィルタの3つの係数a0、b0、b1を計算してラダーフィルタの引数に渡していました。DSP48Eの使用数を見積るために、引数をカットオフ周波数とフィードバック係数に変更して、ラダーフィルタ関数内で係数の計算を行います。とりあえず、固定少数点を24/16の計40ビットにした場合です。以前と同様にDSP48Eは16個使われています。

1次ローパスフィルタのiir_lpf1〜4は同じ計算をしていますが、計算結果を後から使うため、4つ別に定義していました。以前と同様に、これを1つにして、固定少数点を20/16の計36ビットにして確認をします。DSP48Eが8個に減りました。

上記の固定少数点を20/12の計32ビットに縮小した場合です。32ビットは切りが良いです。DSP48Eが6個に減りました。

固定少数点20/12の計32ビットのCシミュレーション結果のグラフです。少数点が12ビットでも問題無さそうです。各ビットの小数点値を確認しておきます。
01 0.50000000
02 0.25000000
03 0.12500000
04 0.06250000
05 0.03125000
06 0.01562500
07 0.00781250
08 0.00390625
09 0.00195312
10 0.00097656
11 0.00048828
12 0.00024414

ソースの抜粋です。本体関数とiir_lpfの最適化指示です。乗算、除算を同時に1つのみ、インターフェースをap_ctrl_chainに設定しました。

引数をカットオフ周波数とフィードバック係数にしたので、本体内で各係数a0、b0、b1を都度計算します。設定が変わってもダイナミックに計算できます。

また、iir_lpfが1個なので、各計算値を本体内で保持する必要があります。

タイミング解析です。引数をカットオフ周波数とフィードバック係数すると、計算量が増えて100MHzでは動作できなくなりますが、33.3MHzでは動作が可能で、レイテンシは120になりました。この部分はSPI−DACと同じ15.36MHzで動作させる予定なので問題なさそうです。

アナライザのウインドでレイテンシを確認します。レイテンシの大部分が除算に使われていることが分かります。その後、IP化しておきます。

以前に作成した、DDSの方形波をSPI−DACに出力するロジックにIP化したラダーフィルタを1個だけ組み込んでみます。DDSから入力して、SPI−DACに出力します。カットオフ周波数は10KHz、フィードバック係数は2.0です。

合成後、インプリします。なぜかラダーフィルタ部のDSP48Eの使用量が8個に増えています。DDS部でも1chあたり2個使っているので、現状で24個になっています。

回路図も確認しておきます。DDS−DCO−>ダラーフィルタ(iir_lpf)>SPI−DAC用シフトレジスタに接続されていのが分かります。

9月17日
EG
(エンベロープジェネレータ)を作成します。まず、gccで基本動作の確認をします。EGの波形はコンデンサの充放電波形のexp(−t)系の過度現象を使
いますが、これをFPGAにそのまま実装するのはリソースを使いそうです。exp()の代わりに、積算型のべき乗の関数を作りました。EGは連続して計算するの
で、結果を残しておい
て、関数が呼ばれたら1回計算して戻ります。関数の戻り値はプラス乗なので、マイナス乗にするには1を割り算します。

上の積算型のべき乗の関数を使って、コンデンサの充放電波形を作ってみます。充放電は無限に続くので、充電時は0.98を上限に、放電時は0.02を下限にして終了するようにしました。

CSVを作って、オープンオフィスに読み込み、グラフ化しました。充放電波形の生成分の動作は問題ないようです。

続いて、普通のEGです。アタック、ディケイ、サスティーン、リリースでステートを作成し、サスティーン以外は上記の積算型のべき乗関数を呼びます。C言語では外からのゲートのON/OFF制御ができないので、プログラム内で適当に待ちを作って、それらしく動作させます。下はアタックとディケイのステート分です。ロジックにする時は例えば、アタック中にゲートがOFFになった時の処理も必要です。

べき乗関数のタイミング設定(テストのためアタック、ディケイ、リリースに共通)を1.2に、サスティーンレベルを0.5に設定して、CSVを生成します。

オー
プンオフィスでグラフ化しました。正常動作しているようです。動作はゲートONで0.98になるまで充電し、その後、サスティーンレベルに向けて放電しま
す。サスティーンレベル以下になったら、ゲートがOFFになるまで、そのまま保持します。ゲートがOFFになったら0に向けて放電し、0.02以下になったら終了します。

別の設定です、左はサスティーンレベルを1に、タイミングを早くしたもの。右はサスティーンレベルを0.3に、タイミングを遅くした時です。


上記と同じ浮動小数点処理のまま、HLSに移行して、Cシミュレーションします。結果は良好です。


続いて、合成します。DSP48は5個使われています。後で固定少数点版を作成して比べる予定です。

ラダーフィルタの動作確認をしました。24KHzのフルスイングの波形が出ています。本来は660Hzの方形波にフィードバックが掛かった波形のはずですが、動作不良です。24KHzはサンプリング周波数の半分です。FPGA内のロジアナ機能のILA使って動作の確認をする必要があります。

9月22日
上記のラダーフィルタの不具合を確認します。HLSに戻って確認します。具合の出ている、C++のap_fixed.hを使って固定少数点を20/12の計32ビットにしたバージョンです。Cシミュレーションでは正常ですが、C/RTL協調シミュレーションでは出力値が異常です。


波形でも24KHzで0xFFFF/0x0000のフルスイングが出ています。アナログディスカバリの波形が台形になっていたのはDACのスルーレートの影響で、データが24KHzのフルスイングそのものでした。Cシミュレーション後にC/RTL協調シミュレーションの結果を確認しないで、IP化して組み込んだのが原因でした。後で不具合の原因を確認します。

下はC/RTL協調シミュレーションも正常に動作している、自作の48ビット/16ビット合計64ビットの固定小数点用の乗算/除算関数を呼び、引数をカットオフ周波数とフィードバック係数に変更してたバージョンです。


波形の戻り値も正常に出ています。とりあえず、このバージョンをIP化して、再組み込みしてみます。

DDS−DOCとSPI−DACの間に上記のラダーフィルタを入れました。アナログディスカバリでSPI−DACの出力を確認します。入力は660Hzの方形波、カットオフ10KHz、フィードバック係数2.0の波形です。オープンオフィスで出したシミュレーション結果の波形と同じになりました。基本的な部分は正常動作しているようです。フィードバックを大きくして、共振するとSPI−DACの入力範囲を超えて波形が反転しますが、これは後で対策します。また、小さい周期的なノイズ混じっているようです。これも後で調べる必要があります。

9月23日
XADC2〜4を外部のボリュームに接続してAD変換し、カットオフ、外部CV入力、フィードバック係数(レゾナンス)を制御します。XADC1はDDS−DCOの外部周波数変調入力ですが、現状は未接続です。

アナログディスカバリのウインドを動画キャプチャしました。入力は660Hzの方形波で、中央のボリュームは外部CVで外部から三角波のLFOを入れました。カットオフ、LFOレベル、レゾナンスの順で変化さ
せています。音はスピーカから出して、ノートPCの内蔵マイク経由で入力しました。約6MバイトのMP4ファイルです。

9月24日
続いて、MC80+パスに接続して、音出ししてみます。久しぶりにTK80から出力してみました。左チャンネルがDDS−DCO(方形波)とダラーフィルタで、右チャンネルが旧DCO(方形波)とダイオードラダーフィルタです。左チャンネルはボリュームを色々と調整して変化を出しています。後段は共に既存シンセのVCAを経由してミキサに接続し、その後PC経由でMP3に録音しました。

SPI−DACの入力範囲を超えた時の波形です。符号が反転しているので、これは制限しないといけないです。

9月25日
アパートのPCでC++のap_fixed.hを使って固定少数点を20/12の計32ビットにしたバージョンの確認をします。DRCエラーが2つ出ていました。エラーコードはWEBで調べると、スケジューリングに間に合っていないというエラーのようです。tmp_28とtmp_32はいずれもsdiv(符号付の除算器)に関係して、最適化支指示でsdivを同時に1つという指定が守れていないということのようです。

上記でも示したレイテンシの非常に長いのがsdivのtmp_28とtmp_32です。

ソースコードのsdivの最適化指示ALLOCATIONを削除してみます。

C/RTL協調シミュレーションを行うと戻り値が正常になりました。リソースも確認しておきます。DSP48Eの使用量は6個です。

波形で先頭の2つの戻り値を確認します。32ビット(20/12)で0.0632、0.2192になっています。正常動作しています。後でもう一度IP化して、組み込んで動作確認します。

9月27日
C++のap_fixed.hを使った固定少数点20/12の計32ビット版をIP化して組み込みました。正常動作しました。


リソースを確認します。DSP48Eはラダーフィルタ部でやはり8個使われてます。この調子だと8ch分のロジックがArtix−7の35Tには入らなそうです。Digikeyやマウザーで100Tの載ったボードが入手できます。価格は35Tのプラス1万円くらいです。


