MPC5777M启动过程(CCFC300x)

1 芯片基础信息:

        基础参数:

        用户核:3 (z710 * 2  + z425 * 1)

        主频:    z710 - 300MHz    z425 - 200MHz

        Flash:CFlash 7.5M    DFlash 0.5M    SRAM 64+340K

Memory Map 见链接: 

MPC5777M & CCFC300x Memory Map

2 启动过程:

        在分析启动过程之前,我们可能有一些这样得问题。

1 MCU启动后,三个核得启动策略是怎样得?

2 CPU上电后,第一条指令去哪里取?

        通过对 Reference Manual 和 Example Project 分析,大致总结出以下启动时序。

常规启动过程:(过程太复杂了,没缕明白)

BAF --> BootHeader --> Startup --> Main

BAF : (Boot Assist Flash)

The Boot Assist Flash (BAF) contains factory code to facilitate the boot-up procedure.

厂商固化程序,包含程序引导过程。

BootHeader : 

这个位置包CPU启动配置,启动地址。

Startup : 

启动代码,同常规MCU一致,由汇编编写得启动程序,包含系统初步初始化过程,例如系统状态设定、RAM初始化、变量初始化。

Main : 

用户程序入口

        通过以上时序,我们开始逐步分析详细得启动过程。

2.1 启动地址与核心配置

        在MCU上电后,电源管理以及复位系统会先一步初始化。在这个过程后,BAF地址被加载到MC_ME 得 IOP复位向量中,执行BAF程序,通过BAF去寻找有效的引导头,即BootHeader。BAF详细得引导过程,这边还暂不清晰,大致过程是,去LowFlash块起始地址寻找BootHeader,即FC0000,FC4000……这4个地址以及Code Flash 前四个块起始地址0x01000000……四个地址。

        MPC5777M参考手册中有描述索引BootHeader失败场景,当BAF未能找到有效得BootHeader时,将进入串行启动模式,通过LINFlexD获取数据,置于RAM中,进一步执行用户数据。

        BootHeader中包括启动得核心配置和启动地址,结构如下表所示。

地址(以0xFC0000为例)描述
0xFC0000Boot ID + VLE En + Boot CPU
0xFC0004Z4_2 Reset Vector
0xFC0008Reserved
0xFC000CReserved
0xFC0010Z7_0 Reset Vector
0xFC0014Z7_1 Reset Vector
0xFC0018Z7_0s Reset Vector

        Boot Header首字包含有效 Boot ID(固定0x5A)和Boot CPU,该数据段可设定启动哪些核心。结构如下表所示。

bit0-bit7bit8-bit15bit16-bit23bit24-bit31
VLEEnBoot IDZ70sEnZ71EnZ70EnZ42En
10x5A

        Offset 4H、10H、14H、18H配置各个CPU启动地址,启动过程该部分数据被加载MC_ME模块(Mode Entry Module)中,MC_ME将复位向量传输到各CPU,每个核从复位地址开始执行。

        参考MPC5777M Example 功能代码如下

        通过宏控制Boot CPU,写入section "rchw" 以及相应得reset vector。该工程设定核心启动地址为0x01000000。

#define MPC57xx_ID    0x005A0000  /* RCHW boot ID for MPC57xx devices     */
#define VLE_ENABLE    0x01000000  /* VLE is enabled                       */

#define CPU2_ENABLED  0x00000001
#define CPU0_ENABLED  0x00000002
#define CPUC_ENABLED  0x00000004
#define CPU1_ENABLED  0x00000008

#if defined(MPC574xP) || defined(MPC5777C) || defined(MPC577xK)
extern void _start(void);
#define ENTRY_POINT  _start
#define RCHW_VAL (VLE_ENABLE | MPC57xx_ID)
#else
#if defined(TURN_ON_CPU0) && defined(TURN_ON_CPU1) && defined(TURN_ON_CPU2)
#define TARGET_CORES (CPU0_ENABLED | CPU1_ENABLED | CPU2_ENABLED)
#elif defined(TURN_ON_CPU0) && defined(TURN_ON_CPU1)
#define TARGET_CORES (CPU0_ENABLED | CPU1_ENABLED)
#elif defined(TURN_ON_CPU0) && defined(TURN_ON_CPU2)
#define TARGET_CORES (CPU0_ENABLED | CPU2_ENABLED)
#elif defined(TURN_ON_CPU1) && defined(TURN_ON_CPU2)
#define TARGET_CORES (CPU1_ENABLED | CPU2_ENABLED)
#elif defined(TURN_ON_CPU0)
#define TARGET_CORES (CPU0_ENABLED)
#elif defined(TURN_ON_CPU1)
#define TARGET_CORES (CPU1_ENABLED)
#elif defined(TURN_ON_CPU2)
#define TARGET_CORES (CPU2_ENABLED)
#else 
#error "Neither core is selected"
#endif /* defined(CPU0) && defined(CPU1) && defined(CPU2) */
#define RCHW_VAL (MPC57xx_ID | TARGET_CORES)
#endif

