ARM中MMU使用实例
MMU_SECDESC_WB”中C/B位都设置了,表示使用Cache和Write buffer,即所谓回写式,在映射Steppingstone和SDRAM等内存时使用“MMU_SECDESC_WB”。
现在来看看mmu_init函数。create_page_table函数设置好了页表,还需要把页表地址告诉CPU,并且在开启MMU之前做好一些准备工作,比如使无效ICache和DCache,设置域访问控制寄存器等。代码的注释就可以帮助读者很好的理解mmu_init函数,不再重复。代码如下:
/*
*启动MMU
*/
void mmu_init(void)
{
unsigned long ttb = 0x30000000;
__asm__(
"movr0, #0\n"
"mcrp15, 0, r0, c7, c7, 0\n"/*使无效ICaches和DCaches */
"mcrp15, 0, r0, c7, c10, 4\n"/* drain write buffer on v4 */
"mcrp15, 0, r0, c8, c7, 0\n"/*使无效指令、数据TLB */
"movr4, %0\n"/* r4 =页表基址*/
"mcrp15, 0, r4, c2, c0, 0\n"/*设置页表基址寄存器*/
"mvnr0, #0\n"
"mcrp15, 0, r0, c3, c0, 0\n"/*域访问控制寄存器设为0xFFFFFFFF,
*不进行权限检查
*/
/*
*对于控制寄存器,先读出其值,在这基础上修改感兴趣的位,
*然后再写入
*/
"mrcp15, 0, r0, c1, c0, 0\n"/*读出控制寄存器的值*/
/*控制寄存器的低16位含义为:.RVI ..RS B... .CAM
* R :表示换出Cache中的条目时使用的算法,
*0 = Random replacement;1 = Round robin replacement
* V :表示异常向量表所在的位置,
*0 = Low addresses = 0x00000000;1 = High addresses = 0xFFFF0000
* I : 0 =关闭ICaches;1 =开启ICaches
* R、S :用来与页表中的描述符一起确定内存的访问权限
* B : 0 = CPU为小字节序;1 = CPU为大字节序
* C : 0 =关闭DCaches;1 =开启DCaches
* A : 0 =数据访问时不进行地址对齐检查;1 =数据访问时进行地址对齐检查
* M : 0 =关闭MMU;1 =开启MMU
*/
/*
*先清除不需要的位,往下若需要则重新设置它们
*/
/* .RVI ..RS B... .CAM */
"bicr0, r0, #0x3000\n"/* ..11 .... .... ....清除V、I位*/
"bicr0, r0, #0x0300\n"/* .... ..11 .... ....清除R、S位*/
"bicr0, r0, #0x0087\n"/* .... .... 1... .111清除B/C/A/M */
/*
*设置需要的位
*/
"orrr0, r0, #0x0002\n"/* .... .... .... ..1.开启对齐检查*/
"orrr0, r0, #0x0004\n"/* .... .... .... .1..开启DCaches */
"orrr0, r0, #0x1000\n"/* ...1 .... .... ....开启ICaches */
"orrr0, r0, #0x0001\n"/* .... .... .... ...1使能MMU */
"mcrp15, 0, r0, c1, c0, 0\n"/*将修改的值写入控制寄存器*/
: /*无输出*/
: "r" (ttb) );
}
mmu_init函数在C语言中嵌入了汇编指令。
第二部分代码leds.c中只有两个函数:wait和main。wait函数用来延迟时间,main函数用来循环点亮4个LED。与前面两章所用的leds.c有两点不同。
(1)操作GPBCON、GPBDAT两个寄存器时使用虚拟地址0xA0000010、0xA0000014,在init.c中已经把虚拟地址0xA0000000 - (0xA0000000 + 1M - 1)映射到物理地址0x56000000 - (0x56000000 + 1M -1);
(2)在定义wait函数时使用了一点小技巧,将它定义成”static inline”类型,原因在源码的第15行给出。
leds.c代码如下:
/*
* leds.c:循环点亮4个LED
*属于第二部分程序,此时MMU已开启,使用虚拟地址
*/
#define GPBCON(*(volatile unsigned long *)0xA0000010)//物理地址0x56000010
#define GPBDAT(*(volatile unsigned long *)0xA0000014)//物理地址0x56000014
#define GPB5_out(1<(5*2))
#define GPB6_out(1<(6*2))
#define GPB7_out(1<(7*2))
#define GPB8_out(1<(8*2))
/*
* wait函数加上“static inline”是有原因的,
*这样可以使得编译leds.c时,wait嵌入main中,编译结果中只有main一个函数。
*于是在连接时,main函数的地址就是由连接文件指定的运行时装载地址。
*而连接文件mmu.lds中,指定了leds.o的运行时装载地址为0xB4004000,
*这样,head.S中的“ldr pc, =0xB4004000”就是跳去执行main函数。
*/
static inline void wait(unsigned long dly)
{
for(; dly > 0; dly--);
}
int main(void)
{
unsigned long i = 0;
//将LED1-4对应的GPB5/6/7/8四个引脚设为输出
GPBCON = GPB5_out|GPB6_out|GPB7_out|GPB8_out;
while(1){
wait(30000);
GPBDAT = (~(i<5));//根据i的值,点亮LED1-4
if(++i == 16)
i = 0;
}
return 0;
}
Makefile内容如下:
objs := head.o init.o leds.o
mmu.bin : $(objs)
arm-linux-ld -Tmmu.lds -o mmu_elf $^
arm-linux-objcopy -O binary -S mmu_elf $@
arm-linux-objdump -D -m arm mmu_elf > mmu.dis
%.o:%.c
arm-linux-gcc -Wall -O2 -c -o $@ $
%.o:%.S
arm-linux-gcc -Wall -O2 -c -o $@ $
ARMMM 相关文章:
- ARM·MMU(11-24)
- ARM MMU工作原理剖析(11-23)
- ARM中MMU的作用(11-11)
- ARM中MMU之地址转换(11-09)
- ARM中MMU地址转换理解(11-09)
- Windows CE 进程、线程和内存管理(11-09)