转载自中电网-毛毛的博客,三篇文章重新编辑合为一篇,内容未作任何改动。
原文链接:
freescale 16位单片机的地址映射--(1)前言
freescale 16位单片机的地址映射--(2)飞思卡尔16位单片机的资源配置
freescale16位单片机的地址映射--(3)codewarrior中的prm文件
前言:
原来一直不太明白单片机的地址映射,也没有仔细的研究过,我想这就是我不是牛人的原因吧。通常开始学单片机,都是写一些比较小的程序,如果不做项目开发之类的,以飞思卡尔
16位单片机的资源配置来说也足够了。但是前一阵子遇到了一个问题,需要在RAM中存一个比较大的常数数组,但是单单存在RAM中的话,肯定是存不下,考虑到数组是常数,所以只能存在ROM里,但是当时时间较短,没有研究明白,还得到了非常惨痛的教训,觉得不甘心,打算再花时间研究了一下。在以后的文章中我会把我的研究心得记录下来,希望大家提出意见。
飞思卡尔16位单片机的资源配置
以MC9S12XS128MAL为例,其实DG128之类的类似。如图一,128代表的是单片机中的FLASH大小为128KByte,同理64代表的是单片机中的FLASH大小为64 K Byte,256代表的是单片机中的FLASH大小为256 KByte。但是S12(X)所使用的内核CPU12(X)的地址总线为16位,寻址范围最大为2^16 =64K Byte,而这64KByte的寻址空间还包括寄存器、EEPROM(利用Data Flash模拟)、RAM等,因此不是所有的64KByte都是用来寻址FLASH。所以在S12(X)系列单片机中,很多资源是以分页的形式出现的,其中包括EEPROM、RAM、FLASH。EEPROM的每页大小为1KByte,RAM的每页大小为4K
Byte,FLASH的每页大小为16K Byte。因此XS128中EEPROM的页数为8K/1K =8页,RAM的页数为8K/4K = 2页,Flash的页数为128K/16K = 8页。
图一
图二
在单片普通模式中,复位后,所有内存资源的映射如图二所示,其中从0x0000-0x07FF的2K范围内映射为寄存器区,如I/O端口寄存器等,当然寄存器没有那么多,后面的一部分其实没有使用;
从0x0800-0x0BFF,共1K的空间,映射为EEPROM区,由上面的分析,XS128中共有8页的共8K的EEPROM,所以这8页的EEPROM都是以分页的形式出现的,可以通过设置寄存器EPAGE选择不同的页并进行访问;
从0x0C00到0x0FFF的1K空间为保留区(其实这里面也有学问,以后探讨);
从0x1000到0x3FFF的12K空间为RAM区,分为三页,但是和前面所说的EEPROM不同,这三页中有2页(对于XS128和XS256)或一页(对于XS64)为固定页,位于12K空间的后一部分,以XS128为例,其内部的RAM资源为8K,所以其三页中的最后两页(0x2000-0x3FFF)为固定页,第一页(0x1000-0x1FFF)为窗口区,通过设置寄存器RPAGE来映射其他分页的RAM,当然在单片普通模式下,XS128内部已经没有其他的RAM了,所以这一页其实也没有用。但是对于XS256,这一页是有用的,因为它总共有12K的RAM。但是,在单片普通模式下,即没有外扩RAM的情况下,用户是不用刻意的去配置RPAGE的,因为复位的时候,已经默认指向那一页的RAM。
从0x4000-0xFFFF的总共48K的空间为Flash区,分为三页。其中第一页和第三页为固定的Flash页,中间的一页(0x8000-0xBFFF)为窗口区,通过设置PPAGE寄存器,可以映射到其他的分页Flash。
在最后的一页固定的Flash区域中的最后256字节中,保存的是中断向量。
对于RAM和Flash来说,其实固定页和其他的分页资源是统一编址的,不同的是固定页不可以通过寄存器(RPAGE、PPAGE)改变映射,而其他的页必须通过寄存器的设置来选择映射不同的页。
codewarrior中的prm文件
网上广泛流传的一篇文章讲述的是8位飞思卡尔单片机的内存映射,这几天,研究了一下Codewarrior 5.0prm文件,基于16位单片机MC9S12XS128,一点心得,和大家分享。有什么错误请指正。
正文:
关于Codewarrior 中的.prm 文件
要讨论单片机的地址映射,就必须要接触.prm文件,本篇的讨论基于Codewarrior
5.0 编译器,单片机采用MC9S12XS128。
通过项目模板建立的新项目中都有一个名字为“project.prm”的文件,位于ProjectSettings->Linker
Files文件夹下。一个标准的基于XS128的.prm文件起始内容如下:
.prm文件范例:
NAMES
END
SEGMENTS
RAM =
READ_WRITE
DATA_NEAR 0x2000 TO
0x3FFF;
ROM_4000 =
READ_ONLY
DATA_NEAR IBCC_NEAR
0x4000 TO
0x7FFF;
ROM_C000 =
READ_ONLY
DATA_NEAR IBCC_NEAR
0xC000 TO
0xFEFF;
//OSVECTORS = READ_ONLY 0xFF10 TO0xFFFF;
EEPROM_00 =
READ_ONLY
DATA_FAR IBCC_FAR
0x000800 TO
0x000BFF;
EEPROM_01 =
READ_ONLY
DATA_FAR IBCC_FAR
0x010800 TO
0x010BFF;
EEPROM_02 =
READ_ONLY
DATA_FAR IBCC_FAR
0x020800 TO
0x020BFF;
EEPROM_03 =
READ_ONLY
DATA_FAR IBCC_FAR
0x030800 TO
0x030BFF;
EEPROM_04 =
READ_ONLY
DATA_FAR IBCC_FAR
0x040800 TO
0x040BFF;
EEPROM_05 =
READ_ONLY
DATA_FAR IBCC_FAR
0x050800 TO
0x050BFF;
EEPROM_06 =
READ_ONLY
DATA_FAR IBCC_FAR
0x060800 TO
0x060BFF;
EEPROM_07 =
READ_ONLY
DATA_FAR IBCC_FAR
0x070800
TO
0x070BFF;
PAGE_F8 =
READ_ONLY
DATA_FAR
IBCC_FAR
0xF88000 TO
0xF8BFFF;
PAGE_F9 =
READ_ONLY
DATA_FAR IBCC_FAR
0xF98000 TO
0xF9BFFF;
PAGE_FA =
READ_ONLY
DATA_FAR IBCC_FAR
0xFA8000 TO
0xFABFFF;
PAGE_FB =
READ_ONLY
DATA_FAR IBCC_FAR
0xFB8000 TO
0xFBBFFF;
PAGE_FC =
READ_ONLY
DATA_FAR IBCC_FAR
0xFC8000 TO
0xFCBFFF;
PAGE_FE =
READ_ONLY
DATA_FAR IBCC_FAR
0xFE8000 TO
0xFEBFFF;
END
PLACEMENT
_PRESTART,
STARTUP,
ROM_VAR,
STRINGS,
VIRTUAL_TABLE_SEGMENT,
//.ostext,
DEFAULT_ROM,
NON_BANKED,
COPY
INTO
ROM_C000
;
OTHER_ROM
INTO
PAGE_FE, PAGE_FC, PAGE_FB, PAGE_FA,PAGE_F9, PAGE_F8;
//.stackstart,
SSTACK,
//.stackend,
PAGED_RAM,
DEFAULT_RAM
INTO
RAM;
DISTRIBUTE
DISTRIBUTE_INTO
ROM_4000, PAGE_FE, PAGE_FC, PAGE_FB,PAGE_FA, PAGE_F9, PAGE_F8;
CONST_DISTRIBUTE
DISTRIBUTE_INTO
ROM_4000, PAGE_FE, PAGE_FC, PAGE_FB,PAGE_FA, PAGE_F9, PAGE_F8;
DATA_DISTRIBUTE
DISTRIBUTE_INTO
RAM;
//.vectors INTO OSVECTORS;
END
ENTRIES
//_vectab OsBuildNumber_OsOrtiStackStart _OsOrtiStart
END
STACKSIZE
0x100
VECTOR 0 _Startup
//VECTOR 0 Entry
//INIT Entry
1 .prm
文件组成结构
按所含的信息的不同.prm文件有六个组成部分构成,这里仅讨论和内存空间映射关系紧密的三个部分,其他的不做讨论。
·
SEGMENTS
…
END
定义和划分芯片所有可用的内存资源,包括程序空间和数据空间。一般我们将程序空间定义成ROM,把数据空间定义成RAM,但这些名字都不是系统保留的关键词,可以由用户随意修改。用户也可以把内存空间按地址和属性随意分割成大小不同的块,每块可以自由命名。例如同样是RAM,可以使用不同的属性,使其有复位后变量清零和不清零之分。
关于内存划分的具体方法在后面详解。
·
PLACEMENT
…
END
将指派源程序中所定义的各种段,如数据段DATA_SEG、CONST_SEG和代码段CODE_SEG
被具体放置到哪一个内存块中。它是将源程序中的定义描述和实际物理内存挂钩的桥梁。
·
STACKSIZE
定义系统堆栈长度,其后给出的长度字节数可以根据实际应用需要进行修改。堆栈的实际定位取决于RAM内存的划分和使用情况。默认的情况下,堆栈放在RAM区域的起始部分。当然,堆栈的定义不只有这种方式,还可以使用STACKTOP关键字。后面将详细讨论。
2
内存划分的具体方式
由SEGMENTS开始到END为止,中间可以添加任意多行内存划分的定义,每一行用分号结尾。定义行的语法型式为:
[块名]
= [属性1] [属性2]
,…
,[属性n]
[起始地址]
TO
[结束地址];
其中,
·
“块名”的定义和C语言变量定义相同,是以英文字母开头的一个字符串,用户可以自己任意定义块名。
·
“属性”用户是不能自己定义的,因为属性名指定了上面所说的“块名”所对应的不同的内存类型和访问方式,而不同物理内存的类型和访问方式是一定的。
对于“属性1”,Codewarrior 5.0中可以有三种不同的类型,对于只读的Flash-ROM区属性一定是READ_ONLY,对于可读写的RAM区属性可以是READ_WRITE,也可以是NO_INIT。它们两者的关键区别是ANSI-C的初始化代码会把定位在READ_WRITE块中的所有全局和静态变量自动清零,而NO_INIT块中的变量将不会被自动清零。当然只是复位时不清零,掉电时还是清零的,但是对于单片机系统,变量在复位时不被自动清零这一特性有时是很关键的,在某些应用中有特殊的用途。
对于“属性2…属性n”,根据上面给出的.prm的范例文件可以看出来,可能的形式有“DATA_FAR”、“DATA_NEAR”、“IBCC_FAR”、“IBCC_NEAR”四种类型。其中,“DATA_FAR”和“DATA_NEAR”相对应,当内存区域包含变量或者是常量时(通常是RAM、Flash和EEPROM),必须指明上面两种属性中的一种,由于涉及到内存的分页,可以这样理解:“DATA_FAR”属性指定的内存块为可以保存数据的非固定页,而“DATA_NEAR”属性指定的内存块为可以保存数据的固定页;同理“IBCC_FAR”和“IBCC_NEAR”相对应,当内存区域包含代码时(Flash和EEPROM),必须指明上面两种属性中的一种,“IBCC_FAR”属性指定的内存块为可以保存代码的非固定页,而“IBCC_NEAR”属性指定的内存块为可以保存代码的固定页
讨论到这里,细心的读者已经发现,在上面的.prm文件范例中,RAM的属性有“DATA_FAR”和“DATA_NEAR”两种,Flash的属性中也是四种都有,但是EEPROM中却只有“DATA_FAR”和“IBCC_FAR”两种,这正好验证了上一篇文章(飞思卡尔16位单片机的资源配置)中所提到的,RAM、Flash中都有固定页,但是EEPROM中全部是非固定页。
·
起始地址和结束地址决定了一内存块的物理位置,对于固定页,用4位16进制数表示,而对于非固定页,则用6位16进制表示,多出来的两位其实是寄存器EPAGE、RPAGE或PPAGE的值,可见,对于分页的资源,是通过寄存器(EPAGE、RPAGE或PPAGE)和16位的地址总线的组合来进行寻址的。
“TO”是系统保留的关键字,必须大写。
下面,根据上面范例提供的内容,举几个例子:
例1RAM
=
READ_WRITE
DATA_NEAR 0x2000
TO
0x3FFF;
上面这句话的意思是:分配0x2000-0x3FFF的区域的块名为“RAM”(当然可以定义别的名称),由上一篇文章而知,这一区域的物理内存的性质为RAM,属性应该为“READ_WRITE”,并且这一区域中的两页都为固定页,所以为“DATA_NEAR”。
例2将8K字节RAM的后面4K字节定义成非自动清零的数据保留区,则应如下定义:
SEGMENTS
……
RAM =
READ_WRITE
DATA_NEAR 0x2000
TO
0x2FFF;
RAM_NO_INIT =
NO_INIT
DATA_NEAR 0x3000
TO
0x3FFF;
……
END
注意,各部分RAM的分配地址不应该存在重叠的部分,否则会发生错误。
例3EEPROM_00
=
READ_ONLY
DATA_FAR
IBCC_FAR
0x000800
TO
0x000BFF;
XS128单片机中的EEPROM由Data-Flash模拟,所以属性为READ_ONLY。EEPROM全部为非固定页,所以用“DATA_FAR”、“IBCC_FAR”。后面的起始地址和结束地址分别为6位的16进制数,前两位的“00”实质指的是EEPROM分页寄存器EPAGE的值为0x00。
用SEGMENTS只是从单片机的物理内存这一角度对其进行空间划分。源程序本身并不知道物理内存被分割和属性定义的这些细节。它们两者之间必须通过下面的PLACEMENT建立联系。
3
程序段和数据段的放置
PLACEMENT-END内所描述的信息是告诉连接器源程序中所定义的各类段应该被具体放置到哪一个内存块中去。其语法型式为: