1. SECTIONS到底意味着什么
在一个裸版程序里面含有*.lds文件,而lds文件意味着如果你的程序烧录在nandflash,那在nandflash的内存将根据lds文件指定偏移来分布,下面从不同场景来解释SECTIONS的内容。
2. 小于4K程序
若程序小于4K,那程序的整个重定位地址设置为0,可以没有拷贝到sdram的处理。
uart.lds
SECTIONS {. = 0x00000000;.text : { *(.text) }.rodata ALIGN(4) : {*(.rodata)} .data ALIGN(4) : { *(.data) }.bss ALIGN(4) : { *(.bss) *(COMMON) }
}
head.S
.text
.global _start
_start:ldr r0, =0x53000000 @ WATCHDOG寄存器地址mov r1, #0x0 str r1, [r0] @ 写入0,禁止WATCHDOG,否则CPU会不断重启ldr sp, =1024*4 @ 设置堆栈,注意:不能大于4k, 因为现在可用的内存只有4K@ nand flash中的代码在复位后会移到内部ram中,此ram只有4Kbl main @ 调用C程序中的main函数
halt_loop:b halt_loop
Nandflash其内存结构如下:
一开始运行之前,默认会从NandFlash拷贝4K大小的程序代码到芯片片内内存上面并且从0开始执行,由于片内内存只有4K,所以链接脚本只适用于程序运行空间小于4K的情况。
3. 大于4K程序
若要支持程序大于4K,就需要考虑将程序拷贝到sdram中运行,修改head.s文件,添加从片内内存拷贝到Sdram的操作,同时链接脚本设置重定位地址为0x30000000。
uart.lds
SECTIONS {. = 0x30000000;.text : { *(.text) }.rodata ALIGN(4) : {*(.rodata)} .data ALIGN(4) : { *(.data) }.bss ALIGN(4) : { *(.bss) *(COMMON) }
}
head.S
.extern main
.text
.global _start
_start:
Reset: ldr sp, =4096 bl disable_watch_dog bl clock_init bl memsetup bl nand_initldr r0, =0x30000000 mov r1, #0 mov r2, #(1024*10) bl nand_read ldr pc, =on_sdram
onsdram:ldr sp, =0x34000000 ldr lr, =halt_loop ldr pc, =main
halt_loop:b halt_loop
此时NanFlash和Sdram内存分布如下:
当重定位0x30000000后,其内存的指令码后会发生什么变化呢?以main函数里面,获取全局变量值为例,
重定位0x00的时候:
690: e59f2224 ldr r2, [pc, #548] ; 8bc <main+0x234>
8bc: 000009d4 ldrdeq r0, [r0], -r4
9d4: 00000000 andeq r0, r0, r0
重定位0x30000000的时候:
30000690: e59f2224 ldr r2, [pc, #548] ; 300008bc <main+0x234>
300008bc: 300009d4 ldrdcc r0, [r0], -r4
300009d4: 00000000 andeq r0, r0, r0
三句汇编代码其作用是获取全局变量的值,将其放入寄存器r2中。可以看到第一条汇编指令码和第三条汇编指令码没有变化,重点分析第二条汇编指令码,第二条汇编指令码实际上是第一条汇编寄存器寻址偏移, [pc, #548]的值需要找8bc或300008bc偏移的汇编指令,而其汇编指令又指向0x9d4或0x300009d4偏移,最终获取在其偏移的内容。由此推想若重定位到0x00,当执行到main函数时,会导致执行地址还在片内范围,假设9d4大于4K,则寻址大于片内内存4K,程序就会溢出。所以链接脚本需要设置重定位地址为0x30000000。
5. 大于4K程序并支持中断
当需要支持中断时,head.S需要加上中断向量表,如下:
@******************************************************************************
@ File锟斤拷head.S
@ 锟斤拷锟杰o拷锟斤拷锟斤拷SDRAM锟斤拷锟斤拷锟斤拷锟斤拷锟狡碉拷SDRAM锟斤拷然锟斤拷锟斤拷锟斤拷SDRAM锟斤拷锟斤拷执锟斤拷
@****************************************************************************** .extern main
.text
.global _start
_start:
@******************************************************************************
@ 中断向量,本程序中,除Reset和HandleIRQ外,其它异常都没有使用
@******************************************************************************b Reset@ 0x04: 未定义指令中止模式的向量地?
HandleUndef:b HandleUndef@ 0x08: 管理模式的向量地址,通过SWI指令进入此模式
HandleSWI:b HandleSWI@ 0x0c: 指令预取终止导致的异常的向量地址
HandlePrefetchAbort:b HandlePrefetchAbort@ 0x10: 数据访问终止导致的异常的向量地址
HandleDataAbort:b HandleDataAbort@ 0x14: 保留
HandleNotUsed:b HandleNotUsed@ 0x18: 中断模式的向量地址b HandleIRQ@ 0x1c: 快中断模式的向量地址
HandleFIQ:b HandleFIQReset: ldr sp, =4096 @ 锟斤拷锟斤拷栈指锟诫,锟斤拷锟铰讹拷锟斤拷C锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷前锟斤拷要锟斤拷锟秸�bl disable_watch_dog @ 锟截憋拷WATCHDOG锟斤拷锟斤拷锟斤拷CPU锟结不锟斤拷锟斤拷锟斤拷bl clock_init @ 锟斤拷锟斤拷MPLL锟斤拷锟侥憋拷FCLK锟斤拷HCLK锟斤拷PCLKbl memsetup @ 锟斤拷锟矫存储锟斤拷锟斤拷锟斤拷锟斤拷使锟斤拷SDRAMbl nand_initldr r0, =0x30000000 @1. 鐩爣鍦板潃=0x30000000锛岃繖鏄疭DRAM鐨勮捣濮嬪湴鍧�mov r1, #4096 @2. 婧愬湴鍧� = 4096锛岃繛鎺ョ殑鏃跺�欙紝main.c涓殑浠g爜閮藉瓨鍦∟AND Flash鍦板潃4096寮�濮嬪mov r2, #(1024*20) @3. 澶嶅埗闀垮害= 1024(bytes)锛屽浜庢湰瀹為獙鐨刴ain.c锛岃繖鏄冻澶熶簡bl nand_read @璋冪敤C鍑芥暟nand_read_llldr pc, =on_sdram @ 跳到SDRAM中继续执行
on_sdram:msr cpsr_c, #0xd2 @ 进入中断模式ldr sp, =4096 @ 设置中断模式栈指针msr cpsr_c, #0xdf @ 进入系统模式ldr sp, =0x34000000 @ 锟斤拷锟斤拷栈指锟斤拷msr cpsr_c, #0x5f @ 设置I-bit=0,开IRQ中断ldr lr, =halt_loop @ 锟斤拷锟矫凤拷锟截碉拷址ldr pc, =main @ 锟斤拷锟斤拷main锟斤拷锟斤拷
halt_loop:b halt_loopHandleIRQ:sub lr, lr, #4 @ 计算返回地址stmdb sp!, { r0-r12,lr } @ 保存使用到的寄存器@ 注意,此时的sp是中断模式的sp@ 初始值是上面设置的4096ldr lr, =int_return @ 设置调用ISR即EINT_Handle函数后的返回地址ldr pc, =Uart0_Handle @ 调用中断服务函数,在interrupt.c中int_return:ldmia sp!, { r0-r12,pc }^ @ 中断返回, ^表示将spsr的值复制到cpsr
此时需要修改链接脚本,若延续上述写法,如下:
SECTIONS {. = 0x30000000;.text : { *(.text) }.rodata ALIGN(4) : {*(.rodata)} .data ALIGN(4) : { *(.data) }.bss ALIGN(4) : { *(.bss) *(COMMON) }
}
则会有严重的问题,这时看head.S生成的汇编指令
Disassembly of section .text:30000000 <_start>:
30000000: ea000006 b 30000020 <Reset>
第一条指令码,直接程序跑飞,因为这条指令码ea000006表示跳转到30000020执行,而30000020位于Sdram,还并没有初始化,所以链接脚本应该这样写:
SECTIONS {. = 0x00000000;.init : AT(0){ head.o init.o nand.o} . = 0x30000000;.text ALIGN(4) : AT(4096) { *(.text) } .rodata ALIGN(4) : {*(.rodata)}.data ALIGN(4) : { *(.data) }__bss_start = .;.bss ALIGN(4) : { *(.bss) *(COMMON) }__bss_end = .;
}
可以看到main函数的重定位也以0x30000000为基准。当执行到main函数时,此时能保证在sdram里执行,并且也能保证取数据段的时候,能得到偏移哪怕大于4K也能正确的重定位到,看如下main汇编指令码:
300025a4 <main>:
300025a4: e92d4080 push {r7, lr}
300025a8: e24dd080 sub sp, sp, #128 ; 0x80
300025ac: e3a0207d mov r2, #125 ; 0x7d
此时看nandflash和sdram的内存分布如下: