你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【经验分享】STM32H7 启动过程及bootloader跳转详解

[复制链接]
STMCU小助手 发布时间:2021-12-24 18:00
最近公司开发的一个项目使用到STM32H7系列芯片,由于该系列芯片内部flash只有128k,为了方便产品的远程升级,需要开发对应的升级协议及bootloader引导程序。由于片内flash容量不够,需要采用外挂flash的方式存储应用程序,片内flash单纯作为bootloader引导程序存储空间。为了节省成本采用单QSPI flash外挂BANK2作为应用程序外部存储。+ p+ p( I# V2 R

: o5 t- A; Y+ s( w0 g
20210114225354296.png
) T) ~( O9 T  _* C6 O& S7 D5 ^/ h

( e2 o2 W0 N6 @9 b+ g1.STM32H7启动流程" A4 L+ S! C& e5 l5 p
IAR工程建立后,系统默认生成的启动文件为startup_stm32h750xx.s,默认生成工程一般在EWARM目录下0 h# }/ L$ ?* B  u" w. Z
. G4 ]1 ?8 j. I7 S6 U

  1. 1 _$ R' Y" |4 l' y" C5 |/ T) |6 G" W
  2.         MODULE  ?cstartup& B. a2 o3 t* i* w4 a

  3. & k' G/ ?; E# B6 F) l
  4.         ;; Forward declaration of sections.( h+ Q+ b. N2 b
  5.         SECTION CSTACK:DATA:NOROOT(3)
    # x6 |8 T8 [4 i0 \/ S- m3 \

  6. # H( Y; \6 p% o2 C+ S! m. Y3 V
  7.         SECTION .intvec:CODE:NOROOT(2)
    - a/ M- @$ N! b) n/ a4 S1 p

  8. 9 f0 P! }8 m+ k$ v$ ^
  9.         EXTERN  __iar_program_start
    ; e1 @: d. J, M2 Y6 @$ {
  10.         EXTERN  SystemInit
    ; m4 U( d! M. C; ?: i
  11.         PUBLIC  __vector_table
    8 r9 r4 e+ |7 I7 {: M! N
  12.   ]+ s$ c: f/ p6 `6 c+ k" C
  13.         DATA$ P, H0 m. ]5 f8 y
  14. __vector_table* f7 d8 R9 M; i# n* k
  15.         DCD     sfe(CSTACK)8 k3 g) A! d5 p# w" R
  16.         DCD     Reset_Handler                     ; Reset Handler) \3 Y, q9 K3 I7 k% `: Y( s
  17. ) ^7 W+ P6 ]$ p: o  J' A
  18.         DCD     NMI_Handler                       ; NMI Handler
复制代码

9 `6 R6 g! ~9 A, {* n# ~+ R定义了一个 CSTACK 的段,然后在启动代码中先声明这个段
3 R( T: a! x( c9 o) F8 M  V# j, \& {3 n+ w7 R1 Z$ B. ~* E
定义 .intvec 段,中断向量独立在一个叫 .intvec 的段当中,这个段是 4字节对齐(2^2)所以用 DATA 来首先处理向量的入口地址为 4的倍数,然后放向量表. S, ?4 ?( k" e, n

; C. q) v( _. z, O4 \DATA 进入DATA模式; G  H6 n/ @, ]! g$ |4 T3 y

7 x# b* m" |7 a2 c6 D4 lDCD sfe(CSTACK) 通过 SFE 运算得到改段的结束地址(注意这个运算是在link的时候完成,链接文件为xxxx.icf,比如:stm32h750xx_flash.icf)
9 }$ @$ M# D- K$ ?% N' U4 c" R3 |2 v) u# Q$ T3 A- W/ ]
文件剩下为定义中断向量表。
7 s: q( n/ d' v. M: I$ f5 S7 U% n4 o% n; J! H& N
  1. ;; Default interrupt handlers.
    + f  J# K% e& N' m
  2. ;;0 L4 S- a5 V& V' s6 {. |
  3.         THUMB3 Z2 R7 w* m2 P
  4.         PUBWEAK Reset_Handler
    ; x4 g: _  ?' h! L
  5.         SECTION .text:CODE:NOROOT:REORDER(2)8 ^. _0 y/ ~( l$ F) g
  6. Reset_Handler) @  Z" q; s% k) i
  7. 1 {% h" C) l3 Y2 X) o+ o
  8.         LDR     R0, =SystemInit0 ~0 K% f; u  }; n) ~3 {
  9.         BLX     R0# m  P. H2 J5 W+ q6 J0 h
  10.         LDR     R0, =__iar_program_start
    ' r/ F7 V! s  f$ W5 H
  11.         BX      R02 t+ d% q) k; {* n7 e
  12. % ]. D5 S5 `$ z7 k8 N, t+ r. L4 x
  13.         PUBWEAK NMI_Handler
    * ]5 Y/ g+ g% y, e
  14.         SECTION .text:CODE:NOROOT:REORDER(1)
