首页 > 代码库 > [转]S3C2440_MMU详细说明

[转]S3C2440_MMU详细说明

 一、内存管理单元MMU介绍内存管理单元简称MMU,它负责虚拟地址到物理地址的映射,并提供硬件机制的内存访问权限检查。MMU使得每个用户进程拥有自己独立的地址空间,并通过内存访问权限的检查保护每个进程所用的内存不被其他进程破坏。
重点就在于地址映射:页表的结构与建立、映射的过程。

1S3C2440 MMU地址变换过程 
1)地址的分类
一个程序在运行之前,没有必要全部装入内存,仅需要将那些要运行的部分先装入内存,其余部分在用到时从磁盘载入,当内存不足时,再将暂时不用的部分调出到磁盘。
这使得大程序可以在较小的内存空间中运行,也使得内存中可以同时装入更多的程序并发执行,这样的存储器一般称为虚拟存储器。
虚拟地址最终需要转换为物理地址才能读写实际的数据,通过将虚拟地址空间和物理空间划分为同样大小的空间(段或页),然后两个空间建立映射关系。
由于虚拟地址空间远大于物理地址,可能多块虚拟地址空间映射到同一块物理地址空间,或者有些虚拟地址空间没有映射到具体的物理地址空间上去(使用到时再映射)。

ARM cpu地址转换涉及三种地址:虚拟地址(VAVirtual Address)、变换后的虚拟地址(MVAModified Virtual Address)、物理地址(PAPhysical Address
没有启动MMU时,CPU核心,cacheMMU,外设等所有部件使用的都是物理地址。
启动MMU后,CPU核心对外发出虚拟地址VAVA被转换为MVAcacheMMU使用,在这里MVA被转换成PA;最后使用PA读取实际设备

①CPU核心看到和用到的只是虚拟地址VA,至于VA如果去对应物理地址PACPU核心不理会
②caches和MMU看不到VA,他们利用MVA转换得到PA
③实际设备看不到VAMVA,读写它们使用的是物理地址PA

MVA是除CPU核心外的其他部分看到的虚拟地址,VAMVA的变化关系
如果VA<32M,需要使用进程标识号PID(通过读CP15C13获得)来转换为MVA

if (VA  < 32M) then
        MVA = VA | (PID << 25)
else
        MVA = VA

使用MVA,而不使用VA的目的是,当有重叠的VA时,转换为MVA地址并不重叠,减小转换为PA的代价
比如两个进程12VA都是0-32M-1),则MVA分别为0x02000000-0x03ffffff0x04000000-0x05ffffff
下文说到虚拟地址,如果没有特别指出,就是指MVA

2)虚拟地址到物理地址的转换过程
arm cpu使用页表来进行转换,页表由一个个条目组成,每个条目存储一段虚拟地址对应的物理地址及访问权限,或者下一级页表的地址
S3C2440最多会用到两级页表,以段(Section1M)的方式进行转换时只用到一级页表,以页(Page)的方式进行转换时用到两级页表。
页的大小有3种:大页(64KB),小页(4KB),极小页(1KB)。条目也称为描述符,有:段描述符、大页描述符、小页描述符、极小页描述符-保存段、大页、小页、极小页的起始物理地址;粗页表描述符、细页表描述符,它们保存二级页表的物理地址。

下图为S3C2440的地址转换图

 

TTB base代表一级页表的地址,将它写入协处理器CP15的寄存器C2(称为页表基址寄存器)即可,一级页表的地址是16K对齐,使用[31:14]存储页表基址[13:0]0
一级页表使用4096个描述符来表示4GB空间,每个描述符对应1MB的虚拟地址,存储它对应的1MB物理空间的起始地址,或者存储下一级页表的地址。使 用MVA[31:20]来索引一级页表(20-31一共12,2^12=4096,所以是4096个描述符),得到一个描述符,每个描述符占4个字节。
一级页表描述符格式如下:

 

 一级页表描述符

最低两位:
0b00:无效

