打印

EasyARM2200开发板学习笔记:启动代码分析

[复制链接]
3784|11
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
olyaim|  楼主 | 2008-7-17 10:49 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
;定义堆栈的大小
FIQ_STACK_LEGTH     EQU     0
IRQ_STACK_LEGTH     EQU     9*8          ;每层嵌套需要9个字堆栈,允许8层嵌套
ABT_STACK_LEGTH     EQU     0
UND_STACK_LEGTH     EQU     0

;处理器模式常量定义
NoInt        EQU 0x80                ;禁止IRQ中断
USR32Mode   EQU 0x10                ;用户模式
SVC32Mode   EQU 0x13                ;管理模式
SYS32Mode   EQU 0x1f                ;系统模式
IRQ32Mode   EQU 0x12                ;中断模式
FIQ32Mode   EQU 0x11                ;快速中断模式

PINSEL2     EQU 0xE002C014
BCFG0       EQU 0xFFE00000
BCFG1       EQU 0xFFE00004
BCFG2       EQU 0xFFE00008
BCFG3       EQU 0xFFE0000C

;引入的外部标号在这声明
IMPORT  FIQ_Exception          ;快速中断异常处理程序
IMPORT  __main                     ;C语言主程序入口 
IMPORT  TargetResetInit        ;目标板基本初始化
IMPORT  StackUsr                    ;用户模式栈
IMPORT  bottom_of_heap            ;用户模式堆
IMPORT  SoftwareInterrupt        ;软件中断处理程序

;给外部使用的标号在这声明
EXPORT  Reset
EXPORT  __rt_div0
EXPORT  __user_initial_stackheap

        CODE32

        AREA  vectors,CODE,READONLY
        ENTRY