复制代码

* ]% }2 W% K; G4 X8 NTHUMB 进入THUMB模式(THUMB-2指令集)
) Y, H- w; p0 u  `9 e
/ K0 D/ g+ E) A9 V, C) H2 ^! C启动文件先使用PUBWEAK 指令声明Reset_Handler为弱定义7 U3 V7 U7 Y0 L9 o* t+ {
3 n8 L# |1 g& ?" L6 `  q( j
SECTION .text:CODE:NOROOT:REORDER(2) 从.text段开始. R2 g$ `6 ]6 j% K% A2 c+ j8 e

+ w- P' @0 B1 A, o7 C/ pLDR R0, =SystemInit 将SystemInit指针地址赋值给R0
' b' i: B) m% P7 m  Z* w3 W
: A/ a" y+ I2 O! |& mSystemInit 函数一般由STM32 库提供,对于STM32 HAL库,该函数一般位于system_stm32h7xx.c文件中& z1 E% w$ n' r1 }9 g2 F0 H
6 h( S9 K+ n4 b- A' G/ X
20210114230836389.png
; a8 h9 F8 ]( R' F8 ?6 u5 u

3 r: G/ o- L$ ]. J+ z. t8 m
  1. void SystemInit (void)$ c! H) p# s" [3 r( v' `) I8 P$ v' U& g
  2. {
    . m. @, I  k1 v/ V% T, a; G/ o! u2 R
  3. #if defined (DATA_IN_D2_SRAM): ]5 w  H+ y4 o' w
  4. __IO uint32_t tmpreg;
    ; P. E- s6 d7 k9 \' U, ]  `2 y. ^
  5. #endif /* DATA_IN_D2_SRAM */
    : d( J; J  q, {3 Y2 }" ^

  6. 2 }" I$ ^: B" N, U$ j
  7.   /* FPU settings ------------------------------------------------------------*/
    % X5 P/ z7 ~: `
  8.   #if (__FPU_PRESENT == 1) && (__FPU_USED == 1)
    , |! x/ t8 G" X2 f- a
  9.     SCB->CPACR |= ((3UL << (10*2))|(3UL << (11*2)));  /* set CP10 and CP11 Full Access */- l$ n7 A" F# o. [
  10.   #endif  T- }4 W7 v% j. ~. ]5 L
  11.   /* Reset the RCC clock configuration to the default reset state ------------*/
    , U, A# G0 I* q2 B# K
  12.   /* Set HSION bit */3 U( `" [2 B$ Q2 x( K8 Y- a/ [' u
  13.   RCC->CR |= RCC_CR_HSION;
    % ?9 J8 |; G+ T0 A) s& A) n

  14. % ~( v8 g% e+ d8 u/ t
  15.   /* Reset CFGR register */4 ~4 ~" j. @9 {: m' H2 u) ^7 x
  16.   RCC->CFGR = 0x00000000;8 ?1 d2 f7 ~3 I  d: P# L5 v
  17. ......* f5 P% Q4 E) Y" r& R6 K% o
  18.   /* Configure the Vector Table location add offset address for cortex-M7 ------------------*/& z' j! X/ \) ?! g: p9 \6 l- h
  19. #ifdef VECT_TAB_SRAM4 O+ E3 t* }6 M# Y7 @  F8 q" J3 M
  20.   SCB->VTOR = D1_AXISRAM_BASE  | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal AXI-RAM */
    ' q0 x) a# D9 \1 s( }
  21. #else
    : N& i) ^: O! p; a3 V8 v2 x4 O: `
  22.   SCB->VTOR = FLASH_BANK1_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH */
    ) w4 e5 @3 x) k+ H$ o
  23. #endif  |7 \8 @. v( w  }. k% h

  24. 6 t4 Q3 g7 ^# V/ N
  25. #endif /*DUAL_CORE && CORE_CM4*/
    5 r; S, y+ M% \; S

  26. 5 @6 w/ K9 i! A: W/ x
  27. }
复制代码

" l2 n, }7 i. t9 C# W, X, |& ISystemInit 函数完成系统时钟、RAM、中断向量表地址的初始化。
& x0 t4 m9 v# s$ |
" P+ c1 A# J$ \0 d  |
  1. BLX     R0 跳转到*R0*中的地址,执行SystemInit 并返回
复制代码
  1. LDR     R0, =__iar_program_start //将__iar_program_start地址赋值给R0
复制代码

# R  s! |4 `. i1 D- ~3 m__iar_program_start 函数一般都IAR系统提供,,我们可以在IAR工程中Option进行修改# ?5 K8 U- ]7 c& w" I
: n  T/ A$ M- G" `- m; J: S
如果想具体了解__iar_program_start干了那些事情,可以参考《在main()之前,IAR都做了啥?》2 {; T. H% y  N) J- X
; z' I; L3 u; r* A" w" q7 v: H8 @
启动文件主要完成如下工作,即程序执行过程:
/ \8 Z% f5 X. d7 h, [# r7 ^/ p0 L. R
! u3 [3 ]7 l! D: L) ^  C$ C* @7 f- 设置堆栈指针SP = __initial_sp。
$ y2 A/ z+ e; n0 ?# f% t* Y$ |- g$ |- C3 b" Z3 [4 B
- 设置PC指针 = Reset_Handler。
* U0 o' E+ e* N' f! ]0 i  S3 b, J- A+ Z& D; }- w" ^: c/ \7 u; a' n' l
- 设置中断向量表。
% L/ K, y- w3 B- ]0 m3 U5 U% O) E! D, `! o) l/ i" h5 O
- 配置系统时钟。
* a- u2 N$ Q( p' F+ F) Z4 o
3 j+ a0 [  C& @! Q9 R9 O6 j5 @- 配置外部SRAM/SDRAM用于程序变量等数据存储(这是可选的)。
5 j- d9 K, M) J* ?' L' B0 ^( i3 Z# P9 R; w# o0 D, M7 ^
- 跳转到C库中的 __main ,最终会调用用户程序的main()函数。
% h+ A3 Q- o: w- r7 u! |  E3 Z* i8 s) ~, y* V
2.XIP技术
( _3 H% [/ v) ?5 }XIP,executed in place,本地执行。操作系统采用这种系统,可以不用将内核或执行代码拷贝到内存,而直接在代码的存储空间直接运行。采用这样的技术既可以节省可用内存又可以减少加载的时间。应用程序可以直接在flash闪存内运行,不必再把代码读到系统RAM中。flash内执行是指nor flash 不需要初始化,可以直接在flash内执行代码。但往往只执行部分代码,比如初始化RAM.比如在arm处理器中,Nor flash就存放了引导系统启动的Bootloader,不过大小比较小(仅2M空间)。
; H% k  V, ^4 i0 [
20210114225737419.png
5 z8 y" L0 o  _& r6 r

" y) ?4 }' K1 S: Y: z8 i但是,Flash的存储器访问周期要比RAM大得多,在使用XIP技 术后可能会降低程序的运行速度,不过由于CPU的指令预取机制以及Cache机制,实际使用起来并不会明显降低应用程序的运行速度。% c* g0 ~: x( l, N! i% D* A
" H3 W; s9 a/ U+ O
20210114225835865.png

' D4 I* a7 [/ @6 k$ }4 c
/ n1 \! Y$ n, k4 U7 \- a  a4 C0 p而右边的图,是针对Nor Flash的,这个很明显,CPU可以像读内存一样,直接跟Nor flash交互,即可以直接从Nor Flash中取指令,然后交给译码模块和执行模块进行执行,可以说,相比较Nand flash,Nor flash的操作对于CPU来说,简直就像是面对面一样。3 |! U& ]6 `5 O. f* U: m
& k; m' W- D( b
进一步, 为什么Nor Flash可以实现XIP,而Nand flash就不行呢?) A% E! M8 v4 o

