初步认识IROM和IRAM
S5PV210含有一个内64K的IROM和96K的IRAM,系统启动时主要依靠它们,IROM和IRAM所处的存储空间见下图:
S5PV210有IROM,且只能从IROM启动,不再支持原来的直接从外部介质启动方式。IROM代码(BL0)运行完毕后,根据OM[5:0]确定外部启动介质(BL1和BL2)完成启动。S5PV210支持的外部启动介质包括:NAND Flash、OneNAND、SD/MMC、eMMC、eSSD、UART/USB。
完整的启动序列
系统刚启动时,会运行IROM中的固化代码,进行一些通用的初始化,具体步骤包括:
第一步 关闭看门狗;
第二步 初始化icache;
第三步 初始化堆栈;
第四步 设置时钟;
第五步 判断启动设备(nand/sd/onenand等),检查校验和,然后从启动设备中拷贝前16K的代码到IRAM的0xD0020000处;
第六步 若是安全模式启动,则进行完整性检查;
第七步 跳转到IRAM的0xD0020010地址上继续运行;
UART /USB启动模式
OM[5:4] = 0b10即可选择UART/USB启动模式此时IROM代码运行完毕后,会先尝试从UART 启动,若失败则会尝试从USB启动。若失败则会根据OM[3:0]选择相应的启动介质再次尝试启动。
UART启动时,必须在S5PV210上电前将串口连接好,上位机使用dnw工具来发送启动代码可执行文件。本教材中使用USB启动方式作为调试,因此不详细探讨UART启动方式。
1、USB启动方式必须借助dnw工具,教材中使用dnw v0.60c。
2、打开dnw软件,将dnw中USB下载地址设置为0xd0020010。
3、复位开发板。
4、若是初次使用则Windows会弹出发现新硬件图标,索引安装开发板dnw USB驱动即可。若已经安装驱动,则dnw会显示USB:OK,表示USB连接已经成功。
5、dnw菜单中USB Port->Transmit,选择编译好的bin文件即可。USB download完成后S5PV210会即刻跳转到0xd0020010处执行。
1、// 关闭看门狗
ldr r0, =0xE2700000
mov r1, #0
str r1, [r0]
2、控制icache
// 打开icache可提高运行速度
#ifdef CONFIG_SYS_ICACHE_OFF
// clear bit 12 (I) I-cache
bic r0, r0, #0x00001000
#else
// set bit 12 (I) I-cache
orr r0, r0, #0x00001000
#endif
mcr p15, 0, r0, c1, c0, 0
栈的整体作用
1) 保存现场;
2) 传递参数:汇编代码调用C函数时,需传递参数;
3) 保存临时变量:包括函数的非静态局部变量以及编译器自动生成的其他临时变量;
4、重定位代码到IRAM+0x4000
重定位:对于程序而言,我们需要理解两个概念,一是程序当前所处的地址,即程序在运行时,所处的当前地址;二是程序的链接地址,即程序运行时应该位于的运行地址。编译程序时,可以指定程序的链接地址。
对于S5PV210而言,启动时只会从NAND Flash/sd等启动设备中拷贝前16K的代码到IRAM中,那么当我们的程序超过16K怎么办?那就需要我们在前16K的代码中将整个程序完完整整地拷贝到DRAM等其他更大存储空间,然后再跳转到DRAM中继续运行我们的代码,这个拷贝然后跳转的过程就叫重定位。本章中我们主要学习如何重定位,但是并不会涉如何使用到DRAM,而是简单地将代码从IRAM的0xD0020010处拷贝到IRAM的0xD0024000处,然后跳转到0xD0024000处继续运行我们的代码。
链接脚本:链接脚本就是程序链接时的参考文件,其主要目的是描述如何把输入文件中的段(SECTION)映射到输出文件中,并控制输出文件的存储布局。链接脚本的基本命令式SECTIONS命令,一个SECTIONS命令内部包含一个或多个段,段(SECTION)是链接脚本的基本单元,它表示输入文件中的某个段是如何放置的。
链接脚本的标准格式如下:
SECTIONS
{
sections-command
sections-command
}
/*link.lds*/
SECTIONS
{
. = 0xD0024000;
.text : {
start.o
* (.text)
}
.data : {
* (.data)
}
bss_start = .;
.bss : {
* (.bss)
}
bss_end = .;
}
1) 在链接脚本中,单独的点号(.)代表了当前位置,. = 0xD0024000; 表示程序的链接地址是0xD0024000;
2) link.lds中的.text 、 .data 、 .bss分别是text段、data段、bss段的段名(这些段名并不是固定的,是可以随便起的)。.text段包含的内容是start.o和其余代码中所有的text段;.data段包含的内容是代码中所有的data段;.bss段包含的内容是代码中所有的bss段。
3) bss_start和bss_end保存的是bss段的起始地址和结束地址,在start.S中会被用到。
data、text、bss段:
1) data段:数据段(datasegment)通常是指用来存放程序中已初始化的全局变量的一块内存区域。数据段属于静态内存分配。
2) text段:代码段通常是指用来存放程序执行代码的一块内存区域。这部分区域的大小在程序运行前就已经确定,并且内存区域通常属于只读,某些架构也允许代码段为可写,即允许修改程序。在代码段中,也有可能包含一些只读的常数变量,例如字符串常量等。
3) bss段:指用来存放程序中未初始化的全局变量的一块内存区域。BSS是英文BlockStarted by Symbol的简称。当我们的程序有全局变量是,它是放在bss段的,由于全局变量默认初始值都是0,所有我们需要手动清bss段。
/*start.S*/
.global _start
_start:
// 关闭看门狗
ldr r0, =0xE2700000
mov r1, #0
str r1, [r0]
// 设置栈,以便调用c函数
ldr sp, =0xD0037D80
// 重定位
// _start当前所位于的地址:0xd0022000
adr r0, _start
// _start的链接地址:0xd0024000
ldr r1, =_start
// bss段的起始地址
ldr r2, =bss_start
cmp r0, r1
beq clean_bss
copy_loop:
ldr r3, [r0], #4 // 源
str r3, [r1], #4 // 目的
cmp r1, r2
bne copy_loop
// 清bss段
clean_bss:
ldr r0, =bss_start
ldr r1, =bss_end
cmp r0, r1
beq run_on_dram
mov r2, #0
clear_loop:
str r2, [r0], #4
cmp r0, r1
bne clear_loop
// 跳转
run_on_dram:
ldr pc, =main
/*main.c*/
#define GPJ2CON (*(volatile unsigned long *) 0xE0200280)
#define GPJ2DAT (*(volatile unsigned long *) 0xE0200284)
// 延时函数
void delay(unsigned long count)
{
volatile unsigned long i = count;
while (i--);
}
void main()
//LED 闪烁
{
GPJ2CON = 0x00001111;
// 配置引脚
while(1)
// 闪烁
{
GPJ2DAT = 0;
// LED on
delay(0x100000);
GPJ2DAT = 0xf;
// LED off
delay(0x100000);
}
}
5、重定位代码到DRAM
DRAM0对应的地址是0x2000_0000~0x3FFF_FFF共512M,DRAM1对应的地址是0x4000_000~0x7FFF_FFFF共1G。
代码下载链接:http://download.csdn.net/detail/klcf0220/5508253
喜欢开源,乐意分享的大神们,欢迎加入QQ群:176507146,你值的拥有哦!