Chinaunix首页 | 论坛 | 博客
  • 博客访问: 83753
  • 博文数量: 32
  • 博客积分: 2033
  • 博客等级: 大尉
  • 技术积分: 380
  • 用 户 组: 普通用户
  • 注册时间: 2008-04-18 15:43
文章分类

全部博文(32)

文章存档

2011年(1)

2010年(31)

我的朋友

分类: LINUX

2010-04-19 16:04:21

前言:

     由于做毕业设计的原因,开始接触arm嵌入式在积累linux,arm,datesheet,arm

等一连串的基础后,终于可以接触到具体的代码了,不过种子在春天总是要发芽的。

      我要分析的是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代码分析,对头文件进行重组,删减不必要程序,完成分析文档,尽量做详细说明。
 
 
先解释下head.S后缀S为什么必须是大写,很多人在make的时候发现在这个head.S这个文件这出错,那是因为他们的文件head.s的s是小写。那这是为什么了?
由于这段代码主要是汇编编写,而GNU as是不能做宏处理和文件包含处理的。在head.S文件里有很多预处理,现在要处理这些宏和文件包含,只有交给CPP预处理器来做。CPP如何识别这些文件呢?答案是通过后缀。
 
man gcc可以获得
 
          
这就是为什么用head.S的原因了!
 
 
[vivi/arch/s3c2410/head.S]的结构,在head.S主要做的是bootloader启动的第一阶段,
大致流程如下图:
 
 
 
 
 

/*
 * 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节

文件: 001.PDF
大小: 1223KB
下载: 下载



@ 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"
#define MACH_TYPE  193 
#define ARCHITECTURE_MAGIC ((ARM_PLATFORM << 24) | (ARM_S3C2410_CPU << 16) | MACH_TYPE)

在"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:

the S3C2410X 有不同电源管理方案 来维持最佳的电源使用方式, the S3C2410X的电源管理模块能激活四种工作模式,分别为 
NORMAL mode: 这个模块为CPU和外围设备提供一样的电源供应,当所有设备开启时他消费的功率最大。 这些设备能通过设置软件来开关其运行。

SLOW mode: 在这种模式下,PLL不工作,时钟信号直接由外部晶振供应。

IDLE mode: (空闲模式)这种模式下电源不给CPU供电,但任何对CPU的中断请求都从IDLE mode惊醒。


Power-OFF mode: 在这个模式下除了给wake-up逻辑单元供电,电源断开了多有的电源供应 激活Power-OFF mode 需要两个独立的电源源, 一个用于 wake-up logic. 另一个用于CPU和内部电路

在Power-OFF模式下CPU和内电路电源供应被断开

从Power-OFF模式返回可以通过设置EINT[15:0] or by RTC alarm interrupt.

具体参考:

Figure 7-8. Power Management State Diagram

Table 7-2. Clock and Power State in Each Power Mode

在这里将详细讨论Power-OFF MODE

Procedure to Enter Power_OFF mode(在Power_OFF MODE的程序设置)在这里我们主要关注后面四项,因为我们要设置的就是SDRAM在POWER OFF MODE 下的寄存器设置。
1. Set the GPIO configuration adequate for Power_OFF mode.
2. Mask all interrupts in the INTMSK register.
3. Configure the wake-up sources properly including RTC alarm.
The bit of EINTMASK corresponding to the wake-up source has not to be masked in order to let the
corresponding bit of SRCPND or EINTPEND set. Although a wake-up source is issued and the corresponding
bit of EINTMASK is masked, the wake-up will occur and the corresponding bit of SRCPND or EINTPEND will
not be set.
4. Set USB pads as suspend mode. (MISCCR[13:12]=11b)
5. Save some meaning values into GSTATUS3,4 register. These register are preserved during Power_OFF
mode.
6. Configure MISCCR[1:0] for the pull-up resisters on the data bus,D[31:0]. If there is an external BUS holder,
such as 74LVCH162245, turn off the pull-up resistors. If not, turn on the pull-up resistors
7. Stop LCD by clearing LCDCON1.ENVID bit.
8. Read rREFRESH and rCLKCON registers in order to fill the TLB.
9. Let SDRAM enter the self-refresh mode by setting the REFRESH[22]=1b.
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.


@ 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。
WTCON:watchdog控制寄存器
WTDAT:watchdog数据寄存器
WTCNT:watchdog记数寄存器

以上各个寄存器的详细信息请参考s3c2410数据手册上关于第18章watchdog部分;

在这里我们只需要设置WTCON
WTCON:watchdog控制寄存器。

地址:0x53000000

16位起作用【15:0】

[15:1]可以使用默认值

[0]-------1  watchdog正常工作,0 watchdog 计时到了不发生重启


#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
10 = nRTS0 11 = Reserved
GPH0 [1:0] 00 = Input  01 = Output
10 = nCTS0 11 = Reserved


    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.
1: The pull-up function is disabled.

在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
read to the corresponding pin. When the port is configured as output port,
data written in this register can be sent to the corresponding pin. When the
port is configured as functional pin, undefined value will be read.

在这里设置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 */
#define INT_CTL_BASE  0x4A000000
/* Offset */
#define oSRCPND   0x00
#define oINTMOD   0x04
#define oINTMSK   0x08
#define oPRIORITY  0x0a
#define oINTPND   0x10
#define oINTOFFSET  0x14
#define oSUBSRCPND  0x18
#define oINTSUBMSK  0x1C