const uint32_t __attribute__ ((section(".rchw"))) RCHW1 = RCHW_VAL;
#if defined(MPC574xP) || defined(MPC5777C) || defined(MPC577xK)
const uint32_t __attribute__ ((section(".cpu0_reset_vector"))) RCHW2 = (uint32_t)ENTRY_POINT;
#else
#if defined(TURN_ON_CPU0)
const uint32_t __attribute__ ((section(".cpu0_reset_vector"))) RCHW2_0 = (uint32_t)0x1000000;
#endif
#if defined(TURN_ON_CPU1)
const uint32_t __attribute__ ((section(".cpu1_reset_vector"))) RCHW2_1 = (uint32_t)0x1000000;
#endif
#if defined(TURN_ON_CPU2)
const uint32_t __attribute__ ((section(".cpu2_reset_vector"))) RCHW2_2 = (uint32_t)0x1000000;
#endif
#endif

        接下来我们查看链接文件,确定各section段位置划分。

MEMORY
{

    flash_rchw : org = 0x00FC0000,   len = 0x4
    cpu0_reset_vec : org = 0x00FC0000+0x10,   len = 0x4
    cpu1_reset_vec : org = 0x00FC0000+0x14,   len = 0x4
    cpu2_reset_vec : org = 0x00FC0000+0x04,   len = 0x4    
    cpuc_reset_vec : org = 0x00FC0000+0x18,   len = 0x4    
        
    m_text :	org = 0x1000000, len = 2048K  
    m_data :	org = 0x40000000,   len = 128K
    
    local_imem  : org = 0x52000000,   len = 16K
    local_dmem  : org = 0x52800000,   len = 64K   
}

        可以确定该工程将BootHeader放在了BAF第一个检索扇区0x00FC0000上。

        通过 m_text 也证实了核心启动地址为0x01000000。

2.2 启动代码

        常规MCU启动代码,通常需要负责以下关键任务:

        1,中断向量初始化

        2,堆栈初始化

        3,数据段初始化

        4,BSS段清零

        5,Call Main

2.2.1 MPC5777M Startup.s

        以下是MPC5777M Example得启动程序,接着来分析一下。

1,Trun of SWT (Watchdog)

        部分MCU在设计时,会默认打开WDG,防止芯片在复位至开启WDG过程中卡死。但当前过程仍需要关掉WDG,我们不确定在初始化过程中得耗时,也没有足够得喂狗条件。这个关闭过程,可通过工程宏控制。

#if defined(DISABLE_SWT2)
;#SWT2
		e_lis	r4, 0xFC05
		e_or2i	r4, 0x8000

		e_li	r3, 0xC520
		e_stw	r3, 0x10(r4)

		e_li	r3, 0xD928
		e_stw	r3, 0x10(r4)

		e_lis	r3, 0xFF00
		e_or2i	r3, 0x010A
		e_stw	r3, 0(r4)
#endif

2,Enable BTB

        Flush & Enable BTB,启用分支预测以提高指令执行效率。

		e_li	r3, 0x201
		mtspr	1013, r3
		se_isync

3,Init Core Registers

        PowerPC e200这个平台,通过寄存器32个 r0-r31,还有很多配置状态相关。这里初始化了可能导致Lock Step错误相关寄存器,MPC5777M仅 Z7_0 有锁步核。原文描述是这个样子。

        The E200Z4 core needs its registers initialising before they are used otherwise in Lock Step mode the two cores will contain different random data. If this is stored to memory (e.g. stacked) it will cause a Lock Step error.

		; General
        e_li	r0, 0
		e_li	r1, 0
		e_li	r2, 0
		e_li	r3, 0
        ...
		e_li	r31, 0
        ; Special
		mtspr	1, r1		;#XER
		mtcrf	0xFF, r1
		mtspr	CTR, r1
		mtspr	272, r1		;#SPRG0
		mtspr	273, r1		;#SPRG1
		mtspr	274, r1		;#SPRG2
		mtspr	275, r1		;#SPRG3
		mtspr	58, r1		;#CSRR0
		mtspr	59, r1		;#CSRR1
		mtspr	570, r1		;#MCSRR0
		mtspr	571, r1		;#MCSRR1
		mtspr	61, r1		;#DEAR
		mtspr	63, r1		;#IVPR
		mtspr	256, r1		;#USPRG0
		mtspr	62, r1		;#ESR
		mtspr	8,r31		;#LR