0b01:粗页表(Coarse page table
        [31:10]为粗页表基址,此描述符低10位填充0后就是一个二级页表的物理地址,二级页表含256个条目(使用[9:2]2^8=256 个),称为粗页表(Coarse page table)。其中每个条目表示4KB大小的物理地址空间,一个粗页表表示1MB物理地址

0b10:段(Section
        [31:20]为段基址,、此描述符低20位填充0后就是一块1MB物理地址空间的起始地址。MVA[19:0],用来在这1MB空间中寻址。描述符的位[31:20]MVA[19:0]构成了这个虚拟地址MVA对应的物理地址

以段的方式进行映射时,虚拟地址MVA到物理地址PA的转换过程如下:
①页表基址寄存器位[31:14]MVA[31:20]组成一个低两位为032位地址,MMU利用这个地址找到段描述符
②取出段描述符的位[31:20](段基址),它和MVA[19:0]组成一个32位的物理地址(这就是MVA对应的PA

 段地址转换过程

0b11:细页表(Fine page table
        [31:12]为细页表基址(Fine page table base address),此描述符的低12位填充0后,就是一个二级页表的物理地址。此二级页表含1024个条目(使用[11:2]10位),其中每个条目表 示大小1kb的物理地址空间,一个细页表表示1MB物理地址空间

以大页(64KB),小页(4KB)或极小页(1KB)进行地址映射时,需要用到二级页表,二级页表有粗页表、细页表两种,二级页表描述符格式如下:

 

二级页表描述符


最低两位:
0b00:无效

0b01:大页描述符
        [31:16]为大页基址,此描述符的低16位填充0后就是一块64KB物理地址空间的起始地址粗页表中的每个条目只能表示4KB物理空间,如果 大页描述符保存在粗页表中,则连续16个条目都保存同一个大页描述符。类似的,细页表中每个条目只能表示1KB的物理空间,如果大页描述符保存在细页表 中,则连续64个条目都保存同一个大页描述符。
下面以保存在粗页表中的大页描述符为例,说明地址转化那过程
①页表基址寄存器[31:14]MVA[31:20]组成一个低两位为032位地址,MMU利用这个地址找到粗页表描述符
②取出粗页表描述符的[31:10](即粗页表基址),它和MVA[19:12]组成一个低两位为032位物理地址,通过这个地址找到大页描述符
③取出大页描述符的[31:16](即大页基址),它和MVA[15:0]组成一个32位的物理地址,即MVA对应的PA
步骤②和③中,用于在粗页表中索引的MVA[19:12]、用于在大页内寻址的MVA[15:0]有重合的位[15:12],当位[15:12]0b0000变化到0b1111时,步骤②得到的大页描述符相同,所以粗页表中有连续16个条目保存同一个大页描述符

 

 大页的地址转换过程(大页描述符保存在粗页表中)

0b10:小页描述符
[31:12]为小页基址(Small page base address),此描述符的低12位填充0后就是一块4kb[11:0],一共12,2^12=4096)物理地址空间的起始地址。粗页表中每个条 目表示4kb的物理空间,如果小页描述符保存在粗页表中,则只需要用一个条目来保存一个小页描述符。类似的,细页表中每个条目只能表示1kb的物理空间, 如果小页保存在细页表中,则连续4个条目都保存同一个小页描述符。
下面以保存在粗页表中的小页描述符为例,说明地址转换过程:
①页表基址[31:14]MVA[31:20]组成一个低两位为032位地址,MMU利用这个地址找到粗页表描述符
②取出粗页表描述符[31:10](即粗页表基址),它和MVA[19:12]组成一个低两位为032位物理地址,用这个地址找到小页描述符
③取出小页描述符的位[31:12](即小页基址),它和MVA[11:0]组成一个32位物理地址(即MVA对应的PA
小页描述符保存在细页表中,地址转换过程和上面类似。

 

小页的地址转换过程(小页描述符保存在粗页表中)

0b11:极小页描述符
        [31:10]为极小页基址(Tiny page base address),此描述符的低10位填充0后就是一块1KB物理地址空间的起始地址。极小页描述符只能保存在细页表中,用一个条目来保存一耳光极小页描述符
下面是极小页的地址转换过程:
①页表基址寄存器[31:14]MVA[31:20]组成一个低两位为032位地址,MMU通过这个地址找到细页表描述符
②取出细页表描述符[31:12](即细页表基址),它和MVA[19:10]组成一个低两位为032位物理地址,通过这个地址即可找到极小页描述符
③取出极小页描述符[31:10](即极小页基址),它和MVA[9:0]组成一个32位的物理地址(即MVA对应的PA

 

 极小页的地址转换过程(极小页描述符保存在粗页表中)

从段、大页、小页、极小页的地址转换过程可知
①以段进行映射时,通过MVA[31:20]结合页表得到一段(1MB)的起始物理地址,MVA[19:0]用来在段中寻址
②以大页进行映射时,通过MVA[31:16]结合页表得到一个大页(64KB)的起始物理地址,MVA[15:0]用来在小页中寻址
③以小页进行映射时,通过MVA[31:12]结合页表得到一个小页(4KB)的起始物理地址,MVA[11:0]用来在小页中寻址
④以极小页进行映射时,通过MVA[31:10]结合页表得到一个极小页(1KB)的起始物理地址,MVA[9:0]用来在极小页中寻址

 

2、内存的访问权限检查

它决定一块内存是否允许读、是否允许写。这由CP15寄存器C3(域访问控制)、描述符的域(Domain)、CP15寄存器C1R/S/A位、描述符的AP位共同决定。

决定是否对某块内存进行权限检查,“AP”决定如何对某块内容进行权限检查。

S3C244016个域,CP15寄存器C3中每两位对应一个域(一共32位),用来表示这个域是否进行权限检查

每两位数据的含义

00:无访问权限(任何访问都将导致“Domain fault”异常)

01:客户模式(使用段描述符、页描述符进行权限检查)

10:保留(保留,目前相当于无访问权限

11:管理模式(不进行权限检查,允许任何访问)

Domain占用4位,用来表示内存属于0-15,哪一个域

例如:

①段描述符中的“Domain”0b0010,表示1MB内存属于域2,如果域访问控制寄存器的[5:4]等于0b00,则访问这1MB空间都会产生“Domain fault”异常,如果等于0b01,则使用描述符中的“Ap”位进行权限检查

② 粗页表中的“Domain”0b1010,表示1MB内存属于域10,如果域访问控制寄存器的[21:20]等于0b01,则使用二级页表中的大页/小 页描述符中的"ap3""ap2""ap1""ap0"位进行权限检查,如果等于0b11,则允许任何访问,不进行权限检查。

如下图:

  

 

 一级页表描述符

 

 二级页表描述符

APap3ap2ap1ap0结合CP15寄存器C1R/S位,决定如何进行访问检查。
段描述符中AP控制整个段(1MB)访问权限;大页描述符每个apx0-3)控制一个大页(64KB)中1/4内存的访问权限,即ap3对应大页高端的 16KBap0对应大页低端的16KB;小页描述符与大页描述符类似,每个apx0-3)控制一个小页(4KB)的1/4内存的访问权限;极小页中的 ap控制整个极小页(1KB)的访问权限。
下表为APSR的对照表

AP

S

R

特权模式

用户模式

说明

00

0

0

无访问权限

无访问权限

任何访问将产生“Permission fault”异常

00

1

0

只读

无访问权限

在超级权限下可以进行读操作

00

0

1

只读

只读

任何写操作将产生”Permission fault“异常

00

1

1

保留

-

-

01

x

x

/

无访问权限

只允许在超级模式下访问

10

x

x

/

只读

在用户模式下进行写操作将产生"Permission fault"异常

11

x

x

/

/

在所有模式下允许任何访问

xx

1

1

保留

-

-

 

3TLB的作用

MVAPA的转换需要访问多次内存,大大降低了CPU的性能,有没有办法改进呢?

程序执行过程中,用到的指令和数据的地址往往集中在一个很小的范围内,其中的地址、数据经常使用,这是程序访问的局部性。

由此,通过使用一个高速、容量相对较小的存储器来存储近期用到的页表条目(段、大页、小页、极小页描述符),避免每次地址转换都到主存中查找,这样就大幅提高性能。这个存储器用来帮助快速地进行地址转换,成为转译查找缓存(Translation Lookaside Buffers, TLB

CPU发出一个虚拟地址时,MMU首先访问TLB。如果TLB中含有能转换这个虚拟地址的描述符,则直接利用此描述符进行地址转换和权限检查,否则MMU访问页表找到描述符后再进行地址转换和权限检查,并将这个描述符填入TLB中,下次再使用这个虚拟地址时就直接使用TLB用的描述符。

使用TLB需要保证TLB中的内容与页表一致,在启动MMU之前,页表中的内容发生变化后,尤其要注意。一般的做法是在启动MMU之前使整个TLB无效,改变页表时,使所涉及的虚拟地址对应的TLB中条目无效。

 

4Cache的作用

同样基于程序访问的局部性,在主存和CPU通用寄存器之间设置一个高速的、容量相对较小的存储器,把正在执行的指令地址附近的一部分指令或数据从主存调入这个存储器,供CPU在一段时间内使用,对提高程序的运行速度有很大作用。这个cache一般称为高速缓存。

①写穿式(Write Through

任一CPU发出写信号送到Cache的同时,也写入主存,保证主存的数据同步更新。优点是操作简单,但由于主存速度慢,降低了系统的写速度并占用了总线的时间。

②回写式(Write Back

数据一般只写到Cache,这样可能出现Cache中的数据得到更新而主存中的数据不变(数据陈旧)的情况。此时可在Cache中设一个标志地址及数据陈旧的信息,只有当Cache中的数据被换出或强制进行清空操作时,才将原更新的数据写入主存响应的单元中,保证了Cache和主存中数据一致。

 

Cache有以下两个操作:

①”清空clean):把CacheWrite buffer中已经脏的(修改过,但未写入主存)数据写入主存

②”使无效Invalidate):使之不能再使用,并不将脏的数据写入主存。

 

S2C2440内置了指令CacheICaches)、数据CacheDCaches)、写缓存(Write buffer),需要用到描述符中的C位(Ctt)和B位(Btt

1)指令CacheICaches

系统刚上电或复位时,ICaches中的内容是无效的,并且ICaches功能关闭。往Icr位(CP15协处理器中寄存器1的第12位)写1可以启动ICaches,写0停止ICaches

ICaches一般在MMU开启后使用,此时描述符的C位用来表示一段内存是否可以被Cache。若Ctt=1,允许Cache,否则不允许。如果MMU没有开启,ICaches也可以被使用,此时CPU读取指令时所涉及的内存都被当做允许Cache

ICaches关闭时,CPU每次取指都要读取主存,性能低,所以通常尽早启动ICaches

ICaches开启后,CPU每次取指时都会先在ICaches中查看是否能找到所用指令,而不管Ctt0还是1。如果找到成为Cache命中,找不到称为Cache丢失,ICaches被开启后,CPU的取指有如下三种情况:

①Cache命中且Ctt1时,从ICaches中取指,返回CPU

②Cache丢失且Ctt1时,CPU从主存中取指,并且把指令缓存到Cache

③Ctt为0时,CPU从主存中取指

2)数据CacheDCaches

ICaches相似,系统刚上电或复位时,DCaches中的内容无效,并且DCaches功能关闭,Write buffer中的内容也是被废弃不用的。往Ccr位(CP15协处理器 中寄存器1的第二位)写1启动DCaches,写0停止DCachesWrite bufferDCaches紧密结合,额米有专门的控制来开启和停止它

ICaches不同,DCaches功能必须在MMU开启之后才能被使用。

DCaches被关闭时,CPU每次都去内存取数据。

DCaches被开启后,CPU每次读写数据时都会先在DCaches中查看是否能找到所要的数据,不管Ctt0还是1,找到了成为Cache命中,找不到成为Cache丢失。

通过下表可知DCachesWrite bufferCcrCttBtt各种取值下,如何工作,Ctt and Ccr 意为 CttCcr进行逻辑与后的值

 

Ctt and Ccr

Btt

DCachesWrite buffer 和主存的访问方式

0

0

Non-cachednon-buffered(NCNB)

读写数据时都是直接操作主存,并且可以被外设中止;

写数据时不使用Write bufferCPU会等待写操作完成;

不会出现Cache命中

0

1

Non-Cached buffered(NCB)

读数据时都是直接操作主存;

不会出现Cache命中;

写数据时,数据线存入Write buffer,并在随后写入主存;

数据存入Write buffer后,CPU立即继续执行;

读数据时,可以被外设中止;

写数据时,无法被外设中止

1

0

Cached,write-through(写通)mode

读数据时,如果Cache命中则从Cache中返回数据,不读取主存;

读数据时,如果Cache丢失则从读主存中返回数据,并导致“linefill”的动作;

写数据时,数据先存入Write buffer,并在随后写入主存;

数据存入Write buffer后,CPU立即继续执行;

写数据时,如果Cache命中则新数据也写入Cache中;

写数据时,无法被外设中止

1

1

Cached,write-back(写回) mode

读数据时,如果Cache命中则从Cache中返回数据,不读取主存;

读数据时,如果Cache丢失则从读主存中返回数据,并导致“linefile”的动作;

写数据时,如果Cache丢失则将数据先存入Write buffer,存储完毕后CPu立即继续执行,这些数据在随后写入主存;

写数据时,如果Cache命中则在Cache中更新数据,并设置这些数据为脏的,但是不会写入主存;

无论Cache命中与否,写数据都无法被外设中止

使用Cache时需要保证CacheWrite buffer的内容和主存内容一致,保证下面两个原则:

①清空DCaches,使主存数据得到更新

②使无效ICaches,使CPU取指时重新读取主存

在实际编写程序时,要注意如下几点:

①开启MMU前,十五小ICachesDCachesWrite buffer

②关闭MMU前,清空ICachesDCaches,即将数据写到主存上

③如果代码有变,使无效ICaches,这样CPU取指时会从新读取主存

④使用DMA操作可以被Cache的内存时,将内存的数据发送出去时,要清空Cache;将内存的数据读入时,要使无效Cache

⑤改变页表中地址映射关系时也要慎重考虑

⑥开启ICachesDCaches时,要考虑ICachesDCaches中的内容是否与主存保持一致

⑦对于I/O地址空间,不使用CacheWrite buffer

 

5S3C2440 MMUTLBCache的控制指令

S3C2440除了ARM920TCPU核心外,还有若干个协处理器,用来帮助主CPu完成一些特殊功能。对MMUTLBCache等的操作涉及到协处理器。

<MCR|MRC>{条件协处理器编码,协处理器操作码1,目的寄存器,源寄存器1,源寄存器2,协处理器操作码2

<MCR|MRC> {cond} p#,<expression1>,Rd,cn,cm{,<expression2>}

MRC        //从协处理器获得数据,传给ARM920T CPU核心寄存器

MCR        //数据从ARM920T CPU核心寄存器传给协处理器

{cond}        //执行条件,省略时表示无条件执行

p#        //协处理器序号

<expression1>        //一个常数

Rd        //ARM920T CPU核心的寄存器

cncm        //协处理器中的寄存器

<expression2>        //一个常数

其中,<expression1>cncm<expression2>仅供协处理器使用,它们的作用如何取决于具体的协处理器

 

二、MMU使用实例:地址映射

这个实例将开启MMU,并将虚拟地址0xA0000000-0xA0100000映射到物理地址0x56000000-0x56100000GPBCON物理地址为0x56000010GPBDAT物理地址为0x56000014),来驱动LED

将虚拟地址0xB0000000-0xB3FFFFFF映射到物理地址0x30000000-0x33FFFFFF,在连接程序时,将一部分代码的运行地址指定为0xB0004000.

这 个程序只使用一级页表,以段的方式进行地址映射,32CPU虚拟地址空间达到4G,一级页表使用4096个描述符来表示4G空间(每个描述符对应 1MB),每个描述符占4字节,所以一级页表占16KB。这个程序使用SDRAM的开始16KB存放一级页表,所以剩下的内存开始地址就为 0x30004000,这个地址最终会对应虚拟地址0xB0004000(所以代码运行地址为0xB0004000

程序分为两部分:第一部分的运行地址为0,它用来初始化SDRAM,复制第二部分的代码到SDRAM中(存放在0x30004000)、设置页表、启动MMU,最后跳到SDRAM中(地址0xB0004000),第二部分运行地址设为0xB0004000,用来驱动LED

先看连接文件mmu.lds

 

SECTIONS { 

  firtst    0x00000000 : { head.o init.o }

  second    0xB0004000 : AT(2048) { leds.o }

程序分两个段:firstsecondfirsthead.oinit.o组成,加载和运行地址都是0secondleds.o组成,加载地址为2048,重定位地址为0xB0004000

 

@*************************************************************************

@ Filehead.S

功能:设置SDRAM,将第二部分代码复制到SDRAM,设置页表,启动MMU

@       然后跳到SDRAM继续执行

@*************************************************************************       

.text

.global _start

_start:

    ldr sp, =4096                       @ 设置栈指针,以下都是C函数,调用前需要设好栈

    bl  disable_watch_dog               @ 关闭WATCHDOG,否则CPU会不断重启

    bl  memsetup                        @ 设置存储控制器以使用SDRAM

    bl  copy_2th_to_sdram               @ 将第二部分代码复制到SDRAM

    bl  create_page_table               @ 设置页表

    bl  mmu_init                        @ 启动MMU,启动以后下面代码都用虚拟地址

    ldr sp, =0xB4000000                 @ 重设栈指针,指向SDRAM顶端(使用虚拟地址)

    ldr pc, =0xB0004000                 @ 跳到SDRAM中继续执行第二部分代码

halt_loop:

    b   halt_loop

/*

* init.c: 进行一些初始化,在Steppingstone中运行

它和head.S同属第一部分程序,此时MMU未开启,使用物理地址

*/ 

/* WATCHDOG寄存器 */

#define WTCON           (*(volatile unsigned long *)0x53000000)

/* 存储控制器的寄存器起始地址 */

#define MEM_CTL_BASE    0x48000000

 

     /*

关闭WATCHDOG,否则CPU会不断重启

*/

void disable_watch_dog(void)

{

    WTCON = 0;  // 关闭WATCHDOG很简单,往这个寄存器写0即可

}

/*

设置存储控制器以使用SDRAM

*/

void memsetup(void)

{

    /* SDRAM 13个寄存器的值 */

    unsigned long  const    mem_cfg_val[]={ 0x22011110,     //BWSCON

                                            0x00000700,     //BANKCON0

                                            0x00000700,     //BANKCON1

                                            0x00000700,     //BANKCON2

                                            0x00000700,     //BANKCON3  

                                            0x00000700,     //BANKCON4

                                            0x00000700,     //BANKCON5

                                            0x00018005,     //BANKCON6

                                            0x00018005,     //BANKCON7

                                            0x008C07A3,     //REFRESH

                                            0x000000B1,     //BANKSIZE

                                            0x00000030,     //MRSRB6

                                            0x00000030,     //MRSRB7

                                    };

    int     i = 0;

    volatile unsigned long *p = (volatile unsigned long *)MEM_CTL_BASE;

    for(; i < 13; i++)

        p = mem_cfg_val;        //循环复制13个寄存器到内存控制器基址

}

/*

将第二部分代码复制到SDRAM

*/

void copy_2th_to_sdram(void)

{

    unsigned int *pdwSrc  = (unsigned int *)2048;        //第二段代码加载地址2048

    unsigned int *pdwDest = (unsigned int *)0x30004000;        //0x30004000前放页表

 

    while (pdwSrc < (unsigned int *)4096) //4kb最大4096

    {

        *pdwDest = *pdwSrc;

        pdwDest++;

        pdwSrc++;

    }

}

/*

设置页表

*/

void create_page_table(void)

{

/* 

用于段描述符的一些宏定义

*[31:20]段基址,[11:10]AP[8:5]Domain[3]C[2]B[1:0]0b10为段描述符

*/ 

#define MMU_FULL_ACCESS     (3 << 10)   /* 访问权限AP */

#define MMU_DOMAIN          (0 << 5)    /* 属于哪个域 Domain*/

#define MMU_SPECIAL         (1 << 4)    /* 必须是1 */

#define MMU_CACHEABLE       (1 << 3)    /* cacheable C*/

#define MMU_BUFFERABLE      (1 << 2)    /* bufferable B*/

#define MMU_SECTION         (2)         /* 表示这是段描述符 */

#define MMU_SECDESC         (MMU_FULL_ACCESS | MMU_DOMAIN | MMU_SPECIAL | \

                             MMU_SECTION)

#define MMU_SECDESC_WB      (MMU_FULL_ACCESS | MMU_DOMAIN | MMU_SPECIAL | \

                             MMU_CACHEABLE | MMU_BUFFERABLE | MMU_SECTION)

#define MMU_SECTION_SIZE    0x00100000        /*每个段描述符对应1MB大小空间*/

 

    unsigned long virtuladdr, physicaladdr;

    unsigned long *mmu_tlb_base = (unsigned long *)0x30000000;        /*SDRAM开始地址存放页表*/

    /*

     * Steppingstone的起始物理地址为0,第一部分程序的起始运行地址也是0

     * 为了在开启MMU后仍能运行第一部分的程序,

     * 01M的虚拟地址映射到同样的物理地址

     */

    virtuladdr = 0;

    physicaladdr = 0;

     //虚拟地址[31:20]用于索引一级页表,找到它对应的描述符,对应于(virtualaddr>>20)

     //段描述符中[31:20]保存段的物理地址,对应(physicaladdr & 0xFFF00000)

    *(mmu_tlb_base + (virtuladdr >> 20)) = (physicaladdr & 0xFFF00000) | \

                                            MMU_SECDESC_WB;

    /*

     * 0x56000000GPIO寄存器的起始物理地址,

     * GPBCONGPBDAT这两个寄存器的物理地址0x560000100x56000014

     * 为了在第二部分程序中能以地址0xA00000100xA0000014来操作GPBCONGPBDAT

     * 把从0xA0000000开始的1M虚拟地址空间映射到从0x56000000开始的1M物理地址空间

     */

    virtuladdr = 0xA0000000;

    physicaladdr = 0x56000000;

    *(mmu_tlb_base + (virtuladdr >> 20)) = (physicaladdr & 0xFFF00000) | \

                                            MMU_SECDESC;

    /*

     * SDRAM的物理地址范围是0x300000000x33FFFFFF

     * 将虚拟地址0xB00000000xB3FFFFFF映射到物理地址0x300000000x33FFFFFF上,

     * 总共64M,涉及64个段描述符

     */

    virtuladdr = 0xB0000000;

    physicaladdr = 0x30000000;

    while (virtuladdr < 0xB4000000)

    {

        *(mmu_tlb_base + (virtuladdr >> 20)) = (physicaladdr & 0xFFF00000) | \

                                                MMU_SECDESC_WB;

        virtuladdr += 0x100000;        //右移20位就是1

        physicaladdr += 0x100000;        //右移20位就是1

    }

}

/*

启动MMU

*/

void mmu_init(void)

{

    unsigned long ttb = 0x30000000;

__asm__(

    "mov    r0, #0\n"

    "mcr    p15, 0, r0, c7, c7, 0\n"    /* 使无效ICachesDCaches */

    "mcr    p15, 0, r0, c7, c10, 4\n"   /* drain write buffer on v4 */

    "mcr    p15, 0, r0, c8, c7, 0\n"    /* 使无效指令、数据TLB */

    "mov    r4, %0\n"                   /* r4 = 页表基址 */

    "mcr    p15, 0, r4, c2, c0, 0\n"    /* 设置页表基址寄存器 */

    "mvn    r0, #0\n"                   

    "mcr    p15, 0, r0, c3, c0, 0\n"    /* 域访问控制寄存器设为0xFFFFFFFF, 不进行权限检查*/    

    /* 

     * 对于控制寄存器,先读出其值,在这基础上修改感兴趣的位,

     * 然后再写入

     */

    "mrc    p15, 0, r0, c1, c0, 0\n"    /* 读出控制寄存器的值 */

    /* 控制寄存器的低16位含义为:.RVI ..RS B... .CAM

     * R : 表示换出Cache中的条目时使用的算法,

     *     0 = Random replacement1 = Round robin replacement

     * V : 表示异常向量表所在的位置,

     *     0 = Low addresses = 0x000000001 = High addresses = 0xFFFF0000

     * I : 0 = 关闭ICaches1 = 开启ICaches

     * RS : 用来与页表中的描述符一起确定内存的访问权限

     * B : 0 = CPU为小字节序;1 = CPU为大字节序

     * C : 0 = 关闭DCaches1 = 开启DCaches

     * A : 0 = 数据访问时不进行地址对齐检查;1 = 数据访问时进行地址对齐检查

     * M : 0 = 关闭MMU1 = 开启MMU

     */

    /*  

     * 先清除不需要的位,往下若需要则重新设置它们    

     */
                               /* .RVI ..RS B... .CAM */ 

    "bic    r0, r0, #0x3000\n"          /* ..11 .... .... .... 清除VI位 */

    "bic    r0, r0, #0x0300\n"          /* .... ..11 .... .... 清除RS位 */

    "bic    r0, r0, #0x0087\n"          /* .... .... 1... .111 清除B/C/A/M */

    /*

     * 设置需要的位

     */

    "orr    r0, r0, #0x0002\n"          /* .... .... .... ..1. 开启对齐检查 */

    "orr    r0, r0, #0x0004\n"          /* .... .... .... .1.. 开启DCaches */

    "orr    r0, r0, #0x1000\n"          /* ...1 .... .... .... 开启ICaches */

    "orr    r0, r0, #0x0001\n"          /* .... .... .... ...1 使能MMU */

    "mcr    p15, 0, r0, c1, c0, 0\n"    /* 将修改的值写入控制寄存器 */

    : /* 无输出 */

    : "r" (ttb) );

}

 

[转]S3C2440_MMU详细说明