1前言
Keil是业界最好的51单片机开发工具之一,它拥有流畅的用户界面与强大的仿真功能。ARM将Keil公司收购之后,正式推出了针对ARM微控制器的开发工具RVMDK,它将ARM编译器RVCT与Keil的工程管理、调试仿真工具集成在一起,是一款非常强大的ARM微控制器开发工具。2007年5月,ARM正式授权中国深圳英蓓特公司代理中文版RVMDK的出售事务。
S3C2410目前已经在国内外市场得到了普遍的应用,因此移植S3C2410的MDK例程,对于推广MDK具有比较重要的意义。其实,对于MDK例程的移植,无论是从EmbestIDE下,还是从ADS下,其过程都是相似的。
S3C2410基于ARM920T内核,16KB指令Cache,16KB数据Cache,支持MMU,NandFlashBootLoader,内部嵌有4KBRAM,即启动石,亦可在系统启动完成后,做为他用。同时S3CEV2410开发板还集成了32M*2NorFLash,64MSDRAM。因此在移植的时候,可以将程序分别运行在SDRAM和NorFlash中。
2相关技术说明2.1启动代码
MDK一个强大的功能就是能够自动生成启动代码,而且可以进行图形化的代码设置,这就可以极大地方便了工程师,减少了百余行的汇编代码的编写。虽然一些老工程师习惯而且更喜欢文本编辑的方式,但是不可否认,MDK提供的启动代码生成向导,对于加快项目开发进度,提高工作效率,帮助新人迅速进入开发工作具有十分重要的意义。
同样,MDK支持S3C2410启动代码自动生成。1)在MDK中新建工程
打开RVMDK,在主菜单中选择Project�New…�uVisionProject,并给新工程命名为New_MDK.uv2,单击“保存”,见图2-1。
图2-1在RVMDK中新建工程
在RVMDK自动弹出的器件选择窗口(SelectDeviceforTarget)中选择该工程所对应的处理器型号,“S3C2410”,并单击“确定”,见图2-2。当RVMDK提示用户是否自动添加启动代码时,选择“是”。
图2-2启动代码自动生成向导
双击打开S3C2410A.S文件,在MDK的文本编辑区中,显示S3C2410的默认配置的启动代码。点击ConfigurationWizard,就可以通过图形化的形式对
生成的默认启动代码进行个性化的配置,包括:堆栈,时钟管理,中断向量表,看门狗时钟,存储器控制,I/O配置等。如图2-3所示:
图2-3启动代码图形化配置界面
这样,通过以上的步骤,我们就可以快速方便地完成启动代码的生成配置了。
2.2分散加载文件
关于散加载文件的具体内容介绍,可参考附录1“RealviewMDK中链接脚本详细解析”,这里只针对S3C2410以及开发板的特点,给出具体的代码参考。
之前提到的S3C2410及其开发板的一些基本参数,这里我们要关心的是SDRAM和NorFlash的编址问题。通过阅读S3C2410用户指南可知,地址分布如下:
0x00000000~~0x01000000:32MNorFlash0x80000000~~0x81000000:32MNorFlash0x30000000~~0x02000000:64MSDRAM
因此,针对不同的程序运行地址,就有不同的分散加载文件:1)程序运行在NorFlash中(RuninFlash.sct):
;*************************************************************;***Scatter-LoadingDescriptionFilegeneratedbyuVision***;*************************************************************;RuninFlash
LR_ROM10x00000000
*.o(RESET,+First)*(InRoot$$Sections).ANY(+RO)}
{;loadregion
{;loadaddress=executionaddress
ER_ROM10x000000000x0200000
RW_RAM10x300000000x4000000.ANY(+RW+ZI)}
{;RWdata
RW_IRAM10x400000000x00001000.ANY(+RW+ZI)}}
{
2)程序运行在SDRAM中(RuninRAM.sct):
;*************************************************************;***Scatter-LoadingDescriptionFilegeneratedbyuVision***;*************************************************************;RuninRAMLR_ROM10x30000000
*.o(RESET,+First)*(InRoot$$Sections).ANY(+RO)}
RW_RAM10x302000000x3E00000.ANY(+RW+ZI)}
RW_IRAM10x400000000x00001000.ANY(+RW+ZI)}}
{
{;RWdata
{
;loadregion
;loadaddress=executionaddress
ER_ROM10x300000000x02000000{
2.3调试脚本
关于调试脚本的更多原理介绍,请参考附录2“RealviewMDK中调试脚本的详细解析”。
在S3C2410的MDK移植过程中,调试脚本(SDRAM.INI)主要的内容是进行SDRAM的配置和初始化运行指针。
/*******************************************************************//*Ext_RAM.INI:ExternalRAM(SDRAM)InitializationFile*/
/*******************************************************************///<< // /*******************************************************************/ /*ThisfileispartoftheuVision/ARMdevelopmenttools.*/ /*Copyright(c)2005-2006KeilSoftware.Allrightsreserved.*/ /*Thissoftwaremayonlybeusedunderthetermsofavalid,current,*/ /*enduserlicencefromKEILforacompatibleversionofKEILsoftware*/ /*developmenttools.Nothingelsegivesyoutherighttousethissoftware. */ /********************************************************************/ FUNCvoidSetup(void){ _WDWORD(0x53000000,0x00000000);_WDWORD(0x4A000008,0xFFFFFFFF);_WDWORD(0x4A00001C,0x000007FF);_WDWORD(0x4C000014,0x00000003);_WDWORD(0x4C000004,0x0005c042);_WDWORD(0x56000070,0x00280000);_WDWORD(0x56000078,0x00000000);_WDWORD(0x48000000,0x22111110);_WDWORD(0x48000004,0x00000700);_WDWORD(0x48000008,0x00000700);_WDWORD(0x4800000C,0x00000700);_WDWORD(0x48000010,0x00000700);_WDWORD(0x48000014,0x00000700);_WDWORD(0x48000018,0x00000700);_WDWORD(0x4800001c,0x00018005);_WDWORD(0x48000020,0x00000700);_WDWORD(0x48000024,0x008e0459);_WDWORD(0x48000028,0x000000B2);_WDWORD(0x4800002c,0x00000030);_WDWORD(0x48000030,0x00000030);_WDWORD(0x56000014,0x00000001);_WDWORD(0x56000020,0xAAAA55AA);_WDWORD(0x56000028,0x0000FFFF);_WDWORD(0x56000024,0x00000000);} Setup(); //SetupforInit LOADSDRAM\\Button_Test.axfINCREMENTALPC=0x30000000;g,main //Download // //Runtomainfunction 具体的寄存器地址以及初始化参数请查阅S3C2410用户指南。 2.4NorFlash烧写算法 MDK支持多种NorFlash芯片的烧写,故在移植的过程中,只需要选择对应的NorFlash烧写算法即可。 在MDK工具栏中点击 或是Project�OptionsforTarget“XXX”,选择 Uilities选项卡,如图2-4所示: 图2-4Flash烧写配置界面 点击Settings按钮,添加Flash烧写算法。已知S3C2410开发板用到的烧写算法为AM29F160DBFlash,因此,只需在点击Add按钮后,在里面找到对应的算法即可。添加算法后,界面如图2-5所示: 图2-5Flash算法配置界面 2.5MMU S3C2410支持MMU,具体的内容可见附录3“RealViewMDK中如何对MMU进行操作”。 当程序运行在SDRAM中时,需要运行MMU,以便能够找到正确的异常入口。具体的函数实现过程如下: /*****************************************************************name:*func:*para:*ret:*modify:*comment: ****************************************************************/voidEnableMMU(){ unsignedintctl; ctl=ARM_ReadControl();ctl|=(1<<0);ARM_WriteControl(ctl);} /************************************************************name:*func:*para:*ret: InitMMU InitializationtheMMU pTranslationTable-TranslationTableAddressnoneEnableMMUEnabletheMMUnonenone *modify:*comment: *****************************************************************/voidInitMMU(unsignedint*pTranslationTable){ inti; //ProgramtheTTB ARM_WriteTTB((unsignedint)pTranslationTable);//Programthedomainaccessregister ARM_WriteDomain(0xC0000000);//domain15:accessarenotchecked//Resettableentriesfor(i=0;i<0x200;++i) pTranslationTable[i]=0;//Programlevel1pagetableentrypTranslationTable[0x0]= (0x300<<20)|(1<<10)|(15<<5)|1<<4|0x2; pTranslationTable[0x1]= (0x301<<20)|(1<<10)|(15<<5)|1<<4|0x2; pTranslationTable[0x2]= (0x302<<20)|(1<<10)|(15<<5)|1<<4|0x2; pTranslationTable[0x3]= (0x303<<20)|(1<<10)|(15<<5)|1<<4|0x2; for(i=0x200;i<0xFFF;++i) pTranslationTable[i]= (i<<20)|(1<<10)|(15<<5)|1<<4|0x2; //Setas1Mbytesection //PhysicalAddress //Accessinsupervisormode//Domain //Setas1Mbytesection //PhysicalAddress //Accessinsupervisormode//Domain //Setas1Mbytesection //PhysicalAddress //Accessinsupervisormode//Domain //Setas1Mbytesection //PhysicalAddress //Accessinsupervisormode//Domain //Setas1Mbytesection //PhysicalAddress //Accessinsupervisormode//Domain EnableMMU();} //EnabletheMMU /*****************************************************************name:*func:*para:*ret:*modify:*comment: *************************************************************/__inlinevoidARM_WriteTTB(unsignedintttb){ __asm(\"MCRp15,0,(ttb&0xFFFFC000),c2,c0,0\");} /**************************************************************name:*func:*para:*ret:*modify:*comment: *******************************************************************/__inlinevoidARM_WriteDomain(unsignedintdomain){ __asm(\"MCRp15,0,domain,c3,c0,0\");} ARM_WriteDomain WritedomainaccesscontrolDomainNO.none ARM_WriteTTB WriteTranslationtablebaseTTBAddressnone register 因为对于MMU的控制必须在管理态下进行,故应该对启动代码进行相应的修改。其中粗体部分为添加的内容。 ; EnterSupervisorModeandsetitsStackPointer MSRMOVSUB CPSR_c,#Mode_SVC:OR:I_Bit:OR:F_BitSP,R0 R0,R0,#SVC_Stack_Size ;EnableMMUMapAddress0x00to0x300000000,Soifhavenonorflash theinterruptcanalsowork! IFIMPORTSTMFDLDRBLLDMFDENDIF ; EnterUserModeandsetitsStackPointer MSRMOV CPSR_c,#Mode_USRSP,R0 :DEF:ENABLEMMUInitMMUSP!,{R0}R0,=TTB_ADDRInitMMUSP!,{R0} SUBSL,SP,#USR_Stack_Size 2.6工程管理 在S3C2410的MDK例程移植中,需要建立两个目标工程:RuninRAM和RuninFlash。具体的工程管理方法,请参考附4“RealViewMDK中如何方便实现同一程序在不同地址运行的工程管理”。 移植步骤 下面就介绍下如何从其他的开发工具上移植例程到MDK上。 3.1RuninSDRAM 1)新建一个文件夹,用于保存例程的所有文件,将例程中与开发工具无关的源文件拷到该目录下。 2)参照2.1启动代码一节,利用MDK自动生成启动代码,并配置好所需要的参数。同时,依据2.5MMU一节,对启动代码进行修改。 3)根据2.7工程管理,建立工程目录,同时添加相应的源文件。如图3-1所示: 图3-1工程结构目录 4)单击工程属性快捷键,打开工程属性设置窗口,并选择C/C++标签 页,设置编译器属性。选择优化等级,以及优化选项,同时添加包含头文件的目录。如图3-2所示: 图3-2编译器属性配置界面 5)选择Asm标签页进行汇编器属性配置。因为程序运行在SDRAM中时,需要MMU,故需要在Define中预定义:ENABLEMMU。该标号用来作为启动代码中的“IF:DEF:ENABLEMMU”的判断条件。 图3-3汇编器属性配置界面 6)选择Linker标签页进行链接器配置。在链接器的属性配置中,主要添加 分散加载文件,即添加RuninRAM.sct路径,如图3-4所示。 图3-4链接器属性配置界面 7)选择Debug标签页进行调试属性配置。包括添加调试脚本(SDRAM.ini),设置调试方式等。如图3-5所示: 图3-5调试属性配置界面 8)Build工程并适当修改代码。当所有的工程属性都设置好之后,单击“Buildalltargetfile”快捷键 ,对整个工程进行编译链接。对出现的错误或警告进行 相应的修改,重新编译工程。 9)点击Debug调试快捷键 ,将生成的.axf文件下载到SDRAM中进行 在线调试。 10)配置超级终端。在PC机上运行windows自带的超级终端串口通信程序,,设置超级终端:波特率115200、1位停止位、无校验位、无硬件流控制,或者使用其它串口通信程序。(注:超级终端串口的选择根据用户的PC串口硬件不同,请自行选择,如果PC机只有一个串口,一般是COM1)。 11)单击 ,顺利执行程序;或执行单步运行,观察开发板上指示灯和超 级终端输出信息。 12)编写Readme文件,以方便用户进行程序实验。 3.2RuninFlash 1)配置完RuninRAM工程属性后,通过程序运行在Flash中,单击工程属性快捷键RuninFlash进行工程配置。 选择 ,打开工程属性设置窗口,对与 2)选择C/C++标签页,设置编译器属性。选择优化等级,以及优化选项,同时添加包含头文件的目录。如图3-6所示: 图3-6编译器属性配置界面 3)选择Asm标签页进行汇编器属性配置。注意,与RuninRAM不同,当程序运行在NorFlash中时是不需要MMU的,故不需要预定义“ENABLEMMU” 标号。 图3-7汇编器属性配置界面 4)选择Linker标签页进行链接器配置。在链接器的属性配置中,主要添加分散加载文件,即添加RuninFlash.sct路径,如图3-8所示。 图3-8链接器属性配置界面 5)选择Debug标签页进行调试属性配置。包括添加调试脚本(Flash.ini),设置调试方式等。其实Flash.ini和SDRAM.ini类似,只是在.axf引用的位置和最后PC指向的位置不同。在Flash.ini中,PC=0x00000000。这样程序就可以在Flash中进行调试了,需要说明的是在Flash中调试的时候,只能设置2个断 点。如图3-9所示: 图3-9调试属性配置界面 6)参照2.4NorFlash烧写算法一节,添加NorFlash烧写算法。同时在Uilities标签页中选上“UpdateTargetbeforeDebugging”。并添加“InitFile”,Flash.ini。如图3-10所示: 图3-10烧写算法属性配置界面 7)Build工程并适当修改代码。当所有的工程属性都设置好之后,单击“Buildalltargetfile”快捷键 ,对整个工程进行编译链接。对出现的错误或警告进行 相应的修改,重新编译工程。 8)点击Debug调试快捷键线调试。或直接点击 ,将生成的.axf文件下载到Flash中进行在 ,将.asf文件下到NorFlash中。 9)配置超级终端。在PC机上运行windows自带的超级终端串口通信程序,,设置超级终端:波特率115200、1位停止位、无校验位、无硬件流控制,或者使用其它串口通信程序。(注:超级终端串口的选择根据用户的PC串口硬件不同,请自行选择,如果PC机只有一个串口,一般是COM1)。 10)在Flash中调试时,单击 ,顺利执行程序;或执行单步运行,观察 开发板上指示灯和超级终端输出信息。重启开发板,观察开发板上指示灯和超级终端输出信息。 11)编写Readme文件,以方便用户进行程序实验。 4注意事项 1)在移植开始前,请仔细阅读S3C2410用户指南,尤其是memorycontroller,clockpowermanagement,I/Oports和Interruptcontroller这几章。 2)仔细阅读开发板的使用说明,主要关注点在于SDRAM,NorFlash的大小,跳线的设置,LED灯采用的I/O端口等。 3)在编写链接脚本文件的时候,尤其是RuninSDRAM,要注意RW区和RO区不要有交叉。 4)注意.s和.c文件中定义的标号,如果需要的话,应该在编译器和汇编器的属性配置中进行预定义。 5)移植的时候,如果超级终端不能显示,检查软件无误后,可考虑是串口线的问题(交叉或是直连)。 6)注意最后生成文件的大小和实际分配给各个区段的大小。 7)MDK有时候会出现未知原因的不稳定,确定程序无误后,可以考虑多试几次。 附录附1 RealviewMDK中链接脚本详细解析 使用RealviewMDK时不可避免的要涉及到链接脚本文件,特别是编译链接那些大的工程文件时更是如此。在链接脚本中可以指定代码的存储布局,可以将代码段、只读数据段、可读写的数据段分别存放,甚至可以精确地指定代码放置的位置,这一点是关键的,譬如说启动代码就必须放在可知型文件的开始位置。由于链接脚本重要性,开发者必须掌握其编写的方法。 RealviewMDK链接程序使用了两种方式控制程序的链接,即链接控制命令选项和链接脚本文件。当使用链接控制命令选项时,链接器定义了Image$$RW$$Base 、 Image$$RW$$Limit 、 Image$$RO$$Base 、 Image$$RO$$Limit、Image$$ZI$$Base和Image$$ZI$$Limit等6个段地址描述符。这6个描述符可以直接在程序中引用。而在使用链接脚本文件后,这6个描述符号没有了,取而代之的是链接脚本文件中的段描述符,格式为:Image$$段名$$Base和Image$$段名$$Limit。下面将结合3个具体的例子说明链接脚本文件的使用。 例1一个加载区域,多个连续的执行区域。 LR_10x040000{ ER_RO+0 为0x040000. { *(+RO)} ER_RW+0 ER_RO的容量。 { *(+RW)} ER_ZI+0{ *(+ZI)}} ;所有清零数据都连续地放在这个区域。 ;清零数据段ER_ZI紧接ER_RW段的尾地址存放。;所有的可读写的程序都连续地放在这个区域。 ;可读写数据段ER_RW紧接ER_RO段的尾地址存放,即0x040000+ ;所有的只读代码段都连续地放在这个区域。 ;执行区域ER_RO的起始地址紧接载于区域LR_1的起始地址,即;定义载入区域LR_1的起始地址为0x040000。 例2一个加载区域,多个非连续的执行区域。 LR_10x010000{ ER_RO+0 0x010000. { *(+RO)} ;定义载入区域LR_1的起始地址为0x010000。 ;执行区域ER_RO的起始地址紧接载于区域LR_1的起始地址,即为 ;所有的只读代码段都连续地放在这个区域。 ER_RW0x040000;定义可读写数据段ER_RW的起始地址为0x040000.{ *(+RW)} ER_ZI+0;清零数据段ER_ZI紧接ER_RW段的尾地址存放,即为0x040000+ER_RW的 容量。 { *(+ZI)}} ;所有清零数据都连续地放在这个区域。;所有的可读写的程序都连续地放在这个区域。 例3二个加载区域,多个非连续的执行区域。 LR_10x010000{ ER_RO+0{ *(+RO)}} LR_20x040000{ ER_RW+0{ *(+RW)} ER_ZI+0{ *(+ZI)}} ;所有清零段ZI的数据都连续的放在这里。 ;清零段ER_Z的起始地址为0x040000+ER_RW段的容量。;所有可读写的数据段都放在这里。;ER_RO段的起始地址为0x010000.;载入区域LR_2的起始地址为0x040000。;ER_RO段的起始地址为0x010000.;载入区域LR_1的起始地址为0x010000。 上面三个例子中,载入区域和执行区域的名字是可以任意命名的,对这些段地址的引用可以使用如Image$$LR_1$$Base、Image$$LR_1$$Limit、Image$$ER_RW$$Base和Image$$ER_RW$$Limit等。 附2RealviewMDK中调试脚本的详细解析 和其他集成开发环境一样,RealviewMDK中也使用了调试脚本。调试脚本除了可以初始化软硬件的调试环境以外,还可以初始化Flash的烧写环境,甚至可以提供信号函数模拟片上外围设备。所以在使用RealviewMDK调试和烧写的过程中,到处都有调试脚本的身影。下面将分三个方面详细地研究调试脚本的编写和使用。在介绍调试脚本之前,先必须了解RealviewMDK预定义的一些常用命令和函数的用法。 1.常用预定义命令的说明及注意事项: 下表是一些常用的预定义命令和函数的语法格式及说明。预定义的命令语法MAP开始地址,结束地址Go开始地址,结束地址描述在内存中映射一段存储区域。程序从开始地址运行,到结束地址停止。显示所有的CUP引脚寄存器。描述信号函数被延时,直到函数参数地址被读取。信号函数被延时,直到函数参数地址被写入。信号函数延时参数指定的时间,单位为S。信号函数延时参数指定的CUP周期。在参数指定的地址处读取1个字节的数据。在参数指定的地址处读取1个整型的数据。在参数指定的地址处读取1个长整型的数据。向参数指定的地址处写入1个字节的数据。向参数指定的地址处写入1个整型的数据。向参数指定的地址处写入1个长整型的数据。DIRVTREG预定义的函数voidrwatch(ulongaddress)voidwwatch(ulongaddress)voidswatch(floatseconds)voidtwatch(ulongstates)uchar_RBYTE(address)ulong_RWORD(address)ulong_RDWORD(address)uchar_WBYTE(address,ucharval)void_WWORD(address,uintval)void_WDWORD(address,ulongval)2.调试脚本在硬件仿真中的应用 RealviewMDK编译链接好的程序在硬件上运行之前,要求硬件具有合适的环境(例如,时钟的配置、存储控制的配置等),一般这些工作是由启动代码完成的。在RealviewMDK中,通过调试脚本使用MDK预先定义好的寄存器读写命名设置硬件环境。这一工作在硬件调试之前是必须进行的。下面是一个初始化硬件环境的调试脚本函数: FUNCvoidSetup(void){ _WWORD(0xfffffd44,0x00008000); //配置看门狗模式寄存器; _WWORD(0xfffffd60,0x00320100);_WWORD(0xfffffc20,0x00000601);_WWORD(0xfffffc2c,0x00191C05);_WWORD(0xfffffc30,0x00000007);_WWORD(0xfffffd08,0xa5000001);pc=0x200000;} //配置电压效验模式寄存器;//配置主晶振寄存器;//配置锁相环寄存器;//配置主时钟寄存器;//配置复位控制模式寄存器;//设置PC的值。 3.调试脚本在软件仿真调试中的应用 使用RealviewMDK软件模拟器调试程序时,除了像硬件调试那样配置相关的寄存器以外,有时还必须使用信号函数模拟外设信号的输入/输出,甚至完全模拟一个外围设备。下面的程序将模拟一个外围设备向ADC接口输入方波信号: signalvoidADC4_Square(void)floatvolts;floatfrequency;floatoffset;floatduration;voltsoffset =1.6;=0.5; { //峰值的电压; //输出的始终频率,单位HZ;//偏移电压; //持续的时间,单位S; frequency=2400;duration=1000;while(duration>0.0)AD4=volts+offset;swatch(0.5/frequency);AD4=offset; swatch(0.5/frequency);duration-=1.0/frequency;}} { 4.调试脚本在Flash下载中的应用: 使用RealviewMDK进行Flash下载时,目标板的硬件环境也需要配置,其配置方法和硬件调试的情况是一样的,在此不再赘述。 附3RealViewMDK中如何对MMU进行操作 EmbestATEB9200开发板采用AT91RM9200,其ARM核为ARM920T,ARM920T代表着什么呢?可以这么理解ARM920T=ARM9core+MMU+Cache,也就是说ARM920T为实现虚拟内存管理提供了硬件条件,这个硬件条件就是MMU--内存管理单元。不少操作系统需要MMU单元,这为跑操作系统提供了硬件支持,当然有些操作系统不需要MMU,如uClinux等,如果在ARM920T上跑此类操作系统需要禁用MMU。大家可能对S3C2410非常熟悉,它采用的核即为ARM920T。这篇文章介绍MDK下的一个操作MMU的实例。该实例基于EmbestATEB9200开发板。当然对于以ARM920T为核的其它开发板一样有借鉴作用。 ARM920T有四种内存映射模式:Fault、CoarsePage、Section、FinePage。为了简单起见例程里我们用Section模式。ARM920T是32位的CPU,其虚拟地址空间为2^32即4G。我们用Section模式来划分这4G址址空间,每一个Section大小为1M,这样就可得到4K(4096)个Section。怎样管理这些Section呢?通过一张表来记录它们,而这张表被称做页表。在页表里,用4个字节(32位)来记录一个Section的信息。总共有4K个Section,这样就要花费4x4K=16K的内存。这用来描述Section的4个字节也有个形象的名字,叫作描述符。描述符的结构又是什么样的呢。来看一下: 图一描述符结构 Sectionbaseaddress:段基地址。 AP:AccessPermission访问控制位。 Domain:访问控制器的索引,ARM920T支持16个Domains。C:被置位时为write-through(WT)模式。B:被置位时为write-back(WB)模式。 EmbestATEB9200的SDRAM为32M,其物理地址范围是 0x20000000~0x202000000,可划分成32个Section。我们要实现虚址到物理地址的映射,虚地址是如何被转换的呢?其实MMU将虚地址分成两部分:表索引(Tableindex)和段索引(Sectionindex)。Tableindex就是虚地址的高12位,Sectionindex就是虚地址的低20位,如图二。MMU通过Tableindex在页表里取到相应描述符,从描述符里取到对应Section的基地址,再由这个基地址加上偏移Sectionindex来找出真正的物理地址。 图二虚地址结构 下图说明了如何从虚拟地址到物理地址的转换。 图三虚拟地址到物理地址的转换过程 有了以上的基础,我们可以来分析一下例程:先看main函数: intmain(){ void(*pDhrystone)(void)=(void(*)())((unsignedint) AT91F_Dhrystone-0x10000000); //将函数AT91F_Dhrystone的地址减去0x10000000 AT91F_LowLevelInit(); //初始化系统 AT91F_DBGU_Printk(\"-I-Enterinmain()section\\n\\r\");AT91F_DBGU_Printk(\"-I-WOMMU&I+DCachesDisabled\\n\\r\");AT91F_DisableICache();//禁用指令CacheAT91F_DisableDCache();//禁用数据CacheAT91F_Dhrystone(); //测试速度 AT91F_DBGU_Printk(\"\\n\\r-I-WOMMU&ICacheEnabled\\n\\r\");AT91F_EnableICache();AT91F_Dhrystone(); //使能指令Cache//测试速度 AT91F_InitMMU((unsignedint*)0x20010000); //初始化MMU单元并使能MMU,后面有该函数的分析 AT91F_DBGU_Printk(\"\\n\\r-I-MMU&ICacheEnabled\\n\\r\");AT91F_EnableICache();pDhrystone();函数 //AT91F_Dhrystone的虚拟地址,但是转换后的物理地址 是一样的 //这是由于虚拟地址0x10000000映射到物理地址 0x20000000,虚拟 //地址0x20000000映射到物理地址0x20000000。可查看函数 //AT91F_InitMMU的分析。也就是说pDhrystone和AT91F_Dhrystone//共用同一段物理空间,执行同一段程序。以下pDhrystone函数一样。 AT91F_DBGU_Printk(\"\\n\\r-I-MMU&DCacheEnabled\\n\\r\");AT91F_EnableDCache();pDhrystone();while(1);} //使能数据Cache//测试速度//使能数据Cache //测试速度,实际上函数pDhrystone的虚拟地址并不等于 下面分析函数AT91F_InitMMU voidAT91F_InitMMU(unsignedint*pTranslationTable){ inti; AT91F_ARM_WriteTTB((unsignedint)pTranslationTable); //将TranslationTableBase写入到CP15寄存2 中 AT91F_ARM_WriteDomain(0xC0000000);//设置domain15:访问时不检查权限 for(i=0;i<4096;++i)//初始化页表 pTranslationTable[i]=0; //以下分别设置页表项 pTranslationTable[0x0]= (0x0<<20)|(1<<10)|(15<<5)|1<<4|0x2; (0x200<<20)|(1<<10)|(15<<5)|1<<4|(1<<3)|0x2; (0x200<<20)|(1<<10)|(15<<5)|1<<4|0x2; (0xFFF<<20)|(1<<10)|(15<<5)|1<<4|0x2; AT91F_EnableMMU(); 1来启动 //MMU,例如以下语句即可启动MMU: //unsignedintctl=0x01; //__asm(\"MCRp15,0,ctl,c1,c0,0\");} //设置为Section映射模式 //使能MMU,通过协处理器CP15寄存器1的第0位置//设置为Section映射模式 //物理段地址//访问权限为管理模式//Domain15 pTranslationTable[0xFFF]=pTranslationTable[0x200]= //物理段地址//访问权限为管理模式//Domain15//Cachable //设置为Section映射模式//设置为Section映射模式 //物理段地址//访问权限为管理模式//Domain15 pTranslationTable[0x100]= //物理段地址//访问权限为管理模式//Domain15 通过以上对MMU的初始化。我们可以算一下虚拟地址0x10000000所映射的物理地址:首先取得虚拟地址的Tableindex(31:20位),在此为0x100,然后根据TTB+(0x100<<2)=0x20010400,得到这个地址上的数据即为pTranslationTable[0x100],其值为0x2000005FA。最后取其物理段地址<<20(0x200<<20),再加上虚拟地址的Sectionindex(19:0位)即得物理地址:0x20000000。 用同样的办法可算出虚拟地址0x20000000对应的物理地址也是0x20000000。因此启用MMU之后,虚拟地址段0x10000000~0x10100000和0x20000000~0x20100000指向同一物理地址空间0x20000000~0x20100000。 这也就是为什么main函数中pDhrystone和AT91F_Dhrystone是同一个函数的原因所在。 另外这里地址0x00000000映射到物理地址0x00000000,这是由于物理地址0x00000000处有中断向量表。 附: 程序运行的结果为: -I-AT91F_LowLevelInit():Debugchannelinitialized-I-Enterinmain()section-I-WOMMU&I+DCachesDisabled-I-5080Dhrystonepersecond-I-5065Dhrystonepersecond-I-5064Dhrystonepersecond-I-5065Dhrystonepersecond -I-WOMMU&ICacheEnabled-I-20717Dhrystonepersecond-I-20660Dhrystonepersecond-I-20660Dhrystonepersecond-I-20660Dhrystonepersecond -I-MMU&ICacheEnabled-I-20717Dhrystonepersecond-I-20660Dhrystonepersecond-I-20660Dhrystonepersecond-I-20660Dhrystonepersecond -I-MMU&DCacheEnabled -I-27509Dhrystonepersecond-I-27433Dhrystonepersecond-I-27432Dhrystonepersecond-I-27433Dhrystonepersecond 附4RealViewMDK中如何方便实现同一程序在不同 地址运行的工程管理 在嵌入式程序的开发过程中,通常需要把程序运行在处理器地址空间的不同位置,比如内部RAM,外部RAM,内部Flash,外部Flash等。 通常以下两种方法: 一是只建立一个工程,比如说运行在内部RAM中,然后通过修改其分散加载文件、调试初始化文件以及一些其他的配置选项来实现几种运行方式的切换。但由于在调试的过程中,可能存在对程序频繁的修改,工程师常常因为忘记修改某个配置选项,而造成了运行的不成功,给调试造成了极大的困难。 二是为每一个运行方式创建一个工程,对于一个运行方式来说,只要修改其程序,而不需要对工程进行重新配置。然而这种方法也有其自身的缺陷,容易造成程序的不一致,几种运行方式不能实现程序的同步更新。 那么,如何更方便有效地解决这个问题呢?其实,MDK提供了这样的一个工程管理的方法,既能对不同运行方式下工程的实现一次配置,又可以保证不同运行方式下程序的一致性。 这里以英蓓特公司推出的AT91EB40X评估板为例。该款评估板支持AT91ER40162、AT91R40807、AT91M40800、AT91R40008处理器。EMBESTATEB40X评估板除了CPU内部的存储器外,还有一片外部Flash和一片64K的EEPROM,另外,还可支持用户外扩SRAM。同时结合MDK的仿真功能,一个同样的例程可以运行在几种不同的运行方式:硬件仿真、片内RAM,片外RAM,片外Flash。 下面介绍下工程管理的方法: 1)MDK->Project->NewuVisionproject,选择保存路径,选择处理器AT91R400082)Project->Manage->Components,Enviroment,Books…,在选择卡ProjectComponents->ProjectTargets中点击 ,并在光标处输入RAMAT91R40008。按此方法,依次 建立FlashAT9140008,ERAMAT91R40008选项。同时将Target1选项更名为SimAT91R40008。 3)在Groups和Files中,为所需的工程文件创建分组,并在各分组中加入相应的文件。 如图1所示。通过点击SetasCurrentTarget,可以设置当前运行的目标工程,如RAMAT91R40008。 图1 4)点击保存,则在MDK的工具栏上就会显示 旁边的下拉箭头,操作结果如图2所示: ,按 图2 5)分别对所建立的每个工程项目进行配置。以RAM,AT91R40008为例,点击工具栏 中的 按钮,对各个选项卡中的选项进行设置。对于运行在不同地址的工程,这 里只需要对Target选项卡下的地址域,Linker选项卡下的分散加载文件以及Debug选项卡下的初始化文件,和Utilities选项卡下的Flash烧写算法等几个位置作修改即可。 6)每次调试运行时,只需要点击 中的下拉箭头,选 择所需选项,就可以方便地实现程序运行在不同的地址空间的切换。 因篇幅问题不能全部显示,请点此查看更多更全内容