LCD表示もできたし,VGA出力もできた.とりあえず出力はオッケーだ. 次は入力だ.手始めに,PS/2キーボードを繋げてみよう.
PS/2のプロトコルについても, こちらで説明した ユーザーガイド(ug330.pdf)に詳細がある. Spartan-3Eスタータキットのユーザーガイドの日本語版(j_ug230.pdf)も参考になる. またネットで検索しても,いくつか情報が見つかる.
ただしSpartan3Aスタータキットでは, PS/2の分岐ケーブルを使って,2入力(マウスとキーボード) にすることができるようになっているようだ(Spartan3Eボードではできない). これは,Spartan3EスタータキットではPS/2コネクタ(6pin)の1番,5番ピンのみ FPGAに接続されているのに対して, Spartan3Aスタータキットでは2番,6番ピンも接続されているため. なのでSpartan3AスタータキットのUCFファイルでは, PS2_CLK1,PS2_DATA1,PS2_CLK2,PS2_DATA2 の4つの信号が定義されている. これらをキーボードとマウスで使用することができる.
あと,PS/2のプロトコルは双方向らしい.
双方向ってどういうことだ?HDLで書くときは,どーいうタイミングで 値を読み取ればいいんだ!? 出力しながら値を読むこともできるのか??? こーいうことってハードやってる人なら誰でも知ってるあたりまえのこと なのかもしれんが, もともとソフトウエア出身で,回路初心者のぼくにはよくわからん.うーん困った.
PS/2のプロトコルは,ネット上で探すとそれなりに見つかる. 要約すると,こんな感じ.
うーん,プロトコルの内容はわかるんだけど,結局のところ, VerilogHDLではどう書けばいいのだろうか.
「クロックは通常は常に1になっているが,キーボード側からデータを送信したい 場合には0にする」というのは,まあよい.しかし,ということはPC側は 「クロックを通常は1に保ち,キーボード側が0にしたらデータを読み取る」 という動作が必要になる気がする. つまり,1を出力しつつも,0になっていないかどうかを見る必要がある, のだと思う. たとえばVerilogHDLで,あるピンをinoutに定義して
inout CLK; assign CLK = 1'b1; // 通常はhighを出力 always @(CLK) begin if (CLK == 1'b0) ...のように書けば,普段は1なのだが,相手が0を出力したときには反応することが できるのだろうか? うーんよくわからん.そもそも線は1本で, 自身は1を出力しているのに,相手が1とか0を出力したことを こちらで読めるもんなのだろうか? (自身が1を出力しているのだから,読み取る値は常に1になるんではないの?)
それとも
inout CLK; assign CLK = 1'bZ; // ハイインピーダンス always @(CLK) begin if (CLK == 1'b0) ...としてハイインピーダンス状態にしておけばいいのだろうか? でもこれだと,PC側もキーボード側も何も出力していないときに, 「何もないときはクロックは1になっている」ということを実現できない. うーん困った.
で,いろいろ調べたのだけど,結論から言うと, PS/2キーボードは「オープンコレクタ」という接続になっていて, 通常は信号線がhighなのだけど,情報を出力したい場合には lowを出力することで,誰かが0を出力したことを感知できるようだ. 詳しくは「プルアップ」「オープンコレクタ」「ワイアードオア」 「トライステート(3ステート)」「ハイインピーダンス」 などのキーワードで検索とかして調べてほしい. Wikipediaの 「デジタル回路」の説明が詳しくて良いと思う.
これにより双方向通信(もしくはバス接続)で,デフォルトでは1だが, 情報送信時には0にする(そして誰かが0を出力したことを,他の相手は感知できる) ということができるらしい. この場合,FPGA側では通常はクロックをハイインピーダンスにしておけばよい. クロックラインがオープンコレクタでプルアップされているならば, PC側もキーボード側も無出力(ハイインピーダンス)のときには, クロックライン自体はhighの状態になる. そしてPCとキーボードは,クロックラインの状態を見ることができる.
問題は,クロックとデータをオープンコレクタにするためには, これらの線をプルアップしなければならないということ. たとえば Interface誌 2006/01月号にはPS/2キーボードの制御についての記事があるが, ここで扱っている回路は回路側でプルアップが行われているので, 回路側でオープンコレクタになっている. しかしSpartan3Aスタータキットのユーザーガイド(ug330.pdf)の回路図では, スタータキットのPS/2コネクタはFPGAに直結されているため, プルアップはされていない.どーすりゃいいのやら. キーボード側でプルアップされていたりするということはないのかしらん.
UCFファイルでPULLUPというキーワードがある. もしかしたら,プルアップされるように設定することができるのかもしれない (それとも「この線は回路側でプルアップされている」という意味かしら. 詳細未調査.間違っていたらごめんなさい). このへんについてはXilinxのホームページ の,マニュアルのダウンロードページで, ソフトウエアマニュアル→ ISE9.1iソフトウエアマニュアルの 「制約ガイド(日本語版)」(j_cgd.pdf)というのに詳細がある.
まあでもスタータキット標準のUCF(s3astarter.ucf)を見ても, PULLUPはとくに指定されていないようだし, UCFでPULLUP指定しちゃっていいものかどうかよくわからん. 試してみるのが一番手っ取り早いんだけど, FPGA関連の資料って, 「へんなことをすると,チップの破損に繋がる」 みたいなことがいろんなところに書いてあって, 気軽に試すのはちょっとこわい部分がある. とくに今回の「オープンコレクタ」は,失敗すると自分と相手で電圧がぶつかったり, 電流が流れ過ぎたり(そしてこわれたり)するのかなー, などと初心者的に不安になってしまい,十分に調べてから試したい,というのもある.
で,Spartan3Aスタータキットのユーザーガイドをよく読むと, 「キーボードはオープンコレクタ・ドライバを利用するが,キーボードにデータを 送信しないならば,何も考えずにinputで入力してしまってかまわない」 と書いてある (ここでいう「ドライバ」は,「電圧をドライブする回路」という意味ね. いわゆる「デバイスドライバ」ではない.つまりここで言っているのは, 「オープンコレクタにしなけりゃならない」ということ). なので,あまり難しいことは考えずに,ユーザーガイドのとおりに inputで入力することにする. うーん,キーボード側でプルアップされているということなのかなあ... うーん不明.
で,できたのがこんなかんじ.
キーボード上で押されたキーのスキャンコードをLEDで表示する. スキャンコードは,キーボードのキーごとに割り当てられているコードのことで, キーボードはキーが押されると,スキャンコードを送信することで どのキーが押されたのかを通知する.まあこのへんはユーザーガイド参照. ちなみにスペースバーのスキャンコードは0x29,Enterキーは0x5aになっている.
ちなみにキーボードからのデータは, スタートビットが1bit, データが8bit, 奇数パリティが1bit, ストップビットが1ビットの合計11ビットが送られてくる. 奇数パリティって,データ中の1の合計が奇数になるように,パリティを追加する ものなのね.データ中の1の合計が奇数のときにパリティが立つものだと勘違いしてた (よって最初に書いたソースでは,パリティの値が逆になって誤動作していた). パリティの処理を修正して実行したら,あっさり動いた.
スペースバーを押下
LED表示の拡大.スペースバーのスキャンコード(0x29)になっている
Enterを押下
LED表示の拡大.Enterのスキャンコード(0x5a)になっている