下面进行源码内容的分析及修改:
由上面的分析可以知道,最终将所有obj文件链接生成u-boot可执行文件时,用到了/board/mini2440/u-boot.lds这个链接脚本,查看其内容:
------- /board/mini2440/u-boot.lds -------
24 OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
25 /*OUTPUT_FORMAT("elf32-arm", "elf32-arm", "elf32-arm")*/
26 OUTPUT_ARCH(arm)
27 ENTRY(_start)
28 SECTIONS
29 {
30 . = 0x00000000;
31
32
33 . = ALIGN(4);
34 .text :
35 {
36 cpu/arm920t/start.o (.text)
37 *(.text)
38 }
...
同时查看u-boot.dis文件:
------- u-boot.dis -------
1
2 u-boot: file format elf32-littlearm
3
4 Disassembly of section .text:
5
6 33f80000 <_start>:
7 33f80000: ea000012 b 33f80050 <reset>
8 33f80004: e59ff014 ldr pc, [pc, #20] ; 33f80020 <_undefined_instruction>
9 33f80008: e59ff014 ldr pc, [pc, #20] ; 33f80024 <_software_interrupt>
可知代码段中放在最前的是/cpu/arm920t/start.S,同时上电之后最开始执行的标号是/cpu/arm920t/start.S中的_start,因此从这里开始分析。
/cpu/arm920t/start.S:
找到start.S中的_start标号:
------- /cpu/arm920t/start.S -------
41 .globl _start
42 _start: b reset
43 ldr pc, _undefined_instruction
44 ldr pc, _software_interrupt
45 ldr pc, _prefetch_abort
46 ldr pc, _data_abort
47 ldr pc, _not_used
48 ldr pc, _irq
49 ldr pc, _fiq
50
51 _undefined_instruction: .word undefined_instruction
52 _software_interrupt: .word software_interrupt
53 _prefetch_abort: .word prefetch_abort
54 _data_abort: .word data_abort
55 _not_used: .word not_used
56 _irq: .word irq
57 _fiq: .word fiq
58
59 .balignl 16,0xdeadbeef
...
75 _TEXT_BASE:
76 .word TEXT_BASE
77
78 .globl _armboot_start
79 _armboot_start:
80 .word _start
81
82 /*
83 * These are defined in the board-specific linker script.
84 */
85 .globl _bss_start
86 _bss_start:
87 .word __bss_start
88
89 .globl _bss_end
90 _bss_end:
91 .word _end
第41行,.global是编译的关键词,告诉编译器这是一个全局可见的名字(可能是变量,也可能是函数名),在这里_start是一个标号(GNU汇编编译器的默认入口标号),用.global关键字将其定义为一个全局可见的标号,以便在编译链接过程中能够找到。
第59行,.balignl表示结束的地址必须为后面第一个参数(16)的整数倍,如果不是整数倍,则进行填充使其达到整数倍,填充的内容为0xdeadbeef,这条指令用于将后面的指令对齐在16的整数倍的地址上。
第75行到第91行,定义一些标号:
- _TEXT_BASE:TEXT_BASE(0x33F80000);
- _armboot_start:_start(0x33F80000);
- _bss_start:__bss_start(u-boot.lds中定义的BSS起始地址);
- _bss_end:_end(u-boot.lds中定义的BSS结束地址,也是最后的结束地址)
_start标号下第一条指令就是跳转指令,b reset指令表示无条件跳转到reset标号继续执行。
后面为S3C2440的7个中断向量地址,并使用跳转指令指向中断服务程序,当发生中断时,程序指针PC会跳转到相应的中断向量,执行中断程序。
找到reset标号:
------- /cpu/arm920t/start.S -------
106 /*
107 * the actual reset code
108 */
109
110 reset:
111 /*
112 * set the cpu to SVC32 mode
113 */
reset标号下首先对S3C2440的当前程序状态寄存器(CPSR)进行操作,使芯片工作在超级用户模式下(Supervisor)。
查看S3C2440手册中关于CPSR寄存器的说明:
寄存器高4位是程序运行标志位,低8位是状态控制位,手册中具体说明如下:
- 第7位——中断禁止位,置1时禁止中断;
- 第6位——快速中断禁止位,置1时禁止中断;
- 第5位——芯片运行状态位,置1时运行在THUMB状态下,否则运行在ARM状态下;
- 第4~0位——运行模式选择位,其模式设置如下:
10000:User Mode,普通用户模式
10001:FIQ Mode,快速中断模式
10010:IRQ Mode,中断模式
10011:Supervisor Mode,超级用户模式
10111:Abort Mode
11011:Undefined Mode
11111:System Mode
使用状态寄存器访问指令MRS和MSR进行访问:
MRS:状态寄存器(CPSR或SPSR)内容传送到通用寄存器(Rd)
格式:MRS{<cond>} <Rd>, <CPSR/SPSR>
MSR:将操作数(opt)的内容传送到状态寄存器(CPSR或SPSR)的特定域中
格式:MSR{<cond>} <CPSR/SPSR>_域, opt
位[31:24]为条件标志位域,用f表示;
位[23:16]为状态位域,用s表示;
位[15:8]为扩展位域,用x表示;
位[7:0]为控制位域,用c表示。
其中SPSR为程序状态保存寄存器。
CPSR: Current Program Status Register
SPSR: Saved Program Status Register
因此reset标号下的指令:
------- /cpu/arm920t/start.S -------
110 reset:
111 /*
112 * set the cpu to SVC32 mode
113 */
114 mrs r0,cpsr
115 bic r0,r0,#0x1f
116 orr r0,r0,#0xd3
117 msr cpsr,r0
首先通过MRS指令将CPSR的值读到R0中,然后在R0中对CPSR的值进行操作,再通过MSR指令将改变后的值写入CPSR中。
其中:
BIC{<cond>} Rd, Opt1, Opt2:
清除操作数1(Opt1)中的某些位,并将结果写入目的寄存器(Rd)中,操作数2(Opt2)为32位掩码,掩码中置1的位在操作数1中将会被清0。
ORR{<cond>} Rd, Opt1, Opt2:
将两个操作数进行或运算,并将结果写入目的寄存器(Rd)中,常用于将操作数1的某些位置1。
因此首先将CPSR的值传送到R0中:
------- /cpu/arm920t/start.S -------
114 mrs r0,cpsr
然后清除R0的低5位:
------- /cpu/arm920t/start.S -------
115 bic r0,r0,#0x1f
将R0的值或上0b11010010(0xD3),即用于模式选择的低5位[4:0]置为10011,即Supervisor Mode,超级用户模式,并将[7:6]置1,禁止中断:
------- /cpu/arm920t/start.S -------
116 orr r0,r0,#0xd3
最后再将修改好的值写入CPSR:
------- /cpu/arm920t/start.S -------
117 msr cpsr,r0
接下来利用宏定义设置和看门狗、中断以及时钟相关的寄存器:
------- /cpu/arm920t/start.S -------
120 #if defined(CONFIG_S3C2400)
121 # define pWTCON 0x15300000
122 # define INTMSK 0x14400008 /* Interupt-Controller base addresses */
123 # define CLKDIVN 0x14800014 /* clock divisor register */
124 #elif defined(CONFIG_S3C2410)
125 # define pWTCON 0x53000000
126 # define INTMSK 0x4A000008 /* Interupt-Controller base addresses */
127 # define INTSUBMSK 0x4A00001C
128 # define CLKDIVN 0x4C000014 /* clock divisor register */
129 #endif
添加对S3C2440的支持,添加修改如下,因为S3C2440这4个寄存器的地址与S3C2410一致,所以也可直接在124行添加对S3C2440的判断:
------- /cpu/arm920t/start.S -------
120 #if defined(CONFIG_S3C2400)
121 # define pWTCON 0x15300000
122 # define INTMSK 0x14400008 /* Interupt-Controller base addresses */
123 # define CLKDIVN 0x14800014 /* clock divisor register */
124 #elif defined(CONFIG_S3C2410)
125 # define pWTCON 0x53000000
126 # define INTMSK 0x4A000008 /* Interupt-Controller base addresses */
127 # define INTSUBMSK 0x4A00001C
128 # define CLKDIVN 0x4C000014 /* clock divisor register */
129 #elif defined(CONFIG_S3C2440)
130 # define pWTCON 0x53000000
131 # define INTMSK 0x4A000008 /* Interupt-Controller base addresses */
132 # define INTSUBMSK 0x4A00001C
133 # define CLKDIVN 0x4C000014 /* clock divisor register */
134 #endif
接下来关闭看门狗:
------- /cpu/arm920t/start.S -------
136 #if defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410)
137 ldr r0, =pWTCON
138 mov r1, #0x0
139 str r1, [r0]
屏蔽中断:
------- /cpu/arm920t/start.S -------
141 /*
142 * mask all IRQs by setting all bits in the INTMR - default
143 */
144 mov r1, #0xffffffff
145 ldr r0, =INTMSK
146 str r1, [r0]
147 # if defined(CONFIG_S3C2410)
148 ldr r1, =0x3ff
149 ldr r0, =INTSUBMSK
150 str r1, [r0]
151 # endif
以及设置时钟分频:
------- /cpu/arm920t/start.S -------
153 /* FCLK:HCLK:PCLK = 1:2:4 */
154 /* default FCLK is 120 MHz ! */
155 ldr r0, =CLKDIVN
156 mov r1, #3
157 str r1, [r0]
158 #endif /* CONFIG_S3C2400 || CONFIG_S3C2410 */
S3C2440的看门狗、中断设置与S3C2410相似,具体操作见S3C2440数据手册。但两者时钟设置上稍微有些区别,S3C2440内部有两个锁相环(PLL),一个用于FCLK、HCLK、PCLK,另一个用于USB模块(通常设置为48MHz)。FCLK即为CPU运行频率,HCLK用于AHB总线上的外设,PCLK用于APB总线上的外设。
S3C2440通过MPLLCON与UPLLCON两个寄存器来设置时钟频率(注意如果要同时设置MPLL与UPLL时,应该先设置UPLL,中间间隔大约7个时钟周期,然后再设置MPLL):
在刚上电时钟频率默认为晶振频率,即12MHz,通过设置时钟倍频,获得实际工作的时钟频率,计算公式如下,其中Fin在这里为晶振频率,即12MHz,MPLL计算的结果即为FCLK的值,而S3C2410的MPLL计算略有不同(MPLL少乘一个2):
S3C2440通过CLKDIVN寄存器设置FCLK、HCLK、PLCK的分频:
并且,在HDIVN不为0时,需要将CPU的总线模式从快速总线模式改变为其他模式,而S3C2440不支持同步总线模式,因此将其设置为异步总线模式,否则CPU将按HCLK的频率运行:
在ARM920T的技术手册中有详细说明,ARM处理器使用协处理器15(CP15)的寄存器来控制cache、TCM和MMU等,CP15包含了编号为0~15的共16个32位的寄存器,其中总线模式控制在寄存器1(Register 1, control register),其说明与访问指令如下:
总线模式的控制位是第[31:30]位,设置方式如下:
因此,根据上述分析进行修改,首先添加对S3C2440的支持:
------- /cpu/arm920t/start.S -------
136 #if defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410) || defined(CONFIG_S3C2440)
看门狗的设置与S3C2410一致,不需要修改。中断屏蔽寄存器INTMSK的设置也一致,而S3C2440的中断子屏蔽寄存器INTSUBMSK中位[14:0]均需要进行屏蔽,添加修改如下:
------- /cpu/arm920t/start.S -------
151 # elif defined(CONFIG_S3C2440)
152 ldr r1, =0x7fff
153 ldr r0, =INTSUBMSK
154 str r1, [r0]
由于S3C2440的时钟设置与S3C2410不同,同时为了后续程序(SDRAM等)的设置,将S3C2440的时钟频率FCLK设置为200MHz,且分频比为1:2:4,即FCLK : HLCK : PCLK=200MHz : 100MHz : 50MHz,因此添加修改:
------- /cpu/arm920t/start.S -------
157 # if defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410)
158 /* FCLK:HCLK:PCLK = 1:2:4 */
159 /* default FCLK is 120 MHz ! */
160 ldr r0, =CLKDIVN
161 mov r1, #3
162 str r1, [r0]
163 # elif defined(CONFIG_S3C2440)
164 /* FCLK:HCLK:PCLK = 200MHz:100MHz:50MHz */
165 # define MPLLCON 0x4C000004
166 # define MPLL_200MHz ((0x5C<<12)|(0x01<<4)|(0x02))
167 ldr r0, =MPLLCON
168 ldr r1, =0x5c012
169 str r1, [r0]
170
171 ldr r0, =CLKDIVN
172 mov r1, #3
173 str r1, [r0]
174 mrc p15, 0, r0, c1, c0, 0
175 orr r0, r0, #0xc0000000
176 mcr p15, 0, r0, c1, c0, 0
177 # endif
第165行通过宏定义设置MPLLCON寄存器的地址(0x4C000004),设置时钟倍频(注意m、p、s的取值在手册上有推荐范围):
- MDIV=0x5c=92, PDIV=0x01=1, SDIV=0x02=2
- m=MDIV+8=100, p=PDIV+2=3, s=SDIV=2
- Mpll = (2 * m * 12MHz) / (p * 2^s) = 200MHz
第171行开始设置时钟分频比,并修改CPU总线模式为异步总线模式。
上述修改总体如下:
------- /cpu/arm920t/start.S -------
136 #if defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410) || defined(CONFIG_S3C2440)
137 ldr r0, =pWTCON
138 mov r1, #0x0
139 str r1, [r0]
140
141 /*
142 * mask all IRQs by setting all bits in the INTMR - default
143 */
144 mov r1, #0xffffffff
145 ldr r0, =INTMSK
146 str r1, [r0]
147 # if defined(CONFIG_S3C2410)
148 ldr r1, =0x3ff
149 ldr r0, =INTSUBMSK
150 str r1, [r0]
151 # elif defined(CONFIG_S3C2440)
152 ldr r1, =0x7fff
153 ldr r0, =INTSUBMSK
154 str r1, [r0]
155 # endif
156
157 # if defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410)
158 /* FCLK:HCLK:PCLK = 1:2:4 */
159 /* default FCLK is 120 MHz ! */
160 ldr r0, =CLKDIVN
161 mov r1, #3
162 str r1, [r0]
163 # elif defined(CONFIG_S3C2440)
164 /* FCLK:HCLK:PCLK = 200MHz:100MHz:50MHz */
165 # define MPLLCON 0x4C000004
166 # define MPLL_200MHz ((0x5C<<12)|(0x01<<4)|(0x02))
167 ldr r0, =MPLLCON
168 ldr r1, =0x5c012
169 str r1, [r0]
170
171 ldr r0, =CLKDIVN
172 mov r1, #3
173 str r1, [r0]
174 mrc p15, 0, r0, c1, c0, 0
175 orr r0, r0, #0xc0000000
176 mcr p15, 0, r0, c1, c0, 0
177 # endif
178 #endif /* CONFIG_S3C2400 || CONFIG_S3C2410 || CONFIG_S3C2440 */
至此,将CPU设置为Supervisor模式,关闭了看门狗,屏蔽了所有中断,将时钟设置为FCLK : HCLK : PCLK = 200MHz : 100MHz : 50MHz,并将CPU总线设置为异步总线模式。
接下来,跳转到cpu_init_crit标号,进行CPU的一些底层设置:
------- /cpu/arm920t/start.S -------
184 #ifndef CONFIG_SKIP_LOWLEVEL_INIT
185 bl cpu_init_crit
186 #endif
bl为带返回的跳转指令,在跳转前会将R15(PC)的值保存着R14(LR)寄存器中,因此一般用于子程序的调用;在子程序返回时,只需执行指令MOV PC, LR,将跳转前保存的PC值重新放入PC,然后PC自动加4,即返回到调用子程序处的下一条指令继续执行。
在cpu_init_crit标号下,首先使失效指令缓存和数据缓存(Icache/Dcache),以及转换检测缓冲区(Translation Lookaside Buffers: TLB,即页表缓冲,包括指令TLB与数据TLB),即进行刷新与清空,确保以前的缓存内容不会被使用,防止CPU从缓存中取到错误的指令或数据:
------- /cpu/arm920t/start.S -------
266 cpu_init_crit:
267 /*
268 * flush v4 I/D caches
269 */
270 mov r0, #0
271 mcr p15, 0, r0, c7, c7, 0 /* flush v3/v4 cache */
272 mcr p15, 0, r0, c8, c7, 0 /* flush v4 TLB */
cache为高速缓存存储,用于存储CPU频繁使用的数据,当CPU要访问内存数据时,首先在cache中查看是否存在此数据且此数据是否过期,若没有过期则直接从cache中读出数据进行处理,并且CPU会定期将cache的数据写回内存,保证内存中数据的有效性。TLB在CPU访问内存数据时进行地址转换,当访问一个虚拟内存地址时,会利用TLB中的页表将虚拟地址转换为物理地址,然后访问。
通过操作CP15的寄存器7(Register 7, cache operation register)和寄存器8(Register 8, TLB operation register),来使无效缓存以及TLB:
然后禁用内存管理单元(Memory Management Unit: MMU),并重新使能缓存(cache):
------- /cpu/arm920t/start.S -------
274 /*
275 * disable MMU stuff and caches
276 */
277 mrc p15, 0, r0, c1, c0, 0
278 bic r0, r0, #0x00002300 @ clear bits 13, 9:8 (--V- --RS)
279 bic r0, r0, #0x00000087 @ clear bits 7, 2:0 (B--- -CAM)
280 orr r0, r0, #0x00000002 @ set bit 2 (A) Align
281 orr r0, r0, #0x00001000 @ set bit 12 (I) I-Cache
282 mcr p15, 0, r0, c1, c0, 0
通过操作寄存器1(Register 1, control register):
- 首先清零:确定异常寄存器基地址为0x00000000(bit 13),禁止访问MMU(bit 9:8),指定低位优先(小端)存储(bit 7),即内存的最低位地址存储数据的最低位数据,禁用数据缓存(Dcache)(bit 2),禁用地址对齐检查(bit 1),禁用MMU(bit 0);
- 然后置1:重新使能数据缓存(Dcache)(bit 2),重新使能指令缓存(Icache)(bit 12):
然后跳转到lowlevel_init标号,对SDRAM控制单元进行初始化:
------- /cpu/arm920t/start.S -------
289 mov ip, lr
290 bl lowlevel_init
291 mov lr, ip
292 mov pc, lr
这里在使用bl跳转指令前先将LR寄存器中的地址(cpu_init_crit的返回地址)保存到了IP(R12)寄存器中,进行保护,在lowlevel_init标号执行结束后,再从IP寄存器中取出cpu_init_crit的返回地址放入LR寄存器中,然后执行指令MOV PC, LR,跳转到调用cpu_init_crit标号的位置,并继续向下执行。否则在调用bl指令时,会将当前地址保存在LR寄存器中,覆盖了cpu_init_crit的返回地址。
查找lowlevel_init可知,位于/board/mini2440/lowlevel_init.S中。
/board/mini2440/lowlevel_init.S:
lowlevel_init.S中的程序主要用于S3C2440内存控制器的初始化,S3C2440的内存控制单元共有8个bank,其中bank 6和bank 7被设计用于连接SDRAM,内存映射如下:
这里仅关注SDRAM相关的设置,右侧是从NAND Flash启动时的内存映射,当bank 6和bank 7用于连接SDRAM时,其内存大小必须一致。MINI2440开发板使用两块EtronTech EM63A165TS-6 16M*16bit SDRAM,连接bank 6,组成16M*32bit的内存单元,共64M字节地址空间,对应内存地址0x30000000~0x33FFFFFF,数据位宽32bit,因此地址线A0/A1不用,地址线A24/A25用于bank选择,其引脚连接方式如下:
S3C2400内存控制单元的寄存器如下:
0x48000000 BWSCON: BUS WIDTH & WAIT CONTROL REGISTER
0x48000004~0X48000016 BANKCON0~BANKCON5: BANK CONTROL REGISTER
0x4800001C BANKCON6
0x48000020 BANKCON7
0x48000024 REFRESH: REFRESH CONTROL REGISTER
0x48000028 BANKSIZE: BANKSIZE REGISTER
BWSCON为32-bit的总线位宽和等待时间设置寄存器,每4 bits控制一个bank,以bank 7为例:
- ST7[31]:是否使用数据掩码引脚,对于SDRAM来说不需使用,此位清0;
- WS7[30]:是否使用SDRAM的等待信号,通常不使用,此位清0;
- DW7[29:28]:总线位宽,设置为32-bit,为0b10。
因此,BWSCON设置为0x22011110(考虑其它外设总线位宽,bank 0由硬件连线决定)。
BANK0~BANK5连接了不同的外围设备,在后续使用到时再考虑其设置,在这里BANKCON0~BANKCON5保持默认值。
BANKCON6、7均用于设置SDRAM的控制,且其值一致,与SDRAM相关的设置如下:
- MT[16:15]:设置内存类型,0b11-SDRAM(Sync. DRAM);
- Trcd[3:2]:RAS(Row Address Strobe 行地址选通) to CAS(Column Address Strobe 列地址选通) delay,查阅EM63A165TS-6数据手册,可知其仅有最小值要求,为18ns,而内存控制器使用HCLK=100MHz,因此可设置为0b01-3 clocks,稍微留有余量;
- SCAN[1:0]:Column address number,列地址宽度,查阅EM63A165TS-6数据手册可知,地址为(EM63A165共有13根地址线A0-A12,其中A0-A8为列地址),0b01-9-bit。
因此BANKCON6、7设置为0x00018005。
REFRESH寄存器用于设置SDRAM的自动刷新,SDRAM(Synchronous Dynamic Random-Access Memory 同步动态随机存取内存)采用电容结构进行数据存储,由于电容的电荷存在泄漏,因此必须隔一段时间刷新一次。
查阅EM63A165TS-6数据手册,其刷新周期为8192 refresh cycles per 64ms,(目前公认的存储体中电容的数据有效保存期上限是64ms,即每一行刷新的循环周期为64ms,而EM63A165TS-6的一个bank有8192行),根据S3C2440手册进行设置:
- REFEN[23]:使能SDRAM刷新,0b1-Enable;
- TREFMD[22]:刷新模式,其中Self Refresh模式一般在休眠时使用,0b0-CBR/Auto Refresh;
- Trp[21:20]:RAS预充电时间,根据EM63A165TS-6数据手册,最小为18ns,因此设置为0b00-2 clocks;
- Tsrc[19:18]:半行循环时间,根据EM63A165TS-6数据手册,Row Cycle Time Trc最小为60ns,因此设置为默认的0b11-7 clocks;
- Refresh Counter[10:0]:刷新计数器,根据SDRAM刷新周期8192 refresh cycles per 64ms,计算结果为1268。
因此,REFRESH设置为0x008C04F4。
BANKSIZE寄存器:
- BURST_EN[7]:使能ARM核突发传输,0b1-Enable burst operation;
- SCKE_EN[5]:使用SCKE信号进入省电模式,0b1-SDRAM power down mode enable;
- SCLK_EN[4]:仅在访问SDRAM时发出SCLK信号,0b1-SCLK is active only during the access;
- BK76MAP[2:0]:bank6/7 容量,0b001-64MB/64MB。
因此,BANKSIZE设置为0xB1。
MRSRB6/7寄存器,SDRAM模式设置寄存器:
- CL[6:4]:CAS latency,CAS延迟时间,根据EM63A165TS-6手册,最小值为2.5ns,可以设置为0b011-3 clocks。
因此,MRSRB6/7设置为0x30。
在lowlevel_init标号下,将内存控制单元的设置数据保存在标号为SMRDATA的数据缓冲池(利用.ltorg关键字,又叫文字池,使处理器不会错误的把池中的数据当作指令来执行)中。
S3C2440内存控制器的寄存器地址从0x48000000开始是连续的,因此此处采用循环赋值的方式,将各个寄存器的设置值依次写入从0x48000000开始的地址中。
数据缓冲池标号为SMRDATA,代表其起始地址,将其与_TEXT_BASE相减并加上数据缓冲池的大小,用于计算在向寄存器写入值时的最终地址,修改如下:
------- /board/mini2440/lowlevel_init.S -------
132 .globl lowlevel_init
133 lowlevel_init:
134 /* memory control configuration */
135 /* make r0 relative the current location so that it */
136 /* reads SMRDATA out of FLASH rather than memory ! */
137 ldr r0, =SMRDATA
138 ldr r1, _TEXT_BASE
139 sub r0, r0, r1
140 ldr r1, =BWSCON /* Bus Width Status Controller */
141 add r2, r0, #13*4
142 0:
143 ldr r3, [r0], #4
144 str r3, [r1], #4
145 cmp r2, r0
146 bne 0b
147
148 /* everything is fine now */
149 mov pc, lr
150
151 .ltorg
152 /* the literal pools origin */
153
154 SMRDATA:
155 .word (0+(B1_BWSCON<<4)+(B2_BWSCON<<8)+(B3_BWSCON<<12)+(B4_BWSCON<<16)+(B5_BWSCON<<20)+(B6_BWSCON<<24)+(B7_BWSCON<<28))
156 .word ((B0_Tacs<<13)+(B0_Tcos<<11)+(B0_Tacc<<8)+(B0_Tcoh<<6)+(B0_Tah<<4)+(B0_Tacp<<2)+(B0_PMC))
157 .word ((B1_Tacs<<13)+(B1_Tcos<<11)+(B1_Tacc<<8)+(B1_Tcoh<<6)+(B1_Tah<<4)+(B1_Tacp<<2)+(B1_PMC))
158 .word ((B2_Tacs<<13)+(B2_Tcos<<11)+(B2_Tacc<<8)+(B2_Tcoh<<6)+(B2_Tah<<4)+(B2_Tacp<<2)+(B2_PMC))
159 .word ((B3_Tacs<<13)+(B3_Tcos<<11)+(B3_Tacc<<8)+(B3_Tcoh<<6)+(B3_Tah<<4)+(B3_Tacp<<2)+(B3_PMC))
160 .word ((B4_Tacs<<13)+(B4_Tcos<<11)+(B4_Tacc<<8)+(B4_Tcoh<<6)+(B4_Tah<<4)+(B4_Tacp<<2)+(B4_PMC))
161 .word ((B5_Tacs<<13)+(B5_Tcos<<11)+(B5_Tacc<<8)+(B5_Tcoh<<6)+(B5_Tah<<4)+(B5_Tacp<<2)+(B5_PMC))
162 .word ((B6_MT<<15)+(B6_Trcd<<2)+(B6_SCAN))
163 .word ((B7_MT<<15)+(B7_Trcd<<2)+(B7_SCAN))
164 .word ((REFEN<<23)+(TREFMD<<22)+(Trp<<20)+(Trc<<18)+(Tchr<<16)+REFCNT)
165 .word 0xB1
166 .word 0x30
167 .word 0x30
根据分析,修改寄存器具体的值如下:
------- /board/mini2440/lowlevel_init.S -------
45 #define BWSCON 0x48000000
46
47 /* BWSCON */
48 #define DW8 (0x0)
49 #define DW16 (0x1)
50 #define DW32 (0x2)
51 #define WAIT (0x1<<2)
52 #define UBLB (0x1<<3)
53
54 #define B1_BWSCON (DW16)
55 #define B2_BWSCON (DW16)
56 #define B3_BWSCON (DW16)
57 #define B4_BWSCON (DW16)
58 #define B5_BWSCON (DW8)
59 #define B6_BWSCON (DW32)
60 #define B7_BWSCON (DW32)
61
62 /* BANK0CON */
63 #define B0_Tacs 0x0 /* 0clk */
64 #define B0_Tcos 0x0 /* 0clk */
65 #define B0_Tacc 0x7 /* 14clk */
66 #define B0_Tcoh 0x0 /* 0clk */
67 #define B0_Tah 0x0 /* 0clk */
68 #define B0_Tacp 0x0
69 #define B0_PMC 0x0 /* normal */
70
71 /* BANK1CON */
72 #define B1_Tacs 0x0 /* 0clk */
73 #define B1_Tcos 0x0 /* 0clk */
74 #define B1_Tacc 0x7 /* 14clk */
75 #define B1_Tcoh 0x0 /* 0clk */
76 #define B1_Tah 0x0 /* 0clk */
77 #define B1_Tacp 0x0
78 #define B1_PMC 0x0
79
80 #define B2_Tacs 0x0
81 #define B2_Tcos 0x0
82 #define B2_Tacc 0x7
83 #define B2_Tcoh 0x0
84 #define B2_Tah 0x0
85 #define B2_Tacp 0x0
86 #define B2_PMC 0x0
87
88 #define B3_Tacs 0x0 /* 0clk */
89 #define B3_Tcos 0x0 /* 0clk */
90 #define B3_Tacc 0x7 /* 14clk */
91 #define B3_Tcoh 0x0 /* 0clk */
92 #define B3_Tah 0x0 /* 0clk */
93 #define B3_Tacp 0x0 /* 0clk */
94 #define B3_PMC 0x0 /* normal */
95
96 #define B4_Tacs 0x0 /* 0clk */
97 #define B4_Tcos 0x0 /* 0clk */
98 #define B4_Tacc 0x7 /* 14clk */
99 #define B4_Tcoh 0x0 /* 0clk */
100 #define B4_Tah 0x0 /* 0clk */
101 #define B4_Tacp 0x0
102 #define B4_PMC 0x0 /* normal */
103
104 #define B5_Tacs 0x0 /* 0clk */
105 #define B5_Tcos 0x0 /* 0clk */
106 #define B5_Tacc 0x7 /* 14clk */
107 #define B5_Tcoh 0x0 /* 0clk */
108 #define B5_Tah 0x0 /* 0clk */
109 #define B5_Tacp 0x0
110 #define B5_PMC 0x0 /* normal */
111
112 #define B6_MT 0x3 /* SDRAM */
113 #define B6_Trcd 0x1
114 #define B6_SCAN 0x1 /* 9bit */
115
116 #define B7_MT 0x3 /* SDRAM */
117 #define B7_Trcd 0x1 /* 3clk */
118 #define B7_SCAN 0x1 /* 9bit */
119
120 /* REFRESH parameter */
121 #define REFEN 0x1 /* Refresh enable */
122 #define TREFMD 0x0 /* CBR(CAS before RAS)/Auto refresh */
123 #define Trp 0x0 /* 2clk */
124 #define Trc 0x3 /* 7clk */
125 #define Tchr 0x0 /* 0clk */
126 #define REFCNT 1268 /* period=7.8125us, HCLK=100Mhz, (2048+1-7.8125*100) */
至此,lowlevel_init.S修改完毕,下面回到/cpu/arm920t/start.S。
/cpu/arm920t/start.S:
从lowlevel_init跳转回到cpu_init_crit再回到start.S的第185行,开始U-Boot程序的重定位过程,由于S3C2440从NAND Flash启动时,芯片内部固化的程序会将NAND Flash中的前4KB内容拷贝到片内4KB大小的SRAM中,并从0x00000000开始运行,而U-Boot程序一般大于4KB,因此需要将程序从NAND Flash中拷贝到内存即SDRAM(64MB)中继续运行,所以这前4KB的程序的作用主要就是初始化SDRAM,并将程序拷贝到SDRAM中。
程序在链接时将start.S放在代码段的最前,且start.S中标号_start位于起始,因此烧写时,_start标号在NAND Flash中位于0x00000000的地址,在上电从NAND Flash启动时,会将NAND Flash的前4KB按顺序拷贝到片内SRAM,即当程序运行在片内的SRAM中时,其运行地址为0x00000000;而根据链接脚本,若程序运行在内存(SDRAM)中时,其运行地址应该位于0x33F80000处。
第190行首先获取_start标号的运行位置,然后与_TEXT_BASE进行比较,若两者相等则说明程序已经运行在内存中了,则跳到stack_setup标号去进行堆栈的设置,否则需要将程序从Flash中拷贝到SDRAM。
第195行到204行对程序进行拷贝,然而,U-Boot默认程序是存储在NOR Flash中的,CPU可以像读取内存一样直接访问NOR Flash,而NAND Flash无法直接进行访问,因此需要添加NAND Flash的相关操作函数。
------- /cpu/arm920t/start.S -------
75 _TEXT_BASE:
76 .word TEXT_BASE
77
78 .globl _armboot_start
79 _armboot_start:
80 .word _start
...
85 .globl _bss_start
86 _bss_start:
87 .word __bss_start
88
89 .globl _bss_end
90 _bss_end:
91 .word _end
...
189 relocate: /* relocate U-Boot to RAM */
190 adr r0, _start /* r0 <- current position of code */
191 ldr r1, _TEXT_BASE /* test if we run from flash or RAM */
192 cmp r0, r1 /* don't reloc during debug */
193 beq stack_setup
194
195 ldr r2, _armboot_start
196 ldr r3, _bss_start
197 sub r2, r3, r2 /* r2 <- size of armboot */
198 add r2, r0, r2 /* r2 <- source end address */
199
200 copy_loop:
201 ldmia r0!, {r3-r10} /* copy from source address [r0] */
202 stmia r1!, {r3-r10} /* copy to target address [r1] */
203 cmp r0, r2 /* until source end addreee [r2] */
204 ble copy_loop
- adr r0, _start:获得_start的当前执行位置,由PC+offset决定,得到有效地址;
- ldr r0, =_start:得到_start的绝对地址,是由链接时决定的。
添加lowlevel_nand.c文件,存放NAND Flash相关操作函数。
/board/mini2440/lowlevel_nand.c:
S3C2440与NAND Flash引脚连接如下,左侧为时序控制引脚,右侧为命令/地址/数据引脚:
MINI2440开发板使用一块Samsung K9F2G08U0C NAND Flash,大小为(256M + 8M) x 8bit。
- 1 Page = (2K + 64)Bytes
- 1 Block = (2K + 64)B x 64 Pages = (128K + 4K) Bytes
- 1 Device = (2K+64)B x 64Pages x 2048 Blocks = 2112 Mbits
在页读取时,每页最后64Bytes的OOB(Out Of Bank)区域并不会被读出,即CPU只能访问前2048字节。读操作时序如下,首先发送00h命令,然后传送5个周期的地址,前两个地址周期的地址称为列地址(Column Address),表示某一页中的地址,其范围为0~2047,列地址=addr%2048;后三个地址周期为页地址(Page Address),表示读取第几页,页地址=addr/2048;然后发送30h命令,之后读取NAND Flash传送出来的数据。
S3C2440的NAND Flash控制器的寄存器如下:
0x4E000000 NFCONF:NAND FLASH CONFIGURATION REGISTER
0x4E000004 NFCONT:CONTROL REGISTER
0x4E000008 NFCMMD:COMMAND REGISTER
0x4E00000C NFADDR:ADDRESS REGISTER
0x4E000010 NFDATA:DATA REGISTER
0x4E000020 NFSTAT:NFCON STATUS REGISTER
NFCONF寄存器为NAND Flash控制器配置寄存器:
- TACLS[15:14]:命令锁存信号(CLE)和地址锁存信号(ALE)信号的持续时间,查阅K9F2G08U0C的数据手册可知,最小为5ns(tCLH/tALH),因此设置为1,即1个HCLK=10ns;
- TWRPH0[10:8]:写信号(WE)持续时间,查阅K9F2G08U0C的数据手册可知,最小为12ns(tWP),因此设置为3,即4个HCLK=40ns;
- TWRPH1[6:4]:命令/地址/数据在写信号取消之后的持续时间,查阅可知最小为5ns(tDH),因此设置为0,即1个HCLK=10ns。
NFCONT寄存器为NAND Flash控制器的控制寄存器,相关的位如下:
- InitECC[4]:初始化ECC解码/编码器,设置为1;
- Reg_nCE[1]:NAND Flash片选信号;
- MODE[0]:NAND Flash控制器使能,设置为1;
NFCMMD为NAND Flash命令寄存器,所有发送给NAND Flash的命令字都写入此寄存器;
NFADDR为NAND Flash地址寄存器,所有发送给NAND Flash的地址都写入此寄存器;
NFDATA为NAND Flash数据寄存器,发送/接受NAND Flash数据,K9F2G08U0C位宽为8,因此此寄存器仅低8位有效。
NFSTAT为NAND Flash状态寄存器:
- RnB[0]:反映NAND Flash的状态(是否忙)。
在/board/mini2440目录下新建lowlevel_nand.c文件,参考/drivers/nand目录下文件,编写NAND Flash的操作函数等:
tzw@tzw-pc:~/arm/u-boot/u-boot-mini2440-1.1.6$ cd board/mini2440/
tzw@tzw-pc:~/arm/u-boot/u-boot-mini2440-1.1.6/board/mini2440$ touch lowlevel_nand.c
tzw@tzw-pc:~/arm/u-boot/u-boot-mini2440-1.1.6/board/mini2440$ ls lowlevel_nand.c
lowlevel_nand.c
首先通过宏定义设置相关寄存器与配置:
------- /board/mini2440/lowlevel_nand.c -------
7 #define CLKCON (*(volatile unsigned long *)0x4C00000C)
8 #define NFCONF (*(volatile unsigned long *)0x4E000000)
9 #define NFCONT (*(volatile unsigned long *)0x4E000004)
10 #define NFCMMD (*(volatile unsigned char *)0x4E000008)
11 #define NFADDR (*(volatile unsigned char *)0x4E00000C)
12 #define NFDATA (*(volatile unsigned char *)0x4E000010)
13 #define NFSTAT (*(volatile unsigned char *)0x4E000020)
14
15 #define TACLS (0x0)
16 #define TWRPH0 (0x3)
17 #define TWRPH1 (0x0)
18 #define BusWidth (0x0)
19 #define InitECC (0x1)
20 #define Reg_nCE (0x1)
21 #define NANDEN (0x1)
22
23 #define NAND_CMD_SELECT 1
24 #define NAND_CMD_DESELECT 2
25 #define PageSize 2048
初始化函数,首先将CLKCON的第4位置1,使能NAND Flash控制器的HCLK时钟,然后进行设置并复位NAND Flash(发送FFh命令):
------- /board/mini2440/lowlevel_nand.c -------
37 void NAND_Init()
38 {
39 CLKCON |= 1<<4;
40 NFCONF = (TACLS<<12) | (TWRPH0<<8) | (TWRPH1<<4) | (BusWidth);
41 NFCONT = (InitECC<<4) | (Reg_nCE<<1) | (NANDEN);
42 nand_reset();
43 }
NAND Flash读取函数,读取NAND Flash内容放入指定的buf中。其中列地址为addr%2048=addr&2047,页地址为addr/2048=(addr&0x0ffff800)>>11;首先发送00h读取命令,然后发送5个地址周期,再发送30h命令,最后读取一页的数据:
------- /board/mini2440/lowlevel_nand.c -------
45 void NAND_Read_Raw(unsigned char *buf, unsigned long addr, int size)
46 {
47 int i;
48 unsigned int column, page;
49 nand_select_chip(1);
50 for (i=addr; i<(addr+size);) {
51 column = i & 0x7ff;
52 page = (i & 0x0ffff800) >> 11;
53 nand_cmdfunc(0x00, column, page);
54 nand_read_buf(buf, PageSize);
55 buf += PageSize;
56 i += PageSize;
57 }
58 nand_select_chip(0);
59 }
NAND Flash命令与地址函数,首先调用nand_hwcontrol函数发送命令,并判断是否需要发送地址,将列地址与页地址分成5个地址周期进行发送,最后判断是否为NAND Flash读取命令(00h),发送读取的第二个命令字30h;并等待NAND Flash空闲:
------- /board/mini2440/lowlevel_nand.c -------
102 static void nand_cmdfunc(unsigned char command, int column, int page_addr)
103 {
104 nand_hwcontrol(command);
105
106 if (column != -1 || page_addr != -1) {
107 if (column != -1) {
108 nand_addr(column & 0xff);
109 nand_addr(column >> 8);
110 }
111 if (page_addr != -1) {
112 nand_addr((unsigned char)(page_addr & 0xff));
113 nand_addr((unsigned char)((page_addr >> 8) & 0xff));
114 nand_addr((unsigned char)((page_addr >> 16) & 0xff));
115 }
116 }
117
118 if (command == 0x00)
119 nand_hwcontrol(0x30);
120
121 while (!nand_dev_ready());
122 }
NAND Flash复位函数,通过发送FFh命令进行复位:
------- /board/mini2440/lowlevel_nand.c -------
61 static void nand_reset()
62 {
63 nand_select_chip(1);
64 nand_cmdfunc(0xff, -1, -1);
65 nand_select_chip(0);
66 }
NAND Flash底层硬件控制函数,包括片选操作与命令发送:
------- /board/mini2440/lowlevel_nand.c -------
68 static void nand_hwcontrol(unsigned char cmd)
69 {
70 switch (cmd) {
71 case NAND_CMD_SELECT: NFCONT &= ~(1<<1); break;
72 case NAND_CMD_DESELECT: NFCONT |= (1<<1); break;
73 default: NFCMMD = cmd; break;
74 }
75 }
NAND Flash地址发送函数,将地址写入NFADDR寄存器进行传送:
------- /board/mini2440/lowlevel_nand.c -------
77 static void nand_addr(unsigned char addr)
78 {
79 NFADDR = addr;
80 }
NAND Flash读取函数,读取NFDATA寄存器内容:
------- /board/mini2440/lowlevel_nand.c -------
82 static void nand_read_buf(unsigned char *buf, int len)
83 {
84 int i;
85 for (i=0; i<len; i++)
86 buf[i] = NFDATA;
87 }
NAND Flash检测是否空闲的函数,返回NFSTAT第0位:
------- /board/mini2440/lowlevel_nand.c -------
89 static unsigned int nand_dev_ready()
90 {
91 return (NFSTAT & 0x01);
92 }
NAND Flash片选函数,通过调用nand_hwcontrol函数:
------- /board/mini2440/lowlevel_nand.c -------
94 static void nand_select_chip(int select)
95 {
96 if (select == 1)
97 nand_hwcontrol(NAND_CMD_SELECT);
98 else
99 nand_hwcontrol(NAND_CMD_DESELECT);
100 }
/board/mini2440/lowlevel_nand.c文件全部内容如下:
------- /board/mini2440/lowlevel_nand.c -------
1 /*
2 * MINI2440 lowlevel nand functions
3 *
4 * tzw
5 */
6
7 #define CLKCON (*(volatile unsigned long *)0x4C00000C)
8 #define NFCONF (*(volatile unsigned long *)0x4E000000)
9 #define NFCONT (*(volatile unsigned long *)0x4E000004)
10 #define NFCMMD (*(volatile unsigned char *)0x4E000008)
11 #define NFADDR (*(volatile unsigned char *)0x4E00000C)
12 #define NFDATA (*(volatile unsigned char *)0x4E000010)
13 #define NFSTAT (*(volatile unsigned char *)0x4E000020)
14
15 #define TACLS (0x0)
16 #define TWRPH0 (0x3)
17 #define TWRPH1 (0x0)
18 #define BusWidth (0x0)
19 #define InitECC (0x1)
20 #define Reg_nCE (0x1)
21 #define NANDEN (0x1)
22
23 #define NAND_CMD_SELECT 1
24 #define NAND_CMD_DESELECT 2
25 #define PageSize 2048
26
27 void NAND_Init();
28 void NAND_Read_Raw(unsigned char *buf, unsigned long addr, int size);
29 static void nand_reset();
30 static void nand_hwcontrol(unsigned char cmd);
31 static void nand_addr(unsigned char addr);
32 static void nand_read_buf(unsigned char *buf, int len);
33 static unsigned int nand_dev_ready();
34 static void nand_select_chip(int select);
35 static void nand_cmdfunc(unsigned char command, int column, int page_addr);
36
37 void NAND_Init()
38 {
39 CLKCON |= 1<<4;
40 NFCONF = (TACLS<<12) | (TWRPH0<<8) | (TWRPH1<<4) | (BusWidth);
41 NFCONT = (InitECC<<4) | (Reg_nCE<<1) | (NANDEN);
42 nand_reset();
43 }
44
45 void NAND_Read_Raw(unsigned char *buf, unsigned long addr, int size)
46 {
47 int i;
48 unsigned int column, page;
49 nand_select_chip(1);
50 for (i=addr; i<(addr+size);) {
51 column = i & 0x7ff;
52 page = (i & 0x0ffff800) >> 11;
53 nand_cmdfunc(0x00, column, page);
54 nand_read_buf(buf, PageSize);
55 buf += PageSize;
56 i += PageSize;
57 }
58 nand_select_chip(0);
59 }
60
61 static void nand_reset()
62 {
63 nand_select_chip(1);
64 nand_cmdfunc(0xff, -1, -1);
65 nand_select_chip(0);
66 }
67
68 static void nand_hwcontrol(unsigned char cmd)
69 {
70 switch (cmd) {
71 case NAND_CMD_SELECT: NFCONT &= ~(1<<1); break;
72 case NAND_CMD_DESELECT: NFCONT |= (1<<1); break;
73 default: NFCMMD = cmd; break;
74 }
75 }
76
77 static void nand_addr(unsigned char addr)
78 {
79 NFADDR = addr;
80 }
81
82 static void nand_read_buf(unsigned char *buf, int len)
83 {
84 int i;
85 for (i=0; i<len; i++)
86 buf[i] = NFDATA;
87 }
88
89 static unsigned int nand_dev_ready()
90 {
91 return (NFSTAT & 0x01);
92 }
93
94 static void nand_select_chip(int select)
95 {
96 if (select == 1)
97 nand_hwcontrol(NAND_CMD_SELECT);
98 else
99 nand_hwcontrol(NAND_CMD_DESELECT);
100 }
101
102 static void nand_cmdfunc(unsigned char command, int column, int page_addr)
103 {
104 nand_hwcontrol(command);
105
106 if (column != -1 || page_addr != -1) {
107 if (column != -1) {
108 nand_addr(column & 0xff);
109 nand_addr(column >> 8);
110 }
111 if (page_addr != -1) {
112 nand_addr((unsigned char)(page_addr & 0xff));
113 nand_addr((unsigned char)((page_addr >> 8) & 0xff));
114 nand_addr((unsigned char)((page_addr >> 16) & 0xff));
115 }
116 }
117
118 if (command == 0x00)
119 nand_hwcontrol(0x30);
120
121 while (!nand_dev_ready());
122 }
在/board/mini2440/Makefile中添加lowlevel_nand.c的编译:
------- /board/mini2440/Makefile -------
28 COBJS := lowlevel_nand.o mini2440.o flash.o
并保证lowlevel_nand.o能够放在前4KB中,将其紧接start.o放置,在链接脚本/board/mini2440/u-boot.lds中修改:
------- /board/mini2440/u-boot.lds -------
32 . = ALIGN(4);
33 .text :
34 {
35 cpu/arm920t/start.o (.text)
36 board/mini2440/lowlevel_nand.o (.text)
37 *(.text)
38 }
/cpu/arm920t/start.S:
回到start.S中,由于lowlevel_nand.c编写使用C语言,因此在调用前需要设置堆栈,将start.S中的堆栈设置放到重定位的前面:
------- /cpu/arm920t/start.S -------
188 /* Set up the stack */
189 stack_setup:
190 ldr r0, _TEXT_BASE /* upper 128 KiB: relocated uboot */
191 sub r0, r0, #CFG_MALLOC_LEN /* malloc area */
192 sub r0, r0, #CFG_GBL_DATA_SIZE /* bdinfo */
193 #ifdef CONFIG_USE_IRQ
194 sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
195 #endif
196 sub sp, r0, #12 /* leave 3 words for abort-stack */
197
198 #ifndef CONFIG_SKIP_RELOCATE_UBOOT
199 relocate:
- 通过计算,在内存中设置各部分的存放的内容,如CFG_GBL_DATA_SIZE用于存放后续用到的gd_t结构体等;并将堆栈指针SP(R12)指向栈底。
可以知道U-Boot此时对内存的分配如下:
修改重定位部分代码:
------- /cpu/arm920t/start.S -------
199 relocate: /* relocate U-Boot to RAM */
200 adr r0, _start /* r0 <- current position of code */
201 ldr r1, _TEXT_BASE /* test if we run from flash or RAM */
202 cmp r0, r1 /* don't reloc during debug */
203 beq clear_bss
204 # if defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410)
205 ldr r2, _armboot_start
206 ldr r3, _bss_start
207 sub r2, r3, r2 /* r2 <- size of armboot */
208 add r2, r0, r2 /* r2 <- source end address */
209 copy_loop:
210 ldmia r0!, {r3-r10} /* copy from source address [r0] */
211 stmia r1!, {r3-r10} /* copy to target address [r1] */
212 cmp r0, r2 /* until source end addreee [r2] */
213 ble copy_loop
214 # elif defined(CONFIG_S3C2440)
215 bl NAND_Init
216 ldr r0, _TEXT_BASE
217 ldr r1, =0
218 ldr r2, =0x20000
219 bl NAND_Read_Raw
220 # endif
- 在汇编中,调用的函数若需要提供参数,则应该将所需的参数按顺序存放于通用寄存器中(注意保存寄存器中原来的值,并在调用后恢复,这里用到的通用寄存器不需进行保存),即void NAND_Read_Raw(unsigned char *buf, unsigned long addr, int size)函数的三个参数分别写入r0~r2;
- 代码段起始地址TEXT_BASE为0x33F80000,而编译生成的二进制文件u-boot.bin大小约为100kB,因此拷贝的大小设置为0x20000Bytes=128kB。(也可以设置成其他大小,但必须大于等于u-boot.bin的大小);
- 也可以在lowlevel_nand.c中加入串口的程序,便于输出调试信息。
接下来清BSS段,BSS(Block Started by Symbol)段通常用于存放程序中未初始化或者初始化为0的全局变量和静态变量,是内存中的一小块区域,因此在程序执行前需要对其进行清0:
------- /cpu/arm920t/start.S -------
223 clear_bss:
224 ldr r0, _bss_start /* find start of bss segment */
225 ldr r1, _bss_end /* stop here */
226 mov r2, #0x00000000 /* clear */
227
228 clbss_l: str r2, [r0] /* clear loop... */
229 add r0, r0, #4
230 cmp r0, r1
231 ble clbss_l
- 这里的_bss_start与_bss_end为链接脚本u-boot.lds中定义的地址,即对应的内存中的地址;因此,这里清0的即是内存中对应BSS段的内容。
接下来223行到252行直接跳过(#if 0)。
最后,将跳转到start_armboot函数(查找可知在/lib_arm/board.c文件中,注意这个不是前面的_armboot_start,_armboot_start标号的值为_start标号的地址,即0x33F80000),开始执行U-Boot的第二阶段。此阶段代码用C语言编写,所以在前面需要设置堆栈(Stack)并清BSS段:
------- /cpu/arm920t/start.S -------
254 ldr pc, _start_armboot
255
256 _start_armboot: .word start_armboot
至此,/cpu/arm920t/start.S完成的第一阶段任务如下:
1、从_start标号开始执行,首先跳转到reset标号;
2、在reset标号中,设置CPU进入Supervisor模式,然后关闭看门狗、屏蔽中断、设置时钟;
3、跳转到cpu_init_crit标号中,刷新指令/数据缓存,禁用MMU(使用真实的物理地址);
4、跳转到lowlevel_init标号中,初始化存储控制器(重点是初始化SDRAM部分);
5、执行完毕回到start.S,初始化堆栈(Stack)设置;
6、进行代码重定位,将U-Boot代码从NAND Flash拷贝到SDRAM,并清BSS段;
7、最后跳转到start_armboot函数中,开始执行U-Boot第二阶段。