FTDI FT8U245AM でUSBを使ったシリアルI/O の実装を考える。
目標機能 6Mhz のクロックで JTAG または SPI を制御できるもの。
チップ: XC9572(XL) と FTDI FT8U245AM
FTDI 8U245AMは、どんなチップかというと、ホストからは USB RS232C として見えるが、 デバイス側からは、パラレルインターフェイスに見える USB コントローラ。 高速な転送 1MB/sec が可能。
RXF# --------------- ---------------------- ↓_________↑ RD# -------------------- ---------------- ↓_↑ DATA -------------------< >----
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 にするか )。 例外的なのは、
だけだから、それを 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 関係
このチップは外づけの回路がちょっと多く 少し面倒らしい。 実をいうと リセットまわりで どういう回路を組めばよいか良く分からない。
それとは別にひとつみつけた。
リセット回路系は 三者全部ばらばら。なんとなく、リセット回路を使った のがよさそうに思える。ちなみに 千石電商では、ミツミのリセット回路 PST600C を扱っている。これは 4.5V でリセットがかかるもの? MCP100-4.5 と同じようなものなのか? IPI は、MPC100-450 というのを 扱っていた 。
それととっても気になっているのは、ソフトウェア側からリセットできないのか? という点。
普通 ドライバがなんかおかしいと 認識したら、チップに対してリセット かけたりするわけだが、それができるのかどうか。
FT8U245AM の信号をみると そんなものはないようにも見える。
もしなければ、どうしよう。手動リセット回路を付ける?
02/08/12
回路をつくってはテストしているが、どうも肝心なことがわかっていないようだ。
CLK の立ち下がり↓ と 立ち上がり↑ でそれぞれ状態が変わるのだが、 お互いのフェーズで、相手の状態がどう見えるかがわかっていない。
とりあえず、最新版を http:arc/usb_sio-003.tgz に置く。
なんか、立派なプログラムみたいだ。この回路を XC9572XL でコンパイルすると
Macrocells Product Terms Registers Pins Function Block Used Used Used Used Inputs Used 60 /72 ( 83%) 286 /360 ( 79%) 49 /72 ( 68%) 27 /52 ( 51%) 128/216 ( 59%)
という結果になっていて、ちゃんと入るし、まだ少し余裕がある。
上の回路は、シフト動作がちゃんとできていない。これを直して、 内部レジスタをちゃんと定義すれば出来上がり。
プロトコルはだいたいかたまった。
コマンド バイト数 送信データ ( N バイト ) を受信して、 コマンド バイト数 送信データ ( N バイト ) を送り返す。コマンドとバイト数は、受信したもののエコーバック。 バイト数は、1 - 127 バイトの範囲を考えている。 コマンドのフォーマットは、 +---+---+---+---+---+---+---+---+ | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | +---+---+---+---+---+---+---+---+ | 0 | X | ro| chan | bits | +---+---+---+---+---+---+---+---+ bit 7 0: 有効 1: 無効 bit 6 未使用 bit 5 内部チャネルアクセス( chan = 3 ) のとき、データを書くかどうかのフラグ 0 だと、読みこむのみ。 bit 4-3 チャネル 00 外部 1 01 外部2 11 内部 内部チャネルは、レジスタのアクセス。いまのところ 4 bit だけアサイン しているが、もうすこし増やす。 bit 2-0 最後のバイトの有効 bit 数。0 は 8bitを意味する。
回路の説明
重要な内部状態として dir と state がある。 基本的に次のような動きをする clk |~|_|~|_|~|_|~|_|~|_|~|_|~|_|~|_|~|_|~|_|~|_|~|_|~|_|~|_| dir | XX| 01 | 10 | 11 state | 00| 01| 10| 11| 00| 01| 10| 11| 00| 01| 10| | | | | | | | | | | | | | | | | rd ~~~~~|_|~~~~~~~~~~~~~|_|~~~~~~~~~~~~~|_|~~~~ input o o o cmd bytes latch wr _____________|~|_____________|~|_______________ output -------------< >-----------< >------------- cmd bytes 内部レジスタには、 cmd bytes latch の 3つがあって、 どれをアクセスしてるかの 状態が dir 。 それぞれの状態に対して、 read 前処理 --- read --- write 前処理 --- write の state がある。 シフト動作は、write 前処理 に入れている。 シフト動作がうまくいっていないのは、 if (CLK' event and CLK = '0') then : end if; if (CLK' event and CLK = '1') then : end if; という2つの処理がからみあっているため。もっとシンプルにしないと ダメっぽい。
まぁ、悩みは多いが、作っていてたのしめている。
バウンダリスキャンができるおかげで、 (IC を壊してしまう危険性はあるものの)まるでソフトのデバッグのような 感覚で、デバッグできる。
02/08/13
シミュレータの ModelSIM を使ったおかげで 2 つの問題のが分かった。
RD の信号を RD <= '0' or ( CLK ) when state = "01" else '1'; . なんて書いてはダメだということ。state が 少しずれるので、 ヒゲがでる。 CLK ~~~~~~|________|~~~~~~~~|_____ RD ~~~~~~|________|~~~~~~~~||~~~~~~~ ここ これは結局 if (CLK' event) then if (state = "01") then RD <= '0' or ( CLK ) ; else RD <= '1'; end if; こんな風にかかないとダメだった。ちなみに WR についても同じ。
Tri-State の出力を DATA <= (others => 'Z') when not (state = "11") else cmd when dir = "01" else bytes when dir = "10" else latch when dir = "11" else (others => 'Z'); こんな風にかいていたのだが、ModelSIM ではエラーになる。 RD の書きかたを知ったので、 if (CLK' event and CLK = '0') then : のブロックに入れるようにする。具体的には ... 1) 頭で DATA <= (others => 'Z'); を挿入。 2) state = "11" の処理をしているブロックに case dir is when "01" => DATA <= cmd; when "10" => DATA <= bytes; when "11" => DATA <= latch; when others => DATA <= (others => 'Z'); end case; を挿入。
この変更をしたもの。および、ModelSIM 用のデータ test_01.vhd を入れたものを
次は、ModelSIM で シフトのタイミングを確認しながらデバッグしていけば、 動くものが作れると思う。
XC9572XL の状態は....
Macrocells Product Terms Registers Pins Function Block Used Used Used Used Inputs Used usb_sio-003: 60 /72 ( 83%) 286 /360 ( 79%) 49 /72 ( 68%) 27 /52 ( 51%) 128/216 ( 59%) usb_sio-004: Used Used Used Used Inputs Used 64 /72 ( 88%) 320 /360 ( 88%) 60 /72 ( 83%) 27 /52 ( 51%) 143/216 ( 66%)
と変化した。Registerが、11 bit 増えている。たぶん出力用の 8 bit と RD/WR と 出力用のゲートの 11 bitが増えたのだろう。
ずいぶん厳しくなってきたが、こうしないと動かないのではしょうがないか...
シフトの動作もサポートして、 たぶん動きそうなものが出来て来た。ModelSIM のおかげで、 ある程度確認できている。
どのタイミングで <= で代入した データが見えるのかよくわからなかったのだが、
ということらしい。2 つの process ブロック間で相互参照したら、混乱してしまった のだが、ModelSIM で確認しながら作っていったら 希望の動作ができるように なったようだ。
内部レジスタを 5 bit にしようとすると、XC9572XL に入らなくなるようだ。 ここで、いろいろ機能を付加したかったのだが、とりあえずは保留。
いまの内部レジスタの機能は、
もうこれ以外のものを付けられないのか...
ついでなんで、内部レジスタをどうやってアクセスするか 示してみる。
どういうことかというと プロトコルは、cmd , bytes , data (バイト数 bytes) を送り、同じバイト数を受け取る。cmd , bytes はエコーバックされたもの。
cmd のフォーマットは次のようになっている。
cmd format 7 6 5 4 3 2 1 0 +---+---+---+-------+-----------+ | 0 | 0 | ro| chan | bits | +---+---+---+-------+-----------+ chan : 0 => external 1 1 => external 2 3 => internal reg internal reg 3 2 1 0 +----+----+----+----+ |XCK2|XCK1|MS2 |MS1 | +----+----+----+----+ XCK1 1: CK1 is reverse logic 0: CK1 is normal logic XCK2 1: CK2 is reverse logic 0: CK2 is normal logic ro : 1 => do not modefy internal reg 0 => modefy internal reg bits : last byte's shift count 1-7 => 1-7 bits
内部レジスタを read する場合は、bit 5 を 1 にして chan に 3 を入れる。 そして、bits に 4 を入れ 4 回シフト動作させる。
シフトの入力は、LSB から入ってくるので、4 bit のデータが読めるわけだ。
内部レジスタを read and write する場合は、chan に 3 を入れる。 そして、bits に 4 を入れ 4 回シフト動作させる。
シフトの出力は、MSB から出て行くので、 更新する 4 bit のデータを上位 4bit に設定する。 戻りは、read 同様 下位 4 bit に入る。
(ちなみに もとの 下位 4 bit は上位 にシフトしているが 0 なので関係ない。)
こういう処理を external のチャネルに対してもできる。 タイミング、および 出力のデータは以下のとおり。
CK ~~~~~~~|_________|~~~~~~~~|_______ DO -------< MSB >-------- DI o LSB 取り込みシフト MS (内部レジスタの値) CK は、H レベルは、プルアップされた 'H' を出力 L レベルは、'0' DO は、出力期間だけ 値を出しそれ以外は、'Z' MS は、CK と同じく、'H'/'0' 出力
これで完成したら まずまずとしておこう。
ちなみに
Macrocells Product Terms Registers Pins Function Block Used Used Used Used Inputs Used 64 /72 ( 88%) 332 /360 ( 92%) 60 /72 ( 83%) 27 /52 ( 51%) 145/216 ( 67%)
こんな感じ。もっと入るかと思ったが、ダメらしい。
動くと思っていたんだが、005 は動かないことが分かった。 ダメなのは、WR/RD の操作と DATA への出力 --- よくわからないのだが、 どうも 1 CLK 後にずれ込んでいるらしい。
書き直したこれは、どうも動いているように見える。
Macrocells Product Terms Registers Pins Function Block Used Used Used Used Inputs Used 64 /72 ( 88%) 328 /360 ( 91%) 58 /72 ( 80%) 23 /52 ( 44%) 148/216 ( 68%)
report ファイルを見ると、Product Terms がネックになっている。 1,2 bit の状態をあらたに作ると over してしまうようだ。
ModelSIM による グラフ
いまの問題点、
さて、ここまで動いたら、 NAXJPを対応させる ことを考えたい。この先は、電子工作/NAXJP改造へ。
それとは別に、実際のデバイスで、SPI での通信テストをやりたい。 想定しているのは、
データシートをながめていると、タイミングが微妙に違う。 はたして、想定したタイミングだけで できるのか?
... という以前に、CLK を 1/16 程度まで 分周できないとダメだ。 ... それは内部レジスタの設定で行いたいし...
というわけで 変更してみた。
変更内容は
0 6Mhz 1 3Mhz 2 2Mhz 3 1.5Mhz 4 1.2Mhz 5 1 Mhz 6 857 khz 7 750 khz
rpt をみるとほぼ限界、pin の位置を変えただけでも入らなくなる... そろそろ 。。さわりたくないぞ。
Used Used Used Used Inputs Used 68 /72 ( 94%) 332 /360 ( 92%) 62 /72 ( 86%) 24 /52 ( 46%) 150/216 ( 69%)
デバイスのテストは、ATtiny-12L-4SC にきめた。
こいつを
GND -> RESET --+(1) +-- VCC (3.3V) --+ +-- PB2 <- CK --+ +-- PB1 -> DI GND -> GND --+ +-- PB0 <- DO
6 本の線を 空中配線で つないでやる。 つなぐ先は、ピンソケット。
この状態で、Flash ROM に読めるかどうか/書けるかどうかのテストを すればよい。
コマンドは簡単だ。4 バイト分のデータを write/read すればよいらしい。
Chip Erase 1010 1100 | 100x xxxx | xxxx xxxx | xxxx xxxx | Read Program Memory 0010 H000 | xxxx xxxa | bbbb bbbb | oooo oooo | Write Program Memory 0100 H000 | xxxx xxxa | bbbb bbbb | iiii iiii | H = 0 Low Byte H = 1 High Byte a bbbb bbbb アドレス oooo oooo 出力データ iiii iiii 入力データ
全部で、1K バイトのデータが読み書きできるはず。
タイミングチャートは、どうか。
CK ~~|_____|~~~~~|_____|~~~~~ DO ---<XXXXXXXXXXXX>-------- DI o o
クロックの立ち上がりで お互いに 読むので こんな感じでよさそうだ。
最終版の VHDL ソース sub_sio.vhd
-- usb_sio.vhd (version 007) -- desgined by suz -- -- cmd format -- -- 7 6 5 4 3 2 1 0 -- +---+---+---+-------+-----------+ -- | 0 | 0 | ro| chan | bits | -- +---+---+---+-------+-----------+ -- chan : 0 => external 1 -- 1 => external 2 -- 3 => internal reg -- internal reg -- 7 6 5 4 3 2 1 0 -- +----+----+----+----+----+----+----+----+ -- |----| CLK_SEL |CK2 |CK1 |MS2 |MS1 | -- +----+----+----+----+----+----+----+----+ -- CLK_SEL : 0 6Mhz 1 3 Mhz 2 2Mhz .... 7 0.75Mhz -- ro : 1 => do not modefy internal reg -- 0 => modefy internal reg -- bits : last byte's shift count --- 1-7 => 1-7 bits -- 0 => 8 bits -- bytes format -- 7 6 5 4 3 2 1 0 -- +---+---+---+-------+-----------+ -- | 0 | bytes | -- +---+---+---+-------+-----------+ -- bytes => 1 .. 127 -------------------------------------------------------------------------- library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity usb_sio is Port ( DATA : inout std_logic_vector(7 downto 0) bus; CLK : in std_logic; RXF : in std_logic; TXF : in std_logic; RESET : in std_logic; RD : out std_logic; WR : out std_logic; EEREQ : out std_logic; --DCNT : out std_logic_vector(3 downto 0); OCLK : out std_logic; DI1 : in std_logic; DO1 : out std_logic; CK1 : out std_logic; MS1 : out std_logic; DI2 : in std_logic; DO2 : out std_logic; CK2 : out std_logic; MS2 : out std_logic ); end usb_sio; -------------------------------------------------------------------------- architecture behavioral of usb_sio is signal state: std_logic_vector(1 downto 0); -- "00" before-read -- "01" read -- "10" befor-write -- "11" write signal dir: std_logic_vector(1 downto 0); -- "00" NONE; -- "01" CMD -- "10" BYTES -- "11" LATCH signal cmd : std_logic_vector(7 downto 0); signal bytes : std_logic_vector(7 downto 0); signal latch : std_logic_vector(7 downto 0); signal lrxf : std_logic; signal ltxf : std_logic; signal bits : std_logic_vector(3 downto 0); signal do_shift : std_logic; signal do_load : std_logic; signal stat2 : std_logic_vector(1 downto 0); signal m_reg : std_logic_vector(7 downto 0) := "00001111"; signal iclk : std_logic := '0'; signal icnt : std_logic_vector(2 downto 0) := "000"; begin MS1 <= '1' when (m_reg(0) = '1') else '0'; MS2 <= '1' when (m_reg(1) = '1') else '0'; EEREQ <= 'Z'; --DCNT(3) <= dir(1); --DCNT(2) <= dir(0); -- DCNT(1) <= state(1); --DCNT(0) <= state(0); OCLK <= iclk; RD <= '0' when (stat2 = "00" and state = "01") else '1'; WR <= '1' when (stat2 = "10" and state = "11") else '0'; CK1 <= (not m_reg(2)) when ( do_shift = '1' and do_load = '0' and cmd(4 downto 3) = "00" and iclk = '0') else m_reg(2); CK2 <= (not m_reg(3)) when ( do_shift = '1' and do_load = '0' and cmd(4 downto 3) = "01" and iclk = '0') else m_reg(3); process (CLK) begin if (CLK' event) then icnt <= icnt + 1; if (icnt = m_reg(6 downto 4)) then iclk <= not iclk; icnt <= "000"; end if; end if; end process; process (iclk) begin if (iclk' event and iclk = '0') then if (RESET = '0') then dir <= "00"; state <= "00"; DATA <= (others => 'Z'); DO1 <= 'Z'; DO2 <= 'Z'; else DATA <= (others => 'Z'); DO1 <= 'Z'; DO2 <= 'Z'; case state is when "00" => if (lrxf = '0') then state <= "01"; case dir is when "01" => dir <= "10"; when "10" => dir <= "11"; when "11" => if (bytes(6 downto 0) = 0) then dir <= "01"; else dir <= "11"; end if; when others => dir <= "01"; end case; end if; when "01" => state <= "10"; when "10" => if (cmd(7) = '1' or ((dir(1) = '1') and (bytes(7) = '1'))) then dir <= "00"; state <= "00"; elsif (do_shift = '1') then -- DO1 <= latch(7); -- for test case cmd(4 downto 3) is when "00" => DO1 <= latch(7); when "01" => DO2 <= latch(7); when others => null; end case; elsif (ltxf = '0') then state <= "11"; case dir is when "01" => DATA <= cmd; when "10" => DATA <= bytes; when "11" => DATA <= latch; when others => DATA <= (others => 'Z'); end case; end if; when others => state <= "00"; end case; end if; end if; if (iclk' event and iclk = '1') then if (RESET = '0') then lrxf <= '1'; do_shift <= '0'; do_load <= '0'; m_reg <= "00001111"; else lrxf <= RXF; ltxf <= TXF; stat2 <= state; if (state = "01") then do_shift <= '0'; do_load <= '0'; case dir is when "01" => cmd <= DATA; when "10" => bytes <= DATA; when "11" => latch <= DATA; if bytes(6 downto 0) = "000001" and not (cmd(2 downto 0) = "000") then bits <= '0' & cmd(2 downto 0); else bits <= "1000"; end if; do_shift <= '1'; do_load <= '1'; bytes(6 downto 0) <= bytes(6 downto 0) - 1; when others => null; end case; end if; if (state = "10" and do_shift = '1') then do_load <= '0'; if (bits = "0000" ) then do_shift <= '0'; if (cmd(4 downto 3) = "11") then latch <= m_reg; if (dir = "11" and cmd(5 downto 3) = "011") then m_reg <= latch; end if; end if; end if; bits <= bits - 1; if (do_load = '0') then case cmd(4 downto 3) is when "00" => latch <= latch(6 downto 0) & DI1; when "10" => latch <= latch(6 downto 0) & DI2; when others => null; end case; end if; end if; end if; end if; end process; end behavioral;