更全的杂志信息网

无需内存管理单元的微控制器动态程序管理方法

更新时间:2016-07-05

引 言

现代计算机系统中,操作系统使用内存管理单元(MMU)为每个进程提供虚拟地址空间,MMU负责虚拟地址到物理地址的转换。物理地址对应真实内存,所有进程共享物理内存。进程在设计时都假定其是系统中唯一的代码,可以使用任何地址空间。出于对功耗和面积的考虑,嵌入式系统中多数微控制器(MCU)并没有MMU单元,例如基于ARM Cortex-M架构的系列处理器。

使用μCLinux可以在不具有MMU的MCU中动态加载程序。这引出一个问题:为什么不推荐μCLinux?首先,μCLinux对RAM的需求在兆字节级别,这对于许多小型系统来说过于庞大。此外,μCLinux需要一台Linux计算机来编译程序。最重要的一点是,已有的MCU程序要运行于μCLinux下,需要重写代码并更换开发环境。所以考虑到工作量及硬件成本,μCLinux并不是最佳选择。

本文讨论一种无需MMU即可实现系统运行时动态地添加、更新和删除应用程序的方法。本方法在工程实践中取得了良好的效果。

1 硬件平台及软件开发环境

为便于阐述,给定如下硬件平台及软件开发环境。

假设有如下硬件系统:处理器基于Cortex-M内核且没有MMU单元;处理器通过扩展总线与外部SRAM相连;通过SDIO接口与外部SD卡相连;通过SPI总线与外部SPI-Flash相连。硬件平台框架示意图如图1所示。

图1 硬件平台框架示意图

软件开发使用IAR Embedded Workbench for ARM Version:6.30。

2 设计目标

假设有5个可以正常工作于前述硬件平台的独立应用程序,这些程序使用IAR开发,其名称分别为app1~app5。现在,把app1装入SPI-FLASHA,把app2装入SPI-FLASHB,把app3~app5装入SD卡(基于FAT文件系统)。

设计一个程序管理器,可以按需动态装载上述任意应用程序并执行。应用程序执行完毕或用户中断应用程序后,系统控制权可以返回程序管理器,以便后续加载应用程序。

3 程序管理器与程序加载器

程序管理器与程序加载器都可以称为loader,但两者的工作机制有很大不同。一般来说,loader的工作主要是初始化硬件并加载目标程序,例如,Cortex-A系列MPU通过前级loader初始化外部SDRAM并加载uboot,进而由uboot加载Linux。这里的loader和uboot都可以看作程序加载器,两者的共同点也很明显,即加载目标程序后,目标程序即开始运行,控制权交给目标程序。除非系统复位,否则,控制权永远不会返回到loader。

相比而言,程序管理器除了加载目标文件并引导其运行外,还必须具有控制权接管和多次加载目标文件的能力。这一点,有点操作系统任务调度管理的意味。

八要落实地方政府水资源管理责任主体,强化考核评估和监督,加强国情水情宣传,加大部门之间、流域之间、地方之间协调配合力度,形成水资源管理的合力。

从式(13)中可知ΔV由四部分组成,第一部分为用户n自身满意度函数值的改变量,第二部分为用户n放弃an策略后干扰用户集In满意度函数值改变量,第三部分为用户n选择an′策略后干扰用户集In′满意度函数值改变量,第四部分为剩余用户满意度函数值改变量.由于集合V-In-In′-n中用户或处于n的干扰范围之外,或与n使用不同信道,故有

4 设计实现

基于ARM Cortex-M系列的处理器,解决应用程序加载问题的一种常见方法是在编译时将固定地址分配给应用程序。例如,应用程序A加载于地址0x2 1000处,应用程序B加载于地址0x2 2000处。这类处理方法有两个明显的不足:应用程序的扩展规模受限;系统运行时不能动态地添加、更新和删除应用程序。预先分配地址技术无法实现应用程序的动态管理。

4.1 设计思想

针对不具有MMU单元的MCU,动态管理应用程序可以使用以下几种方法:在程序管理器中实现ELF分析器;使用可重定位代码编译,使用程序管理器转化二进制程序的内存位置;使用位置无关代码(PIC)进行编译,并在载入应用程序时调整程序管理器的全局偏移寄存器。

使用ELF语法分析器的缺点是:程序管理器需要比其它方法进行更多的处理,意味着程序管理器将占用大量的程序存储空间。

使用可重定位代码和内存位置转化技术比构建ELF解析器简单,并且性能比位置无关的代码略好。

