×
单片机 > 单片机程序设计 > 详情

TQ2440启动代码分析(一)

发布时间:2020-05-23 发布时间:
|
看了好几天的启动代码了,终于把大部分的东西都看懂了,在此整理下自己对TQ2440开发板启动代码的理解(参考了网上找的一些资料),并贴出来供大家看一下,希望对大家有帮助,如果有什么错了的地方,也请大家不吝赐教。

;=========================================

; NAME: 2440INIT.S

; DESC: C start up codes

;       Configure memory, ISR ,stacks

;      Initialize C-variables

;=========================================

启动代码主要是在主程序运行之前初始化系统硬件及软件的运行环境,它的主要功能包括以下的几个方面:

1、建立中断向量表

2、初始化系统堆栈

3、应用程序执行环境初始化

4 跳转至主函数

接下来对各个部分进行分析

;GET 是包含头文件的意思,相当于C语言中的include,是将一个源文件包含到当前源文件中,并将被包含的文件在其当前位置进行汇编处理

       GET option.inc  ;option.inc文件包含了开发板的配置信息—堆栈、时钟等

       GET memcfg.inc ;存储控制文件

       GET 2440addr.inc ;寄存器地址地址定义

 

BIT_SELFREFRESH EQU   (1<<22)  ;SDRAM自刷新标志位

;预定义的工作模式

;ARM 有7 种模式,用户模式,快速中断模式,中断模式,管理模式,中止模式,未定

;义模式和系统模式。系统堆栈的初始化主要是给各个处理器模式分配堆栈空间。堆栈是为

;中断或程序跳转服务的,当发生中断或程序跳转时,需要将当前处理器的状态及一些参数

;保持在堆栈中,当中断处理完毕以后或程序执行完后返回时,再将堆栈保存的现场数据进

;行恢复,以保证原来的程序正确运行

;Pre-defined constants

USERMODE    EQU        0x10  ;用户模式

FIQMODE     EQU 0x11      ;快速中断模式

IRQMODE     EQU 0x12              ;中断模式

SVCMODE     EQU        0x13              ;管理模式

ABORTMODE   EQU       0x17    ;中止模式

UNDEFMODE   EQU       0x1b       ;未定义模式

MODEMASK    EQU       0x1f        ;模式掩码

NOINT       EQU    0xc0                     ;无中断模式

;The location of stacks

;定义各种模式下使用的堆栈起始地址,_STACK_BASEADDRESS是由option.inc定义的

UserStack       EQU       (_STACK_BASEADDRESS-0x3800)  ;0x33ff4800 ~ 

SVCStack       EQU       (_STACK_BASEADDRESS-0x2800)  ;0x33ff5800 ~

UndefStack     EQU       (_STACK_BASEADDRESS-0x2400)  ;0x33ff5c00 ~

AbortStack     EQU       (_STACK_BASEADDRESS-0x2000)  ;0x33ff6000 ~

IRQStack       EQU       (_STACK_BASEADDRESS-0x1000)  ;0x33ff7000 ~

FIQStack EQU       (_STACK_BASEADDRESS-0x0) ;0x33ff8000 ~

 

;Check if tasm.exe(armasm -16 ...@ADS 1.0) is used.

; 判断是否THUMB指令

;   [ 代表IF,| 指的是ELSE,] 相当于ENDIF

       GBLL    THUMBCODE ;定义一个局部变量THUMBCODE

       [ {CONFIG} = 16   ;如果是16位代码,则将THUMBCODE设为真

THUMBCODE SETL  {TRUE} ;

           CODE32  ;否则是ARM指令

             |

THUMBCODE SETL  {FALSE}

    ]

; 宏定义,用于子程序的返回

             MACRO

       MOV_PC_LR

             [ THUMBCODE  ;如果目标地址是THUMB的指令,则跳到THUMB地址

           bx lr    ;在ARM模式中,要用BX指令跳转到THUMB指令,并转换模式

             |

           mov pc,lr  ;如果目标地址是ARM指令,则直接把函数返回地址给PC

             ]

       MEND

 

             MACRO

       MOVEQ_PC_LR  ;带条件的函数返回,与MOV_PC_LR类似

             [ THUMBCODE

        bxeq lr

             |

           moveq pc,lr

             ]

       MEND

 

PS:下面这一段是网上找的,我觉得它已经介绍的非常好了,所以就直接拿过来用来,呵呵

;===============================================================

;下面这个宏是用于第一次查表过程的实现中断向量的重定向,你会发现

