8051 Memory Spaces

载自::http://blog.sina.com.cn/litianping0709 作者:叶雨荫城(阿雨)

今天来复习下8051单片机的内存空间模型,之前看过嵌入式开发的书籍,提到内存空间主要分为三大块:段、堆和栈,其中段主要分为指令段、数据段,即为.text,.data,.bss,.rdata。用于存放程序段及相应的静态数据(全局变量或者static类型的数据)。而动态数据主要存放在堆和栈中。其中堆主要用于用户自定义的内存分配,如malloc(c语言)或者new(java或者c++语言)操作导致的内存分配,而栈主要用于函数调用过程中形参及局部变量的分配。而对于8051单片机来说,存储模式完全是另外一种形式,与avr与MSP430不同,8051的存储模式主要有两个特征:(1)有限的栈空间(2)code banking--代码分页

 

当然本文只介绍contiki支持的特性,不是sdcc手册或者soc的数据手册,如果想详细了解,请参考它们相关的数据手册。

SoC Physical Memory and Memory Spaces

首先介绍一下物理内存及地址映射机制。

Physical Memory

soc上的物理内存主要分为如下几大块:

Flash:存储代码段及数据常量

Static RAM - (S)RAM: 数据存储

Special Function Registers (SFRs): 控制硬件

Flash Information Page (Info Page): 设备信息和配置

XREG: 外部寄存器,不能称之为SFRS,原因下面有介绍

Memory Spaces

用非常简单的形式,8051片上系统有四种不同但是覆盖的地址空间:DATA, CODE, SFR and XDATA。

CODE: 只读程序存储空间. 地址最大空间为64KB. 映射到flash。

DATA: 快速读取(一条指令), 读/写数据存储空间. 地址大小为256bytes. 映射到SRAM.

DATA的低128字节可以利用间接或者直接寻址

DATA的高128字节只能间接寻址

SFR: 读/写SFR存储空间, 一条cpu指令就能直接存取.

SFR的地址被8整除,但也可bit-addressable。

XREG没有映射到SFR地址空间(which is why they are not called SFRs)

XDATA: 存取比较慢,通常需要4-5个指令周期, 宽度16-bit , 读/写存储空间. XDATA可以寻址整体RAM区,同时可以寻址SFRS,部分flash空间,RF寄存器,XREGS,在cc2530上,XDATA同时映射到了Info Page上。

 

SDCC Terminology

sdcc利用自己的一些术语(storage classes)来定位存储空间,下表列出了这种对应关系

映射关系对应表
SoC Memory SpaceSDCCStorage Class SpecifierPhysical Memory
XDATAxdata / far / external RAM__xdata or __farSRAM, (parts of) Flash,
SFR, RFR, XREG, Info Page...
DATAidata / internal RAM__idataSRAM
lower 128 bytes of DATA
(the directly-addressable part)
data / near__data or __nearSRAM
lower 256 bytes of XDATApdata / Paged Data__pdataSRAM
CODEcode__codeFlash
SFRsfr__sfr or __sfr16
or __sfr32 or __sbit
SFR

 

Building Software with SDCC

