電子工作/XC9572XLと8U245AMで作るUSBシリアルIO ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ SuzTiki:電子工作 FTDI FT8U245AM でUSBを使ったシリアルI/O の実装を考える。 ・ なぜこれが欲しいのかは、電子工作/XC9572XLで作るROMライタを参照。 目標機能 6Mhz のクロックで JTAG または SPI を制御できるもの。 チップ: XC9572(XL) と FTDI FT8U245AM FTDI 8U245AMは、どんなチップかというと、ホストからは USB RS232C として見えるが 、デバイス側からは、パラレルインターフェイスに見える USB コントローラ。高速な転 送 1MB/sec が可能。 ・ データ受信(HOST->DEV) RXF# --------------- ---------------------- ↓_________↑ RD# -------------------- ---------------- ↓_↑ DATA -------------------< >---- ・ データ送信(DEV->PC) TXF# --------- ___________________↑ WR ---- ___________↑ ↓____________ DATA --------------< >---- こんな感じでデバイスとデータをやりとりする。基本的には姉妹品の FTDI FT8U232AM と同じようなものだが、データの伝送路があるだけで、他の制御信号はない。 プロトコルの制御を考えてやる必要があるが、CPLD で実装した簡単なステートマシンを 付けることにより、高速な転送を生かしたモノが作れそうだ。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 概要 ・ ハードの構成 6M Hz +---------------------+ | ↓ +----------+ +-------------+ CLK | | |-----> | |--------> |---> USB ==== | FTDI |<---- | CPLD | DO | | 8U245AM|<=====> | XC9572(XL) |--------->|---> | | D0-D7 | | DI | デバイス +----------+ | |<-------- |<--- | | 他制御 | | |--------->|---> +-------------+ USB で、SPI/JTAG のような 3線+αのデバイスを制御してやろうというもの。普通の人 には全く役にたたない。(たぶん) SPI のインターフェイスをもったデバイスは、いろい ろあって、 MMC などもそれに含まれる。JTAG は、FPGA/CPLD プログラミングには必要 なインターフェイス。スパルタンII の XC2S100 とかは 1Mbit クラスの初期化データを JTAG を通して送り込まないといけない。 ポイントは、制御をマイクロコントローラを介さずに CPLD で作ったステートマシンを 使うことで、高速な通信ができること。 USB を使う利点としては、高速以外にもある。この装置と PC 間は USB なので信号の劣 化がない。デバイスとをつなぐ線だけが問題なので、その線をみじかくしてやれば、エ ラーが起こる率を減らせるのもポイント。 ステートマシンを作るには、状態を定義して、どのような条件で別の状態に遷移するか を定義していく。ソフトでもステートマシンはよくつかうが、ハードでも似たようなも の。 たぶん、VHDL で記述しても C で記述しても上の説明と対応がつくコードになると思う 。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ プロトコルとステートマシン 受信(HOST->DEV) 総バイト数 (1バイト N+2) コマンド 5 bit 最後のデータの有効 bit 数 ( 3bit) データ N バイト 送信(DEV->HOST) 総バイト数 (1バイト N+2) 最後のデータの有効 bit 数 ( 3bit) コマンド 5bit データ N バイト こんな感じにする。送信:受信は 1:1 に対応するが、必ずしも 1パケット毎に同期を取 る必要はない。 基本的なシーケンスは、 1 バイト受信して転送数カウンタに入れる。 転送数カウンタの内容を1バイト送信する。(転送数カウンタ--) 1 バイト受信してコマンドレジスタ に入れる。 コマンドレジスタの内容を1バイト送信する。(転送数カウンタ--) 転送数カウンタの数だけ 次の動作をくり返す 1 バイト受信して シフトレジスタに入れる。 CLK を出力しながら、8 ビット分シフトする。 ( 最後 (=1) の場合は 有効bit数分) 入力した シフトレジスタの内容を 1バイト送信する。(転送カウンタ--) 最初にもどる ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ステートマシンの設計その1 内部データ 計 32 bit コマンドレジスタ 8 bit 転送数カウンタ 8bit シフトレジスタ 8bit シフト数カウンタ 3bit シリアル出力ラッチ 1bit RXF# ラッチ TXF# ラッチ 状態 : 転送先 DIR 2 bit 受信/送信 転送/他の動作 もうちょっと状態を詰める。 1バイト受信 して DIR に入れる。DIR は3種類 0: 転送数カウンタ 1: コマンドレジスタ 2: シフトレジスタ _______ ______ _______ ______ o CLK |_____| |_____| |_____| |_____| | o RXE# ラッチ ↑ ↑ ↑ ↑ o ラッチしておいたRXE# が 0なら ____ _____ o RD# |_____| o 取りこみ ↑ o 次の状態に行く。 DIR を 1バイト送信。 DIR は3種類 0: 転送数カウンタ 1: コマンドレジスタ 2: シフトレジスタ _______ ______ _______ ______ o CLK |_____| |_____| |_____| |_____| | o TXE# ラッチ ↑ ↑ ↑ ↑ o ラッチしたTXE# が 0なら o 送信元のTriState ゲート ________ ______________ | | +-----------+ o WR +-----+ ________| |________ o 転送カウンタ-- ↑ o 次の状態に行く。 CLK を出力しながら、8 ビット分シフトする。 ロード処理 _______ o CLK |_____| | ↑ o LOAD +------------+ ____| |______ o シフト数カウンタ にロード 転送数カウンタ == 1 かつ コマンドレジスタの 有効bit数 != 0 なら コマンドレジスタの 有効bit数 そうでないなら 8 o 転送数カウンタ == 0 なら 最初に戻る シリアル入出力 _______ o CLK |_____| | oシフト数カウンタ -- ↑ oシフト数カウンタ値 0 チェック (0 なら次) ↑ # カウンター -- は ↑後変化するから、0 チェックは、↑前の値 _____ ______ o CLKOUT |_____| o シリアル出力ラッチ ↑ o シフト(入力) ↑ 状態遷移 # DIR 送信/受信# 転送#/他動作 遷移 0 00 0 0 変わらず/NEXT 1 00 1 0 変わらず/NEXT 2 01 0 0 変わらず/NEXT 3 01 1 0 変わらず/NEXT 4a 10 0 1 シフトレジスタ数 ロード 状態0/NEXT 4b 10 0 0 変わらず/NEXT 5a 10 1 1 シリアル入出力 変わらず/NEXT 5b 10 1 0 変わらず/状態4a 状態遷移を整理するとこんな感じ。NOP 入れて 12 状態にすると、 DIR 送受信 転送/他動作 となって、きれいになる。 で、状態遷移は 同じか +1 するか jump かになる。 これだけきれいだと、ROM 作って 表現しても、ロジックの組み合わせで表現しても 論理の量はあんまり変わらないかも知れない。 _______ ______ _______ ______ o CLK |_____| |_____| |_____| |_____| | o 主な動作 ↑ ↑ ↑ ↑ o 遷移 ↑ ↑ ↑ ↑ o TXF#/TXF# | | | | のラッチ o 送受信の 切り替え | | | | | シリアル入出力 ________ ____ o CLKOUT |_____| o シリアル出力ラッチ ↑ o シフト(入力) ↑ コマンドレジスタフォーマット 7 \チャネル 6 / 5 出力1 4 出力2 3 出力3 2 -+- 1 | 最後のデータの有効 bit 数 (0 のときは 8) 0 -+- チャネルは CLK/DI/DO の切り替え、外部2ch に加えて 内部入出力 or 設定も作る。 のこり はダイレクトに ON/OFF できることにする。 うまくいけば、32bit ぐらい余裕がある。これで、内部に SPI デバイスを 作る。16bit を状態そのものをあらわすデータに使い、それを設定するための シフトレジスタとして 16bit を使う。ちなみに 8 の倍数である必要はない。 内部用としては、入力 CLOCK の 分周切り替えとか。6Mhz では厳しい場合 4bit ぐらい使って 1/16 にするとか。たぶんタイミングとか気にしなくて良いはず。 なぜなら 6Mhz のクロックよりこまかくなることがないから。 送信データなしモードも作りたいなぁ。これは HOST から送るだけにして 速度をかせぎたいとき有用だから。( 全部双方向だと 1/2 のスループット になってしまう。) じゃぁ、受信データなしモードも必要か。どっちにしても、シーケンスは同じで、 実際に WR/RD# を変化させるところに条件を入れれば良いのだろう。 ただ、クロックの制限で 6Mbps 以上は出せないから... こんなの無駄かも? 後重要なのは、CLK の 正論理/負論理 切り替え。 それ以外は HOSTのソフト でどうにでもできるが、これだけはサポートしないと。 困ったときにどうにもならない。 JTAG,SPI は、__↑↓__ で、動くようだ。MMC なんかだと ___ ___ みたいだ。 ↓↑ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ステートマシンの設計その2 状態はかならずクロックと同期して変わるように設計できた ( とおもう。) ___________ __________ | | ↓ | ↓ クロック ----------- ----------- ______ | | | | 状態 A | 次の状態 | 制御はクロックの立ち下がり(↓) で状態を遷移させる。 次の状態 = f(今の状態) で済むなら話は簡単だが、状態遷移には、他の条件がはいってくる。その他の条件がい つ確定するかというと、状態 A になる直前に決まっているように (設計できた) まずあげられるのは、送信できるか/受信できるかという信号線 TXF#/RXF# の状態。 外から来る信号なので、状態の変化は、クロックとは非同期なわけだが、送信したから 、送信できなくなるのであり、受信したから(空になって) 受信できないという状態にな る。依存関係があって、突然送受信できなくなることはない。そういうわけなので、状 態が変わるときの値をおぼえておいて (ラッチして) 今の状態で送信/受信できるかどう かを判断する。 あと、送信データが来るかどうかの判断。シフトレジスタ数と名前付けたレジスタに何 が入っているかで判断できる。何が入っているが確定するのは、値を変更した次の状態 に入ってからで、どの状態に遷移するかの判断には使えない。そういうわけで、シフト レジスタ数ロードという状態を独立に作っている。 さてそうやっていって整理したら 状態遷移 # DIR 送信/受信# 転送#/他動作 遷移 0 00 0 0 変わらず/NEXT 1 00 1 0 変わらず/NEXT 2 01 0 0 変わらず/NEXT 3 01 1 0 変わらず/NEXT 4a 10 0 1 シフトレジスタ数 ロード 状態0/NEXT 4b 10 0 0 変わらず/NEXT 5a 10 1 1 シリアル入出力 変わらず/NEXT 5b 10 1 0 変わらず/状態4a こんな風になった。... よおく見ると2進数の値に近いようになっている。じゃぁ完全 に2進数にしたらどうかというのがひとつの案になる。 状態遷移 # DIR 送信/受信# 転送/他動作# 遷移 0 00 0 0 NEXT(NOP) 1 00 0 1 変わらず/NEXT 2 00 1 0 NEXT(NOP) 3 00 1 1 変わらず/NEXT 4 01 0 0 NEXT(NOP) 5 01 0 1 変わらず/NEXT 6 01 1 0 NEXT(NOP) 7 01 1 1 変わらず/NEXT 8 10 0 0 シフトレジスタ数 ロード 状態0/NEXT 9 10 0 1 変わらず/NEXT 10 10 1 0 シリアル入出力 変わらず/NEXT 11 10 1 1 変わらず/状態8 4つも状態が入ってしまって遅くなったわけだが、状態の値そのものが意味を持つから、 変換のための論理がなくなる。しかもだいたいの状態変化は、0 か +1 である。 こうなると、バイナリカウンタでシーケンスを組める。バイナリカウンタの動作は、な にもしないか、+1 するか、決まった値をロードするか。( 0 にするか )。例外的なのは 、 ・ 状態8 の 0 にするケースと ・ 状態11 の状態 8 に跳ぶケース。 だけだから、それを 0 にする、値をロードするという機能に割り当ててもよい。 また、N ビットの ROM に何をするかを割り当てて状態から引いてきた ROM の値で動作 をきめてもよい。 # DIR 送信 転送 シフト シリアル /受信# レジスタ数 入出力 ジャンプ先(3bit) ロード 0 00 0 1 0 0 000 1 00 1 1 0 0 000 2 01 0 1 0 0 000 3 01 1 1 0 0 000 4 10 0 0 1 0 000 (状態0) 5 10 0 1 0 0 000 6 10 1 0 0 1 000 7 10 1 1 0 0 100 (状態4) こんな感じで、アドレス 3 bit データ 9bit のテーブルとしても表現できる。 (テーブ ルから引くなら状態が整然としていても得しないので、 NOP は削る。) なおここで書いたジャンプ先は、なにもしない, +1 , 値をロードするという3つの撰択 枝のうち値をロードするときの値。 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ FT8U245AM 関係 このチップは外づけの回路がちょっと多く少し面倒らしい。実をいうとリセットまわり でどういう回路を組めばよいか良く分からない。 ・ モルフィー企画の FTDI ボードには回路図がある。 ・ あと HDL にも USB-001の回路図として出ている。 それとは別にひとつみつけた。 ・ http://www.geocities.com/Skimask87/usb_adapter.html リセット回路系は三者全部ばらばら。なんとなく、リセット回路を使ったのがよさそう に思える。ちなみに千石電商では、ミツミのリセット回路 PST600C を扱っている。これ は 4.5V でリセットがかかるもの? MCP100-4.5 と同じようなものなのか? IPI は、 MPC100-450 というのを扱っていた。 それととっても気になっているのは、ソフトウェア側からリセットできないのか? とい う点。 普通ドライバがなんかおかしいと認識したら、チップに対してリセットかけたりするわ けだが、それができるのかどうか。 FT8U245AM の信号をみるとそんなものはないようにも見える。 もしなければ、どうしよう。手動リセット回路を付ける? ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ (最終更新 Fri Jul 5 13:10:39 2002)