i.MX rt 系列微控制器的學習記錄

北緯三十度夏至發表於2022-04-10

雜記

前言

我總是很希望自己能產生一種感知電壓變化的能力,就像B站上的教學動圖中,電流從電源流出時導線就像LED亮起來一樣,我將指尖觸到導線上就能感受到實時的電壓變化。我在上學和工作時經常由於無法理解或者認知錯誤陷入非常迷惘和痛苦中,比如在我理解數學和電磁場的基本理論時,或者我的程式碼執行中出現了我認為不可能出現的現象。前者在我不轉行的前提下暫時不會遇到,而後者幾乎就是我工作的常態了。

瞭解微控制器內部是怎麼執行的對一個微控制器工程師來說極其重要。在新手無知時期我以為學習微控制器就是學習外設,畢竟當時我就用用串列埠、ADC和定時器,再點兩個燈。啟動時硬體做了什麼,程式碼存在哪裡,CPU跑起來後怎麼取指令,資料儲存在哪裡,C語言怎麼跑起來的,堆在哪裡,棧在哪裡,變數在哪裡,常量在哪裡,外設怎麼初始化,收發怎麼進行,中斷觸發條件是否設定好了,中斷優先順序是否設定合理了,標誌位復位的時機是否適合,針對指標的處理是否讀錯了範圍,寫操作有無越界,任務的設定、業務邏輯的設計是否合理,狀態機的設計的容錯效能夠不夠……邏輯怎麼在跑、程式什麼時候崩掉永遠是個疑問。

微控制器是怎麼啟動的?以i.MX rt 1060為討論物件

我們的程式碼大多從main函式開始執行,而且電子工程師習慣使用C語言進行程式設計。微控制器在進入main函式之前進行了哪些操作呢?從硬體上講,從上電到CPU執行第一條指令前,再從CPU執行第一條程式碼到進入main函式時,各進行了哪些操作呢?

其實我並不知道微控制器硬啟動做了什麼,如果我是設計師,大概是檢查供電是否正常或溫度是否正常吧,再確定指令的載入位置。對於我們使用者來講,有個要點是如何決定CPU從哪個儲存器載入第一條程式碼,或者說把哪裡的程式碼對映到0位置。ST的手冊說是SYSCLK的第4個上升沿鎖存BOOT引腳的值,決定從Boot,SRAM還是從flash啟動,CH32V307的參考手冊中也是這樣。

恩智浦的跨界微控制器在18年開始被我關注,當時我被它宣稱的600MHz主頻深深迷住。如此強悍的效能想必能做出很多有意思的東西。然而從入手至今,我也沒有用它做出能拿得出手的東西,非常尷尬。原因其實非常簡單,我並不會用這個晶片,想把這個晶片的外設用起來,並設計合理的程式讓核心不受約束地跑起來其實並不簡單。

很多微控制器上電後必然會先執行廠商自帶的Boot程式,Boot程式根據其他的資訊(比如引腳狀態)選擇工作模式,或者進行從外部的串列埠或者USB下載程式碼,比如i.MX rt系列。微控制器啟動過程可以分為硬體啟動和軟體啟動,從上電到核心執行第一條程式碼前的這段時間可以稱之為硬體啟動,從核心開始執行第一條程式碼開始到進入main函式即為軟體啟動。感謝前人制定了C語言的標準,讓我們能以一個共識編寫啟動之後的程式碼。

啟動模式