位置无关代码是一个很好的解决方案,但由于使用全局偏移的间接层而略微降低了性能。由于ARM指令集针对PIC操作进行了优化,大多数代码的运行开销可低至几乎没有额外运行成本。

本文所实现的动态程序管理方法基于位置无关代码(PIC)技术,PIC技术允许将代码放在任何地址。在PIC代码中,所有分支和跳转目标都基于PC相对偏移;所有对数据部分的引用都通过全局偏移寄存器(GOR)进行间接寻址。

本文所设计的程序管理器(loader)使用PIC技术来加载多个应用程序。程序管理器根据SRAM的真实地址在加载应用程序时更新全局偏移寄存器GOR中的基地址。GOR本身是一个寄存器,其值在切换至应用程序代码之前由程序管理器设置。

4.2 设计步骤

应用程序动态管理,需要结合处理器的架构特点并充分利用编译器的多种机制才能实现。针对本文的硬件平台及开发环境,对系统资源做如下划分:loader程序的存储位置及运行位置均位于MCU片内Flash;应用程序存储于外部存储器,运行于外部SRAM;全局栈区及loader程序所使用的内存变量位于MCU片内RAM;应用程序使用的内存变量位于外部SRAM。

__intial_spEQU0x20000400

采用1967 MW保证出力作调峰电站设计:第1期工程增加的装机容量为新建了第3厂房,装机3 900 MW(3台600 MW机组和3台700 MW机组。最后1台24号机组已于1980年5月投入运行)。至此全厂常规机组装机容量为6 180 MW,年发电量为216亿kW·h;1984年完成抽水站2台各50 MW、4台各53.5 MW,合计为314 MW抽水蓄能机组安装工程,两项合计总扩机容量为4 214 MW,全厂总装机容量为6 494 MW。

结合Cortex-M系列MCU的架构特点,配合IAR的相关机制,在应用程序设计中完成以下工作:

(1) 源代码修改

① 用汇编语言编写一个名为ropi_rwpi_header.s的模块,并将其加入应用程序工程文件。该模块主要内容如代码段1所示,用于记录应用程序的关键信息,如ROM起始地址,RO、RW容量,程序入口偏移等。这些信息由汇编文件指导IAR编译器自动产生。

【代码段1】

DATA

ropi_rwpi_header:

DC32ROM_address ; 编译时ROM 地址,定义于链接脚本icf文件中

循环式空气源热泵热水系统按加热方式可以分为2类:(1)连续循环式加热。系统只设置1个保温水箱。加热时,先将保温水箱中的冷水加满,再用水泵将保温水箱中的水送入热泵机组循环加热,直至达到设定温度。

DC32ROPI$$Length; 包含本头模块的RO字节数

DC32RWPI$$Length; 程序RW字节数

DC32main - . - 4; 代码起始至main函数的偏移

END

② 修改应用程序的启动文件。在启动文件头部引入main符号,并将NVIC向量表的第二项,即系统上电入口地址修改为main,如代码段2所示。

③ 解析目标文件的头部信息区,得到目标文件的入口偏移、目标程序大小及全局变量容量等信息;

SECTION.intvec:CODE:ROOT(2)

EXTERNmain

PUBLIC __vector_table

养老服务中心里生活不能自理的老人也有。85岁的关阿姨刚完成了一场手术,无法下地行走,只能依靠轮椅。她的老伴早年已经去世,女儿又在美国工作,身边仅仅只有护工陪伴。一次,女儿回国看到社区里新开设了一家养老中心,考查后不久后便将关阿姨送来与其他老人一同生活。关阿姨告诉记者,“我住在这里很开心,女儿在国外也很放心,每天都有陪护人员帮助我锻炼身体。”

DATA

4.2.1 应用程序设计

__vector_table

DCD __intial_sp

DCDmain

③ 在应用程序main函数中新增三个函数调用语句。即SystemInit、__iar_data_init3和nvic_update。其中,SystemInit是CMSIS内建函数,用于设置处理器时钟系统;__iar_data_init3是IAR内建函数,用于运行时数据初始化;nvic_update需要自行编写,用于更新向量表入口并设置处理器向量表偏移寄存器VTOR。nvic_update依据loader在加载应用程序时传入的程序基地址,将NVIC向量表中的地址增加相应的偏移。需要注意的是,NVIC中的第一个32位数据是编译时的栈顶,该数据无需任何操作,忽略即可。

(2) 编译器设置及链接脚本修改

① 在IAR工程设置C/C++ Compiler选项的Code页面中,依照图2完成设置,指示编译器产生PIC代码。

图2 指示IAR编译器生成PIC代码

② 在Linker选项的Library页面中,依照图3所示将入口符号设置为ropi_rwpi_header。

图3 修改程序入口符号

在将自主开发的针对《C语言程序设计》的游戏助学软件提供给本院的17级新生使用后,学生的对C语言的学习兴趣得到了很大的提高,课程的学习效果也增强了不少。如果通过对课程最终的试卷考核结果进行分析的话,这届新生班级的C语言卷面平均成绩普遍要高于前几届,尤其卷面上的客观题(选择题、填空题、程序阅读题)的平均得分均超过了往届平均分6分以上。这也表明了在这个学期引入游戏助学软件,取得了比较好的教学效果。

将医院的感染控制工作落实到各个环节当中,明确每个人的责任,使其能够充分的了解到自身的责任以及义务。如:根据《医疗卫生机构医疗废物管理办法》相关规定,对护理人员进行医疗废物处置的情况进行管理,要求护理人员必须严格对废物进行严格分类处理,每天都要有的专门的管理人员与护士对医疗废物进行清点封存,然后双方要对记录进行签字,才能够对废弃物进行回收,以防出现遗失以及污染。

图4 修改起始地址

④ 参照代码段3的示例内容修改IAR应用工程的icf链接脚本,强制编译器在目标文件头部加入ropi_rwpi_header模块。

【代码段3】

define exported symbol ROM_address = __ICFEDIT_region_ROM_start__;

define block RO with alignment = 4, fixed order{

ro object ropi_rwpi_header.o,

ro section .intvec,

ro, ro data

};

block HEAP

装修的老气沉沉的前台和装扮的老气沉沉的前台小姐让环境很肃穆。酒店的墙壁上挂满了钟,意淫着酒店经常招待世界各地的客人。在这些钟里,除了北京时间是准确的以外,其他时间都是随性的。这象征了北京永远正确,世界上其他国家乱七八糟。在钟表的中央有一副画,画的内容是青松和流水,老鹰和老虎。

place in ROM_region{

企业在以前的生产经营活动中,依据国家标准和行业习惯,结合企业自身特点形成一些个性术语,在PLM系统规划时将这些与业务和产品相关的各类名词术语、符号内容、计量单位做统一规定,统一管理,制定了《产品型号编制方法》《产品和一级部装参数表》等。

block RO };

define movable block RW with alignment = 8, fixed order, static base{

rw,

199 Application of three-dimensional printing in urology: a recent progress

"ROM":

};

"RAM":

place in RAM_region { block RW };

The development characteristics and prevention measure of geological hazard about loess

原有应用程序工程完成上述步骤后,编译即得到可动态管理的应用程序。从上述步骤可以看出,本方法不需要对已有代码进行任何重写,只需简单的处理即可。

4.2.2 Loader程序设计

基于前述资源划分,结合Cortex-M系列MCU的架构特点,配合IAR的相关机制,需要在loader程序设计中完成以下工作:

(1) 源代码修改

晚饭时候,康芳没敢问楚墨喝不喝酒,楚墨就自己去商店,一会儿回来,一手提一捆“昆嵛”牌啤酒,一手拿一包“将军”牌香烟。“有‘将军’牌的!”楚墨兴冲冲地对康芳说,“刚才您也许问错了牌子。”

① 在loader程序的启动文件中增加外部SRAM初始化函数,完成外部SRAM初始化;

其次,匿名特性保证民意真实。当前,新媒体平台尚未实名化,匿名环境下被管制风险较小,大众意见表达更加自由,态度立场更加鲜明,避免了传统民意调查中可能发生的“作秀”问题。尽管新媒体的匿名性也带来了话语极端化的负面影响,但整体上有利于形成高压舆论态势,提升政府对国民情绪的感知度。

② 加载应用程序时,从SPI-Flash或SD卡读入目标应用程序。例如从SD卡读入目标文件可使用fopen类函数和“rb”参数完成;

【代码段2】

④ 依据头部信息区代码容量分配程序基址。此步骤有两种方法:动态管理和静态管理。动态管理是依据目标程序容量申请堆空间,并将外部存储器中的应用程序读入堆起始地址;静态管理是人为将外部SRAM划分成程序区及数据区。静态管理的好处是,避免了多次加载不同应用程序时造成的内存碎片;

