Chinaunix首页 | 论坛 | 博客
  • 博客访问: 351744
  • 博文数量: 73
  • 博客积分: 4096
  • 博客等级: 上校
  • 技术积分: 1102
  • 用 户 组: 普通用户
  • 注册时间: 2007-11-21 14:53
文章分类
文章存档

2009年(3)

2008年(70)

我的朋友

分类:

2008-09-25 15:41:03

基于2410开发板。

   2410芯片上有8个管脚nGCS[7:0]用来做外接ROM芯片选择。如果ROM芯片的使能管脚nCE和nGCS0相连,就相当于将ROMA映射到bank0地址空间。
首先、初始化硬件。
    硬件的初始化通过配置特殊控制寄存器来完成。包括:
    1、关watchdog timer
       涉及到的硬件:看门狗定时器
       看门狗定时器是系统软件崩溃后进行恢复的一种方法,一次WDT超时溢出将产生一次器件复位。
       关闭看门狗计时器
          Register:WTCON
          Address:0x********
          ldr r0,=WTCON
          ldr r1,=0x0
          str r1,[r0]
    2、屏蔽所有的中断
       为中断提供服务通常是OS设备驱动程序的责任。因此,boot的执行过程中可以不必响应任何中断,通过对CPSR寄存器进行配置,屏蔽所有的中断。
       Register:INMSK
       Address:0x********
       ldr r0,=INMSK
       ldr r1,=0x07FFFFFF   ;disable all interrupt
       str r1,[r0]
提到CPSR寄存器,顺便复习下ARM中的寄存器。
    ARM共37个32位寄存器。
    31个通用寄存器。
    6个状态寄存器。
   
    31个通用寄存器分为:备份寄存器、未备份寄存器、程序计数器PC。
    未备份寄存器:R0-R7。用于6种工作模式。
    备份寄存器:R8-R12。每个寄存器对应2个不同的物理寄存器。
       即: r8 、 R8fiq
          
r9 、 R9fiq
          
r10 、 R10fiq   
                r11 、 R11fiq
          
r12 、 R12fiq
       R13和R14分别对应6个不同的物理寄存器。
         
R13_   R14_ 

其中MODE可以是下面几种模式之一:usr,svc,abt,und,irq,fiq
      
    程序计数器R15,即PC
       由于ARM9采取流水线处理器机制,即:
    取指--->译码--->执行--->存储--->写。
   所以,当程序执行时,PC指向的地址,是当前执行的程序的所在地址+8字节。因为对于PC而言,是三级流水线。
      
   由于ARM指令是字对齐的,PC值的第1位和第0位总为0.
所以,写入R15的值必须满足b[1:0]的值为0。

   CPSR:当前程序状态寄存器。在任何工作模式下都可被访问。包含了:条件标志位、中断禁止位、当前处理器模式标志以及其它的一些控制和状态位。

   SPSR:备份程序状态寄存器。当特定的异常发生时,用于保存CPSR的内容,不然中断的处理程序会修改CPSR的原有内容。中断退出后,用SPSR恢复CPSR。由于用户模式和系统模式不是异常模式,所以没有SPSR 。
   
    CPSR和SPSR的格式相同。如下:

31
30
29
28
27
26
25-8
7
6
5
4
3
2
1
0
N
Z
C
V
Q
DNM

I
F
T
M4
M3
M2
M1
M0
   
      条件标志位:
      N: 2个有符号整数运算时,
          1----运算结果为负数。
          0----运算结果为正数或0。
      Z: 1----运算的结果为0。
          0----运算的结果不为0。
          对于CMP指令,Z=1,表示进行比较的2个数大小相等。

         C——下面分四种情况讨论C的设置方法:
        在加法指令中(包括比较指令CMP),当结果产生了进位,则C=1,表示无符号运算发生上溢出;其他情况C=0。
        在减法指令中(包括减法指令CMP),当运算中发生错位,则C=0,表示无符号运算数发生下溢出;其他情况下C=1。
        对于包含移位操作的非加碱运算指令,C中包含最后一次溢出的的位的数值
        对于其他非加减运算指令,C位的值通常不受影响

        V——对于加减运算指令,当操作数和运算结果为二进制的补码表示的带符号数时,V=1表示符号为溢出;通常其他指令不影响V位。


       
***Q标识位***
    在ARM V5的E系列处理器中,CPSR的bit[27]称为q标识位,主要用于指示增强的dsp指令是否发生了溢出。同样的spsr的bit[27]位也称为q标识位,用于在异常中 断发生时保存和恢复CPSR中的Q标识位。
    在ARM V5以前的版本及ARM V5的非E系列的处理器中,Q标识位没有被定义。

    CPSR中的控制位:
    I、F、T、M[4:0]
    I=1,禁止IRQ中断。
    F=1,禁止FIQ中断。
    T=0,执行ARM指令。
    T=1,执行Thumb指令。

    M[4:0]控制位,控制处理器模式。
