まずは、ベース。
これをベースにする。テストに使う gcc は自分で作ったもの。問題はあるが なんとかごまかして使っていこうと思う。再現性の問題があるので、バイナリを固定する。具体的には以下のもの。
- AVR_Toolchain-20110409-win.tar.gz Windows 版ベース (27MB ぐらい)
- AVR_Toolchain-20110409-lin.tar.gz Linux 版ベース (25MB ぐらい)
- AVR_Toolchain-20110409-src.tar.gz パッチ集
これでも完成度は低い。が、当面 20110409 版で固定する。
20110409 版では、1word lds/sts に対応できた。ちょっとしたテストコードならなんとかなるレベルになった。あとはごまかして使う。
08 で変更したものは、INT1 , RAM window の追加。RAM/ROM の タイミング(1/4 read, 3/4 write) 、2word LDS/STS のスキップ。どれも テストはしていない。
あと iort40.h の整備。... 大体入れたいものは入れた。これ以上機能は追加しないで実際に動かす方に重点を移す。
ちなみに、RAM/ROM タイミング変更 + 2word LDS/STS disable を基本としようと思う。USART/SPI master と INT1 , Timer0 が一応使え 4KB のプログラムメモリがあって 680 スライス -- Spartan-3A の 50A に載る。
さて、ここまで楽しく作ってきたが テストはやっぱり面倒そうだ。一体どうしたら良いのだろう?
ちょっと案を考えてみよう。
ひとつは、他のシステムのテスト用ツールを借りる方法。-- 調べてみると simulavr がそれを持っている。
よくわからないものの、命令ごとに python のスクリプトが用意されていたりするようだ。だが、改造は必要そうだし python は苦手だし ... 最終手段と考えておこう。
もうひとつ考えてみた。
ある命令列を実行して、どこかの IOR を SBI/CBI する。そうすると、レジスタを ダンプするような test bench を組んでおく。で、正しい 結果と diff を取る。
INT0 を使って PORT に SBI して posedge で割り込みを起こせば良さそう。
正しい結果は、別のもので作る。たとえば 実際の AVR でも良い。テストできないのは、1word LDS/STS のみ だし、それは gcc では当面使えない。SBI/CBI すると割り込みを起こすようにしておいて、レジスタダンプを取り、ホストに転送するようにしておけば良い。
そう考えると テストコードは ひとつの .s として作り、SBI/CBI は マクロとして仕込むのが良さそうだ。.s は 基本 gcc で生成するが バグとかあるはずで、手で修正した .s で管理する。
あ、テストベンチでレジスタダンプを取るにしても 横から GPR をアクセスできない。... いや 本体と GPR を分けて テストベンチ上で統合すればできるか。... でも実際に割り込みを起こして 割り込みルーチンの中でダンプさせた方が楽か。ダンプの指示には PORT も使える。
メモリの内容をテストするなら、レジスタにロードして ダンプさせれば良いし、SREG や SP のテストも同様にできる。
もうひとつは、md5 とが gzip のような 圧縮・伸張 あと暗号化、ランダム。こういった データを変換するもの。正しく動いたかは出力を照合することで判断できる。
もっとも ダメならどこでおかしくなったのか調べる必要があるから、レジスタダンプのやりかたも併用することになる。
それと基本的な命令パターンの確認もやらないといけないだろう。どういう命令が動いたのか一覧表を作ってテストしてないものが分かるようにしないと。一区切りするタイミングがわからない。
追記: libgcc.a で使っていない命令
どうせ gcc だけでは 使う命令が少なくてテストしきれない。なんて書いたが本当だろうか?
gcc のコード生成を丹念に調べれば 使っていない命令は分かる。だが、コンパイラが libgcc.a を勝手に使うわけだから libgcc.a も gcc の一部と考えた方がよい。... というわけで libgcc.a で使っていない命令を調べてみた。
調べる方法:
avr-ar -x libgcc.a
avr-ld -r -o ../xx.o *.o
avr-objdump -d ../xx.o | ....
ar で全部バラして 1 つの オブジェクトにまとめなおす。それに対して ディスアセンブルて集計。
で見てみると 使っていない命令は、
NOP , ROR , BSET , CBI, SBI , BLD , RETI , SLEEP , WDR
だった。NOP,ROR 以外は 一般命令とは言えないようなもの。
ちなみに BSET は sei などの元の命令。( cli は使っているが、SREG をリストアしていて sei は使わない ) 。
あと 条件分岐命令は、brcc, brcs, breq, brge, brlt, brne , brpl , brtc の 8種類。命令としては、BRBC/BRBS の 2 つになるが、16 パターンのうち 8 パターンだけ使っていることになる。
次に、コード生成で現れる命令を調べてみたところ ..
上記のうち 使っている命令は、
NOP, ROR, BLD
WDR(__builtin_avr_wdr), SLEEP(__builtin_avr_sleep),
BSET(__builtin_avr_sei)
があった。ちなみに、__buildin_avr_xx が使えるのは、sei/cli/wdi/sleep/swap/delay_cycles (他 fmul 等もある )
bld/bst は、6bit の arithmetic shift right とか補助的に使われているようだ。
うまくパターンの組み合わせを引き出すのは難しそうだが、gcc のコードだけでいけるかも知れない。
テストプログラムの選定
最初は dhrystone を考えていたのだが、ここみると 2.5KB 以上の RAM が必要だそうだ。ソースをチェックしたが int で 51 x 51 の配列を使っている。
次に、以前 USBNIX で使った minicrypt を検討。minicrypt は、いにしえの UNIX version 7 で使われた 暗号のコード -- 簡略化された エニグマ -- を移植したもの。。。。だと思っていたのだが、過去記事みたら記憶違いだった。サイズの問題で入らないので方針を変更してでっち上げたものだった。
そのままだとグローバル変数のアクセスで gcc の問題に引っかかるので、修正が必要だった。それはともかく、出てくる命令の種類が少ないような ...
で、次に md5 。これは良さそうな気がするのだが、サイズもデカそうな... 今は minicrypt に md5 のエッセンスを混ぜられないか検討中。
md5 を簡略化してみたが、使う命令が 27 と少なかった。ううむ。
08A
- rtavr-wk08a.tar.gz
テストするためのフレームワークとして、INT0 を使うことにした。... が、良く考えたら INT0 は まだちゃんとテストしていない。
ISR(INT0_vect) {
bit_clear(PORTC, PC2);
}
int main() {
MCUCR = (3 << ISC00); // posedge
bit_clear(PORTC, PC2);
bit_set(DDRC, PC2);
bit_set(GIFR,INTF0);
bit_set(GIMSK,INT0);
sei();
nop();
bit_set(PORTC, PC2);
PINC;
PINC;
bit_set(PORTC, PC2);
nop();
nop();
return 0;
}
テストプログラムは、こういう風にした。PC2 に SBI することで INT0 の posedge で INT0 を発生させる。割り込み処理の方では、PC2 を元に戻す。
INT0 をソフトウェア割り込みとして使ったのは初めてなのだが、例外(フォルト)ではなく、あくまで割り込みなので、数命令ずれて割り込みが発生する。それを確認するために PINC; とか nop とかいれている。
実はこれをコンパイルするのが一苦労。add4 でようやく ちゃんとコンパイルできるようになった。
出来たコードの一部を載せるとこう。
0000005e <__vector_1>:
2f: 933f push r19
30: 932f push r18
31: b72f in r18, 0x3f ; 63
32: 932f push r18
33: e030 ldi r19, 0x00 ; 0
34: 98ea cbi 0x1d, 2 ; 29
35: 912f pop r18
36: bf2f out 0x3f, r18 ; 63
37: 912f pop r18
38: 913f pop r19
39: 9518 reti
00000074 <main>:
3a: ec80 ldi r24, 0xC0 ; 192
3b: bf8a out 0x3a, r24 ; 58
3c: 98ea cbi 0x1d, 2 ; 29
3d: 9ae2 sbi 0x1c, 2 ; 28
3e: 9a58 sbi 0x0b, 0 ; 11
3f: 9a60 sbi 0x0c, 0 ; 12
40: 9478 sei
41: 0000 nop
42: 9aea sbi 0x1d, 2 ; 29
43: b38b in r24, 0x1b ; 27
44: b38b in r24, 0x1b ; 27
45: 9aea sbi 0x1d, 2 ; 29
46: 0000 nop
47: 0000 nop
48: e080 ldi r24, 0x00 ; 0
49: e090 ldi r25, 0x00 ; 0
4a: 9508 ret
シミュレーション結果の一部はこれ。9AEA が INT0 を発生させる SBI 。次の B38B が PIN の変化を見るための IN 命令 (x2) 。この IN 命令 2 つを実行して 割り込みが受け付けられる。
実を言うとこの動作は、実際のAVR とタイミングが違う。まず PIN の読み込み。マニュアルには、PORT に out した次の命令で PIN から in しても反映されないと書いてある。nop を1命令入れないと正しい値は読めない。 ( 当然ながら PORT を in する場合は nop は不要 )
だが、RTAVR は nop は不要。周辺装置は postedge で動作するので PORT の変化は postedge 。これを PIN に移すのは、negedge 。次の PIN は その次の postedge でちゃんと読める。
ちなみに イメージの INST は S1 の状態。実行(S2) は 1クロックずれる。9ARA の sbi の実行の次のクロックで 割り込みの要求が出ているのだが、それが受け付けられるのは さらに 1 クロック後。なので、sbi から 2 命令すべって 割り込みが実行されている。
説明したのは、08A の動作で、実はバグがあった。INT0 の受けつけで不要なラッチがあって 無意味に 1 クロック遅らせていた。
あと INTF0 に 1 を書くと 割り込み原因が クリアされる仕様 になっていなかったので修正している。
あと、low lovel の受付とか 細かい仕様の違いは残っているが、直す予定はない。
これを元にして、レジスタダンプの テストベンチを作る予定。
あと ldd/std 命令を入れてみた。1word LDS/STS と競合するのは、変位が 32-63 の範囲。0-31 に制限すれば 競合を避けられる。
動くかどうかは分からないが、まずは規模を見積もるのが目的。50A 用の rtavr_defs.v に 追加すると 665 スライスが 682 スライスになった。... なんとかなるかも。
ldd/std も 1 クロックで動作させる。だが、実行アドレスの加算が 遅延を増やしそう。
対策のひとつとして、上位 3bit は加算しないことにした。気休めかも知れないが。これによって、8KB アラインをまたぐ加算は不可能になった。ROM のサポート範囲は 8KB までだし、問題は起きないはず。
あと対策として考えられるのは、ラッチする 前で 加算していたのを ラッチ後の出力に対して行う。使うまでに値が安定すれば良いから 余裕があるかも知れない。ただ、規模が増えたりすると面白くないし ... 実際に比べてみて決めるつもり。あと、ちょっと面倒 ステートが S1 → S2 に移動するから 状態用のラッチもいるし。
ちなみに avr-gcc には、 -mmorder1 とか -morder2 とかのオプションがある。同じようにして、ldd/std に対応させるようなオプションは作れそう。ただ binutils も 対応しないといけない。
binutils -- いじっていたら 1word lds/sts がサポートできそうな感じになってきた。これなら..というわけで バイナリを全部作りなおすことに。パッチも整理する。
暫定的に AVR_Toolchain-20110408 版を置いた。
rtavr-wk08a の tb_int0 ぐらいは問題なくビルドできる。tb_queen も rom_data が作れるようになった。
だが、
queen_r.c:(.text+0xa): warning: internal error: out of range error
queen_r.c:(.text+0x10): warning: internal error: out of range error
queen_r.c:(.text+0x70): warning: internal error: out of range error
queen_r.c:(.text+0x80): warning: internal error: out of range error
queen_r.o: In function `main':
queen_r.c:(.text+0x282): warning: internal error: out of range error
c:/avr_toolchain/bin/../lib/gcc/avr/4.4.3/../../../../avr/lib/avrtiny10\libc.a(printf.o): In function `printf':
/xhl1/as5/avr-libc-1.7.1-fix2/build2/avr/lib/avrtiny10/../../../../libc/stdio/printf.c:44: warning: internal error: out of range error
なんてエラーが出ている。まだまだ。
ところで、ldd/std のテスト。tb_008 の後ろに追加。