;中断向量表
Reset
        LDR     PC, ResetAddr        ;复位入口,程序在系统中执行的第一条指令
        LDR     PC, UndefinedAddr    ;未定义指令异常入口地址
        LDR     PC, SWI_Addr            ;软件中断入口地址
        LDR     PC, PrefetchAddr        ;取指令中止异常入口地址
        LDR     PC, DataAbortAddr    ;取数据中止异常入口地址
        DCD     0xb9205f80            ;保留向量,值是其它中断向量累加和的补码
        LDR     PC, [PC, #-0xff0]    ;中断请求IRQ入口地址
        LDR     PC, FIQ_Addr            ;快速中断请求FIQ入口地址

ResetAddr               DCD     ResetInit
UndefinedAddr           DCD     Undefined
SWI_Addr                DCD     SoftwareInterrupt
PrefetchAddr            DCD     PrefetchAbort
DataAbortAddr           DCD     DataAbort
Nouse                   DCD     0
IRQ_Addr                DCD     0
FIQ_Addr                DCD     FIQ_Handler

;未定义指令
Undefined
        B       Undefined                ;发生未定义指令异常时执行死循环

;取指令中止
PrefetchAbort
        B       PrefetchAbort            ;发生取指令中止异常时执行死循环

;取数据中止
DataAbort
        B       DataAbort                ;发生取数据中止异常时执行死循环

;快速中断
FIQ_Handler
        STMFD   SP!, {R0-R3, LR}    ;现场保护,将R0-R3, LR入栈
        BL      FIQ_Exception        ;调用快速中断异常处理程序
        LDMFD   SP!, {R0-R3, LR}    ;异常中断返回
        SUBS    PC,  LR,  #4            ;PC指向中断前没有被执行的指令


InitStack
;初始化堆栈
        MOV     R0, LR

;设置中断模式堆栈
        MSR     CPSR_c, #0xd2    ;IRQ和FIQ禁止, 中断模式
        LDR     SP, StackIrq        ;设置中断模式堆栈指针,指向StackIrq
;设置快速中断模式堆栈
        MSR     CPSR_c, #0xd1    ;IRQ和FIQ禁止, 快速中断模式
        LDR     SP, StackFiq        ;设置快速中断模式堆栈指针,指向StackFiq
;设置中止模式堆栈
        MSR     CPSR_c, #0xd7    ;IRQ和FIQ禁止, 中止模式
        LDR     SP, StackAbt        ;设置中止模式堆栈指针,指向StackAbt
;设置未定义模式堆栈
        MSR     CPSR_c, #0xdb    ;IRQ和FIQ禁止, 未定义模式
        LDR     SP, StackUnd        ;设置未定义模式堆栈指针,指向StackUnd
;设置系统模式堆栈
        MSR     CPSR_c, #0xdf    ;IRQ和FIQ禁止, 系统模式
        LDR     SP, =StackUsr    ;设置系统模式堆栈指针,指向StackUsr

        MOV     PC, R0                ;返回

ResetInit
;复位入口
;初始化外部总线控制器,根据目标板决定配置

        LDR     R0, =PINSEL2        ;将PINSEL2的地址赋给RO
    IF :DEF: EN_CRP                ;判断是否有预定义EN_CRP宏
        LDR     R1, =0x0f814910    ;PINSEL2.2=0, 禁止JTAG调试
    ELSE
        LDR     R1, =0x0f814914    ;PINSEL2.2=1, 使能JTAG调试
    ENDIF
        STR     R1, [R0]

        LDR     R0, =BCFG0        ;初始化存储器组0的配置寄存器
        LDR     R1, =0x1000ffef
        STR     R1, [R0]

        LDR     R0, =BCFG1        ;初始化存储器组1的配置寄存器
        LDR     R1, =0x1000ffef
        STR     R1, [R0]

;       LDR     R0, =BCFG2        ;初始化存储器组2的配置寄存器
;       LDR     R1, =0x2000ffef
;       STR     R1, [R0]

;       LDR     R0, =BCFG3        ;初始化存储器组3的配置寄存器
;       LDR     R1, =0x2000ffef
;       STR     R1, [R0]
        
        BL      InitStack         ;跳转到初始化堆栈代码段
        BL      TargetResetInit  ;跳转到目标板基本初始化
        
        B       __main                ;跳转到c语言入口

__user_initial_stackheap        ;库函数初始化堆和栈
MOV   pc,lr


__rt_div0                            ;整数除法除数为0错误处理函数
B       __rt_div0

StackIrq           DCD     IrqStackSpace + (IRQ_STACK_LEGTH - 1)* 4
StackFiq           DCD     FiqStackSpace + (FIQ_STACK_LEGTH - 1)* 4
StackAbt           DCD     AbtStackSpace + (ABT_STACK_LEGTH - 1)* 4
StackUnd           DCD     UndtStackSpace + (UND_STACK_LEGTH - 1)* 4


    IF :DEF: EN_CRP
        IF  . >= 0x1fc            ;判断当前代码地址是否已超过0x1FC
        INFO    1," The data at 0x000001fc must be 0x87654321.
Please delete some source before this line."
        ENDIF
CrpData
    WHILE . < 0x1fc                ;未到0x1FC则填充NOP指令
        NOP
    WEND
CrpData1
        DCD    0x87654321         ;在0x1FC处放置0x87654321,启动加密功能
    ENDIF

;/* 分配堆栈空间 */
        AREA    MyStacks, DATA, NOINIT, ALIGN=2
IrqStackSpace       SPACE   IRQ_STACK_LEGTH * 4  ;中断模式堆栈空间
FiqStackSpace       SPACE   FIQ_STACK_LEGTH * 4  ;快速中断模式堆栈空间
AbtStackSpace       SPACE   ABT_STACK_LEGTH * 4  ;中止义模式堆栈空间
UndtStackSpace      SPACE   UND_STACK_LEGTH * 4  ;未定义模式堆栈
END

相关帖子

来自 2楼
olyaim|  楼主 | 2008-7-19 19:24 | 只看该作者

EasyARM2200开发板学习笔记:代码镜像的分析

https://bbs.21ic.com/upfiles/img/20079/200793193410450.gif

      从上图可看出RO(Code+RO Data)段的大小是1544bytes,RW段的大小为0,ZI段的大小为1128bytes。Debug Image是给调试器用的,并没有加载到开发板存储中去,再因为ZI段是在程序运行时才在RAM中生成,所以真正加载到开发板中的大小只有1544bytes,这点也可以通过编译器输出BIN二进制文件的大小看出。
      那么这些输出段加载到开发板存储时是怎么存放的呢?这要由连接器的连接类型来确定,连接类型分为Partial、Simple和Scattered三种,这里只介绍最后一种连接类型的加载方式,使用这种类型要用到scatter文件,以下是周立功开发板例子程序中附带的两个scatter文件,可以看出加载域都是在0x80000000处,所以加载到开发板中的RO段就从0x80000000开始放置。其中code存放在0x80000000~0x800005A3,大小刚好1444bytes,RO Data存放在0x800005A4~0x80000607,大小刚好100bytes。因为此例RW Data为0,所以在以下两种分散加载文件中,真正加载到开发板存储器中的区域是0x80000000~0x80000607。

(左)
ROM_LOAD 0x80000000
{
    ROM_EXEC 0x80000000
    {
        Startup.o (vectors, +First)
        * (+RO)
    }

    IRAM 0x40000000
    {
        Startup.o (+RW,+ZI)
    }

    STACKS 0x40004000 UNINIT
    {
        stack.o (+ZI)
    }

    ERAM 0x80040000
    {
        * (+RW,+ZI)
    }

    HEAP +0 UNINIT
    {
        heap.o (+ZI)
    }
}

(右)
ROM_LOAD 0x80000000
{
    ROM_EXEC 0x80000000
    {
        Startup.o (vectors, +First)
        * (+RO)
    }

    IRAM 0x40000000
    {
        Startup.o (+RW,+ZI)
    }

    STACKS 0x40004000 UNINIT
    {
        stack.o (+ZI)
    }

    ERAM 0x81000000
    {
        * (+RW,+ZI)
    }

    HEAP +0 UNINIT
    {
        heap.o (+ZI)
    }
}

      加载成功后,程序开始启动,启动过程中会对stack和heap进行初始化,先是给不同处理器模式的SP(R13)寄存器进行初始化,让SP都指向各自的stack区域。Stack区域在编译连接时根据上面的分散加载文件已经确定了存放位置和大小,下面左边是周立功EasyARM2200开发板的例子中初始化栈指针的代码。

InitStack    
        MOV     R0, LR

;设置管理模式的栈指针
        MSR     CPSR_c, #0xd3
        LDR     SP, StackSvc
;设置中断模式的栈指针
        MSR     CPSR_c, #0xd2
        LDR     SP, StackIrq
;设置快速中断模式的栈指针
        MSR     CPSR_c, #0xd1
        LDR     SP, StackFiq
;设置中止模式栈
        MSR     CPSR_c, #0xd7
        LDR     SP, StackAbt
;设置未定义模式的栈指针
        MSR     CPSR_c, #0xdb
        LDR     SP, StackUnd
;设置系统模式的栈指针
        MSR     CPSR_c, #0xdf
        LDR     SP, =StackUsr

        MOV     PC, R0

      那么上面这些栈指针所指向的栈空间的定义又在哪里呢?分析Startup.s发现下面的代码,再联系前面所提到的分散加载文件中的IRAM段,可以得出下面的SvcStackSpace对应的地址应该是0x40000000+RW size,因为Startup.s中没有RW段和其它ZI段,所以:
SvcStackSpace=0x40000000,StackSvc=0x3FFFFFFC
IrqStackSpace=0x40000000,StackIrq=0x400003FC
FiqStackSpace=0x40000400,StackFiq=0x400003FC
AbtStackSpace=0x40000400,StackAbt=0x400003FC
UndStackSpace=0x40000400,StackUnd=0x400003FC
       
      还差一个系统模式的栈,这个栈的定义是在stack.s中,可以根据程序需要增加空间。根据分散加载文件中的STACKS段,可以得出StackUsr对应的地址是0x40004000,它的空间大小在这程序中定义为1bytes。

;定义堆栈的大小
USR_STACK_LEGTH         EQU         256
SVC_STACK_LEGTH         EQU         0
FIQ_STACK_LEGTH         EQU         0
IRQ_STACK_LEGTH         EQU         256
ABT_STACK_LEGTH         EQU         0
UND_STACK_LEGTH         EQU         0

……

;指定不同处理器模式的堆栈指针
StackSvc           DCD     SvcStackSpace + (SVC_STACK_LEGTH - 1)* 4
StackIrq           DCD     IrqStackSpace + (IRQ_STACK_LEGTH - 1)* 4
StackFiq           DCD     FiqStackSpace + (FIQ_STACK_LEGTH - 1)* 4
StackAbt           DCD     AbtStackSpace + (ABT_STACK_LEGTH - 1)* 4
StackUnd           DCD     UndStackSpace + (UND_STACK_LEGTH - 1)* 4

……

;分配堆栈空间
    AREA    MyStacks, DATA, NOINIT, ALIGN=2
SvcStackSpace      SPACE   SVC_STACK_LEGTH * 4  ;管理模式堆栈空间
IrqStackSpace      SPACE   IRQ_STACK_LEGTH * 4  ;中断模式堆栈空间
FiqStackSpace      SPACE   FIQ_STACK_LEGTH * 4  ;快速中断模式堆栈空间
AbtStackSpace      SPACE   ABT_STACK_LEGTH * 4  ;中止义模式堆栈空间
UndStackSpace      SPACE   UND_STACK_LEGTH * 4  ;未定义模式堆栈

      至此我们已经定义的栈空间大小是(256*4+1)=1025bytes,存储对齐后占用了1028bytes。而从上面生成Image的图中可以看出ZI段的大小是1128bytes,那么还有100bytes在哪定义,下面我们通过IDA反汇编来看看另外的ZI段放在什么地方,怎么定义的?

ERAM:80040000                 AREA ERAM, DATA
ERAM:80040000                 ; ORG 0x80040000
ERAM:80040000 __libspace_start % 0x60  ;DATA XREF: ROM_EXEC:off_80000578o
ERAM:80040000                          ;ROM_EXEC:80000600o
==========================================================================
HEAP:80040060                 AREA HEAP, DATA
HEAP:80040060                 ; ORG 0x80040060
HEAP:80040060                 EXPORT bottom_of_heap
HEAP:80040060 bottom_of_heap  % 1      ;DATA XREF: ROM_EXEC:off_80000100o
HEAP:80040061                 ALIGN 4  ;4字节对齐

      可以看出库函数所需要的栈空间和由heap.s声明的堆空间加起来刚好100bytes。存放的位置也正符合上面左边的分散加载文件中ERAM段和HEAP段的定义。那么由heap.s声明的堆空间是在什么时候定义的呢?要不要对其初始化呢?中间还有加载域到运行域的复制,ZI段的清零等工作又是怎么进行的呢?请等下一个笔记《EasyARM2200开发板学习笔记:__main()和__rt_entry()》

使用特权

评论回复
板凳
lomeisi_99| | 2008-7-17 14:54 | 只看该作者

什么开发环境?

PLL, 紧张中断,remap等都好像没看到吗?
路过,随便看看,哈哈。

使用特权

评论回复
地板
olyaim|  楼主 | 2008-7-17 16:25 | 只看该作者

不好意思,还没有展开。

不好意思,还没有展开。

使用特权

评论回复
5
olyaim|  楼主 | 2008-7-17 16:27 | 只看该作者

EasyARM2200开发板学习笔记:目标板基本初始化

/***************************************************************
** 函数名称: InitialiseUART0
** 功能描述: 设置串口0 
** 输 入: bps:波特率
**
** 输 出: 无
**         
** 全局变量: 无
** 调用模块: 无
**
** 作 者: 陈明计
** 日 期: 2004年2月2日
**-------------------------------------------------------------
** 修改人:
** 日 期:
**-------------------------------------------------------------
***************************************************************/
void InitialiseUART0(uint32 bps)
{      
    uint16 Fdiv;
    
    PINSEL0 = (PINSEL0 & 0xfffffff0) | 0x05;  //选择管脚为UART0

    U0LCR = 0x80;                      //允许访问分频因子寄存器
    Fdiv = (Fpclk / 16) / bps;         //设置波特率
    U0DLM = Fdiv / 256;                            
    U0DLL = Fdiv % 256;                        
    U0LCR = 0x03;           //禁止访问分频因子寄存器,且设置为8,1,n
    U0IER = 0x00;           //禁止中断
    U0FCR = 0x00;           //初始化FIFO




/***************************************************************
** 函数名称: TargetResetInit
** 功能描述: 调用main函数前目标板初始化代码,根据需要改变,不能删除
** 输 入: 无
**
** 输 出: 无
**         
** 全局变量: 无
** 调用模块: 无
**
** 作 者: 陈明计
** 日 期: 2004年2月2日
**-------------------------------------------------------------
** 修改人:
** 日 期:
**-------------------------------------------------------------
***************************************************************/
void TargetResetInit(void)
{
//判断是否在编译器Preprocessor选项卡中预定义了宏变量“__DEBUG”
#ifdef __DEBUG
    BCFG0 = 0x10000400;
    MEMMAP = 0x3;  //用户外部存储器模式,中断向量从外部存储器重新映射
#endif

//判断是否在编译器Preprocessor选项卡中预定义了宏变量“__OUT_CHIP”
#ifdef __OUT_CHIP
//  BCFG0 = 0x10000400;
    BCFG1 = 0x10000400;
    MEMMAP = 0x3;  //用户外部存储器模式,中断向量从外部存储器重新映射#endif

//判断是否在编译器Preprocessor选项卡中预定义了宏变量“__IN_CHIP”
#ifdef __IN_CHIP    
    BCFG0 = 0x10000400;
    MEMMAP = 0x1;  //用户Flash模式,中断向量不重新映射
#endif

    //引脚P0.0~P0.4设置为UART0和I2C
    PINSEL0 = (PINSEL0 & 0xFFFF0000) | 0x05 | 0x50;

/* 设置系统各部分时钟 */
    PLLCON = 1;
#if (Fpclk / (Fcclk / 4)) == 1
    VPBDIV = 0;  //VPB总线时钟为处理器时钟的1/4
#endif
#if (Fpclk / (Fcclk / 4)) == 2
    VPBDIV = 2;  //VPB总线时钟为处理器时钟的1/2
#endif
#if (Fpclk / (Fcclk / 4)) == 4
    VPBDIV = 1;  //VPC总线时钟与处理器时钟相同
#endif

#if (Fcco / Fcclk) == 2
    PLLCFG = ((Fcclk / Fosc) - 1) | (0 << 5);
#endif
#if (Fcco / Fcclk) == 4
    PLLCFG = ((Fcclk / Fosc) - 1) | (1 << 5);
#endif
#if (Fcco / Fcclk) == 8
    PLLCFG = ((Fcclk / Fosc) - 1) | (2 << 5);
#endif
#if (Fcco / Fcclk) == 16
    PLLCFG = ((Fcclk / Fosc) - 1) | (3 << 5);
#endif
    PLLFEED = 0xaa;
    PLLFEED = 0x55;
    while((PLLSTAT & (1 << 10)) == 0);  //确认PLL的锁定状态
        PLLCON = 3;  //PLL使能关连接到处理器作为系统时钟源
    PLLFEED = 0xaa;
    PLLFEED = 0x55;

/* 设置存储器加速模块 */
    MAMCR = 0;        //MAM功能禁止
#if Fcclk < 20000000
    MAMTIM = 1;       //MAM取指周期为1个cclk,实际上是关闭了MAM
#else
#if Fcclk < 40000000
    MAMTIM = 2;       //MAM取指周期为2个cclk
#else
    MAMTIM = 3;       //MAM取指周期为3个cclk
#endif
#endif
    MAMCR = 2;        //MAM功能完全使能

/* 设置串行口 */
    InitialiseUART0(115200);

/* 设置实时时钟 */
    CCR = 1;
    PREINT = Fpclk / 32768 - 1;
    PREFRAC = Fpclk - (Fpclk / 32768) * 32768;
    YEAR = 2008;
    MONTH = 7;
    DOM = 17;

    VICIntEnClr = 0xffffffff;  //清零中断使能寄存器并禁止中断请求
    VICVectAddr = 0;           //清零向量地址寄存器
    VICIntSelect = 0;          //初始化所有中断请求输入类型为IRQ
    T0IR = 0xffffffff;
    T0TCR = 0X02;
}

使用特权

评论回复
6
ccf_2006| | 2008-7-17 17:21 | 只看该作者

周立功的源码,会被通缉的

周的网站上可下载,
产品都生产了,至今还不知道heap初始化是那一块,
有高手告我

使用特权

评论回复
7
armecos| | 2008-7-20 14:24 | 只看该作者

关于启动代码,

《ecos增值包》里包括了redboot、u-boot、bootrom等启动部分的源码,用GNU实现,内容更丰富。
关于EasyARM2200开发板,www.armecos.com免费下载里有很多试用程序,可以完成非常复杂的应用。

更多内容,详见:
《培训系列“丛书”》
www.armecos.com
-----------------------------------
More details, see:
《"Series Books" of Training》
www.armecos.com

使用特权

评论回复
8
sy55| | 2008-9-29 10:58 | 只看该作者

qqqq

qqqq

使用特权

评论回复
9
db10| | 2008-9-29 14:03 | 只看该作者

....

感觉分析得还可以的.给你裤子!

使用特权

评论回复
10
db10| | 2008-9-29 14:15 | 只看该作者

...

 已经在:EasyARM2200开发板学习笔记:代码镜像的分析
 这编**以前被加裤子了.

 2200感觉就是一个不成功的ARM7,带总线只能连PSRAM,还有速度也不快,干不了太多事情.我之前用这个东东也做了一款产品,没有过多长就替换成别的ARM了.

使用特权

评论回复
11
52mcu| | 2008-11-10 23:25 | 只看该作者

《EasyARM2200开发板学习笔记:__main()和__rt_entry()》呢?

期待

使用特权

评论回复
12
andysun1986| | 2010-3-29 20:20 | 只看该作者
[img][/img]

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

2

主题

11

帖子

0

粉丝