基于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_<MODE> R14_<MODE> 其中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-UC75的 CS(片选管脚)接到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 或者里.复制前后代码和数据段的分部如下图所示: <?XML:NAMESPACE PREFIX = V /><?XML:NAMESPACE PREFIX = O /><!--[if !vml]--><!--[endif]--> 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; }
关闭
站长推荐
/3
文章评论(0条评论)
登录后参与讨论