Z に 0x60 を入れて +2/+3/+4/+5 のオフセットで std して ldd しているのだが、動いている。実際の AVR は 2 クロックかかるが、こいつは (無条件で) 1 クロック。使いこなせれば強力かも知れない。
テストしていて思ったのだが、Tiny40 は 256B の RAM がある。なのに LDS/STS のアドレススペースは 7bit 。下位 128B しかアクセスできない。LDD/STD を完全に置き換えるなら 8bit 分確保できるのに。
これは! 今回作ったような 半分しか OFFSET できない LDD/STD を入れる構想がもともとあったのでは?
ldd/std のテスト(gcc)
ちょっと ldd/std を本当に使うかテストしてみた。
.global aaa
.type aaa, @function
aaa:
/* prologue: function */
/* frame size = 0 */
mov r30,r24
mov r31,r25
std Z+1,__zero_reg__
/* epilogue start */
ret
.size aaa, .-aaa
これは、-mmcu=rtavr40 -mreduced-lddstd -Os でコンパイルした場合。
引数を Z に移して +1 のアドレスに 0 を入れている。ちゃんと使ってくれている!
.global aaa
.type aaa, @function
aaa:
/* prologue: function */
/* frame size = 0 */
mov r30,r24
mov r31,r25
subi r30,lo8(-(1))
sbci r31,hi8(-(1))
st Z,__zero_reg__
subi r30,lo8(1)
sbci r31,hi8(1)
/* epilogue start */
ret
.size aaa, .-aaa
-mmcu=rtavr40 -Os だけだと、こう。 std Z+1,__zero_reg__ を 5 命令で置き換えている。最適化オプションを変えても無意味。なぜなら、この 5 命令が 1 単位になっているから。
せめて後ろの 2 命令は捨てれば良いのにと思うし、実際そういう最適化もあちこち入っているのだが... このケースには入っていないのかも。
AVR_Toolchain 20110409
out of range error が直った。これでいく。
08B
- rtavr-wk08b.tar.gz
上で書いた変更を反映した。 - tb_008 に ldd/std テストを追加。
- ldd/std の 変位の加算
あと、テストのためのプログラム tb_shuffle 追加。乱数のようなもので 種を元にデータを変更していく。演算の種類を増やしたものを適当に作ってみた。どこかでループしていたりするかも知れないが乱数自体が目的ではないので気にしない。
PC で検算できるようにしておいた。すくなくとも結果が同じでないとダメなわけだから 演算結果をダンプしてみようと思う。
08C
- rtavr-wk08c.tar.gz