注意本文是對i.MX rt 1060參考手冊的個人理解,必然存在錯誤。所謂的跨界微控制器想必就是恩智浦用cortex m7的核心搭配它以往設計的外設做出來的奇特產品,最麻煩的一條是,它沒有供使用者存放程式碼的大容量非易失性儲存器——是的,它需要外接flash。我挺不能理解這點的,1064不就合封了flash嗎?按照這個晶片最廣泛的用法,大家肯定會外接一個8腳的QSPI nor-flash,然後配一個SDRAM,程式碼就存在nor-flash中。當我們將Boot_mode[2]的兩個位設為00b的時候,晶片可以從外部裝置啟動,而後面通過BOOT_CFGx[X]這幾個位可以設為特定的裝置啟動。其實這裡非常複雜,因為3種啟動模式中的 Boot From Fuses和Internal Boot其實挺相似,他們的用法被啟動所使用的熔絲及其位域定義給環環相套得麻煩且複雜。首先Boot_mode[2]兩位為0時是從Boot From Fuses啟動,這個啟動會參照Boot eFUSE的值BT_FUSE_SEL這位,如果這位是0,就直接跳到序列下載器(Serial Downloader,就是Boot_mode[2]=1時的工作模式),如果是1,則按照Boot eFUSE的其他配置位(BOOT_CFGx[X])選擇從哪裡啟動。Boot_mode[2]兩位為2時,如果BT_FUSE_SEL位是0,則查詢BOOT_CFGx[X]的對映pad的狀態,這些Pad的狀態決定BOOT_CFGx[X]的值;如果BT_FUSE_SEL為1,則由Boot eFUSE的BOOT_CFG1[7:4]位來確定,注意需要注意的是,此時Boot eFUSE其他各位也各有新的定義。在i.MX rt系列中我發現了挺多熔絲(Fuse)的,其實熔絲是對晶片初始設定的一些定義,我發現相當多的暫存器定義在熔絲中是有體現的,在熔絲中甚至能對SDRAM的SEMC進行初始化配置,而熔絲的性質決定其是一次性程式設計(OTP)的,初始化應該都是0,經修改後為1,這為使用者的產品化提供了方便,畢竟可以對晶片硬啟動狀態也進行配置,與將配置存在flash中相比,熔絲不在哈佛結構的4GB絕對地址空間中,需要特殊的操作,這樣可以避免誤操作。Boot_mode[2]兩位為1時即為從外部串列埠或者USB口進行配置,我沒有深入研究不敘述。

所以在出廠狀態下(未對Boot eFUSE進行程式設計狀態下),應將Boot_mode[2]設為01b,將BOOT_CFG1[7:4]設為0000b,即可實現在QSPI nor-flash中儲存程式碼。

nor-flash存放的image

1060以特定的配置和nor-flash通訊上後,也不能直接從0地址開始讀程式碼就當是第一條指令了,1060預設的載入nor-flash的配置應該會存在熔絲中,但是1060的預設設定只是比較基礎的一部分,1060以這個基礎的配置和nor-flash進行起初的通訊。在微控制器正常跑起來時,應以最優化的通訊配置和nor-flash進行通訊,因此還有部分針對所選nor-flash的配置內容需要從nor-flash中載入,也就是說nor-flash儲存的image中儲存的不只是程式碼段和讀寫的資料。由於這個nor-flash不合封在一個chip中,所以nor-flash中的資料分佈和傳統微控制器的flash是不一樣的。

由於1060將nor-flash對映到0x6000-0000的區域,這裡做個實驗,將0x6000-0000開始向後偏程式大小再偏8KB空間位置的內容列印出來。程式碼如下圖。

圖一 列印出全部image

偏移8KB是個經驗值,實際上我確實成功得列印出全部image,一直列印到全ff區域了。

從分散載入檔案和手冊裡可以看出,儲存在nor-flash中image分為配置引數(Configuration parameter),映象向量表(Image vector table,IVT),啟動資料(Boot data)域, 裝置配置資料(Device configuration data,DCD),後面才是傳統微控制器映象bin檔案中常見的中斷向量表,程式碼段和資料段。

配置引數

從列印的資料來看,第一段,是配置儲存映象的flash的引數(Configuration parameter),大小為512(0x200)位元組,這裡擷取一部分:

圖二 配置引數截圖的一部分

根據參考手冊FlexSPI Configuration Block及Serial NOR configuration block這兩節的解釋,這裡選部分解析下:

表一 配置引數各域定義分析

偏移(位元組)

域名

含義

0x42464346

0

Tag