明白了硬件,接下来我们需要明白相应的变量是分配到什么内存空间的。这个主要由SDCC的命令行参数来决定(或者由#pragma directives来决定,这里暂且不做讨论)。

SDCC Memory Models. Variables, Function Parameters and the Stack

当为8051-compatible芯片创建代码时,SDCC利用四种模型之一来创建:small,medium,large,huge。这些模型利用命令行参数指定,--model-foo (e.g. --model-large). 模型的选择主要影响下列因素:

  • 为缺少存储类型指定标识符(storage classes specifier)的变量分配空间的策略
  • 代码分页策略(code banking)

本篇文章主要集中讨论第一个方面

当然,用户可以还有另外一个选项--stack-auto。stack-auto的出现与否直接影响到函数参数和自动变量的存储空间分配策略。同时还会对函数的可重入性(reentrancy)产生间接但是非常重要的影响。

当--stack-auto参数没有指定时,编译器默认的操作就是将局部变量放在内部或者外部RAM中(取决于存储模型),这样的操作导致的结果就好像这些变量是static!因此没有--stack-auto选项,函数是non-reentrant。当--stack-auto指定之后,局部变量将会放到stack中。

当然这听起来有点迷糊,下面的表格列出了一些例子,展示了根据不同的存储模型/stack-auto相应的结合所带来的不同效果。

SDCC Memory Models and Variable Allocation
Example CodeSmallMediumLarge / HugeSmall + stack-autoMedium + stack-autoLarge / Huge + stack-auto
File Scope
int foo;datapdataxdatadatapdataxdata
static int foo;datapdataxdatadatapdataxdata
__data int foo;datadatadatadatadatadata
__xdata int foo;xdataxdataxdataxdataxdataxdata
__data static int foo;datadatadatadatadatadata
__xdata static int foo;xdataxdataxdataxdataxdataxdata
Local Scope
int foo;datapdataxdatastackstackstack
static int foo;datapdataxdatadatapdataxdata
__data int foo;datadatadataerrorerrorerror
__xdata int foo;xdataxdataxdataerrorerrorerror
__data static int foo;datadatadatadatadatadata
__xdata static int foo;xdataxdataxdataxdataxdataxdata
Function Parameters
void bar(int a, int b, int c);datapdataxdataregisters and stackregisters and stackregisters and stack

 

基于contiki的大小限制,需要加上--stack-auot进行编译。同样,small和medium模型不适合。因此,对与8051的移植,我们要不适用large或者huge模型。如此,在创建contiki的代码时,我们的变量将会根据绿色部分的规则进行存储空间的分配。

The Stack

现在大家应该清楚什么变量将会被分配到且开发者怎么样决定这样的操作。这很重要,因为我们知道8051的stack非常有限,基本上,栈就位于DATA的存储空间上。DATA当然也支持bit变量。栈会位于DATA剩余的任何部分,因此,栈的理论最大深度为256bytes,当然此时是假设没有任何数据位于DATA中,contiki的8051的移植保留的栈空间大小为223字节。

 

Understand (and Avoid) Stack Overflows

从上可以看到,栈的最大深度为223字节。这很重要:假设在一次执行的过程中stack的大小超过这个值,节点将会崩溃。为了避免这些情况,有些小tips帮助你写出stack-friendly的代码。先总结下变量分配规则吧(--model-large(或者huge) --stack-auto):

  • 有storage class标识的变量随标识符指定,当然局部或者非静态变量不能指定storage class,否则会导致编译错误。
  •  动态的局部变量分配在stack中
  • 全局变量分配在外部的RAM中(SRAM)
  • 静态变量(不分范围)分配在外部RAM中(XRAM)。
  • A const is allocated on flash, thus shares space with code.常量存储在flash上,与代码段共享空间
  • 每个函数调用将会将变量放在stack中,返回时销毁。当然多少字节取决于函数的参数和返回值。

 

看一个例子:

struct some_big_struct {
 uint32_t first_field;
 uint32_t second_field;
 uint8_t and_a_buffer[64];
};
static struct some_big_struct do_this;
int some_function {
 struct some_big_struct eeek;
 unsigned char huge_buffer[128];
 printf("Avoid this way of printing the values of a, b, c and d, which are %u, %lu, %lu and %d respectively\n", a, b, c, d);
 printf("Vals: \n");
 printf("a= %u ", a);
 printf("b= %lu ", b);
 printf("c= %lu ", c);
 printf("d= %d\n", d);
} 
在上述的例子中,some_big_struct的结构体为72个字节,eeek为一个动态分配的局部变量。根据上述的规则,他们将会被分配在stack空间中。这样就意味着你用一个简单的变量就消耗了我们mcu的30%的栈空间
The do_this variable is allocated in XRAM, of which we have just under 8KBytes. do_this变量分配到XRAM中,这个变量同样占据了72个字节,但是不会是我们的节点崩溃。但是有两点不好:(1)我们必须主要不要过量消耗我们的XRAM空间。(2)这个变量是静态的,只要进行了分配,它就一直存在,如果你只是用一次,那么你就是在浪费我们mcu的空间。为了更好的做出决定,下面是一些参考。
 ProsCons
On Stack
  • It will fall out of scope when no longer used
  • Ideal when we won't be using the variable for the entire software lifecycle
  • It may lead to stack overflows
  • Overallocating will lead to a crash: The toolchain does not warn us at all
On XDATA
  • Ideal when the varible will be used a lot
  • Takes burden away from the stack
  • Overallocating is easy to debug: The linker throws an error
  • Once declared, the space is reserved "forever"
  • May result in "Insufficient XRAM errors" (but this happens at build time)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值