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

基于STM32的内存管理相关内存架构经验分享

[复制链接]
攻城狮Melo 发布时间:2023-6-12 21:27

使用一个STM32芯片,对于内存而言有两个直观的指标就是 RAM 大小,FLASH大小,比如STM32F103系列(其他系列也是如此):

微信图片_20230612212610.png


: B; c& V4 i8 g1 `; L$ t- @

那么着两个大小意味着什么?怎么去理解这两个内存,那就得从什么是Flash,什么是RAM说起。

一、FLASH 和 RAM基本概念

先来看一张图:

微信图片_20230612212606.png

5 }% }- X( S3 x

1.1 FLASH是什么

通过上图我们可以知道,FLASH属于 非易失性存储器:

扩展一点说,FLASH又称为闪存,不仅具备电子可擦除可编程(EEPROM)的性能,还不会断电丢失数据同时可以快速读取数据,U盘和MP3里用的就是这种存储器。在以前的嵌入式芯片中,存储设备一直使用ROM(EPROM),随着技术的进步,现在嵌入式中基本都是FLASH,用作存储Bootloader以及操作系统或者程序代码或者直接当硬盘使用(U盘)。

然后 Flash 主要有两种NOR Flash和NADN Flash。(对于这两者的区别,下面的话供参考,因为这些介绍都是基于早些年的技术了)

NOR Flash的读取和我们常见的SDRAM的读取是一样,用户可以直接运行装载在NOR FLASH里面的代码,这样可以减少SRAM的容量从而节约了成本。

NAND Flash没有采取内存的随机读取技术,它的读取是以一次读取一块的形式来进行的,通常是一次读取512个字节,采用这种技术的Flash比较廉价。用户不能直接运行NAND Flash上的代码,因此好多使用NAND Flash的开发板除了使用NAND Flah以外,还作上了一块小的NOR Flash来运行启动代码。

STM32单片机内部的FLASH为 NOR FLASH。Flash 相对容量大,掉电数据不丢失,主要用来存储 代码,以及一些掉电不丢失的用户数据。


: C' G1 Z* `* P  g, r( ]

1.2 RAM是什么

RAM 属于易失性存储器:

RAM随机存储器(Random Access Memory)表示既可以从中读取数据,也可以写入数据。当机器电源关闭时,存于其中的数据就会丢失。比如电脑的内存条。

RAM有两大类,一种称为静态RAM(Static RAM/SRAM),SRAM速度非常快,是目前读写最快的存储设备了,但是它也非常昂贵,所以只在要求很苛刻的地方使用,譬如CPU的一级缓冲,二级缓冲。另一种称为动态RAM(Dynamic RAM/DRAM),DRAM保留数据的时间很短,速度也比SRAM慢,不过它还是比任何的ROM都要快,但从价格上来说DRAM相比SRAM要便宜很多,计算机内存就是DRAM的。

DRAM分为很多种,常见的主要有FPRAM/FastPage、EDORAM、SDRAM、DDR RAM、RDRAM、SGRAM以及WRAM等,这里介绍其中的一种DDR RAM。

DDR RAM(Date-Rate RAM)也称作DDR SDRAM,这种改进型的RAM和SDRAM是基本一样的,不同之处在于它可以在一个时钟读写两次数据,这样就使得数据传输速度加倍了。这是目前电脑中用得最多的内存,而且它有着成本优势,事实上击败了Intel的另外一种内存标准-Rambus DRAM。在很多高端的显卡上,也配备了高速DDR RAM来提高带宽,这可以大幅度提高3D加速卡的像素渲染能力。

为什么需要RAM,因为相对FlASH而言,RAM的速度快很多,所有数据在FLASH里面读取太慢了,为了加快速度,就把一些需要和CPU交换的数据读到RAM里来执行(注意这里不是全部数据,只是一部分需要的数据,这个在后面介绍STM32的内存管理中会提到)。

STM32单片机内部的 RAM 为  SRAM。RAM相对容量小,速度快,掉电数据丢失,其作用是用来存取各种动态的输入输出数据、中间计算结果以及与外部存储器交换的数据和暂存数据。

" v5 y: Y. I2 p1 }5 F: W  b+ G% ]

二、STM32的内存架构2.1 Cortex-M3的存储器映射分析

