您的位置:首页 > 其它

四、mini2440裸机程序之MMU(下)

2012-11-16 12:05 399 查看
1.本实验相关管脚介绍

1)原理图





2)由上图可以看出,4个LED灯所对应的管脚:

名称

对应管脚

管脚功能

对应逻辑

LED1

GPB5

内部上拉输出

0:灯亮

1:灯灭

LED2

GPB6

内部上拉输出

0:灯亮

1:灯灭

LED3

GPB7

内部上拉输出

0:灯亮

1:灯灭

LED4

GPB8

内部上拉输出

0:灯亮

1:灯灭

2.相关的寄存器:



1)与LED相关:

注释:

① GPBCON : 用于配置端口B的各个管脚功能

② GPBDAT : 端口B的数据寄存器

③ GPBUP : 用于配置端口B是否使用上拉功能

2)与MMU相关

① CP15 register 1(control register)

相关bit(M,A,S,R,RR,V(可有可无))



注:
这个放在最后连着开启MMU时一次性设置就好了,需要设置的有:使能Icache,Dcache;使能数据地址对齐异常检查;关于覆写算法默认是ramdom,我们选择round-robin;关于异常向量表的起始位置,我们选择放在最低地址0x0就可以了(这个实验我们还不会用到向量表,所以也可以先忽略掉);endianness选择小端就行了,默认也是小端;开启MMU;

关于R,S这两个值,要与AP关联上,由下图可以看出,当AP=00时,我们这里选择S:R=10:这样在AP=00时,SP为RO,UP为NA;AP=01时,SP为R/W,UP为NA;AP=10,SP为R/W,UP为RO;AP=11,SP为W/R,UP为R/W.

综上所属,使用配置命令如下:

MRC p15 , 0 , R1 , c1 , c0 , 0 (R1 [29:0]= 000000000000000 1 0 1 00 0 1 0 1111 1 1 1B)



②CP15 register 2(Translation table base register(TTB))



注:
在启动MMU之前我们需要设置好TTB,使用配置命令如下:

MCR p15 , 0 , R1 , c2 , c0 , 0 (R1=页表基址)

③CP15 register 3(Domainaccess control register)



以下为在domain access control寄存器里的access control bits的取值及说明:



这里我们根据顺序来设置:D0=00(No access)D1=01(Client)D2=11(Manager)其它都设置为10(reserved),使用命令配置如下:

MCR p15 , 0 , R1 , c3 , c0 , 0 (R1 = 10101010101010101010101010110100B)

④CP15 register 5(fault status register)



⑤CP15 register 7(cacheoperations register)(write-only)

这个寄存器用于管理ICache 和 DCache. 提供功能如下:





格式如下:



注:

开启MMU之前我们需要InvalidateCache,并且清除write buffer,所以使用命令配置如下:

MCR p15 , 0 , R1 , c7 , c7 , 0 (R1=SBZ)
MCR p15 , 0 , R1 , c7 , c10 , 4 (R1=SBZ)

⑥CP15 register 8(TLB operation register )



可以使无效整个TBL(s),….其中还可以无效单独有一个记录项(通过使用MVA),MVA被提供在Rd里.当需要用上MVA时,register 10 的MVA格式如下:



注:

在开启MMU之前我们需要进行一次InvalidateTLBs。这样在开启MMU之后就可以自动重新设置TLB的内容了。所以使用命令配置如下:

MCR p15 , 0 , Rd , c8 , c7 , 0 (Rd=SBZ)

⑦CP15 register 10(TLB lockdown register)



格式如下:



复位时为0x0

3.程序流程图设计:

1)主程序流程图:



2)建立页表子程序流程图:



注:这里需要注意的是:要把物理地址为0~代码的结束地址,不然使用开启MMU指令后并不正确执行后续的指令,我个人的理解是:在开启MMU之后,CPU上所看到的指令地址会变成VA,这时如果不进行映射到相同的PA地址,那经过MMU之后就可能映射到不可预测的物理地址了或者出现异常。

3)拷贝测试代码子程序流程图:



4)初始化MMU子程序流程图:



4.程序设计:

①Makefile

demo_mmu.bin : demo_mmu.o led_display.o

arm-linux-ld -Tmmu.lds -o demo_mmu_elf demo_mmu.o led_display.o

