|
大家好,我是痞子衡,是正經(jīng)搞技術(shù)的痞子。今天痞子衡給大家分享的是IAR啟動(dòng)函數(shù)流程里的段初始化函數(shù)__iar_data_init3實(shí)現(xiàn)。
+ U# B$ i- g# Z# e, U- K9 B本篇是 《IAR啟動(dòng)函數(shù)流程及其__low_level_init設(shè)計(jì)對(duì)函數(shù)重定向的影響》 一文的后續(xù),在上篇文章里我們?cè)?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)并沒(méi)有詳細(xì)介紹,今天我們就仔細(xì)說(shuō)說(shuō)這個(gè) __iar_data_init3() 函數(shù):6 f4 ]( ]! _8 V
Note 1:閱讀本文前需要對(duì) 《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ū)填上對(duì)應(yīng)的初值(初值來(lái)自于下載了程序鏡像文件的 Flash 區(qū)),然后應(yīng)用程序才能正常運(yùn)行。# x" j! K2 _! J) o4 q
Note: 除了 .data/.bss/.textrw 之外,還有一些段(.noinit/CSTACK/HEAP等)也鏈接在 RAM 區(qū),但這些段對(duì)初值沒(méi)有依賴,所以不需要初始化。.bss // 初值為 0 的靜態(tài)/全局變量(RAM)- Z: J& u6 R' s" A
.data // 初值為非 0 的全局變量(RAM)' X) N1 {' w) I& _" V9 m: i7 f
.data_init // .data 段的初值(Flash)
8 h& k) w2 u: V! G- K.textrw // __ramfunc 修飾的重定向函數(shù)實(shí)際執(zhí)行區(qū)(RAM)
+ m! V( w6 _7 _.textrw_init // .textrw 段的機(jī)器碼存儲(chǔ)區(qū)(Flash)
7 N/ s' d5 q O1 f/ S5 t! H二、RW/ZI段初始化的一般實(shí)現(xiàn)應(yīng)用程序工程在編譯鏈接結(jié)束后,.data/.bss/.textrw 段實(shí)際鏈接地址就確定了(這里指默認(rèn)由 IAR 鏈接器自由分配具體鏈接地址,而不是用戶在鏈接文件中指明具體鏈接地址的情況),我們知道了這些段的鏈接地址,就可以完成對(duì)應(yīng)初始化工作(說(shuō)白了,就是初值數(shù)據(jù)從 Flash 到 RAM 的拷貝工作),實(shí)際鏈接地址可以通過(guò)如下 IAR 鏈接器提供的接口來(lái)獲取,具體拷貝過(guò)程可參看 《IAR下將關(guān)鍵函數(shù)重定向到RAM中執(zhí)行的方法》 一文最后一節(jié)里的代碼。
1 ?& h8 Z7 D W; u' i9 SNote: IAR 鏈接器為了后續(xù)初始化的方便,都是將程序中全部的全局變量緊挨著放到一塊連續(xù)的 RAM 區(qū)域(.data),然后其全部初值也一一對(duì)應(yīng)緊挨著放一起(.data_init,下載到一塊連續(xù)的 Flash 區(qū));對(duì)于 .textrw 的處理也類似。#pragma section = ".data" h( y. ^; N _8 W2 W7 U7 }
#pragma section = ".data_init"# l- _) O' X( e6 ~$ F( ^
#pragma section = ".bss"6 w3 `1 [6 t0 R5 v9 S
#pragma section = ".textrw"5 N7 l4 A3 ]+ ]1 |' S" b
#pragma section = ".textrw_init"3 I+ o# W& t. M3 p4 c' ?* O! C+ g
uint8_t *data_ram = __section_begin(".data");3 H: J* s( ]7 N/ {% e N) _
uint8_t *data_rom = __section_begin(".data_init");
8 _* y4 ^2 w' |, v$ m2 w( u4 cuint8_t *data_rom_end = __section_end(".data_init");
0 u9 V- Z" a; ^2 Ouint8_t *bss_start = __section_begin(".bss");5 C. d+ f# ^! O6 G; a- r
uint8_t *bss_end = __section_end(".bss");
% B$ m# H3 x3 v+ kuint8_t *code_relocate_ram = __section_begin(".textrw");+ \7 i, f9 [( K9 j3 g' M# T
uint8_t *code_relocate_rom = __section_begin(".textrw_init");
1 f' R: S- S4 Z& D$ o" L; i# O& Huint8_t *code_relocate_rom_end = __section_end(".textrw_init");
8 G# E' @$ I4 ?* t) y4 D) ^段初始化的一般實(shí)現(xiàn)雖然簡(jiǎn)單,但有個(gè)缺點(diǎn),就是對(duì)于用戶自定義 RW/ZI 段或者多個(gè)分散的 RW/ZI 段無(wú)法自動(dòng)適應(yīng),需要根據(jù)實(shí)際情況不斷調(diào)整代碼實(shí)現(xiàn)。: i* H* G1 S" } E) u6 b
三、__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 文件中:" x! t; W3 E& L2 \
\IAR Systems\Embedded Workbench 9.10.2\arm\src\lib\init\data_init.c U% P8 l8 G) b% `' `& U
\IAR Systems\Embedded Workbench 9.10.2\arm\src\lib\init\zero_init3.c - 存放 __iar_zero_init3 函數(shù)+ H2 m) E5 X4 F9 j" ~
\IAR Systems\Embedded Workbench 9.10.2\arm\src\lib\init\copy_init3.c - 存放 __iar_copy_init3 函數(shù)" ^5 \9 w1 x0 b6 b
在 data_init.c 文件中有一個(gè)叫 IAR_DATA_INIT 的函數(shù),其實(shí)它就是 __iar_data_init3,光看這個(gè)函數(shù)里的代碼會(huì)讓人有點(diǎn)摸不著頭腦,因?yàn)橛昧?IAR 鏈接器里的接口及一些特殊定義,我們結(jié)合一個(gè)具體應(yīng)用程序工程來(lái)講解會(huì)更清晰。
% n* X9 Y' R. u4 o# M// 在 IAR 目錄 \arm\inc\c\DLib_Product.h 中宏定義
5 V4 ~. ~0 }1 _, P# m/ h# X% n#define _DLIB_ELF_INIT_INTERFACE_VERSION 3$ v) q1 ]& R, j8 U# T+ N
// 在 IAR 目錄 \arm\src\lib\init\data_init.h 中的宏定義2 ^) y' J) `, d% ~& Q) b% }; i
#define IAR_DATA_INIT _GLUE(__iar_data_init, _DLIB_ELF_INIT_INTERFACE_VERSION)
7 Z: N8 q# [3 l7 |#pragma section = "Region$$Table" const TABLE_MEM
3 M% K W7 R9 j) o4 avoid IAR_DATA_INIT(void)
q4 N1 k# t) B' T6 m- n4 l6 ]{& {& @# i6 @( ]& y4 |$ ^9 d' l
FAddr TABLE_MEM const * pi = __section_begin("Region$$Table");
, B, o4 c& w9 v" Q: L1 Z table_ptr_t pe = __section_end ("Region$$Table");7 z$ w4 `& _$ ?& q: [
while (pi != pe)$ w% U$ P7 y; ~, H
{( f( q: f! M) |3 P5 \ ~* K- f. Q* I
init_fun_t * fun = FAddr_GetPtr(pi);
# D% s/ }7 H; J- d. S' K7 h ++pi;
; J8 a! d" l2 M) ?& ? pi = fun(pi);% R: b Y, F, r/ \+ [) o
}
( K# o) F" n( j" F2 m8 {! H}
* C5 k3 I; r9 A) B! }. f我們現(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ū)),查看其對(duì)應(yīng)映射文件(.map),摘出其中跟段初始化相關(guān)的一些內(nèi)容如下,初始化工作包含:利用 __iar_zero_init3 函數(shù)清零起始地址為 0x20000040 長(zhǎng)度為 0x4c 字節(jié)的 ZI 段空間,利用 __iar_copy_init3 函數(shù)拷貝 0x40 字節(jié) RW 段數(shù)據(jù)(從 0x300060fc 到 0x20000000):
6 i0 r3 D. s8 L/ `, I' _*******************************************************************************4 i2 A# L+ c! p/ O
*** INIT TABLE; p) b" d: N: C% S& B
***
3 Y/ b# m( z7 q, d Address Size
$ R1 F: g3 ?& z! o ------- ----! V, S3 Q. \6 n. W, w" b
Zero (__iar_zero_init3)
! _3 O; V, e* i! S4 q$ B& r 1 destination range, total size 0x4c:1 v' d/ z, ~+ [" t
0x2000'0040 0x4c
# u/ y! v; f; O! S4 n$ I" p. n# |2 JCopy (__iar_copy_init3)3 P# c. D4 s t
1 source range, total size 0x40:
- A! H/ `4 F! T/ @ \ 0x3000'60fc 0x40) J: a; y+ G0 {# e+ n3 y2 b0 w6 d4 U
1 destination range, total size 0x40:
7 z @$ s _5 K) W/ a0 f% N5 X; J 0x2000'0000 0x40
( x) y; @& G0 m% o, C1 z3 P*******************************************************************************2 E0 h! H( F+ {: C- Z
*** ENTRY LIST5 E/ o& x4 I; f: \# ^
***
- w2 e& G- D5 ~' {! BEntry Address Size Type Object
) v0 @4 [+ e1 n( A. ?7 [ ---- ------- ---- ---- ------) c3 Y' ^& F# [5 a5 b5 p! Y
.iar.init_table$$Base 0x3000'63d4 -- Gb - Linker created - @; C9 i5 @& {, J/ Z5 K
.iar.init_table$$Limit 0x3000'63f8 -- Gb - Linker created -
5 k9 R K1 p, t# U% x2 oRegion$$Table$$Base 0x3000'63d4 -- Gb - Linker created -6 e! v' O1 l' k
Region$$Table$$Limit 0x3000'63f8 -- Gb - Linker created -5 f y% a A- L: ^% P7 Y
__iar_copy_init3 0x3000'630d 0x2c Code Gb copy_init3.o [6]
5 g/ C2 Q, L, [5 O__iar_zero_init3 0x3000'613d 0x3c Code Gb zero_init3.o [6]' i9 v. T( A/ \. c, d/ L- {" q$ R# _
在映射文件里,我們知道了 Region$$Table 區(qū)域的起止地址 [0x300063d4 - 0x300063f8),打開鏡像文件或者在線調(diào)試找到這段區(qū)域里的內(nèi)容,你會(huì)發(fā)現(xiàn)段初始化工作所需的全部信息(操作函數(shù)地址、操作數(shù)據(jù)長(zhǎng)度、操作源地址、操作目標(biāo)地址)都記錄在里面,其中特別注意的是涉及 Flash 區(qū)的地址都是以相對(duì)地址來(lái)存放的:
) g1 m7 r: N' m# T+ @2 l3 T$ FNote:FAddr_GetPtr 函數(shù)負(fù)責(zé)地址轉(zhuǎn)換,0x300063d4 地址處的值是 0xfffffd69,那么 0x300063d4 + 0xfffffd69 = 0x13000613d,保留低 32bit 即是 __iar_zero_init3 函數(shù)地址。 |
|