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

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

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

4 s3 E$ g0 S! h0 y9 u
20210114225354296.png

# ~& w2 D) `+ d# N8 d5 }7 G! u5 U- {; L  G2 L" A( l% ?
1.STM32H7启动流程5 x% e/ y, ~% b) K0 U
IAR工程建立后,系统默认生成的启动文件为startup_stm32h750xx.s,默认生成工程一般在EWARM目录下- X( e  ^) p# X9 x
* M* @' d% t6 M( o

  1. 6 q  s" F6 s; h7 U* }2 C6 b
  2.         MODULE  ?cstartup
    / Y: W3 F- v6 Y% m4 \9 n

  3. : z  G. U/ z  s' q6 S2 X+ Z0 q% l
  4.         ;; Forward declaration of sections.
    ( d. M" W7 J  E& @5 ?
  5.         SECTION CSTACK:DATA:NOROOT(3)9 V% I$ d" s4 W4 ?" S1 M/ E3 v5 k" |2 o

  6. 8 ]1 ~3 l7 Z, m6 [! B
  7.         SECTION .intvec:CODE:NOROOT(2)
    ! H" u4 p# \: ~; e" u* g
  8. ( G, y9 @% z  o  A7 B" a0 W
  9.         EXTERN  __iar_program_start* _8 l* G4 X0 Z1 o) W
  10.         EXTERN  SystemInit6 f3 B8 V- P! I; ?8 g4 j3 {
  11.         PUBLIC  __vector_table( O4 e( }* B) A0 H, i% ~# g

  12. 2 x2 R6 Q9 f3 h2 X1 i+ n% U
  13.         DATA
    5 d/ x, z. i- ^! x& ]2 l' M2 U
  14. __vector_table
    8 \1 P- L6 k) p" L% |( D
  15.         DCD     sfe(CSTACK)7 |4 L7 @* p4 D; O% s( ~1 F$ @
  16.         DCD     Reset_Handler                     ; Reset Handler2 t( H+ ]. k2 R/ i
  17. 4 i+ A7 e  O0 h/ g. W* f$ [$ @
  18.         DCD     NMI_Handler                       ; NMI Handler
复制代码
7 B: L. |8 Y  a8 O2 e8 @
定义了一个 CSTACK 的段,然后在启动代码中先声明这个段1 o) K& M1 ^/ p

* {. U; X1 Q* w3 Z. q定义 .intvec 段,中断向量独立在一个叫 .intvec 的段当中,这个段是 4字节对齐(2^2)所以用 DATA 来首先处理向量的入口地址为 4的倍数,然后放向量表
2 a' A. l, A3 q1 [5 U1 U" i" Z6 q2 N. U# e: ?
DATA 进入DATA模式
9 x- x7 X+ J3 G" R. T( j) z* `" `* ~/ U! L
DCD sfe(CSTACK) 通过 SFE 运算得到改段的结束地址(注意这个运算是在link的时候完成,链接文件为xxxx.icf,比如:stm32h750xx_flash.icf)
- H% W. p: X3 d! y+ k' x# r7 G1 e3 W2 c  v3 y5 g  j
文件剩下为定义中断向量表。
0 g; R% ^( \; E1 u; T8 s2 ]$ r9 N7 {$ D2 c1 O" o9 @6 Q
  1. ;; Default interrupt handlers.
    : Z! s' P. _/ b$ a  y% F/ z
  2. ;;
    5 O" f: |5 \. T6 v5 _+ b7 j. n
  3.         THUMB
    6 G' M5 a6 ^5 u$ o7 L1 _! S1 }9 M
  4.         PUBWEAK Reset_Handler- I& T( o. p+ ~: b% k3 w" u+ w
  5.         SECTION .text:CODE:NOROOT:REORDER(2)3 i$ R" \1 F/ t- w" ~
  6. Reset_Handler1 p* p$ L) {* ]3 X) P: b) w2 ^

  7. 8 ?  W, o9 H) z. W) {
  8.         LDR     R0, =SystemInit$ N& n" e6 d; G! `% f3 d8 c! O
  9.         BLX     R0! C# a' o+ |2 J
  10.         LDR     R0, =__iar_program_start( D7 W2 }( A$ E- y. H0 ^; G
  11.         BX      R0
    4 r' _* I3 s: g3 U/ j1 i/ E
  12. 1 u% O7 @( @. N% Y
  13.         PUBWEAK NMI_Handler
    6 d* b$ y9 h; E* N
  14.         SECTION .text:CODE:NOROOT:REORDER(1)
复制代码
$ E) R1 J1 R% Y4 f$ R
THUMB 进入THUMB模式(THUMB-2指令集)
" F+ }  w9 |' J+ q! @
/ J; f0 A4 K* x6 @$ H  M, D- |启动文件先使用PUBWEAK 指令声明Reset_Handler为弱定义$ ]7 Q. K9 s  Q

. |. M- o( o/ b/ F# K- h6 b/ ^SECTION .text:CODE:NOROOT:REORDER(2) 从.text段开始8 W. ?# F3 J. k; L) ~1 k

* V; H, h5 o- F1 h0 P5 JLDR R0, =SystemInit 将SystemInit指针地址赋值给R03 O+ R; [8 D- z' u+ c2 `

0 M! n: }' v! B: KSystemInit 函数一般由STM32 库提供,对于STM32 HAL库,该函数一般位于system_stm32h7xx.c文件中+ _; u* R1 v/ Q, }8 @4 O
6 T. a$ F' x, H
20210114230836389.png

5 X+ E" M: H! }7 r0 x3 m
' Q1 Y+ v  T9 |3 J" O( @9 q2 ]
  1. void SystemInit (void)
    - D+ G/ B5 @7 q. S, i$ C& J# g
  2. {
    0 r1 E( P. n8 v% p5 T+ P
  3. #if defined (DATA_IN_D2_SRAM)
    + ]3 C0 v1 \$ ]* H  K
  4. __IO uint32_t tmpreg;2 O" {7 ^2 M/ ]* ]
  5. #endif /* DATA_IN_D2_SRAM */
    1 [' Z  H8 d; `; b- S3 T, H- x4 C

  6. 2 a/ g7 T, \# |4 s* a/ N
  7.   /* FPU settings ------------------------------------------------------------*/
    - M- Z6 s' T# O, ?/ Y
  8.   #if (__FPU_PRESENT == 1) && (__FPU_USED == 1)
    , I, j3 a! J, S6 R  W
  9.     SCB->CPACR |= ((3UL << (10*2))|(3UL << (11*2)));  /* set CP10 and CP11 Full Access */2 _% J9 h" @$ B6 G& ]
  10.   #endif
    0 @( ]* |- q1 V7 _; F, ?% F5 @- _
  11.   /* Reset the RCC clock configuration to the default reset state ------------*/2 `; T# K5 Z0 Q9 Y; k5 y* T
  12.   /* Set HSION bit */! y/ m1 }7 G: v: ~7 }
  13.   RCC->CR |= RCC_CR_HSION;7 h9 B2 D) b- }) o

  14. ( F* W3 N. ^* {) v- ~6 k  `
  15.   /* Reset CFGR register */
    0 c# B1 x! l/ ^
  16.   RCC->CFGR = 0x00000000;! B3 H; y. q/ C" j& j5 r! l
  17. ......* E3 J2 U& V# ^. P- d
  18.   /* Configure the Vector Table location add offset address for cortex-M7 ------------------*/
    ) _  F. m3 Z  P0 H& W3 L! w
  19. #ifdef VECT_TAB_SRAM: @- m6 k+ [9 E$ I; e6 {
  20.   SCB->VTOR = D1_AXISRAM_BASE  | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal AXI-RAM */
    + h: }3 M; a1 j$ Q4 w# |
  21. #else" B' _: C/ S# L4 T8 |& R; O* E
  22.   SCB->VTOR = FLASH_BANK1_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH */
    ; f7 u# p( A6 x
  23. #endif
    4 T& Z: t8 `* T# O, l$ r) D" h

  24. / }& J4 X- J6 v$ e2 |' M, d; g
  25. #endif /*DUAL_CORE && CORE_CM4*/
    ( n* }- n* c( H& w, m6 ]5 D

  26. , u4 M* W8 w. Y
  27. }
复制代码

4 ?$ M4 D0 a2 T. cSystemInit 函数完成系统时钟、RAM、中断向量表地址的初始化。
' G# Y( ]1 {4 g3 m' Y2 K7 _9 E
$ K6 T! [- H( D8 i  j! y; b
  1. BLX     R0 跳转到*R0*中的地址,执行SystemInit 并返回
复制代码
  1. LDR     R0, =__iar_program_start //将__iar_program_start地址赋值给R0
复制代码

9 ?6 `0 o; ^, d$ w( u8 B__iar_program_start 函数一般都IAR系统提供,,我们可以在IAR工程中Option进行修改8 m& o6 J$ T; g

: f' E! B" t& A5 ^5 u6 m" z( U. z( C如果想具体了解__iar_program_start干了那些事情,可以参考《在main()之前,IAR都做了啥?》8 j- k2 Y% ~; S$ k' T4 y7 w. p

$ S# ?2 ^. p( g; O启动文件主要完成如下工作,即程序执行过程:( }- Y7 C: y3 @

3 H* m! b* c, ^4 U# Y! L5 S- 设置堆栈指针SP = __initial_sp。; w9 t3 a5 Y& Y& k
( b4 B9 c! s7 n4 Q  g" j
- 设置PC指针 = Reset_Handler。
* j  _( S7 {8 Z$ j' W0 z  I7 |6 j4 r) I; K
- 设置中断向量表。; g" |2 v( e+ m. n

: R) w; r( u! K% S& l4 X7 L; w; D- 配置系统时钟。' |- r8 A4 U& T5 X
& @+ c- v0 Y5 }+ J1 J
- 配置外部SRAM/SDRAM用于程序变量等数据存储(这是可选的)。# A$ T1 a0 e- m& q9 W0 t" ]

: b& e8 s: x- m* j0 L+ t! X  B- 跳转到C库中的 __main ,最终会调用用户程序的main()函数。) w! ]( b0 V9 ~2 J; ^7 u7 ?

( X9 n% F9 P" k2.XIP技术
( U2 C0 ]" s; z& VXIP,executed in place,本地执行。操作系统采用这种系统,可以不用将内核或执行代码拷贝到内存,而直接在代码的存储空间直接运行。采用这样的技术既可以节省可用内存又可以减少加载的时间。应用程序可以直接在flash闪存内运行,不必再把代码读到系统RAM中。flash内执行是指nor flash 不需要初始化,可以直接在flash内执行代码。但往往只执行部分代码,比如初始化RAM.比如在arm处理器中,Nor flash就存放了引导系统启动的Bootloader,不过大小比较小(仅2M空间)。- h- _! H% J. r' q, |# c1 Y
20210114225737419.png

" n! E/ O; ~+ o% o$ T- g! J9 p1 e* d. n2 q# q
但是,Flash的存储器访问周期要比RAM大得多,在使用XIP技 术后可能会降低程序的运行速度,不过由于CPU的指令预取机制以及Cache机制,实际使用起来并不会明显降低应用程序的运行速度。
! O8 r& f$ f: T1 E4 X2 O5 o5 U: a4 M. p' D2 Q
20210114225835865.png
& |* d  E+ L$ V- E
" o; h' S. g( v3 ~! S9 D/ |; X
而右边的图,是针对Nor Flash的,这个很明显,CPU可以像读内存一样,直接跟Nor flash交互,即可以直接从Nor Flash中取指令,然后交给译码模块和执行模块进行执行,可以说,相比较Nand flash,Nor flash的操作对于CPU来说,简直就像是面对面一样。2 b' D5 E1 i  L
! J% G' @" L4 o  l
进一步, 为什么Nor Flash可以实现XIP,而Nand flash就不行呢?, r0 J2 k/ {1 j& ^* A- z0 D
: @9 g% J5 r7 ~0 U" I- ?7 P* B
有一个概念:嵌入式系统中代码的执行方式:
. F% ~. A! I9 o: ^
9 _  t. S- {7 w(1)完全映射:嵌入式系统程序运行时,将所有代码从非易失存储器(Flash、ROM等)复制到RAM中运行。
# z1 x7 m( H+ u6 S* M
- P1 R  q5 U" ?(2)按需分页:只复制部分代码到RAM中,这种方法对RAM中的页进行导入/导出管理,如果访问位于虚存中但不在物理RAM中会产生页错位,这时才将代码和数据映射到RAM中。
; b: h; x3 b9 O1 e
& u, ~8 B9 H( Y! G  Z% i+ V9 ](3)XIP:在系统启动时,不将代码复制到RAM,而是直接在非易失性存储位置执行,RAM中只存放需要不断变化的数据部分,如下图
/ {/ b. M+ _/ s  ]3 k# H2 f! }6 A3 k! W
20210114225911954.png
* @4 L- ~* V4 L3 W

* H7 ]2 N4 ?0 F# v& r' h" q7 A; b3 }如果非易失性存储器(Flash)的读取速度与RAM相近,则XIP可以节省复制和解压的时间,Nor flash和rom的读取速度比较看(约100ns),比较适合XIP,而Nand flash的读取操作是基于扇区的,速度相对很慢(us级),因此不适合实现XIP系统,不过Nand flash的写速度比Nor的快,更适合做存储和下载系统。
* N  k: J. s5 ~1 N% V. j2 e% C  P. y) r0 c$ p4 v! |. X+ c* j6 o( f: \
解释二:
) X" K3 S3 l- o/ M5 j/ i两种芯片的结构不同
$ O4 g. p$ D: V1 [* nNOR flash之所以可以片内执行,就是因为他符合CPU去指令译码执行的要求。CPU送一个地址出来,Nor flash就能给出一个数据让CPU执行,中间不需要额外的处理操作。; y( t. n4 Q, q+ Q) m) d) J
NAND flash不一样是因为nand flash有地址,数据,命令共用IO口的问题,cpu把地址发出来之后,并不能直接得到数据,还需要控制线的操作才能完成。就是他没有专用的SRAM接口。: q/ P. N. ^! X% r6 }) G' O

/ n3 G) A: A8 `% I& d解释三:( v8 r; w- D. ^4 v5 p4 r( j) |' _
芯片内执行主要是是看芯片可不可以线性存储代码(假如硬件支持芯片接口),只要能保证芯片的存储空间是线性的(也就是无坏块),都可以片上执行,在读取Flash时候,容易出现“位翻转(bitconvert),在Flash的位翻转(一个bit位发生翻转)现象上,NAND的出现几率要比NorFlash大得多。这个问题在Flash存储关键文件时是致命的,所以在使用NandFlash时建议同时使用EDC/ECC等校验算法。 ”4 v9 [9 |, t' j

7 b8 w* b( ^" J# r: X* V/ {但是,如果能保证不出错,也还是可以进行XIP,可以在其上执行代码的:2 Y1 T" G' b  D4 E
“所谓XIP,就是CODE是在FLASH上直接运行. NANDFLASH只是不适合做XIP,但并不是不能做XIP“  X0 F* j( ]' x# w3 j9 p2 U
要一段CODE能够正确的运行,要保证它的CODE是连续的,正确的.
2 d0 m9 P% r$ ~! R由于一些电气特性的原因,NOR FLASH能够做到这一点,不存在坏道或坏块,所以能够做XIP.+ _9 o5 x: M0 v! t- Y& f$ T
而对于NAND FLASH, 它只保证它的BLOCK 0是好的,其他的块并不保证,虽然出错的几率比较低,但还是有出错的可能,所以CODE可能无法连续正确地执行.
* a8 L2 ^3 A9 e3 q3 S# F4 E8 S' O, ^
但只要你有额外的保障措施,比如说在执行CODE之前去做一次ECC校验,来确保CODE是连续正确的.那你也可以做XIP. 有人这么做了,而且也证明是成功的: J. ~. k& U' x% V2 y, ~$ z9 Y' x
' l- I9 c/ q% y3 f5 v' I
由于芯片外挂的是W25Q16 为NOR flash,支持XIP,并且STM32H7系列芯片QSPI FLASH支持内存映射模式。STM32H7芯片可以将W25Q16芯片内的应用程序映射到地址0x90000000,虚拟为芯片内部内存进行执行。由于产品为了节省成本没有采用QSPI 双flash模式,执行效率会再降低一点。经过测试从bootloader跳转到应用程序,起来需要经过约2.6s左右。具体中断执行效率还需进行进一步测试。: f$ S' F) g9 a+ u3 m
QSPI执行速度对比图! Q' z  V* _9 ~$ _

2 a, \( q6 s# \- i( i" S+ Y8 B* N$ ^$ h
20210114230031779.png
* L# a7 M( z  g. _$ u! v. T
  s/ K& Q& o, v9 ~$ _
3.启动文件修改
. @. K# ~4 f( f! i" y! w+ ]对于bootloader程序,程序再芯片内部128k flash中执行,程序默认启动执行地址为0x8000000,无需修改IAR 的link链接配置文件。9 q5 P$ ?) N1 F0 G
对于应用程序,需将IAR 的link链接配置文件****.icf文件中的__ICFEDIT_intvec_start__ 地址,__ICFEDIT_region_ROM_start__地址,__ICFEDIT_region_ROM_end__地址进行修改,具体可以通过IAR options->Linker->Config,里面进行修改4 I* w; y6 E4 u' G  O' @

+ \: w& f! L5 ~  o, i
20210114225558558.png

' V5 r7 G; I) j  ?3 Y% ?
4 a; l. \! q1 T9 X, R5 k
20210114225618875.png
$ E1 u) n$ N+ y* V
1 ]3 f0 }1 e; r
修改完成后,查看IAR配置后的icf文件,具体内容如下
/ q" D  D; r& |
) T0 X+ D2 C) e$ S- T1 I
  1. /*###ICF### Section handled by ICF editor, don't touch! ****/+ y- [( N; c4 J) q& q+ ?% P0 i+ `
  2. /*-Editor annotation file-*/
    9 @9 g1 f6 V% K3 ]- q, l
  3. /* IcfEditorFile="$TOOLKIT_DIR$\config\ide\IcfEditor\cortex_v1_0.xml" */
    : _: ^- i& ]: Q* H+ l
  4. /*-Specials-*/
    : `8 b. M$ l- C
  5. define symbol __ICFEDIT_intvec_start__ = 0x90000000;
    ! f# l+ m4 a9 {% w8 u3 _
  6. /*-Memory Regions-*/: E. l. c+ ^& ~. i+ \/ y; ~
  7. define symbol __ICFEDIT_region_ROM_start__     = 0x90000000;% W1 o9 U" n& R$ i; D3 ~
  8. define symbol __ICFEDIT_region_ROM_end__       = 0x90100000;$ `( \- t2 s7 w4 ]; C- u; R
  9. define symbol __ICFEDIT_region_RAM_start__     = 0x20000000;
    1 Q, Y: d) R/ R( K3 f
  10. define symbol __ICFEDIT_region_RAM_end__       = 0x2001FFFF;  _& Z& W1 l- H6 ?
  11. define symbol __ICFEDIT_region_ITCMRAM_start__ = 0x00000000;, [. P2 S2 T1 `6 i- L- o
  12. define symbol __ICFEDIT_region_ITCMRAM_end__   = 0x0000FFFF;
复制代码

* }, u9 G- q2 D# r9 u9 K) ^4.bootloader程序与应用程序设计
$ u+ P6 C. c5 @: ]# |7 G对于bootloader而言,主要的任务有一下点:6 e2 o, L. X9 Q' n/ z8 V) t
9 G; Q) i8 P/ E% r5 Q3 H- v
1、接受和处理上位机下发的更新程序,并将其写入到外部flash备份区9 z  N1 N1 j& |* h3 Q! o

8 K+ F9 u- W/ L0 o# q2、对更新到外部备份区的程序拷贝到应用区: ]5 r) `+ O: C- O2 j
* ^4 f2 {6 e7 t; d/ }. W
3、初始化QSPI为内存映射模式,引导程序启动,跳转到应用程序$ H, _9 N# K* b7 [7 X

. i, i5 c, Q( [) g6 p/ _9 ^8 l) S) d' p/ y3 X
2021011423012391.png

" `. E. B4 u# `8 l- g& k. C: C
& T0 k; Y8 o5 w% {* y7 V
20210114230131258.png
& H& i# ~" {' d, h+ \

# Z! w0 E% U4 ]% S$ b我们将STM32H7整个内部128k flash分配为bootloader程序存储区。装置上电,首先从内部flash 地址0x8000000进行启动,具体执行流程如下图所示:
& H- |+ u$ L" t* j4 o; E6 r7 o8 p. e3 Z0 \  G. Z
20210114230154821.png
: I, {4 [8 A5 H

9 }* l+ J& J$ V; ?- N* D6 F# I$ B' w# U/ E

6 p& g; b: y5 B$ o' E
- ]6 j1 b+ _; X1 \1 ]2 Z% {8 {, g! h7 e) V
赞 收藏 评论0 发布时间:2021-12-24 18:00

举报

0个回答

所属标签

相似技术帖

官网相关资源

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