出处:http://blog.chinaunix.net/u1/58203/showart_489453.html
我要分析的是arm启动时的bootloader,关于bootloader的源代码版本有很多,比较经典的有blob,u-boot,vivi等,而且网上关于这些源码的分析,移植的文章也是很多。但详细可供参考的却寥寥无几。于是只有自己重新进行分析细化,在提升自己读写程序经验的同时,也培养自己写文档的习惯,当然也趁这个机会,为网络做些贡献。希望种下的大树,也能够乘凉。
软件环境:1: vivi-embeder-1.0.0.tar.bz2(验证可用) 下载地址http://www.eefocus.com/bbs/attachment.php?aid=2887 下载所在论坛用户名和密码都用cellboy 2:Soursce Insight 3.5(可到网上下载一个绿色版本使用) 这个软件的好处是有跟踪功能,这对于定义了大量头文件的vivi源代码来说绝对是个好的选择。强烈建议使用,虽然一开始上手有点生涩,花一个晚上摸索下吧~爱上这个软件可不要说是我介绍的。 |
硬件环境: s3c3410开发平台(arm920t) NAND flash (SAMSUNG K9F1208)64MB Sdram (HY57V561620BT-H) 32MX2=64M 串口 (RS232) 2 个 ......... |
/*
* vivi/arch/s3c2410/head.S:
* Author: Janghoon Lyu <nandy@mizi.com>
* Date : $Date: 2003/02/26 10:38:11 $
*
* $Revision: 1.18 $
* modify by Yong Fu :Readonly
* Date :2008/03/05
*/
#include "config.h"
#include "linkage.h"
#include "machine.h"
@
@code start
@
.text @下面是代码段
ENTRY(_start) @"vivi/include/linkage.h" 的宏封装,其等同与
.globle _start ; .align 0; _start: @.align 0该指令表示把当 @前位置对齐到下一个字(4字节为一字)的起始位置. |
ENTRY(ResetEntryPoint) @其等同与
.globle ResetEntryPoint; .align 0; ResetEntryPoint: |
@
@ Exception vector table (physical address = 0x00000000)
@
ARM体系结构规定在上电复位后的起始位置,必须有8条连续的跳转指令,通过硬件实现。他们就是异常向量表。ARM在上电复位后,是从0x00000000开始启动的,其实如果bootloader存在,在执行下面第一条指令后,就无条件跳转到Reset,下面一部分并没执行。设置异常向量表的作用是识别bootloader。以后系统每当有异常出现,则CPU会根据异常号,从内存的0x00000000处开始查表做相应的处理 |
@ 0x00: Reset
b Reset
@ 0x04: Undefined instruction exception
UndefEntryPoint:
这个变量并没有进行宏定义,我猜原因是异常向量是由硬件实现若发生对应异常,自己直接跳转到这个位置,UndefEntryPoint:是可以去掉的。 |
b HandleUndef
@ 0x08: Software interrupt exception
SWIEntryPoint:
b HandleSWI
@ 0x0c: Prefetch Abort (Instruction Fetch Memory Abort)
PrefetchAbortEnteryPoint:
b HandlePrefetchAbort
@ 0x10: Data Access Memory Abort
DataAbortEntryPoint:
b HandleDataAbort
@ 0x14: Not used
NotUsedEntryPoint:
b HandleNotUsed
@ 0x18: IRQ(Interrupt Request) exception
IRQEntryPoint:
b HandleIRQ
@ 0x1c: FIQ(Fast Interrupt Request) exception
FIQEntryPoint:
b HandleFIQ
8条连续跳转的指令,7种处理器模式,一种未使用。 |
@
@ VIVI magics
@
关于magics number(魔幻数字),大致了解了下其作用,主要是为了提高程序的健壮性,反馈给用户程序信息。但是具体使用方法还有待研究,关于magic number,上传一份资料。具体见1-5节
|
@ 0x20: magic number so we can verify that we only put
.long 0
@ 0x24:
.long 0
@ 0x28: where this vivi was linked, so we can put it in memory in the right place
.long _start
这处存放的_start是在代码的第一句中定义的全局变量, |
@ 0x2C: this contains the platform, cpu and machine id
.long ARCHITECTURE_MAGIC
ARCHITECTURE_MAGIC这个变量出现在 "vivi/include/platform/Smdk2410.h" #include "architecture.h" 在"architecture.h"中ARM_PLATFORM=1(arm开发板的信息) ARM_S3C2410_CPU=6(armcpu的信息) 很容易推出ARCHITECTURE_MAGIC=0x10600c1 定义platform,cpu,machine的id信息; |
@ 0x30: vivi capabilities
.long 0
#ifdef CONFIG_PM @CONFIG_PM为配置电源管理模式
在这里先对电源管理模式进行一下描述: 在S3C2410的数据手册第七章有专门章节说明POWER MANAGEMENT:
在这里将详细讨论Power-OFF MODE Procedure to Enter Power_OFF mode(在Power_OFF MODE的程序设置)在这里我们主要关注后面四项,因为我们要设置的就是SDRAM在POWER OFF MODE 下的寄存器设置。 |
@ 0x34:
b SleepRamProc
只要翻译出上面四句话,跳转到下面的SleepRamProc 子例程就相当好理解了 |
#endif
#ifdef CONFIG_TEST
@ 0x38:
b hmi
#endif
@
@ Start VIVI head
@
Reset:
@ disable watch dog timer
mov r1, #0x53000000
mov r2, #0x0
str r2, [r1] @将r2里面的值存储到r1所包含的地址中
初始化硬件过程中第一件事就是关闭看门狗,以免watchdog记时超时引发系统重起,系统上电复位后watchdog默认是开着的,而vivi不需要他。 s3c2410用了3个寄存器对watchdog进行操作,3个寄存器分别为:WTCON,WTDAT,WTCNT。 以上各个寄存器的详细信息请参考s3c2410数据手册上关于第18章watchdog部分; 在这里我们只需要设置WTCON |
#ifdef CONFIG_S3C2410_MPORT3
mov r1, #0x56000000
mov r2, #0x00000005
str r2, [r1, #0x70]
将值0x5(0101)写入 GPHCON 0x56000070 Configure the pins of port H GPH1 [3:2] 00 = Input 01 = Output |
mov r2, #0x00000001
str r2, [r1, #0x78]
将值0x1写入 GPHUP 0x56000078 Pull-up disable register for port H GPH[10:0] 0: The pull-up function attached to to the corresponding port pin is enabled. 在GPHO设置上拉电阻 |
mov r2, #0x00000001
str r2, [r1, #0x74]
将值0x01写入 GPHDAT 0x56000074 The data register for port H When the port is configured as input port, data from external sources can be 在这里设置GPH0为高电平【1】,GPHO1为低电平【0】这两位的值发送到对应的端口 |
#endif
对于上面这段代码,整体在做什么,现在还不太清楚,看到后面的程序应该会清楚。这段代码是可选执行的,完全可以去除。 |
@ disable all interrupts
mov r1, #INT_CTL_BASE
mov r2, #0xffffffff
str r2, [r1, #oINTMSK]
ldr r2, =0x7ff
str r2, [r1, #oINTSUBMSK]
上电复位后,所有中断默认是关闭的,所以可以不需要代码实现。但是为了保险起见可以写入。 [ vivi/include/S3c2410.h] /* Interrupts */ 在这里先只讨论(详见S3C2410手册) 对INTMSK写0xffffffff 对INTSUBMSK写0x7ff 即可以屏蔽所有中断: |
@ initialise system clocks
mov r1, #CLK_CTL_BASE
mvn r2, #vLOCKTIME
str r2, [r1, #oLOCKTIME]
[ vivi/include/S3c2410.h] /* Clock and Power Management */ #define vLOCKTIME 0x00ffffff #define vCLKDIVN 0x3 /* FCLK:HCLK:PCLK = 1:2:4 */ /* 50.00 MHz */ 解释如下:
|
设置系统时钟s3c2410中第七章有详细说明 看了下基本认识如下(挑有用的看)主要是查看datasheet
CLOCK & POWER MANAGEMENT 1:时钟电源管理包含三部分: clock control, USB control, and power control.主要讨论clock control部分,关于power control等会还会涉及到。 FCLK for CPU(ARM920T) HCLK for the AHB bus peripherals(ARM920T, the memory controller, the interrupt controller, the PCLK for the APB bus peripherals.(看门狗, IIS, I2C, PWM timer, MMC interface, 【s3c2410只能支持AMBA 2 specification(AMBA是一种开放的ARM总线解决方案标准,为ARM广泛使用),这个版本的AMBA包含AMBA 2 AHB Interface、AMBA 2 APB Interface。也就是在S3C2410时钟电源模块中的两种总线,这两种总线所连的外设是有区别的。AHB总线连接高速外设,低速外设则通过APB总线互连。AHB总线对应Hclk,APB总线对应Pclk。如上分别连接的设备】 2:S3c2410通过PLL (Phase-Locked Loop) Block实现高频输出,它有两个PLL,MPLL用于(FCLK,HCLK,PCLK) ,UPLL用于usb block(减小电源的功率) 虽然MPLL在系统复位是默认开启的,但是在没写入值到MPLL寄存器中,. 那么外部晶振直接作为系统时钟,即使用户想直接用当前的时钟频率(12MHZ)也必须重新对MPLLCON写入控制值。 3:先看下面一段
1)简单的说是这样,在系统上电复位后,几个ms后晶振开始振荡起来,pll开始工作,但是由于PLL工作的不稳定性,在给MPLLCON赋值之前,cpu时钟等于FIN(12MHZ),这一部分都是硬件自己完成。开始执行代码。 2)只有在给PLLCON写入值,PLL才开始工作,在经过PLL Lock-time后PLL才能输出一个新的频率值。这一部分必须软件完成。这也是为什么即使就是MPLLCON有默认的值,也要重新输入值的原因。 软件设置过程具体参考上一个文字框 |
mov r1, #CLK_CTL_BASE
mov r2, #0x3
str r2, [r1, #oCLKDIVN]
mrc p15, 0, r1, c1, c0, 0 @ read ctrl register
orr r1, r1, #0xc0000000 @ Asynchronous
mcr p15, 0, r1, c1, c0, 0 @ write ctrl register
NOTES
1. CLKDIVN should be set carefully not to exceed the limit of HCLK and PCLK.
2. If HDIVN="1", the CPU bus mode has to be changed from the fast bus mode to the asynchronous bus
mode using following instructions.
MMU_SetAsyncBusMode
mrc p15,0,r0,c1,c0,0
orr r0,r0,#R1_nF:OR:R1_iA
mcr p15,0,r0,c1,c0,0
If HDIVN="1" and the CPU bus mode is the fast bus mode, the CPU will operate by the HCLK. This feature
can be used to change the CPU frequency as a half without affecting the HCLK and PCLK.
这段的基本意思是说假如HDIVN=1,那么CPU模式在从fast bus mode变化到异步模式需要执行一下以上三行代码~
这也相当与飞机的软着陆方式
bl memsetup
#ifdef CONFIG_PM
@ Check if this is a wake-up from sleep
Procedure to Wake-up from Power_OFF mode
(电源从POWER-OFF MODE中wake-up需做的事,在这里主要讨论如何设置SDRAM那部分,第2,3,4、5)
1. The internal reset signal will be asserted if one of the wake-up sources is issued. This reset duration is
determined by the internal 16-bit counter logic and the reset assertion time is calculated as tRST = (65535 /
XTAL_frequency).
2. Check GSTATUS2[2] in order to know whether or not the power-up is caused by the wake-up from
Power_OFF mode.
3. Release the SDRAM signal protection by setting MISCCR[19:17]=000b.
4. Configure the SDRAM memory controller.
5. Wait until the SDRAM self-refresh is released. Mostly SDRAM needs the refresh cycle of all SDRAM row.
6. The information in GSTATUS3,4 can be used for user’s own purpose because the value in GSTATUS3,4 has
been preserved during Power_OFF mode.
7. – For EINT[3:0], check the SRCPND register.
– For EINT[15:4], check the EINTPEND instead of SRCPND (SRCPND will not be set although some bits of
EINTPEND are set.).
– For alarm wake-up, check the RTC time because the RTC bit of SRCPND isn’t set at the alarm wake-up.
– If there was the nBATT_FLT assertion during POWER_OFF mode, the corresponding bit of SRCPND has
been set.
ldr r1, PMST_ADDR
ldr r0, [r1]
tst r0, #(PMST_SMR)
bne WakeupStart
2. Check GSTATUS2[1] in order to know whether or not the power-up is caused by the wake-up from
Power_OFF mode.
翻译出来就很明白这段代码在做什么。
【GENERAL STATUS REGISTER (GSTATUSn)
通用状态寄存器】
GSTATUS2 0x560000B4 —— Reset status
OFFRST [1] Power_OFF reset. The reset after the wakeup from Power_OFF mode.
The setting is cleared by writing "1" to this bit.
如果GSTATUS2[1]=1则表示SDRAM处于wake-up状态,不等则跳转到WakeupStart
head.S定义如下:
PMST_ADDR:
.long 0x560000B4
"vivi/include/S3c2410.h"中定义如下
#define PMST_WDR (1 << 2)
#endif
#ifdef CONFIG_S3C2410_SMDK
@ All LED on
mov r1, #GPIO_CTL_BASE
add r1, r1, #oGPIO_F
ldr r2,=0x55aa
str r2, [r1, #oGPIO_CON]
mov r2, #0xff
str r2, [r1, #oGPIO_UP]
mov r2, #0x00
str r2, [r1, #oGPIO_DAT]
配置GPIO口点亮所有开发板上面的LED,具体输出IO口根据具体的开发板配置
#endif
#if 0@这段代码没执行,代码所做的事是使处理器工作在管理模式,指令arm执行
@ SVC
mrs r0, cpsr
bic r0, r0, #0xdf
orr r1, r0, #0xd3
msr cpsr_all, r1
#endif
@ set GPIO for UART(这段代码可选)
mov r1, #GPIO_CTL_BASE
GPIO_CTL_BASE=0x56000000
add r1, r1, #oGPIO_H
oGPIO_H=0x70
这样r1=0x56000070
GPHCON 0x56000070 R/W Configure the pins of port H
ldr r2, gpio_con_uart
gpio_con_uart=vGPHCON=0x0016faaa
str r2, [r1, #oGPIO_CON]
oGPIO_CON=0x0 ,将r2的值载入到0x56000070
ldr r2, gpio_up_uart
gpio_up_uart=vGPHUP=0x000007ff
str r2, [r1, #oGPIO_UP]
oGPIO_UP=0x8,将0x7ff的值载入0x56000078
作用是使上拉电阻不起作用
bl InitUART
#ifdef CONFIG_DEBUG_LL
@ Print current Program Counter
ldr r1, SerBase
mov r0, #'\r'
bl PrintChar
mov r0, #'\n'
bl PrintChar
mov r0, #'@'
bl PrintChar
mov r0, pc
bl PrintHexWord
#endif
#ifdef CONFIG_BOOTUP_MEMTEST
@ simple memory test to find some DRAM flaults.
bl memtest
#endif
#ifdef CONFIG_S3C2410_NAND_BOOT
bl copy_myself
@ jump to ram
ldr r1, =on_the_ram
add pc, r1, #0
nop
nop
1: b 1b @ infinite loop
on_the_ram:
#endif
#ifdef CONFIG_DEBUG_LL
ldr r1, SerBase
ldr r0, STR_STACK
bl PrintWord
ldr r0, DW_STACK_START
bl PrintHexWord
#endif
@ get read to call C functions
ldr sp, DW_STACK_START @ setup stack pointer
mov fp, #0 @ no previous frame, so fp=0
mov a2, #0 @ set argv to NULL
bl main @ call main
mov pc, #FLASH_BASE @ otherwise, reboot
@
@ End VIVI head
@
/*
* subroutines
*/
@
@ Wake-up codes
@
#ifdef CONFIG_PM
WakeupStart:
@ Clear sleep reset bit
ldr r0, PMST_ADDR
mov r1, #PMST_SMR
str r1, [r0]
这段代码详见上面 Check if this is a wake-up from sleep
@ Release the SDRAM signal protections
ldr r0, PMCTL1_ADDR
ldr r1, [r0]
bic r1, r1, #(SCLKE | SCLK1 | SCLK0)
str r1, [r0]
3. Release the SDRAM signal protection by setting MISCCR[19:17]=000b.在下面
“Set the SDRAM singal protections”
有详细说明
@ Go...
ldr r0, PMSR0_ADDR @ read a return address
ldr r1, [r0]
mov pc, r1
“head.S”有如下定义
PMSR0_ADDR:
.long 0x560000B8
GSTATUS3 0x560000B8 Infrom register 0x0
INFORM [31:0] Inform register. This register is cleared by nRESET or watchdog timer.
Otherwise, preserve data value.
GSTATUS3寄存器中保存了return address?没看到????
当程序转移时,转移指令执行的最终结果就是要改变PC的值,此PC值就是转去的地址,以此实现转移
nop
nop
1: b 1b @ infinite loop
设置这个死循环的作用是,假如上面的指令没实现跳转,程序将在这永远执行,不会顺序执行到下面。
SleepRamProc:
@ SDRAM is in the self-refresh mode */
ldr r0, REFR_ADDR
在head.S后面有这样的定义
REFR_ADDR:
.long 0x48000024
REFRESH CONTROL REGISTER(SDRAM刷新控制寄存器)
SDRAM refresh control register
9. Let SDRAM enter the self-refresh mode by setting the REFRESH[22]=1b.
由上面这句话可知,只需设置REFRESH[22]=1b
[ vivi/include/S3c2410.h]
#define SELF_REFRESH (1 << 22)
ldr r1, [r0]
orr r1, r1, #SELF_REFRESH
使用orr语句赋值的原因是说只改变REFRESH的[22]这一位
str r1, [r0]
10. Wait until SDRAM self-refresh is effective.
11. Set MISCCR[19:17]=111b to make SDRAM signals(SCLK0,SCLK1 and SCKE) protected during Power_OFF
mode
12. Set the Power_OFF mode bit in the CLKCON register.
@ wait until SDRAM into self-refresh
mov r1, #16
1: subs r1, r1, #1
bne 1b
10. Wait until SDRAM self-refresh is effective.
设置延时程序使SDRAM的自我刷新有效。
解释下bne 1b
意思是上面减法r1的值不为0则跳转,跳转到程序向前标号最近为1的地方执行,若指令为bne 1f则为程序向后寻找跳转
@ Set the SDRAM singal protections
11. Set MISCCR[19:17]=111b to make SDRAM signals(SCLK0,SCLK1 and SCKE) protected during Power_OFF
mode
找出MISCCR寄存器(混杂控制寄存器):
MISCCR 0x56000080 Miscellaneous control register
Set MISCCR[19:17]=111b
nEN_SCKE [19] 0: SCKE = Normal 1:SCKE = L level
Used to protect SDRAM during the Power_OFF moe.
nEN_SCLK1 [18] 0: SCLK1= SCLK 1:SCLK1= L level
Used to protect SDRAM during the Power_OFF moe.
nEN_SCLK0 [17] 0: SCLK0= SCLK 1:SCLK0= L level
Used to protect SDRAM during the Power_OFF moe.
下面语句中
head.S中有定义如下:
PMCTL1_ADDR:
.long 0x56000080
[ vivi/include/S3c2410.h]中有如下定义:
#define SCLKE (1 << 19)
#define SCLK1 (1 << 18)
#define SCLK0 (1 << 17)
ldr r0, PMCTL1_ADDR
ldr r1, [r0]
orr r1, r1, #(SCLKE | SCLK1 | SCLK0)
str r1, [r0]
/* Sleep... Now */
12. Set the Power_OFF mode bit in the CLKCON register.
CLOCK CONTROL REGISTER (CLKCON)
CLKCON 0x4C00000C Clock generator control register
POWER-OFF [3]
Control Power Off mode of S3C2410.
0 = Disable, 1 = Transition to Power_OFF mode
0
head.S中有定义如下:
PMCTL0_ADDR:
.long 0x4c00000c
[ vivi/include/S3c2410.h]中有如下定义:
#define SLEEP_ON (1 << 3)
ldr r0, PMCTL0_ADDR
ldr r1, [r0]
orr r1, r1, #SLEEP_ON
str r1, [r0]
1: b 1b
上面这句代码,执行起来是一个无限循环,放在这的意思,实在迷惑。
做个大胆猜测,S3C2410进入POWER-OFF MODE,就在这跳转,直到
WAKE-UP的到来?
#ifdef CONFIG_TEST
hmi:
ldr r0, PMCTL0_ADDR
PMCTL0_ADDR=0x4C00000C
CLKCON 0x4C00000C R/W Clock generator control register 0x7FFF0
使用默认值
代码的具体功能?然道是测试所有设备工作状态
ldr r1, =0x7fff0
str r1, [r0]
@ All LED on
mov r1, #GPIO_CTL_BASE
add r1, r1, #oGPIO_F
ldr r2,=0x55aa
str r2, [r1, #oGPIO_CON]
mov r2, #0xff
str r2, [r1, #oGPIO_UP]
mov r2, #0xe0
str r2, [r1, #oGPIO_DAT]
1: b 1b
#endif
#endif
ENTRY(memsetup)
@ initialise the static memory
SDRAM初始化
@ set memory control registers
mov r1, #MEM_CTL_BASE
"vivi/include/S3c2410.h"
#define MEM_CTL_BASE 0x48000000
adrl r2, mem_cfg_val
add r3, r1, #52
1: ldr r4, [r2], #4
str r4, [r1], #4
cmp r1, r3
bne 1b
在SDRAM设置过程中,通过一个比较循环完成13个寄存器的配置,其写入的值如下:
"head.S"有如下定义:
@ Memory configuration values
.align 4
mem_cfg_val:
.long vBWSCON
.long vBANKCON0
.long vBANKCON1
.long vBANKCON2
.long vBANKCON3
.long vBANKCON4
.long vBANKCON5
.long vBANKCON6
.long vBANKCON7
.long vREFRESH
.long vBANKSIZE
.long vMRSRB6
.long vMRSRB7
"vivi/include/Smdk2410.h"
/* initial values for DRAM */
#define vBWSCON 0x22111110
#define vBANKCON0 0x00000700
#define vBANKCON1 0x00000700
#define vBANKCON2 0x00000700
#define vBANKCON3 0x00000700
#define vBANKCON4 0x00000700
#define vBANKCON5 0x00000700
#define vBANKCON6 0x00018005
#define vBANKCON7 0x00018005
#define vREFRESH 0x008e0459
#define vBANKSIZE 0xb2
#define vMRSRB6 0x30
#define vMRSRB7 0x30
先看S3C2410的第五部分有如下一段话:
The S3C2410X has the following features:
— Little/Big endian (selectable by a software)
— Address space: 128Mbytes per bank (total 1GB/8 banks)
— Programmable access size (8/16/32-bit) for all banks except bank0 (16/32-bit)
— Total 8 memory banks
Six memory banks for ROM, SRAM, etc.
Remaining two memory banks for ROM, SRAM, SDRAM, etc .
— Seven fixed memory bank start address
— One flexible memory bank start address and programmable bank size
从这段文字我们可以知道,S3C2410支持1GB的内存寻址,一共8个BANK,8*128MB=1GB,而且只有在bank6和bank7上才能接SRAM。
从开发板datasheet可知:
在板上用了两块HY57V561620BT-H SDRAM构成动态存储器
查看HY57V561620BT-H的datesheet可知
HY57V561620BT-H 4Banks x 4Mbits x16=256Mbite=32MB
两片为64MB够成32bit位宽可以查看其寻址范围为:
[0x30000000-0x33ffffff]
由于Bank 6 and 7 must have the same memory size
bank7的内存空间为
[0x34000000-0x37ffffff]
下面介绍具体的寄存器配置:
在配置的13个寄存器中:有些是默认值,只是为了写程序方便才配置如此多
BWSCON 0x48000000 Bus width & wait status control register
32位,8个bank,除了bank0只使用了【2:1】其他四位配置一个bank。
主要配置每个bank的位宽,以bank6为例,其为32位所以选择10
DW6 [25:24] Determine data bus width for bank 6.
00 = 8-bit 01 = 16-bit, 10 = 32-bit 11 = reserved
顾配置其值为0x22111110
BANK CONTROL REGISTER (BANKCONN: nGCS0-nGCS5)
for bank0-5 control,使用默认值就可
BANKCON0 0x48000004 R/W Bank 0 control register 0x0700
BANKCON1 0x48000008 R/W Bank 1 control register 0x0700
BANKCON2 0x4800000C R/W Bank 2 control register 0x0700
BANKCON3 0x48000010 R/W Bank 3 control register 0x0700
BANKCON4 0x48000014 R/W Bank 4 control register 0x0700
BANKCON5 0x48000018 R/W Bank 5 control register 0x0700
BANK CONTROL REGISTER (BANKCONn: nGCS6-nGCS7)
BANKCON6 0x4800001C R/W Bank 6 control register
BANKCON7 0x48000020 R/W Bank 7 control register
[16:15] Determine the memory type for bank6 and bank7
00 = ROM or SRAM 01 = Reserved (Do not use)
10 = Reserved (Do not use) 11 = Sync. DRAM
很显然这里选择11
[14:4]采用默认值:
Trcd [3:2] RAS to CAS delay
00 = 2 clocks 01 = 3 clocks 10 = 4 clocks
查看datesheet Trcd的-H指标为至少20ns,而HCLK的时钟周期为10ns,所以选01
SCAN [1:0] Column address number
00 = 8-bit 01 = 9-bit 10= 10-bit
Column Address : CA0 ~ CA8, 选01
所以写入该寄存器的值为0x18005
REFRESH 0x48000024 R/W SDRAM refresh control register
具体参考S3C2410的第五章REFRESH的datesheet
·REFEN[23]:开启自动模式,设为1
·TREFMD[22]:设为Auto refresh模式,设为0
·Trp[21:20]:看看RAS precharge Time,查看SDRAM手册,发现-H系列此参数至少为20ns,所以至少应该为2个clock。可以设为00
·Tsrc:也就是RAS Cycle Time,至少65ms,所以至少得6.5clock,按照可选值,应该设置为11
·Refresh[10:0]:
公式refresh period = (2^11 - refresh_count +1)/Hclk,由此推导出refresh_count=2^11+1-refresh period*Hclk。带入数值,计算得出2^11+1-7.8125u*100M=1268=0x04f4与0459有点误差但无大碍
·其余的保留值,均设置为0
所以写入该寄存器的值为0x8e04f4
mov pc, lr
lr=R14,pc=R15,在执行带链接的分支指令时,lr得到PC的拷贝,当程序返回了,把lr返回给PC
#ifdef CONFIG_S3C2410_NAND_BOOT
@
@ copy_myself: copy vivi to ram
@
copy_myself:
mov r10, lr
把程序计数器的值保存在r10中
@ reset NAND
因为前4KB自动引导到 S3C2410X 的内在SRAM buffer (“Steppingstone”)后 ,NAND FLASH 被自动禁用
提供S3C2410 NAND Flash的中文datesheet
文件:
S3C2410中文手册第六章.pdf
大小:
257KB
下载:
下载
mov r1, #NAND_CTL_BASE
"vivi/include/S3c2410.h"
/* NAND Flash Controller */
#define NAND_CTL_BASE 0x4E000000
#define oNFCONF 0x00
#define oNFCMD 0x04
#define oNFADDR 0x08
#define oNFDATA 0x0c
#define oNFSTAT 0x10
#define oNFECC 0x14
ldr r2, =0xf830 @ initial value
str r2, [r1, #oNFCONF]
将0xf830 装载到0x4E000000
NFCONF 0x4E000000 R/W NAND Flash configuration
Enable/Disable [15] 1=Enable NAND Flash Controller
Initialize ECC [12] 1 : Initialize ECC
NAND Flash Memory chip enable [11] NAND Flash Memory nFCE control
0 : NAND flash nFCE = L (active)
1 : NAND flash nFCE = H (inactive)
(After auto-boot, nFCE will be inactive.)
由TACLS=0, TWRPH0=1, TWRPH1=0
可设置位【10:8】=000,【6:4】=011,【2:0】=000
ldr r2, [r1, #oNFCONF]
bic r2, r2, #0x800
使NFCONF【11】=0 0:NAND flash nFCE = L 使能芯片
这里有个问题,为什么不在上面直接写0,而是在配置好了才使能芯片???
str r2, [r1, #oNFCONF]
mov r2, #0xff @ RESET command
strb r2, [r1, #oNFCMD]
将0xff 装载到0x4E000004 为什么写0xff,参考k9f1208的datesheet就明白
mov r3, #0 @ wait
1: add r3, r3, #0x1
cmp r3, #0xa
blt 1b
延时小程序,给NAND Flash准备时间,准备好
2: ldr r2, [r1, #oNFSTAT] @ wait ready
NFSTAT 0x4E000010 R NAND Flash operation status
只读寄存器,这段代买测试NAND Flash 运行状态,若其值为0x00则说明NAND Flash的状态还是busy
tst r2, #0x1
beq 2b
ldr r2, [r1, #oNFCONF]
orr r2, r2, #0x800 @ disable chip
str r2, [r1, #oNFCONF]
@ get read to call C functions (for nand_read())
ldr sp, DW_STACK_START @ setup stack pointer
mov fp, #0 @ no previous frame, so fp=0
@ copy vivi to RAM
ldr r0, =VIVI_RAM_BASE
mov r1, #0x0
mov r2, #0x20000
bl nand_read_ll
tst r0, #0x0
beq ok_nand_read
#ifdef CONFIG_DEBUG_LL
bad_nand_read:
ldr r0, STR_FAIL
ldr r1, SerBase
bl PrintWord
1: b 1b @ infinite loop
#endif
ok_nand_read:
#ifdef CONFIG_DEBUG_LL
ldr r0, STR_OK
ldr r1, SerBase
bl PrintWord
#endif
@ verify
mov r0, #0
ldr r1, =0x33f00000
mov r2, #0x400 @ 4 bytes * 1024 = 4K-bytes
go_next:
ldr r3, [r0], #4
ldr r4, [r1], #4
teq r3, r4
bne notmatch
subs r2, r2, #4
beq done_nand_read
bne go_next
notmatch:
#ifdef CONFIG_DEBUG_LL
sub r0, r0, #4
ldr r1, SerBase
bl PrintHexWord
ldr r0, STR_FAIL
ldr r1, SerBase
bl PrintWord
#endif
1: b 1b
done_nand_read:
#ifdef CONFIG_DEBUG_LL
ldr r0, STR_OK
ldr r1, SerBase
bl PrintWord
#endif
mov pc, r10
@ clear memory
@ r0: start address
@ r1: length
mem_clear:
mov r2, #0
mov r3, r2
mov r4, r2
mov r5, r2
mov r6, r2
mov r7, r2
mov r8, r2
mov r9, r2
clear_loop:
stmia {r2-r9}
subs r1, r1, #(8 * 4)
bne clear_loop
mov pc, lr
#endif @ CONFIG_S3C2410_NAND_BOOT
#ifdef CONFIG_BOOTUP_MEMTEST
@
@ Simple memory test function
@
为了确保所安排的地址范围的确是可读写的RAM空间,因此必须对所安排的地址范围进行测试
memtest:
mov r10, lr
#ifdef CONFIG_DEBUG_LL
mov r0, #'M'
ldr r1, SerBase
bl PrintChar
mov r0, #'T'
ldr r1, SerBase
bl PrintChar
mov r0, #'S'
ldr r1, SerBase
bl PrintChar
mov r0, #'T'
ldr r1, SerBase
bl PrintChar
mov r0, #' '
ldr r1, SerBase
bl PrintChar
#endif
/* check the first 1MB in increments of 4k */
mov r7, #0x1000
mov r6, r7, lsl #8 /* 4k << 2^8 = 1MB */
mov r5, #DRAM_BASE
mem_test_loop:
mov r0, r5
bl testram_nostack
teq r0, #1
beq badram
add r5, r5, r7
subs r6, r6, r7
bne mem_test_loop
@ the first megabyte is OK. so let us clear it.
mov r0, #((1024 * 1024) / (8 * 4)) @ 1MB in steps of 32 bytes
mov r1, #DRAM_BASE
mov r2, #0
mov r3, #0
mov r4, #0
mov r5, #0
mov r6, #0
mov r7, #0
mov r8, #0
mov r9, #0
clear_loop_memtest:
stmia {r2-r9}
subs r0, r0, #(8 * 4)
bne clear_loop_memtest
#ifdef CONFIG_DEBUG_LL
ldr r0, STR_OK
ldr r1, SerBase
bl PrintWord
#endif
mov pc, r10 @ return
badram:
#ifdef CONFIG_DEBUG_LL
ldr r0, STR_FAIL
ldr r1, SerBase
bl PrintWord
#endif
1: b 1b @ loop
@ testmem.S: memory tester, test if there is RAM available at given location
@
@ Copyright (C) 2001 Russell King (rmk@arm.linux.org.uk)
@
@ This version clobbers registers r1-r4, so be sure to store their contents
@ in a safe position. This function is not APCS compliant, so only use it
@ from assembly code.
@
@ r0 = address to test
@ returns r0 = 0 - ram present, r0 = 1 - no ram
@ clobbers r1 - r4
ENTRY(testram_nostack)
ldmia r0, {r1, r2} @ store current value in r1 and r2
mov r3, #0x55 @ write 0x55 to first word
mov r4, #0xaa @ 0xaa to second
stmia r0, {r3, r4}
ldmia r0, {r3, r4} @ read it back
teq r3, #0x55 @ do the values match
teqeq r4, #0xaa
bne bad @ oops, no
mov r3, #0xaa @ write 0xaa to first word
mov r4, #0x55 @ 0x55 to second
stmia r0, {r3, r4}
ldmia r0, {r3, r4} @ read it back
teq r3, #0xaa @ do the values match
teqeq r4, #0x55
bad: stmia r0, {r1, r2} @ in any case, restore old data
moveq r0, #0 @ ok - all values matched
movne r0, #1 @ no ram at this location
mov pc, lr
#endif @ CONFIG_BOOTUP_MEMTEST
@ Initialize UART @初始化串口
@
@ r0 = number of UART port
InitUART:
#define oULCON 0x00 /* R/W, UART line control register */
#define oUCON 0x04 /* R/W, UART control register */
#define oUFCON 0x08 /* R/W, UART FIFO control register */
#define oUMCON 0x0C /* R/W, UART modem control register */
#define oUTRSTAT 0x10 /* R , UART Tx/Rx status register */
#define oUERSTAT 0x14 /* R , UART Rx error status register */
#define oUFSTAT 0x18 /* R , UART FIFO status register */
#define oUMSTAT 0x1C /* R , UART Modem status register */
#define oUTXHL 0x20 /* W, UART transmit(little-end) buffer */
#define oUTXHB 0x23 /* W, UART transmit(big-end) buffer */
#define oURXHL 0x24 /* R , UART receive(little-end) buffer */
#define oURXHB 0x27 /* R , UART receive(big-end) buffer */
#define oUBRDIV 0x28 /* R/W, Baud rate divisor register */
ldr r1, SerBase
mov r2, #0x0
str r2, [r1, #oUFCON]
str r2, [r1, #oUMCON]
mov r2, #0x3
str r2, [r1, #oULCON]
ldr r2, =0x245
str r2, [r1, #oUCON]
#define UART_BRD ((50000000 / (UART_BAUD_RATE * 16)) - 1)
mov r2, #UART_BRD
str r2, [r1, #oUBRDIV]
mov r3, #100
mov r2, #0x0
1: sub r3, r3, #0x1
tst r2, r3
bne 1b
#if 0
mov r2, #'U'
str r2, [r1, #oUTXHL]
1: ldr r3, [r1, #oUTRSTAT]
and r3, r3, #UTRSTAT_TX_EMPTY
tst r3, #UTRSTAT_TX_EMPTY
bne 1b
mov r2, #'0'
str r2, [r1, #oUTXHL]
1: ldr r3, [r1, #oUTRSTAT]
and r3, r3, #UTRSTAT_TX_EMPTY
tst r3, #UTRSTAT_TX_EMPTY
bne 1b
#endif
mov pc, lr
@
@ Exception handling functions
@
HandleUndef:
#ifdef CONFIG_DEBUG_LL
mov r12, r14
ldr r0, STR_UNDEF
ldr r1, SerBase
bl PrintWord
bl PrintFaultAddr
#endif
1: b 1b @ infinite loop
HandleSWI:
#ifdef CONFIG_DEBUG_LL
mov r12, r14
ldr r0, STR_SWI
ldr r1, SerBase
bl PrintWord
bl PrintFaultAddr
#endif
1: b 1b @ infinite loop
HandlePrefetchAbort:
#ifdef CONFIG_DEBUG_LL
mov r12, r14
ldr r0, STR_PREFETCH_ABORT
ldr r1, SerBase
bl PrintWord
bl PrintFaultAddr
#endif
1: b 1b @ infinite loop
HandleDataAbort:
#ifdef CONFIG_DEBUG_LL
mov r12, r14
ldr r0, STR_DATA_ABORT
ldr r1, SerBase
bl PrintWord
bl PrintFaultAddr
#endif
1: b 1b @ infinite loop
HandleIRQ:
#ifdef CONFIG_DEBUG_LL
mov r12, r14
ldr r0, STR_IRQ
ldr r1, SerBase
bl PrintWord
bl PrintFaultAddr
#endif
1: b 1b @ infinite loop
HandleFIQ:
#ifdef CONFIG_DEBUG_LL
mov r12, r14
ldr r0, STR_FIQ
ldr r1, SerBase
bl PrintWord
bl PrintFaultAddr
#endif
1: b 1b @ infinite loop
HandleNotUsed:
#ifdef CONFIG_DEBUG_LL
mov r12, r14
ldr r0, STR_NOT_USED
ldr r1, SerBase
bl PrintWord
bl PrintFaultAddr
#endif
1: b 1b @ infinite loop
@
@ Low Level Debug
@
#ifdef CONFIG_DEBUG_LL
@
@ PrintFaultAddr: Print falut address
@
@ r12: contains address of instruction + 4
@
PrintFaultAddr:
mov r0, r12 @ Print address of instruction + 4
ldr r1, SerBase
bl PrintHexWord
mrc p15, 0, r0, c6, c0, 0 @ Read fault virtual address
ldr r1, SerBase
bl PrintHexWord
mov pc, lr
@ PrintHexNibble : prints the least-significant nibble in R0 as a
@ hex digit
@ r0 contains nibble to write as Hex
@ r1 contains base of serial port
@ writes ro with XXX, modifies r0,r1,r2
@ TODO : write ro with XXX reg to error handling
@ Falls through to PrintChar
PrintHexNibble:
adr r2, HEX_TO_ASCII_TABLE
and r0, r0, #0xF
ldr r0, [r2, r0] @ convert to ascii
b PrintChar
@ PrintChar : prints the character in R0
@ r0 contains the character
@ r1 contains base of serial port
@ writes ro with XXX, modifies r0,r1,r2
@ TODO : write ro with XXX reg to error handling
PrintChar:
TXBusy:
ldr r2, [r1, #oUTRSTAT]
and r2, r2, #UTRSTAT_TX_EMPTY
tst r2, #UTRSTAT_TX_EMPTY
beq TXBusy
str r0, [r1, #oUTXHL]
mov pc, lr
@ PrintWord : prints the 4 characters in R0
@ r0 contains the binary word
@ r1 contains the base of the serial port
@ writes ro with XXX, modifies r0,r1,r2
@ TODO : write ro with XXX reg to error handling
PrintWord:
mov r3, r0
mov r4, lr
bl PrintChar
mov r0, r3, LSR #8 /* shift word right 8 bits */
bl PrintChar
mov r0, r3, LSR #16 /* shift word right 16 bits */
bl PrintChar
mov r0, r3, LSR #24 /* shift word right 24 bits */
bl PrintChar
mov r0, #'\r'
bl PrintChar
mov r0, #'\n'
bl PrintChar
mov pc, r4
@ PrintHexWord : prints the 4 bytes in R0 as 8 hex ascii characters
@ followed by a newline
@ r0 contains the binary word
@ r1 contains the base of the serial port
@ writes ro with XXX, modifies r0,r1,r2
@ TODO : write ro with XXX reg to error handling
PrintHexWord:
mov r4, lr
mov r3, r0
mov r0, r3, LSR #28
bl PrintHexNibble
mov r0, r3, LSR #24
bl PrintHexNibble
mov r0, r3, LSR #20
bl PrintHexNibble
mov r0, r3, LSR #16
bl PrintHexNibble
mov r0, r3, LSR #12
bl PrintHexNibble
mov r0, r3, LSR #8
bl PrintHexNibble
mov r0, r3, LSR #4
bl PrintHexNibble
mov r0, r3
bl PrintHexNibble
mov r0, #'\r'
bl PrintChar
mov r0, #'\n'
bl PrintChar
mov pc, r4
#endif
@
@ Data Area
@
@ Memory configuration values
.align 4
mem_cfg_val:
.long vBWSCON
.long vBANKCON0
.long vBANKCON1
.long vBANKCON2
.long vBANKCON3
.long vBANKCON4
.long vBANKCON5
.long vBANKCON6
.long vBANKCON7
.long vREFRESH
.long vBANKSIZE
.long vMRSRB6
.long vMRSRB7
@ Processor clock values
.align 4
clock_locktime:
.long vLOCKTIME
mpll_50mhz:
.long vMPLLCON_50
#ifdef CONFIG_S3C2410_MPORT1
mpll_100mhz:
.long vMPLLCON_100
#endif
mpll_200mhz:
.long vMPLLCON_200
clock_clkcon:
.long vCLKCON
clock_clkdivn:
.long vCLKDIVN
@ initial values for serial
uart_ulcon:
.long vULCON
uart_ucon:
.long vUCON
uart_ufcon:
.long vUFCON
uart_umcon:
.long vUMCON
@ inital values for GPIO
gpio_con_uart:
.long vGPHCON
gpio_up_uart:
.long vGPHUP
.align 2
DW_STACK_START:
.word STACK_BASE+STACK_SIZE-4
#ifdef CONFIG_DEBUG_LL
.align 2
HEX_TO_ASCII_TABLE:
.ascii "0123456789ABCDEF"
STR_STACK:
.ascii "STKP"
STR_UNDEF:
.ascii "UNDF"
STR_SWI:
.ascii "SWI "
STR_PREFETCH_ABORT:
.ascii "PABT"
STR_DATA_ABORT:
.ascii "DABT"
STR_IRQ:
.ascii "IRQ "
STR_FIQ:
.ascii "FIQ"
STR_NOT_USED:
.ascii "NUSD"
.align 2
STR_OK:
.ascii "OK "
STR_FAIL:
.ascii "FAIL"
STR_CR:
.ascii "\r\n"
#endif
.align 4
SerBase:
#if defined(CONFIG_SERIAL_UART0)
.long UART0_CTL_BASE
#elif defined(CONFIG_SERIAL_UART1)
.long UART1_CTL_BASE
#elif defined(CONFIG_SERIAL_UART2)
.long UART2_CTL_BASE
#else
#error not defined base address of serial
#endif
#ifdef CONFIG_PM
.align 4
PMCTL0_ADDR:
.long 0x4c00000c
PMCTL1_ADDR:
.long 0x56000080
PMST_ADDR:
.long 0x560000B4
PMSR0_ADDR:
.long 0x560000B8
REFR_ADDR:
.long 0x48000024
#endif
文章评论(0条评论)
登录后参与讨论