2011年04月21日

AVR互換コア(テスト3)

AVR互換コアのテストも 新たな段階に入った。最終目的は gcc で動くコアなので、avr-gcc のバグがネックになっている。もはやコア自体のデバッグではなく avr-gcc のデバッグがメイン。

    注意:ここからはAVR_Toolchain のバイナリも更新しながらになるので、Version を合わせないとこれから書くことが再現しないかも知れない。また、ソースコードに添付する rom_data も コンパイラが変わるのでバージョン毎に変わる場合がある。

まずは、テストのための AVR_Toolchain について。

    4/9 版 : rtavr-wk09c.tar.gz まで使用
  • AVR_Toolchain-20110409-win.tar.gz -- Windows 版 AVR_Toolchain (25.9 MB )
  • AVR_Toolchain-20110409-lin.tar.gz -- Linux 版 AVR_Toolchain (21.2 MB)
  • AVR_Toolchain-20110409-src.tar.gz -- AVR_Toolchain パッチ集 (同じものはバイナリにも添付)

    4/21 版 (アップデート) : rtavr-wk10.tar.gz のみ使用
  • AVR_Toolchain-20110421-src.tar.gz -- AVR_Toolchain パッチ集 (同じものはバイナリにも添付)
  • AVR_Toolchain-update-20110421-win.tar.gz -- Windows 版 AVR_Toolchain アップデート(5.8 MB)
  • AVR_Toolchain-update-20110421-lin.tar.gz -- Linux 版 AVR_Toolchain アップデート(5.1 MB)

    4/22 版 (アップデート) :
  • AVR_Toolchain-20110422-src.tar.gz -- AVR_Toolchain パッチ集 (同じものはバイナリにも添付)
  • AVR_Toolchain-update-20110422-win.tar.gz -- Windows 版 AVR_Toolchain アップデート(8.2 MB)
  • AVR_Toolchain-update-20110422-lin.tar.gz -- Linux 版 AVR_Toolchain アップデート(7.5 MB)

    4/24 版 (アップデート) : (-Os では影響なし)
  • AVR_Toolchain-20110424-src.tar.gz -- AVR_Toolchain パッチ集 (同じものはバイナリにも添付)
  • AVR_Toolchain-update-20110424-win.tar.gz -- Windows 版 AVR_Toolchain アップデート(5.8 MB)
  • AVR_Toolchain-update-20110424-lin.tar.gz -- Linux 版 AVR_Toolchain アップデート(5.1 MB)

    注意1) アップデートは cd AVR_Toolchain してから上書きで展開する。4/22 版は、4/9 版に当てても OK 。
    注意2) 4/24 版は、4/22 版を当てた上でに上書き。
    注意3) Linux 版 は、cc1/cc1plus に実行権が付いていないので chmod +X が必要。

  • その他のツール (覚え書き)

    Verilog シミュレータとして、『Icarus Verilog for Windows』-- Windows 版 setup (GTKWave 同梱) を使っている。Icarus Verilog は 結果を比較するのに必要。GTKWave も使うが結果比較がメインになっていて 補助的になって来ている。

    AVR Studio 4.18SP3 のシミュレータ は attiny40 に対応しているので これも併用。Xiliinx の ISE も Implement のチェックや Verilog の文法チェックに併用している。

  • avr8l_trace (rtavr ソースに添付 )

    AVR のプログラムを実行させて、メモリやレジスタに変化があったときに、フラグやPC, SP と共に そのデータ(+アドレス or レジスタ番号)をログするもの。

    テストベンチの方でも全く同じものを出力するようにしていて、2つのログを取って diff をかけ検証するのがもともとの目的。

    これからは、gcc のデバッグに使う。レジスタ・メモリの変化をログし、SLEEP 命令での終了時に レジスタ・メモリダンプを取るので、ログみて追跡が可能。

    これは、simulavr-0.1.2.6 のコア decode.c だけを取ってきて、でっち上げたもの。ライセンスは GPL 。

ここまでの経緯



    avr8l_trace と AVR互換コア rtavr のログを比較することで、コアのデバッグをしてきたが、前記事で書いたように 用意したテストプログラムは 完全に一致するようになった。

    ただ、テストプログラム自体は 正しく動いていない。tb_shuffle にある shuffle プログラムは、PC でも動かせ 正しい結果を得ることができるようになっている。この結果と avr8l_trace の結果が一致しないのだ。

    AVR がちゃんとしているなら、その原因は avr-gcc 。これからは、avr8l_trace を動かして gcc のオブジェクトを検証していくことがメインになる。

バグ発見



    shuffle プログラムの初期化 shuffle_init の結果もまた違っていた。


    0120: 42 81 22 0c 44 97 a7 39 a0
    0130: 00 00 00 00 00 00 00

    shuffle_init の結果はここなのだが、正しい結果は、

    0120: 42 39 81 f6 22 61 0c 38 44
    0130: 22 97 ff a7 23 39 a0

    int16_t の配列に初期値を書き込んでいるだけなのだが、まるで int8_t の配列に書くような結果になっている。

    st 命令での X/Y/Z レジスタの 増加が +2 ではなく +1 になっている ... と目星をつけて avr-gcc のソースを見たら。まさにそういうところがあった。

    AVR8L への対応は、オリジナルのコード or AVR8Lのコード という 構造になっている。変更点で st Z 命令 ( Z レジスタであることは 逆アセンブルすることで分かる ) という条件で探すことでバグはすぐ見つかった。

    これを直すことで、shuffle() 関数が return して来るようになった。shuffle() 関数をなんども実行して データをシャッフルしていくのだが、ループ 1 回目の結果は OK になった。

      avr-gcc での AVR8L への対応は 基本的に オリジナルのコードと同じ動作をするようにしている。なので、おそらくバグは少ない。ひょっとしたらこれだけかも知れない。

reduced-ldd/std 命令対応



    AVR8L への対応コードは かなりひどい。特に ldd/std 命令の代替コードがでかくなる。最悪は 1 命令が 5 命令になるが、普通でも 2 倍以上になるところがかなりある。

      ひどいと言っても、他の対応はあり得ない。AVR8L は、あくまでおまけ。通常の AVR コア用のコードに影響がある変更はできないだろう。 誰が設計しても多分こう作るはず。別のアーキテクチャとして設計すれば、話は別できれいなコードも出せるのだろうが、それをするだけの価値があるのかどうか?

    rtavr では、0-31 バイトの範囲だけしかアクセスできない 縮小 ldd/std 命令(標準は 0-63)を追加したので、これに対応することにした。

    対応といっても本質はたいしたことがない。オリジナルのコード or AVR8Lのコード の if 文を変えるだけである。(if 文の条件は結構面倒)

    対応したのは、14 ヶ所。 2 倍以上になるところに限定した。

      これらは、-mreduced-lddstd オプションを付けないと有効にならない。オプションを付けないと AVR8L の範囲のコードになる。

    以上の変更をしたのが、最初の変更である 4/21 版 (AVR_Toolchain-20110421)。

    これでテストをやってみることにする。

ver wk10



今回は、これからスタート。4/21 版を使い tb_shuffle での結果。

*** inst-count 34407 ***
*** gpr dump ***
r16:01 r17:27 r18:e1 r19:00 r20:0f r21:69 r22:37 r23:01
r24:2c r25:0b r26:37 r27:01 r28:26 r29:01 r30:17 r31:01
*** ram dump ***
*
0060: 15 00 0e 00 0d 00 0c 00 1b 00 0a 00 0d 00 12 00
0070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
*
0100: 00 00 00 01 e6 00 ea 62 25 d5 97 51 ac 3e 17 a2
0110: 26 a2 24 23 23 3f 69 08 00 17 01 80 4f 27 01 27
0120: 01 26 01 27 01 00 4b e2 6a 9b f9 f4 7b ca b2 57
0130: 72 66 ff 66 d3 2c 0b 80 4f 0f 3f 01 00 00 00 2b
****************


shuffle() を 16 回 call した結果。命令数は 34407 で今までとは桁が違う。これでも avr8l_trace と rtavr は結果が一致している。

0120: e2 6a 9b f9 f4 7b ca b2 57
0130: 72 66 ff 66 d3 2c 0b


    NORMAL ldd/std opt.
    138c2ff005f01fae0e780ef64dcc849b OK 2416 2074
    738a9ac281929bc25eb22b147db37878 OK 4909 4263
    54686801a3f07d4466e05ecce3c83b32 OK 7292 6338
    3bca3564801286aa41827b2ecd4476ac OK 9984 8710 NG
    d0847570a698d026c4705c4ca76b500e NG 12536 10938
    466a156a287445f2e712c2d2d1b960cc
    c7aa70cab6ca732c2854cf44f21b76e4
    e448582824c7b3ce0e36d20773db98ab
    52cd7f4a03e54d5cc398b943730ebc49
    7a6f419c754675e5e0fae738556c996b
    e5be69be8c04f2f4cb44811a6ab02e2b
    d53c90f4456ad096d1924ac4bf7c56c4
    702ab66b79efc91ef2704f6a9c9ef74c
    50e4625837e849a23e437df4b87c481c
    887aefbc940d6cc0de68e2caa357ee4e
    5b4451aeb62f0ac8fd0a494986751e02

16 回分の結果を先に載せるとこう。1 回目は合ったが、16 回目は違う。


1回目:
0120: 8c 13 f0 2f f0 05 ae 1f 78
0130: 0e f6 0e cc 4d 9b 84
2回目:
0120: 8a 73 c2 9a 92 81 c2 9b b2
0130: 5e 14 2b b3 7d 78 78
3回目:
0120: 68 54 01 68 f0 a3 44 7d e0
0130: 66 cc 5e c8 e3 32 3b
4回目:
0120: ca 3b 64 4d 12 80 aa 86 82
0130: 41 2e 7b 44 cd ac 76

3bca 3564 8012 86aa 4182 7b2e cd44 76ac
3bca 4d64 8012 86aa 4182 7b2e cd44 76ac
XXXX

5回目:
0120: 24 7f 70 01 98 a6 26 d0 70
0130: c4 4c 5c 6b a7 0e 50

d084 7570 a698 d026 c470 5c4c a76b 500e
7f24 0170 a698 d026 c470 5c4c a76b 500e
XXXX


細かく調べると 4回目まで合っている 5 回目の頭が違う。(マチガイ:後述)

    ldd/std 版もチェックしたが全く同じ結果。命令数は随分減る。1 割以上。rtavr は ldd/std 共に 1 クロックだから 実際それだけ速い。

    text data bss dec hex
    1286 0 16 1302 516 NORMAL
    1042 0 16 1058 422 ldd/std opt
    914 0 16 930 3a2 tiny2313

    サイズを比較してみると.. 2.5割も違う。tiny コアと比べて 1割増えるだけ。ROM は 8KB までだから サイズは重要。ldd/std に対応して良かった。


4回目 5 4 3 4 8 0 2 6
5回目 6 5 6 4 10 0 3 6
差異 1 1 3 0 2 0 1 0

shuffle 1 回は 8 回のループになっていて それぞれが、8 つのケースの switch 分のどこかを通る。

どこを通ったかの積算値はあって 正しければこういう配分。


0060: 05 00 04 00 03 00 04 00 08 00 00 00 02 00 06 00
0070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

0060: 06 00 05 00 06 00 04 00 0a 00 00 00 03 00 06 00
0070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

AVR では 0x60 から int16_t の配列に入っている。値は正しいから違うパスを通ったわけではない。
5 回目で通っていないところは、とりあえず関係ない。また 通ったところも過去に通っているから常に違うというわけではない。

コンパイラがこういう性質のコードをバグで出すとは考えにくく、libgcc のバグの方が濃厚。

    オリジナルは信用できる、AVR8L のコードは信用できないが、前のバグのように 違う場合は常に違うと思える。


0000048a <__mulhi3>:
000004b0 <__divmodhi4>:
000004d6 <__udivmodhi4>:

使っている関数は 3 つだけなのでこれらをチェックしてみよう。

    これら 3 つの関数は すべて libgcc.S -- アセンブラなのだった。バグが出そうなのは

  • レジスタの重複使用 または存在しないレジスタへのアクセス

      r0 - r15 は存在しない。これを使うアセンブラだと、avr-as でエラーになるのだった。

  • __tmp_reg__ , __zero_reg__ の扱い (レジスタの重複使用 or 0 に戻さない)

      __tmp_reg__ , __zero_reg__ は r18/r19 に define している。r0/r1 や Atmel のコード での定義 r16/r17 だと思ってコードを作ると重複してしまう。

  • 引数のレジスタ渡しの 数が減ったことに対応していない。(r18/r19 を使う等)

      独自仕様として r18/r19 を __tmp_reg__ , __zero_reg__ に割り付けたが、もとは 第四引数。割り付けなくとも 使っていたら 要注意なのだ。また、r18/r19 を 使ってしまっても __tmp_reg__ , __zero_reg__ を使わないなら問題ない。といっても __zero_reg__ を使ってしまったら return 前に clr しておく必要がある。

    まぁこういうところ。ちなみに、92-gcc-4.4.3-avrtiny10-fix2.patch で libgcc.S も幾つか直しているのだが、全部はみていない。

    改めてみてみると 未対応のコードが沢山ある。... 直すの面倒だが 意識的に使わないことが難しいから、直さざるを得ないだろう。

    __mulhisi3 : ToDO
    __umulhisi3 : ToDo
    __mulsi3: 引数 4 対応はしたが未確認
    __udivmodsi4: 引数 4 対応はしたが未確認

チェックしたが、上記 3 関数は問題なさそう。... となると gcc か。ちょっとばかり気が重い。

5 回目の最初だけが違うというのはひとつのヒント。

c = (*(state + 1) >> 4) & 0x7;

この c で switch していて シフトする元の値は 4 回目の 2 個目 4d64 だから c は 6 。

case 6:
out[i] = (*state / (16-i)) * b;
nop();
break;

... やっぱり 割り算/掛け算が怪しい。これだけ取り出して確認するか。

バグ発見2



    よおくみたら、4 回目の 2 loop 目 で既に違っていた。この値は 次の 1 loop 目に影響する。-- 調べるところが違っていたわけだ。

    4 回目 loop 2回目の 結果は次のようになった。

    lc 003 i 1 *state = 6801 b = a3 c = 7 result 5d65 (3564)
    84 d0 70 75 98 a6 26 d0 70 c4 4c 5c 6b a7 0e 50
    6 5 6 4 10 0 3 6

    case 7:
    out[i] = (*state) * 3;
    nop();
    break;

    動いたコードは、もっと単純で x3 しているだけ。

    これと同じ入力で __mulhi3 を実行してみると結果が違うことが分かった。

    結果:
    __mulhi3 : 0x0003 (正: 0x3803)
    bufi[1] : 0x2565 (正: 0x5d65)

    どうも、上位バイトが おかしい。そういう目で libgcc.S を見てみたら...あった!

    #if defined (__AVR_TINY__)
    subi r_arg1L, lo8(0)
    sbci r_arg1L, hi8(0)

    あと一ヶ所 同様のバグがあり、直したところ ... shuffle() , N-queen 共に動くようになった。

    N-queen :
    命令数
    DEPATH = 4 2006
    DEPATH = 5 5974
    DEPATH = 6 19236
    DEPATH = 7 66824
    DEPATH = 8 252463

    DEPATH = 8 で、完全に一致。

    shuffle も ループ 100 回を試す。

                      命令数
    91a970f2f7442a4a4dca2e464b62dd4c 225580
    71 65 100 88 207 66 104 99

    0120: a9 91 f2 70 44 f7 4a 2a ca
    0130: 4d 46 2e 62 4b 4c dd

    91a9 70f2 f744 2a4a 4dca 2e46 4b62 dd4c
    91a9 70f2 f744 2a4a 4dca 2e46 4b62 dd4c


    これも、完全に一致。

    さて、実を言うと このテストは、-mreduced-lddstd オプションで ldd/std 命令を使っている。( 4/21 版より ldd/std を使うパターンを増やしている。)

    で、オプションなしを試したところ.... 全然動かない。

バグ発見3



    幸いなことに、『動くコード』と『動かないコード』がある。『動くコード』を『動かないコード』に近づければ、どこかで動かなくなるわけだから、機械的にバグを見つけられる。

    やってみたところ。

      < subi r28,lo8(-(1))
      < sbci r29,hi8(-(1))
      < ld r24,Y+
      < ld r25,Y
      < subi r28,lo8((1)+1)
      < subi r29,hi8((1)+1)
      ---
      > ldd r24,Y+1
      > ldd r25,Y+2

    ここにバグがあるということが分かった。

    -- subi/sbci でないといけないのに subi/subi になっている。バグとわかって眺めてもすぐには気がつかなかった。

    で、hi8() に対して subi しているところを探すと実に沢山あった。(当然ながらすべて AVR8L 対応のコード。ベースにそんなコードはない。)

    以上のバグをまとめて、update を作る。今回は、オプションの部分ではないので、avr-libc まで作り直さないといけない。

    パッチ集は用意できた。
    今回の追加パッチ:

    81-gcc-4.4.3-avrtiny10-bug2.patch
    82-gcc-4.4.3-avrtiny10-bug3.patch
    95-gcc-4.4.3-avrtiny10-lddstd2.patch

    あと 4/21 版更新。

    92-gcc-4.4.3-avrtiny10-fix2.patch


    これで大分使い物になったはず。... といっても libgcc や avr-libc の対応は大変なので ちゃんとするには 時間がかかる。

    全部自前のコードで 32bit 乗算を使わなければ、たぶん大丈夫というレベル。

    これからの計画

    gcc は、これで終わりにしたいが... __mulhisi3 / __umulhisi3 など libgcc.S ぐらいはなんとかしておこうと思う。avr-libc は量が多すぎなので、ボチボチやる。

    ようやく avr-gcc も使えるようになったから、AVR互換コア のテストも、もう少しやる。

    その後は、FPGA ボードの組み立てとかツール作りで、しばらくお休み。

    FPGA が使えるようになったら、AVR互換コアを使って FLASH を読み書きするファームウェアを作る。それの完了で 晴れて AVR互換コア完成。

    まだまだ道のりは遠い。

10A



  • rtavr-wk10a.tar.gz

    Windows 版バイナリも用意できたので確認してみる。

    shuffle

    inst-count
    normal -mreduced-lddstd
    5 loop 13164 11206
    100 loop 264330 225580

    91a9 70f2 f744 2a4a 4dca 2e46 4b62 dd4c
    -0120: a9 91 f2 70 44 f7 4a 2a ca
    -0130: 4d 46 2e 62 4b 4c dd
    -0120: a9 91 f2 70 44 f7 4a 2a ca
    -0130: 4d 46 2e 62 4b 4c dd

    N-queen :

    normal -mreduced-lddstd
    DEPATH = 5 10241 5974
    DEPATH = 6 32872 19236
    DEPATH = 7 114609 66824
    DEPATH = 8 434083 252463

    これからは、これらのテストがルーチンワーク化してくる。AVR互換コアを ちょっと変更したら確認することになるからだ。

    で、ループの上限とか テストによって変えたかったりするし、あちこちのファイルを変更するのは面倒でしかたがない。

    それで tb_xx を tests の下に移動しパラメータを統一的に扱えるようにした。

    パラメータは tests/build.inc に集約。tests で make することでテストする。

    make の引数は、個別のテスト用に shuffle quteen tbi 。
    全体では all clean 以外に rom rom_clean check など。

      ちゃんとサポートしたのは、tb_shuffle, tb_queen, tbi_001 。あとは移動しただけ。

    make で新たに使ったコマンドは diff , head 。結果を比較し、その先頭 (最大 20行)だけ表示するのに使う。

    ところで、notyet なものがいくつかある。

    • rtavr_alu.v

      ALU をモジュールとして分離 するつもりはあまりないのだが、規模を見るためにちゃんと作っておこうか悩み中。(分離してしまうと フラグの計算に GPR_DI が使えず規模が増える見込み)

    • rtavr_intcnt.v

      割り込みコントローラだけを分離したものを 既にテストベンチで使っている。こちらは分離しても良いかなと思っていたりする。

    • rtavr_gpr16w.v

      失敗したまま放置中。かみきさんの記事によると トリプルポートの分散RAM というのは ISE で合成してくれるらしい。ただし、dual-port の倍のリソースを使うらしい。多分 書き込み専用 1 , 読み出し専用 2 になるから インデックスレジスタのことを考慮すると 16bit になる。今の 4 倍 64 個の LUT を使うのだろう。

      それでも、インデックスレジスタと Rd の同時書き込みはできないからパイプライン制御は今と同じ。

      ちょっと面白くないので、実際にクロックのボトルネックが分かって改善することになるまで保留しておく。失敗したコードはもう削除したい。

    • rtavr_ior_timer16.v

      16bit のタイマー1はやっぱり欲しい。用意はしているのだが、IOR 空間への割付けと 16bit アクセスの方法のところで 悩み中の後、放置。

    • rtavr_ior_twi.v

      いずれ作るつもり。これはテンプレートで中身はまだない。

    どうも Version 1 になった後も notyet は残りそう。