FCFB的ascii碼

0x56010400

4

版本

1.40版本

0x01

0xC

讀樣本時鐘源

DQS內部迴環

0x03

0xD

片選保持時間

預設應該是0x03

0x03

0xE

片選建立時間

預設應該是0x03

0x00

0xF

卷地址寬度

 

0x00

0x44

裝置型別

1是序列nor,2是序列NAND;

(這個位元組為什麼不填1呢)

0x04

0x45

Flash引腳型別

4腳,即QSPI

0x06

0x46

序列時脈頻率

100MHz

0x0a1804eb

0x80

查詢表(從此起始)

256位元組的查詢表

0x00000100

0x1C0

頁大小

一頁256位元組,不被ROM使用

(後面半句看不懂)

0x00001000

0x1C4

扇區大小

每個扇區4KB,不被ROM使用

(後面半句看不懂)

0x00000000

0x1C8

時鐘速度

不改變。不被ROM使用。

(不改變的意思應該是繼續使用熔絲裡的設定,全程使用30MHz)

(後面半句看不懂)

可將其中原來由熔絲配置的內容根據選定的nor-flash型號重新配置一次,達到最優通訊技能。配置引數中也有比如查詢表這種更為複雜的部分,還有一些看似重複的設定。這裡並不追求每個位都瞭解,知道有這個部分就行了,未來根據需要再詳細瞭解。

映象向量表

映象向量表(Image vector table,IVT)是一個指示映象各部分位置的向量表,是放在固定位置的,nor-flash是放在4KB的地方。這裡擷取IVT的內容:

圖三 IVT截圖

根據參考手冊Image vector table structure這節的解釋,這裡嘗試人肉解析下:

表二 映象向量表部分的解析

偏移(位元組)

域名

含義

0x412000d1

0

版本:A,長度32位元組

0x60002000

4

入口

映象的第一條可執行指令的絕對地址

0x000022ac

12

DCD地址

裝置配置資料(DCD)的絕對地址

0x60001020

16

Boot資料

啟動資料域的絕對地址

0x60001000

20

自己

指示自己(IVT)的絕對地址

啟動資料域

    啟動資料(Boot data)域只有一個字的大小,它的作用就是指示整個映象的地址和長度。

圖四 啟動資料域的截圖

    這個域存放在0x6000-1020的地址,整個映象放在0x6000-0000處,大小為8MB,後面的元件標誌就先不管了。啟動資料域的地址也符合映象向量表的說明,映象也確實存放在0x6000-0000的位置,從FLEX-SPI啟動就從FLEX-SPI執行唄,映象就存在nor-flash上,不需要跳到別的位置取映象。

裝置配置資料

裝置配置資料(Device configuration data,DCD),這個簡寫很有迷惑性啊。這個資料按照參考手冊的說法,其存在的意義是在使用之前對某些外設的暫存器進行配置讓其能夠直接使用。挺,ummm,無語的。我想到一個應用就是在__mian進行分散載入的RW資料搬運前就將SDRAM初始化好,免得在應用資料中初始化一次並自己進行分散載入。IVT指示這個功能存放在0x000022ac,這裡就有點疑惑了。

0x000022ac這個地址是在ITCM空間的。DCD資料由軟體來定義,即為SDK中evkmimxrt1060_sdram_ini_dcd.c的dcd_data[]陣列,在我實驗的工程中為內容就一個位元組,且為0,我也確實在映象向量表指示的CDC的位置找到了這個0。但是ITCM的內容佈局是由分散載入檔案確定的,而且起碼在上電後確定ITCM的大小和硬體位置時,ITCM中都是亂碼,這就要求從nor-flash拷貝資料到ITCM中這個行為應該也是由CPU實現的,屬於軟啟動的一部分。CDC程式碼被設計存放到ITCM的空間,似乎就能說明CDC段執行時間是在分散載入之後。可能就是__mian中的一個部分。

