20年前にFPGAで8オペFM音源を作りました。
http://kwhr0.g2.xrea.com/hard/fm.html
そのソースだけはあったのですが、テストベンチなどは見つかりませんでした。
GOWINでも使えるかな?
と思い、Xilinxのプリミティブを使っていたのでまずはそれに相当するVerilogを書いて
合成は通るようになったものの必要なはずの乗算器が最適化で消されたりしてる...
音を出してみましたが変調のかかり具合がおかしく、ブツブツとノイズも乗っている...
テスト環境が無いしこれは書き直した方がいいな
ということで、改良版を書いてみました。
旧作は1クロック/オペレータで処理するパイプライン構造でした。
しかし、リソースの不足からオペレータの変調元が複数の場合、一つずつ加算する間ストールさせていました。
今回はTang Consoleがターゲットなので、リソースを気にせず、
8つのオペレータの前回の値を同時に読み出し、変調元以外はマスクして一気に加算するようにしました。
また、旧作の同時発音数は32だったのですが、64にしてみました。
パイプラインはBSRAMが入る場所から4段くらいで実装できるのですが、
次サンプルの変調のための書き戻しがちょうど1ボイス分遅れると設計しやすいという理由でオペレータ数に合わせて8段にします。
単純に流すだけなので、クリティカルパスになるところにパイプラインレジスタを入れていきます。
BSRAMについて:
毎サンプル更新があり、外部からも書き込めるとなると、書き込みが2ポート、読み出しが1ポート必要です(アドレスは全て異なる)。
そのままでは実装できません。
外部から書き込むときだけストールさせてアドレス、データ入力を切り替える方法もあります。
制御がちょっと手間というのもありますが、1サンプル分計算するのとパラメータを更新する処理をはっきり分けた方がテストもしやすいと考えました。
そこで、最初は計算を止めておいて、すべてのパラメータを外部から書き込める状態にします。
そして実行開始を指示したら計算を始め、書き込みはパイプライン出力のみとします。
これならSDPRAMで済みます。
停止を指示したら、1サンプル分計算し終わってFIFOに出力し、最初のボイスを書き戻す直前で止めます。
次に実行開始したら最初のボイスから計算を始めます。
その際、パイプラインには停止直前のデータが残っていますが、パラメータが更新されたかもしれないので8クロック分捨てます。
実装を終えたソースを見ていて、ふと気がつきます。
1ボイスの処理に8クロックかかるのであれば、パイプライン外で変調元を1つずつ読んで個別のレジスタに入れておき、
ボイスの区切りで一気にパイプライン側に転送すれば、変調用に8つもBSRAMを使わなくて済むことに。
ということで、変調用BSRAMは1つで、ストールも無しで実装でき、旧作より改良ができました。
ところで、32ボイスに設定してみても旧作よりBSRAMの数は増えています。
旧作では、リソース不足のため目的が異なる2つのデータを1つのBRAMに同居させることが必要で、
そのためにプリミティブを使っていました。
これをあちこちで行っており、20年後に読むと訳のわからないソースになってしまいました。
今回はリソースは余っているのでPLL以外はプリミティブ(IP)を使わないようにしました。
プロセッサは以下の波形メモリ音源
http://kwhr0.g2.xrea.com/hard/wtsg/index.html
を作ったときと同様、f6800wideを使います。
最初、8音くらいでFIFOがアンダーフローするくらい遅かったんです。
音階によって各種パラメータを内挿する機能を実装しており、大量の乗算が必要なためです。
そこで、I/Oとして内挿回路を追加しました。
乗算はそこに任せると、嘘みたいに速くなりました。64ボイス目一杯使うMIDIがなかったのですが、50くらいまでは大丈夫でした。
クロックについて:
波形メモリ音源では1音あたり8クロック、FM音源は1クロック/オペレータなので同じく8クロックになります。
ということは波形メモリ音源と同様、fs=96kHz目標だと50MHz弱のクロックが必要です。
しかし、MPUからパラメータを書いている間は止めておくので、もっと高いクロックが必要になります。
そこで、FM音源は120MHzとしました。i2s.v内の式で逆算すると、fs=94kHz弱になります。
無駄に速いですが、FIFOが小容量で済んでいます。
MPUは40MHzとしました。
おまけ機能:
localparamのSINとMODULATEを0にすると、元の波形が矩形波になり、変調もかからないのでPSG音源になります。
その場合でも、1つのボイス内に複数のキャリアがあれば同時に鳴るので、音に厚みを出せます。
操作法については波形メモリ音源のときと同じです。
https://github.com/kwhr0/FM-tone-generator-FPGA