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

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

[复制链接]
STMCU小助手 发布时间:2021-12-24 18:00
最近公司开发的一个项目使用到STM32H7系列芯片,由于该系列芯片内部flash只有128k,为了方便产品的远程升级,需要开发对应的升级协议及bootloader引导程序。由于片内flash容量不够,需要采用外挂flash的方式存储应用程序,片内flash单纯作为bootloader引导程序存储空间。为了节省成本采用单QSPI flash外挂BANK2作为应用程序外部存储。
5 }  z+ I5 f& I( P- s/ {+ Y5 Q4 [2 b% {- o, a
20210114225354296.png

: {2 B- Z) Q" Y& {$ o0 J: y2 c6 G
1.STM32H7启动流程  b( D" J( y* _. |: A3 Q4 w
IAR工程建立后,系统默认生成的启动文件为startup_stm32h750xx.s,默认生成工程一般在EWARM目录下% Y5 o& S/ A( B- P: c. U
$ z4 t: }$ |& f" C* }; o) ]- U
  1. ' K# Y$ i5 j7 C' ~/ V- |
  2.         MODULE  ?cstartup
    & u- j3 ~9 {5 \+ y8 M8 R; `- }
  3. , {5 ]2 h" H3 `. Y
  4.         ;; Forward declaration of sections.
    : t" x) W) c* L
  5.         SECTION CSTACK:DATA:NOROOT(3)* F7 U* m' J+ b' h; ]  K8 y

  6. 9 O- h+ S& G; R  q
  7.         SECTION .intvec:CODE:NOROOT(2)  W8 |  k+ ]7 z6 c$ @. V
  8. . i4 g8 P4 L- e5 x9 l
  9.         EXTERN  __iar_program_start
    % z3 U% Q# X$ h/ r5 k* ~7 _5 a
  10.         EXTERN  SystemInit
    9 H, J* p# _7 ?* d' ]3 B6 v
  11.         PUBLIC  __vector_table
    / O6 a3 ^8 \+ e% R" q( w
  12. , v4 g- d1 a  y0 Y. l
  13.         DATA
    * y$ F4 n6 M; U- k
  14. __vector_table
    ( Q! F2 }6 B) y2 a3 r$ S
  15.         DCD     sfe(CSTACK)
    3 Y2 Z6 t# w# P/ H8 S  o
  16.         DCD     Reset_Handler                     ; Reset Handler
    5 M$ H  a3 Z- v* E

  17. ' x. m- g/ U7 q# K
  18.         DCD     NMI_Handler                       ; NMI Handler
复制代码

  r: u; {- ^" x( i, u定义了一个 CSTACK 的段,然后在启动代码中先声明这个段) G2 T$ N# r3 p7 C. e' a  d# \7 u

/ @& ]7 A- F, Q. w) q7 ^定义 .intvec 段,中断向量独立在一个叫 .intvec 的段当中,这个段是 4字节对齐(2^2)所以用 DATA 来首先处理向量的入口地址为 4的倍数,然后放向量表
2 K) p5 ?/ n5 P. O: m! \( L' p2 ?
3 M! S1 @. Q% x6 Q3 r& dDATA 进入DATA模式
" C+ I+ ~* }, S$ i! O, w6 b
' L+ w0 T/ X. ^. pDCD sfe(CSTACK) 通过 SFE 运算得到改段的结束地址(注意这个运算是在link的时候完成,链接文件为xxxx.icf,比如:stm32h750xx_flash.icf)
; }1 Q3 c! O0 w* l) V. T- d0 y7 V, d
7 g- o6 V+ X) W. d' D3 t文件剩下为定义中断向量表。+ S& Q( m# G5 w