在《ARM Cotrex-M3权威指南》中有关 M3的存储器映射表:


) _4 C) L: E' c5 y" g1 O4 m7 |6 s

微信图片_20230612212603.png

  \' D' X+ p3 p( j' J, o

存储器映射 是用 地址来表示 对象,因为Cortex-M3是32位的单片机,因此其PC指针可以指向2^32=4G的地址空间,也就是图中的 0x00000000到0xFFFFFFFF的区间,也就是将程序存储器、数据存储器、寄存器和输入输出端口被组织在同一个4GB的线性地址空间内,数据字节以小端格式存放在存储器中。


& \% Q" D9 {" ]) a. e4 g, I1 c
1 ?  b8 s( M% i" d5 U
2.2 STM32 的存储器映射分析

STM32存储器映射表(选用的是STM32F103VE的,不同的型号Flash 和 SRAM 的地址空间不同,起始地址都是一样的):


, K1 m) l% S' g; f4 G% L  K

微信图片_20230612212559.png

! J4 N/ Z9 x4 u' {; N% D

那么我们所需要分析的STM32 内存,就是图中 0X0800 0000开始的 Flash 部分 和 0x2000 0000 开始的SRAM部分,这里还要介绍一个和Flash模块相关的部分:

2 q6 i: B- f, V  f5 ]3 T- V9 V' a

微信图片_20230612212556.png

7 D7 A8 p2 \' ?6 O

