资源描述
S3c2440启动是从文件vivi/arch/s3c2440/head.S开始的
1. 在这个文件中,做了这样的工作:
(1) 关闭看门狗
(2) 禁止所有中断
(3) 初始化system clock
(4) 初始化串口
(5) 初始化nand flash相关的控制寄存器
(6) 把vivi copy 到 SDRAM上,最后把pc指向ram
(7) 设置堆栈
(8) call 一个叫做main的函数。这个函数是c写的
2. main()这个函数在vivi/init/main.c中
(1) 程序正常跳转到这里后,首先打印一些进本信息
(2) 调用若干个初始化的函数
<1> reset_handler() vivi/lib/reset_handler.c中
(主要做了clear memory的工作)
<2> board_init() (没找到)
<3> mem_map_init() vivi/arch/mmu.c中
(如果使用nand flash,会在这里使它看起来线性化;清空cache;invalidate I & D tlb)
mmu_init vivi/arch/mmu.c中
(初始化cache,load页表指针,mmu 使能等等)
<4> heap_init 初始化堆 vivi/lib/heap.c
(这里好像malloc了一段空间,具体的没看懂)
<5> mtd_dev_init vivi/driver/mtd/mtdcore.c中
mtd_init() 在vivi/driver/mtd/maps/s3c2440_flash.c 中,这里根据所使用的flash,做相应的cpu控制初始化,分配内存
add_command(&flash_cmd) 注册了flash这个用户命令
<6> init_priv_data() vivi/lib/priv_data/rw.c中
读出boot的基本参数
<7> misc() (没找到)
init_builtin_cmds() vivi/lib/command.c中
(注册用户的基本命令,包括boot, bon, load, help 等等)
<8> boot_or_vivi() 就在main.c中,等待用户输入,有输入进入vivi_shell,没有输入,超时后启动linux
到这里main就结束喽
好像还有一件事没有记下来,就是vivi是怎么启动到linux的。这是vivi执行了boot命令,调用了 vivi/lib/boot_kernel.c 中的command_boot()函数。这样linux就启动了。
S3C2440启动过程兼示例代码分析
才开始学嵌入式没多久,用得友善之臂的2440板子,前段时间一直在搞linux系统,软件移植,内核裁剪等,最近发现还是应该从最基本的学起,想自己写一个bootloader,然后就研究了友善之臂那个板子上光盘里面配的一个示例程序,哎,搞了一天多才研究出来一点点东西,贴上来,免得忘了!
S3C2440启动过程(约略):
1、关闭watch dog
2、屏蔽所有中断
3、屏蔽所有子中断
4、设置时钟更改稳定等待时间(LOCKTIME)
5、设置各部件时钟频率与CPU主频的比例关系(Fclk:Hclk:Pclk)
6、设置USB总线时钟频率(UPLLCON)
7、nap * 7
8、设置主时钟(MPLLCON)
9、判断是否是从睡眠状态唤醒的,如果是,则做出相应处理(具体处理还没看)
10、初始化存储设备、包括外存,内存等(BWSCON,BANKCONn,BANKSIZE,MRSRB6,MRSRB7)
11、初始化处理器各模式的堆栈(还没看懂)
12、Setup IRQ handler(也没咋看懂,似乎是对IRQ终端进行处理)
13、进入C程序(bl Main)
代码分析:
;=========================================
; NAME: 2440INIT.S
; DESC: C start up codes
; Configure memory, ISR ,stacks
; Initialize C-variables
; HISTORY:
; 2002.02.25:kwtark: ver 0.0
; 2002.03.20:purnnamu: Add some functions for testing STOP,Sleep mode
; 2003.03.14:DonGo: Modified for 2440.
; 2009.02.19:PuHao: Comments
;=========================================
;=========================
; ENTRY 这里是关键的入口,正文数量超过最大限制,只有忽略其他我没看懂的了,等下能不能把那个文件传上来,好恶心的规定……
;=========================
ResetHandler
;WTCON定义在2440addr.inc里面,WTCON EQU 0x53000000
;把WTCON地址放到R0里,然后在设置r1为0,最后把r1中的0值
;拷贝到r0所指向的地址里面,即禁用watch dog
ldr r0,=WTCON ;watch dog disable
ldr r1,=0x0
str r1,[r0]
;基本同上,INTMSK 0x4A000008 R/W Interrupt mask control
ldr r0,=INTMSK
ldr r1,=0xffffffff ;all interrupt disable
str r1,[r0]
;基本同上
ldr r0,=INTSUBMSK
ldr r1,=0x3ff ;all sub interrupt disable,0x3ff => 0000 0011 1111 1111
str r1,[r0]
;由于条件为FALSE,我觉得这段根本就不会执行
[ {FALSE}
; rGPFDAT = (rGPFDAT & ~(0xf<<4)) | ((~data & 0xf)<<4);
; Led_Display
ldr r0,=GPFCON
ldr r1,=0x5500
str r1,[r0]
ldr r0,=GPFDAT
ldr r1,=0x10
str r1,[r0]
]
;由于在更改了主时钟控制器(MPLL)之后,新的频率需要一定时间过后才能稳定
;所以需要等待一段时间,而这个等待的时间就是使用LOCKTIME寄存器来设置的
;LOCKTIME[15:00] MPLL lock time count value for FCLK, HCLK, and PCLK
;LOCKTIME[32:16] UPLL lock time count value for UCLK.
; FCLK用于CPU核
; HCLK用于AHB总线的设备(比如SDRAM)
; PCLK用于APB总线的设备(比如UART)
; UPLL为USB的控制时钟,根据USB规范,好像是48MHz
; ARM920T内核使用FCLK
; 内存控制器,LCD控制器等使用HCLK;
; 看门狗、串口等使用PCLK
;To reduce PLL lock time, adjust the LOCKTIME register.
ldr r0,=LOCKTIME
ldr r1,=0xffffff ;0000 0000 1111 1111 1111 1111 1111 1111
str r1,[r0] ;为什么UPLL的等待时间要少一些呢?
[ PLL_ON_START
; Added for confirm clock divide. for 2440.
; Setting value Fclk:Hclk:Pclk
; 这里设置的 Fclk:Hclk:Pclk = 1:3:6,因为 CLKDIV_VAL = 7
ldr r0,=CLKDIVN
ldr r1,=CLKDIV_VAL ; 0=1:1:1, 1=1:1:2, 2=1:2:2, 3=1:2:4, 4=1:4:4, 5=1:4:8, 6=1:3:3, 7=1:3:6.
str r1,[r0]
;UPLL和MPLL的控制参数
;PLLCON Bit Description Initial State
;MDIV [19:12] Main divider control 0x96 / 0x4d
;PDIV [9:4] Pre-divider control 0x03 / 0x03
;SDIV [1:0] Post divider control 0x0 / 0x0
;InputFrequency OutputFrequency MDIV PDIV SDIV
;12.0000MHz 48.00 MHz(Note) 56(0x38) 2 2
;12.0000MHz 96.00 MHz(Note) 56(0x38) 2 1
;12.0000MHz 271.50 MHz 173(0xad) 2 2
;12.0000MHz 304.00 MHz 68(0x44) 1 1
;12.0000MHz 405.00 MHz 127(0x7f) 2 1
;12.0000MHz 532.00 MHz 125(0x7d) 1 1
;Configure UPLL
; 根据手册,要对PLL进行设置的话,就需要先设置UPLLCON,再设置MPLLCON
; 并且中间需要有至少七个指令周期的延时
; (Needs intervals approximately 7 NOP)
; U_MDIV EQU 56 ;Fin=12.0MHz Fout=48MHz
; U_PDIV EQU 2
; U_SDIV EQU 2
ldr r0,=UPLLCON
ldr r1,=((U_MDIV<<12)+(U_PDIV<<4)+U_SDIV)
str r1,[r0]
;(Needs intervals approximately 7 NOP)
nop ; Caution: After UPLL setting, at least 7-clocks delay must be inserted for setting hardware be completed.
nop
nop
nop
nop
nop
nop
;Configure MPLL
; M_MDIV EQU 68 ; 68 = 0x44 Fin=12.0MHz Fout=304.8MHz
; M_PDIV EQU 1
; M_SDIV EQU 1 ; 2440A
; 设为(0x44 << 12)|(0x01 << 4)|(0x01),即0x00044011
; Fclk:Hclk:Pclk = 1:3:6 => Fclk = 300MHz, Hclk = 100MHz, Pclk = 50MHz
;
ldr r0,=MPLLCON
ldr r1,=((M_MDIV<<12)+(M_PDIV<<4)+M_SDIV)
str r1,[r0]
]
;Check if the boot is caused by the wake-up from SLEEP mode.
; GSTATUS2 Bit Description
; Reserved [3] Reserved
; WDTRST [2] Boot is caused by Watch Dog Reset cleared by writing “1”
; SLEEPRST [1] Boot is caused by wakeup reset in sleep mode cleared by writing “1”.
; PWRST [0] Boot is caused by power on reset cleared by writing “1”
ldr r1,=GSTATUS2
ldr r0,[r1]
tst r0,#0x2
;In case of the wake-up from SLEEP mode, go to SLEEP_WAKEUP handler.
bne WAKEUP_SLEEP
;EXPORT伪指令声明一个全局标号
EXPORT StartPointAfterSleepWakeUp
StartPointAfterSleepWakeUp
;Set memory control registers
ldr r0,=SMRDATA
ldr r1,=BWSCON ;BWSCON Address
add r2, r0, #52 ;End address of SMRDATA
;r2 = r0 + 52
; beq %F[ ]中,b为跳转指令,eq为相等条件标识,整条语句的意思就是
; 如果相等,则在此条语句后面的代码中搜索[ ]标号并跳转。
; 同样的 bcc %B[ ] 其中cc为无符号数小于,%B在此条语句前面的代码中搜索并跳转
;下面是一个循环
;首先把r0指向的地址单元的一个字(4字节)的内容复制到r3中,
;然后r0+4,这样的话下次取的值将会是SMRDATA中的下一个个字
;
;将r3中的值写入到r1的地址单元(BWSCON)中,并将r1+1,即
;下一次将会写入到BWSCON的下一个控制器
;控制器以此为:BWSCON - BANKCON0 - BANKCON1 - BANKCON2 ……BANKCON7
; BANKSIZE - MRSRB6 - MRSRB7
0
ldr r3, [r0], #4
str r3, [r1], #4
cmp r2, r0 ;CPSR = r2 - r0
bne %B0 ;不等于0时跳转,也就是说一直循环直到将上面所说的控制器全部
;设置了值之后才继续执行下一步
;Initialize stacks
bl InitStacks
; Setup IRQ handler
ldr r0,=HandleIRQ ;This routine is needed
ldr r1,=IsrIRQ ;if there isn't 'subs pc,lr,#4' at 0x18, 0x1c
str r1,[r0]
;If main() is used, the variable initialization will be done in __main().
[ :LNOT:USE_MAIN
;Copy and paste RW data/zero initialized data
LDR r0, =|Image$$RO$$Limit| ; Get pointer to ROM data
LDR r1, =|Image$$RW$$Base| ; and RAM copy
LDR r3, =|Image$$ZI$$Base|
;Zero init base => top of initialised data
CMP r0, r1 ; Check that they are different
BEQ %F2
1
CMP r1, r3 ; Copy init data
LDRCC r2, [r0], #4 ;--> LDRCC r2, [r0] + ADD r0, r0, #4
STRCC r2, [r1], #4 ;--> STRCC r2, [r1] + ADD r1, r1, #4
BCC %B1
2
LDR r1, =|Image$$ZI$$Limit| ; Top of zero init segment
MOV r2, #0
3
CMP r3, r1 ; Zero init
STRCC r2, [r3], #4
BCC %B3
]
[ :LNOT:THUMBCODE
bl Main ;Don't use main() because ......because what?
b .
]
[ THUMBCODE ;for start-up code for Thumb mode
orr lr,pc,#1
bx lr
CODE16
bl Main ;Don't use main() because ......because what?
b .
CODE32
]
;function initializing stacks
;初始化堆栈的代码还没仔细研究,正文字数超过最大限制,只有不贴下面的了……
一:地址空间的分配
1:s3c2440是32位的,所以可以寻址4GB空间,内存(SDRAM)和端口(特殊寄存器),还有ROM都映射到同一个4G空间里.
2:开发板上一般都用SDRAM做内存flash(nor、nand)来当做ROM。其中nand flash没有地址线,一次至少要读一页(512B).其他两个有地址线
3:norflash不用来运行代码,只用来存储代码,NORflash,SDRAM可以直接运行代码)
4:s3c2440总共有8个内存banks
6个内存bank可以当作ROM或者SRAM来使用
留下的2个bank除了当作ROM 或者SRAM,还可以用SDRAM(各种内存的读写方式不一样)
7个bank的起始地址是固定的
还有一个灵活的bank的内存地址,并且bank大小也可以改变
5:s3c2440支持两种启动模式:NAND和非NAND(这里是nor flash)。
具体采用的方式取决于OM0、OM1两个引脚
OM[1:0所决定的启动方式
OM[1:0]=00时,处理器从NAND Flash启动
OM[1:0]=01时,处理器从16位宽度的ROM启动
OM[1:0]=10时,处理器从32位宽度的ROM启动。
OM[1:0]=11时,处理器从Test Mode启动。
当从NAND启动时
cpu会自动从NAND flash中读取前4KB的数据放置在片内SRAM里(s3c2440是soc),同时把这段片内SRAM映射到nGCS0片选的空间(即0x00000000)。cpu是从0x00000000开始执行,也就是NAND flash里的前4KB内容。因为NAND FLASH连地址线都没有,不能直接把NAND映射到0x00000000,只好使用片内SRAM做一个载体。通过这个载体把nandflash中大代码复制到RAM(一般是SDRAM)中去执行
当从非NAND flash启动时
nor flash被映射到0x00000000地址(就是nGCS0,这里就不需要片内SRAM来辅助了,所以片内SRAM的起始地址还是0x40000000). 然后cpu从0x00000000开始执行(也就是在Norfalsh中执行)。
总结:
Arm的启动都是从0地址开始,所不同的是地址的映射不一样。在arm开电的时候,要想让arm知道以某种方式(地址映射方式)运行,不可能通过你写的某段程序控制,因为这时候你的程序还没启动,这时候arm会通过引脚的电平来判断。
1当引脚OM0跟OM1有一个是高电平时,这时地址0会映射到外部nGCS0片选的空间,也就是Norflash,程序就会从Norflash中启动,arm直接取Norflash中的指令运行。
2当OM0跟OM1都为低电平,则0地址内部bootbuf(一段4k的SRAM)开始。系统上电,arm会自动把NANDflash中的前4K内容考到bootbuf(也就是0地址),然后从0地址运行。
这时NANDFlash中的前4K就是启动代码(他的功能就是初始化硬件然后在把NANDFlash中的代码复制到RAM中,再把相应的指针指向该运行的地方)
为什么会有这两种启动方式,关键还是两种flash的不同特点造成,NOR FLASH容量小,速度快,稳定性好,输入地址,然后给出读写信号即可从数据口得到数据,适合做程序存储器。NAND FLASH 总容量大,但是读写都需要复杂的时序,更适合做数据存储器。这种不同就造成了NORflash可以直接连接到arm的总线并且可以运行程序,而NANDflash必须搬移到内存(SDRAM)中运行。
在实际的开发中,一般可以把bootloader烧入到Norflash,程序运行可以通过串口交互,进行一定的操作,比如下载,调试。这样就很可以很方便的调试你的一些代码。Norflash中的Bootloader还可以烧录内核到Norflash等等功能。
我也在用这块板子.
说下我的看法吧.
主流的s3c2440A处理器,arm9内核
接口齐全,文档也比较丰富.利用板载资源可以学习不少东西.完整的嵌入式wince和linux都可以开发.还可以移植其它系统,当然这方面用的就少了.
目前来看是做学习板较好的厂商.售后没接触过,因为我还没遇到什么问题需要联系售后的.
有专门的交流论坛.
烧程序需要串口,jtag调试需要并口.一般笔记本都不带这些接口了.所以需要买个转接的,usb转串口的统统不好用,我试过N多的了,不好用.买个了华塑pc卡转串口的,可卡好像有毛病,有时超级终端输不进去.也许我买到假货了.后来我买了个OpenJtag,这个东西可以转串口,可以转Jtag,烧写速度也快.但是我不习惯在Eclipse下搞.所以只把它当串口用了.
有点扯远了,友善的板子不错.性价比高.推荐.
micro2440和mini2440相比,micro是做的核心版和底板是分开的,所以用jtag调试时可以不用底板,micro多了点硬件资源,串口也多了.其它没什么区别.
性价比高的是mini2440,不用在这上面过多考虑了.用mini的人很多.
不管怎样,选个开发板后好好研究下,肯定有成果的.
展开阅读全文