tb_shuffle をテストしたが、shuffle_init からリターンするときに 0x555 に飛んでいってしまうというバグがまず見つかった。
調べたところ、
b4: 40f0 sbci r31, 0x00 ; 0
b5: 9508 ret
0000016c <shuffle>:
b6: 930f push r16
b7: 931f push r17
b8: 93df push r29
ここが該当する ところで、ret の後 push が続くと 起きるバグということが分かった。
push 命令が無効化されているのに v_ea_push が 1 になってしまうために、v2_ea の計算が push 用になってしまうのが原因。
08C ではこのように直った。
だが、2つ目のバグがすぐ見つかる。
次は、shuffle() のリターンで。0x25E に飛んでいくバグ。期待されるのは、0x05E。
これは、上位バイト のメモリ(0x162)に 0x62 が書き込まれている ためと分かった。
さらに調べると。ここで 0x62 を書き込んでいる。... ここまでは分かった。が、なぜなのかサッパリ分からない。gcc のバグかも知れないし。
... これは、AVR Studio のシミュレータでどうなるか追ったほうが良いかも知れない。
動き切って結果が違うのなら、実機を使ったり 、別のマシンで検証用の計算結果を作ったりすることができるが、アドレス計算がもとで動かないのであれば、その方法は使えない。
simulavr を使うという手もあるが、動ききりさえすれば、検証するだけで済むから、今回だけ なんとか凌ぎたい。手間からいうと AVR Studio のシミュレータの方がてっとり早そうだ。
こんなことが何度も起きるようなら、ちょっと方法を考えようと思う。案としては、simulavr をベースに トレースするようなものを作る。
実際に AVR Studio 4.18SP3 までインストールしてみたところ- SP3 は、"Simulator 2" で attiny40 をサポートしていた。
- 違うパスの avr-gcc を使うことができるようになっていた。
... というわけで、変更なしに シミュレータにかけることができることがわかった。ソースから生成しても 同じ HEX になることを確認して
Debug → "Start Debugging"
とすると Disassenbler リストが出てきた。この上で 動かしていくことができる。
まずは、走り切るのかどうか。- 1 回目の shuffle() call すら無限ループらしく戻って来ない。
どうも動かないものが出来てしまったようだ。途中まで テストはできるが ... その先が困ってしまう。なかなかに、厳しい。
ちょっとやってみよう。
0xc6 まで AVR Studio で動かしてみた。
これに対応する。rtavr のシミュレーション結果
ざっと見て、Stack Pointer が 0x106 で EA として出ている Z ポインタが 0x128 で一致していることは分かる。あと、この時点で 263 命令をこなしている。
ここから 1 命令づつ追っていけば .. どこかで正しくなくなるはず。 根気よくやれば、必ず分かるのである。... だが、どこまでやれば良いのか
もう少し進めて 0x220 で止めてみた。

