;================================================
; head.S
; 功能: 关闭看门狗、设置SDRAM,创建页表,启动MMU、进入main函数
;================================================
IMPORT disable_watch_dog ; 声明关闭看门狗C函数
IMPORT memsetup ; 声明存储控制器设置C函数
;IMPORT copy_2th_to_sdram ; 声明复制代码C函数
IMPORT create_page_table ; 声明设置页表C函数
IMPORT mmu_init ; 声明mmu初始化启动C函数
IMPORT main ; 声明C函数
CODE32
AREA Init, CODE, READONLY
ENTRY
ldr sp, =4096 ; 设置栈指针,以下都是C函数,且注意不可以大于4K
bl disable_watch_dog ; 关闭看门狗,否则CPU会不断重启
bl memsetup ; 设置存储控制器以便使用SDRAM(存放页表)
;bl copy_2th_to_sdram ; 将第2部分led.c代码复制到SDRAM里
bl create_page_table ; 创建页表
bl mmu_init ; 启动mmu
bl main ; 调用main函数
halt_loop
b halt_loop
END
/******************************************************************************
** init.c: 进行一些初始化,在steppingstone中执行,
** 此时MMU未开启,使用物理地址,属于第1部分代码
******************************************************************************/
#define WTCON (*(volatile unsigned long int *)0x53000000) // WATCHDOG寄存器
#define MEM_CTL_BASE 0x48000000 // 存储控制器的寄存器起始地址
/*
* 关闭看门狗,否则CPU会不断重启
*/
void disable_watch_dog(void)
{
WTCON = 0; // 关闭看门狗,写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[i] = mem_cfg_val[i]; // 给13个寄存器赋值
}
}
/*
* 创建页表
*/
void create_page_table(void)
{
/*
* 用于段描述符的一些宏
*/
#define MMU_FULL_ACCESS (3<<10) // 访问权限为“读/写”
#define MMU_DOMAIN (0<<5) // 属于第0个域
#define MMU_SPECIAL (1<<4) // 必须是1
#define MMU_CACHEABLE (1<<3) // cache使能
#define MMU_BUFFERABLE (1<<2) // buffer使能
#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)
unsigned long vir_add, phy_add;
unsigned long *mmu_table_base = (unsigned long *)0x30000000; // 此页表基地址与CP15的C2中存储的基地址是同一个地址
/*
* 将steppingstone在内的起始0~1M的虚拟地址映射到同样的物理地址
*/
vir_add = 0;
phy_add = 0;
*(mmu_table_base + (vir_add >> 20)) = ((phy_add & 0xFFF00000) | MMU_SECDESC_WB); // 描述符的寻址及其属性定义(《ARM体系机构及编程》181页)
/*
* 0x56000000是GPIO寄存器的起始地址
* GPBCON和GPBDAT这2个寄存器的物理地址是0x56000010和0x56000014
* 为了可以在led.c的代码中用虚拟地址0xA0000010和0xA0000014操作GPBCON和GPBDAT
* 将0xA0000000开始的1M虚拟地址映射到0x56000000开始的1M物理地址上
*/
vir_add = 0xA0000000;
phy_add = 0x56000000;
*(mmu_table_base + (vir_add >> 20)) = ((phy_add & 0xFFF00000) | MMU_SECDESC);
/*
* SDRAM的物理地址是0x30000000~0x33FFFFFF,
* 将SDRAM映射到同样的虚拟地址上
* 共64M,涉及到64个段描述符
*/
vir_add = 0x30000000;
phy_add = 0x30000000;
while(vir_add < 0x34000000)
{
*(mmu_table_base + (vir_add >> 20)) = ((phy_add & 0xFFF00000) | MMU_SECDESC_WB);
vir_add += 0x100000;
phy_add += 0x100000; // 1M对齐
}
}
/*
* 启动mmu
*/
void mmu_init(void)
{
// 内嵌汇编,操作CP15协处理器
__asm{
//MMU_SetTTBase,设置一级页表基地址(即存放页表的起始存储地址)
mov r0,#0x30000000; // r0=TTBase 即页表的基地址
mcr p15,0,r0,c2,c0,0; // C2中存放地址转换表基地址
//映射之后,地址需经过MMU,它能控制你的访问权限。
//域访问控制寄存器设为0xffffffff,就是让你有权限访问映射的空间
mvn r0, #0; // 数据取反传送指令
mcr p15,0,r0,c3,c0,0; // 访问类型为管理者权限
//MMU_EnableMMU,使能MMU
mrc p15,0,r0,c1,c0,0; // 读出协处理器C1
orr r0,r0,#01; // 或操作,使最低位为1
mcr p15,0,r0,c1,c0,0; // 给C1赋值
}
}
/******************************************************************************
** led.c: 点亮4个LED, 属于第2部分代码
** 此时MMU已开启,使用虚拟地址定位寄存器,但代码本身仍在SRAM中运行
*****************************************************************************/
#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))
void wait(unsigned long dly)
{
for(; dly > 0; dly--);
}
int main(void)
{
GPBCON = GPB5_out|GPB6_out|GPB7_out|GPB8_out; // 将LED1-4对应的GPB5/6/7/8四个引脚设为输出
while(1)
{
GPBDAT = 0x00000000; // 亮
wait(30000);
GPBDAT = 0xFFFFFFFF; // 灭
wait(30000);
}
return 0;
}
---------------华丽分割线------------------------
在设置编译器的RO时设置为0x00000000,因为我的代码比较小,所以没有将代码复制到SDRAM里面,只是用SDRAM来储存页表,因此仍需要对SDRAM进行初始化。