: t9 ^2 T: x* `5 z) s
  1. ;; Default interrupt handlers.' P$ x' m3 ~: Y2 y* Z7 [" H
  2. ;;
    2 \" \. x: P' J1 k/ W) g5 D
  3.         THUMB
    . M: Y1 t/ C1 M. H" l
  4.         PUBWEAK Reset_Handler
    8 H0 ?1 I8 o! V9 w6 k& U, ]9 C
  5.         SECTION .text:CODE:NOROOT:REORDER(2)  ?9 H5 a! y0 h; I
  6. Reset_Handler
    1 T* t3 h% b7 ]# g. d* z3 J
  7. & u! @! t6 B) u# `
  8.         LDR     R0, =SystemInit3 C5 m- l+ W6 z& R; [
  9.         BLX     R0
    : w- ^; |* E8 \* y8 J
  10.         LDR     R0, =__iar_program_start
    . R, k% r0 F, t- I$ X" L) @
  11.         BX      R0( S5 V4 n% M3 [* O% e4 v9 m) }

  12. ! [& W. w' e6 i
  13.         PUBWEAK NMI_Handler
    : w( Z/ o5 E) y: P3 R7 v% ?4 Z1 ?
  14.         SECTION .text:CODE:NOROOT:REORDER(1)
复制代码
6 h' f9 `. m5 G- I5 N; M
THUMB 进入THUMB模式(THUMB-2指令集)9 J  s! S0 Y  f" F, l# `/ U5 @
# q# S8 o$ h: s9 A
启动文件先使用PUBWEAK 指令声明Reset_Handler为弱定义7 s% z) x0 I* P

7 x; l& n' X" {3 Y, r% G& C0 l9 s4 MSECTION .text:CODE:NOROOT:REORDER(2) 从.text段开始# I$ p# `' E* Q* u3 r) N

, p5 j# a7 N/ N6 l$ RLDR R0, =SystemInit 将SystemInit指针地址赋值给R0% x9 D! R$ [; u% n( n

  k! f- `+ v+ o5 O3 d: jSystemInit 函数一般由STM32 库提供,对于STM32 HAL库,该函数一般位于system_stm32h7xx.c文件中8 \- W6 V4 d  h: a7 o7 B* }( C; N

  ~7 T* J) d% x
20210114230836389.png
/ Q$ [/ M2 @. D- A, d6 m

' G5 Y) L: ~* p+ J
  1. void SystemInit (void)1 z, B; X- m" B4 W, p, v
  2. {" a7 V. Z( [  I3 u# K( L, J7 {( Z
  3. #if defined (DATA_IN_D2_SRAM); S, ]1 d/ l8 K. Z% O
  4. __IO uint32_t tmpreg;
    - [2 r, p3 ]6 T9 \# p( n
  5. #endif /* DATA_IN_D2_SRAM */6 i) r1 G' A2 \# W2 l6 \

  6. ! p/ [/ z/ [: z% u# U6 n
  7.   /* FPU settings ------------------------------------------------------------*/$ X* e& v% v: z. F, F' S
  8.   #if (__FPU_PRESENT == 1) && (__FPU_USED == 1)( l) A% }  t" i7 o/ x# W2 S: \
  9.     SCB->CPACR |= ((3UL << (10*2))|(3UL << (11*2)));  /* set CP10 and CP11 Full Access */2 x5 r+ Z/ e  o6 p9 D$ k
  10.   #endif
    5 p+ i' [1 c8 V) L& k; p
  11.   /* Reset the RCC clock configuration to the default reset state ------------*/
    ) V) {8 T, k$ t& C4 q! F
  12.   /* Set HSION bit */* z. ~! l- c5 v* s) c% O0 d6 C
  13.   RCC->CR |= RCC_CR_HSION;/ D2 L( e3 m, K  A( g  S7 R7 I

  14. - ?5 \8 l& N0 e3 x6 i% D
  15.   /* Reset CFGR register */* |; u$ v7 [9 t. k' j! I+ U3 d
  16.   RCC->CFGR = 0x00000000;
    ! w9 |7 i8 u2 c6 f
  17. ......
      W/ Z: u) [1 Y) `
  18.   /* Configure the Vector Table location add offset address for cortex-M7 ------------------*/
    3 f% {$ B' B! ^. m8 Z& q
  19. #ifdef VECT_TAB_SRAM3 e! l5 e! i- Y1 t  F" L
  20.   SCB->VTOR = D1_AXISRAM_BASE  | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal AXI-RAM */
    0 M- g6 ^+ q( m' W
  21. #else- M4 H7 c3 |! Q1 y' ~+ L# g
  22.   SCB->VTOR = FLASH_BANK1_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH */8 E4 V7 l5 R; z: r5 a2 c" D
  23. #endif8 v- x. ^2 R/ r, W9 ?1 t
  24. ' V2 w; i& H) n: y# ^! \4 P
  25. #endif /*DUAL_CORE && CORE_CM4*/8 X0 M9 I3 O( [6 K. \8 Q6 w. J% @
  26. . v$ m, g3 h5 g/ N. w) N8 _- q' U
  27. }
复制代码
$ ^6 q% y! [8 j. ?$ h5 h7 L" v
SystemInit 函数完成系统时钟、RAM、中断向量表地址的初始化。8 Y0 ^) T) z# e4 @