arm-linux-objcopy -O binary -S demo_mmu_elf demo_mmu.bin

arm-linux-objdump -D -m arm demo_mmu_elf > demo_mmu.dis

%.o : %.c

arm-linux-gcc -Wall -O2 -c -o $@ $<

%.o : %.S

arm-linux-gcc -Wall -O2 -c -o $@ $<

clean :

rm -f demo_mmu.bin demo_mmu_elf demo_mmu.dis *.o

②mmu.lds

SECTIONS {

first 0x00000000 : {demo_mmu.o}

second 0xB0005000 : AT(2048) {led_display.o}

}

③demo_mmu.S

#define WTCON 0x53000000

#define BWSCON 0x48000000

#define BANKCON6 0x4800001C

#define REFRESH 0x48000024

#define BANKSIZE 0x48000028

#define MRSRB6 0x4800002C

#define MMU_TTB 0x30000000

#define MMU_FPTB (0x30004000)

#define NCNB_CTTBTT (0x00<<2)

#define WT_CTTBTT (0x03<<2)

#define MANAGER_DOMAIN (0x02<<5)

#define SPUP_WR_SECTIONAP (0x03<<10)

#define SECTION_FLAG (0x01<<4)|(0x02)

#define FINE_FLAG (0x13)

#define SPUP_WR_TINYAP (0x03<<4)

#define TINY_FLAG (0x03)

.Text

.global _start

_start:

/******关闭看门狗**********/

ldr r0 , =WTCON

mov r1 , #0x0

str r1 , [r0]

/*****END关闭看门狗********/

/***调用初始化内存子程序***/

bl memory_init

/**********END*************/

/*****调用建立页表子程序***/

bl build_page

/***********END************/

/*****调用初始化MMU子程序**/

bl mmu_init

/***********END************/

@启动MMU

mcr p15 , 0 , r1 , c1 , c0 , 0 @设置control register.启动MMU

/**********让灯全亮************

@把LED1-4管脚置为输出

ldr r0 , =GPBCON

ldr r1 , [r0] @把GPBCON里的内容加载到r1里

ldr r2 , =(0xFF<<10)

bic r1 , r1 ,r2 @操作数取反码或上r1,用于清零工作

ldr r2 , =(0x55<<10)

orr r1 , r1 , r2

str r1 , [r0]

@灯全亮

ldr r0 , =GPBDAT

ldr r1 , [r0]

ldr r2 , =(0x0F<<5)

bic r1 , r1 , r2

str r1 , [r0]

***********END***************/

/****调用测试代码子程序****/

bl copy_from_bootsram_to_sdram

/***********END************/

@设置栈顶指针

ldr sp , =0xB4000000

/****************灯全灭**************/

ldr r0 , =GPBCON

ldr r1 , [r0] /*把GPBCON里的内容加载到r1里*/

bic r1 , r1 , #0x3FC00 /*操作数取反码或上r1,用于清零工作*/

orr r1 , r1 , #0x15400

str r1 , [r0]

ldr r0 , =GPBDAT

; ldr r1 , [r0]

; bic r1 , r1 , #0x1E0

; orr r1 , r1 , #0x1E0

; str r1 , [r0] /*此时4个LED等全灭*/

/***************END灯全灭***************/

@跳转到测试代码执行

ldr pc , =main

halt_loop:

b halt_loop

memory_init:

/*******内存初始化子程序*********/

@BWSCON[27:24] = 0 0 10B

ldr r0 , =BWSCON

ldr r1 , [r0]

ldr r2 , =(0x0F<<24)

bic r1 , r1 , r2

ldr r2 , =(0x02<<24)

orr r1 , r1 , r2

str r1 , [r0]

@BANKCON6[16:15]=11B;BANKCON6[3:0]=00 01B

ldr r0 , =BANKCON6

ldr r1 , [r0]

ldr r2 , =(0x03<<15)

bic r1 , r1 , r2

orr r1 , r1 , r2

ldr r2 , =0x0F

bic r1 , r1 , r2

ldr r2 , = 0x01

orr r1 , r1 , r2

str r1 , [r0]

@REFRESH[23:18] = 1 0 00 00B;REFRESH[10:0] = 0x7A3