使用者程式程式碼

    這裡才是傳統微控制器的映象檔案部分,即為中斷向量表,程式碼段,只讀資料段和讀寫資料段。按照映象向量表的指示,這部分資料存放在0x6002-0000的位置,按照我的分散載入檔案的佈局設計,使用者程式程式碼段在nor-flash佈局設計為:

表三 使用者程式碼段的佈局

順序

程式碼名稱

解釋

 

startup_MIMXRT1062.O(RESET,+FIRST)

.S檔案中的中斷向量表

 

* (InRoot$$Sections)

 __main

 

system_MIMXRT1062.O(+RO)

system_MIMXRT1062.c的程式碼段

 

startup_MIMXRT1062.O(+RO)

.S檔案中程式碼段

按照上表,使用者程式程式碼的第一部分是中斷向量表,這部分的原始碼為:

圖五 中斷向量表的原始碼

在映象中對應部分的十六進位制程式碼為:

圖六 使用者程式程式碼中的中斷向量表的截圖

都只截圖了部分啊,都太長了。可以看到中斷向量表的第一個字為棧地址,0x2002-0000,DTCM首地址向後128KB的地址,DTCM預設按熔絲設定,其大小剛好也就是128KB,也就棧地址放在DTCM的最後,棧向下生長嘛~其後都是各中斷的位置,0x60002419就是reset中斷的位置,但是別說8位元組對齊了,這個地址連4位元組對不對齊,我查了下map檔案,Reset_Handler還真的被放在這個9結尾的地址,所有的中斷服務函式的地址都不是.s檔案要求的8位元組對齊。無法理解。

中斷向量表共1個棧指標和255箇中斷服務函式的入口,共1KB,其中最重要的就是Reset_Handler,畢竟其他所有的函式都從這裡開始。

按照上表,中斷向量表接下來被佈局的就是"* (InRoot$$Sections)"這個程式碼,看map檔案,這個裡面就是__mian的實現。

接下來就是各種使用者函式的機器指令程式碼了。跟著一些只讀和初始化不為0的變數們。

至此,對i.MX rt 1060的nor-flash映象的解讀就結束了。

i.MX rt 1060的硬啟動和軟啟動

我這裡將硬啟動重新定義為:從上電到核心執行第一條使用者指令前的這段時間可以稱之為硬體啟動,將核心執行使用者的第一條指令開始即認為開始軟啟動。但實際上我所知的仍然非常之少,這裡仍然僅僅只能是記錄我目前的認知。

第一條使用者程式碼指的是.S裡定義的Reset_Handler裡第一條指令,關閉全域性中斷,CPSID,十六進位制碼為B672。之前應該是也存在核心執行指令的行為的,但是作為使用者這之前我無法干預核心的具體行為,所以也歸入硬啟動的範疇。

硬啟動的過程其實已經在上章隱晦地說完了。1060上電按照OCROM中的廠商程式碼啟動起來,並按照熔絲中的配置驅動通訊外設,1060讀取Boot_mode[2]和BOOT_CFGx[X]的值決定從哪裡去取使用者的指令,我將其配置成從FLEX-SPI的nor-flash啟動,啟動的初始化引數設定由熔絲和BOOT_CFGx[X]共同決定。1060從固定的映象向量表位置去讀配置引數,根據配置引數更加高效地通過FLEX-SPI和nor flash通訊。

問題只在於DCD是在什麼時候執行,我認為其在.S檔案之前執行,由廠商程式碼喚起,屬於硬體啟動的一部分。

硬軟之間

在上述操作完成之後,並不是說核心就取Reset_Handler裡的指令了,核心怎麼會知道Reset_Handler的位置?映象向量表裡可沒有這個引數。映象的第一條可執行指令的絕對地址是0x60002000,核心會取這個地址的值0x2002-0000,為棧指標,即核心將0x2002-0000存進主棧指標暫存器MSP,然後偏移4個位元組取第二個字的值,認定其為Reset_Handler的地址,進入Reset_Handler取第一條指令,關閉全域性中斷,自此硬啟動結束,軟啟動開始。

