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 が書かれている。