⑤ 依据头部信息区数据容量分配数据基址。此步骤有两种方法:动态管理和静态管理。动态管理是依据目标程序全局变量容量申请堆空间,并将配分的起始地址写入GOR;静态管理则是将人为划分的数据区起始地址写入GOR。静态管理的好处是避免了多次加载不同应用程序时造成的内存碎片;

③ 在Linker选项的Config页面中,依照图4示例将.intvec start及ROM地址设置为0x00。其它参数对PIC代码没有意义,忽略即可。本步骤也可在icf文件中将__ICFEDIT_region_ROM_start__和__ICFEDIT_intvec_start__定义为0x00实现,效果相同。

⑥ 将步骤④ 得到的起始地址加上头信息区的偏移值后赋值给函数指针;

⑦ 通过固定内存区域或以函数参数的方式将起始地址传入对应函数,以便应用程序修改NVIC向量偏移及设置VTOR;

⑧ 调用函数指针。

至此,应用程序开始运行。如果应用程序设计是可返回的或可通过命令终止的,则当应用程序结束后系统控制权将自动返还至loader,以便系统进行后续程序管理。

上述步骤的代码举例请参阅代码段4,内容仅供参考。

【代码段4】

typedef int function(void);

typedef function * function_p;

struct ropi_rwpi_header_layout{

uint ROM_address;

size_t code_bytes;

size_t data_bytes;

size_t start_offset;

};

//数据基址寄存器

static __no_init char* rwpi_data @ R9

//程序执行函数

void execute(char * program_image){

int status = 0;

struct ropi_rwpi_header_layout ropi_rwpi_header;

//从SD卡读取应用程序文件

FILE* f = fopen(program_image, "rb");

fread((char*) &ropi_rwpi_header, 1, sizeof(ropi_rwpi_header), f);

//为PIC代码动态申请空间并拷贝代码

unsigned char* ropi_code = malloc(ropi_rwpi_header.code_bytes - sizeof(ropi_rwpi_header));

fread(ropi_code, 1, ropi_rwpi_header.code_bytes- sizeof(ropi_rwpi_header), f);

fclose(f);

//动态申请RAM并写入基址寄存器

rwpi_data = malloc(ropi_rwpi_header.data_bytes);

function_p application = (function_p)(ropi_code + ropi_rwpi_header.start_offset);

status = application(ropi_code); // 应用程序执行完毕或被用户终止返回

free(rwpi_data);

free(ropi_code);

}

(2) 编译器设置及链接脚本修改

修改loader程序的链接器脚本文件,将堆区置于外部SRAM。

依据上述方法,可将一个普通loader程序转换为动态程序管理器loader。将编译后的loader下载至MCU片内程序存储器,将编译得到的应用程序写入SPI-Flash或SD卡。启动系统,即可按需动态加载、删除或更新应用程序。

结 语

本文简要讨论了几种动态程序管理技术,展示了一种在没有MMU单元的MCU上进行动态应用程序管理的方法,并给出步骤和关键代码。工程实践表明,本方法运行可靠、稳定。本方法的背景知识及实现步骤体现了深入理解处理器架构及编译系统机制的重要性。只有加强对相关机制的深入理解,才能在有限的硬件资源下灵活实现各种复杂机制,以满足不断变化的工程实践需求。

参考文献

[1] 唐思超.嵌入式系统软件设计实战--基于IAR Embedded Workbench[M].北京:北京航空航天大学出版社,2010.

[2] Arm.Cortex-M3 Technical Reference Manual[EB/OL].[2018-02].www.arm.com.

[3] IAR Systems.ARM IAR Assembler Reference Guide[EB/OL].[2018-02].www.iar.com.

[4] IAR Systems.IAR Development Guide-Compiling and Linking[EB/OL].[2018-02].www.iar.com.

唐思超
《单片机与嵌入式系统应用》2018年第05期文献

服务严谨可靠 7×14小时在线支持 支持宝特邀商家 不满意退款

本站非杂志社官网,上千家国家级期刊、省级期刊、北大核心、南大核心、专业的职称论文发表网站。
职称论文发表、杂志论文发表、期刊征稿、期刊投稿,论文发表指导正规机构。是您首选最可靠,最快速的期刊论文发表网站。
免责声明:本网站部分资源、信息来源于网络,完全免费共享,仅供学习和研究使用,版权和著作权归原作者所有
如有不愿意被转载的情况,请通知我们删除已转载的信息 粤ICP备2023046998号