在这里先只讨论(详见S3C2410手册)
INTMSK(INTERRUPT MASK REGISTER)  主中断屏蔽寄存器  (32位有效)
地址:  0x4A000008

INTSUBMSK  (INTERRUPT SUB MASK REGISTER) 副中断屏蔽寄存器  (前11位有效)
地址:0x4A00001C

写 1 不响应请求, 写 0 响应请求

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 CLK_CTL_BASE  0x4C000000

/* Offset */
#define oLOCKTIME  0x00 /* PLL lock time count register */
#define oMPLLCON  0x04 /*  MPLL configuration register */

#define oUPLLCON  0x08 /* R/W, UPLL configuration register */
#define oCLKCON   0x0C /* R/W, Clock generator control reg. */
#define oCLKSLOW  0x10 /* R/W, Slow clock control register */
#define oCLKDIVN  0x14 /* R/W, Clock divider control */

#define vLOCKTIME  0x00ffffff

#define vCLKDIVN  0x3  /* FCLK:HCLK:PCLK = 1:2:4 */
#define vMPLLCON_50  ((MDIV_50 << 12) | (PDIV_50 << 4) | (SDIV_50))
#define vMPLLCON_200  ((MDIV_200 << 12) | (PDIV_200 << 4) | (SDIV_200))

/* 50.00 MHz */
#define MDIV_50   0x5c
#define PDIV_50   0x4
#define SDIV_50   0x2
/* 200.00 MHz */
#define MDIV_200  0x5c
#define PDIV_200  0x4
#define SDIV_200  0x0

解释如下:

用三个寄存来来控制时钟频率,

由于晶振初始频率是12MHZ=FIN


1:LOCKTIME寄存器:0x4c00000000 锁相环时间控制器设置pll时间,等待locktime时间到来,产生新的Fclk

U_TIME  [23:12] UPLL lock time count value for uclock    初始值为0xfff
M_TIME  [11:0 ]   Mpll   lock time count value for fclk,hclk,pclk   初始值为0xfff

写入的值为预定值

2:MPLLCON寄存器:0x4c0000 0004 (主锁相环时钟控制寄存器)

MDIV [19:12]  mian divider control        初始值 0x28
PDIV [ 9  : 4]   Pre-divider control      初始值 0x08
SDIV [ 1  : 0]   post divider control     初始值 0x00

时钟输出FOUT=FCLK=m*FIN/(p*2^s)
m=MDIV+8;p=PDIV+2;s=SDIV;


3:CLKDIVN寄存器:0x4C0000 0014  时钟分频控制寄存器

HDIVN [1]   0:HCLK=FCLK  1:HCLK=0.5FCLK
PDIVN [0]   0:PCLK=HCLK  1:PCLK=0.5HCLK



     @ now, CPU clock is 200 Mhz
    mov    r1, #CLK_CTL_BASE
    ldr    r2, mpll_200mhz
    str    r2, [r1, #oMPLLCON]

设置系统时钟s3c2410中第七章有详细说明

看了下基本认识如下(挑有用的看)主要是查看datasheet

   

CLOCK & POWER MANAGEMENT 

1:时钟电源管理包含三部分: clock control, USB control, and power control.主要讨论clock control部分,关于power control等会还会涉及到。
S3C2410X 的时钟管理功能能产生三部分的时钟需求:

FCLK for CPU(ARM920T)

HCLK for the AHB bus peripherals(ARM920T, the memory controller, the interrupt controller, the
LCD controller, the DMA and the USB host block)

PCLK for the APB bus peripherals.(看门狗, IIS, I2C, PWM timer, MMC interface,
ADC, UART, GPIO, RTC and SPI)

【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:先看下面一段

CLOCK CONTROL LOGIC
The clock control logic determines the clock source to be used, i.e., the PLL clock (Mpll) or the direct external
clock (XTIpll or EXTCLK). When PLL is configured to a new frequency value, the clock control logic disables the
FCLK until the PLL output is stabilized using the PLL locking time. The clock control logic is also activated at
power-on reset and wakeup from power-down mode.


Power-On Reset (XTIpll)
Figure 7-4 shows the clock behavior during the power-on reset sequence. The crystal oscillator begins oscillation
within several milliseconds. When nRESET is released after the stabilization of OSC (XTIpll) clock, the PLL
starts to operate according to the default PLL configuration. However, PLL is commonly known to be unstable
after power-on reset, so Fin is fed directly to FCLK instead of the Mpll (PLL output) before the software newly
configures the PLLCON. Even if the user does not want to change the default value of PLLCON register after
reset, the user should write the same value into PLLCON register by software.


The PLL restarts the lockup sequence toward the new frequency only after the software configures the PLL with a
new frequency. FCLK can be configured as PLL output (Mpll) immediately after lock time.

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

阅读(1224) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~