libgcc.S : __mulhisi3 / __umulhisi3 と __mulsi3

    __mulhisi3 / __umulhisi3 (libgcc.S) を見てみた。

    乗算のないコアでは、両方とも 最後に __mulsi3 に rjmp している。

    __mulsi3 は、32bit の乗算。引数が 4 バイト x 2 なので 普通のコアでは r19/r18 も引数になっている。
    AVR8L では、2 つめの引数全部 r21/r20/r19/r18 がスタックになる。

    これの対処は、処理の前に r19/r18 に スタックから LD してきて最後に r19 を CLR 。

    in r30,__SP_L__
    in r31,__SP_H__
    subi r30, lo8(-3);
    sbci r31, hi8(-3);
    ld r_arg2L, Z+
    ld r_arg2H, Z+
    ld r_arg2HL, Z+
    ld r_arg2HH, Z

    :
    #if defined (__AVR_TINY__)
    clr __zero_reg__ ; clear zero_reg
    #endif
    ret

    4/22 版ではこうなっている。

    まずは、試してみよう。


      int32_t t_mul32(int32_t a, int32_t b) {
      return (a - 1) * ( b - 1 );
      }

      main側
      buf32[0] = t_mul32(0x1234, 0x5678);


    結果は 0x062597b5 になるはずだ。

    実行した結果は、

    0060: 4e eb 65 5d 01 00 00 1e b5 97 25 06 00 00 00 00
    == == == ==

    確かに合っている。

    次に __umulhisi3 rjmp までのコードはこれだけ。

    // mov_l r22, r20
    // mov_h r23, r21
    // clr r24
    // clr r25
    // clr r20
    // clr r21

    上位 2 バイトを それぞれ 0 にする。

    それはすぐ分かるが問題は引数。普通のコアでは、r21/r20 と r19/r18 に uint16_t で入っているようだ。
    たぶん r19/r18 だけがスタックに入っている。

    対処は、最初に r19/r18 をロードしてきて、rjmp 前に r21/r20/r19/r18 をストアする。
    r19/r18 の値は変更しないから、skip 。r21/r20 の CLR は必要なく r24 をストア。

    #if defined (__AVR_TINY__)
    in r30,__SP_L__
    in r31,__SP_H__
    subi r30, lo8(-5);
    sbci r31, hi8(-5);
    st Z+, r24
    st Z , r24
    #else
    clr r20
    clr r21
    #endif

    これで良いはず。

    __mulhisi3 は u が付かないから signed で マイナスの 対処がある。

    // mov_l r18, r24
    // mov_h r19, r25
    // clr r24
    // sbrc r23, 7
    // dec r24
    // mov r25, r24
    // clr r20
    // sbrc r19, 7
    // dec r20
    // mov r21, r20

    これはもう面倒なのですなおに、ロードしてストアで対処する。

    #if defined (__AVR_TINY__)
    in r30,__SP_L__
    in r31,__SP_H__
    subi r30, lo8(-3);
    sbci r31, hi8(-3);
    ld r18, Z+
    ld r19, Z
    subi r30, lo8(1);
    sbci r31, hi8(1);
    #endif
    ()
    #if defined (__AVR_TINY__)
    st Z+, r18
    st Z+, r19
    st Z+, r20
    st Z, r21
    clr __tmp_reg__
    #endif

    ロードしたとき Z は元に戻す。

    ただ元のコードを見ると ...いきなり r19/r18 を潰している。
    乗算ありの場合は、r19 を使っているし。たぶんもともとバグっている。

      この2つの関数は、他と記法が違うし取ってつけたような感じ。int16_t 2つなら 第一,第二引数用の r25/r24/r23/r22 を使うのが普通なのに そうでないみたいだし。よく分からない。

      普通に uint16_t, int16_t の乗算をすると __mulhi3 が使われる。結果を 32bit にしたら __mulsi3 。... 固定小数点とか特殊用途向け?

        mov_l r22, r20
        mov_h r23, r21

        最初の mov は、これでないとつじつまが合わない。とりあえず直しておく。


      追記: mulhisi3 がどのように使われるか分かった。

      (define_expand "<any_extend:u>mulhisi3"
      [(set (reg:HI 18) (match_operand:SI 1 "register_operand" ""))
      (set (reg:HI 20) (match_operand:SI 2 "register_operand" ""))
      (set (reg:SI 22)
      (mult:SI (any_extend:SI (reg:HI 18))
      (any_extend:SI (reg:HI 20))))
      (set (match_operand:SI 0 "register_operand" "") (reg:SI 22))]
      "!optimize_size"
      "")

      これは、avr.md での定義。重要なのは、!optimize_size -- 要するに -Os では決して使われない。そして -Os しか使うつもりはないから ... どうでもよいということ。

      あと、(reg:HI 18), (reg:HI 20) (reg:SI 22) というのが見える。引数が変則なのはこのせい。それは良いが (reg:HI 18) を引数に使えない AVR8L ではどうなるのだろう? 使うつもりはないが気になる。


      t_mul1.c:41: error: unrecognizable insn:
      (insn 11 10 12 3 t_mul1.c:39 (set (reg:HI 18 r18)
      (reg:SI 47)) -1 (nil))
      t_mul1.c:41: internal compiler error: in extract_insn, at recog.c:2048

      こうなった。なるほど無理なものは無理。なら、"!optimize_size" を "!optimize_size && !AVR_TINY" とすべき。で、__umulhisi3 / __mulhisi3 自体は不要。

