MC9S12XE 启动过程
2019年05月21日 23:37:20 风拂寒雪 阅读数 82更多
分类专栏: MC9S12XE
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/sinat_31773903/article/details/90348117
0、注意
本文与CodeWarrior5.1 IDE与CPU12密切相关,对二者进行深入了解有助于理解MC9S12XE的具体启动细节,所以需要参考CodeWarrior5.1 IDE的参考手册:HC(S)08/RS08 and S12(X) Build Tools Utilities Manual和S12(X)Build Tools Reference Manual以及CPU12 的参考手册CPU12/CPU12X Reference Manual。二者均可在NXP官网下载。
1)HC(S)08/RS08 and S12(X) Build Tools Utilities Manual和S12(X)Build Tools Reference Manual主要介绍了CodeWarrior 5.1的个主要工具,具体如下:
2)CPU12/CPU12X Reference Manual主要介绍CPU12和CPU12X的CPU结构、寻址方式和汇编指令集,具体内容如下:
1.CodeWarrior 5.1智能链接器
C程序经过编译后生成obj文件,然后链接器将其链接成最终可执行程序。在CodeWarrior 5.1中链接器生成.abs文件,然后烧录器再将.abs文件转换成.abs.s19文件。具体链接过程为:链接器将导入的obj文件中程序用到的初始化数据、启动数据结构、需要拷贝的数据、堆栈、固定页代码、非固定页代码、数据等代码或数据类型根据prm文件中设置的对应代码或数据类型指定的内存资源进行整合、归类并进行地址分配。例如在prm文件中,PLACEMENT…END如下定义:
_PRESTART, /* Used in HIWARE format: jump to _Startup at the code start */
_PRESTART, /* startup data structures */
ROM_VAR, /* constant variables */
STRINGS, /* string literals */
VIRTUAL_TABLE_SEGMENT, /* C++ virtual table segment */
//.ostext, /* eventually OSEK code */
NON_BANKED, /* runtime routines which must not be banked */
COPY /* copy down information: how to initialize variables */
/* in case you want to use ROM_4000 here as well, make sure
that all files (incl. library files) are compiled with the
option: -OnB=b */
INTO ROM_C000;
.map文件如下:
- 该部分定义的是对应类型代码和数据对应的地址和所在的内存资源块,如.init、.startData、.copy、text由于prm文件中PLACEMENT定义将_PRESTART、START、COPY、NON_BNAKED置于ROM_C000,所以链接器将其分配到ROM_C0000,地址范围为0xC000-0xC052。
- 该段定义的对应类型的代码和数据包含的具体函数和数据。Delay_Ms、Test_Func1、Test_Func2、main、init函数属于.text,所以包含在ROM_C000内。变量Test_Var1_Init、Test_Var2_Init是定义的全局变量属于.data,所以放在RAM内,Test_Var1_No_Init是定义的全局const变量,所以属于.rodata.也就是只读数据,放电ROM_C000.
- .copy文件,对于初始化的全局变量来说,在启动过程中需要将ROM内的变量拷贝到RAM内对全局变量的进行初始化,所以链接器内含有.copy模块用于启动时将负责的变量从ROM拷贝到RAM内,Test_Var1_Init和Test_Var2_Init是需要初始化的全局变量,所以在.copy内。
- _startupData是链接器生成的用于对所使用RAM进行清零并将ROM内的数据拷贝到RAM内进行数据初始化等的结构体数据,链接器生成的_startupData位于.map文件。本工程生成的如下:
_startupData结构体在start12.h内定义,定义如下:
typedef struct _Range {
unsigned char * __far beg; int size; /* [beg..beg+size] */
} _Range;
typedef struct _Copy {
int size; unsigned char * __far dest;
} _Copy;
extern struct _tagStartup {
#ifndef __NO_FLAGS_OFFSET
unsigned char flags;
#endif
#ifndef __NO_MAIN_OFFSET
_PFunc main; /* top level procedure of user program */
#endif
#ifndef __NO_STACKOFFSET_OFFSET
Word stackOffset; /* 16bit, initial value of the stack pointer */
#endif
unsigned int nofZeroOuts; /* number of zero out ranges */
_Range *PTR16 pZeroOut; /* vector of ranges with nofZeroOuts elements */
#if defined(__BANKED_COPY_DOWN)
_Copy *__pptr toCopyDownBeg; /* rom-address where copydown-data begins */
#else
_Copy *PTR16 toCopyDownBeg; /* rom-address where copydown-data begins */
#endif
#if 0 /* switch on to implement ROM libraries */
unsigned int nofLibInits; /* number of library startup descriptors */
_LibInit *PTR16 libInits; /* vector of pointers to library startup descriptors */
#endif
#ifdef __cplusplus
unsigned int nofInitBodies; /* number of init functions for C++ constructors */
_Cpp *PTR16 initBodies; /* vector of function pointers to init functions for C++ constructors */
unsigned int nofFiniBodies; /* number of fini functions for C++ destructors */
_Cpp *PTR16 finiBodies; /* vector of function pointers to fini functions for C++ destructors */
#endif
} _startupData;
_startupData结构体内变量的意义如下,如若没有使用,则不会生成对应类型的数据:
其中主要用到的是nofZeroOuts、pZeroOut和toCopyDownBeg,正如表中解释所说nofZeroOuts是要清零的RAM块的块数,pZeroOut为_Range结构体变量,内含有要清零的RAM块的起始地址和清零数目,toCopyDownBeg为_Copy结构变量,内含有需初始化的全局变量的RAM起始地址和需初始化内存的数目。
2.CPU12架构
CPU12含有A、B两个8位累加器(或者A、B组成一个16位累加器D),X、Y两个索引寄存器,堆栈指针SP、程序指针PC和状态码寄存器,如下所示:
其中状态寄存器中的各位与51单片机类型,具体含义如下:
3.启动程序解释
MC9S12的启动文件由汇编实现,主要使用第2节中的A、B、D、X、Y寄存器是实现,一般启动过程如下:
0)根据宏定义判断是否开启看门狗、是否进行内存分页使能判断、是否进行内存控制等;
1)初始化堆栈指针,代码如下:
INIT_SP_FROM_STARTUP_DESC();
该代码的具体意思为#define INIT_SP_FROM_STARTUP_DESC() __asm LDS #__SEG_END_SSTACK;
,也即是将堆栈栈尾的地址载入堆栈指针SP。
2)将需要清零的全局变量清零,也就是链接器中生成的RAM中应该清零的内存块清零。该段根据链接器生成的_startupData中nofZeroOuts和pZeroOut将对应RAM块清零,代码如下:
ZeroOut:
#if defined(__HIWARE_OBJECT_FILE_FORMAT__) && defined(__LARGE__)
LDX _startupData.pZeroOut:1 ; in the large memory model in the HIWARE format, pZeroOut is a 24 bit pointer
#else
LDX _startupData.pZeroOut ; *pZeroOut
#endif
LDY _startupData.nofZeroOuts ; nofZeroOuts
BEQ CopyDown ; if nothing to zero out
NextZeroOut: PSHY ; save nofZeroOuts
#if defined(FAR_DATA)
LDAB 1,X+ ; load page of destination address
LDY 2,X+ ; load offset of destination address
#if defined(__HCS12X__)
STAB __GPAGE_ADR__
#else /* defined(__HCS12X__) */
__PIC_JSR(_SET_PAGE) ; sets the page in the correct page register
#endif /* defined(__HCS12X__) */
#else /* FAR_DATA */
LDY 2,X+ ; start address and advance *pZeroOut (X = X+4)
#endif /* FAR_DATA */
#if defined(__HCS12X__) && defined(FAR_DATA)
PSHX
LDX 0,X ; byte count
#if defined(__OPTIMIZE_FOR_SIZE__)
CLRA
NextWord: GSTAA 1,Y+ ; clear memory byte
__FEED_COP_IN_HLI() ; feed the COP if necessary /*lint !e505 !e522 asm code */
DBNE X, NextWord ; dec byte count
#else
LDD #0
LSRX
BEQ LoopClrW1 ; do we copy more than 1 byte?
NextWord: GSTD 2,Y+ ; clear memory word
__FEED_COP_IN_HLI() ; feed the COP if necessary /*lint !e505 !e522 asm code */
DBNE X, NextWord ; dec word count
LoopClrW1:
BCC LastClr ; handle last byte
GSTAA 1,Y+ ; handle last byte
LastClr:
#endif
PULX
LEAX 2,X
#elif defined(__OPTIMIZE_FOR_SIZE__) /* -os, default */
LDD 2,X+ ; byte count
NextWord: CLR 1,Y+ ; clear memory byte
__FEED_COP_IN_HLI() ; feed the COP if necessary /*lint !e505 !e522 asm code */
DBNE D, NextWord ; dec byte count
#else /* __OPTIMIZE_FOR_TIME__ */
LDD 2,X+ ; byte count
LSRD ; /2 and save bit 0 in the carry
BEQ LoopClrW1 ; do we copy more than 1 byte?
PSHX
LDX #0
LoopClrW: STX 2,Y+ ; Word-Clear
__FEED_COP_IN_HLI() ; feed the COP if necessary /*lint !e505 !e522 asm code */
DBNE D, LoopClrW
PULX
LoopClrW1:
BCC LastClr ; handle last byte
CLR 1,Y+
LastClr:
#endif /* __OPTIMIZE_FOR_SIZE__/__OPTIMIZE_FOR_TIME__ */
PULY ; restore nofZeroOuts
DEY ; dec nofZeroOuts
BNE NextZeroOut
3)将需要初始化的全局变量初始化,也就是链接器中生成的RAM中应该赋值的内存块从对应的ROM中拷贝赋值。该段根据链接器生成的_startupData中ntoCopyDownBeg将数据从ROM中拷贝到RAM中对全局变量进行初始化,代码如下:
opyDown:
#if defined(__BANKED_COPY_DOWN)
LDAA _startupData.toCopyDownBeg:0 ; get PAGE address of .copy section
STAA __PPAGE_ADR__ ; set PPAGE address
LDX _startupData.toCopyDownBeg:1 ; load address of copy down desc.
#elif defined(__ELF_OBJECT_FILE_FORMAT__)
LDX _startupData.toCopyDownBeg ; load address of copy down desc.
#else
LDX _startupData.toCopyDownBeg:2 ; load address of copy down desc.
#endif
NextBlock:
LDD 2,X+ ; size of init-data -> D
BEQ funcInits ; end of copy down desc.
#ifdef FAR_DATA
PSHD ; save counter
LDAB 1,X+ ; load destination page
LDY 2,X+ ; destination address
#if defined(__HCS12X__)
STAB __GPAGE_ADR__
#else /* __HCS12X__ */
__PIC_JSR(_SET_PAGE) ; sets the destinations page register
#endif /* __HCS12X__ */
PULD ; restore counter
#else /* FAR_DATA */
LDY 2,X+ ; load destination address
#endif /* FAR_DATA */
#if defined(__HCS12X__) && defined(FAR_DATA)
#if defined(__OPTIMIZE_FOR_SIZE__) /* -os, default */
Copy: PSHA
LDAA 1,X+
GSTAA 1,Y+ ; move a byte from ROM to the data area
PULA
__FEED_COP_IN_HLI() ; feed the COP if necessary /*lint !e505 !e522 asm code */
DBNE D,Copy ; copy-byte loop
#else
LSRD ; /2 and save bit 0 in the carry
BEQ Copy1 ; do we copy more than 1 byte?
Copy: PSHD
LDD 2,X+
GSTD 2,Y+ ; move a word from ROM to the data area
PULD
__FEED_COP_IN_HLI() ; feed the COP if necessary /*lint !e505 !e522 asm code */
DBNE D,Copy ; copy-word loop
Copy1:
BCC NextBlock ; handle last byte?
LDAA 1,X+
GSTAA 1,Y+ ; move a byte from ROM to the data area
#endif
#elif defined(__OPTIMIZE_FOR_SIZE__) /* -os, default */
Copy: MOVB 1,X+,1,Y+ ; move a byte from ROM to the data area
__FEED_COP_IN_HLI() ; feed the COP if necessary /*lint !e505 !e522 asm code */
DBNE D,Copy ; copy-byte loop
#else /* __OPTIMIZE_FOR_TIME__ */
LSRD ; /2 and save bit 0 in the carry
BEQ Copy1 ; do we copy more than 1 byte?
Copy: MOVW 2,X+,2,Y+ ; move a word from ROM to the data area
__FEED_COP_IN_HLI() ; feed the COP if necessary /*lint !e505 !e522 asm code */
DBNE D,Copy ; copy-word loop
Copy1:
BCC NextBlock ; handle last byte?
MOVB 1,X+,1,Y+ ; copy the last byte
#endif /* __OPTIMIZE_FOR_SIZE__/__OPTIMIZE_FOR_TIME__ */
BRA NextBlock
- 4)如果采用了C++类型的函数,则还需要进行函数初始化,代码如下:
funcInits: ; call of global construtors is only in c++ necessary
#if defined(__cplusplus)
#if defined(__ELF_OBJECT_FILE_FORMAT__)
#if defined( __BANKED__) || defined(__LARGE__)
LDY _startupData.nofInitBodies; load number of cpp.
BEQ done ; if cppcount == 0, goto done
LDX _startupData.initBodies ; load address of first module to initialize
nextInit:
LEAX 3,X ; increment to next init
PSHX ; save address of next function to initialize
PSHY ; save cpp counter
CALL [-3,X] ; use double indirect call to load the page register also
PULY ; restore cpp counter
PULX ; restore actual address
DEY ; decrement cpp counter
BNE nextInit
#else /* defined( __BANKED__) || defined(__LARGE__) */
LDD _startupData.nofInitBodies; load number of cpp.
BEQ done ; if cppcount == 0, goto done
LDX _startupData.initBodies ; load address of first module to initialize
nextInit:
LDY 2,X+ ; load address of first module to initialize
PSHD
PSHX ; save actual address
JSR 0,Y ; call initialization function
PULX ; restore actual address
PULD ; restore cpp counter
DBNE D, nextInit
#endif /* defined( __BANKED__) || defined(__LARGE__) */
#else /* __ELF_OBJECT_FILE_FORMAT__ */
LDX _startupData.mInits ; load address of first module to initialize
#if defined( __BANKED__) || defined(__LARGE__)
nextInit: LDY 3,X+ ; load address of initialization function
BEQ done ; stop when address == 0
; in common environments the offset of a function is never 0, so this test could be avoided
#ifdef __InitFunctionsMayHaveOffset0__
BRCLR -1,X, done, 0xff ; stop when address == 0
#endif /* __InitFunctionsMayHaveOffset0__ */
PSHX ; save address of next function to initialize
CALL [-3,X] ; use double indirect call to load the page register also
#else /* defined( __BANKED__) || defined(__LARGE__) */
nextInit:
LDY 2,X+ ; load address of first module to initialize
BEQ done ; stop when address of function == 0
PSHX ; save actual address
JSR 0,Y ; call initialization function
#endif /* defined( __BANKED__) || defined(__LARGE__) */
PULX ; restore actual address
BRA nextInit
#endif /* __ELF_OBJECT_FILE_FORMAT__ */
done:
#endif /* __cplusplus */
}
}
#endif /* __ONLY_INIT_SP */
#if defined( __ELF_OBJECT_FILE_FORMAT__) && defined(__cplusplus ) && 0 /* the call to main does not support to return anymore */
#if !defined(FAR_DATA) && (defined( __BANKED__) || defined(__LARGE__))
static void __far Fini(void)
#else
static void Fini(void)
#endif
{
/* purpose: 1) call global destructors in C++ */
asm {
#if defined( __BANKED__) || defined(__LARGE__)
LDY _startupData.nofFiniBodies; load number of cpp.
BEQ done ; if cppcount == 0, goto done
LDX _startupData.finiBodies ; load address of first module to finalize
nextInit2:
LEAX 3,X ; increment to next init
PSHX ; save address of next function to finalize
PSHY ; save cpp counter
CALL [-3,X] ; use double indirect call to load the page register also
PULY ; restore cpp counter
PULX ; restore actual address
DEY ; decrement cpp counter
BNE nextInit2
#else /* defined( __BANKED__) || defined(__LARGE__) */
LDD _startupData.nofFiniBodies; load number of cpp.
BEQ done ; if cppcount == 0, goto done
LDX _startupData.finiBodies ; load address of first module to finalize
nextInit2:
LDY 2,X+ ; load address of first module to finalize
PSHD
PSHX ; save actual address
JSR 0,Y ; call finalize function
PULX ; restore actual address
PULD ; restore cpp counter
DBNE D, nextInit2
#endif /* defined(__BANKED__) || defined(__LARGE__) */
5)判断是否开启看门狗,并调用main函数,单片机开始运行。