vivi boot loader的实现
参考资料:
1. 嵌入式系统 Boot Loader 技术内幕, 詹荣开 (zhanrk@sohu.com)
2. Getting started with VIVI, Janhoon Lyu, nandy@mizi.com
3. 嵌入式设备上的 Linux 系统开发,A. Santhanam etc.
4. Linux system development on an embedded device, A. Santhanam
5. vivi 有关资料 http://www.mizi.com/developer/s3c2410x/index.html
6. smdk2410的硬件和软件/linux相关资料 http://www.samsung.com search 2410
:本文文字结构照抄” 嵌入式系统 Boot Loader 技术内幕, 詹荣开
(zhanrk@sohu.com)” 一文,以vivi中head.S作为stage1, main()作为stage2,解释了
VIVI for SMDK2410 (based on S3C2410) 开发系统的bootloader的实现。将原文放在
这里是为了方便读者。注意,VIVI的实现并非完全跟原文一致。多谢原文作者詹
大侠的详细解释。
附录有一节__SETUP在 kernel的作用来自 jeppeter (member) from http://
linuxforum.net
文中对MTD subsystem linux没作解释。Google “MTD linux subsystem 文件系统
JFSS2 ”可以获得足够的解释。
如有错误,烦请email jonesxu@gmail.com告知。多些
Ver.0.95
Jones S Z Xu
jonesxu@gmail.com
2004-09-29
Chapter 1 Boot loader基本结构
由于 Boot Loader 的实现依赖于 CPU 的体系结构,因此大多数 Boot Loader 都分为
stage1 和 stage2 两大部分。依赖于 CPU 体系结构的代码,比如设备初始化代码等,通常都放在
stage1 中,而且通常都用汇编语言来实现,以达到短小精悍的目的。而 stage2 则通常用 C语言来
实现,这样可以实现给复杂的功能,而且代码会具有更好的可读性和可移植性。
Boot Loader 的 stage1 通常包括以下步骤(以执行的先后顺序):
硬件设备初始化。
为加载 Boot Loader 的 stage2 准备 RAM 空间。
拷贝 Boot Loader 的 stage2 到 RAM 空间中。
设置好堆栈。
跳转到 stage2 的 C 入口点。
Boot Loader 的 stage2 通常包括以下步骤(以执行的先后顺序):
初始化本阶段要使用到的硬件设备。
检测系统内存映射(memory map)。
将 kernel 映像和根文件系统映像从 flash 上读到 RAM 空间中。
为内核设置启动参数。
调用内核。
1.1 Boot Loader 的 stage1
1.1.1 基本的硬件初始化
这是 Boot Loader 一开始就执行的操作,其目的是为 stage2 的执行以及随后的 kernel 的执行准备
好一些基本的硬件环境。它通常包括以下步骤(以执行的先后顺序):
1. 屏蔽所有的中断。为中断提供服务通常是 OS 设备驱动程序的责任,因此在 Boot Loader 的执
行全过程中可以不必响应任何中断。中断屏蔽可以通过写 CPU 的中断屏蔽寄存器或状态寄存器
(比如 ARM 的 CPSR 寄存器)来完成。
2. 设置 CPU 的速度和时钟频率。
3. RAM 初始化。包括正确地设置系统的内存控制器的功能寄存器以及各内存库控制寄存器等。
4. 初始化 LED。典型地,通过 GPIO 来驱动 LED,其目的是
明系统的状态是 OK 还是 Error。
如果板子上没有 LED,那么也可以通过初始化 UART 向串口打印 Boot Loader 的 Logo 字符信息
来完成这一点。
5. 关闭 CPU 内部指令/数据 cache。
VIVI在第一阶段完成以下任务
Disable watch dog timer ; disable all interrupts ;
initialise system clocks; initialise the static memory All LED on
set GPIO for UART Initialize UART 0 ;
copy_myself to ram; jump to ram
get read to call C functions setup stack pointer
call main
1.1.2 为加载 stage2 准备 RAM 空间
为了获得更快的执行速度,通常把 stage2 加载到 RAM 空间中来执行,因此必须为加载 Boot
Loader 的 stage2 准备好一段可用的 RAM 空间范围。
由于 stage2 通常是 C 语言执行代码,因此在考虑空间大小时,除了 stage2 可执行映象的大小
外,还必须把堆栈空间也考虑进来。此外,空间大小最好是 memory page 大小(通常是 4KB)的倍
数。一般而言,1M 的 RAM 空间已经足够了。具体的地址范围可以任意安排,比如 blob 就将它的
stage2 可执行映像安排到从系统 RAM 起始地址 0xc0200000 开始的 1M 空间内执行。但是,将
stage2 安排到整个 RAM 空间的最顶 1MB(也即(RamEnd-1MB) - RamEnd)是一种值得推荐的方
法。
为了后面的叙述方便,这里把所安排的 RAM 空间范围的大小记为:stage2_size(字节),把起始地
址和终止地址分别记为:stage2_start 和 stage2_end(这两个地址均以 4 字节边界对齐)。因此:
stage2_end=stage2_start+stage2_size
另外,还必须确保所安排的地址范围的的确确是可读写的 RAM 空间,因此,必须对你所安排的地
址范围进行测试。具体的测试
可以采用类似于 blob 的方法,也即:以 memory page 为被测试
单位,测试每个 memory page 开始的两个字是否是可读写的。为了后面叙述的方便,我们记这个
检测算法为:test_mempage,其具体步骤如下:
1. 先保存 memory page 一开始两个字的内容。
2. 向这两个字中写入任意的数字。比如:向第一个字写入 0x55,第 2 个字写入 0xaa。
3. 然后,立即将这两个字的内容读回。显然,我们读到的内容应该分别是 0x55 和 0xaa。如果不
是,则说明这个 memory page 所占据的地址范围不是一段有效的 RAM 空间。
4. 再向这两个字中写入任意的数字。比如:向第一个字写入 0xaa,第 2 个字中写入 0x55。
5. 然后,立即将这两个字的内容立即读回。显然,我们读到的内容应该分别是 0xaa 和 0x55。如
果不是,则说明这个 memory page 所占据的地址范围不是一段有效的 RAM 空间。
6. 恢复这两个字的原始内容。测试完毕。
为了得到一段干净的 RAM 空间范围,我们也可以将所安排的 RAM 空间范围进行清零操作。
1.1.3 拷贝 stage2 到 RAM 中
拷贝时要确定两点:(1) stage2 的可执行映象在固态存储设备的存放起始地址和终止地址;(2)
RAM 空间的起始地址。
1.1.4 设置堆栈指针 sp
堆栈指针的设置是为了执行 C 语言代码作好准备。通常我们可以把 sp 的值设置为(stage2_end-
4),也即在 1.1.2 节所安排的那个 1MB 的 RAM 空间的最顶端(堆栈向下生长)。
此外,在设置堆栈指针 sp 之前,也可以关闭 led 灯,以提示用户我们准备跳转到 stage2。
经过上述这些执行步骤后,系统的物理内存布局应该如下图 2所示。
1.1.5 跳转到 stage2 的 C 入口点
在上述一切都就绪后,就可以跳转到 Boot Loader 的 stage2 去执行了。比如,在 ARM 系统中,
这可以通过修改 PC 寄存器为合适的地址来实现。
head.S 负责完成硬件初始化操作,具体分析见源码注释 ,汇编差不多忘光了,下面注释中有关汇
编的东西多些。
其中"linkage.h"
#define SYMBOL_NAME_STR(X) #X
#define SYMBOL_NAME(X) X
#ifdef __STDC__
#define SYMBOL_NAME_LABEL(X) X##:
#else
#define SYMBOL_NAME_LABEL(X) X/**/:
#endif
#define __ALIGN .align 0
#define __ALIGN_STR ".align 0"
#ifdef __ASSEMBLY__
#define ALIGN __ALIGN
#define ALIGN_STR __ALIGN_STR
#define ENTRY(name) \
.globl SYMBOL_NAME(name); \
ALIGN; \
SYMBOL_NAME_LABEL(name)
#endif
其中"machine.h" 包括了
smdk2410.h (有关开发板的配置) ,
包括memory map, Porocessor memory map ,FLASH, ROM, DRAM的物理地址和在 VIVI中用
的虚拟地址(?),Architecture magic and machine type, UART,CPU,DRAM的初始化参数等
smdk2410.h进一步包括 s3c2410.h, 有关 CPU的设置,Definition of constants related to
the S3C2410 microprocessor(based on ARM 920T).
/*
* vivi/arch/s3c2410/head.S:
* Initialise hardware
*
* Copyright (C) 2001 MIZI Research, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*
* Author: Janghoon Lyu
* Date : $Date: 2003/02/26 10:38:11 $
*
* $Revision: 1.18 $
*
*
* History:
*
* 2002-05-14: Janghoon Lyu
* - Initial code
*
*/
#include "config.h" //Æautoconf.h 空的
#include "linkage.h" //定义
#include "machine.h"
@ Start of executable code
ENTRY(_start) //入口点
ENTRY(ResetEntryPoint)
@
@ Exception vector table (physical address = 0x00000000)
@ //异常向量 表 物理地址 0x0000000
@ 0x00: Reset //最 基本操作 :复位 B 是最简单的分支。一旦遇到一个 B 指令,ARM 处
理器将立即跳转到给定的地址,从那里继续执行。注意存储在分支指令中的实际的值是相对当前的
R15 的
b Reset
@ 0x04: Undefined instruction exception //处理未定义的指令
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 // fast 中断处理
FIQEntryPoint:
b HandleFIQ
@
@ VIVI magics
@
@ 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
@ 0x2C: this contains the platform, cpu and machine id
.long ARCHITECTURE_MAGIC
@ 0x30: vivi capabilities
.long 0
#ifdef CONFIG_PM // power management //vivi未用
@ 0x34:
b SleepRamProc
#endif
#ifdef CONFIG_TEST //test mode vivi未用
@ 0x38:
b hmi
#endif
@
@ Start VIVI head
@
Reset: //第一步 RESET
@ disable watch dog timer //disable watch dog定时器
mov r1, #0x53000000
mov r2, #0x0
str r2, [r1] //add 0x5300_0000 清 0,bit 5=0 disable this timer
#ifdef CONFIG_S3C2410_MPORT3 //另一种 Platform 非 SMDK
mov r1, #0x56000000
mov r2, #0x00000005
str r2, [r1, #0x70]
mov r2, #0x00000001
str r2, [r1, #0x78]
mov r2, #0x00000001
str r2, [r1, #0x74]
#endif
@ disable all interrupts //禁止 所有中断
mov r1, #INT_CTL_BASE //0x4A00_0000 source pending register
mov r2, #0xffffffff
str r2, [r1, #oINTMSK] //0x4A00_0008
//0x4A00_0008 INTERRUPT MASK register=0xFFFFFFFF, disable all int
ldr r2, =0x7ff
str r2, [r1, #oINTSUBMSK] //0x4A00_001C
//Interrupt sub mask register , bit[10:0] = 1 ->0x7FF ->disable all
@ initialise system clocks //初始化系统时钟
mov r1, #CLK_CTL_BASE // LOCK TIME COUNT REGISTER(LOCKTIME)
//0x4c000000
mvn r2, #0xff000000
str r2, [r1, #oLOCKTIME] //0x4C000000 ->0xFF00_0000;
@ldr r2, mpll_50mhz //CPU定成 50Mhz
@str r2, [r1, #oMPLLCON] //
#ifndef CONFIG_S3C2410_MPORT1 //如果未定义成 MPORT1 (一种 plat form)
@ 1:2:4
mov r1, #CLK_CTL_BASE
mov r2, #0x3
str r2, [r1, #oCLKDIVN] //
// vCLKDIVN 0x3 /* FCLK:HCLK:PCLK = 1:2:4 */
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
@ now, CPU clock is 200 Mhz //CPU定成 200Mhz
mov r1, #CLK_CTL_BASE
ldr r2, mpll_200mhz
str r2, [r1, #oMPLLCON]
#else //platform= MPORT1 ,以下不理
@ 1:2:2
mov r1, #CLK_CTL_BASE
ldr r2, clock_clkdivn
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
@ now, CPU clock is 100 Mhz
mov r1, #CLK_CTL_BASE
ldr r2, mpll_100mhz
str r2, [r1, #oMPLLCON]
#endif
bl memsetup //第 2步memsetup
#ifdef CONFIG_PM //如果有 Power management:不用
@ Check if this is a wake-up from sleep
ldr r1, PMST_ADDR
ldr r0, [r1]
tst r0, #(PMST_SMR)
bne WakeupStart
#endif
#ifdef CONFIG_S3C2410_SMDK //SMDK platform
@ All LED on //点灯,好歹通知一下外面的同志 3
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]
#endif
#if 0
@ 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 // 0x5600_0000
add r1, r1, #oGPIO_H
// oGPIO_H 0x70 PORT H CONTROL REGISTERS
ldr r2, gpio_con_uart // vGPHCON= 0x0016faaa
str r2, [r1, #oGPIO_CON] // 01 01 10 11 11 10 10 10 10 10 10 B
//oGPIO_CON = 0x0
// GPH0 bit[1:0] = 10 nCTS0
// GPH1 bit[3:2] = 10 nRTS0
// GPH2 bit[5:4] = 10 TXD0
// GPH3 bit[7:6] = 10 RXD0
// GPH4 bit[9:8] = 10 TXD1
// GPH5 bit[11:10] = 10 RXD1
// GPH6 bit[13:12] = 11 nRTS1
// GPH7 bit[15:14] = 11 nCTS1
// GPH8 bit[17:16] = 10 UEXTCLK
// GPH9 bit[19:18] = 01 Output
// GPH10 bit[21:20] = 01 Output
ldr r2, gpio_up_uart // vGPHUP 0x000007ff = 0111 1111 1111 B
str r2, [r1, #oGPIO_UP]
// oGPIO_UP 0x8 /* R/W, Pull-up disable register */
// 0x7FF -> 1: The pull-up function is disabled. For all GPHx
// reg GPHUP 0x56000078
bl InitUART //initialize UART
#ifdef CONFIG_DEBUG_LL //low level debugging info
@ Print current Program Counter //vivi def没用
ldr r1, SerBase //往串口上输出 info
mov r0, #'\r'
bl PrintChar
mov r0, #'\n'
bl PrintChar
mov r0, #'@'
bl PrintChar
mov r0, pc
bl PrintHexWord
#endif
#ifdef CONFIG_BOOTUP_MEMTEST // comment 'Low Level Hardware Debugging'
bool ' Enable simple memory test' CONFIG_BOOTUP_MEMTEST //vivi def没用
@ simple memory test to find some DRAM flaults.
bl memtest //check the first 1MB in increments of 4k//改大点 3
#endif
#ifdef CONFIG_S3C2410_NAND_BOOT
bl copy_myself
@ jump to ram
ldr r1, =on_the_ram //将 on_the_ram的地址装入 r1
add pc, r1, #0 //pc = r1+0
nop
nop
1: b 1b @ infinite loop //硬是看不懂这个 B
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
// STACK_BASE+STACK_SIZE-4
// STACK_BASE = (VIVI_PRIV_RAM_BASE - STACK_SIZE)
//STACK从上往下用。 所以 STACK_START = STACK_BASE+STACK_SIZE-4
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, //FLASH_BASE=ROM_BASE0 =
0x0
@
@ End VIVI head
@
/*
* subroutines
*/
@
@ Wake-up codes
@
#ifdef CONFIG_PM
WakeupStart: // power management 用
@ Clear sleep reset bit
ldr r0, PMST_ADDR
mov r1, #PMST_SMR
str r1, [r0]
@ Release the SDRAM signal protections
ldr r0, PMCTL1_ADDR
ldr r1, [r0]
bic r1, r1, #(SCLKE | SCLK1 | SCLK0)
str r1, [r0]
@ Go...
ldr r0, PMSR0_ADDR @ read a return address
ldr r1, [r0]
mov pc, r1
nop
nop
1: b 1b @ infinite loop
SleepRamProc: //power management用
@ SDRAM is in the self-refresh mode */
ldr r0, REFR_ADDR
ldr r1, [r0]
orr r1, r1, #SELF_REFRESH
str r1, [r0]
@ wait until SDRAM into self-refresh
mov r1, #16
1: subs r1, r1, #1
bne 1b
@ Set the SDRAM singal protections
ldr r0, PMCTL1_ADDR
ldr r1, [r0]
orr r1, r1, #(SCLKE | SCLK1 | SCLK0)
str r1, [r0]
/* Sleep... Now */
ldr r0, PMCTL0_ADDR
ldr r1, [r0]
orr r1, r1, #SLEEP_ON
str r1, [r0]
1: b 1b
#ifdef CONFIG_TEST
hmi:
ldr r0, PMCTL0_ADDR // PMCTL0_ADDR: .long 0x4c00000c, Clock Gen Ctrl
ldr r1, =0x7fff0 // reset clock gen ctrl
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) //memsetup子程序
@ initialise the static memory
@ set memory control registers
mov r1, #MEM_CTL_BASE //memory controller
adrl r2, mem_cfg_val
add r3, r1, #52
1: ldr r4, [r2], #4
str r4, [r1], #4
cmp r1, r3
bne 1b
mov pc, lr //这里返回了么?
注意注意:这里memsetup已经返回了,下面是独立的子程序了呢
#ifdef CONFIG_S3C2410_NAND_BOOT // NAND如此,NOR应该如何处理呢?
@ //不需要 copy vivi to ram?????
@ copy_myself: copy vivi to ram
@
copy_myself:
mov r10, lr
@ reset NAND
mov r1, #NAND_CTL_BASE
ldr r2, =0xf830 @ initial value
str r2, [r1, #oNFCONF]
ldr r2, [r1, #oNFCONF]
bic r2, r2, #0x800 @ enable chip
str r2, [r1, #oNFCONF]
mov r2, #0xff @ RESET command
strb r2, [r1, #oNFCMD]
mov r3, #0 @ wait
1: add r3, r3, #0x1
cmp r3, #0xa
blt 1b
2: ldr r2, [r1, #oNFSTAT] @ wait ready
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 //(DRAM_BASE + DRAM_SIZE - VIVI_RAM_SIZE)
//0x33f00000
mov r1, #0x0 //start address, now vivi is in steppingstone
mov r2, #0x20000 //128k ?
bl nand_read_ll
// nand_read_ll(unsigned char *buf, unsigned long start_addr, int size)
// ro = buf , r1 =start , size = r2=128k ??yeah ? 要这么多干吗?
tst r0, #0x0 //返回值在 r0中
beq ok_nand_read //nand_read_ll()顺利返回
#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 //flash start add? no. 是用 NAND启动时,从 NAND copy
到 phy add=0的 4Kbytes SRAM(stepping stone)中的 vivi
ldr r1, =0x33f00000 //VIVI_RAM_BASE
// VIVI_RAM_BASE = (DRAM_BASE + DRAM_SIZE(64M) - VIVI_RAM_SIZE)
//= 0x3000_0000+0x0400_0000-0x0010_0000 = 0x33f0_0000
mov r2, #0x400 @ 4 bytes * 1024 = 4K-bytes //移 4K去 VIVI_RAM_BASE ?NO
比较 4Kbytes? YES
go_next:
ldr r3, [r0], #4 //将[r0]指向的数据(32bit?)放进 r3 ,然后 r0+4->r0
ldr r4, [r1], #4 //将[r1]指向的数据(32bit?)放进 r4 ,然后 r1+4->r1
teq r3, r4 //比较 r3/r4的大小
bne notmatch //出现不匹配的情况
subs r2, r2, #4 //r2-4 ->r2, if subs 结果为 0,flagZ==1
beq done_nand_read //若 r2==0条件(flagZ==1)成立,跳到 done_nand_read
bne go_next //flagZ==0; 说明 r2!=0
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 //返回罗,在函数入口处mov r10, lr
@ 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 r0!, {r2-r9}
subs r1, r1, #(8 * 4)
bne clear_loop
mov pc, lr
#endif @ CONFIG_S3C2410_NAND_BOOT
#ifdef CONFIG_BOOTUP_MEMTEST // Low Level Hardware DebuggingÆ Enable simple
memory test //vivi未用
@
@ Simple memory test function
@
memtest:
mov r10, lr
#ifdef CONFIG_DEBUG_LL //low level debugging,往 serport上面捣腾信息
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 */ //循环测试 1M得 SDRAM//我们应该改大
点
mov r7, #0x1000
mov r6, r7, lsl #8 /* 4k << 2^8 = 1MB */
mov r5, #DRAM_BASE
//DRAM_BASE =DRAM_BASE0= 0x30000000 /* base address of dram bank 0 */
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 r1!, {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 memtest 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
@ //called by memtest
@ 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
//SerBase = UART0_CTL_BASE = UART_CTL_BASE =0x50000000
// 0x5000_0000 UART channel 0 line control register
InitUART:
ldr r1, SerBase //0x5000_0000
mov r2, #0x0
str r2, [r1, #oUFCON] //清零 oUFCON
//#define oUFCON 0x08 /* R/W, UART FIFO control register */
// UFCON0 0x50000008 R/W UART channel 0 FIFO control register
Tx FIFO Trigger Level [7:6] : 00 : Empty
Rx FIFO Trigger Level [5:4]: 00 : 00 = 4-byte
Tx FIFO Reset [2] : 0 = Normal
Rx FIFO Reset [1] : 0 = Normal
FIFO Enable [0