X ポインタから r24/r25 をロードしているが、正しい値は 0x5c/0x42 だが、ロードしている値は、0x00/0x42 。SP/X は正しい値。
ここで既におかしくなっている。... のだが、実行命令数は 2780 。ちょっと多すぎる。やはりシステマティックにやらないとやってられない。
さて、どうしよう。- rtavr 側では、SP / SREG は いつでもわかる(ようにできる)。だが レジスタの内容は直接は分からない。
- 分かるのはせいぜい Rd を指定して書き込んだ値。
- メモリの内容もわからない。... が、書き込んだ値は分かる(ようにできる) 。
ここから考えて ...- rtavr.v から GPR と RAM を切り離し test bench の方で 組み込んで Watch できるようにする。
- あるいは、切り離さずに Watch するデータを output として test bench で見えるようにする。
- test bench では、$fdisplay() とかを使って Watch するデータを契機に ログを取る。
取るログは、SREG と SP あと レジスタに ロードした値 (+レジスタ番号) と メモリにストアした値。(+EA ) - このログに合わせたデータを シミュレータから取り出す。
- 検証は、形式を合わせて diff をとれば良い。
- こういうことをしたいなら、simulavr を使うのが 良さそうだ。... が、attiny40 には対応していないように見える。改造しないと。
- simulavr は、python 以外に TCL にも対応しているそうだ。それを使うという手もある。... だがどうせ改造するなら、ダイレクトに log を取るコードにしてしまうのも手かも知れない。
L r17 = 00 : PC 0013 FLAGS 02 SP 0000
L r29 = 3f : PC 0015 FLAGS 00 SP 0000
L r29 = 01 : PC 0016 FLAGS 00 SP 0000
L r26 = 00 : PC 0019 FLAGS 02 SP 013f
L r27 = 60 : PC 001a FLAGS 00 SP 013f
L r30 = 00 : PC 001b FLAGS 02 SP 013f
L r31 = 06 : PC 001c FLAGS 00 SP 013f
L r16 = 45 : PC 001d FLAGS 00 SP 013f
L r26 = 00 : PC 0024 FLAGS 02 SP 013f
L r27 = 60 : PC 0025 FLAGS 00 SP 013f
L r16 = 00 : PC 0026 FLAGS 02 SP 013f
S mem[0060] = 00 : PC 0028 FLAGS 2d SP 013f
S mem[0061] = 00 : PC 0028 FLAGS 2d SP 013f
S mem[0062] = 00 : PC 0028 FLAGS 2d SP 013f
S mem[0063] = 00 : PC 0028 FLAGS 2d SP 013f
S mem[0064] = 00 : PC 0028 FLAGS 2d SP 013f
S mem[0065] = 00 : PC 0028 FLAGS 2d SP 013f
S mem[0066] = 00 : PC 0028 FLAGS 2d SP 013f
S mem[0067] = 00 : PC 0028 FLAGS 2d SP 013f
S mem[0068] = 00 : PC 0028 FLAGS 2d SP 013f
S mem[0069] = 00 : PC 0028 FLAGS 2d SP 013f
S mem[006a] = 00 : PC 0028 FLAGS 2d SP 013f
S mem[006b] = 00 : PC 0028 FLAGS 2d SP 013f
こんな風にレジスタ、メモリの変化をログすることは出来た。ただし注意点があって、(1) PREDEC/POSTINC による変化はログされない。(2) PC は常に +1 の値。
面白いことに、bss のクリアは PREDEC/POSTINC を使っているので、(ログでは)レジスタの変化なしで連続にメモリに書き込んでいる。ループの終了判定もフラグだけ変化させる cpi/cpc なのでレジスタの変化なし。
改造した結果バグが入っていないことを検証するなら自己完結するのだが、正しいかどうか検証するなら他のもので同じデータを作らないといけない。
さて、検証用データをどう作ろう。... って simulavr でなんとかするつもりだが、別の方法もある。たとえば、kx_avr など他のコアを rtavr に組み込むとか。.. 想定のうちだからなんとかなるかも知れない。組み込めてしまえば、同じ test bench が使える。
さて、どっちが楽だろう?
あ、kx_avr は xilinx のプリミティブ使っているから無理。AVR Core は VHDL だし。... ちょっと厳しいか。
simulavr を見てみる。...
AVR Core の部分は、decoder.c のようだ。でこんな風に使っている。
opi = decode_opcode (opcode);
result = opi->func (core, opcode, opi->arg1, opi->arg2);
ざっとみたところ 命令は、16bit 長だから 65536 個の配列を作っておいて、そこに opcode に対応する関数を登録しているようだ。で、table 引き 1 回で済ましている。
decoder.c だけ使ってあと適当にでっち上げても動かせそうな感触。ちょっとやってみるか。
L r17 = 00 : PC 0013 FLAGS 02 SP 0000
L r28 = 3f : PC 0015 FLAGS 00 SP 0000
L r29 = 01 : PC 0016 FLAGS 00 SP 0000
L r17 = 00 : PC 0019 FLAGS 00 SP 013f
L r26 = 60 : PC 001a FLAGS 00 SP 013f
L r27 = 00 : PC 001b FLAGS 00 SP 013f
L r30 = 06 : PC 001c FLAGS 00 SP 013f
L r31 = 45 : PC 001d FLAGS 00 SP 013f
L r18 = ff : PC 001f FLAGS 00 SP 013f
S mem[060] = ff : PC 0020 FLAGS 00 SP 013f
L r17 = 00 : PC 0024 FLAGS 00 SP 013f
L r26 = 60 : PC 0025 FLAGS 00 SP 013f
L r27 = 00 : PC 0026 FLAGS 00 SP 013f
S mem[060] = 00 : PC 0028 FLAGS 00 SP 013f
S mem[13f] = 2b : PC 002c FLAGS 35 SP 013f
なんかでっち上げることが出来た。使わない命令は全部削ってしまって 1word LDS/STS と reduced LDD/STD に対応。IOR は SREG と SP のみ。当然割り込みもない。
フォーマットもすり合わせた。($fdisplay は fprintf とちょっと違うようだ)
あと PC は 2 進んでいるようだ。ROM のアドレスは 既に次の fetch アドレスになっている。
rom ファイルは、rom_data.mem があるのを前提にできるから 安易に fscanf で読み込む。
使い方は、
avr8l_trace [-l loops] [-f rom_file]
パラメータなしだと rom_data.mem を読み込んで 200 回実行する。
それは良いのだが、結果が全然違う。まぁどちらかがバグっているわけで、同じになるようにデバッグしていけば良い。必ず先に進めるわけでだいぶ気が楽になった。
2 行目 L r28 のレジスタ番号が違っているのは、レジスタ番号を適当に出力していたから。そもそも書き戻す番号は GPR から外に出ていない。.. 対応したら合った。
PC が +2 も進んでいると 見るとき混乱するし バグっているかも知れないので、test bench 側でラッチして正しい PC にすることにした。
4/6 行目 FLAG が 02 になっている。ldi が並んでいるところだし。これは.. ldi で Z flag を変化させていたバグ。CMD_OP3_WRF というのを前に作ったので同様に CMD_OP2_WRF を新設。
... とまぁこんな感じでデバッグが進む。
その後の動きが違うのは、decoder に提供したプリミティブ関数がバグったため。 - SP3 は、"Simulator 2" で attiny40 をサポートしていた。
08D
- rtavr-wk08d.tar.gz
すぐバグが見つかるのだが、ここまでを一旦スナップショット。
バグは、0x60 から連続でメモリに書きこんでいるところ。フラグが 0x2d になっているが 0x35 が正しい。
フラグは、cpc 命令での変化で、S と V が違う。V が違えば S も違うので V の計算が原因。
調べたら確かに間違い。修正できた。
次に 0x4e あたり フラグが 0x81 になっているが 正しくは 0xa1 。似た様な感じで SUBI での H flag の計算のバグ。
続く MOV で今度はフラグを変更してしまっている。これは、OP3_WRF の条件の間違い。
さて、これを直して diff を取ってみたら今度は SP がずれているのが見えてきた。
(avr8l_trace)
< S mem[013f] = 2b : PC 002a FLAGS 02 SP 013f
< S mem[013e] = 00 : PC 002a FLAGS 02 SP 013e
< S mem[013d] = 00 : PC 0038 FLAGS 02 SP 013d
< S mem[013c] = 00 : PC 0039 FLAGS 02 SP 013c
< S mem[013b] = 01 : PC 003a FLAGS 02 SP 013b
< S mem[013a] = 3f : PC 003b FLAGS 02 SP 013a
(rtavr)
> S mem[013f] = 00 : PC 002a FLAGS 02 SP 013e
> S mem[013e] = 2b : PC 002b FLAGS 02 SP 013d
> S mem[013d] = 00 : PC 0038 FLAGS 02 SP 013c
> S mem[013c] = 00 : PC 0039 FLAGS 02 SP 013b
> S mem[013b] = 01 : PC 003a FLAGS 02 SP 013a
> S mem[013a] = 3f : PC 003b FLAGS 02 SP 0139
AVR Studio に判定を仰ぐと 0x13f には 0x2b が 入るのが正しい。が、0x3b 終了時の SP は 0x139 が正しい。後者は、avr8l_trace でメモリに書く前に SP を変更しておかないと 表示が合わなくなるという問題。
前者には問題が 2つ rcall での HI-byte と LO-byte の積む順番が逆ということと、PC 。rtavr では PC を進ませているが、 avr8l_trace では、進ませずに一気に書く。PC は表示だけの問題だから rtavr に合わせるよう細工する。
これを直すと次は
> L r31 = 00 : PC 0041 FLAGS 00 SP 0139
39a41
> L r23 = 80 : PC 004b FLAGS 00 SP 0126
なんだろう?
実際のトレースは、
L r28 = 26 : PC 003e FLAGS 00 SP 0139
L r29 = 01 : PC 003f FLAGS 00 SP 0139
L r18 = 00 : PC 0040 FLAGS 00 SP 0139
L r31 = 00 : PC 0041 FLAGS 00 SP 0139 <<<
L r24 = c0 : PC 0045 FLAGS 00 SP 0126
L r23 = 80 : PC 004b FLAGS 00 SP 0126 <<<
L r16 = 26 : PC 004c FLAGS 80 SP 0126
コードはこれ。
3e: 51c3 subi r28, 0x13 ; 19
3f: 40d0 sbci r29, 0x00 ; 0
40: b72f in r18, 0x3f ; 63
41: 94f8 cli
42: bfde out 0x3e, r29 ; 62
43: bf2f out 0x3f, r18 ; 63
44: bfcd out 0x3d, r28 ; 61
45: ec80 ldi r24, 0xC0 ; 192
46: bf8a out 0x3a, r24 ; 58
47: 98ea cbi 0x1d, 2 ; 29
48: 9ae2 sbi 0x1c, 2 ; 28
49: 9a58 sbi 0x0b, 0 ; 11
4a: 9a60 sbi 0x0c, 0 ; 12
4b: 9478 sei
4c: 2f0c mov r16, r28
なるほど、cbi/sbi は ロードしてストアしているが、なぜか レジスタへのロードもしているわけか。
-- 修正。
次、
< S mem[0139] = 00 : PC 005a FLAGS 80 SP 0126
59: 2f90 mov r25, r16
5a: 8b4b std Y+19, r20 ; 0x13
LDD/STD がバグっていた。-- 修正。
次
< L r20 = 40 : PC 0104 FLAGS 99 SP 0106
---
> L r20 = 02 : PC 0104 FLAGS 99 SP 0106
... 値が全然違う。
103: 9555 asr r21
104: 9547 ror r20
105: 956a dec r22
これ、間違っていたので直そうとしたが、まだダメ。フラグは posedge で変化するので GPR が取り込むタイミングで値が変わってしまっている。ROR などで C_in を GPR が 取り込む場合 ラッチしておかないとダメなようだ。これは直すが、他に該当するものがないか気になる。
< L r27 = 00 : PC 010f FLAGS a1 SP 0106
< L r20 = 00 : PC 0110 FLAGS a1 SP 0106
< L r21 = 00 : PC 0111 FLAGS a1 SP 0106
---
> L r27 = 00 : PC 010f FLAGS a3 SP 0106
> L r20 = 00 : PC 0110 FLAGS a3 SP 0106
> L r21 = 00 : PC 0111 FLAGS a3 SP 0106
10d: 1fbb adc r27, r27
10e: 5aa0 subi r26, 0xA0 ; 160
10f: 4fbf sbci r27, 0xFF ; 255
110: 914d ld r20, X+
これは ... Z flag が立ってしまっているわけだが、SBC/SBCI は 前の状態が Z=1 でなければ 0 になってしまう仕様だった。... 知らなかった。--- 直した。
次
< S mem[0106] = 39 : PC 0138 FLAGS 80 SP 0105
< S mem[0105] = 01 : PC 0139 FLAGS 80 SP 0104
---
> S mem[0106] = 39 : PC 0138 FLAGS 80 SP 0105
> S mem[0105] = 00 : PC 0139 FLAGS 80 SP 0104
rcall での PUSH 。HI/LO 入れ替えた修正をしたが失敗。
これを直す前に... 気分転換に規模確認。
50A 用の設定で 711 ... 厳しくなった。が、動かす方が先。
修正の多くは、ロジックを増やすものではないので、あまり規模が増えるのを心配していないのだが、フラグの処理は別。ちょっと最適化を考えないと。
LDD/STD をあきらめれば 10 スライスぐらいは減るが、なんとか残したい。
次、
< L r22 = b8 : PC 024b FLAGS ac SP 0104
< L r23 = 84 : PC 024c FLAGS 8c SP 0104
---
> L r22 = b8 : PC 024b FLAGS b4 SP 0104
> L r23 = 84 : PC 024c FLAGS 94 SP 0104
24b: 0f66 add r22, r22
24c: 1f77 adc r23, r23
V flag (overflow) の計算式が add と sub で違った。前の修正は add 用を sub 用に変えただけだった。
08E
- rtavr-wk08e.tar.gz
行き詰まった。とりあえず、スナップショット。
< L r18 = b8 : PC 0249 FLAGS 94 SP 0104
< L r21 = 84 : PC 024a FLAGS 94 SP 0104
< L r18 = 38 : PC 0249 FLAGS 99 SP 0104
< L r21 = d0 : PC 024a FLAGS b4 SP 0104
< L r25 = d0 : PC 0255 FLAGS 82 SP 0104
< L r24 = 38 : PC 0256 FLAGS 82 SP 0104
---
> L r25 = 00 : PC 0255 FLAGS 82 SP 0104
> L r24 = 00 : PC 0256 FLAGS 82 SP 0104
247: ff80 sbrs r24, 0
248: c002 rjmp .+4 ; 0x496 <__mulhi3_skip1>
249: 0f26 add r18, r22
24a: 1f57 adc r21, r23
00000496 <__mulhi3_skip1>:
24b: 0f66 add r22, r22
24c: 1f77 adc r23, r23
24d: 1763 cp r22, r19
24e: 0773 cpc r23, r19
24e: 0773 cpc r23, r19
24f: f029 breq .+10 ; 0x4aa <__mulhi3_exit>
250: 9596 lsr r25
251: 9587 ror r24
252: 5080 subi r24, 0x00 ; 0
253: 4080 sbci r24, 0x00 ; 0
254: f791 brne .-28 ; 0x48e <__mulhi3_loop>
000004aa <__mulhi3_exit>:
255: 2f95 mov r25, r21
256: 2f82 mov r24, r18
257: 9508 ret
問題の場所。レジスタの変化がないのに、違うところが動いている。最後には <__mulhi3_exit>: には来る。
cp/cpc が見えるし、レジスタやメモリに変化がなくても SREGが変化したり や PC が +1 でないケースもログを取るようにしている。だがこんな感じなのだ。
ちなみに AVR Studio で 255 -- <__mulhi3_exit> まで実行させると 2679 サイクル。結構動いているかも。
AVR Studio で確認したら 247 の sbrs でスキップしないと 次の rjmp で 249/24a の次に飛ぶ。3 回目の sbrs で スキップして 249/24a が初めて実行される。r24 の値は 0x11 。
で、調べてみると skip 周りなにか変。そもそも s2 に入ってから skip することが分かっても遅い。副作用のある s0 があるから s1 で skip することが分かるようにするか skip 命令だと分かった時点で 副作用のある s0 を 止めておかないといけない。だが、現状そうなっていない。
skip する命令は 3 つ
ひとつはレジスタ。s2 のときにラッチするから s1 で分かる。あと CPSE 命令。Rd == Rr の条件だからこれも可能。
あとひとつは 、IO レジスタ。これは? 1bit バスをもうひとつ作るか 時分割? どちらも嫌だ。
一方、副作用のある s0 を 止める仕組みもある。だが、skip する場合 skip も遅らせないといけない。一応 2 word LDS/STS 用の仕組みはあるから拡張することはできる。
どうやら 副作用のある s0 を 止める方が楽らしい。
折衷案もあり得る。skip 命令を 2 種類作る。レジスタ系は S1 でやった方が規模が小さくなるような気がする。
... 検討してたはずなのに実装がおかしくなったのは、たぶん CPSE 命令に後で気がついたため。対応しようとして段々おかしくしていった気がする。
... というわけで じっくり検討しようと思う。
skip 命令は一ヶ所しかないようなので、ちょっと後回し。
次の問題は、 CPC で Z flag が立ってしまうもの。SBC/SBCI と同様だった。サイクル数 458。
次。ANDI/ORI で C flag を 0 にしていた。サイクル数 507。
次。BRTC で ブランチしない。C flag を 0 にしていた。サイクル数 828。
これは、rcall の後ろにある 無効化された スキップ命令が、BRTC を無効化してしまっていたため。
次。27b の com 命令のフラグが違う。サイクル数 1034。
81 が正しいが 99 。V flag が間違っている。COM は無条件に 0 。
次からは不定値が出だす。このまま続けてもどうせ int0 を起こして 結果が変わるので少しコードを変更。
次のコードは、上記のバグが出た所。
107: 2f94 mov r25, r20
108: 7097 andi r25, 0x07
265: 9508 ret
000004cc <__divmodhi4_neg1>:
266: f7f6 brtc .-4 ; 0x4ca <__divmodhi4_exit>
267: 9590 com r25
268: 9581 neg r24
269: 4f9f sbci r25, 0xFF ; 255
26a: 9508 ret
278: 955a dec r21
279: f7a9 brne .-22 ; 0x4de <__udivmodhi4_loop>
27a: 9580 com r24
27b: 9590 com r25
27c: 2f68 mov r22, r24
不定値は、00f1 の LD Z+ 。5 回目 で サイクル数 1244。アドレス(Z)は 0x130 。
どうも 書きこんでいないエリアを初めてアクセスしたようだ。RAM に初期値 0 を設定することでクリア。
08F
- rtavr-wk08f.tar.gz
次 4 回目の 0x260 の and 。結果が 1 なのに Z flag が 1 になっている。既に 2000 命令を越えている。
verilog シミュレータで該当箇所を探すのも億劫になってきた。
とりあえず、スナップショット。この問題もあるが skip 命令も修正しないと。
skip 命令を修正したら、仕切りなおして別記事にしようと思う。随分動くようになったし、テスト方法も新たなもの検討すべき時期になった。
バグも沢山出ている。場当たり的に直してきたが、一度整理して 同じようなバグがないか 調べてみる必要もある。
そういえば、avr8l_trace 仕様を変更した。-d オプションを付けると、フラグが変化したとき等のログを出力するようになる。ただ、これだと差分が大量にでるので標準にはしない。
(続く)
関連記事:
- AVR互換コアの仕様(メモ)
- AVR互換コアの仕様(その2)
- AVR互換コアの仕様(その3)
- AVR互換コア(その4)
- AVR互換コア(その5)
- AVR互換コア(その6 デバイス)
- AVR互換コア(設計メモ)
- AVR互換コア(テスト) (この記事)
- Tiny20/Tiny40
- AVR Toolchain
- AVR Toolchain (その2)
著作権について
ここで提示しているコードは正しく動作しないとはいえ、既に著作権は発生しています。
著作権は、すzが保持しており放棄はしていません。
教育目的および私用目的では、もともと著作権の範囲外なので自由につかえます。また、ライセンスとして、GPL を適用しています。GPL に従う範囲において 個別の許可なく使用することができます。許諾のための連絡も不要です。
なお、GPL なので、生成したバイナリを作り直せる範囲のソース開示が必要になります。FPGA だと 通常 チップ全体になってしまいます。また、開示したソースを GPL の範囲で再利用されることを妨げることはできなくなります。このコードを利用する場合この点に留意してください。
個別の許可を得れば、GPL 以外の条件での使用は可能です。が、作業中のものには許可は出さない予定です。作業が完了(もしくは中断)したとき、ライセンスは見直します。
なお、すでに公開してしまったものの、ライセンスを取り消すことはできないと考えていますが、ライセンスの追加は可能です。また、新しく公開するものについては、ライセンスしないことすら可能で、どのような制限もありません。変更する可能性がありますので、この点にも留意してください。