;在_ISR_STARTADDRESS=0x33FF_FF00里定义的第一级中断向量表

;是采用型如Handle***的方式的. 而在程序的ENTRY处(程序开始处)采用的是

;b Handler***的方式.

;在这里Handler***就是通过HANDLER这个宏和Handle***进立联系的.

;这种方式的优点就是正真定义的向量数据在内存空间里,而不是在ENTRY处

;的ROM(FLASH)空间里, 这样,我们就可以在程序里灵活的改动向量的数据了.

;其中HANDLER是一个宏,用于查找中断处理程序的入口地址。这些地址存放在

;由HandleXXX指向的表项中,该表定位在RAM高端,基地址为_ISR_STARTADDRESS。

;假如_ISR_STARTADDRESS为 0x800000000,当IRQ中断时,根据b HandlerFIQ,先跳转

;再根据^ _ISR_STARTADDRESS基地址+HandleIRQ 的偏移地址(4*6)得到的中断地址

;0x80000000+0x00000024=0x80000024[page]

;===============================================================            

MACRO

$HandlerLabel HANDLER $HandleLabel

 

$HandlerLabel

       sub  sp,sp,#4   ;decrement sp(to store jump address)

       stmfd      sp!,{r0}  ;PUSH the work register to stack(lr does not push because it return to original address)

       ldr     r0,=$HandleLabel;load the address of HandleXXX to r0

       ldr     r0,[r0]      ;load the contents(service routine start address) of HandleXXX

       str     r0,[sp,#4]      ;store the contents(ISR) of HandleXXX to stack

       ldmfd   sp!,{r0,pc}     ;POP the work register and pc(jump to ISR)

       MEND

 

;IMPORT指令类似于C语言的extern关键字,下面这些古怪的变量是由编译器生成的

       IMPORT  |Image$$RO$$Base|    ; Base of ROM code,即代码开始的地方

       IMPORT  |Image$$RO$$Limit|  ; End of ROM code (=start of ROM data)

       IMPORT  |Image$$RW$$Base|   ; Base of RAM to initialise,RAM起始地址

       IMPORT  |Image$$ZI$$Base|   ; Base and limit of area初始化的起始地址

       IMPORT  |Image$$ZI$$Limit|  ; to zero initialise初始化的结束地址

;===============================================================

107 ;在这里用IMPORT伪指令(和c语言的extren一样)引入外部变量MMU的快速总线

108 ;模式和异步总线模式两个变量

109 ;===============================================================

       IMPORT MMU_SetAsyncBusMode

       IMPORT MMU_SetFastBusMode ;

 

       IMPORT  Main            ; The main entry of mon program

       IMPORT  RdNF2SDRAM   ; Copy Image from Nand Flash to SDRAM

 

初始化程序中必须指明入口地址,因为处理器复位(仿真时,装载image)后PC 要找到入口开始执行代码,当各种异常或是中断产生的时候也要找到各个异常的入口开始执行代

码。从这里开始就是真正的代码入口了!

       AREA    Init,CODE,READONLY;声明一个名为Init的代码段,

       ENTRY   程序的入口点,(调试用)

       ;ENTRY只是定义一个普通的入口点,且在程序中可以多处定义,如果要使用它作为整

个映像文件的唯一入口点,还需要设置链接器中的相关选项。

       EXPORT __ENTRY

__ENTRY

ResetEntry  ;复位后的入口

       ;1)The code, which converts to Big-endian, should be in little endian code.

       ;2)The following little endian code will be compiled in Big-Endian mode.

       ;  The code byte order should be changed as the memory bus width.

       ;3)The pseudo instruction,DCD can not be used here because the linker generates error.

       ASSERT :DEF:ENDIAN_CHANGE

       [ ENDIAN_CHANGE

              ASSERT  :DEF:ENTRY_BUS_WIDTH

;如果没有定义ENTRY_BUS_WIDTH就报错

              [ ENTRY_BUS_WIDTH=32

                     b     ChangeBigEndian       ;DCD 0xea000007

              ]

 

              [ ENTRY_BUS_WIDTH=16

                     andeq      r14,r7,r0,lsl #20   ;DCD 0x0007ea00

              ]

 

              [ ENTRY_BUS_WIDTH=8

                     streq       r0,[r0,-r10,ror #1] ;DCD 0x070000ea

              ]

              |

              b     ResetHandler;复位异常,开发板上电或复位时进入0x00

       ]

中断向量表一般位于启动代码的开始部分,它是用户程序与启动代码之间以及启动代码

的各部分之间联系的纽带。它由一个一个的跳转函数组成,它就象一个普通的散转函数,只

不过散转的过程中有硬件机制参与,当系统发生异常时,ARM 处理器会通过硬件机制强制

将PC 指针指向中断向量表中对应的异常跳转函数存储的地址,然后程序会跳转到相应的中

断服务程序去执行。

b     HandlerUndef ;handler for Undefined mode 未定义异常,遇到无法识别的指令时0x04

b     HandlerSWI   ;handler for SWI interrupt,软中断异常0x08

b     HandlerPabort ;handler for PAbort指令预取错误时进入0x0c

b     HandlerDabort       ;handler for DAbort数据访问不能完成时进入0x10

b     .             ;reserved, 保留 0x14

b     HandlerIRQ    ;handler for IRQ interrupt发生IRQ 中断时进入0x18

b     HandlerFIQ    ;handler for FIQ interrupt发生FIQ 中断时进入0x1c

 

;@0x20

b     EnterPWDN   ; Must be @0x20.

158 ;===============================================================

159 ;下面是改变大小端的程序,这里采用直接定义机器码的方式,至说为什么这么做

160 ;就得问三星了反正我们程序里这段代码也不会去执行,不用去管它

161 ;===============================================================

ChangeBigEndian

;@0x24

       [ ENTRY_BUS_WIDTH=32

              DCD       0xee110f10     ;0xee110f10 => mrc p15,0,r0,c1,c0,0

              DCD       0xe3800080    ;0xe3800080 => orr r0,r0,#0x80;  //Big-endian

              DCD       0xee010f10     ;0xee010f10 => mcr p15,0,r0,c1,c0,0

       ]

       [ ENTRY_BUS_WIDTH=16

              DCD 0x0f10ee11

              DCD 0x0080e380

              DCD 0x0f10ee01

       ]

       [ ENTRY_BUS_WIDTH=8

              DCD 0x100f11ee

              DCD 0x800080e3

              DCD 0x100f01ee

       ]

       DCD 0xffffffff  ;swinv 0xffffff is similar with NOP and run well in both endian mode.

       DCD 0xffffffff

       DCD 0xffffffff

       DCD 0xffffffff

       DCD 0xffffffff

       b ResetHandler

 

;如第前面所说,这里采用HANDLER宏去建立Hander***和Handle***之间的联系

HandlerFIQ    HANDLER HandleFIQ

Ha ndlerIRQ   HANDLER HandleIRQ

HandlerUndef HANDLER HandleUndef

HandlerSWI   HANDLER HandleSWI

HandlerDabort       HANDLER HandleDabort

HandlerPabort HANDLER HandlePabort

[page]

IsrIRQ

       sub  sp,sp,#4       ;reserved for PC,给PC寄存器保留

       stmfd      sp!,{r8-r9};工作寄存器入栈保护

 

       ldr   r9,=INTOFFSET;INTOFFSET在2440addr.inc中定义

       ldr   r9,[r9]

       ldr   r8,=HandleEINT0;HandleEINT0 的地址就是中断的入口地址

       add  r8,r8,r9,lsl #2;逻辑左移就相当于乘以4

       ldr   r8,[r8]

       str    r8,[sp,#8]

       ldmfd      sp!,{r8-r9,pc},将地址从堆栈中弹出给PC

 

 

       LTORG

;用于声明一个数据缓冲池

;=======

上电和复位后,程序开始从位于0x0 执行b ResetHandler 程序跳转到这里执行,将看门

狗,中断之类的程序关掉,以免打扰初始化程序的进行。

; ENTRY

;=======

ResetHandler

       ldr   r0,=WTCON       ;watch dog disable

       ldr   r1,=0x0

       str    r1,[r0]

WTCON 为看门狗控制寄存器,此处将其写入0x0,就是禁止它的所有功能,包括定时

器定时,溢出中断及溢出复位。

       ldr   r0,=INTMSK

       ldr   r1,=0xffffffff  ;all interrupt disable

       str    r1,[r0]

INTMSK 为中断屏蔽寄存器,写入0xffffffff,就是禁止所有的中断产生,因为中断

向量表还未初始化,如果此时产生中断会使程序进入未知的状态而跑飞。因为外设的中断太

多,INTMSK 不够用,还需要INTSUBMSK 来将剩余的中断源也禁止掉。

       ldr   r0,=INTSUBMSK

       ldr   r1,=0x7fff             ;all sub interrupt disable

       str    r1,[r0]

 

       [ {FALSE}  ;亮灯用的,可以用来调试用

              ;rGPFDAT = (rGPFDAT & ~(0xf<<4)) | ((~data & 0xf)<<4);

              ; Led_Display

              ldr   r0,=GPBCON

              ldr   r1,=0x155500

              str    r1,[r0]

              ldr   r0,=GPBDAT

              ldr   r1,=0x0

              str    r1,[r0]

       ]

 

       ;To reduce PLL lock time, adjust the LOCKTIME register.

       ldr   r0,=LOCKTIME;设置pll 锁定时间

       ldr   r1,=0xffffff

       str    r1,[r0]

LOCKTIME 为PLL 锁定时间计数寄存器,重新设定分频值时,PLL 进入锁定,输出稳

定频率的时钟需要一定的时间。这里设置成默认的值,以满足锁定的要求。

 

       [ PLL_ON_START

              ; Added for confirm clock divide. for 2440.

              ; Setting value Fclk:Hclk:Pclk

              ldr   r0,=CLKDIVN;用于设定FCLK,HCLK和PCLK的比例

              ldr   r1,=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.,在option.inc中有定义

              str    r1,[r0]

 

       ;program has not been copied, so use these directly

              [ CLKDIV_VAL>1              ; means Fclk:Hclk is not 1:1.

                     mrc p15,0,r0,c1,c0,0

                     orr r0,r0,#0xc0000000;R1_nF:OR:R1_iA

                     mcr p15,0,r0,c1,c0,0

                     |

                     mrc p15,0,r0,c1,c0,0

                     bic r0,r0,#0xc0000000;R1_iA:OR:R1_nF

                     mcr p15,0,r0,c1,c0,0

              ]

 

              ;Configure UPLL

              ldr   r0,=UPLLCON

              ldr   r1,=((U_MDIV<<12)+(U_PDIV<<4)+U_SDIV)                      ;Fin = 12.0MHz, UCLK = 48MHz

              str    r1,[r0]

              nop  ; Caution: After UPLL setting, at least 7-clocks delay must be inserted for setting hardware be completed. ;在UPP 设定之后,必须等待7 个时钟的延迟,设定才会有效

              nop

              nop

              nop

              nop

              nop

              nop

              ;Configure MPLL,设置MPLL

              ldr   r0,=MPLLCON

              ldr   r1,=((M_MDIV<<12)+(M_PDIV<<4)+M_SDIV)                           ;Fin = 12.0MHz, FCLK = 400MHz

              str    r1,[r0]

       ]

;查看是否是由睡眠状态启动,如果是则跳转到WAKEUP_SLEEP状态

       ;Check if the boot is caused by the wake-up from SLEEP mode.

       ldr   r1,=GSTATUS2

       ldr   r0,[r1]

       tst    r0,#0x2

       ;In case of the wake-up from SLEEP mode, go to SLEEP_WAKEUP handler.

       bne  WAKEUP_SLEEP

 

       EXPORT StartPointAfterSleepWakeUp

StartPointAfterSleepWakeUp

 

初始化内存控制器其实就是对S3C2440 的memory bank 进行设置,使其扩展的存储器

或外部设备能够被处理器通过内存控制器正确读写。由于S3C2440 的最终应用程序是在

SDRAM(bank6)中运行,并与C 语言变量等的用户数据,各种模式的堆栈,中断向量表,

都被定位在SDRAM 的空间,所以它必须在涉及这些处理之前完成初始化工作。

       ;Set memory control registers

      ;ldr  r0,=SMRDATA

      adrl  r0, SMRDATA       ;be careful!

       ldr   r1,=BWSCON       ;BWSCON Address

       add  r2, r0, #52      ;End address of SMRDATA

0

       ldr   r3, [r0], #4

       str    r3, [r1], #4

       cmp r2, r0

       bne  %B0

这段是功能寄存器初始化,把13 个存储控制器的内容批量的读取到了对应的特殊功能

寄存器中,首先是下面有一个数据区SMRDATA,在程序的后面有定义,这个数据区给13 个寄存器分配52 字节的地址空间。在上面的代码中,r0 是这个数据区的起始地址,r2 是数据区的结束地址,r1 是寄存器的起始地址。这样,用一个判断语句就可以把内存中的数据赋给这13 个存储控制寄存器了。

关键字:TQ2440  启动代码 

『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
51单片机CO2检测显示程序解析