ob10000 user pc,r14~r0,CPSR
0b10001 FIQ PC,R14_FIQ-R8_FIQ,R7~R0,CPSR,SPSR_FIQ
0b10010 IRQ PC,R14_IRQ-R13_IRQ,R12~R0,CPSR,SPSR_IRQ
0B10011 SUPERVISOR PC,R14_SVC-R13_SVC,R12~R0,CPSR,SPSR_SVC
0b10111 ABORT PC,R14_ABT-R13_ABT,R12~R0,CPSR,SPSR_ABT
0b11011 UNDEFINEED PC,R14_UND-R8_UND,R12~R0,CPSR,SPSR_UND
0b11111 SYSTEM PC,R14-R0,CPSR(ARM V4以及更高版本)


   所以此处,关中断,只要设置CPSR的[7:6]为1。
   3、初始化PLL和时钟。
      PLL的输出频率就是处理器的工作频率。
    此处涉及到的硬件:
     晶振电路
      晶振电路用于向CPU及其他外围电路提供工作时钟。
    
锁相环(PHASE-LOCKED-LOOP)PLL
    锁相环是一个相位误差控制系统。它比较输入信号和振荡器输出信号之间的相位差,从而产生误差控制信号来调整振荡器的频率,以达到与输入信号同频同相。
     锁相环一般都提供了倍频、分频、相移三个功能,片内的PLL电路兼有频率放大和信号提纯的功能,因此,系统可以以较低的外部时钟信号获得较高的工作频率,以降低因高速开关时钟所造成的高频噪声。
    相关寄存器
       Register:PLLCON(PLL configuration Register)
       Address:0x********

       Register:CLKCON(Clock generator Control)
       Address:0x********

       Register:LOCkTIME(PLL Lock time count)
       Address:0x********
    设置主频
    PLLCON Register用来配置主频,可以通过修改这个Register的内容达到修改配置主频的目的。具体的配置和计算方法如下所示:
    设置主频为60MHZ
    ;M_DIV EQU 0x34
    ;P_DIV EQU 0x3
    ;S_DIV EQU 0x1
    [PLLONSTART
       ldr r0,=PLLCON
       ldr r1,=((M_DIV<<12)+(P_DIV<<4)+S_DIV)
       str r1,[r0]
    ]
    主频计算公式:
    PLLCON[19:12]=M_DIV(Main divider control)
    PLLCON[9:4]=  P_DIV(Pre-divider Control)
    PLLCON[1:0]=  S_DIV(post divider Control)
   
    Fpllo = (m*Fin)/(p*2^s)
    m=(M_DIV+8), p=(P_DIV+2), s=S_DIV
    代入上面的值:
    m=0x34+8=0x3c, p=0x3+2=5, s=1
    Fpllo = (0x3c*10)/(5*2^1)=0x3c=60(MHZ)

    4、初始化RAM
       通过一组专用的特殊功能寄存器来控制外部存储器的读/写操作。通过对该组特殊功能寄存器编程,可以设定外部数据总线宽度,访问周期,定时的控制信号(例如RAS和CAS)等参数。
    主要通过对13个Memory CONTROLLER SPECIAL Register来完成。
    vivi的head.S中的相应代码为:

    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
    注:要理解这些寄存器需要查看相应的芯片手册和资料。
    1)、BWSCON:对应BANK0-BANK7,每BANK使用4位。
    4位分别表示:
       STx:启动/禁止SDRAM的数据掩码引脚。
             SDRAM:  0
             SRAM :  1
       WSx:是否使用存储器的WAIT信号,通常设置为0 。
       DWx:使用两位来设置存储器的位宽。
             8位:   00
            16位:   01
            32位:   10
            保留:    11
      特殊的是BANK0对应的4位,它们由硬件跳线决定,是只读的。
 本开发板的s3c2440芯片外接了2片32M的SDRAM
:K4S561632H-UC75 
组成64M,位宽为32位的存储器。
    连接方法:2片
K4S561632H-UC75CS(片选管脚)接到nGCS6。那么我们在进行内存设置时,只需将BANK6对应的4位设置为0010即可。
   本开发板vivi的BWSCON设置值为:    0x22111110
   定义的宏在:
include/platform/smdk2410.h
             #define vBWSCON
0x22111110
    2)、BANKCON0-BANKCON5:我们没用到,使用默认值0x00000700即可
  
