TQ2440裸机启动代码分析

来源:互联网 发布:淘宝双十一下载 编辑:程序博客网 时间:2024/05/17 22:32
/* * forsakening @hdu @2013-5-30 * TQ2440出来好久了,启动代码别人也分析了一遍又一遍,有linux下的,有ADS的,各种!!! * 自己搞一份过来,一边听歌,一边分析,爽栽!!栽。。栽。。Pong。。。 * 里面关于ADS ARM汇编的语法没有介绍,只是介绍了各个阶段的主要功能 * 这份启动代码经TQ2440测试是完全有效的,ADS环境下编译通过,下载到NAND里面即可 * 代码地址(包含一简单ADS测试程序):2440init.s启动代码,nand.c完成代码搬运 * :http://download.csdn.net/detail/forsakening/5485315 *//* ADS汇编包含头文件 */    GET option.inc    /* 包含栈地址、中断服务程序基地址和系统时钟初始化相关常量 */GET memcfg.inc    /* SDRAM初始化相关定义 */GET 2440addr.inc    /* S3C2440处理器特殊功能寄存器地址 *//* 程序状态寄存器CPSR模式控制位进行定义 */;Pre-defined constantsUSERMODE    EQU 0x10FIQMODE     EQU 0x11IRQMODE     EQU 0x12SVCMODE     EQU 0x13ABORTMODE   EQU 0x17UNDEFMODE   EQU 0x1bMODEMASK    EQU 0x1fNOINT       EQU 0xc0/*  * 定义各个模式堆栈地址:理论上堆栈可分配在任何未使用的连续内存片, * 考虑到设置不当会产生溢出,所以一般将堆栈放在内存的高地址处 */;The location of stacksUserStackEQU(_STACK_BASEADDRESS-0x3800);0x33ff4800 ~SVCStackEQU(_STACK_BASEADDRESS-0x2800);0x33ff5800 ~UndefStackEQU(_STACK_BASEADDRESS-0x2400);0x33ff5c00 ~AbortStackEQU(_STACK_BASEADDRESS-0x2000);0x33ff6000 ~IRQStackEQU(_STACK_BASEADDRESS-0x1000);0x33ff7000 ~FIQStackEQU(_STACK_BASEADDRESS-0x0);0x33ff8000 ~/*  * 声明一个宏:将$HandleAddr表示的地址赋值给程序计数器PC, * 进而实现程序跳转到$HandleAddr指向的函数处去执行 * 如下面的HandlerIRQ HANDLER HandleIRQ,代表的就是跳转到HandleIRQ处去执行 * 实际上HandleIRQ是IRQ中断的服务程序 */ MACRO$HandlerLabel HANDLER $HandleAddr$HandlerLabelsub    sp,sp,#4;decrement sp(to store jump address)stmfdsp!,{r0};PUSH the work register to stack(lr does not push because it return to original address)ldr     r0,=$HandleAddr;load the address of HandleXXX to r0ldr     r0,[r0] ;load the contents(service routine start address) of HandleXXXstr     r0,[sp,#4]      ;store the contents(ISR) of HandleXXX to stackldmfd   sp!,{r0,pc}     ;POP the work register and pc(jump to ISR)MEND/*  * IMPORT伪操作告诉编译器该文件需要引用|Image$$RO$$Base|,Main等几个外部符号 * 相当于C语言中的extern * |Image$$RO$$Base|等是ADS编译器自动生成的标记各个段的起始结束地址 * ARM可执行程序包括RO RW ZI这三个段 */IMPORT  |Image$$RO$$Base|; Base of ROM codeIMPORT  |Image$$RO$$Limit|  ; End of ROM code (=start of ROM data)IMPORT  |Image$$RW$$Base|   ; Base of RAM to initialiseIMPORT  |Image$$ZI$$Base|   ; Base and limit of areaIMPORT  |Image$$ZI$$Limit|  ; to zero initialiseIMPORT  Main; The main entry of mon programIMPORT  RdNF2SDRAM; Copy Image from Nand Flash to SDRAM/* 1.程序主体,真正开始的地方,ADS编译器由ENTRY确定是代码开始位置 *//*  * 2.系统上电会执行b ResetHandler,而当发生异常或中断时, *   ARM处理器便强制把PC指针指向异常中断向量表对应的位置处 *   如发生软中断:b HandlerSWI *   如发生IRQ中断:b HandlerIRQ */AREA    Init,CODE,READONLYENTRYResetEntrybResetHandlerbHandlerUndef;handler for Undefined modebHandlerSWI;handler for SWI interruptbHandlerPabort;handler for PAbortbHandlerDabort;handler for DAbortb.;reservedbHandlerIRQ;handler for IRQ interruptbHandlerFIQ;handler for FIQ interrupt/*  * 前面定义宏的展开,HandlerIRQ HANDLER HandleIRQ, * 就是跳转到HandleIRQ对应的地址去执行程序,这就是所谓的"中断服务程序" */HandlerFIQHANDLER HandleFIQHandlerIRQHANDLER HandleIRQHandlerUndefHANDLER HandleUndefHandlerSWIHANDLER HandleSWIHandlerDabortHANDLER HandleDabortHandlerPabortHANDLER HandlePabort/* * 中断服务程序的二次查表:S3C2440有32个中断源 * 每个中断源发出中断请求时,都会执行b HandlerIRQ这条指令 * 为了区分具体是32个中的哪一个,需要进行二次查表,进而执行具体的中断服务程序 * 以下代码中:INTOFFSET(32位寄存器)是32个中断源中断偏移寄存器地址, * 读取这个寄存器的值即可知道具体是哪个中断源:如外部中断0 UART中断等 * EINT0(外部中断0)是INOFFSET的第0位,HandleEINT0代表了EINT0的服务程序 * add r8,r8,r9,lsl #2 :因为中断服务函数表中的每项占4个字节,所以r9左移2位, * 相当于乘以4,再加上HandleEINT0,即代表了具体某个中断源的服务函数 * IsrIRQ的调用在后面会有解释 */IsrIRQsubsp,sp,#4       ;reserved for PCstmfdsp!,{r8-r9}ldrr9,=INTOFFSETldrr9,[r9]ldrr8,=HandleEINT0addr8,r8,r9,lsl #2ldrr8,[r8]strr8,[sp,#8]ldmfdsp!,{r8-r9,pc}LTORG/* 正常启动执行的动作 */ResetHandler    /* 关闭看门狗 */ldrr0,=WTCON       ;watch dog disableldrr1,=0x0strr1,[r0]/* 关闭总中断 */ldrr0,=INTMSKldrr1,=0xffffffff  ;all interrupt disablestrr1,[r0]    /* 关闭子中断 */ldrr0,=INTSUBMSKldrr1,=0x7fff;all sub interrupt disablestrr1,[r0]/* 系统时钟初始化部分 */;To reduce PLL lock time, adjust the LOCKTIME register.ldrr0,=LOCKTIMEldrr1,=0xffffffstrr1,[r0]ldrr0,=CLKDIVNldrr1,=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.strr1,[r0];CLKDIV_VAL>1 means Fclk:Hclk is not 1:1.[ CLKDIV_VAL>1 ; means Fclk:Hclk is not 1:1.mrc p15,0,r0,c1,c0,0orr r0,r0,#0xc0000000;R1_nF:OR:R1_iAmcr p15,0,r0,c1,c0,0|mrc p15,0,r0,c1,c0,0bic r0,r0,#0xc0000000;R1_iA:OR:R1_nFmcr p15,0,r0,c1,c0,0];Configure UPLLldrr0,=UPLLCONldrr1,=((U_MDIV<<12)+(U_PDIV<<4)+U_SDIV);Fin = 12.0MHz, UCLK = 48MHzstrr1,[r0]nop; at least 7-clocks delay must be inserted for setting hardware be completed.nopnopnopnopnopnop;Configure MPLLldrr0,=MPLLCONldrr1,=((M_MDIV<<12)+(M_PDIV<<4)+M_SDIV);Fin = 12.0MHz, FCLK = 200MHzstrr1,[r0]/* 对SDRAM进行初始化 */;Set memory control registers  adrl r0, SMRDATA;be careful! ldmia r0,{r1-r13}ldrr0,=BWSCONstmia r0,{r1-r13}    /* 初始化堆栈,InitStacks在后面,bl:带返回地址的跳转指令 */blInitStacks;===========================================================/* * 区分是从nor还是nand启动:BWSCON寄存器反应了系统总线宽度的信息 * and指令测试某几位是0还是1,s用于表示该指令运算结果影响标志位 * 如注释所说“OM[1:0] != 0, NOR FLash boot”,从nor启动时BWSCON的低三位为010,和6做与运算得到非0 * bne:若不为0,说明是从nor启动,则执行copy_proc_beg * 否则执行下一语句:nand启动 * nand启动时:adr r0, ResetEntry,这句比较难以理解,总之执行完这句话后r0 = 0 * 所以接下来的bnecopy_proc_beg并不会执行(nand启动方式下) */ldrr0, =BWSCONldrr0, [r0]andsr0, r0, #6;OM[1:0] != 0, NOR FLash bootbnecopy_proc_beg;do not read nand flashadrr0, ResetEntry;OM[1:0] == 0, NAND FLash bootcmpr0, #0;if use Multi-ice, bnecopy_proc_beg;do not read nand flash for boot;nop;===========================================================/* * nand_boot_beg:标号,相当于函数名 * RdNF2SDRAM:read nand-flash to sdram将程序从nand搬运到sdram中,C语言实现, * 所以前面用IMPORT声明了一下,非本文件的函数符号 * ldrpc, =copy_proc_beg 将copy_proc_beg的绝对地址赋给PC * 绝对地址 = 链接地址(TQ2440为0x30000000) + 偏移量 * 所以,在这句话之后,PC已经指向了SDRAM中的copy_proc_beg,而非从nand复制到steppingstone里面的copy_proc_beg了 */nand_boot_begbl RdNF2SDRAMldrpc, =copy_proc_beg;===========================================================/* * copy_proc_beg:将程序从加载域copy到运行域 * adr r0, ResetEntry将ResetEntry的相对地址赋给r0,实际上0 * BaseOfROM在后面会有定义,由ADS编译器自动生成:代表了运行域的base地址,实际上是0x300000000,也是RO段的开始地址; * 所以这两句不会执行 * ldreq r0, TopOfROM * beq InitRam * ldr r3, TopOfROM:代表运行域中RO段的结束地址 * 标号0的行为是将RO段从加载域复制到运行域 */copy_proc_begadrr0, ResetEntryldrr2, BaseOfROMcmpr0, r2ldreqr0, TopOfROMbeqInitRamldr r3, TopOfROM0ldmiar0!, {r4-r7}stmiar2!, {r4-r7}cmpr2, r3bcc%B0        /* r2 = r2 -r3; r0 = r0 - r2,此时r0是RW段的起始地址 */subr2, r2, r3subr0, r0, r2/* * 标号0:完成RW段的从加载域至运行域的搬移 * 标号1:完成BSS段的初始化 */InitRamldrr2, BaseOfBSSldrr3, BaseOfZero0cmpr2, r3ldrccr1, [r0], #4strccr1, [r2], #4bcc%B0movr0,#0ldrr3,EndOfBSS1cmpr2,r3strccr0, [r2], #4bcc%B1;===========================================================    /* * str r1,[r0]:将IsrIRQ地址赋值给HandleIRQ地址里,就是说执行HandleIRQ会执行IsrIRQ */  ; Setup IRQ handlerldrr0,=HandleIRQ;This routine is neededldrr1,=IsrIRQ;if there is not 'subs pc,lr,#4' at 0x18, 0x1cstrr1,[r0]    /* 跳转到Main()函数,C语言实现,前述IMPORT声明过 */ bMain;Do not use main() because ....../* * 初始化各个模式的堆栈:1.更改工作模式,用到CPSR *                       2.对应工作模式下的SP寄存器是私有的 */InitStacksmrsr0,cpsrbicr0,r0,#MODEMASKorrr1,r0,#UNDEFMODE|NOINTmsrcpsr_cxsf,r1;UndefModeldrsp,=UndefStack; UndefStack=0x33FF_5C00orrr1,r0,#ABORTMODE|NOINTmsrcpsr_cxsf,r1;AbortModeldrsp,=AbortStack; AbortStack=0x33FF_6000orrr1,r0,#IRQMODE|NOINTmsrcpsr_cxsf,r1;IRQModeldrsp,=IRQStack; IRQStack=0x33FF_7000orrr1,r0,#FIQMODE|NOINTmsrcpsr_cxsf,r1;FIQModeldrsp,=FIQStack; FIQStack=0x33FF_8000bicr0,r0,#MODEMASK|NOINTorrr1,r0,#SVCMODEmsrcpsr_cxsf,r1;SVCModeldrsp,=SVCStack; SVCStack=0x33FF_5800;USER mode has not be initialized.movpc,lr;The LR register will not be valid if the current mode is not SVC mode.LTORG/* 初始化SDRAM的一些参数,DCD用于分配一块连续的内存单元 */SMRDATA DCD 0x22011000DCD 0x00000700   ;GCS0DCD 0x00000700   ;GCS1DCD 0x00000700   ;GCS2DCD 0x00000700   ;GCS3DCD 0x00000700   ;GCS4DCD 0x00000700   ;GCS5DCD ((B6_MT<<15)+(B6_Trcd<<2)+(B6_SCAN))    ;GCS6DCD ((B7_MT<<15)+(B7_Trcd<<2)+(B7_SCAN))    ;GCS7DCD ((REFEN<<23)+(TREFMD<<22)+(Trp<<20)+(Tsrc<<18)+REFCNT);DCD 0x32    ;SCLK power saving mode, BANKSIZE 64M/64MDCD 0xB1DCD 0x30    ;MRSR6 CL=3clkDCD 0x30    ;MRSR7 CL=3clk/* 编译器自动生成的几个参数进行引用 */BaseOfROMDCD|Image$$RO$$Base|TopOfROMDCD|Image$$RO$$Limit|BaseOfBSSDCD|Image$$RW$$Base|BaseOfZeroDCD|Image$$ZI$$Base|EndOfBSSDCD|Image$$ZI$$Limit|/* * 定义了一张内存表: * 启动代码中的标号    内存地址 * HandleReset         0x33ffff00 * HandleUndef         0x33ffff04 *  ...                  .... * HandleFIQ           0x33ffff1C * HandleEINT0         0x33ffff20 * 码字不容易:http://blog.csdn.net/forsakening/article/details/8996343 */ALIGNAREA RamData, DATA, READWRITE^   _ISR_STARTADDRESS; _ISR_STARTADDRESS=0x33FF_FF00HandleReset #   4HandleUndef #   4HandleSWI#   4HandlePabort    #   4HandleDabort    #   4HandleReserved  #   4HandleIRQ#   4HandleFIQ#   4;IntVectorTable  ;@0x33FF_FF20HandleEINT0#   4HandleEINT1#   4HandleEINT2#   4HandleEINT3#   4HandleEINT4_7#   4HandleEINT8_23#   4HandleCAM#   4HandleBATFLT#   4HandleTICK#   4HandleWDT#   4HandleTIMER0 #   4HandleTIMER1 #   4HandleTIMER2 #   4HandleTIMER3 #   4HandleTIMER4 #   4HandleUART2  #   4;@0x33FF_FF60HandleLCD #   4HandleDMA0#   4HandleDMA1#   4HandleDMA2#   4HandleDMA3#   4HandleMMC#   4HandleSPI0#   4HandleUART1#   4HandleNFCON#   4HandleUSBD#   4HandleUSBH#   4HandleIIC#   4HandleUART0 #   4HandleSPI1 #   4HandleRTC #   4HandleADC #   4;@0x33FF_FFA0END

 

后记:

1) 这是基于ADS开发环境的启动代码,我参考的是王小强大大的《ARM处理器裸机开发实战》,在此多谢王大大了!其实基于linux的启动代码更加容易理解,但是汇编的编写风格又是不一样,uboot就是在linux环境下编译的。

2) 若这里面有些代码不是太明白,可以通过ADS的反汇编来查看,这个比较容易理解。 

原创粉丝点击