& C- w9 C+ {, D8 d; N9 ~
  1. BLX     R0 跳转到*R0*中的地址,执行SystemInit 并返回
复制代码
  1. LDR     R0, =__iar_program_start //将__iar_program_start地址赋值给R0
复制代码

) i; V9 d2 q6 h+ ^/ F__iar_program_start 函数一般都IAR系统提供,,我们可以在IAR工程中Option进行修改
% l' ]& F7 E- `. Z3 l! @  O* l2 L; G2 R$ @( |/ i+ o( h
如果想具体了解__iar_program_start干了那些事情,可以参考《在main()之前,IAR都做了啥?》; a! B: Y1 R; x( {- M* U
; ~* N# g" l/ G/ d% U
启动文件主要完成如下工作,即程序执行过程:
' n. N; g' g4 ?; k" A6 S: S& l* ~7 I$ w5 P0 h
- 设置堆栈指针SP = __initial_sp。
- d6 b( p8 s$ J1 x* k+ h: b. Q* [& F, K) N! _# E
- 设置PC指针 = Reset_Handler。% }8 }! o5 m# X$ v  q  s

2 ~" I4 W5 n% U- 设置中断向量表。$ O! Z8 ?# R9 v5 y" i2 B9 a' _
7 h1 o: K( o2 c) d( ?
- 配置系统时钟。
( k3 r2 y6 d" Y1 q) j4 @
4 l" N  s5 _2 T' n, ?! D6 l- 配置外部SRAM/SDRAM用于程序变量等数据存储(这是可选的)。! F. y( V' r  O
" o' i3 E, t6 G4 `- `
- 跳转到C库中的 __main ,最终会调用用户程序的main()函数。
# B: L8 r1 a$ @& \) Z1 K4 b. W! ~+ w- |/ p- \: N4 I
2.XIP技术
, F8 x) m8 c8 ?& Q* nXIP,executed in place,本地执行。操作系统采用这种系统,可以不用将内核或执行代码拷贝到内存,而直接在代码的存储空间直接运行。采用这样的技术既可以节省可用内存又可以减少加载的时间。应用程序可以直接在flash闪存内运行,不必再把代码读到系统RAM中。flash内执行是指nor flash 不需要初始化,可以直接在flash内执行代码。但往往只执行部分代码,比如初始化RAM.比如在arm处理器中,Nor flash就存放了引导系统启动的Bootloader,不过大小比较小(仅2M空间)。
2 _: G0 t# E5 A: Y# r2 `  n
20210114225737419.png
7 m, g+ \( n2 G  w

/ Z5 Z3 I- L4 C9 f  F/ l8 y但是,Flash的存储器访问周期要比RAM大得多,在使用XIP技 术后可能会降低程序的运行速度,不过由于CPU的指令预取机制以及Cache机制,实际使用起来并不会明显降低应用程序的运行速度。9 g/ d- N, \) b$ l  i! G
- A& g$ V- D* G. R0 ]
20210114225835865.png

( j2 c: w, K3 d  ^  A/ e. o' y; R4 r/ j/ n4 x) Z; [- p
而右边的图,是针对Nor Flash的,这个很明显,CPU可以像读内存一样,直接跟Nor flash交互,即可以直接从Nor Flash中取指令,然后交给译码模块和执行模块进行执行,可以说,相比较Nand flash,Nor flash的操作对于CPU来说,简直就像是面对面一样。
' u5 b2 |7 D$ k" [( C& ^, i; k" t* ?  R; o2 x/ `5 g
进一步, 为什么Nor Flash可以实现XIP,而Nand flash就不行呢?$ R. m; S" s, Q* e* S- e. o( ~

; p6 ]; `8 ?7 w& ]+ a# G. k- Q有一个概念:嵌入式系统中代码的执行方式:6 Y8 G( f# ?$ J$ F  Z8 }
& }. c/ e% W. q1 @$ i( m% \; V7 P" l
(1)完全映射:嵌入式系统程序运行时,将所有代码从非易失存储器(Flash、ROM等)复制到RAM中运行。
7 x8 ?5 v2 U5 u. P5 D, z4 G2 p
2 [4 N7 T  h, k(2)按需分页:只复制部分代码到RAM中,这种方法对RAM中的页进行导入/导出管理,如果访问位于虚存中但不在物理RAM中会产生页错位,这时才将代码和数据映射到RAM中。  T5 c2 m4 m( V" J+ `! C* ?3 e
2 g. \( m7 W/ }! m) c+ J2 R( U& `
(3)XIP:在系统启动时,不将代码复制到RAM,而是直接在非易失性存储位置执行,RAM中只存放需要不断变化的数据部分,如下图
: _( K% L2 {9 u; e$ L0 G4 Y# b- {# c( N4 ?# G
20210114225911954.png
$ x1 i' u, X( m& I: ]+ _
: Q# G- [( k/ H) a! V  p4 d
如果非易失性存储器(Flash)的读取速度与RAM相近,则XIP可以节省复制和解压的时间,Nor flash和rom的读取速度比较看(约100ns),比较适合XIP,而Nand flash的读取操作是基于扇区的,速度相对很慢(us级),因此不适合实现XIP系统,不过Nand flash的写速度比Nor的快,更适合做存储和下载系统。4 C' e  Z) t# k
# ~( u5 L1 s: Z
解释二:
0 t8 B# O3 [0 `5 }+ w2 v* g两种芯片的结构不同
% x+ e) h  H. [. G7 sNOR flash之所以可以片内执行,就是因为他符合CPU去指令译码执行的要求。CPU送一个地址出来,Nor flash就能给出一个数据让CPU执行,中间不需要额外的处理操作。9 n2 o7 ?( Y8 Q6 N. O, s( `
NAND flash不一样是因为nand flash有地址,数据,命令共用IO口的问题,cpu把地址发出来之后,并不能直接得到数据,还需要控制线的操作才能完成。就是他没有专用的SRAM接口。
; L8 l* x  Y3 ?1 i3 C9 Q! q5 I, w  x
- {- a: y) f: T  M解释三:  T6 f4 i# F! Q4 {& T
芯片内执行主要是是看芯片可不可以线性存储代码(假如硬件支持芯片接口),只要能保证芯片的存储空间是线性的(也就是无坏块),都可以片上执行,在读取Flash时候,容易出现“位翻转(bitconvert),在Flash的位翻转(一个bit位发生翻转)现象上,NAND的出现几率要比NorFlash大得多。这个问题在Flash存储关键文件时是致命的,所以在使用NandFlash时建议同时使用EDC/ECC等校验算法。 ”" @; U( s. u% N' |9 l

& C+ B8 I& p. f3 F. A但是,如果能保证不出错,也还是可以进行XIP,可以在其上执行代码的:
9 g; u. S. I; e. S4 l" }9 X# P9 H/ k9 f“所谓XIP,就是CODE是在FLASH上直接运行. NANDFLASH只是不适合做XIP,但并不是不能做XIP“2 A5 U1 i, K  S1 p/ l2 y
要一段CODE能够正确的运行,要保证它的CODE是连续的,正确的.
8 i5 Q  m6 S4 w6 H, X8 p由于一些电气特性的原因,NOR FLASH能够做到这一点,不存在坏道或坏块,所以能够做XIP.( |2 h' r# ?0 K( q: R$ M+ |
而对于NAND FLASH, 它只保证它的BLOCK 0是好的,其他的块并不保证,虽然出错的几率比较低,但还是有出错的可能,所以CODE可能无法连续正确地执行.; b7 g: a" E, [) S2 f. Y# P* k; k& O

2 i' A) h  Y0 f但只要你有额外的保障措施,比如说在执行CODE之前去做一次ECC校验,来确保CODE是连续正确的.那你也可以做XIP. 有人这么做了,而且也证明是成功的, y* \5 q. C" U: u+ K
# K7 B# L3 R% S# N  [
由于芯片外挂的是W25Q16 为NOR flash,支持XIP,并且STM32H7系列芯片QSPI FLASH支持内存映射模式。STM32H7芯片可以将W25Q16芯片内的应用程序映射到地址0x90000000,虚拟为芯片内部内存进行执行。由于产品为了节省成本没有采用QSPI 双flash模式,执行效率会再降低一点。经过测试从bootloader跳转到应用程序,起来需要经过约2.6s左右。具体中断执行效率还需进行进一步测试。9 m* B) O" t6 E5 m2 i* x
QSPI执行速度对比图
& j& A7 w2 T+ H% }9 r
8 g6 X) ~( m- J) B
20210114230031779.png
/ h& K2 M  H# v: Q' z% H+ Q

; I$ z: Z# K* P* H; r. i) n& e( Q3.启动文件修改! f. u$ B7 F% p* `/ ~# d
对于bootloader程序,程序再芯片内部128k flash中执行,程序默认启动执行地址为0x8000000,无需修改IAR 的link链接配置文件。& v; A( Q' ~) ^8 d( F/ ]
对于应用程序,需将IAR 的link链接配置文件****.icf文件中的__ICFEDIT_intvec_start__ 地址,__ICFEDIT_region_ROM_start__地址,__ICFEDIT_region_ROM_end__地址进行修改,具体可以通过IAR options->Linker->Config,里面进行修改6 b- @4 H$ ]& [' F

) G/ G7 e. {2 k! o4 M
20210114225558558.png
2 o# W5 I5 o% \5 w5 r$ P: X
- }  O+ U, ]7 V, J3 E) q1 C5 h4 e
20210114225618875.png

) Y/ |/ D1 n; E# ]0 k+ P& b! A. k$ [/ `2 {5 F4 @9 s
修改完成后,查看IAR配置后的icf文件,具体内容如下4 f9 a8 B; a, l! v9 ~1 Z