10B



  • rtavr-wk10b.tar.gz

    テスト環境の整理と小変更。

    • PORT_HAVE_PIN_WRITE (50A デフォルト化)

      PIN に WRITE すると PORT が反転する機能を デフォルトにした。(+ 4 slices)

    • PW_SLEEP

      SLEEP 中 であることを示す PW_SLEEP を常に出力。(+ 0 slices)

    • PRC_WATCH_WDR

      WDR も PRC_WATCH_WDR を define することで PW_WDR を 出力 (+4 slices)

    PW_WDR を LED につなげれば、目視で確認できるかも知れないし PRC_WATCH から分離して独立させた。

    あと、PW_WDR のバグ(typo)修正。他にも 1 つ typo 修正。

    さて、遅延の調べ方が少し分かったので調べてみたら、意外なことに IOR からの読み込みがボトルネックだった。

    GPR を外してみて調べると EA が出力されてから GPR_DI が確定するまで 22ns もかかっている。
    IOR 側だけで調べると、レジスタ値が変化してから DO が確定するまでの時間だった。

    IOR は、POS EDGE で変化するが、基本同じクロックでは読み込まない。... と思っていたが違った。タイマーをはじめいろいろある。セレクタだけの 非同期なのがマズイようだ。

    RAM のように 2X クロックで駆動して、1/4 でラッチするとかしないとダメなようだ。いっそのこと DCM 前提で 4 相化したほうが良いような気がしてきた。

    ... と書いたが、レベルセンシティブなラッチを試してみることにした。

    always @(CLK , DO_async)
    begin
    if (~CLK) r_do = DO_async;
    end

    こんな記述。CLK = L の間は従来どおりだが H になった時点で固定する。

      ラッチを使うと Warning が出て不安なので、『XST ユーザガイド』を見てみた。これは、case 文や if 文の 記述ミスで バグが出やすいので意図的に出しているとのこと。機能的に問題があるわけではなさそう。

      always @(CLK or DO_async)
      begin
      if (~CLK) r_do = DO_async;
      end

      あと、『XST ユーザガイド』の例では always の指定で信号を or でつないでいる。, を or に直そうと思う。それか 1/4 CLK ( CLK2X posedge で CLK == 0 )で 取り込むか。


    DOラッチ ラッチなし
    Total Number Slice Registers 399 391
    Number used as Flip Flops 391 391
    Number used as Latches 8 -
    Number of 4 input LUTs 1,101 1,113
    Number of occupied Slices 684 687
    Total Number of 4 input LUTs 1,138 1,149
    Number used as a route-thru 37 36
    Number used for Dual Port RAMs 16
    Number of bonded IOBs 12
    Number of BUFGMUXs 2
    Number of RAMB16BWEs 3

    規模が増えるのを心配していたのだが、なんと減った! 

    たぶん セレクタ(LUT)ばかりで FF を使っていないところに FF を入れた結果になったのだろう。

      現状、IOR のセレクタは、モジュール単位と上位レイヤの 2 段階のセレクタになっている。階層をなくした方が 効率が良いような気がする。ただ、レジスタの数は数十個あって面倒。

    この修正で、IOR 自体のボトルネックは、PORT 出力になった。12ns ぐらいだから気にしなくて良くなった。

    では、再度 GPR を外した調査。

    ボトルネックは GPR_DI で同じだが、演算系に変わった s_smd_alu_sub_all, s2_cmd_load, s2_cmd_op1 がトップ 3 でどれも 17.5 ns ぐらい。3/4 CLK で 確定している必要があるから 42.8 MHz ぐらい。

    では、GPR はどうなのだろう? 調べてみると INDEX が 16ns 弱。

    INDEX は、3/4 CLK から GPR を読み込んで 0 or -1 したのを セレクタにかけて 4/4 CLK で EA としてラッチ。 これが 16ns だと 15.6 MHz CLK でしか動かないことになる。セレクタ以降が入っていない値だからもっと遅くなるかも知れない。

    これは.. よくしらべないと。

      少なくとも、PAD からの入出力には余分に時間がかかる。距離も遠くなるし。16ns からいくらか割り引いても良いようだ。

    実際のところよく分かっていない。.. のだが、 JA に関係するところでまずそうなところを直した。

    • JA, INDEX の算出の 簡易化

      もともと v_DOBL, v_DOBH という共通の 結果を使っていたのだが、JA, INDEX では余計なものが入っている。

      v_DOBL, v_DOBH とかを展開して、余計なものを省いた 式に変更。... これは少し規模が増える。

    • フォワーディングするかどうかの判断。

      FORWARD_TO_BL,BH というのがあって、addr をラッチして比較を後でしている。addr をラッチするぐらいなら、そのタイミングで条件そのものを ラッチした方が良い。

      ... これは少し規模が減る。

    • その他。

      DOBH は、CLK の negedge で ドライブしていたのだが、ややこしいので、CLK2X の negedge で ドライブするように変更。

      r_clk 削除。冗長なのでヤメ。


    もうひとつ、無駄を見つけた。

    wire [7:0] v_DOBL = FORWARD_TO_BL ? DI2 : s1_DOBL;
    wire [7:0] v_DOBH = FORWARD_TO_BH ? DI2 : gpr[r_addrb];

    DI2 を使っているが、これは 書き込み用だった。インデックスレジスタの 更新値も含んでいる。
    ただの DI で良かった。

                                 変更後   変更前(DI)  変更前
    Total Number Slice Registers 392 392 399
    Number used as Flip Flops 384 384 391
    Number used as Latches 8 8 8
    Number of 4 input LUTs 1,100 1,106 1,101
    Number of occupied Slices 680 683 684
    Total Number of 4 input LUTs 1,136 1,143 1,138
    Number used as a route-thru 36 37 37
    Number used for Dual Port RAMs 16 16 16

    結果として規模は 4 減った。

10C



  • rtavr-wk10c.tar.gz

    まずは、上記のことを反映

    大胆な変更をしても make だけでチェックできるのは便利。以前なら小変更でもバグを入れたかどうかの確認が大変だった。

    さて、IOR のアドレスデコードを 上位レイヤと各モジュールの二段階でやっているのを 上位レイヤで全部やることにした。

    結構な変更量があるが、この際やっておきたい。いずれ Spartan-6 にも載せるし、どのようにセレクタを組むかの制限をなくして ISE まかせにするほうが良さそうだ。

    ただ、50A 用では規模が増えるかも知れない。余裕がある 今が入れるチャンス。


    変更後 変更前
    Total Number Slice Registers 392 392
    Number used as Flip Flops 384 384
    Number used as Latches 8 8
    Number of 4 input LUTs 1,077 1,100
    Number of occupied Slices 668 680
    Total Number of 4 input LUTs 1,113 1,136
    Number used as a route-thru 36 36
    Number used for Dual Port RAMs 16 16


    ... なんて思ったのだが、規模は減った。(-12)

    現時点のフルスペックの IOR では、当然ながらさらに減る。
    IOR だけ Implement してみると...


    変更後 変更前
    Total Number Slice Registers 326 326
    Number used as Flip Flops 318 318
    Number used as Latches 8 8
    Number of 4 input LUTs 543 606
    Number of occupied Slices 409 450
    Total Number of 4 input LUTs 565 637
    Number used as a route-thru 22 31
    IOB Flip Flops 48 48
    Number of BUFGMUXs 1 1


    なんと -41 。一割近い。6 input LUT がある Spartan-6 ではもっと効果があるはず。

    ところで、RAMWIN がなにか変。最初 RAMWIN も enable にしていたのだが なにかバグっているらしいので外して比較した。

    Spaartan-6 (xc6slx9-2ftg256) で確認。同じ IOR のみ。

    変更後 変更前
    Number of Slice Registers 326 326
    Number used as Flip Flops 318 318
    Number used as Latches 8 8
    Number of Slice LUTs 466 427
    Number of occupied Slices 190 181
    Number of LUT Flip Flop pairs used 541 501
    Number of bonded IOBs 162 162
    IOB Flip Flops 48 48

    増えた。何故? 現状 define IOR_LARGE_MAPPER で切り分けられるようにしている。コードが二重になっているから 修正するとき二ヶ所直さないといけないので前のコードを消そうと思ったが、これは残さないと。


    変更後 変更前
    Number of Slice Registers 405 392
    Number used as Flip Flops 397 384
    Number used as Latches 8 8
    Number of Slice LUTs 852 822
    Number of occupied Slices 316 325
    Number of LUT Flip Flop pairs used 924 903
    Number with an unused Flip Flop (531) (525)
    Number with an unused LUT ( 72) ( 81)
    Number of fully used LUT-FF pairs (321) (297)
    Number of RAMB16BWERs 3 3
    Number of bonded IOBs 12 12
    IOB Flip Flops 48 48
    Number of BUFG/BUFGMUXs 2 2

    ついでに 50A 用構成の 比較データを残しておこう。... 今度は減った。あと、FF の数が増えている。+13 .. これは何だろう? 同じ値になる FF を作った?

    それはともかく、316 スライスで済むのか。... Spartan3 の 半分以下。

      ロジックセル スライス 乗算器
      (換算) ブロックRAM DCM
      50A 1584 704 6K x 9bit 3 2 501 円
      200A 4032 1792 32K x 9bit 16 4 1014 円
      LX9 9152 1430 64K x 9bit 16 4 1286 円

    200A が 1792 スライスで 668 消費。LX9 が 1430 スライスで 316 消費しているわけだ。

    200A は、2.68 個入り、LX9 は 4.53 個入る。200A の 2倍にもならないわけか。性能や価格差からいうと それでも買いだが。

    wk10c は、ここまで。
      その後の変更:
    • IOR_DRIVEN_CLK2X

      IOR のレベル・センシティブ ラッチを 1/4 CLK で取り込む同期型にする。規模は 668 で変わらない。
      どちらが良いか分からないのだが、 レベル・センシティブ ラッチだと Warning が沢山でるので、これをデフォルトにする。

    そろそろ、いいか -- とは思うのだが、性能を知りたい 。いったい何 MHz までいけるのだろうか? へたな分析では、全然わからない。40 MHz はクリアできたのだろうか?


GPR 高速版



    実際のところ より高クロックで動くのかどうか分からないのだが、2倍速をやめたやつを作ってみた。テストは通るようだが、まだちゃんと検証していないので soc/notyet に置くことにする。(10C にも置いてあるが全然だめ)

    基本的な考え方は、上位バイトと下位バイトの 2 つの組に分けて、すなおなアクセスにする。

    rtavr_gpr_16w rtavr_gpr_16
    Total Number Slice Registers 392 392
    Number of 4 input LUTs 1,124 1,077
    Number of occupied Slices 712 668 (+44)
    Total Number of 4 input LUTs 1,190 1,113
    Number used as a route-thru 66 36
    Number used for Dual Port RAMs 80 16 (16 x 5)

    やってみたらこうなった。44 スライスの増加 -- そして Dual Port が 5 倍!

    上位バイト
    gpr_hi[r_wb_addr] WRITE or INDEX
    gpr_hi[s2_addrbh] Rr_in (or alt INDEX)
    gpr_hi[s2_addrbl] Rd_in
    下位バイト
    gpr_lo[r_wb_addr] WRITE
    gpr_lo[r_addral] INDEX
    gpr_lo[s2_addrbh] Rr_in (or alt INDEX)
    gpr_lo[s2_addrbl] Rd_in

    アクセスは、こうなっている。ポートが増える毎に Dual Port 1 個分増えるようだ。-- もともと 2 になっている所に + 3 ポート で計 5 個分。

    ちなみに 上位バイト と 下位バイト が同じにならないのは、そういう設計だから。(下位バイトは実際に 4 つ同時にアクセスするケースがある。)
    あと、Dual Port 1 個 は 16 個のレジスタになるから、本当は 32 個使える。KX_AVR とかが 64 になっていたのは、こういう理由。

      分散RAM とポート数の関係について、不思議に思っていたのだがようやく分かった。

    さて、作っていて GPR_DI (= Rd_out) の フォワーディングの処理が不要だと分かった。分散RAM は WRITE FIRST なのだ。むしろ ラッチが必要になるところが出た (INDEX)。

    そうなると .. 2倍速の GPR も事情は同じだ。FORWARD_TO_BH は常に 0 で良い。それと、バグ (ではあるがあまり深刻ではない問題)に気がついた。

    • バグ: インデックスレジスタの下位バイトへは フォワーディングされない。

      ldi r30, lo8(val_a)
      ldi r31, hi8(val_a)
      ld r20, Z

      このコードは動くのだが、

      ldi r31, hi8(val_a)
      ldi r30, lo8(val_a)
      ld r20, Z

      これは 2倍速版では 動かない。-- いままでそんなことが起きたことはないのは、そのようなコードは gcc は出さないから。必ず lo0 - hi8 の順で使う。アドレス計算では必然だし、変数の アドレス取得でも lo8 - hi8 の順になっている。直すには 検出して パイプラインストールさせれば良いのだが、規模が増える。

      下位バイト
      gpr_lo[r_wb_addr] WRITE (r30)
      gpr_lo[r_addral] INDEX (r30)
      gpr_lo[s2_addrbh] alt INDEX (r31)
      gpr_lo[s2_addrbl] Rd_in (r20)

      ちなみに 最後の ld r20, Z が 4 port 使おうとするケース。-- 実際には、 alt INDEX は、r31 なので アクセスされないが、GPR はそんなことは知らないのだ。



    変更後 変更前
    Total Number Slice Registers 391 392
    Number of 4 input LUTs 1,063 1,077
    Number of occupied Slices 667 668
    Total Number of 4 input LUTs 1,099 1,113
    Number used as a route-thru 36 36
    Number used for Dual Port RAMs 16 16

    フォワーディングの件、修正してみると、-1 減った。規模よりは、JA/INDEX の 条件が減るのが嬉しい。

    チェック+ストールのコードも作ってみた。

    N-queen クロック数
    STALL_WB_CONFLICT_1 7600  : WBする命令 + INDEX 命令でストール
    STALL_WB_CONFLICT_2 6985 : LO8 に WB する命令 + INDEX 命令でストール
    STALL_WB_CONFLICT_3 6319 : r26/r28/r30 に WBする命令 + 対応する INDEX
                   命令でストール
    チェックなし 6319

    (実行命令数 5974 )

    STALL_WB_CONFLICT_3 に引っかかる組み合わせは存在しない。STALL_WB_CONFLICT_2 のチェックは簡単なのだが、1割もストールで遅くなる。(命令数は同じだから クロック数の差が ストール回数)

    入れるのなら、STALL_WB_CONFLICT_3 だが、実際これに引っかかる命令は出ないだろう。で、規模だけ増える。... なかなか悩ましい。

    ところで、STALL_WB_CONFLICT_2 は、高速版にも 有効だ。このケースがなくなれば、下位バイトの INDEX のポートと WRITE のポートを 共有できるのだ。


    高速版 GPR (rtavr_gpr_16w) 修正後 修正前
    Total Number Slice Registers 392 392
    Number of 4 input LUTs 1,117 1,124
    Number of occupied Slices 710 712
    Total Number of 4 input LUTs 1,182 1,190
    Number used as a route-thru 65 66
    Number used for Dual Port RAMs 64 80

    で、やってみた。Dual Port RAMs は 64 と想定どおりにできたが、規模はあまり変わらない。それで、1 割クロックが増える。

    なら、元のコードの方が良い。