2.3 STM32的 Flash 组织
0 ]5 a0 @. p6 {9 ]; T& P$ E/ {* O1 a
+ _% I% @) P( ~  P
% [2 p6 i0 O% `8 U
微信图片_20230612212552.png
1 @* c: ]* U$ ^" ]7 S4 C/ h
  K& Y7 P: G" D" a+ U+ G2 Z! A2 d& g! |$ l

STM32的Flash,严格说,应该是Flash模块。该Flash模块包括:Flash主存储区(Main memory)、Flash信息区(Informationblock),以及Flash存储接口寄存器区(Flash memory interface)。

主存储器,该部分用来存放代码和数据常数(如加const类型的数据)。对于大容量产品,其被划分为256页,每页2K,小容量和中容量产品则每页只有1K字节。主存储起的起始地址为0X08000000,B0、B1都接GND的时候,就从0X08000000开始运行代码。

信息块,该部分分为2个部分,其中启动程序代码,是用来存储ST自带的启动程序,用于下载,当B0接3.3V,B1接GND时,运行的就这部分代码,用户选择字节,则一般用于配置保护等功能。

闪存储器块,该部分用于控制闪存储器读取等,是整个闪存储器的控制机构。

对于主存储器和信息块的写入有内嵌的闪存编程管理;编程与擦除的高压由内部产生。

在执行闪存写操作时,任何对闪存的读操作都会锁定总线,在写完成后才能正确进行,在进行读取或擦除操作时,不能进行代码或者数据的读取操作。

2 d! `7 {+ m( i

三、STM32 的内存管理

STM32 的内存管理起始就是对 0X8000 0000 开始的 Flash 部分 和 0x2000 0000 开始的 SRAM 部分使用管理

$ V! o6 Q. I. W9 e# Y( ?: @; }8 W( ]

3.1 C/C++ 程序编译后的存储数据段

参考博文:STM32内存结构介绍

在了解如何使用内存管理之前,先得理解一下 6 个储存数据段 和 3种存储属性区 的概念:

3 Q: I' X8 W! X/ f+ s  o  v- C& d


9 b& N9 w$ T9 x. d, d% I% g

, C, i% O# \6 t+ b! r  x

.data

数据段,储存已初始化且不为0的全局变量和静态变量(全局静态变量和局部静态变量)。static声明的变量放在data段。数据段属于静态内存分配,所以放在RAM里,准确来说,是在程序运行的时候需要在RAM中运行。


/ w9 {8 t4 s' g- i' H6 s

.BSS

Block Started by Symbol。储存未初始化的,或初始化为0的全局变量和静态变量。BSS段属于静态内存分配,所以放在RAM里。

# v' {# i& t0 R: M" g1 S

.text(CodeSegment/Text Segment)

代码段,储存程序代码。也就是存放CPU执行的机器指令(machineinstructions)。这部分区域的大小在程序运行前就已经确定,并且内存区域通常属于只读(某些架构也允许代码段为可写,即允许修改程序)。在代码段中,也有可能包含一些只读的常数变量,例如字符串常量等。放在Flash里。


/ \- x+ u$ x4 V; f% x$ ~

.constdata

储存只读常量。const修饰的常量,不管是在局部还是全局放在Flash 里。所以为了节省 RAM,把常量的字符串,数据等 用const声明


2 E7 o* ~/ g6 J( H

heap(堆)

堆是用于存放进程运行中被动态分配的内存段。他的大小并不固定,可动态扩张或者缩减,由程序员使用malloc()和free()函数进行分配和释放。当调用malloc等函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张);当利用free等函数释放内存时,被释放的内存从堆中被剔除(堆被缩减)。放在RAM里其可用大小定义在启动文件startup_stm32fxx.s中。


& C1 X) I( a9 ^4 T& q3 e* z

stack(栈)

栈又称堆栈,是用户存放程序临时创建的局部变量,由系统自动分配和释放。可存放局部变量、函数的参数和返回值(但不包括static声明的变量,static意味着 放在 data 数据段中)。除此以外,在函数被调用时,其参数也会被压入发起调用的进程栈中,并且待到调用结束后,函数的返回值也会被存放回栈中。???由于栈的先进先出(FIFO)特点???上面这句话正确吗?Cortex-M3/M4的堆栈是向下生长,第一个入栈的元素应该是最后一个才能出来??所以栈特别方便用来保存/恢复调用现场。从这个意义上讲,我们可以把堆栈看成一个寄存、交换临时数据的内存区。放在RAM里其大小定义在启动文件startup_stm32fxx.s中。

. Y5 L0 u5 P; \8 S$ L4 V; g) p' _4 x- i

3.2 STM32 程序编译后的内存占用情况
4 ]3 ^. |' t& }& |* u  \6 b( ~) S
3.2.1 MDK 编译

MDK编译后的结果:


  {% L# m( P9 n# G

微信图片_20230612212546.png

2 B' m, \* Y) x

Code:程序代码部分。.text 段放在ROM里面,就是Flash,需要占用flash空间

RO-data(Read Only)只读数据 程序定义的常量,只读数据,字符串常量(const修饰的).constdata 段放在flash里面,需要占用flash空间

RW-data(Read Write)可读可写数据 已经初始化的全局变量和静态变量(就是static修饰的变量);.data 段需要在 RAM里面运行,但是起初需要保存在 Flash里面,程序运行后复制到 RAM里面运行,需要占用Flash空间

ZI-data(Zero Initialize)未初始化的全局变量和静态变量,以及初始化为0的变量;.BSS段ZI的数据全部是0,没必要开始就包含,只要程序运行之前将ZI数据所在的区域(RAM里面)一律清 0,不占用Flash,运行时候占用RAM.

heap 和 stack 其实也属于 ZI,只不过他不是程序编译就能确定大小的,必须在运行中才会有大小,而是是变化的

因为RAM掉电丢失,所以 RW-data 数据也得下载到ROM(flash) 中,在运行的时候复制到 RAM中运行,如下图所示:

" @0 Q4 u3 B4 {/ b0 V  p

微信图片_20230612212543.png

' k9 i/ S# z) ?6 K' Z% _

由上我们得知:

程序占用   Flash = Code + RO data + RW data

程序运行时候占用   RAM = RW data + ZI data。

Code + RO data + RW data 的大小也是生成的 bin 文件的大小

+ s" S7 T+ m) _

3.2.1 GCC 编译

GCC编译结果:


  W7 f2 ?2 f% F& B& I1 ~

微信图片_20230612212540.png


1 O; O( d2 Y/ N# g

GCC编译, 图中红色的部分是占用 Flash 的大小:Flash = text + data 。蓝色部分是运行时候占用 RAM大小:RAM = data + bss。

. x7 `0 U% k4 k& H0 p  s% q8 n

3.3 STM32 程序的内存分配

我们前面说到的 stack(栈) 和 heap(堆),程序编译完成以后并不能知道运行时候实际占用RAM 大小。但是我们可以知道的是  stack(栈) 和 heap(堆)的起始地址,和能够使用的最大空间,我们先看能够使用的空间大小。


8 S' `$ c( M: c

3.3.1 MDK 环境

MDK是在 startup_stm32fxxx.s 中定义的:


% W0 l, @  ]. x- h

微信图片_20230612212537.png

; a& R+ O0 _& T: _

在 startup_stm32fxxx.s 中我们可以看到关于 Stack_Size 和 Heap_Size的定义,图中的定义就是规定本程序中 栈 的大小为 1K, 堆的大小为 0.5K。

startup_stm32fxxx.s文件是系统的启动文件,startup_stm32fxxx.s主要完成三个工作:栈和堆的初始化、定位中断向量表、调用Reset Handler。(关于STM32的启动文件,我们有单独的一篇文章,请查看另一篇博文    STM32的启动过程(startup_xxxx.s文件解析))

我们在生成的.map文件可以看到 HEAP 和 STACK的起始地址(不懂的可以先看博文下面一节的内容——四、MDK生成的.map文件简析),我们要注意的是:堆使用时候从起始地址开始,往上加栈使用时候从结束地址,就是__initial_sp(栈顶指针的地址)开始,往下减他们的空间大小定义好了,如果入栈元素过大,使得元素减到了堆的地址范围,就是栈溢出,这会导致改变堆中相应地址元素的值。同样的,当申请的动态内存过大,使得堆的变量加到了栈的地址范围,就是堆溢出。

在实际项目中,如果程序复杂,中断嵌套太多,栈需要多设置一点空间如果你使用裸机程序,从来不使用标准库的malloc,heap可以没有,因为永远不会用到,还一直占用着一片RAM区。

所以在我们知道了以上的知识后,我们可以按照自己的工程需求,定义Stack_Size 和 Heap_Size。


  P) m2 [* b  f+ r9 y, v

3.3.2 GCC 环境

如果是在GCC编译器下面,关于 Stack_Size 和 Heap_Size的定义如下图:

9 V* B8 F7 x; |7 c+ _

微信图片_20230612212533.png

* W- E6 D5 Q7 F  h7 {" m

四、MDK生成的.map文件简析

为了更深层次的理解上述内容,我们还有必要分析一下MDK生成的  .map 文件, 那么既然要分析,除了我们关注的flash 和 ram部分的内容,其他的地方也稍微做一下笔记:


& @1 K' }3 j9 e8 u2 [; \2 |, {0 ?

4.1  Section Cross References

主要是不同文件中函数的调用关系:


$ c4 W% [( ]* n" w) g7 k. f8 u

微信图片_20230612212526.png

9 \1 h/ [- n/ t3 h2 \! L; E8 ~; J8 @

我们查看一下 clock.c 文件下的 rt_tick_increase函数:


* v7 x. Z. C5 }3 G# Q

微信图片_20230612212519.png

4 ]% ]6 _5 f6 Y& o) ]8 `5 X

4.2  Removing Unused input sections from the image

被删除的冗余函数:

( o  B; x) n7 K7 }

微信图片_20230612212512.png


8 M7 ]# d% }- [" j

删除函数功能在MDK的配置中可以设置,勾选以后删除得多,不勾选删除得少,如下图:


) e, u+ i, R3 l

微信图片_20230612212507.png


! y& ?5 U1 ?/ F  K, V3 F' M

在 Removing Unused input sections from the image 的最后会列出删除的冗余函数的大小,如果在MDK上改变上图所示的配置,下图中的删除总代码可以看到变化:


. j/ b$ d) M$ }/ z2 _4 M

微信图片_20230612212503.png


: m$ j( N0 U4 L  a4 Q% ?

4.3 Image Symbol Table

- L- P4 V+ P% d( h* t4 F4.3.1 Local Symbols

局部标号, 用Static声明的全局变量地址和大小, C文件中函数的地址和用static声明的函数代码大小, 汇编文件中的标号地址(作用域限本文件):

& W3 J/ R: y- H  z

微信图片_20230612212459.png

& E+ l: X7 J; X

我们找个占用内存地址的地方看一下:

3 d4 O& @4 k6 d% S% i! E- |

微信图片_20230612212453.png


  S4 u7 ?: h& _2 M/ D; ~$ J  ^1 x

我们在 startup_stm32f103xg.s ,可以看到RESET函数:我们继续往下看:

2 f( B& X' \. ]) j1 t- s

微信图片_20230612212445.png


4 I1 n! |% v& G3 [  T3 ]; s

上图中的 i.RCC_Delay 下面跟了一个 RCC_Delay,说明这个函数是用static修饰的,我们找到 stm32f1xx_hal_rcc.c下的RCC_Delay  如图:

/ n" \; U$ x% H. g# j; r

微信图片_20230612212431.png

* u' a6 e5 M+ ~$ _

我们接着看到 SRAM区:


* Y" ?1 Z" G2 V. L- f+ J

微信图片_20230612212427.png

9 B" B% ^% d* t1 F# J

我们继续看到后面的.bss段,包括了 HEAP 和 STACK区域:


# t" E- W% m/ v* z7 k$ p$ w

微信图片_20230612212422.png


- w2 Z4 k8 u1 O( r" {0 l

通过上图中我们可以看到 HEAP 的起始地址为 0x20002338, ram从 0x2000 0000开始存放的依次为 .data、.bss、HEAP、STACK。HEAP在 startup_stm32fxxx.s 中定义过大小为 0x200, 所以结束地址为0x20002538, HEAP 是和 STACK连接在一起的,所以STACK的起始地址为 0x20002538,大小 0X400,结束地址为  0x20002938。最后我们可以看到 __initial_sp 指向的是 0x20002938,入栈从高地址开始入栈,地址越来越小。


$ b# J# ?( M/ f4 y

4.3.2 Global Symbols

全局标号, 全局变量的地址和大小, C文件中函数的地址及其代码大小, 汇编文件中的标号地址(作用域全工程):


9 |" K9 n+ u7 W1 E# `6 M# Y

微信图片_20230612212420.png


* T5 h: o. P+ r1 l) Q+ |

我们找到Flash地址开始的部分:

- F; X4 S9 Q1 Y" j+ U

微信图片_20230612212416.png

& D) p9 u7 h- m$ N$ v) U

