微波EDA网,见证研发工程师的成长!
首页 > 硬件设计 > 嵌入式设计 > ARM中MMU使用实例

ARM中MMU使用实例

时间:11-11 来源:互联网 点击:

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 $@ $

Copyright © 2017-2020 微波EDA网 版权所有

网站地图

Top