指令分为两种:
位置无关编码(PIC):汇编源文件被编码成二进制可执行程序时编码方式与位置(内存地址)无关位置相关编码:汇编源文件被编码成二进制可执行程序时编码方式与位置(内存地址)相关在程序设计编译链接过程会给程序一个运行地址,而且必须给编译连接器指定这个地址,最后得到的二进制程序是和指定的链接地址相关的,这个地址叫做”链接地址”。 所以我们在程序编译时其实就已经知道程序将来运行时的地址,这个地址叫做”运行地址”,运行地址和链接地址相关,但是不一定是同一个,程序执行时必须放在指定的链接地址下,否则不能运行,这些程序指令就是位置相关代码,我们之前使用的ld链接器指定的“-Ttext 0x0”就是这个作用,意味着这个程序将来会放在0x0地址去运行。 但是有个别的指令可以和链接地址无关,这些代码在实际运行时放在哪里都可以正常运行,这些指令就是位置无关指令。 链接地址和运行地址可能相同也可能不同,例如我们的“-Ttext 0x0”期望在0x0地址运行,但是实际上我们程序是在内存地址0xd0020010位置运行的,这两个地址看起来不同,但实际上是同一个,因为S5PV210内部做了映射,把SRAM0xd0020000位置映射到了0x0这个地址,因为我们把代码烧写在了这个内存区域,但是大部分的指令都是位置相关代码,这就决定了运行地址和链接地址必须相同,否则一定出错。 在S5PV210中,三星推荐的启动方式,bootloader必须小于96K,并且必须大于16K,如果bootloader是80K,则启动过程为:
上电运行iROM中的BL0,BL0加载bootloader中的前16K到SRAM中作为BL1来运行,BL1运行时加载剩余的BL2部分,也就是80-16=64K,到SRAM中去运行BL2运行会初始化DDR并将OS搬运到DDR中去运行,在Uboot启动过程中,Uboot的大小是没有限制的,假设Uboot是200K,则启动过程为:
上电运行iROM中的BL0,BL0加载Uboot中的前16K到SRAM中作为BL1来运行,BL1运行时初始化DDR,然后将整个Uboot搬运到DDR中,然后使用长跳转指令从SRAM跳转到DDR中继续执行Uboot16K之后剩余的部分,直到Uboot完全启动。Uboot启动后,在Uboot中使用命令启动OS。链接地址和运行地址有时候不能相同,而且不能全部使用位置无关指令,则需要重定位来解决该问题,
运行地址是在程序运行时决定的,在编译链接阶段没有权利也没有办法决定程序的运行地址。 链接地址是程序在编译链接阶段由-Ttext或者lds链接文件决定,程序在编译连接阶段,程序员期望程序运行在一个合适的地址,就把这个地址作为程序的链接地址。
程序段是程序在编译之后由源码得到的程序的组成部分,每个二进制程序被分成了若干段,并对每个段命名,段名分为两种: - 内置段名:由编译连接器内部的命名规则对段的命名 - 自定义段名:由程序员自己命名的段名
内置段名一般有以下几种:
代码段(.text):又称作文本段,程序中函数被编译之后会被放在代码段中数据段(.data):程序中被显示初始化且值不为0的全局变量会被编译进数据段BSS段(.bss):又称作ZI(zero initial)程序中,没有被初始化的全局变量或者初始化为默认值的全局变量会被编译进BSS段链接脚本指定了链接的地址和规则,整个链接操作都按照链接脚本指定的规则进行,程序员通过链接脚本来指挥链接器来处理.o目标代码的段,将其链接到合适的位置,从而形成可执行的程序,链接脚本中关键内容有: - 段名:定位.o目标代码中的段 - 地址:作为链接地址的内存地址,
将指定的段名防止到指定的连接地址,就完成了该段的链接. 连接脚本示例:
SECTIONS { . = 0xd0024000 ; // .代表当期位置 .text : { start .o * ( .text) } .data : { * ( .data) } bss_start = . ; .bss : { * ( .bss) } bss_end = . ; } 123456789101112131415161718192021连接脚本由一个或者多个SECTIONS{}组成:
. = 0xd0024000;表示将当前位置的地址设置为0xd0024000,.text : {}表示该段为代码段,下面的.data : {},.bss : {}等同start.o表示start.o在前面,所以start.S会被首先执行*(.text)匹配模式,表示剩余的代码都属于代码段并依次排列bss_start = .;表示将bss_start的值设置为当前地址,bss_end等同,这两个符号之所以定义在这里是为了可以在别的文件中引用,以知道bss段的起止地址目标:将代码从0xd0020010重定位到0xd0024000, 代码本来是从0xd0020010地址开始运行的,因为BL1从这里开始运行,但是因为特殊原因,我们又需要代码从0xd0024000开始执行,这时候就需要重定位,在某些情况下,重定位是必须的,例如Uboot.
长跳转也就是一个跳转,在ARM中常用分支指令(b,bl)来完成短跳转,跳转通常是指令通过给PC寄存器赋值,从而完成代码跳转执行,长跳转和普通跳转的区别在于长跳转的目标地址和当前地址的距离较大,范围较宽
代码重定位之后,我们需要去代码被拷贝的目的地的代码区域执行,这个 目标地址通常会距离我们当前地址较远,所以才需要使用重定位,长跳转示例:ldr pc,=led_blink,这样我们就会跳转到目的地的led_blink函数来执行了,这里不能使用短跳转(b,bl),短跳转会跳转到运行地址附近的led_blink函数,只有使用长跳转,才能跳转到链接地址上的led_blink
重定位其实就是,在运行地址位置执行一段位置无关代码,这段代码将整个程序拷贝到链接地址上,然后使用一个长跳转,跳转到链接地址的对应函数上去,在链接地址上继续执行,从而实现重定位之后的无缝连接。
重定位代码如下:
// 重定位开始 adr r0,_start // adr 用于加载_start运行地址到 r0 中 ldr r1,=_start // ldr 加载_start的链接地址到 r1 中 // 获取BSS段起始地址,等于重定位代码的结束地址 ldr r2,=bss_start // bss_start是在 lds 链接脚本中指定的 cmp r0, r1 // 比较_start的运行地址和链接地址 beq clean_bss // 如果相等就直接跳转到clean_bss函数 // 如果上一句不相等,则进入代码拷贝阶段 copy_loop: ldr r3,[ r0], #4 // 拷贝源 str r3,[ r1], #4 // 存储到目的,长度是bss_start - _start,代码段+数据段的长度 cmp r1, r2 // 比较拷贝是否完成,也就是到没到重定位代码的结束地址 bne copy_loop // 拷贝没有完成就继续循环 // 如果拷贝完成,进入clean_bss函数,该函数用于清理bss clean_bss: ldr r0,bss_start // 为了满足C语言的要求,需要清理BSS的 ldr r1,bss_end // 一般是不需要手动清理的,这里我们做了重定位,所以需要手动清理 cmp r0, r1 // 比较bss段起始地址和结束地址 beq run_on_dram // 如果相等则直接进入run_on_dram函数 mov r2, #0 // 不相等的话就把r2设置为0 clear_loop: // 开始清理bss str r2,[ r0], #4 // 把r2赋值给r0然后r0+=4 cmp r0, r1 bne clear_loop // 循环,直到bss_end // 从这一句开始就是位置相关代码了,必须在指定的连接地址开始执行 // 所以在这一句之前,必须保证代码已经拷贝完毕 run_on_dram: // 长跳转到led_blink开始第二阶段 ldr pc,=led_blink // 长跳转 // 重定位完毕 12345678910111213141516171819202122232425262728293031重定位放置在icache开关之后。
SDRAM是DRAM的一种,前面的S代表Sync,表示具有可同步性,常见的DDR就是SDRAM的一种,SDRAM属于动态内存,需要进行初始化之后才能使用,这点和SRAM是有很大的不同。 SDRAM属于SOC外部外设,通过地址总线和数据总线和SOC通信。
SDRAM使用之前必须要初始化,S5PV210共有两个内存端口,分别是DRAM0和DRAM1,地址范围分别是:
DRAM0对应Port1:0x20000000-0x3fffffff,大小为512MB,引脚为xm1DRAM1对应Port2:0x40000000-0x7fffffff,大小为1G,引脚为xm2可以看到整个S5PV210最多支持1.5G内存,X210开发板上共有512MB内存,DRAM0/1上各有256MB,所以可以得到X210上内存地址为:
DRAM0:0x20000000到0x2fffffff,大小为256MBDRAM1:0x40000000到0x4fffffff,大小为256MBDDR初始化完毕之后,这些地址都是可用的,其他地址则不可用。 每个DDR端口都有三类总线组成,分别是地址总线ADDR14个,控制总线,数据总线32个。 X210开发板共使用了4片内存,每片128MB,数据总线为16bit,通过两个内存之间进行并联,从而实现了数据总线32bit。X210上的每片内存分为8个Bank,CPU通过DDR端口中的BA0-BA2来选择Bank,每个Bank有128Mbit的范围,通过行地址和列地址进行寻址,寻址范围是2的24次方(16MB = 128Mbit)。
在进行重定位之前,先去执行SDRAM初始化,可以直接跳转到SDRAM的初始化函数:
// 初始化SDRAM bl sdram_asm_init // bl短跳转到sdram_asm_init函数进行SDRAM初始化 12SDRAM的初始化需要先初始化DRAM0,然后初始化DRAM1,步骤在手册的1.2.1.3
设置IO端口驱动强度,DDR和SOC通过总线链接,物理表现上就是引脚,工作时需要驱动信号,驱动强度就是设置了该信号的强度设置端口时钟,设置PHYCONTROL寄存器,从而使内存芯片的时钟和CPU的时钟保持一致设置DDR2,DMC_CONCONTROL,DMC_MEMCONTROL,DMC_MEMCONFIG等一系列寄存器DRAM初始化之后可以使用DRAM进行重定位,步骤和之前完全一致。