2012年03月23日

LPCインターフェイスめも

Lattice のリファレンスデザインのなかに

があるのを見つけた。そういえば、自作FPGA ボード を相互に接続するのに I/O が 7本 + 1(入力専用)しかなく、これで実装できるインターフェイスを探したときに LPC を知ったのだった。

    簡単に説明すると、最低 LRESET, LCLK, LFRAME , LAD[3:0] の 7 本の線で接続できる バス規格。周波数は、33 MHz 。

LPC は Intel が提唱した規格で PC の SUPER I/O 用に実際に使われていた。SUPER I/O というのは、所謂レガシーインターフェイスをまとめたもので、今でもデジキーで入手できるチップがある。

もの自体には興味がないが、データシートは実装を実際にどういう風にするのかという参考になる。

    ちょっと見てみたところ、LDRQ , SERIRQ といった DMA や割り込み要求の線がある。実際には 7 本では済まないわけだが、イベントを通知する線は まぁ必要だろう。

    SUPER I/O のデバイスは、シリアル/パラレル/ゲームポート/MIDI/FDC など。あと ハードウェアモニタ関連で、ファンとか 電圧監視用 ADC とか GPIO とか。

    そういえば、PC の bios の FLASH にも使われていた。LPC を初めて知ったのは、これかも知れない。

    こっちのデータシートは、どういう風にアクセスするのか詳細が書いてある。

LPC の詳細

    さて、RD1049 をひもといて見る。ドキュメントは RD1049 自体のもの以外に LPC の仕様書が入っている。規模は、MachXO2 で HOST 用が 84 LUT , デバイス用が 73 LUT 。

    どうも、アドレス 16bit / データ 8bit の ISA バスのような 仕様で使っているようだ。

    次に 仕様書をちょっと見てみた。

    16bit の I/O アドレス以外に 32bit のメモリアドレスが定義されていて、いろいろな転送モードがある。どうも ISA バスの機能を包含することが(もともとの規格では)重要らしい。

      Host Initlated Memory Read
      Host Initlated Firmware Memory Read
      Host Initlated I/O Read

      Host Initlated Memory Write
      Host Initlated Firmware Memory Write
      Host Initlated I/O Write

      Peripheral Initlated Memory Read
      Peripheral Initlated I/O Read (8bit , 16bit, 32bit)

      Peripheral Initlated Memory Write
      Peripheral Initlated I/O Write (8bit , 16bit, 32bit)

      DMA Read (8bit , 16bit, 32bit)
      DMA Write (8bit , 16bit, 32bit)

    RD1049 がサポートしているのは、Host Initlated I/O Read/Write のみのようだ。どんなアクセスをするのか、DMA も加えて表にしてみた。

      START CYCTYPE+DIR ADDR TAR SYNC DATA TAR total
      Host Initlated I/O Read H1 H1 H4 H2 P1 P2 P2 13
      CHANNEL+SIZE
      DMA Write Cycle (8bit) H1 H1 H2 H2 P1 P2 P2 11
      DMA Write Cycle (16bit) H1 H1 H2 H2 P2 P4 P2 14
      DMA Write Cycle (32bit) H1 H1 H2 H2 P4 P8 P2 20

      START CYCTYPE+DIR ADDR DATA TAR SYNC TAR total
      Host Initlated I/O Write H1 H1 H4 H2 H2 P1 P2 13
      CHANNEL+SIZE
      DMA Read Cycle (8bit) H1 H1 H2 H2 H2 P1 P2 11
      DMA Read Cycle (16bit) H1 H1 H2 H4 H4 P2 P4 18
      DMA Read Cycle (32bit) H1 H1 H2 H8 H8 P4 P8 32

    必ずホストが、転送を開始する DMAも同じで、デバイス側の要求が受け付けられたら HOST が転送をスタートさせる。

    H1 というのは、Host 側が 1 回(4bit の)転送を行うという意味。P2 は デバイス側は 2 回。
    DMA では ADDR の代わりに CHANNEL と SIZE が送られる。あと LFRAME は、START の 1 クロックだけ L になる。

    33 MHz 4bit だから 16.5 MB/sec の帯域だが、転送にはこのシーケンスが必要なので、実際の帯域は随分落ちる。Host Initlated I/O Read/Write では 13 クロックで 1 バイトだから 2.56 MB/sec 。

    ちょっと思ったより帯域が少ないのでがっかりだが ... 仕様など知ったことではないというスタンスでプロトコルを 再定義すれば 512B のバースト転送とかで、16 MB/sec ぐらいにはなるかも知れない。

      よく分かって書いているわけではないが、DMA の TAR を 2 クロックに限定して DMA のサイズを 再定義してやるとか。ついでに書くと DDR も CHANNEL か SIZE で指定できるようにしたりして。

      追記: DMA の TAR のクロック数は、上記の表の通りである必要はない。TAR の後に来るのは、SYNC か START で DATA 待ちみたいな感じ。


    以下 START 等の意味。(関係なさそうなものは省略しているので注意)

    START

      0000 Start
      1111 Stop/Abort

      普通 0000 でスタートする。スタートは LFRAME が L になる。Stop/Abort は、wait のタイムアウト時に使う。

    CYCTYPE + DIR

      000x I/O Read
      001x I/O Write
      100x DMA Read
      101x DMA Write

      x は 0 にせよということだが、読む方はきにしない?

    SIZE

      xx00 (0) 8bit
      xx01 (1) 16bit
      xx11 (3) 32bit

      2 のべき乗ルールなら良かったのだが、N+1 になっているようだ。

    SYNC

      0000 Ready
      0101 Short Wait
      0110 Long Wait
      1001 Ready More
      1010 Error

      Short Wait/Long Wait で 所謂 Wait が挿入できるようだ。Short /Long で Timeout のパラメータを変えるものらしい。Short は 数クロック / Long でも 1 マイクロ秒 (30 クロック)程度。

      Timeout したときに HOST は、LFRAME を L にするのだが、数クロック後に 1111(START:abort) を送出して H に戻す。LFRAME=L と同時に 1111 を送出すると信号がぶつかるからダメ。

    TAR (Turn Around)

      1111 zzzz ...

      LAD も weak pull-up だそうだ。最初だけ 1111 にせよということらしい。

    CHANNEL

      なんでも良いかと思ったらルールがあった。0-3 が 8bit で 5-7 が 16 bit 4 が バスマスタ用って書いてある。あと bit3 は、TC(Terminal Count) で 1 だと 最後の転送であることを指示する。

      そして、LDRQ もルールがあった。LCLK に同期したシリアルで、

      LCLK ____ ____ ____ ____ ____ ____
      _____| |____| |____| |____| |____| |____| |____

      LDRQ __________ _________
      |_________| bit2 | bit1 | bit0 |

      こんな風に送るものらしい。

      書き忘れたが、他のデータも negedge LCLK でデータ送出、posedge LCLK で受け取り。

      ちなみに、SERIRQ (Serialized IRQ) というのも プロトコルがあり、LDRQ より複雑そう。
      ( LPC の仕様書には詳細は記述されてない。PCI の仕様書で定義されているらしい。)

おわりに

    なかなか難しいものようだ。だが完全オリジナルよりは、LPC ベースに検討した方が良さそう。

    やりたいことは、もともと 7bit + 入力 1 での デバイスの拡張なのだ。DMA の使い方を変更・拡張して、自分の目的にあったものを作ってみたい。

    あと、lattice のリファレンスデザインは、 lattice のデバイスにインプリメントする場合に限り自由なのであって、本当に自由にしたければ自分で作らないといけない。あくまで参考として使うようにしないと。

おまけ

    RD1049 レベルのものを書いてみた。

  • lpc_master.v
  • lpc_slave.v

    本当は、Host-Peripheral の関係だが、なんとなく master-slave というネーミングにした。あと用語で LAD とすべきところを LDA にしてしまった。(未修正) 。まぁプロトタイプというか書いてみただけだから ... 。もちろん動く保証もない。

    基本的に posedge で state を変更し 入力処理をして、negedge では、state に応じた出力処理になるよにしている。規模は master が 80 LUT , slave が 65 LUT 。RD1049 と似た様なもの。

    これを元にどう膨らませようか思案中。

    まず slave 側。I/O 空間にレジスタをマッピングして定義したデバイスを動作させるようにして、512B ぐらいのバッファを DMA の手順でバースト転送できるようにしたい。あと、クロックは、LCLK をマスタークロックとして全部同期させる。

    slave 側のデバイスとして SDカードをサポートするのをまず想定している。それ以外に複数の slave で共通のデバイスをもたせたりもしたい。なかなかに難しそうな...

    master 側は、LCLK を マスタークロック にしないで ハンドシェークさせるつもり。master 側が数分の1 のクロックで動作しても問題ないようにしたいが、master 側 の方が速かったりするケースにも対応したいような... これもやっぱり難しそう。

追記: OpenCores の LPC モジュール

  • wb_lpc -- Wishbone LPC Host and Peripheral Bridge

    というのがあった。Features を見てみると DMA や firmware アクセス , Serialized IRQ など フルスペックのように見える。

    DMA の DIR (Read/Write の方向) をどう設計すべきか、参考になるかと期待したのだが ... よくわからなかった。より上位で決めるような設計のようだ。

    serirq については、少し分かった。

      START FRAME -- IRQ0 FRAME ... IRQN FRAME -- STOP FRAME --

      START FRAME 0 ... 0 1 1 (6 - 12 clocks)
      P/H H
      IRQN FRAME 0/1 1 1 (3 clocks)
      P
      STOP FRAME 1 0 0 1 1 (5 clocks)
      H H
      IRQ : IRQ0 ... IRQ15 IOCHECK ( INTA INTB INTC INTD .. )

    最初は、Peripheral が 0 にする場合もあれば Host が 0 にする場合もある。続く 3-7 clock Host が 0 にする。0 の期間は、4,6,8 clock だそうだ。どちらも出力しなくても 1 になるから オープンドレイン?

    ちなみに、信号は、posedge で出力、negedge で取り込み。

    次に 3 clock (S - R - T)づつの FRAME が続く。 IRQ15 までの 16 個かと思ったが、未定義を含めて 17個 - 32個分ある。(数は設定で決まる。)

