试验意图:启用MMU,映射SDRAM的地址空间,操作虚拟地址完结“点灯大法”,借此把握MMU的运用。
实 验环境及阐明:恒颐S3C2410开发板H2410。H2410中心板扩展有64MB的K4S561632 SDRAM(4M*16bit*4BANK),地址规模是0x30000000~0x33FFFFFF。GPIO端口的地址规模是 0x56000000~0X560000B0。
实 验思路:开发板上电发动后,主动将NandFlash开端的4K数据复制到SRAM中,然后跳转到0地址开端履行,然后初始化存储操控器SDRAM,把 2K后的代码从SRAM中复制到SDRAM中(寄存在0x30004000,前16KB用来寄存页表)、设置页表、发动MMU完结虚拟地址映射GPIO寄 存器和SDRAM,终究跳转到SDRAM中(地址0xB0004000)运转。从头设置栈指针,跳到点灯代码的进口点完结点灯操作。
常识把握:MMU地址转化、内存拜访权限查看、TLB及Cache的运用
一、MMU地址转化:
1. 首要弄铲除为什么要运用MMU纳?MMU即内存办理单元,直白一点的讲,就像食堂的餐具,一切的学生一同吃饭时不够用,但食堂又不想再出资购买新的餐具 (原因很明显:一方面要本钱,另一方面又占当地。这就像添加内存相同),那么有没有解决方法?依据以往经历得知不或许全校园的学习一同都到食堂吃饭,所以 食堂就找几个人担任餐具的办理(相当于MMU),他们一方面发放餐具,确保来的同学有餐具可用,另一方面又收回用完的餐具(这就相当于虚拟地址到物理地址 之间建立了一个映射相同,内存仍是那么多,但从恣意单个程序视点都如同用不完相同)。当然假如有同学一个人拿好几套餐具必定不答应的(这就相当于内存的权 限查看)。MMU在地址转化进程中触及到三种地址:(VA—Virtual Address,虚拟地址)—这个就相当于餐具寄存的当地(我们都能够领到餐具)。CPU中心看到和用到的仅仅虚拟地址VA,至于VA假如去对应物理 地址PA,CPU中心不理睬,我们也不会去关怀一共有多少餐具吧;(MVA—Modified Virtual Address,改换后的虚拟地址)—这个相当于放假的时分,人很少,只发餐具好了,用过的就不先收回了,节约人员了。Caches和MMU看不到 VA,他们运用MVA转化得到PA,放假了收回餐具的人也不需求一向寻觅用完的餐具;(PA—Physical Address,物理地址)—实践的餐具量,就那些。实践设备看不到VA、MVA,读写它们运用的是物理地址PA,同学们就餐一般会领到餐具。
2. 虚拟地址到物理地址的转化进程。ARM运用页表来进行转化,S3C2410最多会用到两级页表,以段(Section,1M)的方法进行转化时只用到一级 页表,以页(Page)的方法进行转化时用到两级页表。页的巨细有3种:大页(64KB)、小页(4KB)和极小页(1KB)。本文仅仅以段地址转化进程 为例来解说一下,页的转化迥然不同。
★首要有个页表基址寄存器(方位为协处理器CP15的寄存器C2),它里边写入的便是一级页表的地址,经过读 取它就能够找到一级页表寄存的开端方位。一级页表的地址是16K对齐(所以[13:0]为0,运用[31:14]存储页表基址)。一级页表运用4096个 描述符来表明4GB空间,所以每个描述符对应1MB的虚拟地址,存储它对应的1MB物理空间的开端地址,或许存储下一级页表的地址。运用 MVA[31:20]来索引一级页表(31-20一共12位,2^12=4096,所以是4096个描述符),得到一个描述符,每个描述符占4个字节。
★ 描述符终究两位为0B10时,便是段的方法映射。[31:20]为段基址,此描述符低20位填充0后便是一块1MB物理地址空间的开端地址。 MVA[19:0]用来在这1MB空间中寻址。描述符的位[31:20]和MVA[19:0]构成了这个虚拟地址MVA对应的物理地址。以段的方法进行映 射时,虚拟地址MVA到物理地址PA的转化进程如下:①页表基址寄存器位[31:14]和MVA[31:20]组成一个低两位为0的32位地址,MMU利 用这个地址找到段描述符;②取出段描述符的位[31:20](段基址),它和MVA[19:0]组成一个32位的物理地址(这便是MVA对应的PA)。
'700')this.width='700';if(this.offsetHeight>'700')this.height='700';" src="http://www.arm79.com/attachment/Mon_1005/73_67_c19a93f3ccea9b3.jpg" onclick="if(this.width>=700) window.open('http://www.arm79.com/attachment/Mon_1005/73_67_c19a93f3ccea9b3.jpg');" border="0" width="700">
'700')this.width='700';if(this.offsetHeight>'700')this.height='700';" src="http://www.arm79.com/attachment/Mon_1005/73_67_ba0dd29d824d17a.jpg" onclick="if(this.width>=700) window.open('http://www.arm79.com/attachment/Mon_1005/73_67_ba0dd29d824d17a.jpg');" border="0" width="700">
二、内存的拜访权限查看
内 存的拜访权限查看决议一块内存是否答应读/写。这由CP15寄存器C3(域拜访操控)、描述符的域(Domain)、CP15寄存器C1的R/S/A位和 描述符的AP位一同决议。“域”决议是否对某块内存进行权限查看,"AP"决议怎么对某块内容进行权限查看。S3C2440有16个域,CP15寄存器 C3中每两位对应一个域(一共32位),用来表明这个域是否进行权限查看。
每两位数据的意义:00—无拜访权限(任何拜访都将导 致"Domain fault"反常);01—客户形式(运用段描述符、页描述符进行权限查看);10—保存(保存,现在相当于“无拜访权限”);11—办理模 式(不进行权限查看,答应任何拜访)。"Domain"占用4位,用来表明内存归于0-15哪一个域。
三、TLB和Cache
首要说两者都是运用程序拜访的局部性原理,经过设置高速、小容量的存储器来进步功用。
1.(TLB—Translation Lookaside Buffers,转译查找缓存):因为从MVA到PA的转化需求拜访屡次内存,大大降低了CPU的功用,故提出TLB方法改善。当CPU宣布一个虚拟地址 时,MMU首要拜访TLB。假如TLB中含有能转化这个虚拟地址的描述符,则直接运用此描述符进行地址转化和权限查看,不然MMU拜访页表找到描述符后再 进行地址转化和权限查看,并将这个描述符填入TLB中,下次再运用这个虚拟地址时就直接运用TLB用的描述符。运用TLB需求确保TLB中的内容与页表一 致,在发动MMU之前,页表中的内容发生变化后,特别要注意。一般的做法是在发动MMU之前使整个TLB无效,改动页表时,使所触及的虚拟地址对应的 TLB中条目无效。
2.(Cache,高速缓存):为进步程序的运转速度,在主存和CPU通用寄存器之间设置一个高速的、容量相对较小的存储器,把正在履行的指令地址邻近的一部分指令或数据从主存调入这个存储器,供CPU在一段时刻内运用。
★ 写数据的两种方法:①(Write Through,写穿式)—任一CPU宣布写信号送到Cache的一同,也写入主存,确保主存的数据同步更新。长处是操作简略,但因为主存速度慢,降 低了体系的写速度并占用了总线的时刻。②(Write Back,回写式)—数据一般只写到Cache,这样或许呈现Cache中的数据得到更新而主存中的数据不变(数据陈腐)的状况。此刻可在Cache 中设一个标志地址及数据陈腐的信息,只有当Cache中的数据被换出或强制进行”清空“操作时,才将原更新的数据写入主存呼应的单元中,确保了Cache 和主存中数据共同。
★Cache有以下两个操作:①(Clean,清空)—把Cache或Write buffer中现已脏的(修正正,但未写入主存)数据写入主存。②(Invalidate,使无效)—使之不能再运用,并不将脏的数据写入主存。
★S2C2440 内置了(ICaches,指令Cache)、(DCaches,数据Cache)和(Write buffer,写缓存),操作时需求用到描述符中的C位(Ctt)和B位(Btt)。①(ICaches,指令Cache)—体系刚上电或复位 时,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中查看是否能找到所用指 令,而不论Ctt是0仍是1。假如找到成为Cache射中,找不到称为Cache丢掉,ICaches被敞开后,CPU的取指有如下三种状况:Cache 射中且Ctt为1时,从ICaches中取指,回来CPU;Cache丢掉且Ctt为1时,CPU从主存中取指,而且把指令缓存到Cache中;Ctt为 0时,CPU从主存中取指。②(DCaches,数据Cache)—与ICaches类似,体系刚上电或复位时,DCaches中的内容无效,而且 DCaches功用封闭,Write buffer中的内容也是被抛弃不必的。往Ccr位(CP15协处理器 中寄存器1的第二位)写1发动DCaches,写0中止DCaches。Write buffer和DCaches紧密结合,额米有专门的操控来敞开和中止它。与ICaches不同,DCaches功用有必要在MMU敞开之后才干被运用。 DCaches被封闭时,CPU每次都去内存取数据。DCaches被敞开后,CPU每次读写数据时都会先在DCaches中查看是否能找到所要的数据, 不论Ctt是0仍是1,找到了称为Cache射中,找不到称为Cache丢掉。
★运用Cache时需求确保Cache、Write buffer的内容和主存内容共同,确保下面两个准则:①清空DCaches,使主存数据得到更新。②使无效ICaches,使CPU取指时从头读取主存。
在 实践编写程序时,要注意如下几点:①敞开MMU前,使无效ICaches,DCaches和Write buffer。②封闭MMU前,清空ICaches、DCaches,行将“脏”数据写到主存上。③假如代码有变,使无效ICaches,这样CPU取指 时会从头读取主存。④运用DMA操作能够被Cache的内存时:将内存的数据发送出去时,要清空Cache;将内存的数据读入时,要使无效Cache。⑤ 改动页表中地址映射联系时也要慎重考虑。⑥敞开ICaches或DCaches时,要考虑ICaches或DCaches中的内容是否与主存保持共同。⑦ 关于I/O地址空间,不运用Cache和Write buffer。
四、MMU、TLB及Cache的操控指令
S3C2410除了ARM920T的CPU中心外,还有若干个协处理器,用来协助主CPU完结一些特别功用,对MMU、TLB及Cache等的操作就触及到协处理器。格局如下:
{条件} 协处理器编码,协处理器操作码1,意图寄存器,源寄存器1,源寄存器2,协处理器操作码2
{cond} p#,,Rd,cn,cm{,}
MRC //从协处理器取得数据,传给ARM920T CPU中心寄存器
MCR //数据从ARM920T CPU中心寄存器传给协处理器
{cond} //履行条件,省掉时表明无条件履行
p# //协处理器序号
//一个常数
Rd //ARM920T CPU中心的寄存器
cn和cm //协处理器中的寄存器
//一个常数
其间,、cn、cm、仅供协处理器运用,它们的效果怎么取决于详细的协处理器。
示例代码解析:
开 启MMU,并将虚拟地址0xA0000000~0xA0100000映射到物理地址0x56000000~0x56100000(GPFCON物理地址为 0x56000050,GPFDAT物理地址为0x56000054);将虚拟地址0xB0000000~0xB3FFFFFF映射到物理地址 0x30000000~0x33FFFFFF。本示例以段的方法进行地址映射,只运用一级页表,经过上面内容可知一级页表运用4096个描述符来表明4G 空间(每个描述符对应1MB),每个描述符占4字节,所以一级页表占16KB。运用SDRAM的开端16KB寄存一级页表,所以剩余的内存开端地址就为 0x30004000,这个地址终究会对应虚拟地址0xB0004000(所以代码运转地址为0xB0004000)。
★程序履行首要流程的示例代码。
.text
.global _start
_start:
bl disable_watch_dog @ 封闭WATCHDOG,不然CPU会不断重启
bl mem_control_setup @ 设置存储操控器以运用SDRAM
ldr sp, =4096 @ 设置栈指针,以下是C函数调用前需求设好栈
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
★设置页表。
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后仍能运转榜首部分的程序, 将0~1M的虚拟地址映射到相同的物理地址
*/
virtuladdr = 0;
physicaladdr = 0;
/*虚拟地址[31:20]用于索引一级页表,找到它对应的描述符,对应于(virtualaddr>>20)。段描述符中[31:20]保存段的物理地址,对应(physicaladdr & 0xFFF00000)*/
*(mmu_tlb_base + (virtuladdr >> 20)) = (physicaladdr & 0xFFF00000) | MMU_SECDESC_WB;
/*
* 0x56000000是GPIO寄存器的开端物理地址,GPBCON和GPBDAT这两个寄存器的物理地址0x56000010、0x56000014, 为了在第二部分程序中能以地址0xA0000010、0xA0000014来操作GPBCON、GPBDAT,
* 把从0xA0000000开端的1M虚拟地址空间映射到从0x56000000开端的1M物理地址空间
*/
virtuladdr = 0xA0000000;
physicaladdr = 0x56000000;
*(mmu_tlb_base + (virtuladdr >> 20)) = (physicaladdr & 0xFFF00000) | MMU_SECDESC;
/*
* SDRAM的物理地址规模是0x30000000~0x33FFFFFF, 将虚拟地址0xB0000000~0xB3FFFFFF映射到物理地址0x30000000~0x33FFFFFF上, 一共64M,触及64个段描述符
*/
virtuladdr = 0xB0000000;
physicaladdr = 0x30000000;
while (virtuladdr < 0xB4000000)
{
*(mmu_tlb_base + (virtuladdr >> 20)) = (physicaladdr & 0xFFF00000) | MMU_SECDESC_WB;
virtuladdr += MMU_SECTION_SIZE;
physicaladdr += MMU_SECTION_SIZE;
}
}
★ 发动MMU。
void mmu_init(void)
{
unsigned long ttb = 0x30000000;
__asm__(
"mov r0, #0\n"
"mcr p15, 0, r0, c7, c7, 0\n" /* 使无效ICaches和DCaches */
"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 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 */
"bic r0, r0, #0x3000\n" /* ..11 …. …. …. 铲除V、I位 */
"bic r0, r0, #0x0300\n" /* …. ..11 …. …. 铲除R、S位 */
"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) );
}