在 startup_stm32fxxx.s 能看到对应的部分:


% Z8 `/ a" l0 \' P+ b

微信图片_20230612212413.png

, N7 v* E+ W5 b1 I/ W5 ]( |

看到最后的 RAM区,注意下图中标出的两行的功能:


$ P4 D( Y6 V6 g6 d7 J- z/ Y


) Z9 _' ~+ D2 S5 k

4.4  Memory Map of the image

映像文件可以分为加载域(Load Region)和运行域(Execution Region):加载域反映了ARM可执行映像文件各个段存放在存储器中时的位置关系。


! G  i) M- q% s7 z

微信图片_20230612212404.png


' H  B: r. m" M& U+ [. t9 J

其中还能看出来 Flash中存放的都是 code,和 RO_Data:


# l0 m* `! P& C7 L7 k5 l* d) C7 o! x- T

微信图片_20230612212401.png


3 i1 v0 ?7 w2 S% [* I

我们看看SRAM部分:


3 V  g3 l+ \2 J2 D; V

微信图片_20230612212353.png


3 O; e& F, _% R# D; q1 Y

需要注意一下,我们前面代码he 数据部分都是4字节对齐,PAD一般都是补充2个字节,到了栈部分,需要8字节对齐:


# a! X8 u& B) q- N0 f9 d

微信图片_20230612212349.png

% d! Y9 J; g/ O4 \0 }+ v$ n1 o/ L4 r$ W

