MicroBlaze の TLB を直接いじってみる
本や仕様書を読んでも、なかなか MMU の理解が進まないので、実際に MicroBlaze の TLB をいじってみることにした。そーいえば、ポインタを理解したときもデバッガでメモリダンプを見て初めて納得がいった気がする。
TLB を使うための簡単なプログラムを書いてみた:
#include "xutil.h" void hal( void ) { // clear all tlb asm( "addik r3, r0, 63" ); asm( "l1:" ); asm( "mts rtlbx, r3" ); asm( "mts rtlbhi, r0" ); asm( "bgtid r3, l1" ); asm( "addik r3, r3, -1" ); // write pid asm( "mts rpid, r0" ); // write tlb // entry = 0 asm( "mts rtlbx, r0" ); // phys = 0x13000000 // wr = 1 -> enable write // I = 1 -> disable cache asm( "addik r3, r0, 0x13000000" ); asm( "addik r3, r3, 0x0104" ); asm( "mts rtlblo, r3" ); // vir = 0xC0000000 // size = 4k asm( "addik r3, r0, 0xC0000000" ); asm( "addik r3, r3, 0x00C0" ); asm( "mts rtlbhi, r3" ); // VM = 1 // enable MMU asm( "mfs r1, rmsr" ); asm( "addik r1, r0, 0x2000" ); asm( "mts rmsr, r1" ); asm( "nop" ); asm( "nop" ); asm( "nop" ); asm( "nop" ); } int main(void) { xil_printf("hal test\n"); hal(); while ( 1 ) { } return 0; }
関数 hal() というのが実際に TLB にアクセスする箇所だ。わざわざ関数にする必要はあまり無いのだが、デバッガから break point を設定しやすくするためにそうした。
MicroBlaze の TLB は 64 個の UTLB と、MicroBlaze のコンフィグレーションで数を変更可能な ITLB と DTLB から構成され、直接プログラムからアクセス出来るのは UTLB だけだ。ITLB と DTLB は直近に使われた UTLB の値が格納され、アドレス変換する際に優先的に参照される。
UTLB にアクセスするには、レジスタ TLBX に index を書き込むことで、まず 64個のうちどれにアクセスするかが決定される。
一つの UTLB は 32bit に収まらないため、TLBHI と TLBLO という二つのレジスタに分けてアクセスされる。
コードは、コメントにある通りだが一応説明すると、
UTLB の中身は bitstream をダウンロードし直さない限りクリアされない(リセットではクリアされない)ようなので、まずは関数 hal() の冒頭で、UTLB のクリアを行う。このコードは linux で動いているものをそのまま拝借した。
次に、PID レジスタに 0 を書き込む。この値と、UTLB に書かれる TID の値が一致させないと、アドレス変換は有効にならない。
次に、index 0 の UTLB にこれからアクセスするため、TLBX に 0 を書き込む。
次に TLBLO に、変換元の物理アドレスと、write の enable or disable, キャッシュの enable or disable を書き込む。
次に TLBHI に、返還先の仮想アドレスと、ページサイズを書き込む。
ここでは、物理アドレス 0x13000000 番地を、仮想アドレス 0xC0000000 に変換するようにした。サイズは 4k で write enable, キャッシュは disable とした。
最後に MSR の VM ビットを 1 にすることで、実モードから仮想モードに MicroBlaze を切り替える。
以上で、MMU によるアドレス変換が有効になった。はず。
xmd で、MSR の VM ビットに 1 を書く直前までステップ実行してみる。
XMD% stp 1E8: 9403D004 mts rTLBHI, r3 XMD% srrd pc: 000001ec msr: 00000400 ear: 00000000 esr: 00000000 fsr: 00000000 btr: 00000000 pvr0: d7801100 pvr1: 00000000 pvr2: 578351f0 pvr3: 82000000 pvr4: b74d0c00 pvr5: b74d0000 pvr6: 10000000 pvr7: 13ffffff pvr8: 10000000 pvr9: 13ffffff pvr10: 0d000000 pvr11: cac40000 edr: 00000000 pid: 00000000 zpr: 00000000 tlbx: 00000000 tlbsx: 00000000 tlblo: 13000104 tlbhi: c00000c0 XMD% stp 1EC: 94208001 mfs r1 , rMSR XMD% stp 1F0: 30202000 addik r1 , r0 , 8192 XMD% stp 1F4: 9401C001 mts rMSR, r1 XMD% stp ERROR: Cannot Read from target
MSR に書き込み終えた後にさらに stp 実行しようとしたら、TLB ミスの exception が発生した。プログラムを実行するためのエントリを作らなかったためだと思われる。
とりあえずこのエラーは無視して、実際にアドレス変換が有効になったか試してみる。
まず、物理アドレス 0x13000000 にはアクセス出来ないことを確認する。
XMD% mrd 0x13000000 ERROR: Cannot Read from target Error: operation failed due to Data TLB miss exception.
0xC0000000 に 0xDEADBABE を書いてみる。
XMD% mrd 0xC0000000 C0000000: DD7FFFD7 XMD% mwr 0xC0000000 0xDEADBABE XMD% mrd 0xC0000000 C0000000: DEADBABE
次に、仮想モードから実モードに戻し、0x13000000 番地を読んでみる。
XMD% rwr msr 0 XMD% mrd 0x13000000 13000000: DEADBABE
ちゃんと 0xDEADBABE が書かれている。