4. Initialise SRAM ECC

        这里初始化所有可能使用得RAM。

        需要注意的是,该过程初始化并不是将全部内存初始化为0,这个过程主要是为了防止产生ECC问题。

        这里对三段RAM初始化,分别的全局的System RAM 和仅属于当前核心的Local Data RAM和Local Instruction RAM。

#***************************** Initialise SRAM ECC ***************************/
;# Store number of 128Byte (32GPRs) segments in Counter
 e_lis       r5, __SRAM_SIZE@h  # Initialize r5 to size of SRAM (Bytes)
 e_or2i      r5, __SRAM_SIZE@l
 e_srwi      r5, r5, 0x7         # Divide SRAM size by 128
 mtctr       r5                  # Move to counter for use with "bdnz"

;# Base Address of the internal SRAM
 e_lis       r5, __SRAM_BASE_ADDR@h
 e_or2i      r5, __SRAM_BASE_ADDR@l

;# Fill SRAM with writes of 32GPRs    
sram_loop:
    e_stmw      r0,0(r5)            # Write all 32 registers to SRAM
    e_addi      r5,r5,128           # Increment the RAM pointer to next 128bytes
    e_bdnz      sram_loop           # Loop for all of SRAM

;#************************ Initialise Local Data SRAM ECC *********************/
;# Store number of 128Byte (32GPRs) segments in Counter
 e_lis       r5, __LOCAL_DMEM_SIZE@h  # Initialize r5 to size of SRAM (Bytes)
 e_or2i      r5, __LOCAL_DMEM_SIZE@l
 e_srwi      r5, r5, 0x7         # Divide SRAM size by 128
 mtctr       r5                  # Move to counter for use with "bdnz"

;# Base Address of the Local SRAM
 e_lis       r5, __LOCAL_DMEM_BASE_ADDR@h
 e_or2i      r5, __LOCAL_DMEM_BASE_ADDR@l

;# Fill Local SRAM with writes of 32GPRs
ldmem_loop:
    e_stmw      r0,0(r5)            # Write all 32 registers to SRAM
    e_addi      r5,r5,128           # Increment the RAM pointer to next 128bytes
    e_bdnz      ldmem_loop          # Loop for all of SRAM

;#************************ Initialise Local Instruction SRAM ECC **************/
;# Store number of 128Byte (32GPRs) segments in Counter
 e_lis       r5, __LOCAL_IMEM_SIZE@h  # Initialize r5 to size of SRAM (Bytes)
 e_or2i      r5, __LOCAL_IMEM_SIZE@l
 e_srwi      r5, r5, 0x7         # Divide SRAM size by 128
 mtctr       r5                  # Move to counter for use with "bdnz"

;# Base Address of the Local SRAM
 e_lis       r5, __LOCAL_IMEM_BASE_ADDR@h
 e_or2i      r5, __LOCAL_IMEM_BASE_ADDR@l

;# Fill Local SRAM with writes of 32GPRs
limem_loop:
    e_stmw      r0,0(r5)            # Write all 32 registers to SRAM
    e_addi      r5,r5,128           # Increment the RAM pointer to next 128bytes
    e_bdnz      limem_loop          # Loop for all of SRAM
    

5,Load Initialised Data Values from Flash into RAM

        数据段初始化过程,在这里对初始化的全局变量赋值。

        这个架构有两个data段,分别是data和sdata,区别还没有调查,但初始化过程是一致得,这里只列出了data得初始化过程。

DATACOPY:
    e_lis       r9, __DATA_SIZE@ha      # Load upper SRAM load size (# of bytes) into R9
    e_or2i      r9, __DATA_SIZE@l       # Load lower SRAM load size into R9                                     
    e_cmp16i    r9,0                    # Compare to see if equal to 0                                  
    e_beq       SDATACOPY               # Exit cfg_ROMCPY if size is zero (no data to initialise)
                                        
    mtctr       r9                      # Store no. of bytes to be moved in counter
                                        
    e_lis       r10, __DATA_ROM_ADDR@h  # Load address of first SRAM load into R10
    e_or2i      r10, __DATA_ROM_ADDR@l  # Load lower address of SRAM load into R10
    e_subi      r10,r10, 1              # Decrement address to prepare for ROMCPYLOOP

    e_lis       r5, __DATA_SRAM_ADDR@h  # Load upper SRAM address into R5 (from linker file)
    e_or2i      r5, __DATA_SRAM_ADDR@l  # Load lower SRAM address into R5 (from linker file)
    e_subi      r5, r5, 1               # Decrement address to prepare for ROMCPYLOOP
    cmp         cr0,r5, r10
    e_beq       SDATACOPY               # Skip to SDATA copy if addresses is equal (no need to copy data)

DATACPYLOOP:
    e_lbzu      r4, 1(r10)              # Load data byte at R10 into R4,incrementing (update) ROM address
    e_stbu      r4, 1(r5)               # Store R4 data byte into SRAM at R5 and update SRAM address 
    e_bdnz      DATACPYLOOP             # Branch if more bytes to load from ROM

6,Initialize BSS section

        这里对未初始化得全局变量清零。

bss_Init:
    e_lis        r9, __BSS_SIZE@h       # Load upper BSS load size (# of bytes) into R9
    e_or2i       r9, __BSS_SIZE@l       # Load lower BSS load size into R9 and compare to zero
    e_cmp16i     r9,0
    e_beq        bss_Init_end           # Exit if size is zero (no data to initialise)

    mtctr        r9                     # Store no. of bytes to be moved in counter

    e_lis        r5, __BSS_START@h      # Load upper BSS address into R5 (from linker file)
    e_or2i       r5, __BSS_START@l      # Load lower BSS address into R5 (from linker file)
    e_subi       r5, r5, 1              # Decrement address to prepare for bss_Init_loop

    e_lis        r4, 0x0

bss_Init_loop:
    e_stbu       r4, 1(r5)              # Store zero byte into BSS at R5 and update BSS address
    e_bdnz       bss_Init_loop          # Branch if more bytes to load

bss_Init_end:

7,Enable ME Bit in MSR (Machine Check Enable)

        机器检查得中断使能。

		mfmsr	r6
		e_or2i	r6,0x1000
		mtmsr	r6

8,启用ICache

        是否在这个阶段启用ICache由汇编宏来控制,一般情况下,为了避免取指慢导致得代码运行效率下降,是会在这个阶段直接启用ICache。

#ifdef ICACHE_ENABLE
;#****************** Invalidate and Enable the Instruction cache **************
__icache_cfg:
		e_lis	r7, 0x0000
		e_or2i	r7, 0x03FF		#create mask for lower 11 bits
		mfspr	r5, 516			#in lower 11 bits we have icache size
		and.	r7, r7, r5		#check if we have icache
		e_beq	_skip_i_cache	#branch if not

		e_li	r5, 0x2
		mtspr	1011,r5

		e_li	r7, 0x4
		e_li	r8, 0x2
		;#e_lwi r11, 0xFFFFFFFB
		e_lis	r11,0xFFFF
		e_or2i	r11,0xFFFB

__icache_inv:
		mfspr	r9, 1011
		and.	r10, r7, r9
		e_beq	__icache_no_abort
		and.	r10, r11, r9
		mtspr	1011, r10
		e_b		__icache_cfg

__icache_no_abort:
		and.	r10, r8, r9
		e_bne	__icache_inv

		mfspr	r5, 1011
		e_ori	r5, r5, 0x0001
		se_isync
		;#msync
		mtspr	1011, r5
_skip_i_cache:
#endif

9,启用DCache

        DCache是否需要在这个阶段使能就要因工程而议了,DCache虽能优化变量得读写策略使程序执行效率提高,但多核通讯会存在问题,以及DMA和特殊得IP核之间。有效得解决方案是通过MPU对部分内存段做Cache抑制,因此某些架构设计会将DCache使能放置于MPU初始化之后。

#ifdef DCACHE_ENABLE
;#****************** Invalidate and Enable the Data cache **************
__dcache_cfg:
		e_lis	r7, 0x0000
		e_or2i	r7, 0x03FF		#create mask for lower 11 bits
		mfspr	r5, 515			#in lower 11 bits we have dcache size
		and.	r7, r7, r5		#check if we have dcache
		e_beq	_skip_d_cache	#branch if not


        e_li r5, 0x2
        mtspr 1010,r5

        e_li r7, 0x4
        e_li r8, 0x2
        e_lis	r11,0xFFFF
		e_or2i	r11,0xFFFB

__dcache_inv:
        mfspr r9, 1010
        and.  r10, r7, r9
        e_beq   __dcache_no_abort
        and.  r10, r11, r9
        mtspr 1010, r10
        e_b __dcache_cfg

__dcache_no_abort:
        and.  r10, r8, r9
        e_bne __dcache_inv

        mfspr r5, 1010
        e_ori   r5, r5, 0x0001
        se_isync
        msync
        mtspr 1010, r5
_skip_d_cache:
#endif

10,Configure Stack

        在这里设置栈信息。

		e_lis	r1, __SP_INIT@h	;# Initialize stack pointer r1 to
		e_or2i	r1, __SP_INIT@l	;# value in linker command file.

		e_lis	r13, _SDA_BASE_@h	;# Initialize r13 to sdata base
		e_or2i	r13, _SDA_BASE_@l	;# (provided by linker).

		e_lis	r2, _SDA2_BASE_@h	;# Initialize r2 to sdata2 base
		e_or2i	r2, _SDA2_BASE_@l	;# (provided by linker).

		e_stwu	r0,-64(r1)			;# Terminate stack.

11, bl main

        在上述过程完成,启动代码执行结束,通过 bl 跳转到应用程序main。

        在这个启动文件中,虽然包含了大部分必要得系统初始化,但还是少了些必要得步骤,例如中断相关初始化过程,在这个Example中,外部中断是在main中进行得初始化,通常这部分也可以放置于启动程序中。

2.2.2 CCFC300x Startup.s

        相比于MPC5777M,主要差异体现在,CCFC300x工程中在启动代码中增加了多核、ShareMemory、VectorTableMemory。

1,多核判断

        对于SMP工程,多个核心会公用一个elf文件,即三个核心公用唯一得data段、bss段、以及code和rodata段。但在启动过程,data段和bss段只需要在上电时初始化一次(以及share段),这些操作由 boot core 独立完成,后续其他 core 启动不需要再对此类资源重复初始化。故在启动过程中,需要增加多核判断。代码如下。

    ;#***************************** Initialise SRAM ECC ***************************/
    ;# Store number of 128Byte (32GPRs) segments in Counter
        #ifdef ONLY_ONE_ELF
        mfspr       r9, 286             ;# Save coreid to r9
        e_cmp16i    r9, 0
        e_beq       initialise_local_data_SRAM
        e_cmp16i    r9, 1
        e_beq       initialise_local_data_SRAM
        #endif
        e_lis       r5, __SRAM_SIZE@h   # Initialize r5 to size of SRAM (Bytes)
        e_or2i      r5, __SRAM_SIZE@l
        e_srwi      r5, r5, 0x7         # Divide SRAM size by 128
        mtctr       r5                  # Move to counter for use with "bdnz"

    ;# Base Address of the internal SRAM
        e_lis       r5, __SRAM_BASE_ADDR@h
        e_or2i      r5, __SRAM_BASE_ADDR@l

2,Share Memory

        在SMP工程中,需要划分出一段内存给多核使用,这段空间不受DCache影响。Share段数据同样被划分成两段内容,Share Data 和 Share Bss,开发者通过section关键字将某些变量置于Share Memory内。Share Memory初始化过程同data 和 bss一致。share bss初始化代码如下。

        mfspr       r9, 286                 # Save coreid to r9
        e_cmp16i    r9, 0
        e_beq       bss_share_end
        e_cmp16i    r9, 1
        e_beq       bss_share_end          # Only Z4 handles this part

    bss_share_Init:
        e_lis        r9, __BSS_SHARE_SIZE@h       # Load upper BSS load size (# of bytes) into R9
        e_or2i       r9, __BSS_SHARE_SIZE@l       # Load lower BSS load size into R9 and compare to zero
        e_cmp16i     r9,0
        e_beq        bss_share_end                # Exit if size is zero (no data to initialise)

        mtctr        r9                            # Store no. of bytes to be moved in counter

        e_lis        r5, __BSS_SHARE_START@h      # Load upper BSS address into R5 (from linker file)
        e_or2i       r5, __BSS_SHARE_START@l      # Load lower BSS address into R5 (from linker file)
        e_subi       r5, r5, 1                     # Decrement address to prepare for bss_Init_loop

        e_lis        r4, 0x0

    bss_share_loop:
        e_stbu       r4, 1(r5)                     # Store zero byte into BSS at R5 and update BSS address
        e_bdnz       bss_share_loop               # Branch if more bytes to load

    bss_share_end:

3,向量表初始化过程。

        同data段初始化过程一致,对向量表赋初值。

2.3 main

        main开始,需要对系统做进一步初始化,根据实际工程,初始化MPU、中断、核启动和时钟初始化。基本流程如下。

2.3.1 MPU初始化

        在正式启动多核前,尽可能早的初始化MPU,这可以有效避免错误得内存访问,以及share memory可以尽早生效,share memory主要依赖MPU对该段内存得 Cache 抑制属性。

2.3.2 Launch Core

        MPU被正确初始化后,多核在这个阶段可以被启用,通过 MC_ME (Mode Entry Module) 模块,将目标核心启动地址加载到 CADDR (COREx Core Address Register) 中,以启动目标核。

        在此之前,应注意当前模式设定,使能 CCTL (COREx Core Control Register) 寄存器中得模式使能位。详细参考2.3.4章节。

	/* enable core 0 in all modes */
	MC_ME.CCTL1.R = 0x00FE;
	/* Set Start address for core 0: Will reset and start */
    MC_ME.CADDR1.R = 0x1000000 | 0x1;
    /* enable core 1 in all modes */
	MC_ME.CCTL3.R = 0x00FE;
    /* Set Start address for core 1: Will reset and start */
    MC_ME.CADDR3.R = 0x1000000 | 0x1;
    /* Switching mode enables the Launch Core to take effect */
    MC_ME_StPtr->MCTL.R = 0x40000000U | 0x5AF0U;
    MC_ME_StPtr->MCTL.R = 0x40000000U | 0xA50FU;
    while (MC_ME_StPtr->GS.B.S_MTRANS == 1U){}

        在后续由 Launch Core 过程启动的核心,不需要再次初始化SRAM以及各共享段和全局段数据,但栈信息和Cache等独属于单个核心的系统仍需要进行初始化。

        某工程启动实例如下。

2.3.3 中断向量初始化

        启动多核后,每个核心均需要初始化自己得中断向量,他们是独立的。

        在这里,我们设定内核中断向量相关寄存器 —— IVPR(Interrupt Vector Prefix Registers),当CPU检测到异常时,会通过这个寄存器得地址获得中断服务函数。

SetIVPR ((unsigned int) &VTABLE);

        系统得异常定义如下图所示。

        通过MAP文件,我们可以看到VTABLE下得内容为 CPU 得所有异常接口。

                0x01001000       0xf4 ./src/Vector.o
                0x01001000                VTABLE
                0x01001000                IVOR0_Vector
                0x01001010                IVOR1_Vector
                0x01001020                IVOR2_Vector
                0x01001030                IVOR3_Vector
                0x01001040                IVOR4_Vector
                0x01001050                IVOR5_Vector
                0x01001060                IVOR6_Vector
                0x01001070                IVOR7_Vector
                0x01001080                IVOR8_Vector
                0x01001090                IVOR9_Vector
                0x010010a0                IVOR10_Vector
                0x010010b0                IVOR11_Vector
                0x010010c0                IVOR12_Vector
                0x010010d0                IVOR13_Vector
                0x010010e0                IVOR14_Vector
                0x010010f0                IVOR15_Vector

        除系统异常外,所有外部中断均通过 IVOR4 (External Input Interrupt) 进入到 INTC (Interrupt Controller) 模块,所有外部中断最终由INTC接管。因此在初始化IVPR后,还需要对INTC模块进行相应设置。

        外部中断可支持1024个中断源,我们需要一个足够大得数组来存储各外设模块得中断向量,并将其配置到INTC得 IACKR (INTC Interrupt Acknowledge Register) 中。

uint32_t __attribute__ ((section (".intc_vector_table"))) IntcIsrVectorTable[];
/* Set INTC ISR vector table base addr. */
INTC.IACKR[coreId].R = (uint32_t) &IntcIsrVectorTable[0];

        除此之外,还需要建立 IVOR4 与 INTC 得联系,在产生 IVOR4 异常后,需要将其接入到INTC,这段程序我们在 IVOR4 异常服务函数中完成。

    .globl   IVOR4_Handler
    .equ  INTC_IACKR, 0xfc040028  ;# Interrupt Acknowledge Register address
    .equ  INTC_EOIR,  0xfc040038   ;# End Of Interrupt Register address
    .align 4
IVOR4_Handler:
prologue:
    e_stwu      r1,-0x50 (r1)           ;# Create stack frame and store back chain
    e_stmvsrrw      0x0c (r1)           ;# Save SRR[0-1] (must be done before enabling MSR[EE])
    se_stw      r3, 0x08 (r1)           ;# Save working register (r3)
    e_lis       r3, INTC_IACKR@ha       ;# Save address  of INTC_IACKR in r3
    e_lwz       r3, INTC_IACKR@l(r3)    ;# Save contents of INTC_IACKR in r3 (this is vector table address)
    wrteei      1                       ;# Set MSR[EE] (must wait a couple clocks after reading IACKR)
    se_lwz      r3, 0x0(r3)             ;# Read ISR address from Interrupt Vector Table using pointer
    e_stmvsprw      0x14 (r1)           ;# Save CR, LR, CTR, XER
    se_mtLR     r3                      ;# Copy ISR address (from IACKR) to LR for next branch
    e_stmvgprw      0x24 (r1)           ;# Save GPRs, r[0,3-12]
    se_blrl                             ;# Branch to ISR, with return to next instruction (epilogue)

epilogue:
    e_lmvsprw       0x14 (r1)           ;# Restore CR, LR, CTR, XER
    e_lmvgprw       0x24 (r1)           ;# Restore GPRs, r[0,3-12]
    e_lis       r3, INTC_EOIR@ha        ;# Load upper half of INTC_EOIR address to r3
    mbar                                ;# Ensure prior clearing of interrupt flag conmpleted.
    wrteei      0                       ;# Disable interrupts
    e_stw       r3, INTC_EOIR@l(r3)     ;# Load lower half of INTC_EOIR address to r3 and
                                        ;# write contents of r3 to INTC_EOIR
    se_lwz      r3, 0x08 (r1)           ;# Restore working register (r3) (original value)
    e_lmvsrrw       0x0c (r1)           ;# Restore SRR[0-1]
    e_add16i    r1, r1, 0x50            ;# Reclaim stack space
    se_rfi                              ;# End of Interrupt Handler - re-enables interrupts

        下图是ADC产生中断时,程序执行得示意图:IVOR4 -> INTC -> ADC_ISR

2.3.4 时钟和模式初始化

1 模式:

        MPC5777M提供了多种模式,根据系统需求,用户可从一种模式快速切换到另一种模式。

        这其中 DRUN、SAFE、TEST、RUN0 - RUN3为运行模式。模式描述如下图。

        CCFC3007 新增一低功耗模式,Standby Mode,Standby相比HALT和STOP有更好的低功耗等级。

        各个模式转换关系如下图。

        系统上电后,会通过RESET进入到DRUN,通常我们会在RUN0上根据工程需求设定时钟及外设配置(主要配置内容有时钟源使能和系统时钟选择),在配置完成后,将模式切换到用户模式RUN0。

       外设及核运行状态是全局配置,可动态分配某外设或某核在RUN0模式下运行,在RUN1模式下停止。例如:

        Core运行配置:

ModeDRUNRUN0RUN1RUN2RUN3
Core 0ON/OFFON/OFFON/OFFON/OFFON/OFF
Core 1ON/OFFON/OFFON/OFFON/OFFON/OFF
Core 2ON/OFFON/OFFON/OFFON/OFFON/OFF

        外设运行配置:

ModeDRUNRUN0RUN1RUN2RUN3
RunConfig 0ON/OFFON/OFFON/OFFON/OFFON/OFF
……ON/OFFON/OFFON/OFFON/OFFON/OFF
RunConfig 7ON/OFFON/OFFON/OFFON/OFFON/OFF
PeripheralRunConfig
PortRunConfig 0-7
Adc0RunConfig 0-7
……RunConfig 0-7
Spi8RunConfig 0-7

        注意:修改系统配置,需要通过模式切换,才可使修改生效,可以是CurrentMode->CurrentMode。

2 时钟:

        MPC577M提供了四个时钟源,FIRC,XSOC,PLL0 和 PLL1,由CGM (Clock Generation Module) 和MC_ME模块控制时钟选择和分频。

        时钟源 (IRC\XOSC\PLL) 使能,以及系统时钟源选择,由模式控制寄存器 (MC_ME.<Mode>_MC) 控制。

        时钟结构:

        CGM与时钟线对应关系:

System ClockSC_DC0Fast XBAR / Core Z4_2
SC_DC1Slow XBAR
SC_DC2PBRIDGEx
SC_DC3Core Z7_0 / Core Z7_1 / Core Z7_0s
SC_DC4CLK_OUT
Peripheral ClockAC0_DC0PER_CLK
AC0_DC1SD_CLK
AC0_DC2SAR_CLK
AC0_DC3DSPI_CLK0
AC0_DC4DSPI_CLK1 / LIN_CLK
AC1_DC0LFAST_PLL_CLK
AC2_DC0FRAY_PLL_CLK
AC2_DC1SENT_CLK
AC5_DC0PSI5_F189_CLK
AC5_DC1PSI5_F125_CLK
AC5_DC2PSI5_1US_CLK
AC6_DC0SYS_CLK0
AC7_DC0SYS_CLK1
AC8_DC0CAN_CLK
AC9_DC0RTI_CLK
AC10_DC0FEC_REF_CLK

        时钟线与外设对应关系:

3 初始化过程

        (1) 配置时钟源,IRC、XOSC、PLL

        (2) 配置系统时钟分频 CGM.SC

        (3) 配置外设时钟使能、时钟源选择、分频 CGM.AC

        (4) 配置外设运行模式(在那种模式下运行)RunPCn & PCTLn。

        (5) 切换当前模式到目标模式,同时启用时钟源(IRC、XOSC、PLL),不切换系统时钟。

        (6) Wait for the PLL to lock.

        (7) 通过模式切换,修改当前系统时钟为目标时钟(CurrentMode -> CurrentMode)。

4 时钟配置示例:

IRC 16M
XOSC 40M
PLL0Output Frequency(PHI0)300M
 Clock SourceXOSC / 40M
 Input Divider2
 Loop Multiplication15
 PHI0 Reduced Divider1
 PHI1 Reduced Divider6
 VCO Frequency600M
 PHI0 Output Frequency300M
 PHI1 Output Frequency50M
PLL1Output Frequency(PHI0)600M
 Clock SourceXOSC / 40M
 Loop Multiplication30
 PHI0 Reduced Divider1
 VCO Frequency1200M
 PHI0 Output Frequency600M
   
Clock Source PLL1 / 600M
Z4_2 200M
 Z4_2 Clock Divider3
Z7_0 300M
 Z7_0 Clock Divider2
Z7_1 300M
 Z7_1 Clock Divider2
Fast XBAR 200M
 FXBAR Clock Divider3
Slow XBAR 100M
 SXBAR Clock Divider6
PBRIDGE 50M
 PBRIDGE Clock Divider12
CLKOUT 50M
 CLKOUT Clock Divider12
Clock Source PLL0 / 300M
PERCLK 60M
 PERCLK Clock Divider5
SD_CLK 20M
 SD_CLK Clock Divider15
SAR_CLK 30M
 SAR_CLK Clock Divider10
DSPI_CLK0 100M
 DSPI_CLK0 Clock Divider3
DSPI_CLK1 100M
 DSPI_CLK1 Clock Divider3
Clock Source PLL0 / 300M
LFAST PLL 25M
 LFAST PLL Clock Divider12
Clock Source PLL0 / 300M
FRAY_CLK 30M
 FARY_CLK Clock Divider10
SENT_CLK 100M
 SENT_CLK Clock Divider3
Clock Source PLL0 / 300M
PSI5_f189_CLK 6048387
 PSI5_f189_CLK Clock Divider4960
 PSI5_f189_CLK Divider Format2
PSI5_f125_CLK 3M
 PSI5_f125_CLK Clock Divider100
PSI5_1us_CLK 1M
 PSI5_1us_CLK Clock Divider300
Clock Source PLL0 / 300M
SYSCLK0 100M
 SYSCLK0 Clock Divider3
Clock Source PLL0 / 300M
SYSCLK1 100M
 SYSCLK1 Clock Divider3
Clock Source PLL0 / 300M
CAN 50M
 CAN Clock Divider6
Clock Source XOSC / 40M
RTI 40M
 RTI Clock Divider1
Clock Source XOSC / 40M
FEC_REF 40M
 FEC_REF Clock Divider1
   
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值