MMU,用于虚拟地址和物理地址的转换。当使用mmu时,cpu发出地址给MMU,MMU再转换成物理地址传给存储管理器,再获取需要的信息。
(cpu是不管虚拟地址还是物理地址的,它只管发送给MMU(使用MMU时)或者存储管理器(没有使用MMU时))
实验步骤:
1.建立表格(图片右上角,表格中填写的是物理地址的 起始地址!!!)
2.告诉mmu表格地址
3.运行mmu。
代码步骤:
1.设置栈指针,以调用c函数
2.关闭看门狗
3.设置存储管理控制器(13个寄存器,不懂!!!)
4.复制代码到sdram(事先把部分代码放到片内4k内存的2048处,把这部分代码复制到0x30004000处,此处为sdram的地址,映射为0xb0004000)
5.设置页表(steppingstone一个,sdram一个,gpio寄存器一个,至少三个页表)
6.重新设置栈指针(ldr sp, =0xb4000000,指向sdram的顶端?!!!![改成0xb0000000也可以,底端才是0xb000000吗???]) (不懂!!!!)(实践的结果是:不要这步也可以!!!)
7.跳到sdram中继续执行(ldr pc, =0xb0004000,物理地址为0x30004000)
视频中推荐的书籍:linux内核完全注释(教怎么嵌入汇编到c)
建表代码:(注意映射时,是段映射,就是说最小单位是1m!!!!!!!)
以及开启mmu:
-
//设置页表(十分重要,建立三个表:
-
//steppingstone[片内4k],gpio寄存器,sdram)
-
void create_page_table(void)
-
{
-
//用于段描述符的一些宏定义(注意时段描述符)
-
#define MMU_FULL_ACCESS (3 << 10) //访问权限
-
#define MMU_DOMAIN (0 << 5) //属于哪个域
-
#define MMU_SPECIAL (1 << 4) //必须是1
-
#define MMU_CACHEABLE (1 << 3) //cacheable
-
#define MMU_BUFFERABLE (1 << 2) //bufferable
-
#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 ;
-
unsigned long physicaladdr;
-
unsigned long *mmu_tlb_base = (unsigned long *)0x30000000;
-
-
/*建立steppingstone的表,起始地址(虚拟,物理)都是0*/
-
virtuladdr = 0;
-
physicaladdr = 0;
-
*(mmu_tlb_base + (virtuladdr >> 20))
-
= (physicaladdr & 0xfff00000) | MMU_SECDESC_WB; //这句不太懂
-
-
/*0x56000000是GPIO寄存器的起始物理地址,对应虚拟地址0xa0000000*/
-
virtuladdr = 0xa0000000;
-
physicaladdr = 0x56000000;
-
//为什么要加上mmu_tlb_base?相当于0?等号后面也不懂.//
-
*(mmu_tlb_base + (virtuladdr >> 20))
-
= (physicaladdr & 0xfff00000) | MMU_SECDESC; //不太懂;
-
-
/*sdram的物理地址范围是:0x30000000~0x33ffffff,*/
-
/*将虚拟地址0xB0000000 ~ 0xB3ffffff映射到sdram的物理地址*/
-
/*总共64M,涉及64个段描述符*/
-
-
virtuladdr = 0xb0000000;
-
physicaladdr = 0x30000000;
-
-
while (virtuladdr < 0xb4000000)
-
{
-
*(mmu_tlb_base + (virtuladdr >> 20))
-
= (physicaladdr & 0xfff00000 ) | MMU_SECDESC_WB;
-
-
virtuladdr += 0x100000;
-
physicaladdr += 0x100000;
-
}
-
}
-
-
-
/*启动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)
-
);
-
}
阅读(2777) | 评论(0) | 转发(0) |