2011年02月17日

AVR互換コアの仕様(その3)

(その2)からの続き。

(その2)で書いたことを実装中だが、まだ 見せられるレベルで 作れていない。だが、一部のインターフェイスは FIX しつつある。課題とかも含めてここで整理しておこうと思う。
(注意:この記事はインターフェイスの変更があったら随時更新するつもり)

RAM -- まずは簡単な方から、

    module rtavr_sram_2KB(
    input CLK,
    input RESET,
    input WEA,
    input [10:0] ADDRA,
    input [7:0] DIA,
    output [7:0] DOA,

    input WEB, OE,
    input [10:0] ADDRB,
    input [7:0] DIB,
    output [7:0] DOB
    );

  • RAM をモジュールにする場合は、ブロックRAM を使うから 2KB 単位になる。2KB 以上必要とは思えないから、とりあえずは _2KB サフィックスを付けたものを使う。

  • CPU につなげるのは、ポートB の方。ポートA は未使用。

  • (追加4) WRITE_FIRST に統一 , あと RESET をやめた。

  • 128B とかで十分というケースでは、ROM の後ろを間借りしたい。... が、現状は書き込みは出来ても 16bit 単位なので使えない。これについては ROM の項目で述べる。

ROM

    module rtavr_rom # (
    parameter SIZE = 1024
    // parameter SIZE = 2048
    // parameter SIZE = 4096
    ) (
    input CLK,
    input RESET,
    input [11:0] ADDRA,
    output [15:0] DOA,

    input WEB,OE,
    input [12:0] ADDRB,
    input [7:0] DIB,
    output[7:0] DOB
    );

  • サイズとして考えられるのは、3 通りしかない。なので、1KW/2KW/4KW の選択が出来るようにした。

  • (追加4) WRITE_FIRST に統一 , あと RESET をやめた。

  • 書き込みできる 8 bit の ポートB は、CPU のメモリ空間に接続する。が、word 単位で書かないといけないので、RAM の代わりにはならない。

  • Xilinx のプリミティブ RAMB16BWE_S18_S9 を直接使えば、それは可能なはずなのだが、汎用のシミュレータを使いたいし、今は 使わない。

    でも、もし汎用の書き方で ブロックRAM にうまく嵌ることが出来れば、それを採用するつもり。そのときは、RAM も統合可能なようにする。

  • ROM は、本来 18 bit 分あるはずで、これを 書き込み保護のために使おうかと思っている。2bit 余分なので、"00" 『ROM』 "1X" 『書き込み可能』 "X1" 『書き込み済み』 みたいな使い方をして、Write Once もサポートしたい。これらをサポートしても、基本のインターフェイスは変えないで parameter での指定にしたい。

  • 今のところ初期値も考えていない。今は、スクリプトを使って、AVR の HEX ファイルから、直接 初期値つき コードを生成するのが良いかと思っている。

IOR -- 結構悩ましい。

module rtavr_ior (
input CLK,
input RESET,
input WR,RD,
input [7:0] DI,
input [5:0] ADDR,
output [7:0] DO

`ifdef IOR_HAVE_SREG_SP
, input [7:0] SREG_IN
, input [15:0] SP_IN
, output [7:0] SREG_OUT
, output [15:0] SP_OUT
`endif
// external I/O
`ifdef IOR_HAVE_PORTA
, inout [7:0] PORTA
`endif
`ifdef IOR_HAVE_PORTB
, inout [7:0] PORTB
`endif
`ifdef IOR_HAVE_PORTC
, inout [7:0] PORTC
`endif

// interrupt handling
, input RST_INT0
, output INT0
, input RST_TIM0_OVF
, output TIM0_OVF
, input RST_TIM0_COMPA
, output TIM0_COMPA
, input RST_TIM0_COMPB
, output TIM0_COMPB
);
parameter INT0_BIT = 18; // PC2

  • WR, RD, DI, DO, ADDR は CPU 接続用。RAM / ROM とおなじ接続。

  • SREG, SP をここに入れるかどうか悩んだが、AVR Core のインターフェイスをみると IOR に含めているので 入れることにした。これらは、バスからアクセスできないといけないので、どこにおいても邪魔なのだ。あと、他の Core で流用する場合 不要になるので、まるごと外すことも出来るようにした。

  • PORTA/PORTB/PORTC はそれぞれ独立で アタッチできるように変更。ちょっと多すぎる。できたら parameter にしたいが、どうやるか良く分かっていない。parameter 化するなら、それぞれの bit 数も指定したいところ。

  • PORT については、pull-up の機能がなく、DDR での 出力を設定できるのみ。このレベルだと、新PORT の機能も関係ない。もし、keeper や pull-up が制御できるなら 組み込みたいが後回し。

  • 割り込みは、出力と それをリセットする入力の 2 本づつにした。RS FF の R,Q が出ているイメージ。いまのところ INT0 と TIMER0 の割り込みに対応。デバイスを増やすとどんどん増えることになる、配列にしようか悩み中。

  • INT0 は parameter での指定で任意のポートの任意のビットにアサイン可能。動作の設定は IOR 内のレジスタでするが、いまは未実装。

  • あまりに AVR に依存した構造にすると、他の Core での流用が面倒になるので、これぐらいが良いだろうと判断している。

  • 割り込みと TIMER0 は、今は外せない。だが、割り込みなしで使うコードも実際に書けるから割り込みは外せるようにしたい。

    一方 TIMER0 は外すことは、あまり考えていない。たぶん使う。

  • 16bit の TIMER1 もインプリメントしたいが、機能が多いので 後回し。

  • (追記) よくよく考えれば、この PORT は、IOB 経由で 外部との接続をするのにしか使えない。内部の回路と接続するには、inout の定義が問題。それは別途追加すれば良いわけだが .. 現状 INT0 は PORTA/B/C にしか割り当てられない。これについて少々検討が必要だ。

    GPR -- ここから CPU 内部モジュール。

      module rtavr_gpr_16(
      input RESET,
      `ifdef USE_DMY_CLOCK
      input CLK2X_IN,
      output CLK_OUT,
      `else
      input CLK2X,
      input CLK,
      `endif
      input WE,
      input PREDEC, POSTINC,
      input [7:0] DI,
      input [3:0] ADDRAL,
      input [3:0] ADDRAH,
      input [3:0] ADDRBL,
      input [3:0] ADDRBH,
      output [7:0] DOAL,
      output [7:0] DOAH,
      output [7:0] DOBL,
      output [7:0] DOBH
      `ifdef CHECK_CONFLICT
      , output DI_CONFLICT
      `endif
      , output WB_VALID

      , output [11:0] JA < << (追加4)
      , output [15:0] INDEX < << (追加4)
      );


    • 前に説明したとおりのものだが整理しておくと

      o ADDRAL/DOAL (PREDEC/POSTINC)
      インデックスレジスタ 下位バイト(XL/YL/ZL)
      o ADDRAH/DOAH/DI (WE/PREDEC/POSTINC/WB_VALID)
      インデックスレジスタ 上位バイト(XH/YH/ZH)
      + Rd WRITE
      o ADDRBL/DOBL
      Rd READ + ICALL/IJMP 下位バイト
      o ADDRBH/DOBH
      Rr READ (STORE/OUT の Rd を含む)
      + ICALL/IJMP 上位バイト

    • DOBL/DOBH については、DI の先取り機能付き。

    • ADDRAL など レジスタ番号は S0 で指定し、DOAL など出力は S1 で使う。DI は S2 での指定で、1 クロック遅れだが、モジュール内部でレジスタ番号の指定を遅延させる。

    • WB_VALID は、『XH/YH/ZH の更新が保留されている』という意味で、1 クロック遅らせる要求。

    • DI_CONFLICT は、DI と インデックスレジスタ更新が競合したときのチェック用で、デバッグ専用。

    • CLK2X/CLK_OUT は、クロックの扱いが良く分かっていないので仮の対処。DCM を使って両方入力にする予定。

    • (追加4) WRITE_FIRST に統一 、

      先取り用ポートの追加。実効アドレスは、S2 で確定していけないので、S1 に処理を移動。それに伴い 早い時期から読めるように変更。逆に DOAL/DOAL は使わなくなる見込み。(ただし、まだ残しておく)
       - DOAL/DOAL は使わないので削除

    S0_fetch/S1_decode は入力・出力をはっきりさせないと混乱するので独立したモジュールにする。S2_execute については、関連モジュールが多いので、(今のところ)TOP レベルにする。S3 は存在せず、3段パイプラインの予定。
    S0_fetch -- まずは、これから。

      module rtavr_s0_fetch (
      input RESET,
      input CLK,
      `ifdef INCLUDE_ROM
      input [11:0] PC,
      input WEB, OE,
      input [12:0] ADDRB,
      input [7:0] DIB,
      output[7:0] DOB,
      `else
      input [15:0] PM_OUT,
      `endif
      output [15:0] INST,
      output[3:0] GPR_ADDRAL,
      output[3:0] GPR_ADDRAH,
      output[3:0] GPR_ADDRBL,
      output[3:0] GPR_ADDRBH,
      output GPR_PREDEC, GPR_POSTINC

      , output WB_INST <<< (追加1)
      , input WB_EN <<< (追加1)
      ,input S0_VALID <<< (追加5)
      ,output IDX_INST <<< (追加5)

      );

    • 本来のインターフェイスは、 PM_OUT(ROM 出力) を入力して、INST と GPR への指示を出力。

    • 今後の設計で、S1 への指示があれば増やしていく。
    • INCLUDE_ROM は、遅延を見るためだけに使う。

    • (追加1) WB_INST, WB_EN

      GPR に対してレジスタの更新がある指令 GPR_PREDEC, GPR_POSTINC を 出しているが、そうして良いかお伺いを立てることにした。 WB_INSTは、GPR_PREDEC, GPR_POSTINC を出力したいという意味で、 WB_EN が 1 になれば実際に出力する。

      許可を出すのは、S1 で、競合があるなら 0 にした上で、PC を更新しないことで 1 クロックずらす。0 にするケースは、競合以外にもあるが S1 の仕事で S0 は関知しない。

    • S0 は一旦 FIX 。特に S0 でやれることはない。変更するとすれば、S1 の一部を持って行って高速化するとき。

    • (追加5) S0_VALID, IDX_INST

      WB_EN は、 S0_VALID に名前を変更。IDX_INST は、(PREDEC/POSTINC 以外も含めた)インデックスレジスタ参照の場合 1 。

    S1_fetch -- 現状では一部だけ決まっている。説明した内容ですら入っていない。

      module rtavr_s1_decode (
      input RESET,
      input CLK,

      output CMD_LOAD, CMD_LOAD_WR, CMD_STORE,
      output CMD_OP3, CMD_OP2, CMD_OP1,
      output [2:0] OP3_SEL,
      output OP3_WC, CMD_OP3_WR,
      output [2:0] OP2_SEL,
      output [2:0] OP1_SEL,
      output [7:0] IMM_DATA,
      input [15:0] INST,
      input [11:0] JA, <<< 追加4

      output [11:0] PC,
      output EA_ABS,
      output [7:0] ABS_ADDR
      , input S0_WB_INST <<< 追加1
      , output S0_WB_EN <<< 追加1

      , output EA_PUSH, EA_POP <<< 追加2
      , output [1:0] PUSH_SEL <<< 追加2

      , output CMD_SBIX <<< 追加3
      , output CMD_SBRX <<< 追加3
      , output [2:0] SBIX_BIT <<< 追加3
      , input SBIX_BIT_IN <<< 追加3

      , input [15:0] EA_INDEX, <<< 追加4
      , input [15:0] SP_IN, <<< 追加4
      , output [15:0] SP_OUT, <<< 追加4
      , output [15:0] EA, <<< 追加4

      , input IDX_INST <<< (追加5)
      , output S0_VALID <<< (追加5)
      input [7:0] LOAD_DATA, <<< (追加5)
      output CMD_XBI, <<< (追加5)
      output CMD_BST, <<< (追加5)
      output CMD_BLD, <<< (追加5)
      input FLAGS_BIT_IN, <<< (追加5)
      output XBI_BIT_OUT, <<< (追加5)
      output CMD_SETI, <<< (追加5)
      output CMD_CLRI, <<< (追加5)

      input [4:0] INT_VEC, <<< (追加5)
      input INT_REQ, <<< (追加5)
      output INT_ACK, <<< (追加5)
      );

    • S0 の出力 -- INST を入力にして S2 への指示を決めていく。

    • CMD_LOAD -- LD と IN/LDS あと POP 命令で ロードする指示。(アドレスについては後述)

    • CMD_LOAD_WR -- Rd (で指定したレジスタ)に ロードした値を書き込む指示。

    • CMD_STORE -- ST と OUT/STS , PUSH 命令で Rd を 書き込む指示。

    • CMD_OP3 / CMD_OP3_WR / OP3_SEL / OP3_WC -- Rd / Rr の値で演算した結果を Rd に書き戻す。CMD_OP3 は、演算だけを指示。CMD_OP3_WR は Rd に 値を書き込む指示。

      CMD_OP3L は実際に書き戻す指示。OP3_SEL は 演算の種類で、OP3_WC はキャリーフラグの使い方を指示。

    • CMD_OP2 / OP2_SEL / IMM_DATA -- Rd と IMM_DATA で演算した結果を Rd に書き戻す指示。

    • CMD_OP1 / OP1_SEL -- Rd で演算した結果を Rd に書き戻す指示。

    • EA_ABS / ABS_ADDR -- CMD_LOAD/CMD_STORE での有効アドレス(EA) に ABS_ADDR を使う指示で、IN/OUT , LDS/STS のとき EA_ABS が 1 になる。

    • (追加2) EA_PUSH は、有効アドレス(EA) に SP-- を使う指示。PUSH の動作自体も指示していて、PUSH する値は PUSH_SEL で指示。

       -- PUSH_SEL が 00 の場合は Rr を使う。10 は PC の下位バイトで、11 が PCの上位バイト。

      EA_POP は、有効アドレス(EA) に ++SP を使う指示。書き込み先は PUSH_SEL を兼用する。

    • 命令のスキップは、S2 に対して なにも指示をしないことで実装。

    • プログラムカウンタ(PC) は S1 が内包する。ICALL/IJMP 以外は PC の次の値を S1 で決められる。

      ICALL/IJMP の 分岐先アドレスは、JA に出てくる。レジスタ番号は、S0 が (指示なしで)決める。

      JA は、GPR_DOBL/GPR_DOBH のラッチ前の値を先取り。GPR からもらう。

      .. ちょっと POP を 検討。書き込み先 が PC の場合、S1 内で処理をする。書きこむデータは、GPR_DI に入って来ているが、S1 には来ていない。だが、良く考えてみると、JA には、セレクタ経由で GPI_DI のデータが来ているのだ、しかも接続先は PC 。セレクタ をいじってやるだけで、下位 8 バイトは PC に送り込める。

    • (追加3) スキップの条件を得るための(S2 との)インターフェイス

      条件スキップ 命令には SBIC/SBIS , SBRC/SBRS , CPSE がある。それらの命令のときの条件を S2 から受け取りたい。

      SBIC/SBIS では、CMD_LOAD の指定で IOR から読み込んで来る。合わせて CMD_SBIX を指示して、SBIX_BIT で指定したのビットを SBIX_BIT_IN に入れてもらう。

      SBRC/SBRS では、なにもしなくても Rd_in にデータが来ているので、CMD_SBRX を指示して、SBIX_BIT で指定したのビットを SBIX_BIT_IN に入れてもらう。

      CPSE は、CMD_OP3 で 演算結果を出すところまでする。欲しいのは、演算結果が 0 がどうか。これは、Z フラグのデータを生成するための中間データでもある。上記の条件以外なら そのデータを SBIX_BIT_IN に入れてもらう。

      S1 では、SBIX_BIT_IN と それが 1 でスキップなのか 0 でスキップなのか を判断する内部データ s2_sbix_sel と合わせて スキップ条件を生成する。

      ところで、(GPR_DI == 0) というのをベタで書くのと、一旦 wire di_is_zero = (GPR_DI == 0); としてまとめるのと 結果が同じになるかどうか気になる。

      中途半端なものではあるが、今のもので比べてみた -- OK 規模は同じ。

      次、(GPR_DI == 0) というのは、セレクタを幾つか経由したものだから、結果が出るのが遅い。(Rd_in == Rr_in) とダイレクトに書いたらどうなるのだろう?


      (GPR_DI == 0) (Rd_in == Rr_in)
      Number of Slice Flip Flops: 284 284
      Number of 4 input LUTs: 793 805
      Number of occupied Slices: 521 529
      Total Number of 4 input LUTs: 830 843
      Number used as logic: 777 789
      Number used as a route-thru: 37 38

      スライスで +8 か。これでボトルネックを解消できるなら価値があるが.. ボトルネックじゃなかったら無駄。今は覚えておくだけにしよう。

    • (追加4) 実効アドレスの決定を S1 に移動。

      それに伴いいろいろなものが、S1 に移ってきた。

      RAM の仕様から、実効アドレスの決定は、S1 でやっておかないといけない。そうして初めて S2 で使える。

    • (追加5) 全命令実装後

      随分追加した。個々の命令のための ものと、割り込み関係が主。

      EA_POP は削除。PUSH_SEL も 1bit 削除。その代わりに入ったのが、LOAD_DATA で S1 内で POP 操作を行う。PUSH も S1 で行うが、書きこむデータはIMM_DATA を使って送り込む。

    S2_execute -- TOP レベルとして実装。完成形では、この中に 上記すべてのモジュールを含める。モジュール名は rtavr。

      module rtavr (
      input RESET,
      `ifdef INCLUDE_GPR
      input CLK2X
      `else
      input CLK
      `endif
      // external I/O
      , inout [7:0] PORTA
      , inout [7:0] PORTB
      , inout [7:0] PORTC

    • 本来はこういうシンプルなものになる予定。-- だが実際は、全部のサブモジュールを個別に外して外部に 持っていけるようにしていて、結構複雑になっている。

    • 特に GPR は、2倍速なので、遅延を見るときは、GPR と それ以外で見る。

    • インターフェイスとしては、上記に含まれるので特に書くことはない。

    • ついでなので、LOAD/STORE/OP3/OP2/OP1 に(現状)含まれない命令の一覧。

      // #mnemonic opcode desc
      // NOP 0000:0000:0000:0000 # OP3 に含めた。

      // BLD 1111:100d:dddd:0bbb # bit load from T flag
      // BST 1111:101d:dddd:0bbb # bit store to flag T

      // BRBC 1111:01kk:kkkk:ksss # S1 で実行
      // BRBS 1111:00kk:kkkk:ksss # S1 で実行

      // BSET 1001:0100:0sss:1000 # bit set in FLAGS
      // BCLR 1001:0100:1sss:1000 # bit clear from FLAGS

      // IJMP 1001:0100:0000:1001 # S1 で実行
      // ICALL 1001:0101:0000:1001 # S1 で実行

      // RET 1001:0101:0000:1000 # CMD_LOAD + EA_POP x 2 + XX で指示
      // RETI 1001:0101:0001:1000 # 〃

      // SLEEP 1001:0101:1000:1000 # sleep mode
      // WDR 1001:0101:1010:1000 # watch dog reset

      // RJMP 1100:kkkk:kkkk:kkkk # S1 で実行
      // RCALL 1101:kkkk:kkkk:kkkk # S1 で実行

      // CBI 1001:1000:AAAA:Abbb # CMD_LOAD + CMD_STORE + XX で指示 ?
      // SBI 1001:1010:AAAA:Abbb # 〃

      // SBIC 1001:1001:AAAA:Abbb # CMD_LOAD + CMD_SBIX
      // SBIS 1001:1011:AAAA:Abbb # 〃

      // SBRC 1111:110r:rrrr:0bbb # CMD_LOAD + CMD_SBRX
      // SBRS 1111:111r:rrrr:0bbb # 〃

    • NOP は、CPSE の変形として OP3 で 実装する。

    • CBI/SBI / SBIC/SBIS は LOAD/STORE の機能を使う。

    スナップショット:

    • rtavr-wk05g.tar.gz
    • rtavr-wk04.tar.gz
    • rtavr-wk03.tar.gz

      上記のインターフェイスで作っている現状のもの。05g で全命令実装。インターフェイスはほぼ FIX 。


      動くようなものでは全然ないが、とりあえず 規模とかが出た。

        // (1) (2) (3) 
        // Number of Slice Flip Flops: 247 270 284
        // Number of 4 input LUTs: 722 622 793
        // Number of occupied Slices: 472 430 521
        // Total Number of 4 input LUTs: 762 654 830
        // Number of bonded IOBs : 26 26 26
        // IOB Flip Flops : - - -
        // Number of BUFGMUXs : 2 2 2
        // Number used for Dual Port RAMs 16 16 16
        // Number of RAMB16BWEs : 2 2 2
        // Clock to Setup
        //on destination clock CLK2X 13.030 12.816 12.091
        // (3) rtavr-wk04


    • RAMB16BWEs が 2 個なのは、ROM 2KB にしているから。

    • クロックは余裕だが、中身もない。それでも、GPR がネックになりそうな感じ。

      既に 472 スライスまでになってしまった。このうち IOR が結構占めている。
      IOR の規模:

        // Number of Slice Flip Flops: 136
        // Number of 4 input LUTs: 223
        // Number of occupied Slices: 175
        // Total Number of 4 input LUTs: 232
        // Number of bonded IOBs : 106
        // IOB Flip Flops : 71
        // Number of BUFGMUXs : 1
        // Number of RAMB16BWEs : -
        // CLK to Setup 6.264


    • IOB Flip Flops の一部 (71 - 24) は、LUT に移るはず。

      規模についての目標を後退させる。初版は、全部を入れて 50A で動けば良い。( 動かすアプリケーションは、SPI FLASH ライタ。)
      -- それでも厳しい。SPI は ソフト SPI で良いとしても USART 入れたいし、Core は、今 300 スライスぐらいなのが、1.5 倍にはなりそうだし。

      (2) -- 2/17 時点

      NOP を CPSE と一緒にして、GPR の クロックの扱いを少し変更。なぜか大分減った。

      (3) -- 2/20 時点 (rtavr-wk04)

      まだまだだが、スナップショットを取っておく。(追加3) まで。


    メモとか追記とか

    • Spartan-3A アプリケーションノート

      ここに、『APP228 - Virtex デバイスのクォッド ポート メモリ (英語版)』というのがあった、デザインファイルもダウンロードできる。GPRと似たようなものなので、なにか重要なヒントがあるかも知れない。チェックしておきたい。

      ちょっと見た分には、普通の verilog で記述したファイルもあった。見てみると CLK2X と CLK を入力にしている。GPR も最終的にはそうするのだろう。

      ちょっと思い立って、CLK の nagaedge / posedge と CLK2X の posedge の 3 つに分けてみたが、問題なく Implement できた。GPR の 次の版ではこれを採用しよう。

      あと、倍速ベースで『XAPP229 - 多ビット入出力ブロック メモリ』という使い方もある。

      (追記)見てみたら DCM のモジュール CLKDLL を使っていた。CLKDLL には、RST, LOCKED というのがある。-- なるほど、 PLL みたいにロックという概念があるわけか。あと BUFG/IBUFG とかも使っている。単なる 2 倍速なら これをマネすれば問題ないのだろう。あと 3 倍速とかはどうやるのだろう?

      あ、『XAPP462 - Spartan-3 FPGA におけるデジタル クロック マネージャ (DCM) の使用 (日本語版)』-- これか。

    • top level は、rtavr という名前にした。その中に 直接 S2 が入っていて S0/S1 はサブモジュール。(上記で説明したとおり)。

      これを元にして、kx_avr や narve のための top level を別ファイルで作ろうかと思う。インターフェイスがこれで良いかのチェックになるし、ROM/RAM/IOR を CORE と別にテストできる。

    • S1 を検討中だが、パイプライン制御が混乱してきた。ちょっと整理。まず、こいつは 2 word 命令がないので簡単になる。それを念頭において...

      • 分岐: 次の命令はロード済だから、分岐する場合は次のサイクルを無効にする。
      • 条件スキップ: S2 で決まるから 今のサイクルを 無効にする。
      • XH/YH/ZH WB 条件による 1 クロック挿入 (パイプラインストール)
        次の命令を遅らせる。=次のサイクルを無効にするとともに PC を更新しない。
      • Rd の更新 と X/Y/Z の競合による 1 クロック挿入 (パイプラインストール)
        S0 で検出できるので、先取りして 次のサイクルを無効にするとともに PC を更新しない。
        S0 では、PREDEC/POSTINC を 無効にする。

      まだまだある。がとりあえず整理。

      • wire s_invalid 今のサイクルは無効
      • wire s_stall PC , INST を更新しない

      • reg n_invalid < 1 次を無効にする
      • S0_CONFLICT : S0 での競合検出
      • WB_VALID : XH/YH/ZH WriteBack 保留中 (GPR からの指示)

      次に面倒そうなもの。

      • ICALL/RCALL -- 2 クロック
        (1) PUSH PCH を S2 に指示 , 次を無効にする , 分岐先を PC に。
        (2) PUSH PCL を S2 に指示 (INST は無効だが、別状態, PCL は覚えておく)

      • RET/RETI -- 3 クロック
        (1) POP PCL を S2 に指示, PCL はテンポラリ
        (2) POP PCH を S2 に指示
        次を無効にする , 分岐先(PCL/PCH) を PC に。
        (3) 無効

      • 割り込み -- 3 クロック
        (1) 準備 S0 を止める。
        (2) PUSH PCH を S2 に指示 , 分岐先(PCL/PCH)を INT_VEC に。
        (3) PUSH PCL を S2 に指示 (INST は無効だが、別状態, PCL は覚えておく)

      実際の処理の説明が適当になってしまった。が、ここでは状態の把握ができれば良い。

    なにか大変な設計ミスをしたんじゃないかという気がしてきた。

      条件スキップで、スキップする場合 当然ながら次の命令は実行してはいけない。一方 PREDEC/POSTINC は、S0_fetch で更新してしまうことにしている。一体どういうことが問題になるのか CPSE + LD Rd, --X について整理してみよう。

      S0 S1 S2
      CPSE
      LD Rd,--X CPSE << LD の PREDEC/POSTINC を確定
      LD Rd,--X CPSE << LD の PREDEC/POSTINC を実行
      << CPSE の 条件が確定
      LD Rd,--X

      下位バイトの PREDEC/POSTINC の実行は、サイクルの前半でやるので、後半でスキップすることが分かっても間に合わない。

      S0 S1 S2
      CPSE
      LD Rd,--X (x) CPSE << 次の命令をストール
      LD Rd,--X x CPSE << S0 再実行
      LD Rd,--X x
      LD Rd,--X

      どうやって制御するのが良いか分からないが、こういう風に動かないといけない。
      ストール が間に入ったら それをまたいで制御しないといけないわけだ。

      ただ、分岐一般は、S1 で決めてしまうので、ここまで面倒なことにはならない。それが救いだが、条件が前半で確定しないといけないので、性能のボトルネックになるかも知れない。

       -- なかなかに嫌な予感がする。本当に作れるのだろうか?
       -- と弱気になったが、考えてみれば 複数クロック命令と同じようなもの。もともと避けては通れない。

    • rtavr-wk04 (追加3) まで

      とりあえず、スナップショットを取った。

      今はパイプライン制御を入れるためのお膳立て段階。skip の判断はできるようになったし、フラグの値も生成するようにした。分岐系も周りを固めつつある。割り込みは未検討だが、PUSH/POP の基本操作もいれつつある。

      こうやって見ると、まだまだと言いつつ、結構作りこみが進んでいる。 コメントというかメモが大分入っているし、インターフェイスの占める割合が多いが、総規模 2074 行。結構なところまで来たものだ。

      (IOR の拡張をしないなら) 多分 3000行は超えない。気が済むまで実装したら、シミュレータにかけて命令を動かしてみたい。

    • (追加4) インターフェイス見直し

      メモリ (GPR/ROM/RAM) はすべて同期型で WRITE_FIRST となるよう記述を変更。WRITE_FIRST では、アドレスをラッチするように明示する。目的に沿うのでこれを採用。

      メモリが同期型になると、いままでのインターフェイスでは不具合が出る。実行アドレスの決定は S1 でしてしまうことにして、必要な信号も S1 に引き込む。

      インターフェイスが変わったし、ラッチが入り過ぎていたり、入っていない不安もあるので、図にしてみた。

      だいたいは大丈夫のようだ。が、もともとの想定と ステートがずれているような ... 。ROM/RAM のアクセスが非同期だと想定していたが、同期になったため、アドレスの確定が前のステートに移動した。

      あと、モジュール間インターフェイスは全部大文字で統一しているが、内部は プレフィックスも適当な状態。

      s2 に出力するラッチ類は、s2_ を付け、s1 向けに出力するラッチは s1_ を付けようかと思う。あまり ステートを 意識しない内部のラッチは r_ 。

      ラッチ類への入力は、r_ 向けには v_ を使う。s1/s2 向けにも v_ を使ったりするが、意識する場合は、v1_ / v2_ にする。

      i_ は、最初に書いたもので、wire と reg の区別もない。 上記のルールで変換していこうと思う。

      ... ところで、なにか基本的な間違いをしているような気がしてきた。

      ROM/RAM の定義では、あらかじめ アドレスを 設定して置いて posedge で READ or WRITE する仕様になっている。こういう定義だと READ は posedge 以降しか行われないし、WRITE は posedge までにデータを確定しておく必要がある。READ して同じアドレスに書き戻す(read-modefy-write) ということはできない。

      ROM/RAM 自体はそれで良いのだが、他の論理回路は、negedge で動かすべきなのに、posedge で動かしていた。全部チェックして書き換えないと。

      それに加えて、GPR の仕様が合わない。GPR は read-modefy-write が出来ることが前提。どうしたら良いか分からないものの書きなおさないと。

      • S0/S1 では、negedge にする。IOR はそのまま。ちなみに、IOR は、非同期読み出しできるから、CBI/SBI で期待する read-modefy-write は可能。
      • 分散RAM は非同期読み出しが出来るから、アドレスを negedge でラッチする

      なんか、これで良いみたいだ。あと、ROM の読み出しが 想定より遅れる。パイプライン制御で、なにか不具合が起きるかも知れない。ROM を二倍速にして、読み出しを早める手が使えるから 対策はある。必要かどうかパイプライン制御のとき気をつけよう。

    • CLK について。

      いんちき クロックとして、2倍速の CLK2X から CLK を生成しているのだが、これで良いのじゃないかと思えてきた。

      • negedge CLK2X から CLK を生成。
      • GPR は、posedge CLK2X で駆動。negedge CLK2X でアドレスをラッチしているが、一番早いタイミングなので問題ない。
      • その他では、CLK2X は使わず、negedge CLK か posedge CLK のみを使う。

      こうなっているから、安全じゃないかと。DCM の2段使用は、推奨されていないようだから、DCM は、33 MHz の水晶から、望む周波数を作るためだけに使う。DCM の機能をみると N/1.5 , N/3, N/5.5 , N/11 (N は 2 - 32) が出来る。 22MHz, 11MHz, 6MHz , 3MHz の N 倍だけが整数 MHz になりそうだ。

    • シミュレータについて

      そろそろ、シミュレータ も考えようと思う。とりあえず、論理シミュレーションだけで十分。

      ここを見て、Windows 版 がある Icarus Verilog を使ってみようかと思う。以前 GTKWaveも使っているし、多分なんとかなるだろう。

      ISE は重いからあまり使いたくない。シミュレータに移行したら、当面シミュレータだけを使いそう。

    • 割り込みについて

      top level で、ベクタに変換するようにした。割り込みラインを ベクタに変換し、全部の OR を INT_REQ と定義。あと、INT_ACK が 1 だと 割り込み原因を ベクタから逆変換し 割り込みラインをRST する。
      割り込みラインをRST すると割り込みラインが減り 新たな ベクタに変わる。

      top level で、こういう処理を入れるのは、割り込みラインの定義をしている所だから。定義が変わっても 変更は、実体がある IOR と top level だけにしたい。

      これらの信号は、モジュールの外に出しているが、S1 で対応したときに S1 に渡す。

    (続く)
    関連記事:
  • posted by すz at 22:01| Comment(0) | TrackBack(0) | AVR_CORE
    この記事へのコメント
    コメントを書く
    お名前: [必須入力]

    メールアドレス: [必須入力]

    ホームページアドレス:

    コメント: [必須入力]

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


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

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