當前位置:
首頁 > 最新 > Qorivva MPC56xx系列MCU啟動過程全解析

Qorivva MPC56xx系列MCU啟動過程全解析

內容提要

引言

1. 複位函數__startup()概述與解析

2. 初始化函數__init_registers()解析--棧指針(R1)和小數據指針(SDA2-->R2/SDA-->R13)初始化

3. 初始化函數__init_hardware()解析--內核(FPU和SPE)和板級硬體(看門狗、SRAM的ECC、內核系統異常IVORx以及片上外設中斷控制器INTC)初始化

3.1 初始化函數INIT_Derivative()--看門狗和SRAM ECC初始化

3.2 初始化函數EXCEP_InitExceptionHandlers()--e200內核系統異常IVORx向量表初始化

3.3 初始化函數INTC_InitINTCInterrupts()--INTC模塊初始化-->外設中斷向量表設置和中斷向量模式(軟體/硬體向量模式)配置

4. 初始化函數__init_data ()解析

5. 初始化函數__init_user()解析

總結

引言

本文就基於一個新建的CodeWarrior 10.6/7 Qorivva MPC5604B的應用工程,結合其啟動文件、鏈接文件(*.lcf)和編譯鏈接結果map文件,給大家詳細解析在CodeWarrior 10.6/7 IDE中,默認的Qorivva MPC56xx系列MCU的啟動過程。

1.複位函數__startup()概述與解析

從CodeWarrior 10.6/7 Qorivva MPC5604B的應用工程編譯結果map文件最開始的函數調用關係圖可以看到,Qorivva MPC56xx系列MCU的啟動過程--從複位函數運行至跳轉到用戶程序入口main()函數的一些初始化過程和函數調用如下:

