つーわけでこちらは JTAG経由のダウンローダな件。
転送速度は TP240 + wiggler clone で 4kB/s ほど。
けっこうメモ書き長いので使うだけなら途中ぜんぶすっとばして下の「ダウンローダ」の項へ。
これをくみあわせるとまず、
さて。メモリに読み書きできるようになったら、ようやく
ADuC7019/702x のフラッシュの消去単位は 512 バイト。 512 バイトずつメモリに送りこんでは、それをフラッシュに書き込むプログラムを実行、 という風にしてフラッシュに書き込むことができる。 こっちの方法でおよそ 3kB/s 〜 4kB/s になった。
... というのが表書きだが、最初のレジスタの読みかきのタイミングについて一言。
「こうしないとうまくいかん。なんでやねん?」的コメントに満ちあふれていたりする ──
ldr r0, [r1]
については、
EXEC_Instruction(ldr_r0_r1);
EXEC_Instruction(nop);
EXEC_Instruction(nop);
EXEC_Instruction(data);
EXEC_Instruction(nop);
てな具合に、通常 ldr 命令をパイプラインに挿入した 3 サイクル後にデータを挿入するが、
ldr より前のパイプラインが詰まってると、挿入タイミングが後ろにずれる。
実際、たまにレジスタに 0xE1A00000 がロードされてる例を見かけた。
ldr のタイミングミスで、後続の NOP のビットパターンをロードしてしまってるんだな。
事実上 ldr は
EXEC_Instruction(nop);
EXEC_Instruction(nop); // パイプラインフラッシュ。
EXEC_Instruction(ldr_r0_r1);
EXEC_Instruction(nop);
EXEC_Instruction(nop);
EXEC_Instruction(data);
EXEC_Instruction(nop);
こういう形で使う。
ldr r0, [r1]
において、ロードする瞬間には暗黙のうちにアドレスバスに r1 が出力されるが、
この r1 の下位 2 ビットが 0 でない場合 (r1 が alignment してない場合)、
データバスに挿入した値を realignment してから r0 ロードにしてくれるので、
値が変わる (下位2ビットが 1/2/3 の各々について 8/16/24 ビット分だけ右ローテートした値がレジスタに入る)。
ただしく alignment されてることがわかってるのは PC だけなので、事実上 ldr は
ldr r0, [pc] の形でしか使えない。
なお、str 命令のほうはアドレスに使うレジスタのバウンダリに無関係に正しい値が読みとれる。
何事かさせるのにいちいち二セット用意するのは面倒すぎるので、さすがにマニュアルでも
「ARM 命令セットに移る THUMB 命令を挿入し、そっちでお仕事。 復帰する時はまた THUMB から復帰する命令を挿入すればええ」と書いてある。丁寧に最小セットも明示されている。
とっても言い訳っぽいが、実際ただの言い訳だと思う。 arm v5 以降では THUMB に居ても JTAG から挿入するのは ARMセットになった。 ついでに ARM ⇔ THUMB 間を渡り歩くサブルーチンコールが一命令にまとまって PC の扱いが綺麗になった。 ARM ⇔ THUMB interworking 的には arm v6 がとっても羨ましい。
arm v4t での実装は ARM から THUMB への復帰の時の PC のズレを(他のレジスタを非破壊のままで)補正するのが とてつもなくめんどい ... どーせダウンローダでは関係ねー、復帰先はリセットベクタで ARM セットだしぃと言う具合に今回は投げた。わはは。
フラッシュに書き込むと、書き込み動作中は busy フラグが立って、書き込み動作終了後に 異常終了か正常終了のフラグが立つ、ことになっているが、 割り込みを使わない場合の実際の挙動はこんな感じ:
いろいろ試行錯誤したがうまくフラグが捕まらないので、結局 20us を待つだけにして、
正常書き込みになったかどうかはベリファイに任せた。
消去のほうはというと、1 ページ(512 バイト)あたりで 20ms も待つことになっているが、
データ 1 ページ分を RAM に送り込むのには
どーせそれ以上の時間(50ms)がかかるのでこちらも一々ステータスはチェックしていない。
したがって ROM を 0x00000 に張り付けた状態で 0x00000000 に飛べばリセットに ... ならなかった。
もっともハマった点だったりする。 jtager で restart --pc=xxxx などとして止めた場所からの継続実行だとなんだか動くのに、 単なる restart (0x00000000 からの実行。リセット相当ということになっている)すると暴走する。PC の調整をまずったかと思った。
MMU を持たず、特権についてほとんど記述がない CPU 相手だったのが傷を深くした。 jtager が 0 へのジャンプをリセットと同一視していていかにも「これで良いんですっ」風なのも。 OS 使うやつももうすこし面倒見が良いしなぁ。
リセットからの初期化シーケンスだけは、スーパパイザモードにいないといけない。 スタートアップは割り込みハンドラや未定義命令違反などでのスタックの調整を含む。 ユーザーモードからは出来てはいけないことなのでただちに特権違反でシステムモードに入り、 しかるのちそのまま暴走する。
リセットするには 0x00000000 に飛ぶのではなく ちゃんと RSTSTA 叩いてソフトリセットしましょうという話。
逆に言うと、普段使うときも Keil や gcc 付属の startup.s では PC が 0x0 を嘗めるのがリセット(or watchdog) の代わりにならず、暴走する。 手持ちのコードではリセットルーチン冒頭で自分がスーパパイザモードでなければソフトウエアリセットするように直した。
Reset_Handler:
mov r0, #0x80000000
mov ip, r0, asr #15 /* 割り込み関連 MMR = 0xffff0000 */
mov r0, #0
str r0, [ip, #0x08] /* 全割り込み禁止 ← これが特権違反にならねーってのがなんとも... */
MRS R0, CPSR
AND R0, R0, #0x1f
SUBS R0, R0, #0x13
beq IN_SUPMODE
mov r0, #4
strb r0, [ip, #0x230] /* ソフトウエアリセット発生、スーパパイザモード移行 */
IN_SUPMODE:
nop
.if PLL_SETUP
とっても快適。余談だが、gcc が C での unsigned int* base = (unsigned int *)0xffff0000; に対し 上の Reset_Handler: 冒頭二行のようなコードを返してきたときはちょっと感動した。 パイプラインがスムースなので ldr r0, =どっか より普通に速く、 JTAG からデータバスに挿入した時は圧倒的に速い。
つまり
.text
.arm
write_page_hook: .word _write_page_hook - _write_page
write_page_size: .word _write_page_end - _write_page
_write_page:
mov r0, #-2113929216
mov ip, #1 @ movhi
mov r4, r0, asr #15
str ip, [r4, #4] @ movhi
nop
mov lr, #4 @ movhi
...
こういうのを
unsigned int write_page_hook = 0x51; /* 0x144(324) */
unsigned int write_page_size = 88;
unsigned int write_page_code[88] = { /* 352 bytes. */
0xe3a09000,
0xe3a00482,
0xe3a0c001,
0xe1a047c0,
0xe584c004,
0xe1a00000,
0xe3a0e004,
...
こんな風に変換してローダソース中に持つということだ:
% arm-linux-gcc -c write_page.s % arm-linux-ld -Ttext 0 -o fixed.o write_page.o % arm-linux-objcopy -O ihex fixed.o write_page.hex % ./ihex2struct write_page < write_page.hex > write_page.cバイナリをヘッダに直す ihex2struct なんてのはすぐ書けるだろうからどーでも良いとして、 ひとつだけメモ書き。
相対ジャンプの解決はリンカの仕事で、アセンブラがローカルに解決できるものでさえリンカ任せになる。 上の例で言えば write_page.o 内の相対ジャンプのオフセットは間違っている。 適当なセグメントページを固定してインクリメンタルリンカに流す。 text セグメントのページ頭の解釈はまだ間違ってる(間違ってても問題ない)が、相対ジャンプはこの時点で直る。
というところで見付けた wiggler ケーブルを使って ARM の JTAG と通信できるフリーの下位プログラム群。 上位のプロトコルとしては ARM remote debug protocol/inteface をしゃべるもの、 remote gdb protocol をしゃべるもの、自前でテキスト吐くものといろいろあるけど、 そっちは適当に話を合わせれば良いだけだから。
# ./openocd -f openocd.cfgなどとすること。また、TCP ポートとして 3333 を使う。 OCDRemote の 8888 とは異なる。 ちなみに 3333 のが gdb 的には由緒正しかったはず。OCDRemote が 8000 番台に上げた気分も分かるが。
step 実行するのに使うブレークがデフォルトではソフトウエアブレークなことは気になる。 ハードウエアブレークに切替えるフラグは force_hw_bkpts とゆーらしーが未テスト。
また、配布ファイルに configure が含まれておらず、autoconf, automake 一式が要る。 これらを自前で用意するのは *.in が tool の version に煩いからヤなんだよな ...。 いろいろ組合せてみて 2.59 と 1.4 の組では動いた。わりと新しいセットが要るようだ。
# ./jtag_gdbserver -driver wiggler 192.168.63.21:3333必ずクライアント(arm-*-gdb を起動するホスト)を指定する。gdb 上で
(gdb) monitor MemMap.MaxNum = 1 (gdb) monitor MemMap.MaxEntrys[0] = 4 (gdb) monitor MemMap[0][0] = rom, 16, 0x00000000, 62 KByte (gdb) monitor MemMap[0][1] = ram, 32, 0x00010000, 8 KByte (gdb) monitor MemMap[0][2] = rom, 16, 0x00080000, 62 KByte (gdb) monitor MemMap[0][3] = ram, 16, 0xFFFF0000, 64 KByte (gdb) monitor UseMemMap[0]てな具合にメモリマップを指定してようやく使えるようになる。 ある意味でまともだが繁雑な上に I/O マップの扱いが実装されとらんよーな。 それにアドレス毎にワード長が違う ADuC7026 の MMR のような奴を正しく扱わせるのは大変だ。
#./tp_003 IDCODE=3F0F0F0F IceB Debug Control = 0x00000000 Status = 0x00000014 IceB Debug Control = 0x00000000 Status = 0x00000009 READ MEMORY AT 0x00000000: e59ff018 e59ff018 e59ff018 e59ff018 e59ff018 e1a00000 e59ff014 e59ff014 0000003c 00000738 000006f8 00000778 000007ac 00000678 000006b8 e3a00102 e1a0c7c0 e3a00000 e58c0008 e10f0000 e200001f e2500013 0a000001 e3a00004 e5cc0230 e1a00000 e59f00d0 e3a01001 e5801404 e3a01000 e5801408 e3a010f4 e580140c eb00007f e28fa008 e89a003f e880003e ea000005 fffff400 00001000 00000000 00000000 00000000 00000000 e3a00b47 e321f0db e1a0d000 e2400004 e321f0d7 e1a0d000 e2400004 e321f0d1 e1a0d000 e2400080 e321f0d2 e1a0d000 e2400080 e321f0d3 e1a0d000 e2400004 e321f010 e1a0d000 e24dab01 e59f1040 e59f2040 e59f3040 e1520003 34910004 34820004 3afffffb e3a00000 e59f102c e59f202c e1510002 34810004 3afffffc e59fe020 e59f0020 e12fff10 eafffffe ffff0000 00001078 00010000 00010000 00010000 000100e4 0000013c 000005d5 e92d4030 e3e03cff e51320fb e3120004 e59fe088 e59f4088 0a00001e e59e1000 Program halted by user debug request: NO, is a breakpoint/watchpoint BREAK/WATCH: BREAKPOINT Register contents: 000005D5 00010010 FFFFF460 00000160 00000000 00000000 FFFF0000 00000000 00000000 00000000 000116F4 00011B70 00011B74 00011B50 000006A8 00000160 CPSR = 0x20000092 IceB Debug Control = 0x00000000 Status = 0x00000004 IceB Debug Control = 0x00000000 Status = 0x00000019 ...本来 gdb と繋がる予定だったとこが名前から窺えるが、そこまで実装されてない。
... コード全体が小さすぎて -O3 するとコード全部を loop-unroll してくださってとっても大きくなる(26kB)。 -O2 までに留めるべきで、この時で 10kB 弱。やー、ちょっとびっくりした。
#./jtager *** Welcome to JTAGER-0.3.0 ...... *** Copyright (C) 2003-2004, Rongkai Zhanice (EmbeddedICE レジスタ操作)、reg (レジスタ操作)、memdump/memset (メモリ操作)、halt/restart (停止/再実行) と一通りそろっているので EmbeddedICE の動きの理解に良い。 ただ THUMB モードはサポートしていない。停止だけはできるんだけど。 あ、上のサンプルは THUMB サポートを実装したあとのもんで、本来は reg とかやっても「使えんよ」って返事がかえってくる。JTAGER is a free software, covered by the GNU General Public License, and you are welcome to change it and/or distributecopies of it under certain conditions. There is absolutely no warranty for JTAGER. You can type 'help' command to get a list of all commands. You aslo can type 'help ' command to get a verbose information about the command. All commands are case-insensitive, and has GNU-style long command options. Using parallel data port and status port -- 0x378 and 0x379 Jtag interface has been reset successfully! target core type: ARM7TDMI JTAGER:> halt Requesting HALT target ... [OK] The target is halted in THUMB mode. JTAGER:> memdump --base=0x00080000 --size=16 00080000: E59FF018 E59FF018 E59FF018 E59FF018 |................| 00080010: E59FF018 E1A00000 E59FF014 E59FF014 |................| 00080020: 0000003C 00000738 000006F8 00000778 |<...8.......x...| 00080030: 000007AC 00000678 000006B8 E3A00102 |....x...........| JTAGER:> reg R0 = 0x0008005C R1 = 0xE3A00102 R2 = 0xE1A0C7C0 R3 = 0xE3A00000 R4 = 0xE58C0008 R5 = 0xE10F0000 R6 = 0xE200001F R7 = 0xE2500013 R8 = 0x0A000001 R9 = 0x00000000 R10 = 0x000116F4 R11 = 0x00000000 R12 = 0xFFFF0000 R13 = 0x00011B78 R14 = 0x061A0030 R15 = 0x00000330 PC = 0x00000330 CPSR = 0x40000092 SPSR = 0x40000010 JTAGER:>
ちゃんと言うと restart 時に R0 は reg で表示されてる値になっておらず、PC と同内容で上書きされている。つまり:
的な動作をしている。ldr r0, 再実行アドレス mov pc, r0
PC へのロードはレジスタからにしないと実行アドレスが激しくずれるので、これを直すのはけっこう神経を使う。
このあたりを直すつもりならいっそ gdbice のコードをまるごともってきたほうが良いと思う。
本来 flash メモリへの書き込み用ということもあるだろうが ... flash に書き込んだあとはリセットするだけだからレジスタの保管といった類の
しちめんどくさい作業は要らないし。
今回使ったのはこの jtager で、もうすこし早く file の存在を知ってたら openocd にしてたかもしれないし、 ある程度 ARM の EmbeddedICE を理解した今なら gdbice を選択するだろうが、 上位プログラムなしに手でメモリダンプしてみたり EmbeddedICE レジスタを叩いたりできる jtager なしには どーせ何にもできない。
要するに、jtag を操るプログラム自身のデバッグには jtager が必要なのであって、 こいつに THUMB サポート突っ込んでしまったあとは いちいち他のプログラムを読むまでもなく jtager の細工に走ったのは当然の成行きというやつである。
一つめは昔の XiLINX 用パラレルケーブルのピン配置を ARM 用に適当に入れ換えたもの。 「回路図書かないヨカン」などと書いて本気でほったらかしにしておいて 3 年ぶりに回路図書き起こしたのはここで使うためだというのはあっちでは秘密だ。![]()
二つめのは OCDRemote での動作を見るためと、jtag の reset 動作を確実にするために nTRST や nSRST を配線したもの (このあたり、いかに reset に苦労していたか窺える;_;)。 Wiggler というよりは (TRST, SRST 両方の配線のある) Olimex の ARM-JTAG に近い: 回路図。
コメントいくつか。
ARM9 用では ARM7 用で TRST に配線していた D0 を SRST に配線するようになったんかね。
nTRST nSRST Wiggler not D0 nTRST or NC ARM-JTAG not D0 D4 K9JTAG D4 not D0 jtag-arm9 NC not D0 buici not D0 NC hri not D0 NC
ADuC7026 の根性を褒めるべきなんだろうが RAM の16 進ダンプ見つめても直したコードかバグってる旧コード(つまり新コードのロード失敗)かなんて分からんので けっこう邪魔であった。 なるほど世のケーブルのいくつかはケーブル切断時に /OE を上げ、積極的に各ピンを 0V に固定しているわけである。 次に作る時はそうしよう。
どんな風に桶屋がもうかってんのかは調べてないから不明。まあ可能性のあるルートは のきなみ抵抗が入ってるので突入電流で壊れるってことはなかろ。
おぼえがきいくつか。[usage] jtal [-r] [-w] [-v] [-s] [-t] [-d] [-f hexfile] [< hexfile]
- -r
- Intex HEX format のファームイメージを受け取る。
- -f
- ファームイメージ名を指定する。指定しない場合は stdin から受け取る。
- -w
- フラッシュに書く。入力ファイルが無い場合は全データが 0xff とみなす(単に消去する)。
- -v
- フラッシュの内容をベリファイする。 入力ファイルが無い場合は全データが 0xff とみなす(消去されているかどうかを検証する)。 また -v -v と重ねると 1 ページ処理するごとに "*" とか "O" とかのインジケータを出す。
- -t
- 物事のついでに転送速度の測定。
- -s
- 一通り終ったあとターゲットをリセットしない。JTAG による halt モードのまま抜ける。
- -d
- 一通り終ったあと jtager コンソールに移行する。-r と同時に使う場合は -f も指定すべきである。
- -h
- ヘルプメッセージを出す。
書いてみた:
# ./jtal -r -w -t < DAC_thumb.hex Using parallel data port and status port -- 0x378 and 0x379 Jtag interface has been reset successfully! target core type: ARM7TDMI Requesting HALT target ... [OK] The target is halted in ARM mode. Setup time 162.776000 ms Transfer rate 4.093544 kB/s (1125.675000 ms) Jtag interface has been reset successfully! #続いてフラッシュ全体を消去:
./jtal -w -v -t Using parallel data port and status port -- 0x378 and 0x379 Jtag interface has been reset successfully! target core type: ARM7TDMI Requesting HALT target ... [OK] The target is halted in THUMB mode. OK, the flashrom is erased completely. Setup time 162.804000 ms Transfer rate 5.599897 kB/s (11337.351000 ms) Jtag interface has been reset successfully! #も一回書き込む。こんどは書き込み後にベリファイ:
#./jtal -r -w -v -t -f DAC2_thumb.hex Using parallel data port and status port -- 0x378 and 0x379 Jtag interface has been reset successfully! target core type: ARM7TDMI Requesting HALT target ... [OK] The target is halted in ARM mode. OK, new firmware is loaded. Setup time 167.933000 ms Transfer rate 2.744406 kB/s (1679.052000 ms) Jtag interface has been reset successfully! #
% zcat jtager-0.3.0.tar.gz |tar -xv % cd jtager-0.3.0 % zcat ../jtal-0.3.0-k1.diff.gz | patch -p1 % ./configure % make % cd src % cp jtager jtal
ターゲット内コマンド用ファーム src/target/arm7tdmi/*.s をいじった場合は:
% cd src/target/arm7tdmi % cc ihex2struct -o ihex2struct % ./s2h setup_pll % ./s2h write_page % ./s2h erase_page % ./s2h reset % ./s2h feeack % cd ../.. % make現状、*.s を変えても make は見てない。すまん。