4.5  Image component sizes

存储组成大小 这部分的内容就比较直观


. U3 g. U" z( n, L2 }5 b# X

微信图片_20230612212345.png


& h* A. d2 r- h7 R* J% D# ]

最后就是我们熟悉的部分:

$ L5 s# h  |. i; e

微信图片_20230612212327.png

. ]: N+ U& }, A2 S. N! j

五、一些相关的补充内容5.1 STM32 的启动方式

: r( t4 p) }+ K2 O9 {. @0 L; x  E- s' Y; q. m2 k$ d$ A" z. `
BOOT0
BOOT1
启动模式
0XUser Flash memory(从闪存存储器启动)
10System memory(从系统存储器启动)
11Embedded SRAM(从内嵌SRAM启动)

, h  z; a4 e3 S: j- u7 ~; X2 y

第一种启动方式是最常用的用户FLASH启动,正常工作就在这种模式下,STM32的FLASH可以擦出10万次,所以不用担心芯片哪天会被擦爆!

第二种启动方式是系统存储器启动方式,即我们常说的串口下载方式(ISP),不建议使用这种,速度比较慢。STM32 中自带的BootLoader就是在这种启动方式中,如果出现程序硬件错误的话可以切换BOOT0/1到该模式下重新烧写Flash即可恢复正常。

第三种启动方式是STM32内嵌的SRAM启动。该模式用于调试。 用jlink在线仿真,则是下载到SRAM中。

以上三种启动方式我们都很熟悉,但是他的究竟是如何实现的呢?

我们先来看看《Cortex-M3权威指南》关于CM3复位后的动作:


$ a% O/ }; X9 |* a  {8 i: Q8 J& Y2 W7 _

微信图片_20230612212323.png

# @- K: z* }6 J% U6 u7 B

当选择相应的启动方式时,对应的存储器空间被映射到启动空间(0x00000000)。

从闪存存储器启动:主闪存存储器被映射到启动空间(0x0000 0000) ,也就是0x08000000被映射到0x00000000。

从内嵌SRAM启动 :SRAM起始地址 0x2000 0000 被映射到0x00000000。

从系统存储器启动:系统存储器被映射到启动空间(0x0000 0000),也就是0x1FFF F000被映射到0x00000000。(为什么是0x1FFF F000 可以查阅上文中的  2.2小节  STM32 的存储器映射分析,STM32互联型产品这个地址不一样,此地址由ST官方写入了一段BootLoader代码,可以通过官方BootLoader升级MCU固件,无法修改)。

6 B$ C1 t  |0 |8 U- E4 v

5.2 STM32启动地址和 Bootloader 说明

通过上面的内容,我们现在知道STM32 从Flash程序启动以后会从 0X08000000 开始运行,那么他这个地址是否可以修改,答案是当然的!但是单独的改他的启动地址,没有任何意义,一般都是需要使用到 Bootloader 才会使得应用程序的地址发生变化。

在 0x1FFF F000 这个地址上官方写入了一段 BootLoader 用户使用,我们也可以自己写一段 BootLoader 程序方便自己使用,因为是自己写的,他还是用户程序,只是我们自己把程序分成了 BootLoader部分和 应用程序部分,大概的意思如下图所示:


" i+ @- {2 e2 C( a( h8 i+ X

微信图片_20230612212317.png


7 }5 ?8 t) i$ n& V

