|
大家好,我是痞子衡,是正經(jīng)搞技術(shù)的痞子。今天痞子衡給大家分享的是IAR啟動(dòng)函數(shù)流程里的段初始化函數(shù)__iar_data_init3實(shí)現(xiàn)。
" x/ c, T: l& G% I" ]- Z& Y本篇是 《IAR啟動(dòng)函數(shù)流程及其__low_level_init設(shè)計(jì)對函數(shù)重定向的影響》 一文的后續(xù),在上篇文章里我們在 IAR 軟件安裝目錄下找到了標(biāo)準(zhǔn)啟動(dòng)函數(shù) __iar_program_start() 相關(guān)源文件,并且分析了 __iar_program_start() 函數(shù)里的全部動(dòng)作。我們知道了其中負(fù)責(zé) .data/.bss/.textrw 段初始化工作的是 __iar_data_init3() 函數(shù),但是這個(gè)函數(shù)的具體實(shí)現(xiàn)并沒有詳細(xì)介紹,今天我們就仔細(xì)說說這個(gè) __iar_data_init3() 函數(shù): V: Q: K& b4 A! W/ Y; q
Note 1:閱讀本文前需要對 《IAR鏈接文件(.icf)》 有所了解。Note 2:本文使用的 IAR EWARM 軟件版本是 v9.10.2。一、為什么有些段需要初始化?《IAR鏈接文件(.icf)》 一文第一小節(jié)列出了 IAR 工程里定義的全部系統(tǒng)段(Section)名,其中 .data/.bss/.textrw 段是需要初始化的,因?yàn)檫@些段是鏈接在 RAM 里,而 RAM 上電其內(nèi)容都是隨機(jī)值,所以需要一段啟動(dòng)代碼將 .data/.bss/.textrw 段所在的 RAM 區(qū)填上對應(yīng)的初值(初值來自于下載了程序鏡像文件的 Flash 區(qū)),然后應(yīng)用程序才能正常運(yùn)行。
0 w# ^0 k" s+ FNote: 除了 .data/.bss/.textrw 之外,還有一些段(.noinit/CSTACK/HEAP等)也鏈接在 RAM 區(qū),但這些段對初值沒有依賴,所以不需要初始化。.bss // 初值為 0 的靜態(tài)/全局變量(RAM)
. C& {& q1 p- @* [6 }.data // 初值為非 0 的全局變量(RAM): Z7 z6 \: X; ~) w9 E( [! M
.data_init // .data 段的初值(Flash)
" ?! L0 s) r4 t" |. i: J.textrw // __ramfunc 修飾的重定向函數(shù)實(shí)際執(zhí)行區(qū)(RAM)+ I e$ [2 u/ K Q1 u
.textrw_init // .textrw 段的機(jī)器碼存儲(chǔ)區(qū)(Flash)
0 ]6 y, M- z' d+ G4 P7 a9 [% n# ]二、RW/ZI段初始化的一般實(shí)現(xiàn)應(yīng)用程序工程在編譯鏈接結(jié)束后,.data/.bss/.textrw 段實(shí)際鏈接地址就確定了(這里指默認(rèn)由 IAR 鏈接器自由分配具體鏈接地址,而不是用戶在鏈接文件中指明具體鏈接地址的情況),我們知道了這些段的鏈接地址,就可以完成對應(yīng)初始化工作(說白了,就是初值數(shù)據(jù)從 Flash 到 RAM 的拷貝工作),實(shí)際鏈接地址可以通過如下 IAR 鏈接器提供的接口來獲取,具體拷貝過程可參看 《IAR下將關(guān)鍵函數(shù)重定向到RAM中執(zhí)行的方法》 一文最后一節(jié)里的代碼。
0 B6 x4 x7 o0 U; k$ }Note: IAR 鏈接器為了后續(xù)初始化的方便,都是將程序中全部的全局變量緊挨著放到一塊連續(xù)的 RAM 區(qū)域(.data),然后其全部初值也一一對應(yīng)緊挨著放一起(.data_init,下載到一塊連續(xù)的 Flash 區(qū));對于 .textrw 的處理也類似。#pragma section = ".data"
& `+ \4 O; e; h7 u! V% {1 z, Z#pragma section = ".data_init"- |; M3 d! B8 I! I' }
#pragma section = ".bss"6 C1 E$ B( }9 q- q( O
#pragma section = ".textrw"
) u( G5 \& p- }7 x$ a#pragma section = ".textrw_init"- P+ h- s. U+ M6 c0 r4 R
uint8_t *data_ram = __section_begin(".data");6 A8 }9 l3 ~" D8 g5 \
uint8_t *data_rom = __section_begin(".data_init");
3 t0 X' Z5 @& m l) ^7 l2 K9 guint8_t *data_rom_end = __section_end(".data_init");$ g7 Z+ `3 a2 a% ^; e, w! p
uint8_t *bss_start = __section_begin(".bss");
. L# }- P+ b8 X; iuint8_t *bss_end = __section_end(".bss");# d2 x9 U# W+ D9 w! Z
uint8_t *code_relocate_ram = __section_begin(".textrw");
: Y* F; m+ G% ?1 d" cuint8_t *code_relocate_rom = __section_begin(".textrw_init");
# ] h- a! v# l( }( N( v5 N% kuint8_t *code_relocate_rom_end = __section_end(".textrw_init");& S6 g9 O7 D3 X, r2 _
段初始化的一般實(shí)現(xiàn)雖然簡單,但有個(gè)缺點(diǎn),就是對于用戶自定義 RW/ZI 段或者多個(gè)分散的 RW/ZI 段無法自動(dòng)適應(yīng),需要根據(jù)實(shí)際情況不斷調(diào)整代碼實(shí)現(xiàn)。
/ m4 f. N1 r% R6 M9 r( h& l a/ W三、__iar_data_init3() 函數(shù)實(shí)現(xiàn)細(xì)節(jié)前面鋪墊了這么多,終于到了圍觀 IAR 標(biāo)準(zhǔn)段初始化函數(shù) __iar_data_init3() 實(shí)現(xiàn)的時(shí)候了,跟這個(gè)函數(shù)相關(guān)的源文件在如下路徑下,核心代碼在 data_init.c 文件中:) g/ Y7 k' y% \, c9 u% |
\IAR Systems\Embedded Workbench 9.10.2\arm\src\lib\init\data_init.c
3 V& M8 N7 U, T2 e8 Q\IAR Systems\Embedded Workbench 9.10.2\arm\src\lib\init\zero_init3.c - 存放 __iar_zero_init3 函數(shù)
1 J. I( I, W7 ^, l\IAR Systems\Embedded Workbench 9.10.2\arm\src\lib\init\copy_init3.c - 存放 __iar_copy_init3 函數(shù)/ W5 B3 U; ?; \$ F) u
在 data_init.c 文件中有一個(gè)叫 IAR_DATA_INIT 的函數(shù),其實(shí)它就是 __iar_data_init3,光看這個(gè)函數(shù)里的代碼會(huì)讓人有點(diǎn)摸不著頭腦,因?yàn)橛昧?IAR 鏈接器里的接口及一些特殊定義,我們結(jié)合一個(gè)具體應(yīng)用程序工程來講解會(huì)更清晰。3 s0 s# v" W- y$ [. |8 X8 X9 O/ x
// 在 IAR 目錄 \arm\inc\c\DLib_Product.h 中宏定義' U( z8 z. L& f, L( m- A
#define _DLIB_ELF_INIT_INTERFACE_VERSION 3* K5 u% {3 `. u' O! s3 y" L! J
// 在 IAR 目錄 \arm\src\lib\init\data_init.h 中的宏定義$ ]1 K. l6 }: K3 `6 q5 I5 n8 r
#define IAR_DATA_INIT _GLUE(__iar_data_init, _DLIB_ELF_INIT_INTERFACE_VERSION)7 q$ @; ]. {! T1 @9 R
#pragma section = "Region$$Table" const TABLE_MEM3 `1 _. H: G9 }+ Z
void IAR_DATA_INIT(void); z+ }4 e5 T2 P$ c5 A
{
1 H/ ~+ B4 Y" Q9 c FAddr TABLE_MEM const * pi = __section_begin("Region$$Table");! z4 t; h X: T, \* r) L8 _
table_ptr_t pe = __section_end ("Region$$Table");) B8 v9 T- K; z0 j& F7 w& t* F, [
while (pi != pe)
, m% H5 H( _% B# O {
' ^- p6 I! G: v- i init_fun_t * fun = FAddr_GetPtr(pi);8 g# J# E: p( ]6 m+ T
++pi;# U( k1 q' z0 V4 n3 r" R. ^
pi = fun(pi);( |/ N( F% i4 y
}
. N( N* y/ I1 b}6 D. E2 ^7 j8 ~* N3 Q5 V
我們現(xiàn)在隨便編譯一個(gè) SDK 例程(痞子衡選擇的是 \SDK_2.11.0_MIMXRT1170-EVK\boards\evkmimxrt1170\demo_apps\hello_world\cm7\iar,切到 flexspi_nor_debug build,即代碼 RO 段鏈接在 0x30000000 開始的 Flash 區(qū),代碼 RW 段鏈接在 0x20000000 開始的 DTCM 區(qū)),查看其對應(yīng)映射文件(.map),摘出其中跟段初始化相關(guān)的一些內(nèi)容如下,初始化工作包含:利用 __iar_zero_init3 函數(shù)清零起始地址為 0x20000040 長度為 0x4c 字節(jié)的 ZI 段空間,利用 __iar_copy_init3 函數(shù)拷貝 0x40 字節(jié) RW 段數(shù)據(jù)(從 0x300060fc 到 0x20000000):5 B' ~5 B# u6 W- k6 w9 ]7 h. G4 s
*******************************************************************************
; R3 {2 j: ?, @; d*** INIT TABLE1 b2 g" z* U l7 v( \! Y
***
2 f4 \0 _% B5 \ Address Size
# Y; R6 ^4 I7 l! K. c7 j( z ------- ----
/ ~0 r# _0 C- g* Z4 @Zero (__iar_zero_init3)
, j$ O. A- B. \ 1 destination range, total size 0x4c:
, {5 Q$ j9 a6 J. S/ Y 0x2000'0040 0x4c4 }/ o B1 l8 _6 \+ g: k7 d Y
Copy (__iar_copy_init3)
) @7 f3 ?9 v3 W/ {# D 1 source range, total size 0x40:
6 y+ p+ \/ U1 I$ y, v 0x3000'60fc 0x40* W6 v: `- ?3 A) [$ l: E
1 destination range, total size 0x40:
, `! ^, r @9 V; b- ~0 j 0x2000'0000 0x40
9 C% N% ` ]2 y. { s; h*******************************************************************************
) l$ t% ?" |4 J& \*** ENTRY LIST& c. x* `: {5 K# S4 e5 P
***' b8 _' I, a# W: F
Entry Address Size Type Object. q- [& e) [! l9 f7 i
---- ------- ---- ---- ------
2 B1 Z9 b6 O( ^$ t0 i9 y.iar.init_table$$Base 0x3000'63d4 -- Gb - Linker created -
1 W. C" I m i1 W% E.iar.init_table$$Limit 0x3000'63f8 -- Gb - Linker created -
& Y& A" q" ?' h/ X5 S% C2 QRegion$$Table$$Base 0x3000'63d4 -- Gb - Linker created -4 ]! L+ K: I. n$ `1 X: d. }, T
Region$$Table$$Limit 0x3000'63f8 -- Gb - Linker created -
9 w' }( O- i" ^; c__iar_copy_init3 0x3000'630d 0x2c Code Gb copy_init3.o [6]
4 d3 v; N! i3 F! m* ^__iar_zero_init3 0x3000'613d 0x3c Code Gb zero_init3.o [6]
, [+ j7 t9 a$ k( w6 W在映射文件里,我們知道了 Region$$Table 區(qū)域的起止地址 [0x300063d4 - 0x300063f8),打開鏡像文件或者在線調(diào)試找到這段區(qū)域里的內(nèi)容,你會(huì)發(fā)現(xiàn)段初始化工作所需的全部信息(操作函數(shù)地址、操作數(shù)據(jù)長度、操作源地址、操作目標(biāo)地址)都記錄在里面,其中特別注意的是涉及 Flash 區(qū)的地址都是以相對地址來存放的:
* K, C- F" } x2 |7 }Note:FAddr_GetPtr 函數(shù)負(fù)責(zé)地址轉(zhuǎn)換,0x300063d4 地址處的值是 0xfffffd69,那么 0x300063d4 + 0xfffffd69 = 0x13000613d,保留低 32bit 即是 __iar_zero_init3 函數(shù)地址。 |
|