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的反汇编来查看,这个比较容易理解。
- TQ2440裸机启动代码分析
- tq2440 启动代码分析
- TQ2440启动代码分析笔记
- 2440裸机启动代码分析
- TQ2440裸机上的UART程序分析
- TQ2440裸奔程序>>2440init.s启动代码分析
- S3C2440裸机驱动--启动代码
- tq2440 裸机使用mmu
- TQ2440裸机MMU
- 基于TQ2440的ARM启动代码注释分析(基于MDK)
- TQ2440 学习一 启动代码的解释
- 自用的ARM9(tq2440)的启动代码
- TQ2440裸机跑NRF24l01模块
- TQ2440裸机中断(外部中断)
- TQ2440裸机开发实例笔记
- tq2440 的dma裸机驱动
- tq2440裸机开发流程总结
- 给S5PV210裸机程序添加启动代码
- 黑马程序员——java语法二
- csapp2e 家庭作业 3.56
- auto_ptr源码实现
- [C# 线程处理系列]专题五:线程同步
- 1.5 编写代码将字符串中的空格替换为‘%20 ’
- TQ2440裸机启动代码分析
- 第十二堂课后作业
- html中的一些操作记录
- A + B Problem + 数论
- C#读取文件流
- ubuntu 12.04 没有声音
- [C# 线程处理系列]专题六:线程同步——事件构造
- 学习使用 VS 2010 自带报表
- DRS_DDS容灾数据库数据回滚原理整理