ちょっとコードを書いてみた。

    module serirq_slave (
    input LCLK
    , input LRESET
    , inout SERIRQ

    , input [7:0] irq_req
    );

    reg [3:0] state;
    reg [1:0] sub_state; // S R T
    reg serirq_out;

    assign SERIRQ = serirq_out ? 1'b0 : 1'bz;

    always @(negedge LCLK)
    begin
    if (sub_state == 2'b01)
    begin
    if ((state == 4'h1) & (irq_req[0])) serirq_out <= 1'b1;
    else if ((state == 4'h2) & (irq_req[1])) serirq_out <= 1'b1;
    else if ((state == 4'h3) & (irq_req[2])) serirq_out <= 1'b1;
    else if ((state == 4'h4) & (irq_req[3])) serirq_out <= 1'b1;
    else if ((state == 4'h4) & (irq_req[3])) serirq_out <= 1'b1;
    else if ((state == 4'h5) & (irq_req[4])) serirq_out <= 1'b1;
    else if ((state == 4'h6) & (irq_req[5])) serirq_out <= 1'b1;
    else if ((state == 4'h7) & (irq_req[6])) serirq_out <= 1'b1;
    else if ((state == 4'h8) & (irq_req[7])) serirq_out <= 1'b1;
    else serirq_out <= 1'b0;
    end
    else if (sub_state == 2'b00)
    begin
    serirq_out <= irq_req[0] | irq_req[1] | irq_req[2] | irq_req[3]
    | irq_req[4] | irq_req[5] | irq_req[6] | irq_req[7]
    ;
    end
    else serirq_out <= 1'b0;
    end // always

    always @(posedge LCLK)
    begin
    if (LRESET)
    begin
    state <= 4'h0;
    sub_state <= 2'b00;
    end
    else if ((sub_state == 2'b00) & ~SERIRQ)
    begin
    state <= 4'h0;
    sub_state <= 2'b01;
    end
    else if (sub_state == 2'b00)
    begin
    state <= 4'h0;
    sub_state <= 2'b00;
    end
    else if ( (sub_state == 2'b01) & (state == 4'h0) & SERIRQ)
    begin
    state <= 4'h0;
    sub_state <= 2'b10;
    end
    else if ( (sub_state == 2'b01) & (state == 4'h0))
    begin
    state <= 4'h0;
    sub_state <= 2'b01;
    end
    else if ( (sub_state == 2'b11) & ~SERIRQ )
    begin
    state <= 4'h0;
    sub_state <= 2'b00;
    end
    else
    begin
    sub_state <= sub_state + 2'b01;
    end
    end // always
    endmodule


    簡単に説明すると、posedge LCLK で ステート制御。ステートは 3 の倍数で進むので 2 つに分けている。スタートビット検出をきっかけに進む。( 最初のスタートビットは、slave 側が作っている。)

    ストップビットは、2bit 目の 0 を検出したところで IDLE に戻る。

    sub_state は、0 → 1 → 2 → 3 → 1 → 2 → 3 と進む。0 になるのは IDLE だけ。

    master も状態制御は、同じようなもの。スタートストップビットの送出と irq_req が取り込みに変わる。

    冗長な書き方だが、14 スライス/27 LUT 。

LPC マザー

    LPC HOST をいくつか接続できるものを作ってみたい。

    HOST 毎に制御するデバイスが GPIO , SDIO , シリアル とか。共有するデバイスが LCD とか キーボードとか。

    あと入れたいのが、HOST 間通信。4 つ HOST が付けられるとしたら 12 個の FIFO でも実装可能か。
    共有を考えなければ簡単?

    ただ、そんなものを作るぐらいなら、SDIO という気も。

    ちょっとググってみると Posedge SDIO device controller (pdf) なんてものが見つかった。

    SD bus を AHB Bus に変換する IP。チップだと思ったら違った。

    中華Android / 中華PMP に使われている CPU(SoC) は、超低価格なのだが PCI みたいな バスは持っていない。最も高速なインターフェイスが USB device (640Mbps) で次が SDIO (〜 80Mbps x 4 とか) というパターンも多い。これになにかデバイスを付けようとすれば、SDIO のデバイスコントローラが欲しくなるのだった。

      S-ATA が付いているものもある。(Allwinner A10) 。Ethernet は一般的だが 100Mbps で上記より遅い。

    で、公開されている SDIO Simplifyed Specification を見てみた。

       I/O の方式は、SPI , 4bit-SD 以外に 1bit-SD がある。(これはメモリカードも同じ)

       割り込み(IRQ) は、DAT[1] を使うが 4bit-SD では、特定のタイミングでのみ有効(optional) 。詳細は削られている。

       DAT[2] にも Read Wait という機能が割り当てられている(optional)

       I/O 電圧変更は、SPI でも可能なようだ。

      SDIO は、基本的に CMD52(IO_RW_DIRECT), CMD53(IO_RW_EXTENDED) を使う。CMD52 は、レジスタアクセス+I/O 制御。CMD53 が I/O (サイズは 1-512B)

      レジスタ の仕様で 0-FF は、OCCR(Card Common Control Register) といって、いろいろ決まりごとがある。
      256B 単位で Function 1-7 の FBR(Function Basic information Registers) が続く。

      Function の定義には、USB の標準クラスのようなものがある。UART , BlueTooth Type A, BlueTooth Type B, カメラ, GPS, WLAN , SDIO-ATA など。

      CMD52,CMD53 以外で発行されうるコマンドは、

      CMD0 Pin1=High (CS=Low : SPI)
      CMD8
      CMD5 Arg=0 Get IO OCR
      CMD5 Arg=WV
      ACMD41 Arg=00 Get memory OCR (CMD58 : SPI)
      CMD3 (SD only)

      初期化シーケンスから拾ってきただけなので、詳細は知らない。ACMD41 はエラー(OCR invalid)になる。
posted by すz at 22:33| Comment(1) | TrackBack(0) | MachXO2
この記事へのコメント
初めてPOST CODEを自前で表示しようとしていたのですが、LPCインターフェイスの詳細資料があまり見当たらず、こちら参考になりました。
ありがとうございました(^^
Posted by Pi at 2014年05月23日 15:33
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント: [必須入力]

認証コード: [必須入力]


※画像の中の文字を半角で入力してください。
この記事へのトラックバックURL
http://blog.sakura.ne.jp/tb/54603539
※ブログオーナーが承認したトラックバックのみ表示されます。

この記事へのトラックバック