为什么要使用用户 BootLoader :

在有些项目中,可能因为某些原因需要经常更换 程序,如果每次都是重新烧录,特别的麻烦,那么我们就可以自己设计一个 BootLoader,通过 SD卡进行升级:上电后先运行 BootLoader,BootLoader主要工作是检测是否有SD卡,SD卡中是否有需要的BIn文件, 如果检测到就将其复制到 应用程序区域 使得程序得以更新,更新结束以后跳转到应用程序执行;如果没检测到相应的SD卡,就说明程序不需要更新,也跳转到应用程序执行;

以上主要是说明使用 BootLoader 的思路与适用场合,至于具体的实现其实网上有很多教程,如果有机会我也会补充或者单独写一篇文章总结一下

(说明:下面是零碎的笔记,还没整理完,仅供参考 stm32 FLASH的起始地址是0x08000000,当然也可以自定义起始地址,不过记得在main函数中定义变量后加一句SCB->VTOR=FLASH_BASE | OFFSET;OFFSET是想要偏移的量,可宏定义或直接0xXX。当然也可以调用库函数 NVIC_SetVectorTable()进行偏移,效果一样。IAP升级这样用的多)


0 ?- l. X0 B) x2 I+ s

5.3  单片机和 x86cpu运行程序的不同

参考博文:cpu运行时程序是在flash中还是在RAM呢?x86的pc机cpu在运行的时候程序是存储在RAM中的,而单片机等嵌入式系统则是存于flash中

x86cpu和单片机读取程序的具体途径:pc机在运行程序的时候将程序从外存(硬盘)中,调入到RAM中运行,cpu从RAM中读取程序和数据
0 k' G9 h! z9 X0 e( f  o$ T而单片机的程序则是固化在flash中,cpu运行时直接从flash中读取程序,从RAM中读取数据

原因分析 :x86构架的cpu是基于冯.诺依曼体系的,即数据和程序存储在一起,而且pc机的RAM资源相当丰富,从几十M到几百M甚至是几个G,客观上能够承受大量的程序数据。3 o/ K" `8 \" z  j! ]% u) ^: a, K
单片机的构架大多是哈弗体系的,即程序和数据分开存储,而且单片的片内RAM资源是相当有限的,内部的RAM过大会带来成本的大幅度提高。