ldr r0 , =REFRESH

ldr r1 , [r0]

ldr r2 , =(0x3F<<18)

bic r1 , r1 , r2

ldr r2 , =(0x20<<18)

orr r1 , r1 , r2

ldr r2 , =0x7FF

bic r1 , r1 , r2

ldr r2 , = 0x7A3

orr r1 , r1 , r2

str r1 , [r0]

@BANKSIZE[7:0] = 1 0 1 1 0 001 B

ldr r0 , =BANKSIZE

ldr r1 , [r0]

ldr r2 , =0xFF

bic r1 , r1 , r2

ldr r2 , =0xB1

orr r1 , r1 , r2

str r1 , [r0]

@MRSRB6[11:0] = 0 00 011 0 000 B

ldr r0 , =MRSRB6

ldr r1 , [r0]

ldr r2 , =0x3FF

bic r1 , r1 , r2

ldr r2 , =0x030

orr r1 , r1 , r2

str r1 , [r0]

mov pc , lr @函数返回

/******END内存初始化子程序*******/

build_page:

/********建立页表子程序***********/

/*对0地址处的映射*/

ldr r1 , =0x00000000 @VA起始地址

ldr r2 , =0x00000000 @PA起始地址

@*(MMU_TTB&0xFFFFC000+VA>>20<<2) = (PA&0xFFF00000)|(SPUP_WR(0x03)<<AP(10))|(MANAGER(0x02)<<DOMAIN(5))|(0x01<<4)|(NCNB_CTTBTT(0x00)<<Ctt_Btt(2))|(SECTION_FLAG(0x02))

ldr r0 , =(MMU_TTB & 0xFFFFC000)

mov r3 , r1 , LSR#20

add r0 , r0 , r3 , LSL#2 @r0存入段描述符所在一级页表条目地址

mov r3 , r2 , LSR#20

mov r3 , r3 , LSL#20

mov r4 , #0x0

ldr r4 , =SPUP_WR_SECTIONAP | MANAGER_DOMAIN | WT_CTTBTT | SECTION_FLAG

orr r3 , r3 , r4 @r3存入要放进页表的段描述符

str r3 , [r0] @往一级描述符地址放入对应的段描述符

/*END对0地址处的映射*/

/*对内存的映射*/

@VA:0xB0000000~0xB3FFFFFF------>PA:0x30000000~0x33FFFFFF

@using section-mapped

@总共64M,需要映射64次,每一次VA跟PA都要自加1M

ldr r1 , =0xB0000000 @VA起始地址

ldr r2 , =0x30000000 @PA起始地址

ldr r5 , =0xB4000000 @循环计数初值

l: @*(MMU_TTB&0xFFFFC000+VA>>20<<2) = (PA&0xFFF00000)|(SPUP_WR(0x03)<<AP(10))|(MANAGER(0x02)<<DOMAIN(5))|(0x01<<4)|(WT(0x03)<<Ctt_Btt(2))|(SECTION_FLAG(0x02))

ldr r0 , =(MMU_TTB & 0xFFFFC000)

mov r3 , r1 , LSR#20

add r0 , r0 , r3 , LSL#2 @r0存入段描述符所在一级页表条目地址

mov r3 , r2 , LSR#20

mov r3 , r3 , LSL#20

mov r4 , #0x0

ldr r4 , =SPUP_WR_SECTIONAP | MANAGER_DOMAIN | WT_CTTBTT | SECTION_FLAG

orr r3 , r3 , r4 @r3存入要放进页表的段描述符

str r3 , [r0] @往一级描述符地址放入对应的段描述符

add r1 , r1 , #0x100000

add r2 , r2 , #0x100000

cmp r1 , r5

bne l @如果VA没加到结尾就跳转到前一个l执行

/*END对内存的映射*/

/*对IO相关寄存器的映射*/

@VA:0xA0000000~0xA00003FF------>PA:0x56000000~0x560003FF

@using tiny page-mapped

ldr r1 , =0xA0000000 @VA起始地址

ldr r2 , =0x56000000 @PA起始地址

@因为IO相关寄存器地址范围不超过1K,所以我这里选择tiny page直接对1K进行设置

@Level one addr=(MMU_TTB&0xFFFFC000+VA>>20<<2)