複位函數為__startup(),在工程默認添加的啟動文件文件MPC5604B_init_flash.c中通過指定在.__bam_bootarea段中的啟動半字及複位向量常數定義中指定(#pragma section sconst_type ".__bam_bootarea")

Tips:如果編譯目標為RAM,則相應使用的文件為MPC5604B_init_ram.c;

而在MPC5604B_Startup.c中定義的__startup(),則直接跳轉到函數_start(),所以真實的啟動函數應該是_start():

在工程的鏈接文件中,將相應的.__bam_bootarea段分配到了Flash的最開始地址的8個位元組(0x0000_0000~0x0000_0007):

在相應工程編譯結果map文件中,可以看到__bam_bootarea 段的鏈接結果(section layout):地址0x0000000開始的第一個32位(4位元組),存放的是啟動半字(bam_rchw)--高16位,低16位補零;接下來的第二個32位(4位元組),存放的則是我們的複位向量(bam_resetvector),即複位函數__startup()的地址:

接下來我們繼續看在實際的啟動函數_start()中,又做了哪些初始化工作。

啟動函數_start()定義在新建工程時選擇的EWL_Runtime運行時庫中,用戶默認不可修改:

我們可以通過展開工程elf文件的方式打開位於CW10.6安裝目錄下的__start.c源文件:

在__satrt.c中我們可以看到__start()的定義,首先其最內核的存儲器大小端進行設置(通過讀取編譯器設置,配置MSR寄存器的MSR[LE] 和MSR[ILE]位實現,對於Qorivva MPC56/57xx系列MCU使用的PowerPC e200z系列內核其MSR寄存器中未定義MSR[LE] 和MSR[ILE]位,只能使用大端模式):

接著,其通過彙編語言依次調用四個初始化函數__init_registers()、__init_hardware()、__init_data()以及__init_user(),完成對按照EABI(Embedded Application Binary Interface--嵌入式應用程序二進位介面)約定的C語言堆棧設置和好小數據區域(small data area)初始化、板級硬體初始化、RAM的copy-down初始化(.data和.bss段數據RAM初始化和)工作以及用戶個性化初始化(比如對片上Flash訪問匯流排buffer的時鐘,時鐘初始化等),最後跳轉到用戶程序main()函數,以下為該過程在工程map文件中的函數層級調用圖:

下面我們具體來看看在__start()函數中調用的4個__init_初始化函數:

2. 初始化函數__init_registers()解析--棧指針(R1)和小數據指針(SDA2-->R2/SDA-->R13)初始化

首先,來看初始化函數__init_registers(),其與__start()函數同在__start.c中定義,其中首先對stack進行了初始化,然後再對系統的小數據區進行初始化,這裡是根據PowerPC的EABI標準,將鏈接文件中定義且在鏈接結果中產生的相應結果初始化給相應的CPU調用寄存器,stack-->R1(堆棧指針);_SDA2_BASE-->R2;_SDA_BASE-->R13;

Tips:

A. 用於stack初始化的符號_stack_addr 是在工程鏈接文件中通過「_stack_addr = ADDR(stack)+SIZEOF(stack)」定義的,從該定義中可以看到該工程的stack是向下生長的(棧頂為stack最高地址 + 1);

B. 符號_SDA2_BASE和_SDA_BASE是由鏈接器(linker)自動生成的符號,在工程map文件文件最後我們可以看到相應的用於初始化R2和R13的鏈接結果,從工程map文件中可以看到該工程並未使用小數據區:

工程map文件中的符號_SDA2_BASE和_SDA_BASE鏈接結果:

3. 初始化函數__init_hardware()解析--內核(FPU和SPE)和板級硬體(看門狗、SRAM的ECC、內核系統異常IVORx以及片上外設中斷控制器INTC)初始化

接下來我們看看,定義在__ppc_eabi_init.c中的初始化函數__init_hardware(),從下圖中可以看到其主要完成對內核硬體FPU(Float-point Process Unit)--浮點數處理單元和SPE(Signal Process Engine)--信號處理引擎的初始化和使能工作,最後調用usr_init()實現用戶的一些硬體初始化工作:

Tips:

A. FPU和SPE只有PowerPC e200z3及以上的內核才有,MPC560x系列使用的是e200z0內核,沒有FPU和SPE:

下圖為e200z0/z0h內核的MSR寄存器定義,沒有SPE和FP兩個位:

以下截圖為e200z6的內核MSR寄存器定義:

B.usr_init()定義在MPC5604B_init_flash.c(編譯目標為FLASH-->MPC5604B_init_flash.c;編譯目標為RAM-->MPC5604B_init_ram.c)中:

其調用了三個初始化函數INIT_Derivative()、EXCEP_InitExceptionHandlers()和INTC_InitINTCInterrupts()。

3.1 初始化函數INIT_Derivative()--看門狗和SRAM ECC初始化

其中,INIT_Derivative()定在文件MPC5604B_HWInit.c中,該函數先對內核看門狗進行初始化(默認關閉看門狗):然後使用工程鏈接文件中定義的符號L2SRAM_LOCATION--SRAM開始地址和L2SRAM_CNT(RAM大小/128),通過將CPU寄存器(R0~R31,每個通用寄存器都是32位寬,即4位元組,總共32個通用寄存器就是4*32=128位元組,所以循環次數為RAM大小/128)寫入SRAM的方式完成其ECC初始化:

符號L2SRAM_LOCATION--SRAM開始地址和L2SRAM_CNT(RAM大小/128)在工程map文件中的鏈接結果如下:

3.2 初始化函數EXCEP_InitExceptionHandlers()--e200內核系統異常IVORx向量表初始化

函數EXCEP_InitExceptionHandlers()則定義在文件Exceptions.c中,其完成了對PowerPC e200內核IVPR(Interrupt Vector Prefix Register--中斷向量前綴址寄存器)的初始化,其中使用的符號EXCEPTION_HANDLERS在工程鏈接文件中定義,在鏈接文件結果map文件中可以查到相應的鏈接結果,即用於初始化IVPR寄存器的值(如上圖,為0x1000):

Tips:在PowerPC e200z0/z0h內核中,IVPR寄存器提供了CPU內核系統異常IVORx向量表的基地址偏移設置,注意其只有最高20位有效(大端模式),低12位保留,所以其IVORx異常處理向量表必須4KB(0x1000)地址對齊:

3.3 初始化函數INTC_InitINTCInterrupts()--INTC模塊初始化-->外設中斷向量表設置和中斷向量模式(軟體/硬體向量模式)配置

而函數INTC_InitINTCInterrupts()則定義在文件IntcInterrupts.c中,其完成了外設中斷相關的三個初始化工作將軟體向量模式的外設中斷向量表地址賦值給INTC_IACKR寄存器、配置INTC_MCR寄存器,使能軟體向量模式以及使能外設全局中斷(通過將內核MSR寄存器的MSR[EE]位置一):

Tips:

A. 關於INTC(Interrupt Controller--中斷控制器,請參考具體的MCU晶元參考手冊(Reference Manual),其不再內核手冊中),在函數INTC_InitINTCInterrupts()中主要用到了以下2個INTC的配置寄存器:

INTC_MCR--中斷控制器的模塊配置寄存器,其最低位HVEN為硬體向量模式使能位,默認的CodeWarrior 工程都是配置為0--使用軟體向量模式:

INTC_IACKR--中斷控ACK寄存器,其只有最高20位可用(可寫),為中斷向量基地址(VTBA)設置,默認CodeWarrior 工程都是將外設中斷向量表(INTCInterruptsHandlerTable[INTC_INTERRUPTS_REQUEST_VECTOR_TABLE_SIZE])放在SRAM中,通過調用函數INTC_InstallINTCInterruptHandler(),在外設初始化時再將中斷ISR函數地址寫入到該數組中,來完成中斷向量表的初始化:

定義在文件IntcInterrupts.c中的外設中斷向量表INTCInterruptsHandlerTable[INTC_INTERRUPTS_REQUEST_VECTOR_TABLE_SIZE]:

工程map文件中相應的鏈接結果,被鏈接到SRAM的起始地址0x4000_0000(size=(210*4)/16 = 0x384):

定義在文件IntcInterrupts.c中的函數INTC_InstallINTCInterruptHandler()完成外設中斷ISR的初始化和中斷優先順序設置:

B. 所謂PowerPC e200系列內核的外設中斷軟體向量模式就是所有的外設中斷都由CPU系統異常IVOR4統一管理/響應,好處是每個外設中斷的中斷響應壓棧和出棧都是統一的,可以節省代碼空間,缺點是響應慢,因為需要考慮所有可能使用到的通用寄存器和特殊寄存器以及中斷產生是的上下文保護與恢復;而硬體向量模式,則不通過IVOR4來統一管理/響應外設中斷,而是每個外設中斷都可以自己編寫中斷響應壓棧和出棧控制,其優點是響應速度快,實時性好,可以個性化進行壓棧和出棧、缺點是需要佔用更多的代碼空間,且對編程者的要求極高(需要考慮優化保證性能和功能正常),關於Qorivva的系統異常和外設中斷響應,我後期計劃專門寫一篇文章來詳細介紹,敬請關注。

4. 初始化函數__init_data ()解析

接下來,我們接著介紹在__start()函數中調用的第三個初始化函數__init_data (),其在__start.c中定義,其中主要完成對SRAM中的.data段和.bss段的初始化工作,其中.data段為全局變數有初始化值(且不為0)的區域,所以其初始化時需要從Flash中將其初始化值拷貝到相應的運行時SRAM地址,從而完成初始化;而.bss段為全局變數無初始化值(或者初始化值為0)的區域,對該區域的初始化,只需要直接向相應的地址寫0即可:

Tips:在初始化函數__init_data ()中用到了兩個PPC EABI鏈接器默認用於RAM初始化的結構體_rom_copy_info和_bss_init_info用於存放RAM初始化信息,其值有鏈接器根據鏈接結果自動進行初始化:

5. 初始化函數__init_user()解析

接著之前,我們介紹最後一個初始化函數__init_user(),其定義在文件__ppc_eabi_init.c中, 其主要完成了以下三個工作:heap分配和初始化、C++相關初始化和片上Flash配置:

其中前兩項工作對於我們常用的C語言應用工程來說都是不會使用的,所以也不會調用相應的函數進行初始化,而第三項工作對Flash的配置時通過調用函數FlashConfig()實現的:

Tips:

A. 在函數FlashConfig()其通過配置函數拷貝到RAM中(這裡為使用局部數組變數拷貝到Stack上)執行來配置Flash,原因是配置Flash控制器的時候不允許繼續訪問Flash(即直接從Flash上取指,與Flash驅動運行時的要求(不能read-while-write)類似,不允許read/write-while-configure);

B. 該函數中主要通過配置片上C-Flash控制器的PFCR0--Platform Flash Configuration Register 0平台Flash配置寄存器0,其主要是根據內核和匯流排工作頻率配置不同的CFlash bank的訪問等待周期,需要根據實際的內核和匯流排工作頻率進行配置已達到最近性能(具體請參考MCU的參考手冊FLASH控制器相關章節):

C、用戶如需要修改FLASH的配置可以通過修改定義在文件__ppc_eabi_init.c最開始的兩個宏定義--FLASH_REG和FLASH_DATA來完成:

完成以上初始化工作之後,在_start()函數的最後就可以跳轉到main()函數執行用戶代碼了。

總結

本文詳細解析了CodeWarrior 10.6/7 IDE下Qorivva MPC560x系列MCU的啟動過程,由於CodeWarrior 2.10與CodeWarrior 10.6/7 IDE使用相同的工具鏈(Assembler,Compiler和Linker),所以本文的解析同樣適用於CodeWarrior 2.10工程。對於多核MCU以及e200z3及以上版本內核的MCU其過程與MPC560x系列的單核e200z0/z0h相比,其啟動過程稍有差異(主核首先boot,然後再啟動和使能/配置其他內核讓其開始啟動,e200z3及以上版本內核如果選擇了硬體FPU和SPE還需要對其進行初始化),在本文介紹的方法和結論基礎上就能夠非常容易的了解和掌握了。

從本文的解析過程可以看到,CodeWarrior 10.6/7 IDE下Qorivva MPC560x系列MCU的啟動過程有EWL運行時庫中的__start()函數定義,但留出了很多的用戶介面,用戶可以通過修改工程的鏈接文件以及啟動文件,實現個性化的啟動過程。以下截圖總高亮出不同的編譯目標下可以修改的文件:

A. 編譯目標為FLASH:

B. 編譯目標為RAM:

Tips:關於CodeWarrior 2.10/ 10.6/7工程的鏈接文件使用,建議讀者參考官網的應用筆記:

AN4497.CodeWarrior Linker Command File (LCF) for Qorivva/PX.pdf


喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!

TAG: |