定义的宏在:include/platform/smdk2410.h
         #define    vBANKCON0         0x00000700
        
#define    vBANKCON1         0x00000700
        
#define    vBANKCON2         0x00000700
        
#define    vBANKCON3         0x00000700
   
     #define    vBANKCON4         0x00000700
   
     #define    vBANKCON5         0x00000700
    3)、BANKCON6-BANKCON7
    在8个BANK中,只有BANK6和BANk7可以使用SRAM或SDRAM,与
BANKCON0-BANKCON5不同。
    (1)MT([16:15]):用于设置本BANK外接的是SRAM还是SDRAM:
          SRAM:   0b00
         SDRAM:   0b11
    (2)当MT = 0b11 时,还需要设置两个参数:
       Trcd([3:2]):RAS to CAS delay,设为推荐值0b01。
       SCAN([1:0]):SDRAM的列地址位数,本开发板的SDRAM列地址数为9,所以,SCAN=0b01 。
    
    定义的宏在:include/platform/smdk2410.h
        
#define    vBANKCON6     0x00018005
        
#define    vBANKCON7     0x00018005 
         参照3中的设置方法,即:
        BANK6和BANK7的值为:
        0b0000 0000 0000 0001    1000   0000 0000 0101                            MT[16] MT[15]         
           0101:Trcd[3:2] SCAN[1:0]                  
                      0 1       0 1
        Trcd:RAS to CAS delay
   这是用来设定从RAS信号到CAS信号所需的时间周期(clock)。当CPU要从内存读取或写入数据时,必须送出一个正确的地址信号,而地址是由行、列交错而成,所以又分为RAS(列地址信号)和CAS(行地址信号),由于是先寻址RAS、再寻址CAS,因此中间就有延迟时间,建议先设成2个clock(2T),让SDRAM能快点将地址寻址完毕,这样可以将内存性能提高;
        Tcas:CAS pulse width
        Tcp: CAS pre-charge
        CAS: 表示列地址寻址(Column Address Strobe or Column Address Select)   
        RAS: 表示行地址寻址(Row Address Strobe)
 
   4)、REFRESH(SDRAM refresh Control Register):0x008e0459
   Trp[21:20]:DRAM/SDRAM RAS pre-charge Time
    设定当RAS(列地址信号)需要重新寻址时,要隔多久时间才能开始下次的寻址动作,通常被视为RAS的充电时间,理论上是越短越好。其选项值与上面讲到的一样,所以建议将其设定为2T(2个clock周期),这样可以将内存性能提高;若发现系统运行不稳定,再将其改回3T。
    Trc[19:18] SDRAM RC minimum Time
    这个参数用来控制内存的行周期时间。tRC决定了完成一个完整的循环所需的最小周期数,也就是从行激活到行充电的时间。
   其中的R_CNT用于控制SDRAM的刷新周期,占用REFRESH寄存器的[10:0]位,它的取值可如下计算得来。(SDRAM的时钟频率就是HCLK)
    R_CNT=2^11+1-SDRAM时钟频率(MHz)*SDRAM刷新周期(uS)
    在未使用PLL时,SDRAM时钟频率等于晶振频率12MHz;SDRAM的刷新周期在SDRAM的数据手册上有说明。
   
K4S561632H-UC75手册标明:Refresh period is 64ms 。

    5)、BANKSIZE:0xb2
位[7]=1:Enable burst operation
位[5]=1:SDRAM power down mode enable
位[4]=1:SCLK is active only during the access(recommended)
位[2:1]=010 BANK6.BANK7对应的地址空间:010---128M/128M,001--64M/64M

    6)、MRSRB6、MRSRB7:0x00000030
    能让我们修改的只有位[6:4](CL),SDRAM
HY57V561620CT-H不支持CL=1的情况,所以位[6:4]取值为010(CL=2)或011(CL=3)

   5、复制RW到DRAM,将Zi段清零
    一个ARM由RO、RW和ZI三个段组成,其中RO为代码段,RW是已初始化的全局变量,ZI是未初始化的全局变量(与TEXT,DATA和BSS相对应)
    RO段是只读的,在运行的时候不可以改变,所以在运行的时候,RO段可以驻留在FLASH里。
    RW段是可以读写的,所以,在运行的时候必须被装载到SDRAM或者SRAM里,所以Boot要将RW段复制到RAM中,并将ZI段清零,以保证程序可以正确运行。
    编译器使用下列符号来记录各段的起始和结束地址:
     |Image$$RO$$Base| :RO段起始地址
     |Image$$RO$$Limit| :RO段结束地址加1
     |Image$$RW$$Base| :RW段起始地址
     |Image$$RW$$Limit| :ZI段结束地址加1
     |Image$$ZI$$Base| :ZI段起始地址
     |Image$$ZI$$Limit| :ZI段结束地址加1
   这些标号的值是根据链接器中对ro-base和rw-base的设置来计算得到的.
    为了保证程序可以正确运行,在运行的时候必须把可读写的 RW 段被