.S檔案的分析

.S檔案即常說的startup_MIMXRT1062.s檔案,使用者程式碼範疇的啟動檔案。是使用者能控制的第一個執行的程式碼檔案。

那麼.S檔案需要完成什麼任務呢?作為main函式之前的檔案,我覺得它的任務有二:

其一:main函式是由C寫成的,C語言的函式在核心上執行是需要執行環境的,微控制器作為硬體需要為C語言的執行搭建環境,比如,清零RW和ZI段,堆和棧,搬運RW資料;

其二:定義中斷向量表,告知核心中斷向量表的位置;

其三:喚起main函式;

.S檔案中先定義了中斷向量表:

PRESERVE8;要求全文8位元組對齊,聽說8位元組對齊在M7的雙發射結構下執行效率最高

THUMB;使用thumb指令集

AREA RESET, DATA, READONLY;定義一個DATA區,名稱為RESET,只讀

EXPORT __Vectors;匯出__Vectors

EXPORT __Vectors_End;匯出__Vectors_End

EXPORT __Vectors_Size;匯出__Vectors_Size

IMPORT |Image$$ARM_LIB_STACK$$ZI$$Limit|;匯入這個定義

__Vectors DCD |Image$$ARM_LIB_STACK$$ZI$$Limit| ; 棧地址

DCD Reset_Handler ; 復位函式

DCD NMI_Handler;不可遮蔽中斷

DCD HardFault_Handler;硬體錯誤中斷

DCD MemManage_Handler ;儲存管理器中斷

DCD BusFault_Handler ;匯流排錯誤中斷

DCD UsageFault_Handler ;應用錯誤中斷

DCD 0

DCD 0

DCD 0

DCD 0

DCD SVC_Handler

DCD DebugMon_Handler

DCD 0

DCD PendSV_Handler

DCD SysTick_Handler

DCD在彙編中應該是分配一個字的空間的意思。這裡注意和映象中的DCD區分。上文加下來是1060的外設中斷,這裡不繼續列舉。下面討論Reset_Handler中斷的內容。

__Vectors_Size EQU __Vectors_End - __Vectors;定義了__Vectors_Size的意義

AREA |.text|, CODE, READONLY;定義了一個CODE區的程式碼段,只讀

Reset_Handler PROC;函式開始

EXPORT Reset_Handler [WEAK] ;匯出Reset_Handler,且弱定義

IMPORT SystemInit;匯入SystemInit

IMPORT __main;匯入__main

 

CPSID I;關閉全域性中斷

LDR R0, =0xE000ED08;將中斷向量偏移暫存器的地址寫進R0

LDR R1, =__Vectors;將__Vectors寫進R1

STR R1, [R0];將__Vectors(0x6002-0000)寫進中斷向量偏移暫存器,

LDR R2, [R1];取R1指向的值寫進R2,

MSR MSP, R2;將R2的值寫進主棧指標暫存器

LDR R0, =SystemInit;將SystemInit函式的地址寫進R0

BLX R0;執行SystemInit函式

CPSIE I;使能全域性中斷

LDR R0, =__main;將__main的地址載入到R0中

BX R0;執行__main

ENDP;函式結束

其後還弱定義了一些函式,免得編譯報錯找不到。但都是進去就死迴圈。

可以看到軟啟動就是.S檔案中Reset_Handler所完成的就是重定義中斷向量表位置,寫主棧指標,執行系統初始化函式,執行__main,由__main喚起main函式。系統初始化函式中主要是一些關閉看門狗,cache相關的操作,並不一定是時鐘初始化。

編譯器行為和分散載入檔案

微控制器的RAM在什麼位置,flash在什麼位置,各有多大,是可以由分散載入檔案確定的。對於1060這種可以說是很複雜的微控制器系統,各個儲存器有其鮮明的特點,從核心暫存器到cache,到TCM,到OCRAM,再到片外RAM,延遲和讀取時間都會遞增;從另一方面來說,映象的各個部分都需要放在指定的位置,IVT指定了大部分只讀資料的位置,這些都不可以讓連結器自行分配儲存位置。

