MSX用SPDIFカートリッジの作成

MSXに光デジタル出力を付けてみましょう。

概要
 ・SCC(メガロム)とPSGに対応
 ・48KHz出力固定
 ・1bitサンプルやPCMは未対応(対応は簡単?)
 ・MSX-MUSIC未対応(難しいかな・・・?)

完成概要
FPGA基板、バッファ(LS14)、電圧レベル整合用抵抗、12.288MHzオシレーター、光デジタル出力が乗っている。
一番重要なのは、DesignWave 2005年1月号に付いてきたFPGA基板。
参考URL(CQ出版社):http://www.cqpub.co.jp/DWM/contents/dwm0086i.htm

FPGA基板の主要搭載デバイスは下記の通り
 ・Xilinx製FPGA XC3S50(SPARTAN3)
 ・Xilinx製コンフィグROM XCF01SVO20C
 ・TI製レギュレーター TPS73601DBV(3.3V→1.2V)/TPS68325DBV(3.3V→2.5V)
 ・部屋に転がってたLM317(5V→3.3V)
まぁこの辺の構成はCQのページをみてちょーだい。

で、このボードがどうMSXのカードエッジコネクタにささるかというと、
写真上部のコネクタからフラットケーブルで引っ張って、MSXのコネクタ側に持って行きます。
50pinケーブルで、ピンアサインは完全に1:1結線です。

基板の左半分は将来の拡張用。
深く詮索してはいけません。

ソース他
制約ファイルとソースです。
ソース一式

回路構成
まず、MSXは5Vで、Spartan3は3.3V I/Oなので、その辺の整合が必要。
これについては、
xilinxアンサー #19146 Spartan-3 - Spartan-3--3E の I-O は 5V で使用できるか
を参照してください。

つまり、信号ラインに300Ωの抵抗を直接に入れるだけでOKです。
MSX側から見て入力の方は、TTLレベルを満足できてないはずですが、CMOSなので動くでしょう。というか動いてます。

あとは、クロックライン(3.58MHz)についてですが、CQの基板ではクロックラインが併走しているので、
とくにこのように抵抗を入れてしまったりすると、上手く動かない場合があります。
一旦バッファで受け直した方が良いでしょう。

で、実際の所どのような回路になったのかというと、

という、実に単純な回路です。
実際に、FPGAどの端子と、MSXが繋がるかは、制約ファイルを参照してちょうだい。
※ちなみに、「WAIT」はVHDLの予約語なので、端子名には使えません。
今回のソースでは、「WAITo」という端子になっています。

コンパイル注意点
1)
3.58MHzクロック制約の与えかたには注意して下さい。制約ファイルでは、duty50%になっていますが、
3.3V I/Oに5Vで信号を入力している都合上、実際にはかなりdutyは狂います。
まぁ、今のデバイスにしてみれば遅い信号なので、それで特にタイミングが満たせないことは無いと思いますが、
気になる人は気にしましょう。
どのくらい狂うかを先にオシロスコープなどで見ておけば、タイミングレポートを見て問題ないかどうかの判断もできますけどね。

2)
サンプリングレートコンバーター部で3.58MHz系から12.288MHz非同期受け渡し箇所があります。
メタステーブルが必ず発生するところですので、本来は別途タイミング検証が必要な箇所です。
まぁ、これも、単純にシフトで2段受けしてるので、今回は問題ないはずですけども。

3)
何故かISE 8.1でコンパイルすると、正常に動きませんでした・・・・・。何故?

動作
このソースでは、シミュレーションなどを行うと、実機とかなり異なった動作をしています。
聴感上自然になるように修正をしたのですが、主に違う部分を挙げてみます。
1)PSG部
・出力レベルオフセット値
実機ではPSGの出力レベルは、約1Vp-pで、分解能は10bitと思われます。
そのあたりの詳細については、PSG実験室を参照してください。
で、実機では、無音状態と、発音状態のマイナス側が同じレベルになっています。
今回のソースでは、無音状態に対して、発音時では±にレベルを振っています。

波形で書くとこのような違いがあります。

(無音部、とは、イネーブルがOFF、もしくは、ボリューム0の状態です)

ただし、実機でもこの後にDC成分のカットが行われるので、聴感上は、FPGAの波形に近い形になっています。

・エンベローブボリュームの有効範囲
実機では、発音状態がHiレベルの場合(上の実機波形でいう、Hiレベルの箇所)のみ、エンベローブが有効になり、
Lowレベルの箇所にはエンベローブは無効になります。
今回のソースでは、Low側にもエンベローブがかかります。
波形で書くとこのような違いがあります。