装载到 SDRAM 或者里.复制前后代码和数据段的分部如下图所示:




  Stage2、跳转到C语言程序,开始第二阶段的初始化和系统引导。
   
    1)、初始化堆栈。
    对6中工作模式进行堆栈的初始化。
    设置方法:
    改变CPSR内的状态位,使处理器切换到不同的状态,然后给SP赋值。
;****************************************************
;*The function for initializing stack *
;****************************************************
InitStacks
;Don't use DRAM,such as stmfd,ldmfd......
;SVCstack is initialized before
;Under toolkit ver 2.50, 'msr cpsr,r1' can be used instead of 'msr cpsr_cxsf,r1'
   
    mrs r0,cpsr
    bic r0,r0,#MODEMASK
    orr r1,r0,#UNDEFMODE|NOINT
    msr cpsr_cxsf,r1      ;undefMode
    ldr sp,=UndefStack

    orr r1,r0,#ABORTMODE|NOINT
    msr cpsr_cxsf,r1      ;AbortMode
    ldr sp,=AbortStack

    orr r1,r0,#IRQMODE|NOINT
    msr cpsr_cxsf,r1      ;IRQMode
    ldr sp,=IRQStack
   
    orr r1,r0,#FIQMODE|NOINT
    msr cpsr_cxsf,r1      ;FIQMode
    ldr sp,=FIQStack
   
    bic r0,r0,#MODEMASK|NOINT
    orr r1,r0,#SVCMODE
    msr cpsr_cxsf,r1      ;SVCMode
    ldr sp,=SVCStack

    ;USER mode is not initialized.
    Mov pc,lr       ;The LR register may be not valid for the mode changes.
   
    到此,BOOT程序的基本的分析算是完成。通过上面的分析,基本了解了BOOT的基本流程和相关知识。为下一步的初始化和跳转进入OS打下了坚实的基础。

    设置好堆栈后,跳转进入C的main函数。
      BL  Main
    int main(int argc, char *argv[])
{
    int ret;

    /* . */
    /*
     * Step 1:
     *  Print Vivi version information.
     */
    putstr("\r\n");
    putstr(vivi_banner);        //vivi_banner:[init/version.c] .

    reset_handler();            //reset_handler(void): [lib/reset_handle.c] .判断复位,根据硬复位和软复位分别处理。

    /*
     * Step 2:
     *   initialize board environment.
     */
    ret = board_init();         //[arch/s3c2410/smdk.c]  (1)init_time();[arch/s3c2410/proc.c] (2)set_gpios();[arch/s3c2410/smdk.c] .
    if (ret) {
        putstr("Failed a board_init() procedure\r\n");
        error();
    }

    /*
     * Step 3:
     *   MMU management      MMU初始化。
     *   When it's done,vivi is running on the ram and MMU is enabled.
     *  
     */
    mem_map_init();
    mmu_init();
    putstr("Succeed memory mapping.\r\n");

    /*
    /*
     * Now, vivi is running on the ram. MMU is enabled.
     */

    /*
     * Step 4:
     *   initialize the heap area.  堆初始化。
     */
    /* initialize the heap area*/
    ret = heap_init();              //heap_init(void); [lib/heap.c]  .
    if (ret) {
        putstr("Failed initailizing heap region\r\n");
        error();
    }

    /* Step 5:
     *  
     *  
     *    initialize the MTD device. step1-step5:完成设备初始化工作。
     *   
     */
    ret = mtd_dev_init();          //mtd_dev_init(void); [drivers/mtd/mtdcore.c]  .

    /* Step 6:
     *   initialize the private data. step6:为启动LINUX内核和传递参数做准备,把vivi的私有信息、内核启动参数、MTD分区信息
         *  等都放到特定的内存区域,等待后面两项重要工作使用(在step8完成,step7也是为step8服务的)。
     * 此部分的功能是把vivi可能用到的所有私有参数都放在预先规划的内存区域,大小为48K,基地址为0x33df0000。
         * 这48K区域分为三个组成部分:MTD参数、vivi parameter、LINUX启动命令。
         */
    init_priv_data();     //[lib/priv_data/rw.c]  .

    /* Step 7:
     *   initialize the humanmachine environment.
     */
    misc();

    init_builtin_cmds();     //[lib/command.c]  .

    /* Step 8:
     *   boot kernel or step into vivi.
     */
    boot_or_vivi();

   
    return 0;
}


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