: ]  j. x0 G. d* d0 c
  1. /*###ICF### Section handled by ICF editor, don't touch! ****/* C/ Y  G( N  V% O- Y: `3 m
  2. /*-Editor annotation file-*/
    6 U6 }4 o( \0 h; N5 V  E9 ~
  3. /* IcfEditorFile="$TOOLKIT_DIR$\config\ide\IcfEditor\cortex_v1_0.xml" */3 f9 [, {- p2 z" y. r
  4. /*-Specials-*/2 K* e: h$ ~# W$ a
  5. define symbol __ICFEDIT_intvec_start__ = 0x90000000;1 n) m0 a( i' X  s$ {" ?6 ~
  6. /*-Memory Regions-*/
      h. `- \- A0 Y0 w. d' N5 k
  7. define symbol __ICFEDIT_region_ROM_start__     = 0x90000000;
    * r$ A4 Z7 P* v* j. z9 ^& J8 Y
  8. define symbol __ICFEDIT_region_ROM_end__       = 0x90100000;
    3 A! {7 S/ A  k2 T4 u
  9. define symbol __ICFEDIT_region_RAM_start__     = 0x20000000;, q' Z* Z8 ]( O6 z" d% N
  10. define symbol __ICFEDIT_region_RAM_end__       = 0x2001FFFF;
    2 s$ ]5 ^  N$ y2 m! y6 `6 \$ }; H5 X
  11. define symbol __ICFEDIT_region_ITCMRAM_start__ = 0x00000000;
    ) E, C. c! F. ?# w
  12. define symbol __ICFEDIT_region_ITCMRAM_end__   = 0x0000FFFF;
复制代码
- x5 h5 S, @1 K5 w5 l
4.bootloader程序与应用程序设计. h. k9 Y# b! `
对于bootloader而言,主要的任务有一下点:
7 e3 s. o3 W  {  q$ _- N, M4 ]9 N- }7 B+ {9 A/ B
1、接受和处理上位机下发的更新程序,并将其写入到外部flash备份区
5 d' s& |4 q) `9 s) V4 h# S) N% G% }% P8 Y% S# w
2、对更新到外部备份区的程序拷贝到应用区
' [6 O( [- ?, B' a7 [/ O% c; V
1 E( O. L* g5 }) o3、初始化QSPI为内存映射模式,引导程序启动,跳转到应用程序7 H6 u9 I) j* V4 t: p
7 m# z5 }- _. _$ W
2021011423012391.png
* i, W& Z- }$ b" J$ o3 z
6 v$ @' l9 V( i
20210114230131258.png

# y, R5 {- X+ O- c$ b3 ~4 {# P3 C' b" Q( E: S1 \- D: a, E
我们将STM32H7整个内部128k flash分配为bootloader程序存储区。装置上电,首先从内部flash 地址0x8000000进行启动,具体执行流程如下图所示:( a6 |* O: k* P# M& t$ h7 x

8 H& b. Q$ [$ l3 z0 L
20210114230154821.png

3 `- _2 x# K) A& f$ I! m2 B) g% F$ n
% ?; R( S& _& N
  d' J5 P& @, D

) Z0 ^* V' Y) a# g2 ]
8 o5 c) F1 H" Q( D: t  |+ A$ t
收藏 评论0 发布时间:2021-12-24 18:00

举报

0个回答
关于意法半导体
我们是谁
投资者关系
意法半导体可持续发展举措
创新和工艺
招聘信息
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版