エンベローブパターンに対しての、実際の発音パターン(赤線)です。
ただし、これもエンベローブ周期が発音周波数に近い場合のみで差異がでる現象なので、
エンベローブ周期が発音周波数に比べて非常に長く、DCカットをした場合は、大差ありません。
つまり、通常用途では、聴感上、やはりFPGAと近い形になります。
PSG部に関しては、上記2点が出力波形で実機動作と大きく異なる箇所です。
なぜこのような仕様変更をしたのかというと、PCMで出力する場合に、無音状態を0として、
それに対して±の振幅を振りたかったためです。

2)SCC部
・出力計算式
実機では、波形データはすぐにunsignedに変換されてから計算をしていますが、今回のソースではすべてsignedのまま計算しています。
実機計算式については、SCCの仕様を参照してください。
たとえば、実機では無音時の計算結果は640ですが、今回のソースの出力値は0になります。
今回のソースの計算式では、
各チャンネルの出力値=波形データ値×音量
という、単純な計算式で計算され、SCC合計の出力値もこれらの値の合計になります。
つまり、無音状態を0として、忠実に波形データの値が出力されることになります。
また、ビット切り捨ては発生しません。

・チャンネルイネーブル
実機ではチャンネルイネーブルレジスタの値は即時反映ですが、今回のソースは、
チャンネルイネーブルレジスタに「0」を書き込んだ場合のみ、約37ミリ秒(131072クロック)のディレイ後に反映されるようにしています。
また、「0」以外を書き込んだ場合は、即時反映されます。
これは、コナミ純正の音源ドライバソフトの都合で、波形データを書き込む時に、一旦チャンネルイネーブルに0を書き込んだ後、
32バイトの波形を書き込み、チャンネルイネーブルを元に戻すという処理を行っています。
これは、実機のSCCでは、発音時の波形メモリアクセスと、CPUからの波形メモリアクセスが競合すると、
変な値を出力するという仕様の対策かと思われます。
そのため、実機では波形書き換え時にはプチノイズが発生する場合があります。
今回のソースでは、同時アクセスは問題ないので、そのプチノイズ発生を抑えるための対処対処です
ちなみに、実機で発生するプチノイズは、MSX本体が発生するノイズにかき消されて、あんまり解りません・・・・・
が、実機でもSCCのラダー抵抗直後から引き出すと、はっきりと聞こえます。

3)IOアクセス部
内蔵PSGとIO読み出しの競合が起こるため、リード時のアドレスをA2→22に変更しています。
FPGAボードからIOを読み出したい場合は、A0にアドレスをセットし、22から読み出しを行います。
また、汎用IOは実装されていません。

4)おまけ機能
I/Oアドレス20hに、スロット番号を書き込むと、SCCへの書き込み側がそのスロット経由でのみ行われるようになります。
読み出し側はFPGAが実装されているスロットから常に行うので、バスファイトが起きたりすることはありません。
つまり、本物のSCCが実装されているスロット番号を書き込むと、そのSCCへのアクセス信号をのぞき見して、FPGAの方も発音します。
スロット番号の指定形式は、MSX共通の方法です。
この機能を無効にして、FPGAが実装されているスロットからSCCへアクセスする場合は、bit6を1にします(デフォルト)。


ソース解説(一部)
・top.vhd
PSGとSCCの波形の合成をしています。
実機実測値(A1GT)で、PSG ボリューム15の振幅が約335mV、また、SCCは波形が+127と-128で、ボリューム15の振幅は約488mVとなっています。
同条件のデータで、今回のソースでは、PSGモジュールから出力されるPCMデータは±1023(=振幅2046)で、
SCCモジュールから出力されるデータは+1905 -1920(=振幅3825)となります。
つまり、この条件(1chでの最大振幅)では、実機ではSCCはPSGの488/335=約1.46倍の振幅を持っていることになります。
SPDIF出力でこの関係に合わせるために、SCCは2倍、PSGは2.5倍の計算をして合成しています。
すると、SCCは振幅7650、PSGは振幅5115となり、SCCとPSGの比は約1.49倍となり、実機と近い値となります。
この辺りがハードウェア化する上で適当に妥協点でしょう。

・spdif/spdifo.vhd
32ビットパラレルPCMデータ入力→SPDIF出力を行うモジュールの最上位階層です。
12.288MHzクロックで動作しています。PCMデータは12.288MHzクロックに同期して入力する必要があります。

