最后发两个在网上下载的ARM S3C44B0的注释文件,是转载别人的。希望对各位有用。<br /><br />2006/7/22<br />44B0XINIT注释<br />******************************************************* <br />; * NAME : 44BINIT.S * <br />; * Version : 10.JAn.2003 * <br />; * Description: * <br />; * C start up codes * <br />; * Configure memory, Initialize ISR ,stacks * <br />; * Initialize C-variables * <br />; * Fill zeros into zero-initialized C-variables * <br />; ******************************************************* <br />GET option.s ;相当于c语言中的#include 'option.s' <br />GET memcfg.s <br />;Interrupt Control /P154<br />;声明一些符号常量,这些符号常量和地址相应寄存器的地址对应 <br />INTPND EQU 0x01e00004 ;指示中断请求状态寄存器 每一位代变一种中断请求具体表示哪一种中断请参考44b0 spec <br />INTMOD EQU 0x01e00008 ;中断模式寄存器 有两种中断模式对应位为1代表fip mode 0代表riq mode <br />INTMSK EQU 0x01e0000c ;确定哪个中断源被屏蔽 屏蔽的中断源将不被服务 <br />I_ISPR EQU 0x01e00020 ;中断服务挂起寄存器 <br />I_CMST EQU 0x01e0001c ;当前主寄存器irq优先级 <br />;Watchdog timer <br />WTCON EQU 0x01d30000 ;看门狗定时器控制寄存器 <br />;Clock Controller <br />PLLCON EQU 0x01d80000 ; pll控制寄存器 <br />CLKCON EQU 0x01d80004 ;时钟控制寄存器 <br />LOCKTIME EQU 0x01d8000c ;锁定时间计数值寄存器 <br />;Memory Controller <br />REFRESH EQU 0x01c80024 ; Dram/sdram刷新控制寄存器 <br />;下面是对arm处理器模式寄存器对应值的常数定义,arm处理器中有一个CPSR程序状态寄存器 它的后五位决定目前的处理器模式 <br />; pre-defined constants <br />USERMODE EQU 0x10 ;0b10000用户模式 <br />FIQMODE EQU 0x11 ;0b10001FIQ模式 <br />IRQMODE EQU 0x12 ;0b10010IRQ模式 <br />SVCMODE EQU 0x13 ;0b10011管理模式 <br />ABORTMODE EQU 0x17 ;0b10111中止模式 <br />UNDEFMODE EQU 0x1b ;0b11011未定义 <br />MODEMASK EQU 0x1f ;0b11111系统模式 <br />NOINT EQU 0xc0 ; <br />;check if tasm.exe is used. <br />;arm处理器有两种工作状态 1.arm:32位 这种工作状态下执行字对准的arm指令 2.Thumb:16位 这种工作状;态执行半字对准的Thumb指令 <br />;因为处理器分为16位 32位两种工作状态 程序的编译器也是分16位和32两种编译方式 所以下面的程序用;于根据处理器工作状态确定编译器编译方式 <br />;code16伪指令指示汇编编译器后面的指令为16位的thumb指令 <br />;code32伪指令指示汇编编译器后面的指令为32位的arm指令 <br />;这段是为了统一目前的处理器工作状态和软件编译方式(16位编译环境使用tasm.exe编译) <br />GBLL THUMBCODE ;设置一个全局逻辑变量 <br />[ {CONFIG} = 16 ;if config==16 这里表示你的目前处于领先地16位编译方式 <br />THUMBCODE SETL {TRUE} ;设置THUMBCODE 为 true <br />CODE32 ;转入32位编译模式 <br />| 次 ;else <br />THUMBCODE SETL {FALSE} ;设置THUMBCODE 为 false <br />] <br />[ THUMBCODE ;if THUMBCODE==TRUE <br />CODE32 ;for start-up code for Thumb mode;转入32位编译方式 <br />] <br />;注意下面这段程序是个宏定义 很多人对这段程序不理解 我再次强调这是一个宏定义 所以大家要注意了;下面包含的HandlerXXX HANDLER HandleXXX将都被下面这段程序展开 <br />;这段程序用于把中断服务程序的首地址装载到pc中,有人称之为“加载程序”。 <br />;本初始化程序定义了一个数据区(在文件最后),34个字空间,存放相应中断服务程序的首地址。每个字;空间都有一个标号,以Handle***命名。 <br />;在向量中断模式下使用“加载程序”来执行中断服务程序。 <br />;这里就必须讲一下向量中断模式和非向量中断模式的概念 <br />;向量中断模式是当cpu读取位于0x18处的IRQ中断指令的时候,系统自动读取对应于该中断源确定地址上的;指令取代0x18处的指令,通过跳转指令系统就直接跳转到对应地址 <br />;函数中 节省了中断处理时间提高了中断处理速度标 例如 ADC中断的向量地址为0xC0,则在0xC0处放如下;代码:ldr PC,=HandlerADC 当ADC中断产生的时候系统会 <br />;自动跳转到HandlerADC函数中 <br />;非向量中断模式处理方式是一种传统的中断处理方法,当系统产生中断的时候,系统将interrupt ; pending寄存器中对应标志位置位 然后跳转到位于0x18处的统一中断 <br />;函数中 该函数通过读取interrupt pending寄存器中对应标志位 来判断中断源 并根据优先级关系再跳到;对应中断源的处理代码中 <br />MACRO <br />$HandlerLabel HANDLER $HandleLabel <br />$HandlerLabel <br />sub sp,sp,#4 ; Decrement sp(to store jump address) <br />stmfd sp!,{r0} ; pUSH the work register to stack <br />;将要使用的r0寄存器入栈 <br />ldr r0,=$HandleLabel; load the address of HandleXXX to r0 <br />ldr r0,[r0] ; load the contents(service routine start address) of HandleXXX <br />str r0,[sp,#4] ;store the contents(ISR) of HandleXXX to stack <br />;将对应的中断函数首地址入栈 <br />ldmfd sp!,{r0,pc} ; pOP the work register and pc(jump to ISR) <br />;将中断函数首地址出栈 放入程序指针中 系统将跳转到对应中断处理函数 <br />MEND <br />;一个arm由RO,RW,ZI三个断组成 其中RO为代码段,RW是已经初始化的全局变量,ZI是未初始化的全局变量;(对于GNU工具 对应的概念是TEXT ,DATA,BSS)bootloader <br />;bootloader要将RW段复制到ram中并将ZI段清零 编译器使用下列段来记录各段的起始和结束地址 <br />; |Image$$RO$$Base| ; RO段起始地址 <br />; |Image$$RO$$Limit| ; RO段结束地址加1 <br />; |Image$$RW$$Base| ; RW段起始地址 <br />; |Image$$RW$$Limit| ; RW段结束地址加1 <br />; |Image$$ZI$$Base| ; ZI段起始地址 <br />; |Image$$ZI$$Limit| ; ZI段结束地址加1 <br />;这些标号的值是通过编译器的设定来确定的 如编译软件中对ro-base和rw-base的设定,例如 ro-;base=0xc000000 rw-base=0xc5f0000 <br /><br />IMPORT |Image$$RO$$Limit| ; End of ROM code (=start of ROM data) <br />IMPORT |Image$$RW$$Base| ; Base of RAM to initialise <br />IMPORT |Image$$ZI$$Base| ; Base and limit of area <br />IMPORT |Image$$ZI$$Limit| ; to zero initialise <br />IMPORT Main ; The main entry of mon program <br />;下面为代码段 <br />AREA Init,CODE,READONLY <br />;异常中断矢量表(每个表项占4个字节) 下面是中断向量表 一旦系统运行时有中断发生 即使移植了操作;系统 如linux 处理器已经把控制权;交给了操作系统 一旦发生中断 处理器还是会跳转到从0x0开始 <br />;中断向量表中某个中断表项(依据中断类型)开始执行 <br />;具体中断向量布局请参考s3c44b0 spec 例如 adc中断向量为 0x000000c0下面对应表中第49项位置 向量地址0x0+4*(49-1)=0x000000c0 <br />ENTRY <br />;板子上电和复位后 程序开始从位于0x0处开始执行硬件刚刚上电复位后 程序从这里开始执行跳转到标号为ResetHandler处执行 <br />b ResetHandler ;for debug <br />b HandlerUndef ;handlerUndef <br />b HandlerSWI ;SWI interrupt handler <br />b HandlerPabort ;handlerPAbort <br />b HandlerDabort ;handlerDAbort <br />b . ;handlerReserved <br />b HandlerIRQ <br />b HandlerFIQ <br />;***IMPORTANT NOTE*** <br />;If the H/W vectored interrutp mode is enabled, The above two instructions should <br />;be changed like below, to work-around with H/W bug of S3C44B0X interrupt controller. <br />; b HandlerIRQ -> subs pc,lr,#4 <br />; b HandlerIRQ -> subs pc,lr,#4 <br />VECTOR_BRANCH <br />ldr pc,=HandlerEINT0 ;mGA H/W interrupt vector table <br />ldr pc,=HandlerEINT1 ; <br />ldr pc,=HandlerEINT2 ; <br />ldr pc,=HandlerEINT3 ; <br />ldr pc,=HandlerEINT4567 ; <br />ldr pc,=HandlerTICK ;mGA <br />b . <br />b . <br />ldr pc,=HandlerZDMA0 ;mGB <br />ldr pc,=HandlerZDMA1 ; <br />ldr pc,=HandlerBDMA0 ; <br />ldr pc,=HandlerBDMA1 ; <br />ldr pc,=HandlerWDT ; <br />ldr pc,=HandlerUERR01 ;mGB <br />b . <br />b . <br />ldr pc,=HandlerTIMER0 ;mGC <br />ldr pc,=HandlerTIMER1 ; <br />ldr pc,=HandlerTIMER2 ; <br />ldr pc,=HandlerTIMER3 ; <br />ldr pc,=HandlerTIMER4 ; <br />ldr pc,=HandlerTIMER5 ;mGC <br />b . <br />b . <br />ldr pc,=HandlerURXD0 ;mGD <br />ldr pc,=HandlerURXD1 ; <br />ldr pc,=HandlerIIC ; <br />ldr pc,=HandlerSIO ; <br />ldr pc,=HandlerUTXD0 ; <br />ldr pc,=HandlerUTXD1 ;mGD <br />b . <br />b . <br />ldr pc,=HandlerRTC ;mGKA <br />b . ; <br />b . ; <br />b . ; <br />b . ; <br />b . ;mGKA <br />b . <br />b . <br />ldr pc,=HandlerADC ;mGKB <br />b . ; <br />b . ; <br />b . ; <br />b . ; <br />b . ;mGKB <br />b . <br />b . <br />;0xe0=EnterPWDN <br />ldr pc,=EnterPWDN <br />LTORG <br />;下面是具体的中断处理函数跳转的宏,通过上面的$HandlerLabel的宏定义展开后跳转到对应的中断处理;函数(对于向量中断) <br />HandlerFIQ HANDLER HandleFIQ <br />HandlerIRQ HANDLER HandleIRQ <br />HandlerUndef HANDLER HandleUndef <br />HandlerSWI HANDLER HandleSWI <br />HandlerDabort HANDLER HandleDabort <br />HandlerPabort HANDLER HandlePabort <br />HandlerADC HANDLER HandleADC <br />HandlerRTC HANDLER HandleRTC <br />HandlerUTXD1 HANDLER HandleUTXD1 <br />HandlerUTXD0 HANDLER HandleUTXD0 <br />HandlerSIO HANDLER HandleSIO <br />HandlerIIC HANDLER HandleIIC <br />HandlerURXD1 HANDLER HandleURXD1 <br />HandlerURXD0 HANDLER HandleURXD0 <br />HandlerTIMER5 HANDLER HandleTIMER5 <br />HandlerTIMER4 HANDLER HandleTIMER4 <br />HandlerTIMER3 HANDLER HandleTIMER3 <br />HandlerTIMER2 HANDLER HandleTIMER2 <br />HandlerTIMER1 HANDLER HandleTIMER1 <br />HandlerTIMER0 HANDLER HandleTIMER0 <br />HandlerUERR01 HANDLER HandleUERR01 <br />HandlerWDT HANDLER HandleWDT <br />HandlerBDMA1 HANDLER HandleBDMA1 <br />HandlerBDMA0 HANDLER HandleBDMA0 <br />HandlerZDMA1 HANDLER HandleZDMA1 <br />HandlerZDMA0 HANDLER HandleZDMA0 <br />HandlerTICK HANDLER HandleTICK <br />HandlerEINT4567 HANDLER HandleEINT4567 <br />HandlerEINT3 HANDLER HandleEINT3 <br />HandlerEINT2 HANDLER HandleEINT2 <br />HandlerEINT1 HANDLER HandleEINT1 <br />HandlerEINT0 HANDLER HandleEINT0 <br /><br />;One of the following two routines can be used for non-vectored interrupt. <br />;下面这段程序是用来处理非向量中断,具体判断I_ISPR中各位是否置1 置1表示目前此中断等待响应(每次只能有一位置1),从最高优先级中断位开始判断,检测到等待服务 <br />;中断就将pc置为中断服务函数首地址 <br />IsrIRQ ;using I_ISPR register. <br />sub sp,sp,#4 ;reserved for PC <br />stmfd sp!,{r8-r9} <br />;IMPORTANT CAUTION <br />;if I_ISPC isn't used properly, I_ISPR can be 0 in this routine. <br />ldr r9,=I_ISPR <br />ldr r9,[r9] <br />mov r8,#0x0 <br />0 <br />movs r9,r9,lsr #1 <br />bcs %F1 <br />add r8,r8,#4 <br />b %B0 <br />1 <br />ldr r9,=HandleADC <br />add r9,r9,r8 <br />ldr r9,[r9] <br />str r9,[sp,#8] <br />ldmfd sp!,{r8-r9,pc} <br /><br />;**************************************************** <br />;* START * <br />;**************************************************** <br />;扳子上电和复位后 程序开始从位于0x0执行b ResetHandler 程序从跳转到这里执行 <br />;板子上电复位后 执行几个步骤这里通过标号在注释中加1,2,3....标示 标号表示执行顺序 <br />;1.禁止看门狗 屏蔽所有中断 <br />ResetHandler <br />ldr r0,=WTCON ;watch dog disable <br />ldr r1,=0x0 <br />str r1,[r0] <br />ldr r0,=INTMSK <br />ldr r1,=0x07ffffff ;all interrupt disable <br />str r1,[r0] <br />;2.根据工作频率设置pll <br />;这里介绍一下计算公式 <br />;Fpllo=(m*Fin)/(p*2^s) <br />;m=MDIV+8,p=PDIV+2,s=SDIV <br />;Fpllo必须大于20Mhz小于66Mhz <br />;Fpllo*2^s必须小于170Mhz <br />;如下面的PLLCON设定中的M_DIV P_DIV S_DIV是取自option.h中 <br />;#elif (MCLK==40000000) <br />;#define PLL_M (0x48) <br />;#define PLL_P (0x3) <br />;#define PLL_S (0x2) <br />;所以m=MDIV+8=80,p=PDIV+2=5,s=SDIV=2 <br />;硬件使用晶振为10Mhz,即Fin=10Mhz <br />;Fpllo=80*10/5*2^2=40Mhz <br />;**************************************************** <br />;* Set clock control registers * <br />;**************************************************** <br />ldr r0,=LOCKTIME <br />ldr r1,=800 ; count = t_lock * Fin (t_lock=200us, Fin='4MHz') = 800 <br />str r1,[r0] <br />[ PLLONSTART <br />ldr r0,=PLLCON ;temporary setting of PLL <br />ldr r1,=((M_DIV<<12)+(P_DIV<<4)+S_DIV) ;Fin=10MHz,Fout=40MHz <br />str r1,][r0] <br />] <br />ldr r0,=CLKCON <br />ldr r1,=0x7ff8 ;All unit block CLK enable <br />str r1,[r0] <br />;3.置存储相关寄存器的程序 <br />;这是设置SDRAM,flash ROM 存储器连接和工作时序的程序,片选定义的程序 <br />;SMRDATA map在下面的程序中定义 <br />;SMRDATA中涉及的值请参考memcfg.s程序 <br />;具体寄存器各位含义请参考s3c44b0 spec <br />;**************************************************** <br />;* Set memory control registers * <br />;**************************************************** <br />ldr r0,=SMRDATA <br />ldmia r0,{r1-r13} <br />ldr r0,=0x01c80000 ;BWSCON Address <br />stmia r0,{r1-r13} <br />;**************************************************** <br />;* Initialize stacks * <br />;**************************************************** <br />ldr sp, =SVCStack ;Why? <br />bl InitStacks <br />;5.设置缺省中断处理函数 <br />;**************************************************** <br />;* Setup IRQ handler * <br />;**************************************************** <br />ldr r0,=HandleIRQ ;This routine is needed <br />ldr r1,=IsrIRQ ;if there isn't 'subs pc,lr,#4' at 0x18, 0x1c <br />str r1,[r0] <br />;6.将数据段拷贝到ram中 将零初始化数据段清零 跳入C语言的main函数执行 到这步结束bootloader初步引导结束 <br />;******************************************************** <br />;* Copy and paste RW data/zero initialized data * <br />;******************************************************** <br />LDR r0, =|Image$$RO$$Limit| ; Get pointer to ROM data <br />LDR r1, =|Image$$RW$$Base| ; and RAM copy <br />LDR r3, =|Image$$ZI$$Base| <br />;Zero init base => top of initialised data <br />CMP r0, r1 ; Check that they are different <br />BEQ %F1 <br />0 <br />CMP r1, r3 ; Copy init data <br />LDRCC r2, [r0], #4 ;--> LDRCC r2, [r0] + ADD r0, r0, #4 <br />STRCC r2, [r1], #4 ;--> STRCC r2, [r1] + ADD r1, r1, #4 <br />BCC %B0 <br />1 <br />LDR r1, =|Image$$ZI$$Limit| ; Top of zero init segment <br />MOV r2, #0 <br />2 <br />CMP r3, r1 ; Zero init <br />STRCC r2, [r3], #4 <br />BCC %B2 <br />[ : lNOT:THUMBCODE <br />BL Main ; Don't use main() because ......;跳入main函数 <br />B . <br />] <br />[ THUMBCODE ;for start-up code for Thumb mode <br />orr lr,pc,#1 <br />bx lr <br />CODE16 <br />bl Main ; Don't use main() because ......;跳入main函数 <br />b . <br />CODE32 <br />] <br />;4.初始化各模式下的栈指针 <br />;**************************************************** <br />;* The function for initializing stack * <br />;**************************************************** <br />InitStacks <br />; Don't use DRAM,such as stmfd,ldmfd...... <br />;SVCstack is initialized before <br />;Under toolkit ver 2.50, 'msr cpsr,r1' can be used instead of 'msr cpsr_cxsf,r1' <br />mrs r0,cpsr <br />bic r0,r0,#MODEMASK <br />orr r1,r0,#UNDEFMODE|NOINT <br />msr cpsr_cxsf,r1 ;UndefMode <br />ldr sp,=UndefStack <br />orr r1,r0,#ABORTMODE|NOINT <br />msr cpsr_cxsf,r1 ;AbortMode <br />ldr sp,=AbortStack <br />orr r1,r0,#IRQMODE|NOINT <br />msr cpsr_cxsf,r1 ;IRQMode <br />ldr sp,=IRQStack <br />orr r1,r0,#FIQMODE|NOINT <br />msr cpsr_cxsf,r1 ;FIQMode <br />ldr sp,=FIQStack <br />bic r0,r0,#MODEMASK|NOINT <br />orr r1,r0,#SVCMODE <br />msr cpsr_cxsf,r1 ;SVCMode <br />ldr sp,=SVCStack <br />;USER mode is not initialized. <br />mov pc,lr ;The LR register may be not valid for the mode changes. <br />;下面是pwdn模式下的相关寄存器的定义 <br />;**************************************************** <br />;* The function for entering power down mode * <br />;**************************************************** <br />;void EnterPWDN(int CLKCON); <br />EnterPWDN <br />mov r2,r0 ;r0=CLKCON <br />ldr r0,=REFRESH <br />ldr r3,[r0] <br />mov r1, r3 <br />orr r1, r1, #0x400000 ;self-refresh enable <br />str r1, [r0] <br />nop ;Wait until self-refresh is issued. May not be needed. <br />nop ;If the other bus master holds the bus, ... <br />nop ; mov r0, r0 <br />nop <br />nop <br />nop <br />nop <br />;enter POWERDN mode <br />ldr r0,=CLKCON <br />str r2,[r0] <br />;wait until enter SL_IDLE,STOP mode and until wake-up <br />mov r0,#0xff <br />0 subs r0,r0,#1 <br />bne %B0 <br />;exit from DRAM/SDRAM self refresh mode. <br />ldr r0,=REFRESH <br />str r3,[r0] <br />mov pc,lr <br />LTORG <br />;这是上面提到的对存储寄存器初始化的数据map <br />SMRDATA DATA <br />;***************************************************************** <br />;* Memory configuration has to be optimized for best performance * <br />;* The following parameter is not optimized. * <br />;***************************************************************** <br />;*** memory access cycle parameter strategy *** <br />; 1) Even FP-DRAM, EDO setting has more late fetch point by half-clock <br />; 2) The memory settings,here, are made the safe parameters even at 66Mhz. <br />; 3) FP-DRAM Parameters:tRCD=3 for tRAC, tcas='2' for pad delay, tcp='2' for bus load. <br />; 4) DRAM refresh rate is for 40Mhz. <br /><br />DCD 0x11110090 ;Bank0=OM[1:0], Bank1~Bank7=16bit, bank2=8bit; <br />DCD ((B0_Tacs<<13)+(B0_Tcos<<11)+(B0_Tacc<<8)+(B0_Tcoh<<6)+(B0_Tah<<4)+(B0_Tacp<<2)+(B0_PMC)) ;GCS0 <br />DCD ((B1_Tacs<<13)+(B1_Tcos<<11)+(B1_Tacc<<8)+(B1_Tcoh<<6)+(B1_Tah<<4)+(B1_Tacp<<2)+(B1_PMC)) ;GCS1 <br />DCD ((B2_Tacs<<13)+(B2_Tcos<<11)+(B2_Tacc<<8)+(B2_Tcoh<<6)+(B2_Tah<<4)+(B2_Tacp<<2)+(B2_PMC)) ;GCS2 <br />DCD ((B3_Tacs<<13)+(B3_Tcos<<11)+(B3_Tacc<<8)+(B3_Tcoh<<6)+(B3_Tah<<4)+(B3_Tacp<<2)+(B3_PMC)) ;GCS3 <br />DCD ((B4_Tacs<<13)+(B4_Tcos<<11)+(B4_Tacc<<8)+(B4_Tcoh<<6)+(B4_Tah<<4)+(B4_Tacp<<2)+(B4_PMC)) ;GCS4 <br />DCD ((B5_Tacs<<13)+(B5_Tcos<<11)+(B5_Tacc<<8)+(B5_Tcoh<<6)+(B5_Tah<<4)+(B5_Tacp<<2)+(B5_PMC)) ;GCS5 <br />DCD ((B6_MT<<15)+(B6_Trcd<<2)+(B6_SCAN)) ;GCS6 <br />DCD ((B7_MT<<15)+(B7_Trcd<<2)+(B7_SCAN)) ;GCS7 <br />DCD ((REFEN<<23)+(TREFMD<<22)+(Trp<<20)+(Trc<<18)+(Tchr<<16)+REFCNT) ;REFRESH RFEN='1', TREFMD='0', trp='3clk', trc='5clk', tchr='3clk',count=1019 <br />DCD 0x16 ;SCLK power mode, BANKSIZE 32M/32M <br />DCD 0x20 ;MRSR6 CL='2clk' <br />DCD 0x20 ;MRSR7 <br />ALIGN <br />;下面是对ram区域map的定义 <br />AREA RamData, DATA, READWRITE <br />;这里定义了处理器工作于各模式的堆栈区在ram中map <br />^ (_ISR_STARTADDRESS-0x500) <br />UserStack # 256 ;c1(c7)ffa00 <br />SVCStack # 256 ;c1(c7)ffb00 <br />UndefStack # 256 ;c1(c7)ffc00 <br />AbortStack # 256 ;c1(c7)ffd00 <br />IRQStack # 256 ;c1(c7)ffe00 <br />FIQStack # 0 ;c1(c7)fff00 <br />;这里将中断异常向量建立在sdram中 <br />^ _ISR_STARTADDRESS <br />HandleReset # 4 <br />HandleUndef # 4 <br />HandleSWI # 4 <br />HandlePabort # 4 <br />HandleDabort # 4 <br />HandleReserved # 4 <br />HandleIRQ # 4 <br />HandleFIQ # 4 <br />; Don't use the label 'IntVectorTable', <br />;because armasm.exe cann't recognize this label correctly. <br />;the value is different with an address you think it may be. <br />;IntVectorTable <br />HandleADC # 4 <br />HandleRTC # 4 <br />HandleUTXD1 # 4 <br />HandleUTXD0 # 4 <br />HandleSIO # 4 <br />HandleIIC # 4 <br />HandleURXD1 # 4 <br />HandleURXD0 # 4 <br />HandleTIMER5 # 4 <br />HandleTIMER4 # 4 <br />HandleTIMER3 # 4 <br />HandleTIMER2 # 4 <br />HandleTIMER1 # 4 <br />HandleTIMER0 # 4 <br />HandleUERR01 # 4 <br />HandleWDT # 4 <br />HandleBDMA1 # 4 <br />HandleBDMA0 # 4 <br />HandleZDMA1 # 4 <br />HandleZDMA0 # 4 <br />HandleTICK # 4 <br />HandleEINT4567 # 4 <br />HandleEINT3 # 4 <br />HandleEINT2 # 4 <br />HandleEINT1 # 4 <br />HandleEINT0 # 4 ;0xc1(c7)fff84 <br />END <br /><br /><br />这是另外一个人写的体会:<br /><br />ARM(44b0)初学者总结(转载)<br /> 我的开发板是s3c44b0x的, 2m NOR FLASH在bank0,8m sdram在bank6.首先看看我们要解决的问题。有些ARM芯片有内嵌的RAM 和FALSH.这样可以直接在片内运行程序,44B0X片内只有几K CACHE,ROM和RAM都是外接的芯片。我们的程序是要写入FLASH中保存,但执行时是拷到SDRAM中执行的(如在ROM中执行速度会较慢)。要做到这一点需要把程序做成两个分程序:一个是实现你的系统功能的主程序,如果你用嵌入式系统,那就是UCOS和UCLINUX之类的程序,这个程序的代码保存在FLASH中,但执行时会拷到RAM中再执行;一个是引导程序,直接在FLASH中执行,负责把初始化芯片和外设,并把主程序从FLASH中拷到 RAM中,然后跳到主程序去执行,对应的概念是UBOOT等常见的引导程序,这个程序会被写入0X0开始的地址,开机后自动执行。<br /> 那么我们需要解决以下几个问题:<br /> 1.如何编译和调试主程序<br /> 2.如何使中断跳到RAM中的中断服务程序执行<br /> 3.如何把引导程序和主程序写入FLASH中.<br />以下我们来解决这几个问题:<br /> 1 开始在仿真器中写代码和调试<br /> 由于主程序会被拷贝到RAM中执行,则我们应该在编译时就把程序定位到RAM中。这里先要说说几个ADS的参数的意义,在ADS的ARM LINKER页有RO,RW两个参数,此外还有一个ZI没有在页中给出,RO是只读代码的起始地址,由这个地址开始存放编译出来的程序指令;RW是程序的读写段的开始,即你程序中的数据存放的开始地址,ZI紧跟在RW区后,ZI区存放的是需要在程序运行时初始化为0的数据。<br />了解这几个链接参数的意义后我们可以设置这几个参数了:对于我的44B0X板8M SDRAM在0XC00_0000.因此在开发时把ADS中的RO BASE的地址指定为0XC00_0000;置于RW,在程序完成前可以预先估计一下程序的体积有多大,需要用到的数据区有多大,避免数据区太小或代码区覆盖掉前面的数据区就是了,我用了0XC10_0000,1M的代码空间,其他作数据区。这样,我们编译出来的程序代码就是在0XC00_0000中,可以直接由仿真器写入RAM中运行仿真运行。此外,在linker-〉layout页有个object symbol和section的选项,要求你填入映像文件最开始的object文件名和段名,这两个参数在仿真时不填写也不会影响运行,因为仿真器会自动修改pc指针,但要建立能烧写的映像文件,则一定要填写好,具体填写什么后面分析程序时再讲。<br /> 2中断问题<br /> 和所有单片机一样,ARM复位后从地址0X0开始执行,而0X0后是一串默认的中断向量表。对51这样的芯片,我们会直接在这个中断向量表中填入跳转语句,让它跳到指定的ISR处理中断事件。由于我们的主程序是在RAM中执行的,编译时又和引导程序分开,不可能预先知道我们写的ISR具体地址,而预留的中断向量表只够每个中断一个跳转指令,因此我们需要做二次跳转。在内存中建立一个中断向量表,每个中断对应一个字,存放ISR的地址。尔后,对每个中断写一段短的代码,把ISR地址取出,填入PC。而0X0后面中断向量的跳转指令,则是跳到这小段程序中。<br /> 3烧写flash,ADX中似乎有个写入flash的选项,我自己没有具体用过。但听说用jtag写flash会比较慢。由于nor flash或nand flash都是可以编程烧写的,即我们可以写个程序擦写flash,问题是如何读取编译出来的映像文件。这个也不用担心,adx中有个菜单把文件内容写入指定的地址中,把影响文件指定到一个ram地址,然后就用烧写程序把ram的内容拷入rom中就是了。我们有个boot程序,一个主程序要映射到rom 中.假设我把0xc20_0000开始的2m地址作rom的映像,则把boot程序导入0xc20_0000,boot的程序非常短,在 0xc20_1000开始放主程序。然后把0xc20_0000到0xc40_0000的内容全部拷入rom中(当然在导入文件前这些ram应该先被清空或写入ff.)。<br /> 让我们来看看相关的代码,具体认识一下该怎么处理前面说的这些问题,还有另外的一些问题。这里使用的代码是在44b0x的application note的第三章中拿出来的,这个文件在网上应该很容易找到。<br /> 程序的入口在44binit.s汇编文件中,其中一个Init 段是整个程序的入口:<br />AREA Init,CODE,READONLY<br />ENTRY<br />b ResetHandler ;for debug<br />b HandlerUndef ;handlerUndef<br />b HandlerSWI ;SWI interrupt handler<br />b HandlerPabort ;handlerPAbort<br />b HandlerDabort ;handlerDAbort<br />b . ;handlerReserved<br />b HandlerIRQ<br />关键字ENTRY告诉编译器保留这段代码。从代码看INIT段就是要写入0X0地址的原始中断向量,因此把这个文件编译生成的44BINIT.O和 INTT填入上面提到的LAYOUT页对应项中。这样编译器会把该段代码编译到0X0地址。(仿真时你可以试试别填这两个项目,看看ADX中的反汇编代码入口被放到哪里)。<br />这段代码里除了reset句外,有每句都有一个HandlerXXX的标号,这就是前面提到的中断处理程序的入口,它是由前面的一个宏来定义的:<br />MACRO<br />$HandlerLabel HANDLER $HandleLabel<br />$HandlerLabel<br />sub sp,sp,#4 ; Decrement sp(to store jump address)<br />stmfd sp!,{r0} <br />; pUSH the work register to stack(lr does't push because it return to original address)<br />ldr r0,=$HandleLabel; load the address of HandleXXX to r0<br />ldr r0,[r0] ; load the contents(service routine start address) of HandleXXX<br />str r0,[sp,#4] ;store the contents(ISR) of HandleXXX to stack<br />ldmfd sp!,{r0,pc} ; pOP the work register and pc(jump to ISR)<br />MEND<br /> 我自己没有写过宏,所以还是看编译出来的代码比较直接:<br /> HandlerSWI<br /> 0x0c000198: e24dd004 ..M. SUB r13,r13,#4<br /> 0x0c00019c: e92d0001 ..-. STMFD r13!,{r0}<br /> 0x0c0001a0: e59f0458 X... LDR r0,0xc000600<br /> 0x0c0001a4: e5900000 .... LDR r0,[r0,#0]<br /> 0x0c0001a8: e58d0004 .... STR r0,[r13,#4]<br /> 0x0c0001ac: e8bd8001 .... LDMFD r13!,{r0,pc}<br />这是ads输出的汇编代码,就是刚才的宏对应swi的一个实例,其中有两句<br /> LDR r0,0xc000600<br /> LDR r0,[r0,#0]<br />是把0x0c000600的内容载入r0,再把r0地址的ram单元载入r0.去看看0xc000600的内容,是0X0c7fff08,这是我设定的内存中的中断向量表地址之一,中断向量表的起始地址是0X0c7fff00,因此0X0c7fff08存放的刚好就是swi的isr地址。后面程序就跳到对应的ISR去了。(这段宏程序由于我不熟悉arm的汇编,只看过它怎么执行,实在我不知道中断向量表地址是如何被放入0x0c000600等地址的。希望有高手能再详细解释一下具体的编写,编译方法和原理。)<br />在c程序中,我们需要给每个中断向量定义一个宏:<br /> #define pISR_SWI (*(unsigned *)(_ISR_STARTADDRESS+0x08))<br />_ISR_STARTADDRESS是起始地址0X0c7fff00,假设ISR是以下函数:<br />void __irq SWI_UserIsr(void){……………}<br />则在系统初始化时用pISR_EINT0=(unsigned)SWI_UserIsr;这样的语句把ISR的地址填入中断向量表中,对所有中断作同样的处理,然后开中断,系统就能经过上面的宏把跳到ISR执行。<br /> 44binit.s中还有几段值得留意的代码:以下的代码把rw段的数据拷入ram中,并初始化zi段,即把该段清零:<br />LDR r0,=|Image$$RO$$Limit| <br />LDR r1,=|Image$$RW$$Base| <br />LDR r2,=|Image$$ZI$$Base| <br />CMP r0,r1 <br /> BEQ %F1<br />0 CMP r1,r3<br /> LDRCC r2,[r0],#4<br /> STRCC r2,[r1],#4<br /> BCC %B0<br />1 LDR r1,=|Image$$ZI$$Limit|<br /> MOV r2,#0<br />2 CMP r3,r1<br /> STRCC r2,[r3],#4<br /> BCC %B2<br />来看反汇编的代码:<br /> 0x0c000ae0: e59f0194 .... LDR r0,0xc000c7c<br /> 0x0c000ae4: e59f1194 .... LDR r1,0xc000c80<br /> 0x0c000ae8: e59f3194 .1.. LDR r3,0xc000c84<br />0xc000c7c,开始的三个字的内容是: <br /> 0x0c000c7c: 0c000e10 .... DCD 201330192<br /> 0x0c000c80: 0c200000 .. . DCD 203423744<br /> 0x0c000c84: 0c200000 .. . DCD 203423744<br /> 0x0c000c88: 0c200004 .. . DCD 203423748<br />这些反汇编的代码是一个点led的程序的,可以看出我的小程序代码到0x0c000e10就结束了,0x0c200000是我指定的数据区起始地址。这段程序把|Image$$RO$$Limit| 开始的,长|Image$$ZI$$Base| -|Image$$RW$$Base| 的数据区拷到|Image$$RW$$Base|的对应单元,就是0x0c200000开始的一段ram中。后面还有|Image$$ZI$$ Limit|,在我的代码中是0x0c000c88,内容是0x0c200004.这其实表明我的小程序并没有rw区,只有一个初始为0的变量。<br /> 另外还有一段初始化ram控制器的代码:<br /> ;****************************************************<br /> ;* Set memory control registers <br /> ;****************************************************<br /> ldr r0,=SMRDATA<br /> ldmia r0,{r1-r13}<br /> ldr r0,=0x01c80000 ;BWSCON Address<br /> stmia r0,{r1-r13},<br />由于44b0x要求13个控制寄存器要一次完成填入,所以先把参数设定在SMRDATA的地址中,一次载入通用寄存器,在一次填入RAM控制寄存器中。 4510的书上介绍调试前需要用SEMEM命令设置这些寄存器,但我自己没有那么做也可以跑的很好,也许是默认已经用了最保守的配置的原因吧!<br /> 其余的代码解释比较清晰了,最后摘出我的LED程序和这个小程序的BOOT程序以及烧写程序。这几个程序的project都包括了 44binit.s, option.s, memcfg.s,option.h,44b.h几个从app note中抄来的文件,这里只列了我自己写的主要c代码。其他这些文件我除了把ram和rom的对应配置改了一下外,都没有改动。我的引导程序编译出来是 3k,led程序也是3k,因此我把他们分别定位在rom的0x0和0x2000处,一共写了8k。<br />LED程序中的44BINTT.S程序功能和LOAD中的44BINTT.S是重复的,主要是我懒得去修改这些汇编,由着他们占用一点时间吧!<br /> load程序负责把从0x20000处开始的4k程序(即led程序)拷到ram 0xc000000中,run函数把pc指到0x0c000000,开始执行led程序:<br />void (*Run)(void) = (void (*)(void))RAM_ADDR;<br />void Main(void)<br />{ INT32U k ;<br /> INT32U *pulSource = (INT32U*)0x2000,;<br /> INT32U *pulDest = (INT32U*)0x0c000000;<br /> rSYSCFG='CACHECFG';<br /> PortInit(); <br /> for(k=0;k<2000> *pulDest++ = *pulSource++; <br /> Run(); <br />}<br />led程序把两个通用io上连的led作不断的亮和灭:<br />void Main(void)<br />{ INT32U k ;<br /> //INT16U *ptr;<br /> rSYSCFG='CACHECFG';<br /> PortInit(); <br /> while(1)<br /> {<br /> LedDisp(0);<br /> for(k=0;k LedDisp(3); <br /> for(k=0;k }<br />}<br />最后是烧写的程序,详细的代码网上高手们写了不少,我只是给出最简单的实现。烧写时当程序执行到清理完0X0C30_0000到0X0C30_4000的 RAM后,让程序中断下来,通过LOAD MEMORY FORM FILE命令把LOAD.BIN导入0X0C30_0000,LED.BIN导入0X0C30_2000中,继续运行程序直到一个LED亮起,烧写就完成了。拔去仿真器后再上电,可以看到两个LED同时亮灭。<br />#include 'option.h'<br />#include '44b.h'<br />#include 'def.h'<br />//#include 'romdef.h'<br />//#include 'stdio.h'<br />//#include 'stdlib.h' <br /><br /> #define FLASH_START_ADDR 0X0000<br />#define FLASH_ADDR_UNLOCK1 0X5555<br />#define FLASH_ADDR_UNLOCK2 0X2AAA<br />#define FLASH_DATA_UNLOCK1 0XAAAA<br />#define FLASH_DATA_UNLOCK2 0X5555<br />#define FLASH_DATA_WRITE 0XA0A0<br />#define FLASH_ERASE 0X8080<br />#define FLASH_ERASE_SECTOR 0X3030<br />#define FLASH_ERASE_BLOCK 0X5050<br />#define FLASH_ERASE_CHIP 0X1010<br />#define FLASH_SID_QUERY 0X9090<br />#define FLASH_CFI_QUERY 0X9898<br />#define FLASH_SID_EXIT 0XF0F0<br />#define FLASH_OP_TIMEOUT 0Xffff <br /><br /> #define LED_PORTC10 (1 #define LED_PORTC11 (1 #define RAM_ADDR 0xc000000<br />void (*Run)(void) = (void (*)(void))RAM_ADDR;<br />void infoFlash(void);<br />int wait_flash_ready ( INT16U *address, INT16U data );<br />int writeFlash(INT16U *Address,INT16U Data);<br />int eraseSector(INT16U* SectorAddr);<br />int eraseChip(void); <br /><br /> void PortInit(void);<br />void LedDisp(int LedStatus); <br /><br /> //*****************************************<br />// FLASH WIRTING<br />//*****************************************<br />void Main(void)<br />{ INT32U k ;<br /> INT16U *pdist,*psrc;<br /> rSYSCFG='CACHECFG';<br /> PortInit(); <br /> //infoFlash();<br /> eraseChip();<br /> psrc=(INT16U *)0xc300000;<br /> for(k=0;k<0x4000> *psrc++=0x0; //clear ram<br /> psrc=(INT16U *)0xc300000;<br /> pdist=(INT16U *)0x0;<br /> for(k=0;k<0x4000 k ram to> writeFlash(pdist++,*psrc++);<br /> while(1)<br /> {<br /> LedDisp(0);<br /> for(k=0;k LedDisp(2); <br /> for(k=0;k }<br />} <br /><br /> <br />//*****************************************<br />// init the port<br />//*****************************************<br />void PortInit(void)<br />{ <br /><br /> rPDATC = 0xffff; //All IO is high<br /> rPCONC = 0x0f55ff54; <br /> rPUPC = 0x3000; //PULL UP RESISTOR should be enabled to I/O<br />} <br /><br /> //*****************************************<br />// light led<br />//*****************************************<br />void LedDisp(int LedStatus)<br />{<br /> if((LedStatus&0x01)==0x01)<br /> rPDATC &= (~LED_PORTC10); //LED ON<br /> else<br /> rPDATC |= LED_PORTC10; //LED OFF<br /> <br /> if((LedStatus&0x02)==0x02)<br /> rPDATC &=(~LED_PORTC11); //LED ON<br /> else<br /> rPDATC |=LED_PORTC11; //LED OFF<br />} <br /><br /> //*****************************************<br />// show the flash soft id<br />//*****************************************<br />void infoFlash()<br />{<br /> int i,j;<br /> INT16U *pFlash;<br /> *((volatile INT16U *)FLASH_START_ADDR)=FLASH_SID_EXIT;<br /> *((volatile INT16U *)FLASH_START_ADDR+FLASH_ADDR_UNLOCK1)=FLASH_DATA_UNLOCK1;<br /> *((volatile INT16U *)FLASH_START_ADDR+FLASH_ADDR_UNLOCK2)=FLASH_DATA_UNLOCK2;<br /> *((volatile INT16U *)FLASH_START_ADDR+FLASH_ADDR_UNLOCK1)=FLASH_SID_QUERY; <br /> for(i=0;i pFlash='FLASH'_START_ADDR;<br /> i='0';j=0;<br /> i=(INT16U)*pFlash++;<br /> j=(INT16U)*pFlash; <br />}<br />//*****************************************<br />// erase hold flash<br />//*****************************************<br />int eraseChip()<br />{<br /> *((volatile INT16U *)FLASH_START_ADDR)=FLASH_SID_EXIT;<br /> *((volatile INT16U *)FLASH_START_ADDR+FLASH_ADDR_UNLOCK1)=FLASH_DATA_UNLOCK1;<br /> *((volatile INT16U *)FLASH_START_ADDR+FLASH_ADDR_UNLOCK2)=FLASH_DATA_UNLOCK2;<br /> *((volatile INT16U *)FLASH_START_ADDR+FLASH_ADDR_UNLOCK1)=FLASH_ERASE; <br /> *((volatile INT16U *)FLASH_START_ADDR+FLASH_ADDR_UNLOCK1)=FLASH_DATA_UNLOCK1;<br /> *((volatile INT16U *)FLASH_START_ADDR+FLASH_ADDR_UNLOCK2)=FLASH_DATA_UNLOCK2;<br /> *((volatile INT16U *)FLASH_START_ADDR+FLASH_ADDR_UNLOCK1)=FLASH_ERASE_CHIP;<br /> if( wait_flash_ready((INT16U *)FLASH_START_ADDR,0xffff) )<br /> return 1;<br /> else return 0;<br />} <br /><br /> //*****************************************<br />// write one falsh word( 16bit)<br />//*****************************************<br />int writeFlash(INT16U *Address,INT16U Data)<br />{ *((volatile INT16U *)FLASH_START_ADDR)=FLASH_SID_EXIT;<br /> *((volatile INT16U *)FLASH_START_ADDR+FLASH_ADDR_UNLOCK1)=FLASH_DATA_UNLOCK1;<br /> *((volatile INT16U *)FLASH_START_ADDR+FLASH_ADDR_UNLOCK2)=FLASH_DATA_UNLOCK2;<br /> *((volatile INT16U *)FLASH_START_ADDR+FLASH_ADDR_UNLOCK1)=FLASH_DATA_WRITE; <br /> *Address=Data;<br /> if(wait_flash_ready(Address,Data))<br /> return 1;<br /> else return 0;<br />} <br /><br />//*****************************************<br />// wait for operation finish<br />//*****************************************<br />int wait_flash_ready ( INT16U *address, INT16U data )<br />{<br /> INT32U tmp;<br /> INT16U *p;<br /> tmp =0xff;<br /> p='address';<br /> while(((*p)&0x8080)!=(data&0x8080))<br /> {tmp--;<br /> if (tmp==0x0)<br /> return 1; // timeout<br /> }<br /> return 0;<br />}<br /><br /> |
|