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

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

[复制链接]
STMCU小助手 发布时间:2021-12-24 18:00
最近公司开发的一个项目使用到STM32H7系列芯片,由于该系列芯片内部flash只有128k,为了方便产品的远程升级,需要开发对应的升级协议及bootloader引导程序。由于片内flash容量不够,需要采用外挂flash的方式存储应用程序,片内flash单纯作为bootloader引导程序存储空间。为了节省成本采用单QSPI flash外挂BANK2作为应用程序外部存储。) Q+ W. N! d3 Y( Z0 M: Q3 S7 y/ Y
' C! w) }2 L1 B- ~$ w
20210114225354296.png
: H% f$ c( q6 B' X, F6 n4 q
  d/ u" o: `. Y2 q( \
1.STM32H7启动流程$ N- U7 x( Q4 z2 x" [# J
IAR工程建立后,系统默认生成的启动文件为startup_stm32h750xx.s,默认生成工程一般在EWARM目录下
/ l& J( F% l$ Q* l
, O1 b8 H0 d+ r. C( x3 G( ~
  1. * K/ q. m  W+ J3 U0 E
  2.         MODULE  ?cstartup
    " Y. C) m4 ~/ p
  3. ( }5 L6 h+ b1 S0 w, N. A
  4.         ;; Forward declaration of sections.2 k9 L; G+ u7 F* i
  5.         SECTION CSTACK:DATA:NOROOT(3)4 `( v7 F5 y+ A0 b  |, `
  6. " v$ q5 `1 k' B% G" R
  7.         SECTION .intvec:CODE:NOROOT(2)
    9 J' W- h4 D/ c9 M0 z

  8. 0 t% F) E3 x) c& n
  9.         EXTERN  __iar_program_start) \$ Q) I$ h3 f1 L' m/ y, d
  10.         EXTERN  SystemInit' A" |6 E% O$ K( D* U2 _, \+ f
  11.         PUBLIC  __vector_table
    4 D( H* f2 u/ m0 N$ l. G

  12. 7 z6 W# H) e/ j6 m$ n/ R
  13.         DATA
    0 F2 e( ?* a5 Q6 _6 h9 f2 Z: E
  14. __vector_table
    1 }; b0 j$ h1 ]
  15.         DCD     sfe(CSTACK)& E8 G& z3 e* R
  16.         DCD     Reset_Handler                     ; Reset Handler  f8 V' j: h8 [0 ?+ v% i$ C

  17. 3 k$ M. c; P$ [6 L& L. n
  18.         DCD     NMI_Handler                       ; NMI Handler
复制代码

% n2 _0 T$ o+ H9 t1 m  h. l定义了一个 CSTACK 的段,然后在启动代码中先声明这个段. k6 n2 j( L3 K( U& Q# p+ f
" r! D  x2 f0 D% e2 b
定义 .intvec 段,中断向量独立在一个叫 .intvec 的段当中,这个段是 4字节对齐(2^2)所以用 DATA 来首先处理向量的入口地址为 4的倍数,然后放向量表+ d  {) W8 J0 {% j; d* x

1 }' \. d9 r% b) L) ZDATA 进入DATA模式
$ b3 ^) |4 R/ G8 v6 K( I' J; G7 @- r" l. e4 d& o  d) }
DCD sfe(CSTACK) 通过 SFE 运算得到改段的结束地址(注意这个运算是在link的时候完成,链接文件为xxxx.icf,比如:stm32h750xx_flash.icf)
2 x  g' P. H- s6 A
% N8 l! m1 }) `* q0 V/ l# M+ Z5 B文件剩下为定义中断向量表。
$ K2 W7 D9 G. g$ `( p1 V. L# R6 Y4 N4 E5 C! o
  1. ;; Default interrupt handlers.
    $ A5 G* Q6 Y* l# T
  2. ;;
    * Z/ ~  T% U! B
  3.         THUMB
    ' Z# W3 g6 z5 v; F' o
  4.         PUBWEAK Reset_Handler
    . ^9 L2 ~: w( e( h* y
  5.         SECTION .text:CODE:NOROOT:REORDER(2)
    1 _- J% c1 |4 T. }+ s
  6. Reset_Handler
    ( c% Q: y7 O- Z7 R3 s8 L

  7. 9 T) y8 X/ G. E
  8.         LDR     R0, =SystemInit
    8 }& }$ W% k; p; M& j1 T. f$ o0 l6 B
  9.         BLX     R0* T* U% B! v4 |8 l  c
  10.         LDR     R0, =__iar_program_start) S/ n3 m, t0 a9 H7 q- T' f
  11.         BX      R0
    # k  c9 R9 {: k! _& Q; w

  12. 4 `+ m5 m0 r0 \( [" @2 P( U1 O. p
  13.         PUBWEAK NMI_Handler
    : N) K/ D/ J& _2 @! a2 I
  14.         SECTION .text:CODE:NOROOT:REORDER(1)
复制代码

6 T% n, M" d9 w- r) T% F& aTHUMB 进入THUMB模式(THUMB-2指令集)# h2 p9 t+ g( o

5 J4 E% ?+ m2 Z! I) g启动文件先使用PUBWEAK 指令声明Reset_Handler为弱定义
7 K; j/ I: p$ Q1 M  `  z( F' B4 K% l" Z
SECTION .text:CODE:NOROOT:REORDER(2) 从.text段开始# R. R: m: d6 F5 m! d$ [/ d7 S. J0 [

! ]/ i! \" ^% X' ]9 n8 DLDR R0, =SystemInit 将SystemInit指针地址赋值给R03 V  ~+ S5 H( O2 S% W# Z

. E7 k& {) W; T+ W) ^" w+ QSystemInit 函数一般由STM32 库提供,对于STM32 HAL库,该函数一般位于system_stm32h7xx.c文件中
8 g/ O5 _' S- l
4 j3 r# X  b+ d( D- Q5 `/ s' G' x3 z
20210114230836389.png

9 t# Q& t3 U1 Y$ K+ C8 h8 b0 i* a* {4 M4 l0 Z# s' J
  1. void SystemInit (void)
    9 a/ k/ H6 v( x! h$ d
  2. {2 [4 \' X+ L! Q# V
  3. #if defined (DATA_IN_D2_SRAM)
    & B5 `% c# V! }: v7 W) L$ d, R% y) Y
  4. __IO uint32_t tmpreg;
    8 y! v& f; T$ B3 }" T- {
  5. #endif /* DATA_IN_D2_SRAM */) y- `8 m3 v- E" K. ?, g
  6. ( V& H7 Z; `8 h
  7.   /* FPU settings ------------------------------------------------------------*/7 \% _& F5 j) {" R
  8.   #if (__FPU_PRESENT == 1) && (__FPU_USED == 1)7 l; U7 u0 F- I" N  h7 l
  9.     SCB->CPACR |= ((3UL << (10*2))|(3UL << (11*2)));  /* set CP10 and CP11 Full Access */  ?( j2 f2 J7 Q/ `! K
  10.   #endif
    1 j8 D) Q; r! E( W
  11.   /* Reset the RCC clock configuration to the default reset state ------------*/
    ' I+ O9 t, o' C7 ], K
  12.   /* Set HSION bit */
    0 v! A: b6 a. {/ R  H6 v; j1 [0 R  `+ _
  13.   RCC->CR |= RCC_CR_HSION;
    ; {5 W: [; W* T& |
  14. - t' m8 b1 Q+ P! a3 t' q
  15.   /* Reset CFGR register */
    7 ?, [, x/ v5 C4 Y8 d  A( Z
  16.   RCC->CFGR = 0x00000000;
      {, a# E0 b- n
  17. ......
    ; S' X, N- {* w# ^6 F: G; C9 N
  18.   /* Configure the Vector Table location add offset address for cortex-M7 ------------------*/: n9 _3 v8 S9 \, \5 j- H' p
  19. #ifdef VECT_TAB_SRAM
    4 e7 N3 b: \( X9 e
  20.   SCB->VTOR = D1_AXISRAM_BASE  | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal AXI-RAM */
    . N2 l* p3 P( ?- I8 w  X
  21. #else/ c' v% n& W8 y: d
  22.   SCB->VTOR = FLASH_BANK1_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH */* \% K6 s! C. i' \
  23. #endif6 p$ Q& z. ?% J$ ^) u! U

  24. . \- o: q7 Y# r* y) q( U! f. P
  25. #endif /*DUAL_CORE && CORE_CM4*/
    ) Z  V7 u5 u5 i; f
  26. " w7 P! m+ ]5 C+ n0 I2 ^
  27. }
复制代码

) o. R0 Y. M/ A9 ^& |SystemInit 函数完成系统时钟、RAM、中断向量表地址的初始化。% k& n1 S' w3 R. B# L- n

- t& }& `* [5 y/ d
  1. BLX     R0 跳转到*R0*中的地址,执行SystemInit 并返回
复制代码
  1. LDR     R0, =__iar_program_start //将__iar_program_start地址赋值给R0
复制代码
) L9 _7 c8 N, b. @! E7 m& n) E2 ]
__iar_program_start 函数一般都IAR系统提供,,我们可以在IAR工程中Option进行修改
( W' ^9 j: @- b, f3 y' D8 I6 F+ Q: d( p% _. u
如果想具体了解__iar_program_start干了那些事情,可以参考《在main()之前,IAR都做了啥?》
) A0 V, U' g6 T. n0 M
/ U- y# a7 G' ~# Q! ]启动文件主要完成如下工作,即程序执行过程:4 j9 I" H: ^5 l- J3 g( Y
/ A) q: a7 R& r2 o! w8 X; H
- 设置堆栈指针SP = __initial_sp。% R1 h2 e) U9 i3 q' R( c! K7 R) q) Y

$ q! {+ b$ H8 G" P7 r4 ]/ X7 t- 设置PC指针 = Reset_Handler。
# A' _7 A' R& C+ c4 m# H3 m( _1 _
1 L' v5 B* u! c2 W6 c/ Y6 A; V- 设置中断向量表。
% a8 @& {- {- h6 X0 l: F; h
; [) F3 ~/ a7 f" w  Y- 配置系统时钟。+ b% k# U, S6 C( b: E7 B; u5 ?! Q
! H  Z0 T! N; M9 {/ j/ M
- 配置外部SRAM/SDRAM用于程序变量等数据存储(这是可选的)。
( C/ B' `+ [  o( D! A0 T5 ^1 Z
8 \2 h7 I5 k( B" u- 跳转到C库中的 __main ,最终会调用用户程序的main()函数。
# n# f& K& O, j
( Y! N2 c; V3 ?$ }: c2.XIP技术
" A+ P8 J' @% T, B* h& ?XIP,executed in place,本地执行。操作系统采用这种系统,可以不用将内核或执行代码拷贝到内存,而直接在代码的存储空间直接运行。采用这样的技术既可以节省可用内存又可以减少加载的时间。应用程序可以直接在flash闪存内运行,不必再把代码读到系统RAM中。flash内执行是指nor flash 不需要初始化,可以直接在flash内执行代码。但往往只执行部分代码,比如初始化RAM.比如在arm处理器中,Nor flash就存放了引导系统启动的Bootloader,不过大小比较小(仅2M空间)。' l$ z2 J( j+ Y" e- ~6 ^
20210114225737419.png
9 S# I6 N& N' `9 J  C
* f" S: f0 z$ K4 S9 `9 q
但是,Flash的存储器访问周期要比RAM大得多,在使用XIP技 术后可能会降低程序的运行速度,不过由于CPU的指令预取机制以及Cache机制,实际使用起来并不会明显降低应用程序的运行速度。
. W  g' ^2 k: y  k0 A+ e& M! ^1 {, z; R
20210114225835865.png

/ \; E" N3 e) B8 f3 |) E/ a+ @  [  m) Z- q2 K+ i+ ?
而右边的图,是针对Nor Flash的,这个很明显,CPU可以像读内存一样,直接跟Nor flash交互,即可以直接从Nor Flash中取指令,然后交给译码模块和执行模块进行执行,可以说,相比较Nand flash,Nor flash的操作对于CPU来说,简直就像是面对面一样。6 P/ T$ C  X. w3 O6 W$ T: ^& Y

: C* d+ `2 C2 J3 ]$ H进一步, 为什么Nor Flash可以实现XIP,而Nand flash就不行呢?
0 \. R5 S2 v7 ?6 B; [$ c2 ^# Z  ]* e( J
有一个概念:嵌入式系统中代码的执行方式:
3 _$ Q# `& R0 {( ^7 x6 ~( h0 r6 E, c1 G% p1 L' H1 ~# @
(1)完全映射:嵌入式系统程序运行时,将所有代码从非易失存储器(Flash、ROM等)复制到RAM中运行。% `2 K  m$ \" f' j

: f8 l- X3 a7 u(2)按需分页:只复制部分代码到RAM中,这种方法对RAM中的页进行导入/导出管理,如果访问位于虚存中但不在物理RAM中会产生页错位,这时才将代码和数据映射到RAM中。
2 O; u5 c/ |2 _& ?) g4 ?( E) \& b" k+ B8 ]7 a
(3)XIP:在系统启动时,不将代码复制到RAM,而是直接在非易失性存储位置执行,RAM中只存放需要不断变化的数据部分,如下图( K- I2 w, q# p  B. D& i7 ~

$ D& j  z! s) u/ A1 g
20210114225911954.png
& G$ U) |, j! u6 z7 R4 o( j8 Q
2 E. C2 ?$ B- A/ V
如果非易失性存储器(Flash)的读取速度与RAM相近,则XIP可以节省复制和解压的时间,Nor flash和rom的读取速度比较看(约100ns),比较适合XIP,而Nand flash的读取操作是基于扇区的,速度相对很慢(us级),因此不适合实现XIP系统,不过Nand flash的写速度比Nor的快,更适合做存储和下载系统。1 r  Y% `$ F) J3 S6 H. V8 q
! @: ~$ N( R6 V* i% y
解释二:
$ v9 p: l- c8 l8 w两种芯片的结构不同) y5 P$ j- A" f' _3 W2 t7 W* }
NOR flash之所以可以片内执行,就是因为他符合CPU去指令译码执行的要求。CPU送一个地址出来,Nor flash就能给出一个数据让CPU执行,中间不需要额外的处理操作。0 V: O0 M4 d' T3 {  l
NAND flash不一样是因为nand flash有地址,数据,命令共用IO口的问题,cpu把地址发出来之后,并不能直接得到数据,还需要控制线的操作才能完成。就是他没有专用的SRAM接口。/ n/ O5 }. D# _  C; k7 z. H
9 F$ s3 ?. _% B+ V0 \
解释三:% U) ?# {$ S6 {% u/ u- s8 P
芯片内执行主要是是看芯片可不可以线性存储代码(假如硬件支持芯片接口),只要能保证芯片的存储空间是线性的(也就是无坏块),都可以片上执行,在读取Flash时候,容易出现“位翻转(bitconvert),在Flash的位翻转(一个bit位发生翻转)现象上,NAND的出现几率要比NorFlash大得多。这个问题在Flash存储关键文件时是致命的,所以在使用NandFlash时建议同时使用EDC/ECC等校验算法。 ”( H  p7 L  M% Z7 n) h7 c. r
0 Q" N, l6 l) S6 G0 v! V" n
但是,如果能保证不出错,也还是可以进行XIP,可以在其上执行代码的:
: ^1 k$ E' g5 k( ?: g“所谓XIP,就是CODE是在FLASH上直接运行. NANDFLASH只是不适合做XIP,但并不是不能做XIP“
0 Z9 W8 @' R0 ?/ \. o+ V$ @要一段CODE能够正确的运行,要保证它的CODE是连续的,正确的.
1 r, X& M$ g) Z/ ]( ~' T由于一些电气特性的原因,NOR FLASH能够做到这一点,不存在坏道或坏块,所以能够做XIP.7 d" v8 |# O# S; w& ^
而对于NAND FLASH, 它只保证它的BLOCK 0是好的,其他的块并不保证,虽然出错的几率比较低,但还是有出错的可能,所以CODE可能无法连续正确地执行.; G% M$ M2 y7 w
! s# V3 P; p* Y1 j) k6 w0 b
但只要你有额外的保障措施,比如说在执行CODE之前去做一次ECC校验,来确保CODE是连续正确的.那你也可以做XIP. 有人这么做了,而且也证明是成功的: k3 A* X9 r8 c& n

+ }4 `2 l7 R  O* T0 J! \# k+ M由于芯片外挂的是W25Q16 为NOR flash,支持XIP,并且STM32H7系列芯片QSPI FLASH支持内存映射模式。STM32H7芯片可以将W25Q16芯片内的应用程序映射到地址0x90000000,虚拟为芯片内部内存进行执行。由于产品为了节省成本没有采用QSPI 双flash模式,执行效率会再降低一点。经过测试从bootloader跳转到应用程序,起来需要经过约2.6s左右。具体中断执行效率还需进行进一步测试。  ]1 D8 r; w6 Q( _1 h
QSPI执行速度对比图" a8 K6 {  c7 h( A* G. l

3 a% C# t' X% a: E% m
20210114230031779.png
8 L* b  c1 q" V7 N, v6 s

) W- ~7 I* k  s% H6 }6 L3.启动文件修改
0 D1 ~+ n& W* d& [1 B) x对于bootloader程序,程序再芯片内部128k flash中执行,程序默认启动执行地址为0x8000000,无需修改IAR 的link链接配置文件。4 @9 z  ~, t% i9 g6 g" B" O; e
对于应用程序,需将IAR 的link链接配置文件****.icf文件中的__ICFEDIT_intvec_start__ 地址,__ICFEDIT_region_ROM_start__地址,__ICFEDIT_region_ROM_end__地址进行修改,具体可以通过IAR options->Linker->Config,里面进行修改
; r6 F- q, j$ t! [6 c
& U" i4 @- V1 y6 j$ d
20210114225558558.png

& G4 k' m, K$ a6 U4 I! d+ k  }2 L8 p+ P' s3 ^+ W* _
20210114225618875.png
" ~, Q1 U5 p: l$ f, n
8 }( Z7 r7 O! ~' f# M* ?
修改完成后,查看IAR配置后的icf文件,具体内容如下
' x. I" x0 H! U& I  r" @6 O8 G8 x1 i" Z. O/ ?9 O  A2 o; e, H
  1. /*###ICF### Section handled by ICF editor, don't touch! ****/
    3 C$ t7 d8 N$ L) A) L. Y
  2. /*-Editor annotation file-*/
    1 j2 v* |6 F- O$ V
  3. /* IcfEditorFile="$TOOLKIT_DIR$\config\ide\IcfEditor\cortex_v1_0.xml" */
    ' e5 g7 L% O6 _: W  t+ {4 L
  4. /*-Specials-*/9 o( v5 r- F5 |: J$ @
  5. define symbol __ICFEDIT_intvec_start__ = 0x90000000;9 b! L: e  R% M) g0 P8 Z) D
  6. /*-Memory Regions-*/
    8 r2 |4 }% e  I4 i( V1 M& ^
  7. define symbol __ICFEDIT_region_ROM_start__     = 0x90000000;
    1 _/ h6 T9 c1 m! t; h# k* H6 \
  8. define symbol __ICFEDIT_region_ROM_end__       = 0x90100000;
      s$ ~6 F$ _" h& Z6 l2 w6 x0 x
  9. define symbol __ICFEDIT_region_RAM_start__     = 0x20000000;$ x" Y  C. k2 L( j
  10. define symbol __ICFEDIT_region_RAM_end__       = 0x2001FFFF;1 }) f4 d+ c1 _, b
  11. define symbol __ICFEDIT_region_ITCMRAM_start__ = 0x00000000;8 ~2 R- B& m* y5 U
  12. define symbol __ICFEDIT_region_ITCMRAM_end__   = 0x0000FFFF;
复制代码

5 v" j+ m7 K# A0 }5 I8 E4.bootloader程序与应用程序设计4 f! r, c: I; o4 A/ s7 }) E
对于bootloader而言,主要的任务有一下点:
2 y# f/ m' W1 X, v# ^$ S% [8 a$ g' g! S2 [4 g( M
1、接受和处理上位机下发的更新程序,并将其写入到外部flash备份区- g! S1 \6 |' c! |$ k3 l
/ q" Q5 L: t4 i1 \
2、对更新到外部备份区的程序拷贝到应用区
) D8 P( q8 [" q  K" g+ Y
) \7 B6 c1 o+ x! }# [3、初始化QSPI为内存映射模式,引导程序启动,跳转到应用程序3 z5 D1 H% v5 n8 P  l

& ~7 I1 n8 W) u. E6 ^$ I- E& c
2021011423012391.png
. U- |3 S+ F+ v. u$ y7 ^. x' A
3 P2 a. i7 b' h
20210114230131258.png
' S) R! j. U2 c: o# p0 t; g

, U  O3 K4 k$ B/ W! `) j# A我们将STM32H7整个内部128k flash分配为bootloader程序存储区。装置上电,首先从内部flash 地址0x8000000进行启动,具体执行流程如下图所示:
/ B( o& Y0 Z% y( L* r: _- O+ ?
9 X# Q( z% D( V8 }0 `- F0 O% ?+ N
20210114230154821.png

: E& F+ B: e/ V
1 B2 |* b0 |. Z2 [0 x* t0 e* d' B
( b! a( E: Z6 B& b  g' l
- W: H  a" z: J8 H! W3 n% B
6 _- l4 z6 e8 {9 p/ h0 }' ]" k  q. d  w: w6 }* a
收藏 评论0 发布时间:2021-12-24 18:00

举报

0个回答

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版