我有個印象是i.MX rt系列是以恩智浦的應用處理器為原型來設計系統框架的,那麼它的結構肯定和應用處理器有相似之處。瞭解i.MX rt的儲存器分配,可以充分用起這個晶片幾百兆的主頻,將微控制器這一領域玩到極致,併為未來的應用處理器的原理理解打前站,為了Linux的bootloader的編寫提供一些硬體基礎。

使用者程式碼一般是使用C語言寫的,而核心只能執行機器指令,這兩種是存在矛盾的。C語言的存在是為了更方便人類理解,方便人類在不需要了解底層的詳情就能寫出各種複雜的應用。但是如果要從事底層相關C程式碼的設計,那麼瞭解核心瞭解彙編和機器指令是必須的,畢竟底層程式碼的追求永遠是高效,穩定,方便呼叫和精簡。

淺聊編譯

編譯是將C或C艹寫成的原始碼工程轉成核心能直接執行的機器碼的過程。上面提到的幾乎所有行為都是微控制器的行為,而在映象生成之前,對工程相關處理都是工具鏈行為或者說是編譯器行為。作為用C寫程式碼的工程師,需要非常清楚程式碼中哪些語句是核心在執行,哪些是編譯器幫忙做的,那些是看似核心在做但是實際上被優化成編譯器已經幫忙做了。

C原始碼工程從編譯角度可以分為這幾個部分:函式,全域性變數,臨時變數,常量。工程經過編譯器,首先應該是預編譯過程,各.C檔案該包含的包含,該巨集替換的替換;然後進到編譯器和彙編器,轉成.O檔案,此時已經將C原始檔分成了程式碼段(Code),只讀資料段(RO),讀寫資料段(RW)和初始化為零的資料段(ZI),括號中的簡寫只是MDK的習慣標識;最終通過連結器,將各個.O檔案裡的各個段合併到一個檔案中,生成映象檔案。映象檔案的大小取決於程式碼段加只讀資料段加讀寫資料段,這些是需要儲存到非易失性儲存器的,而讀寫資料段和初始化為零的資料段是處理器可能會頻繁讀寫的,需要放在RAM中,所以RAM的佔用由讀寫資料段和初始化為零的資料段決定,map檔案的最後一部分宣告體現了這一點。

圖七 映象執行對ROM和RAM的要求

核心執行中,每執行一個指令都要行一次取指,每進行一次載入或者存取都可能訪問RAM,這些都可能會引起核心等待,畢竟nor-flash之類的非易失性儲存器每訪問一次都是非常慢的,而SRAM結構可能會很快,但是相比於600兆的i.MX rt M7核心,還是太慢了。

SCF檔案解析

在C語言中,讀取暫存器的值,比如 buff[i]= USART1->DATA,這隻需要一句話,但是翻譯成機器碼可能需要:1計算棧偏移;2讀外設資料到R0;3將R0存進棧;這3個指令完成,耗費四五個時鐘週期。這還是能接受的。但是如果取指令存在延遲呢?如果存進棧也存在延遲呢?

這裡提出了很多個問題,但是我這次只想解釋下載入檔案的條目結構。這裡以MDK的scf檔案為例。

首先是定義了儲存器位置和大小:

#define m_itcm_start 0x00000000

#define m_itcm_size 0x00040000

;ITCM空間,程式碼段執行域,預設熔絲設定為128KB,理論上可以做到做小1個時鐘讀寫

 

#define m_flash_config_start 0x60000000

#define m_flash_config_size 0x00001000

;nor-flash空間,flash配置引數空間,儲存nor-flash互動的資料,設為4KB,nor-flash和1060之間通訊有FIFO可以保證不需要每次要資料都重讀一遍,但讀取一次nor-flash的延遲和耗時還是可預見得很大。

 

#define m_ivt_start 0x60001000

#define m_ivt_size 0x00001000

;nor-flash空間,映象向量表,硬啟動最重要的資料了,4KB

 

#define m_interrupts_start 0x60002000

#define m_interrupts_size 0x00000400

;nor-flash空間,中斷向量表,1KB。這裡是未來修改的一個方向,如果每次中斷來了核心都要讀nor-flash取中斷服務函式的地址,這個中斷響應時間必然不小。

 

#define m_text_start 0x60002400

#define m_text_size 0x007FDC00

;nor-flash空間,程式碼段載入域,緊跟中斷向量表,nor-flash的最後部分,其大小不要讓程式碼存到nor-flash的盡頭之外就行了。

 

#define m_data_start 0x20000000

#define m_data_size 0x00020000

;DTCM空間,放置RW資料和ZI資料的,預設最大空間也是128KB,訪問耗時最小1個時鐘

 

#define m_data2_start 0x20200000

#define m_data2_size 0x000C0000

;OCRAM空間,不被設為TCM空間的片上SRAM結構就是OCRAM空間了。訪問耗時4個時鐘,一般給DMA用。

 

#if (defined(__stack_size__))

#define Stack_Size __stack_size__

#else

#define Stack_Size 0x0400

#endif

;1KB的棧

#if (defined(__heap_size__))

#define Heap_Size __heap_size__

#else

#define Heap_Size 0x0400

#endif

;1KB的堆

上面的定義也僅僅是定了位置和大小,實際存什麼進去由下部分的語句約束:

 

LR_m_text m_flash_config_start m_text_start+m_text_size-m_flash_config_start

{;LR_m_text為這個section的名字,後面跟著起始地址和長度

RW_m_config_text m_flash_config_start FIXED m_flash_config_size

{;設一個區域為RW_m_config_text,從0x6000-0000開始,固定4KB大小

    * (.boot_hdr.conf, +FIRST);這個區域存放配置引數,定格對齊存放

    }

 

RW_m_ivt_text m_ivt_start FIXED m_ivt_size

    { ;設一個區域為RW_m_ivt_text,從0x6000-1000開始,固定4KB大小

    * (.boot_hdr.ivt, +FIRST);定格先存放映象向量表

* (.boot_hdr.boot_data);再存放啟動資料域

* (.boot_hdr.dcd_data);最後存放DCD資料

    }

 

VECTOR_ROM m_interrupts_start FIXED m_interrupts_size

{; 設一個區域為VECTOR_ROM,從0x6000-2000開始,固定1KB大小

startup_MIMXRT1062.O(RESET,+FIRST);存放.S中的RESET區域,即中斷向量表

}

 

ER_m_text m_text_start FIXED m_text_size

{;設一個區域為ER_m_text,從0x6000-2400開始,固定m_text_size大小

    * (InRoot$$Sections);存放__main部分

system_MIMXRT1062.O(+RO);存放ystem_MIMXRT1062.O的只讀部分

startup_MIMXRT1062.O(+RO);存放.S檔案的只讀部分

}

 

ER_m_text_ITCM m_itcm_start m_itcm_size

{;設一個區域為ER_m_text_ITCM,從0x0000-0000開始,128KB大小

.ANY (+RO);存放除上面定義之外的所有檔案的只讀部分

}

 

RW_m_data m_data_start m_data_size-Stack_Size-Heap_Size

{;設一個區域為RW_m_data,從0x2000-0000開始,128KB減去堆和棧的大小

.ANY (+RW +ZI);存放所有的RW資料和初始化為0的資料

flexspi_nor_flash_ops.o (+RO +RW +ZI);存放flexspi_nor_flash_ops.o

fsl_flexspi.o (+RO +RW +ZI);存放fsl_flexspi.o

* (NonCacheable.init);存放不需要在cache中執行的資料

* (NonCacheable)

}

 

ARM_LIB_HEAP +0 EMPTY Heap_Size

{;堆空間

}

 

ARM_LIB_STACK m_data_start+m_data_size EMPTY -Stack_Size

{;棧空間

}

}