・psg/psg.vhd
PSGモジュール最上位階層です。
実機PSGと異なり、IOアドレスデコードもこの階層以下で行っています。

・scc/scc.vhd
SCCモジュール最上位階層です。

・scc/ram.vhd
8bit×128wordの2read+1write RAMです。
Xilinxでは、ISEを使うと、blockRAMとして認識してくれます。
他デバイスへ使用する場合は、変更が必要かもしれません。

・fconv/fconv.vhd
PCMデータを、3.58MHz系→12.288MHz系へ載せ替えを行っているモジュールです。
このモジュールは非同期動作レジスタが存在します。
結構難しい動作をしてるので、タイミングチャートを作りました。
タイミングチャート(別ウィンドウで開くのをお勧めします)
まず、SCC/PSGのデータは3.58MHzクロック(CLK)に同期して動きます。PSGは16clk毎のデータが更新されますが、SCCは1clk毎に更新される可能性があります。
このデータは、DINとして、fconvモジュールに入力されます。これを、12.288MHzクロック(CLK12)を256分周した、48KHzのデータとして載せ替えます。
まず、CLKを単純にCLK12で取り込みます。その動作をするのが、meta(0)レジスタです。当然、非同期なので、メタステーブルが発生します。
タイミングチャートでは、上段にメタステーブル後に0に落ち着いた場合、下段に、1に落ち着いた場合のタイミングを書いています。
このmeta(0)をmeta(1)で取り込み、メタステーブルの吸収を行っています。次に、立ち上がり検出用に、meta(1)をmeta(2)で取り込みます。
meta(2:0)が"01"の時、CLKが立ち上がったの条件なので、meta(2:1)="01"をトリガとして、DINをdata12に取り込みます。
すると、DINが確定後、最悪でも12.288MHz×3後に12.288MHzでDINを取り込むことになります。
12.288MHz×3=245ns,3.58MHz=279nsなので、DINが確定しているときに12.288MHzで取り込むことができます。
このようにして、3.58MHzから、12.288MHzへのデータの載せ替えを行っています。
ここのモジュールについては、通常のSTAの他に、メタステーブル検証を行う必要があります。検証ポイントは次の通りです。
 1) meta(0)からmeta(1)への受け渡しが確実にメタステーブルが落ち着いてから行われるか
 2) DINを生成するレジスタ(top/pcmdata)から、DINを受けるレジスタ(fconv/data12)へのパスが、12.288MHz×2以下か
   →遅すぎると、DINを12.288MHzで取り込む時に、DINの変化タイミングと取り込みタイミングが重なってしまいます。
ですがで、今回、この検証はしてません。なぜなら、どちらも、単純なシフトレジスタ構成だからだから12.288MHz周期なら問題が起きるとは思えないからです。
あとは、12.288MHzから48KHzへの載せ替えについては、単純に256分周をすればいいのですが、256回に1回のサンプルを行うと、非常にノイジーな音になってしまいます
ですので、256回の平均値を出力するようにしています。この辺については、普通のサンプリングレートコンバートの考えと同じです。

・slot/slotsnp.vhd
他スロットへのアクセスを監視して、内部SLTSL信号を作成するモジュールです。
実際には、I/OのA8へのアクセスを監視して、基本スロットセレクト信号を作成し、その信号を元に、拡張スロット選択レジスタも実装しています。
CPUバス信号が全てスロットに出てきているので、実現出来る機能です。


おわりに
SCCのテストレジスタと、スナッチャーのSCCに関しては、全く未実装です。
特に、スナッチャーに関しては、メガロムモードでwaveメモリ4番目に値を書き込んだときに、一緒に5番目にも書き込まれるように、設計の変更が必要です。
FPGAのリソースが余っているなら、8bit32wordのRAMを5個載せたり、3portRAMを使えば何も考えずに簡単に実装出来るのですけど、
このFPGAに入れようとするなら、ややめんどうです。やってできないことは無いですが。

FM音源は実装したいですね。これは難しいです。まず、FM音源の動作をしっかり解析しないと。でも、これはもう既に他の人がやっているのかな?
あと重要なのが、FPGAのリソース。XC3S50だと無理だと思います。ボード2個使用ならできるかな?


感想、要望、バグ報告、その他何かありましたら、メールもしくは掲示板にてご連絡ください。
裕之  
ホームに戻る