@*(Level one addr)=(MMU_FPTB&0xFFFFF000)|(MANAGER(0x02)<<DOMAIN(5))|(0x01<<4)|(FINE_FLAG(0x03))

@Level two addr=(MMU_FPTB&0xFFFFF000+(VA>>8)&0xFFC)

@*(Level two addr)=(PA&0xFFFFFC00)|(SPUP_WR(0x03)<<AP(4))|(NCNB(00B)<<Ctt_Btt(2))|(TINY_FLAG(0x03))

ldr r0 , =MMU_TTB & 0xFFFFC000

mov r3 , r1 , LSR#20

add r0 , r0 , r3 , LSL#2 @r0存入小页描述符所在的一级页表条目地址

ldr r3 , =(MMU_FPTB & 0xFFFFF000) | MANAGER_DOMAIN | FINE_FLAG

str r3 , [r0] @往一级页表描述符地址放入对应的小页描述符

ldr r0 , =(MMU_FPTB & 0xFFFFF000)

mov r3 , r1 , LSR#8

ldr r4 , =0xFFC

and r3 , r3 , r4

add r0 , r0 , r3 @r0存入微小页描述符所在的二级细页表条目地址

mov r3 , r2 , LSR#10

mov r3 , r3 , LSL#10

ldr r4 , =SPUP_WR_TINYAP | NCNB_CTTBTT | TINY_FLAG

orr r3 , r3 , r4 @r3存入微小页描述符

str r3 , [r0] @往一级页表描述符索引到的地址放入对应的小页描述符

/*END对IO相关寄存器的映射*/

@返回到主程序

mov pc , lr

/********END建立页表子程序********/

copy_from_bootsram_to_sdram:

/*******拷贝测试代码子程序********/

@我们把测试代码加载在0x800=2048处(加载地址)

@拷贝到0x30005000=0x30000000(转换表基址)+0x4000(转换表大小)+0x1000(细页表大小)

mov r1 , #0x800 @源地址

ldr r2 , =0xB0005000 @目标地址

l1:

ldr r3 , [r1] , #0x04

str r3 , [r2] , #0x04

cmp r1 , #0x1000

bne l1

mov pc , lr

/******END拷贝测试代码子程序******/

mmu_init:

/*********初始化MMU子程序*********/

mov r1 , #0x0

mcr p15 , 0 , r1 , c7 , c7 , 0 @Invalidate Caches

mcr p15 , 0 , r1 , c7 , c10, 4 @清除write buffer

mcr p15 , 0 , r1 , c8 , c7 , 0 @Invalidate TLBs

ldr r1 , =MMU_TTB

mcr p15 , 0 , r1 , c2 , c0 , 0 @设置TTB register

ldr r1 , =0xAAAAAAB4

mcr p15 , 0 , r1 , c3 , c0 , 0 @设置domain access control register

mrc p15 , 0 , r1 , c1 , c0 , 0 @读取cp15 control register放入r1

ldr r2 , =0x3FFFFFFF

bic r1 , r1 , r2

ldr r2 , =0x517F

orr r1 , r1 , r2

@如果现在就启动了MMU,那么下面的返回地址就变成虚拟地址,所以跳转后再启动MMU

mov pc , lr @返回主程序

/******END初始化MMU子程序*********/

/************************************************END************************************************************/

④led_display.c

#define GPBCON (*(volatile unsigned long *)0xA0000010)

#define GPBDAT (*(volatile unsigned long *)0xA0000014)

/**

*如果不开启-O2以上优化,gcc编译器不提供inline优化,所以写入的inline只在编译时加入了-O2优化选项才会有效,这时for里面的循环参数可以不加volatile

*如果开启了-O2以上优化,gcc提供的inline优化有效,如果for循环里做编译器认为没意义的事,循环参数加上volatile声明。

*/

static inline void delay(volatile unsigned long dly)

{

for(; dly > 0; dly--)

}

int main()

{

unsigned long i = 0;

GPBCON &= ~(0xFF<<10);

GPBCON |= (0x55<<10); //把GPB5~8都置为输出功能

while(1){

delay(500000);

GPBDAT = (~(i<<5)); // 根据i的值,点亮LED1,2,3,4

if(++i == 16)

i = 0;

}

return 0;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: