ロード トップ 参照元 逆検索 検索 ヘルプ

[logo] 電子工作/XC9572XLと8U245AMで作るUSBシリアルIO


SuzTiki:電子工作

FTDI FT8U245AM でUSBを使ったシリアルI/O の実装を考える。

目標機能 6Mhz のクロックで JTAG または SPI を制御できるもの。

チップ: XC9572(XL) と FTDI FT8U245AM

FTDI 8U245AMは、どんなチップかというと、ホストからは USB RS232C として見えるが、 デバイス側からは、パラレルインターフェイスに見える USB コントローラ。 高速な転送 1MB/sec が可能。

こんな感じで デバイスとデータをやりとりする。 基本的には 姉妹品の FTDI FT8U232AM と同じようなものだが、データの伝送路があるだけで、他の制御信号はない。

プロトコルの制御を考えてやる必要がある が、CPLD で実装した簡単なステートマシンを付けることにより、 高速な転送を生かしたモノが作れそうだ。


概要

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 つの問題のが分かった。

この変更をしたもの。および、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 程度まで 分周できないとダメだ。 ... それは内部レジスタの設定で行いたいし...


というわけで 変更してみた。

変更内容は

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;

(最終更新 Thu Mar 30 18:59:35 2006)