LPC11U35にSWDでアクセス(3)
- LPC11U35にSWDでアクセス(1)
- LPC11U35にSWDでアクセス(2)
- LPC11U35にSWDでアクセス(3)
- LPC11U35にSWDでアクセス(4)
前回はSWD-DPのDPIDRレジスタにアクセスするところまで進めた。ここではSWD-DPの先にあるMEM-APを使って、マイコン内部のAHBバスにアクセスする。
MEM-APとは
MEM-APはAP(アクセスポート)の種類の1つで、メモリ(SRAM、Flashなど)やメモリマップドI/Oなど、アドレスを使ってリソースにアクセスするような場合に使われる。普通はメインのバスにつながっているので、MEM-APに接続できればだいたいのことはSWDからもできるようになる。
MEM-APを使う
以下の手順で進めていく。
APを使うための準備
はじめに、APにアクセスするにはまずデバッグ回路の電源を入れる必要がある。またエラーフラグが残っていた場合、それらをクリアしておかなくてはならない。これらの操作はDPレジスタで行う。
write_dp_reg(DP_CTRL, 0x50000000, "CTRL"); // debug component power on write_dp_reg(DP_ABORT, 0x1f, "ABORT");; // clear error flags do read_dp_reg(DP_STAT, "STAT"); while ((swd_reg & 0xf0000000) != 0xf0000000);
APレジスタにアクセス
SWDからAPレジスタにアクセスする手順は次の通り。アクセスしたいAPレジスタのアドレスをAとすると
- DPのSELECT[7:4]に、A[7:4]をセット
- A[3:2]を使ってAPアクセス(APnDP = 1にして、他はDPレジスタアクセスと同様)
- 書き込みの場合、WDATAとして書き込む値を送信する
- 読み込みの場合、送られてくるRDATAは無視して3に進む
- 読み込みの場合、DPのRDBUFFに読み出された値がセットされるのでこれを読む
これも関数にしておく。
int read_ap_reg(uint32_t address, const char *name = NULL) { int ack = swd_reg_access(0, 0, DP_SELECT, address & 0xf0); if (ack == SWD_OK) { ack = swd_reg_access(1, 1, address, 0); if (ack == SWD_OK) while ((ack = swd_reg_access(0, 1, DP_RDBUFF, 0)) == SWD_WAIT) { } if (name != NULL && ack == SWD_OK) printf("[AP] %6s = 0x%08x\n", name, swd_reg); } return ack; } int write_ap_reg(uint32_t address, uint32_t value, const char *name = NULL) { int ack = swd_reg_access(0, 0, DP_SELECT, address & 0xf0); if (ack == SWD_OK) { ack = swd_reg_access(1, 0, address, value); if (name != NULL && ack == SWD_OK) printf("[AP] %6s <- 0x%08x\n", name, value); } return ack; }
この関数を使ってAPのIDRを読み出してみる。IDRにはAPの種類などの情報が含まれているので、どんなAPにアクセスしているのかがわかる。
read_ap_reg(AP_IDR); printf("AP class = 0x%01x (if 0x8, MEM-AP)\n", swd_reg >> 13 & 0xf); printf("AP variant = 0x%01x\n", swd_reg >> 4 & 0xf); printf("AP type = 0x%01x (if 0x1, AHB bus)\n", swd_reg & 0xf);
結果は…
=== START === [DP] DPIDR = 0x0bb11477 [DP] CTRL <- 0x50000000 [DP] ABORT <- 0x0000001f [DP] STAT = 0xf0000040 [AP] IDR = 0x04770021 AP class = 0x8 (if 0x8, MEM-AP) AP variant = 0x2 AP type = 0x1 (if 0x1, AHB bus)
メモリにアクセス
IDRの情報からMEM-APにアクセスしていることがわかった。そしてMEM-APは内部のAHBバスにつながっているので、この時点でMEM-APを使ってAHBバスを操作できるはず。試しに0x000~0x100番地の値を読み出してみよう。MEM-APを使ってAHBバスのアドレス空間にアクセスする手順は次の通り。
今回の場合はこのようになる。
puts("configure memory access ..."); read_ap_reg(AP_CSW); write_ap_reg(AP_CSW, swd_reg & 0xffffffc8 | 0x10 | 0x2, "CSW"); // increment single, 32bit puts("read 0x00000000 - 0x00000100"); write_ap_reg(AP_TAR, 0, "TAR"); for (int i = 0; i < 64; i++) read_ap_reg(AP_DRW, "DRW");
インクリメントアクセス(CSW[5:4] = 1)に設定するとDRWにアクセスするたびに自動でアドレスを進めてくれるので、このような連続アクセスにはとても便利。
=== START === [DP] DPIDR = 0x0bb11477 [DP] CTRL <- 0x50000000 [DP] ABORT <- 0x0000001f [DP] STAT = 0xf0000040 [AP] IDR = 0x04770021 AP class = 0x8 (if 0x8, MEM-AP) AP variant = 0x2 AP type = 0x1 (if 0x1, AHB bus) configure memory access ... [AP] CSW <- 0x03000052 read 0x00000000 - 0x00000100 [AP] TAR <- 0x00000000 [AP] DRW = 0x10002000 (以下略)
LPC11U35に書き込んだバイナリと比べてみると、割り込みテーブルが若干変わっている以外は同じ値が読み出せた。割り込みテーブルはOSが実行時に書き換えたと思われる。実際のコードは省くが、同じようにTARにアドレスをセットしてからDRWに書き込みすることでRAMに書き込むこともできる。これでAHBバスへのアクセスは成功した。
ついでに例のアドレスも読み出してみる。
puts("read CRP option (0x000002fc)"); write_ap_reg(AP_TAR, 0x000002fc); read_ap_reg(AP_DRW); printf("CRP option = 0x%08x\n", swd_reg);
read CRP option (0x000002fc) CRP option = 0x4e697370
これを何とかしたいのだ…。
- LPC11U35にSWDでアクセス(1)
- LPC11U35にSWDでアクセス(2)
- LPC11U35にSWDでアクセス(3)
- LPC11U35にSWDでアクセス(4)