10D



  • rtavr-wk10d.tar.gz

    上記のことを反映。少々ややこしいので、ifdef 関係の説明から

    rtavr_defs.v :

    • STALL_WB_CONFLICT_1 -- WBする命令 + INDEX 命令でストール

      調査専用

    • STALL_WB_CONFLICT_2 -- LO8 に WB する命令 + INDEX 命令でストール

      基本調査用 ただし、rtavr_gpr_16w.v の GPR_TUNE_2 (後述) を使う場合は必要。

    • STALL_WB_CONFLICT_3 -- r26/r28/r30 に WBする命令 + 対応する INDEX命令でストール

      標準の rtavr_gpr_16.v を使う場合は基本必要。ただし、avr-gcc では これが必要になるパターンのコードは出ない。

    rtavr_gpr_16.v ローカル define :

    • GPR_TUNE_1 -- v_DOBL,v_DOBH を展開し不要セレクタを削減

    • GPR_TUNE_2 -- FORWARD_TO_BH を常に 0 相当にする。

      なぜか dual port が +8 の 24 になる。

      rtavr_gpr_16w.v ローカル define :

    • GPR_TUNE_1 -- gpr_lo の port 共有化で port 数削減。
      (STALL_WB_CONFLICT_2 の define 必須)

      規模がほとんど削減されない上に パイプラインストールの確率が上がり 10% 遅くなるため、推奨しない。

    tests ビルド用パラメータ build.inc
      build.inc :
    • ROM_HDLS, ROM_HDLS_PT

      使用する gpr の選択。

      rtavr_defs.temp :

      上記 define はこちらを修正する。

    規模について

    rtavr_pgr_16.v : (3) (2) (1)
    Number of Slice Flip Flops 391 391 391
    Number of 4 input LUTs 1070 1066 1068
    Number of occupied Slices 672 670 668
    Total Number of 4 input LUTs 1107 1102 1104
    Number used as a route-thru 37 36 36
    Number used for Dual Port RAMs 24 24 16
    Number of bonded IOBs 12 12 12
    Number of BUFGMUXs 2 2 2
    (3) WK10D 標準
    (2) - STALL_WB_CONFLICT_3
    (3) - GPR_TUNE_1/GPR_TUNE_2

    STALL_WB_CONFLICT_3 のチェックは重くなかったので標準にした。+2 だけの増加で最終的に 672 スライス。

    Dual Port RAM用 LUT が 24 になっている。GPR_TUNE_2 を入れたら +8 になった。+8 ということは 4bit 分だけ 3 port ということ。FORWARD_TO_BH を外すだけだから、port を増やす必要などないはずだが .. 規模には影響しないから増やしたのかも。

    rtavr_gpr_16w:
    Total Number Slice Registers 392
    Number of 4 input LUTs 1,133
    Number of occupied Slices 714
    Total Number of 4 input LUTs 1,198
    Number used as a route-thru 65
    Number used for Dual Port RAMs 80

    rtavr_gpr_16w は、STALL_WB_CONFLICT_3 を外している。最終的に 714 スライス。なんとか規模を減らせないかと試行錯誤したが、これで FIX 。

    rtavr_gpr_16w は、単純化した版として性能の調査に欲しかったのだ。あと、仕様の説明にも使えるし。

    これで Version 1 までに作りたいものは、一応作った。

Slowest paths について

    rtavr_gpr_16w を使い、PW_WATCH を define して 時計アイコンの 『Analize Post-Place & Route Static Timing』で 『Slowest paths』を見てみた。

      PW_WATCH を define すると 重要な信号線を外部に output として出力する。テストベンチで使うのが目的だが、『Slowest paths』では、外部に出した信号しか対象にしないようなので、define することにした。

    最も遅いのは、GPR/s2_DOBL - PW_SREG(Z Flag) で 20.679 ns 。

      s2_DOBL は Rd の値でラッチしたデータ。posedge CLK2X で常に ラッチしている。有効なのは 3/4 CLK (- 1/4 CLK)。PW_SREG は SREG で タイミングは posedge CLK で 2/4 クロック。

      以下 S Flag/V Flag と続く。

      E2.O1 net (fanout=2) 2.064 PW_SREG_1_OBUF
      E2.PAD Tioop 3.996 PW_SREG_1_OBUF

      この 6.060 ns 余計だから引くと 14.619 ns 。3/4 CLK の期間しかないから 最大周波数は、51 MHz 。

      SREG を取り込むタイミングを 3/4 クロックにすれば、まるまる 1 CLK になる。

    フラグに混じって GPR/s2_DOBL - PW_LOAD_REG がエントリしていて 19.078 ns 。

      D7.O1 net (fanout=7) 2.406 PW_LOAD_REG_7_OBUF
      D7.PAD Tioop 3.996 PW_LOAD_REG_7_OBUF

      これらを引いて 12.676 ns 。タイミングは posedge CLK2X で 3/4 クロック。

      こちらはまるまる 1 CLK の猶予があるから 最大は 78 MHz 。

    これらに続くのは、GPR/s2_DOBL - PW_STORE_MEMS で 14.347 ns 。これは、メモリに書くデータ。

      B4.O1 net (fanout=17) 3.297 PW_STORE_MEM_1_OBUF
      B4.PAD Tioop 3.996 PW_STORE_MEM_1_OBUF

      これを引くと、7.054 ns 。これは、1 CLK まるまるだし 他と大分差がある。

    さて、SREG を取り込むタイミングぐらいは、簡単に変更できる。`ifdef IOR_DRIVEN_CLK2X で SREG/SP はメモリなどと同じタイミング の 3/4 での取り込みにすることにした。この修正をしてもタイミングは変わらなかったので 最大は、68.4 MHz 。

    ちょっと甘めかも知れないが、gpr_16w を使えば規模は増えるがこれぐらいで動くということだ。

    2倍速の gpr_16 では、gpr_16 自体がボトルネックだから これだけを Imlement して見る。

    最も遅いのは、r_addrb - INDEX(hi8) で 13.704 ns 。


      E10.O1 net (fanout=1) 1.251 INDEX_9_OBUF
      E10.PAD Tioop 3.996 INDEX_9_OBUF

      同じようにこれを引くと 8.457 ns 。

      r_addrb は negedge CLK2X でラッチして、INDEX はセレクタ と加算(LDD/STD) を介して negedge CLK でラッチ。

      INDEX はセレクタ と加算(LDD/STD) での遅延も大きそうだ。仮に 0 としても最大周波数は 59.1 MHz でボトルネックなのは間違いない。

    いろいろやってみたが、INDEX → EA の遅延がよく分からない。そもそも LDD 命令の加算値など S0 でも分かる。S0 → GPR で受け渡して S1 では関与しないというのが良さそうに思えてきた。

    これをやることにしたのだが、その前にやることがある。

    ldi r30, lo8(val_a)
    ldi r31, hi8(val_a)
    ld r20, Z

    こういうコード -- r31(Z の hi8) を更新したすぐ後に Z を使う -- はわりと多いのだが、フォワーディングすることになり、ボトルネックになる。値の取り込みは 3/4 クロックだから EA のラッチまで 1/4 CLK しかない。値が変わらないなら 2/4 CLK の期間が使える。gpr_16w でも事情は同じで 4/4 CLK の期間が使えるのが、値が変わると 1/4 CLK になってしまう。

    • STALL_WB_CONFLICT_3 -- r26/r28/r30 に WBする命令 + 対応する INDEX命令でストール

    これを

    • STALL_WB_CONFLICT_3 -- r26-r31 に WBする命令 + 対応する INDEX命令でストール

    • STALL_WB_CONFLICT_4 -- r26/r28/r30 に WBする命令 + 対応する INDEX命令でストール

    という風に定義を変える。

    N-queen クロック数
    STALL_WB_CONFLICT_1 7600  : WBする命令 + INDEX 命令でストール
    STALL_WB_CONFLICT_2 6985 : LO8 に WB する命令 + INDEX 命令でストール
    STALL_WB_CONFLICT_3 6416 : r26-r31 に WBする命令 + 対応する INDEX
                   命令でストール
    STALL_WB_CONFLICT_4 6319 : r26/r28/r30 に WBする命令 + 対応する INDEX
                   命令でストール
    チェックなし 6319

    (実行命令数 5974 )

    性能への影響はあるが 1.5 % 程度。


      最も遅いのは、s1_DOAH - INDEX_12 で 10.087 。s1_DOAH は、PREDEC した後のラッチ で 3/4 CLK で取り込み。

      T10.O1 net (fanout=1) 0.475 INDEX_12_OBUF
      T10.PAD Tioop 3.996 INDEX_12_OBUF

      これを引いて、5.616 ns 。1/4 CLK でこれだと、後はセレクタを 0 としても 44.5 MHz 。

      ちょっと変更して ラッチ しない版で試すと r_addrb から 15.136 ns 。例によって引いた値は、10.659 ns 。これは 1/2 CLK で 46.7 MHz 。

      ちなみに、チューニングも合わせてやっていて、今の版は ラッチあり 681 スライス/ なし 685 スライス。ただし、(新)STALL_WB_CONFLICT_3 でないと動かないものになっている。


新たなボトルネック

    gpr_16w で、

    Minimum period: 25.546ns{1} (Maximum frequency: 39.145MHz)

    なんてのが出ている。CLK2X の周波数だろうから 19.6 MHz 。上記と差がありすぎる。-- こうなってしまう理由は何だろう。

  • 内部で生成した CLK の 遅延が大きい?

  • 2 clock で 1set なのを ISE が理解してくれない?

  • 他のボトルネックを見逃していた?

    どうも Default period を見ないといけないようだ。Default period には、CLK2X と CLK があるが、値を眺めると 39.145MHz は CLK の周波数に思える。

    CLK の方は、18.769 ns がボトルネックで s2_sbix_bit - r_pc 。要するにフラグのフォワーディング関係が重いということらしい。

    じゃぁ.. ということで、FLAGS_TUNE という define を作り、今まで考えてきた高速化を入れてみることにした。

    • FLAGS 計算の元になる Rd_out を GPR_DI から ALU_OUT に変更。(セレクタ削減)

    • di_is_zero を GPR_DI から ALU_OUT と OP1_OUT から作るように変更。(セレクタ削減)

    • CPSE で使う di_is_zero は、GPR で生成。

    • OP3 グループから CPSE と NOP を削除 (GPR_DI を使わなくなったので不要)

    これで、gpr_16w が 44.205MHz になった。スライスは +1 のみ。

    次のボトルネックでは、s2_cmd_op2 - Z - s0_inv_jump/r_sleep というものらしい。s2_cmd_op2 - Z はちょっと置いておいて、s0_inv_jump をチェックしてみよう。

    あと、r_pc のデコード。条件がかなり多い。整理できる一部のものは、S0 に持っていくのが良いのかも知れない。r_pc も 演算しているものがあるが、これも S0 で計算してラッチしてしまえる。ちょっと検討してみよう。

    NEXTPC_TUNE という define を作り次の 4 つを S0 でラッチしてみたところ 48.924MHz になった。

      PD_EXT PD_BR PD_JUMPS PD_SKIPS

      PD_JUMPS , PD_SKIPS は、いくつかの命令の グループ。r_pc のセレクタを間引くのが目的。
      PD_BR は、早く判断したいため。
      PD_EXT は、PD_BR を作るついで。

    ただし、規模は、+15 スライス。50A に入れるのは厳しい。

    次のボトルネックは、SBIX_BIT_IN 。これは、Rd を bit 指定で読み込むもの。これは、Rd の出力と同時に作れそうな程のもの。もともと GPR での Rd == Rr の出力と 同様に作ろうかと考えていたもの。

    作ってみたが、関係なかった。関係あるのは、IOR の SBRI の方。

    IOR にラッチいれたから対応できる。SBRI_TUNE で 49.193MHz 。

    で、ボトルネックは変わらず。... s2_skip という状態を reg 3 つから 作っているところがあって セレクタを減らすために reg 化できるので、NEXTPC_TUNE に追加。

    .... もうぼちぼち良いかという感じになってきた。40 MHz弱 を 50 MHz にするのは容易かったが、それ以上は難しそうだ。1 つボトルネックを潰しても、同程度のものが沢山あって、複数潰して 1 段階上がるという雰囲気。それに 最初に立てた 40 MHz - 50 MHz という目標性能ぴったりではないか。

    あとは、整理。2倍速版でも データを取って どの設定がバランスが取れているかみていこう。

(続く)
関連記事:

著作権について

    ここで提示しているコードは正しく動作しないとはいえ、既に著作権は発生しています。
    著作権は、すzが保持しており放棄はしていません。

    教育目的および私用目的では、もともと著作権の範囲外なので自由につかえます。また、ライセンスとして、GPL を適用しています。GPL に従う範囲において 個別の許可なく使用することができます。許諾のための連絡も不要です。

    なお、GPL なので、生成したバイナリを作り直せる範囲のソース開示が必要になります。FPGA だと 通常 チップ全体になってしまいます。また、開示したソースを GPL の範囲で再利用されることを妨げることはできなくなります。このコードを利用する場合この点に留意してください。

    個別の許可を得れば、GPL 以外の条件での使用は可能です。が、作業中のものには許可は出さない予定です。作業が完了(もしくは中断)したとき、ライセンスは見直します。

    なお、すでに公開してしまったものの、ライセンスを取り消すことはできないと考えていますが、ライセンスの追加は可能です。また、新しく公開するものについては、ライセンスしないことすら可能で、どのような制限もありません。変更する可能性がありますので、この点にも留意してください。
posted by すz at 22:49| Comment(0) | TrackBack(0) | AVR_CORE
この記事へのコメント
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント: [必須入力]

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


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

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