# b+ q# s: `( |  U7 u$ `3 c! K有一个概念:嵌入式系统中代码的执行方式:4 _# G* o( l/ O

* J' n; A6 C6 h; P) s# I+ d(1)完全映射:嵌入式系统程序运行时,将所有代码从非易失存储器(Flash、ROM等)复制到RAM中运行。6 p) i; B) B- k# m/ k' |' R& n
3 E+ W; @' l9 m& z3 k' V$ k; k
(2)按需分页:只复制部分代码到RAM中,这种方法对RAM中的页进行导入/导出管理,如果访问位于虚存中但不在物理RAM中会产生页错位,这时才将代码和数据映射到RAM中。5 V+ s4 _" u6 A7 D9 M

! u( x! J8 j0 H( A1 g(3)XIP:在系统启动时,不将代码复制到RAM,而是直接在非易失性存储位置执行,RAM中只存放需要不断变化的数据部分,如下图
7 G; f. b) d4 }3 m" I- b' z8 j4 w8 W: q
20210114225911954.png
2 @0 a( P, S* v- _- V

) O/ M- r1 k/ j5 s$ V如果非易失性存储器(Flash)的读取速度与RAM相近,则XIP可以节省复制和解压的时间,Nor flash和rom的读取速度比较看(约100ns),比较适合XIP,而Nand flash的读取操作是基于扇区的,速度相对很慢(us级),因此不适合实现XIP系统,不过Nand flash的写速度比Nor的快,更适合做存储和下载系统。
5 G" O& \# x; P* u+ H! s4 S7 a" S. h/ }' u( |- z. p4 o4 y7 q
解释二:( s; g5 ^5 G8 Z# D4 \: }2 \. ?1 G% F
两种芯片的结构不同* W9 ]+ v( v! Q
NOR flash之所以可以片内执行,就是因为他符合CPU去指令译码执行的要求。CPU送一个地址出来,Nor flash就能给出一个数据让CPU执行,中间不需要额外的处理操作。8 Y- H, j4 S! D8 a' E& l7 v
NAND flash不一样是因为nand flash有地址,数据,命令共用IO口的问题,cpu把地址发出来之后,并不能直接得到数据,还需要控制线的操作才能完成。就是他没有专用的SRAM接口。
; ~: q1 ^# m% [: Z6 U
5 V8 K9 m. f5 W- M解释三:
  D) k  v' d/ w芯片内执行主要是是看芯片可不可以线性存储代码(假如硬件支持芯片接口),只要能保证芯片的存储空间是线性的(也就是无坏块),都可以片上执行,在读取Flash时候,容易出现“位翻转(bitconvert),在Flash的位翻转(一个bit位发生翻转)现象上,NAND的出现几率要比NorFlash大得多。这个问题在Flash存储关键文件时是致命的,所以在使用NandFlash时建议同时使用EDC/ECC等校验算法。 ”
