原文鏈接: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內所描述的信息是告訴連接器源程序中所定義的各類段應該被具體放置到哪一個內存塊中去。其語法型式為:
[段名1],[段名2],... , [段名n] INTO [內存塊名1],[內存塊名2],… ,[內存塊名n];
和
[段名1],[段名2],... , [段名n] DISTRIBUTE_INTO [內存塊名1],[內存塊名2],… ,[內存塊名n];
其中
· 段名就是在源程序中用“#pragma”聲明的數據段、常數段或代碼段的名字。如果用缺省名“DEFAULT”,則默認的數據段名為DEFAULT_RAM,代碼段和常數段名為DEFAULT_ROM。若程序中定義的段名沒有在PLACEMENT中提及,則將被視同為DEFAULT。幾個相同性質但不同名字的段可以被放置到同一個內存塊中,相互之間用逗號分隔。
· INTO 是系統保留的關鍵詞,在這里為“放入”的意思。
· DISTRIBUTE_INTO 也是系統的保留關鍵字。Codewarrior 具有內存自動優化的功能,但是在“Small memory”模式中,這種功能不會被啟用,只有當16-bit的地址空間不能存放下所有的變量和代碼時,才會啟用這種功能。
在SEGMENTS-END區域中,當在內存模塊的屬性中加上“DATA_FAR”、“DATA_NEAR”、“IBCC_FAR”、“IBCC_NEAR”四種屬性中的任何一種時,那么在PLACEMENT-END區域中,就需要指定段名“DISTRIBUTE”,“CONST_DISTRIBUTE”,“DATA_DISTRIBUTE”(系統默認的,非關鍵字,用戶可以自行更改)所分配的內存空間,這就需要使用“DISTRIBUTE_INTO”關鍵字。
關于內存自動優化功能,可以參考freescale的官方技術手冊“TN262.pdf”。
· 內存塊名就是前面介紹的用SEGMENTS劃分好的不同的內存塊名字。
利用這樣直觀的定位描述文本可以方便靈活的將數據或代碼定位到芯片內存任意可能的位置,實現某些特殊目的的應用。
下面的例子,說明了各種段名、PLACEMENT 和SEGMENTS之間的對應關系。
例4 定義非自動清零的數據段
SEGMENTS
……
RAM = READ_WRITE DATA_NEAR 0x2000 TO 0x2FFF;
RAM_NO_INIT = NO_INIT DATA_NEAR 0x3000 TO 0x3FFF;
……
END
PLACEMENT
……
DATA_PERSISTENT INTO RAM_NO_INIT;
……
END
//源程序編寫:
#pragma DATA_SEGDATA_PERSISTENT //定義復位時非自定清零數據段
byte sysState;
#pragma DATA_SEG DEFAULT
4 堆棧的設置
關于堆棧的設置,Codewarrior提供了兩種方式:“STACKSIZE”命令方式和“STACKTOP”命令方式。這兩種方式在同一個.prm文件中,不能同時存在。當用戶只關心堆棧的大小而不關心堆棧的存放位置時,推薦使用STACKSIZE方式。
系統默認的方式為使用STACKSIZE方式。
STACKSIZE命令方式:
當使用STACKSIZE命令方式時,如果在PLACEMENT-END部分聲明了“SSTACK INTO RAM”,這樣的話,堆棧區就被放在RAM區域的起始部分,下面的例子說明了這種方式:
例5
SEGMENTS
……
RAM = READ_WRITE DATA_NEAR 0x2000 TO 0x3FFF;
……
END
PLACEMENT
……
SSTACK, PAGED_RAM, DEFAULT_RAMINTO RAM;
……
END
STACKSIZE 0x100
上面的例子將堆棧區域存放的地址為0x20FF-0x2000,初始的堆棧指針指向棧頂地址0x20FF。
相反,如果在PLACEMENT-END部分沒有聲明“SSTACK INTO RAM”,則堆棧被分配在RAM區域中已分配空間的后面。請參見例6。
例6
SEGMENTS
……
RAM = READ_WRITE DATA_NEAR 0x2000 TO 0x3FFF;
……
END
PLACEMENT
……
PAGED_RAM, DEFAULT_RAMINTO RAM;
……
END
STACKSIZE 0x100
在這個例子中,如果RAM區域中已經分配的變量占用了4個字節(從0x2000到0x2003),則堆棧放在這四個字節的后面,從0x2103到0x2004,初始的堆棧指針指向0x2103。
STACKYOP命令方式:
當使用STACKTOP命令方式時,如果在PLACEMENT-END部分聲明了“SSTACK INTO RAM”,同樣,堆棧區就被放在RAM區域的起始部分,初始的棧頂則由STACKTOP指定。若沒有相應的聲明,則初始的棧頂由STACKTOP指定,而堆棧的大小則根據處理器的不同由編譯器自行設定,其大小足夠裝下處理器的PC寄存器的值。
評論
查看更多