冯.诺依曼体系与哈佛体系的区别:二者的区别就是程序空间和数据空间是否是一体的。早期的微处理器大多采用冯诺依曼结构,典型代表是Intel公司的X86微处理器。取指令和取操作数都在同一总线上,通过分时复用的方式进行的。缺点是在高速运行时,不能达到同时取指令和取操作数,从而形成了传输过程的瓶颈。5 t% a6 I5 S% i3 a: ?
哈佛总线技术应用是以DSP和ARM为代表的。采用哈佛总线体系结构的芯片内部程序空间和数据空间是分开的,这就允许同时取指令和取操作数,从而大大提高了运算能力。


5 G: m/ }3 ~( f, C

5.4  ARM 和 x86cpu 指令集RISC和CISC说明

(此部分是学习了韦东山老师的视频后来记录的,原视频可以在韦东山老师官网里面找到:百问网)

ARM芯片属于精简指令集计算机(RISC:Reduced Instruction Set Computing),它所用的指令比较简单,有如下特点:① 对内存只有读、写指令 ② 对于数据的运算是在CPU内部实现 ③ 使用RISC指令的CPU复杂度小一点,易于设计

0 P, `5 Z7 P& Z' e8 h

微信图片_20230612212313.png

" f+ }+ q; v: l% L

在ARM架构中,对于乘法运算a = a * b, 在RISC中要使用4条汇编指令:① 读内存a ② 读内存b ③ 计算a*b ④ 把结果写入内存

x86属于复杂指令集计算机(CISC:Complex Instruction Set Computing), 它所用的指令比较复杂,比如某些复杂的指令,它是通过“微程序”来实现的。


: F/ w) s8 h4 L$ o- y: Q

微信图片_20230612212306.png


6 Z; ^5 [1 y' V3 U! b

比如执行乘法指令时,实际上会去执行一个“微程序”, 一样是去执行这4步操作:① 读内存a ② 读内存b ③ 计算a*b ④ 把结果写入内存

: `& l* y, L2 r2 l9 `

RISC和CISC的区别:

  • CISC的指令能力强,单多数指令使用率低却增加了CPU的复杂度,指令是可变长格式;
  • RISC的指令大部分为单周期指令,指令长度固定,操作寄存器,对于内存只有Load/Store操作
  • CISC支持多种寻址方式;RISC支持的寻址方式
  • CISC通过微程序控制技术实现;
  • RISC增加了通用寄存器,硬布线逻辑控制为主,采用流水线
  • CISC的研制周期长
  • RISC优化编译,有效支持高级语言4 A& B% K5 j: t, V

##六、  GCC下生成的.map文件 我们知道了MDK下的.map文件,GCC下的.map文件基本上也可以看懂了,这里添加GCC下.map文件关于RAM的部分:


# f# o3 b+ D( p+ L6 U/ \/ q' y

6.1 RAM部分

RAM部分从0x2000 0000开始,首先存放的是.data部分:


3 [4 D; m6 a: P) ^

微信图片_20230612212302.png

- ^: n0 r& m1 {9 k! M9 V; A

接下来是.bss段:

( v& U+ y) |) S1 V9 B: Q

微信图片_20230612212258.png

' d$ c& U1 d( d6 l# D( B

如果使用了FreeRTOS,那么在.bbs段后面会有关于FreeRTOS部分的数据:


! N$ ~2 ]6 f9 O1 }) F4 _

微信图片_20230612212252.png

/ ]0 K( L$ V( O: b: U, I

最后才到了heap和stack:


8 o. p+ ~  V3 d4 u5 ]

微信图片_20230612212245.png

7 P) D/ A- c8 W' {  g, a" H

如有侵权请联系删除

转载自:矜辰所致2 z4 O2 L! T' G% P# q4 q: I- i


/ f0 J) [! C/ T


  ?" e; q" B1 t' ^/ I# m$ v- j* o8 M- ^# |/ W
微信图片_20230612212409.png
微信图片_20230612212440.png
微信图片_20230612212549.png
赞 收藏 2 评论0 发布时间:2023-6-12 21:27

举报

0个回答

所属标签

相似技术帖

官网相关资源

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