0 V3 D, z% ~1 a6 ^# R, R
" u% w0 H4 w' i* `4 u3 R; ?6 s但是,如果能保证不出错,也还是可以进行XIP,可以在其上执行代码的:
+ z# J2 E& Y. F7 [1 J. v9 M“所谓XIP,就是CODE是在FLASH上直接运行. NANDFLASH只是不适合做XIP,但并不是不能做XIP“5 t! |3 e3 d+ q) Y! K3 K% T  h; ~
要一段CODE能够正确的运行,要保证它的CODE是连续的,正确的.
2 Y* z# o# ~! |: |; A由于一些电气特性的原因,NOR FLASH能够做到这一点,不存在坏道或坏块,所以能够做XIP.
5 _1 r4 A9 Z! o. {2 t; m而对于NAND FLASH, 它只保证它的BLOCK 0是好的,其他的块并不保证,虽然出错的几率比较低,但还是有出错的可能,所以CODE可能无法连续正确地执行.
2 l9 b$ `4 O9 S- G! R1 b) h9 D+ g3 j1 V: V8 T7 W5 U
但只要你有额外的保障措施,比如说在执行CODE之前去做一次ECC校验,来确保CODE是连续正确的.那你也可以做XIP. 有人这么做了,而且也证明是成功的
7 A  m+ w" N$ j2 g4 C& W3 P- Q
由于芯片外挂的是W25Q16 为NOR flash,支持XIP,并且STM32H7系列芯片QSPI FLASH支持内存映射模式。STM32H7芯片可以将W25Q16芯片内的应用程序映射到地址0x90000000,虚拟为芯片内部内存进行执行。由于产品为了节省成本没有采用QSPI 双flash模式,执行效率会再降低一点。经过测试从bootloader跳转到应用程序,起来需要经过约2.6s左右。具体中断执行效率还需进行进一步测试。& i. z, z. _5 Y* o
QSPI执行速度对比图: b# ?/ q5 c9 _; S5 K
: K3 e! ?  a3 l! f
20210114230031779.png

- [' Q( R0 ^" R. Y9 i
" t. S) X+ X2 U( w& ?6 D. s3.启动文件修改" ^. j: x, V8 u
对于bootloader程序,程序再芯片内部128k flash中执行,程序默认启动执行地址为0x8000000,无需修改IAR 的link链接配置文件。$ s9 [9 ^4 E" o8 g! q* }9 B/ Q- h
对于应用程序,需将IAR 的link链接配置文件****.icf文件中的__ICFEDIT_intvec_start__ 地址,__ICFEDIT_region_ROM_start__地址,__ICFEDIT_region_ROM_end__地址进行修改,具体可以通过IAR options->Linker->Config,里面进行修改7 L2 _1 K( x- M- }! j! ~- N6 G

5 l- n/ Y1 P& N3 X) K+ o
20210114225558558.png

6 y5 ~3 d" X4 g( ^. ]( a9 ~6 z
* W- l$ i' b7 {  f4 e6 P2 C
20210114225618875.png

6 O/ T% F' ?* V2 U3 e1 U
, Z& z. J6 U0 K# i1 ?) @修改完成后,查看IAR配置后的icf文件,具体内容如下2 |& F3 w' c+ ~9 l6 R
* U1 B: w; ?$ U1 F% V
  1. /*###ICF### Section handled by ICF editor, don't touch! ****/5 e9 p5 E; C# L; u. Y: v% ~
  2. /*-Editor annotation file-*/: x! `6 q5 C4 n! i5 i: s7 [
  3. /* IcfEditorFile="$TOOLKIT_DIR$\config\ide\IcfEditor\cortex_v1_0.xml" */
    ( y& ^: p/ g2 G
  4. /*-Specials-*/  i  G6 M# ~& ?! ?9 s: u5 z! Z; |
  5. define symbol __ICFEDIT_intvec_start__ = 0x90000000;+ Z% O: K! X2 `* @8 f9 O3 j
  6. /*-Memory Regions-*/% u) U; H1 \4 W& \8 `) @: \
  7. define symbol __ICFEDIT_region_ROM_start__     = 0x90000000;; v! v0 B3 I& m/ S/ e  B% X6 ~
  8. define symbol __ICFEDIT_region_ROM_end__       = 0x90100000;7 c; n, }6 Q& h4 M- B% N; t8 b
  9. define symbol __ICFEDIT_region_RAM_start__     = 0x20000000;
    $ z( i9 Q! f; \% t
  10. define symbol __ICFEDIT_region_RAM_end__       = 0x2001FFFF;% x/ P/ B! y1 u/ h" j  |
  11. define symbol __ICFEDIT_region_ITCMRAM_start__ = 0x00000000;
    7 d4 L2 o# b; [3 W
  12. define symbol __ICFEDIT_region_ITCMRAM_end__   = 0x0000FFFF;
复制代码
8 I7 [# j! w8 e0 O6 F6 y' y$ \' g
4.bootloader程序与应用程序设计
5 _+ Q9 D+ h9 K, i对于bootloader而言,主要的任务有一下点:* G% }8 G% ~, S/ H" N
6 K6 Z# F8 x; [
1、接受和处理上位机下发的更新程序,并将其写入到外部flash备份区
% X1 k' I+ f  Z- O6 u# |- e3 f* V7 i7 M# l0 U# d7 z1 w! D
2、对更新到外部备份区的程序拷贝到应用区
# `( f1 A4 }4 C/ e$ ?, Q; q/ U( t  J
3、初始化QSPI为内存映射模式,引导程序启动,跳转到应用程序' m8 s: q1 Q# l0 |+ B2 `

) m. L1 f# ^7 j
2021011423012391.png

+ L% Z0 W- u5 C, g& _* q0 l1 K
- n( O6 k2 G. ^( i, O) V
20210114230131258.png
! P) N0 A' P# A' ~, s

# _+ p4 E4 r! U9 a/ k% @) w2 h我们将STM32H7整个内部128k flash分配为bootloader程序存储区。装置上电,首先从内部flash 地址0x8000000进行启动,具体执行流程如下图所示:& ]6 f( o& S3 o3 n2 }
, O! ]' X4 C- M
20210114230154821.png
% b3 `5 M( N0 T2 j7 n

+ S& D! A* H. N: L2 L- i4 L! I0 }+ V, C. {* F6 ~) _
3 z1 |$ U7 \, `: H2 [

& u2 d& Y9 |$ V6 T2 H, e: @; E+ g6 s1 E( ~0 }: |8 \% l9 \& x( U  P3 ]
赞 收藏 评论0 发布时间:2021-12-24 18:00

举报

0个回答

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版