自己做产品的产品使用了UCOSII,按理应该缴费,但是我如果不拿的话他怎么知道我用了这个?


软件正常你要是连接网络的话就鈳以知道你用这个软件了用了多长时间,一般付费软件都有这个功能

你知道UCOSII是什么吗就这么回答

你对这个回答的评价是

你对这个回答嘚评价是?

}

Chip的缩写直译是“芯片级系统”,通常简称“片上系统”因为涉及到“Chip”,SoC身上也会体现出“集成电路”与“芯片”之间的联系和区别其相关内容包括集成电路的设計、系统集成、芯片设计、生产、封装、测试等等。跟“芯片”的定义类似嵌入式SoC更强调的是一个整体,在嵌入式应用领域给它的定義为:由多个具有特定功能的集成电路组合在一个模块上形成的系统或产品,其中包含完整的硬件系统及其承载的嵌入式软件

Chip,片上系統)集成芯片设计的重要模块是SoC中成本比重较大的部分。内存管理的软硬件设计是SoC软件架构设计的重要一环架构设计师必须要在成本和效率中取得平衡,做到在节省内存的同时保证整个系统的性能系统内存需求评估是对嵌入式软件架构师的最基本要求,同时也是其最重偠的技能之一一般在SoC项目立项的时候,架构师就要完成系统内存需求评估

嵌入式SoC有两个显著的特点:一是核心硬件设计难度大;二是軟件比重大,需要进行软硬件协同设计举个例子,比如城市相比农村的优势很明显:配套齐全、交通便利、效率高

嵌入式SoC也有类似特點:在单个模块上集成了更多配套的电路,节省了集成电路的面积也就节省了成本,相当于城市的能源利用率提高了;片上互联相当于城市的快速道路高速、低耗,原来分布在电路板上的各器件之间的信息传输集中到同一个模块中,相当于本来要坐长途汽车才能到达嘚地方现在已经挪到城里来了,坐一趟地铁或BRT就到了这样明显速度快了很多;城市的第三产业发达,更具有竞争力而嵌入式SoC上的软件则相当于城市的服务业务,不单硬件好软件也要好;同样一套硬件,今天可以用来做某件事明天又可以用来做另一件事,类似于城市中整个社会的资源配置和调度、利用率方面的提高

可见嵌入式SoC在性能、成本、功耗、可靠性,以及生命周期与适用范围各方面都有明顯的优势

在嵌入式系统综合设计技术中,系统设计往往非常复杂一般需要学习和了解非常多的技术相关知识,且需求、设计、修改一變则设计重来牵一发而动全身。

模块化设计是嵌入式SoC的基础设计思想嵌入式SoC技术设计应用电子系统的基本设计思想就是实现全系统的模块化设计。用户只须根据需要选择并更换各部分模块和嵌入结构就能实现所需要的目标,而不必花时间熟悉特定的电路开发技术模塊化设计的优点就是系统能更接近理想系统,更容易实现设计目标

嵌入式SoC设计重用技术

嵌入式系统应用越来越复杂化,且底层硬件驱动開发关系到整个系统的稳定性如果从最底层的寄存器操作开始,一步一步地构建整个开发平台必须投入大量的资金、人员和时间才有鈳能保证系统的可靠性,因此复杂的嵌入式产品推荐不要一切从头开始要将设计建立在较高的层次之上。将更多的模块技术复用只有這样,才能较快地完成设计保证设计成功,快速满足市场需求

嵌入式SoC设计再利用是建立在核心模块(CORE)基础上的,它是将己经验证好的复雜嵌入式软硬件系统以便后续的设计利用。嵌入式SoC通常由两部分组成一部分称为硬件模块化,具有复杂的高性能的嵌入式处理的最尛系统和特定功能,并被稳定性验证过可被新设计作为特定的功能模块直接调用。

另外一部分是固件(firm core)是在固件的基础上开发的,如图5 1所示开发工程师告别操作寄存器的开发模式,不需要了解ARM硬件功能只需调用底层硬件驱动程序、OS、GUI、FAT文件管理系统、TCP/IP协议栈、CAN-bus高层协議......等固件的API函数,即可快速地开发出一个稳定、可靠的产品这就是嵌入式SoC设计重用技术所要实现的目标。

下面以低端多媒体电子(如bombox、點读学习机、录音笔等)解决方案中的SoC设计为原型说明大致的评估流程。

1. 根据产品规格对各个应用场景进行功能和性能分解

产品规格┅般会描述应用功能场景和性能。架构师要对各个场景进行功能和性能分解分析各个场景在内存使用上的关系。包括:

1)列出所有的应鼡场景明确各个应用的生命周期,在什么时候开始什么时候结束。

2)系统是否要同时支持多个应用(多进程)例如听歌曲的时候要瀏览图片,这意味中两个应用是同时利用内存不能进行应用内存分时复用;

3)系统是否要同时支持多种介质,例如同时访问卡设备和闪存设备一般在单进程时都只是访问单种存储设备,除非是实现数据复制但在多进程的时候,不同的进程访问不同的存储设备也很正常同时访问不同的存储设备意味着两种驱动是同时使用内存。

4)系统是否要同时支持多种文件系统不同的存储设备可能部署不同的文件系统,其同样存在2)中的问题

5)明确系统支持的编解码格式,其表现为算法内存需求不同的编解码格式对内存的要求不同的,同样的算法时不同的速率也导致不同的内存需求。

6)系统性能要求例如LCD刷屏,有大块framebuffer自然会有更好的性能

2. 对系统软件进行分层,明确每一層模块的组成

1)系统分启动、驱动、操作系统、文件系统、中间件(算法、UI)、应用框架、应用等层次一般的消费类电子产品,如多媒体設备、游戏机等产品系统都会分成多个层次每个层次又会有多个模块组成,如驱动分字符设备驱动和块设备驱动一般按键属于字符设備,存储设备一般属于块设备;存储设备里可能支持nand flash、SD-MMC card、Uhost等;文件系统又有FAT32、exfat等等;应用层当然会包括很多个应用程序

2)明确每个应用实現所需要的软件层次。有些应用可能要很多层如音乐,从应用到应用框架(UI+按键)、API、中间件(解码)、操作系统、驱动等层次而设置应用是不需要解码中间件的。

3. 明确每个软件层次中内存分时复用的模块找出最大内存需求的模块

如《在资源紧缺型嵌入式系统中节省內存的软件设计技巧》这篇文章提到,应用、驱动、中间件、数据段都存在着复用的需求要在上一节2)中的各个软件层次中区分各个不哃的组成模块,明确各个模块是否能够进行分时复用在复用的情况下,找出最大内存需求的模块如nand flash驱动要比card驱动复杂,那nand flash驱动的内存需求自然要高;而音乐应用自然要比设置或者FM等应用要复杂其内存需求自然也要更多。

4.对最大内存需求模块的代码进行分析大致明确其常驻内存代码和分块(bank)管理的代码

常驻代码段一般是调用频繁的、性能要求高的代码段,如中断管理、消息管理等一般应用中大量嘚代码是可以按需加载执行的,如音乐的音效管理和音量设置这些功能代码并不要求很高的执行性能其是可以分时加载到内存执行,能夠达到内存分时复用的目的

5.确定各个软件层次的常驻代码空间和分时复用内存的空间

在成本的要求下尽可能减少常驻代码空间,会导致玳码执行性能的降低因为bank代码执行前要先进行加载,一般是从nand flash或者card中读取;在成本的要求下我们也想尽可能减少bank代码复用的内存空间哃样会导致bank代码切换频繁而降低性能,因此也不能一味地减少内存而是仔细分析各个子模块的功能和性能对内存的大致要求。例如两个孓模块的函数实现是8k和4k那我们可以考虑2K的复用空间,即前者分成4个bank后者2个bank,是否能够达到性能;如果复用空间设置为4K效率会高一些,但成本会增加;如果设置为1k那前者就会有8个bank,切换次数过多

6.明确可以固化的代码空间

应用的常驻代码是不能固化的,因为不同的应鼡都需要常驻代码即其是变化的,而像操作系统的中断管理、时间管理、任务调度管理等代码一般是不变的其可以固化到ROM中,这样能達到节省内存的目的

7.考虑其他特殊的需求

通过6,我们可以大致得到整个系统的内存需求这时要考虑一些特殊的场景的内存需求,看之湔制定的内存是否能够满足这个场景例如启动阶段的内存需求分布,OS引导初始化时的内存要求等这些并不是产品的规格,同样是架构設计师要考虑的

一般会对6中得到的内存再次评估,以进行细微的调整

软硬件整合一直是SOC芯片设计的核心技术,由系统软件架构师和芯爿系统设计人员共同评估、设计SOC的各个模块以性能、成本、软件编程灵活性、软件扩展性等作为考量依据,决定模块哪些流程可以软件硬化哪些环节可以硬件软化。

程序的大部分代码都可以在必要的时候才加载到内存去执行运行完后可以被直接丢弃或者被其他代码覆蓋。我们PC上同时跑着很多的应用程序每个应用程序使用的虚拟地址空间几乎可以整个线性地址空间(除了部分留给操作系统或者预留它鼡),可以认为每个应用程序都独占了整个虚拟地址空间(字长是32的CPU是4G的虚拟地址空间)但我们的物理内存只是1G或者2G。即多个应用程序茬同时竞争使用这块物理内存其必然会导致某个时刻只存在程序的某个片段在执行,也即是所有 程序代码和数据分时复用物理内存空间 —这就是内存管理单元(MMU)工作核心作用所在

处理器系列的芯片(如X86、ARM7以上、MIPS)一般都会有MMU,跟操作系统一块实现虚拟内存管理MMU也是Linux、Wince等操作系统的硬件要求。而控制器系统的芯片(面向低端控制领域ARM1,2,MIPS M系列80251等)一般都没有MMU,或者其只有单一的线性映射机制

在阐述控淛器领域的内存管理之前,还是要先介绍处理器领域的虚拟内存管理机制前者很大程度上是对后者核心机制精髓的借鉴。实现虚拟内存管理有几个模块是协调工作的:CPU、MMU、操作系统、物理内存如图示(假设该芯片系列没有cache):

我们根据上图来分析一下CPU访问内存的过程,假设寻址是0x一页大小为4K(12比特)。则虚拟地址会分成两个部分:页映射部分(20bit0x10000)+页内偏移(12bit, 0x8)CPU通过总线把地址信号(0x)送给MMU,MMU会紦该地址的页映射部分(20bit)拿到TLB中匹配

TLB是什么东西?Translation Lookaside Buffer网上有称为“翻译后备缓冲器”。这个翻译都不知道它干什么它的作用就是页表的缓冲,我喜欢叫它为页表cache其结构图如下:

可以想象,TLB就是索引地址数组数组的每个元素就是一个索引结构,包含虚拟页地址和物悝页地址其在芯片内部表现为寄存器形式,一般寄存器都是32位实际上TLB中的页地址也是32位寄存器,只不过索引比较时是比较前20bit后12bit其实吔是有用的,例如可以设置某个bit是表示常驻的即该索引是永远有效的,不能更换这种场景一般是为适合一些性能要求特别高的编解码算法而设计的。非常驻内存的一般在某个时刻(如TLB填满时访问一个新的页地址)就会发生置换

0x的前20bit在TLB中第M个索引中命中,这时就表示该虛拟页在物理内存中已经给它分配好对应的物理内存页表中也已经做好记录。至于虚拟地址对应的代码页是否从外存储(flashcard,硬盘)的程序中加载到内存中还需要要另外的标记怎么标记呢?就是利用上面所讲的TLB低12位的某一bit(我们称为K)来标识1标识代码数据已经加载到內存,0表示还没加载到内存假如是1,那就会用M中的物理地址作为高20bit以页内偏移0x8作为低12bit,形成一个物理地址送到内存去访问。此时该佽访问就会完成

2)  假如 K是0,那意味着代码数据尚未加载到内存这时MMU会向中断管理模块输出信号,触发一个中断进行内核态由操作系統负责将对应的代码页加载到内存。并修改对应页表项的K比特和TLB对应项的K比特为1.

0x的前20bit在TLB所有索引中都没有命中则MMU也会向中断管理模块输絀一个信号触发中断进入内核态,由操作系统将0x右移12位(即除以4K)到页表中去取得对应的物理页值假如物理页值非0有效,说明代码已经加载到内存了这时将页表项的值填入到某一个空闲的TLB项中;假如物理页值为0,说明尚未给这个虚拟页分配实际的物理内存空间这时会給它分配实际的物理内存,并写好页表的对应项(这时K是0)最后将这索引项写入TLB的其中一条。

2)和3)其实都是在中断内核态中完成的為什么不一块做了呢?主要是因为一次中断不应该做太多事情以加大中断延时,影响系统性能当然如果有芯片将两者做成一个中断也昰可以理解的。我们再来看看页表的结构页表当然也可以按TLB那样做成索引数组,但是这样有两个不好的地方:

1)页表是要映射所有的虚擬页面的其维护在内存中也需要不小的空间。页大小是4K时那映射全部就是4G/4K=1M条索引,每条索引4*2=8个字节就是8M内存。

2)假如按TLB那种结构那匹配索引的过程就是一个for循环匹配电路,效率很低要知道我们做这个都是在中断态完成的。

所以一般的页表都是设计成一维数组即鉯整个线性虚拟地址空间按页为单位依次作为数组的下标,即页表的第一个字(4字节)就映射虚拟地址空间的最低4K第二个字映射虚拟地址最低的第二个4K,以此类推页表的第N个字就映射虚拟地址空间的第N个4K空间,即(N-1)*4K~4KN的地址空间这样页表的大小就是1M*4=4M字节,而且匹配索引的時候只是一个偏移计算非常快。

承前启后在引出第二部分之前先明确两个概念:

1. Bank表示代码分块的意思,类似于上面提到的页的概念

2.不同代码分时复用内存:不同代码即意味着不同的虚拟地址对应的代码,(程序链接后的地址都是虚拟地址)内存即物理内存,即一萣大小的不同虚拟地址的代码在不同的时刻都跑在同一块一定大小的物理内存空间上每一块不同的代码块即是不同的代码Bank。

二、控制器領域SOC内存管理单元的软、硬件协同设计

这里专指没有内存管理单元的SoC设计一般为了降低成本,在性能足够时如果16位或者24位字长CPU能够解決问题,一般都不会去选32位字长的CPU除非是计算性能考虑,或者32位CPU的license更便宜(一般很少见)只要能够达到高效地进行内存管理,实现物悝内存分时复用的目的那都可以称为是成功或者有效的。所以在介绍真正的内存管理单元硬件设计之前我们先简单介绍一种利用工具鏈来实现内存分时复用的机制。

CODE BANKING机制是由Keil C针对16位8051系列单片机退出的内存管理解决方案其目的是扩展访问内存地址空间。16位单片机的可访問空间是64K字节假如程序和数据大于64K(系统和应用稍为复杂点就很有可能啦)怎么办?Keil C推出的解决思路是利用P1口作为地址线的扩展去访问最大支持2M空间,即32个64K需要5个P1口线。其译码(即P1口线的高低电平选择)是由keil C的特别编译器在编译时自动产生的当然,也需要在代码和鏈接脚本中主动说明某个函数是bank代码否则所有函数在调用之前都插入一段译码代码,那程序都不用跑了而且其也有公共代码区,例如Φ断处理操作系统常驻段等等,在每个64k中都同样有一份

我们会问,上面讲的CODE BANKING机制只是扩大可访问的内存地址空间跟内存分时复用似乎没有关系。其实我们是可以利用这个机制来实现不同代码分时复用的

如果P1的某条扩展地址线并没有接到实际的物理内存上,即该线无論输出1还是0这个地址访问的物理内存其实是一样的。例如有个地址是0x10008假如P1.0这根线没接到内存,那第17个bit的1其实是没有产生译码作用的即0x10008访问的是0x00008的内存。也即是两个不同的地址其实对应的是同样的物理空间所以我们可以将不同的函数编译链接到不同的虚拟地址,但其朂终会被加载到同样的物理内存中去执行这样就实现了不同代码的内存分时复用。

不过这种方法也有一定的缺陷:

1)  最多只有 32个Bank不能洅多了。一般多媒体消费产品系统都难以满足

2)  应该开发要经常关注调用过程,要经常关注是否调了 Bank代码调试起来也比较麻烦,16位地址还要结合P1口才能确定真正的物理地址

当然,CODE BANKING机制的初衷是为了扩大可访问的内存地址空间而不是给我们这样用的。

终于要进入我们嘚正题了其实有了上面的介绍,下面的阐述你会很容易领会我们会模仿真正的MMU去进行我们的内存管理单元硬件设计,并且考虑相关的細节

我们要清楚,利用控制器领域的CPU(例如251MIPS M系列等)进行SoC设计,一般都会高度集成K级别的内存都会使用SRAM技术集成到SoC中。SoC内存管理单え结构图如下:

将这个图和MMU的结构示意图比较可以发现有以下不同:

2)  Bank 号寄存器组可以理解成跟TLB的索引数组

3)  Bank 内存管理电路跟MMU的管理电蕗的核心电路应该说是一致的,当然会非常简单其都是实现一个循环匹配的过程,实际上就是一个选择器电路

可以说,Bank内存管理电路僦是MMU单元的简化版只是内存中不再存在页表。我们来看看它的工作机制看在没有页表的情况下如何做到映射。这是由操作系统和应用程序、Bank内存管理单元一块完成的架构师的责任!这一定会是SOC软硬件协同设计的一个教科书式经典案例!

内存中划分常驻代码数据区域和若干个Bank区域。有同时竞争内存使用的程序模块就使用同一个Bank空间如两个应用层(音乐和FM)程序约定分时使用某个Bank,而两个存储驱动(如card驅动和flash驱动)约定分时使用另外一个Bank空间我们把使用某个Bank空间的所有虚拟Bank号称为一个Bank组。所以系统中会存储多个Bank组而每个Bank组使用的实際物理地址空间是之前约定好的。

2)  一个虚拟地址分两个部分一个是 Bank号,另一个是Bank内的偏移假如CPU字长是24bit,设定Bank号的比特数是8则虚拟Bank夶小就是16bit,64K物理地址也分两个部分,一个是Bank组号(对应物理内存块)另一个是Bank内偏移。

3)  实际物理 Bank大小是小于虚拟地址Bank大小的可以昰1k,2k等等低16bit的地址都是bank内偏移地址,虚拟和物理内存是一一对应的物理bank大小由操作系统来约定,应用程序编写模块函数时遵守即可

4)  在整个虚拟地址空间中划分不同的 Bank组,就需要设定每一组Bank对应的Bank号的范围假设有8个Bank组,那每个Bank组的Bank号数目就是256/8=32个

5)  Bank 号寄存器组和上媔设定的Bank组是一一对应的,某个时刻每个Bank组只有一个Bank号写入到对应的Bank号寄存器中

6)  通过以上约定和分析,可以知道对于一个虚拟地址,是可以知道它是属于哪个 Bank组的而通过Bank组和Bank内偏移是可以确定它在内存中的确切地址,也意味着它实现MMU中的页表的功能如下图:

假设某个地址,我们来想象一下内存管理单元的工作过程:

1)  将该地址的高 N比特和Bank号寄存器组进行匹配如果匹配命中,证明该地址所在的Bank代碼已经在内存中内存管理单元直接转换为物理地址进行访问。

2)  假设不命中证明对应的 Bank代码没有在内存中,这时会触发一个异常进入Φ断这时操作系统会判断该地址所属哪个Bank组,找出其所对应的文件代码将其从外存储设备中读到内存,并更换对应的Bank号寄存器这时返回后再次访问时就不会再次发生中断,而是命中读取

3)  提到触发异常,而我们的 Bank内存管理电路是自己设计的并不是CPU原有的模块,怎麼触发异常呢一种方法是在不命中时由内存管理单元返回一条未知指令,而每种CPU都有一种未知指令异常这时CPU执行这条指令就会发生异瑺,进入中断而因为这条指令是人为增加的,执行后PC值也增加了在中断管理程序中要将之前保存的PC调整到未知指令执行之前的位置。

叧外架构师还应该按以上机制定义的规则定制好链接脚本,以便应用和驱动人员进行编程时不需要关心底层内存管理的实现底层对上層透明才是优秀的设计!

一、嵌入式系统软件分层

系统软件层次包括:启动、驱动、操作系统、文件系统、libc、中间件、应用框架、应用等層次。

  1. 驱动、文件系统和操作系统的时间管理、中断管理等接口一般都是通过API来进行调用;

  2. libc和中间件、应用框架在系统中的处理可能以API的形式进行调用也可以直接作为静态库与应用直接进行链接。

  3. libc和中间件、应用框架作为静态库时会减少API的占用空间(API往往是常驻空间,沒理由调用API时还要从外存储中将API的代码加载到内存这样效率太低),省去API层也可以提高调用速度但会增加库函数的代码空间。如果库函数链接时可以运行在Bank内存中由于Bank内存可以复用,增加的代码空间可以忽略从这一点来看其又是一个优点。如判断某个文件是哪种解碼格式时其可以作为中间件来实现,并链接到应用的Bank空间因为这是音乐解码前的预处理,可以和解码时刻的控制流复用同一块Bank空间

  4. libc囷中间件、应用框架以API形式来调用时,会产生API的常驻内存空间需求在内存中也只存在一份真正的代码,供所有模块共同调用而且应用開发者无需关心接口实现,也不允许开发者去修改

各个模块应根据实际情况来决定其供上层调用的形式。

代码分页(块Bank)设计请参考:SoC嵌入式软件架构设计之二:没有MMU的CPU实现虚拟内存管理的设计方法 和 SoC嵌入式软件架构设计之三:代码分块(Bank)设计原则。

这里程序段是指可执荇文件中出现的段名如.CODE、.DATA、.BSS等默认段名和其他自定义的段名。GNU工具链各种编译输出段名称是可以在链接脚本中指定的,当然在编写代碼时也可以指定函数或者代码的编译输出段名称如在定义一个数据变量时添加一个属性__attribute__((section("bank_data")))时,该数据变量将会被重定位在bank_data段下图是具有Bank玳码段的程序与可执行文件段名的对应关系图:

三、SoC内置内存规划

一般地,如果SOC中内置SRAM超过32K数字工程师也会将内置内存进行分块,一是為了减少电路延时二是为了让内存得到更有效率的利用。如某块内存在某个时刻是作为代码使用有时也可能作为数据使用(如果是哈佛结构,那就要切换内存的选址译码电路从代码空间转到数据空间),有时也可能用作特别的解码buffer使用而有些解码的缓存是以24bit作为单位,如果所有内存都作为一块来设计显然是满足不了这样的需求的。下图是常见的SRAM示意图:

根据软件分层和程序段综合考虑一般在物悝内存的基础上先进行分层划分内存区域,再进行各层程序的段内存划分有以下原则:

  1. 各层的常驻段(代码和数据)应该紧凑分配,而各层的Bank空间与常驻空间分块也应该紧凑分配。

  2. Bank空间的起始地址应该与扇区单位对齐可取得最好的加载代码性能。

  3. 先把常见的场景的内存分配好再考虑特殊场景的需求,看看特殊场景能否复用普通场景的内存空间

  4. buffer的划分也要考虑场景的复用,否则太浪费如解码的buffer可鉯在未解码的时候用作预处理时的媒体文件有效性判断的buffer。

  5. 有时两组Bank空间可以合并起来当作另一个场景的一组Bank空间来使用如解码时的软件分层比较多,涉及到应用中间件和算法中间件而文件浏览应用则没有这么多层次,可以将两个中间件的Bank合并起来当一组Bank来使用

  6. 一个模块的代码不应该跨越两个物理内存块,否则访问性能会降低

  7. 尽可能提高内存利用率,避免内存碎片

  8. 内存分配的细节要以公共链接文件出现,并用有意义的名称来定义各段的起始地址和长度除架构设计师外,其他人不允许修改该文件

驱动在系统中会按模块进行分类,例如KEY驱动、LCD驱动、文件系统、card驱动、I2C驱动等等;每个模块又有多个接口例如LCD驱动有光标定位、画点、画直线等,而文件系统有fread、fwrite、fseek、fopen等接口以下举例将以文件系统的fopen为例。

4. 驱动接口函数指针数组:

5. 文件系统被加载时会将文件系统的接口函数指针数组fops注册到系统的API管悝数组中。

6. 系统对驱动进行分类管理其有一个记录各个驱动接口函数指针数组基址的全局数组,各个驱动事先按顺序进行约定如数组嘚第一个元素就是按键驱动的key_fops,而第二个就是LCD驱动的lcd_fops以此类推。某个驱动被加载时驱动会把对应的fops通过API管理的接口记录到该数组的对應位置。

在fs.h中表示fopen是文件系统提供的第一个接口,这个常量包含两部分信息一是文件系统在API管理中的索引,二是该接口在自己驱动接ロ中的索引

应用调用时,path和mode等形参会压入栈或者寄存器(MIPS是栈和寄存器(a1,a2,a3,a4)都同样存放而ARM是优先寄存器,不够再转到栈中存放)然後进入fopen的API,其将FILE_OPEN常量赋值给v1通过syscall陷入到异常,进行内核态这时即可以进入到API管理中,API根据FILE_OPEN提供的两部分信息可以迅速找到open的地址异瑺返回时即跳到open的地址执行,此即进行实际的接口调用整个过程完成。

API的传递会包括两部分内容一是接口本身的参数,如fopen的文件名和操作方式二是API的索引号,如FOPEN考虑到系统陷入后的管理最终会跳转调用到目标的接口,应该将接口本身的参数放在API接口的前面(并且尽量鼡寄存器存放完因此参数不宜超过4个),而索引FOPEN放在最后(这里的mips汇编把这个参数放在返回值寄存器v1)这样陷入后的管理就不需要对参數进行调整。这是汇编和C语言混合编程需要考虑的问题

在资源紧缺型嵌入式系统中节省内存的软件设计技巧

资源紧缺型嵌入式系统一般主控都是单片机控制器,内存资源都是K字节级别资源紧缺型的电子产品都是成本敏感的,而成本跟内存的容量是成正比的尤其在SOC芯片設计时是固件开发需要重点关注的。量产前要确定片上RAM的大小而且是在满足功能需求的情况下越小越好。节省内存的常规做法一般是通过高效利用内存来达到目的,此外还有其他的重要的软件设计技巧这自然要考究开发人员的系统软件设计能力和编程开发的技能了。

這里以低端多媒体电子产品为例其一般会自行定制精简版操作系统,驱动、中间件和应用等模块都有所谓麻雀虽小,五脏俱全

分时複用即对代码进行分块(Bank)管理。它的设计需求来源于:

1. 很多电子产品并不是像现在的安卓手机一样同时跑多个应用顶多就听歌时浏览圖片而已,非智能手机也是如此但我们也会看到电子产品里面有有很多的应用,如设置、电子书、电话本、录音啊等等因此,不同时運行的应用占用同一块内存空间理所当然

2. 驱动空间。有很多的驱动并不同时使用如听FM时是FM驱动,听歌又是使用解码器所以很多驱动吔是可以服用同一块空间的。

3. 中间件的复用如UI、硬件驱动的再次封装使用等等,其由对应的应用直接调用一般也存在复用的需求。

4. 数據段的复用应用和驱动都有数据,同样有复用的场景需求

理论上驱动和代码也可以服用空间的,但需要考虑的细节太多而且这样做擴展性不好,所以应用一般是不会跟驱动复用空间的一般较为粗糙地将软件系统分为以下几个部分:启动、驱动、操作系统、中间件、應用等层次。启动为一次性执行不需太多考虑复用的空间。操作系统一般有常驻内存的需求如中断管理、时间管理、调度管理、模块玳码管理、虚拟文件系统等等,当然操作系统的一部分功能并不需要常驻内存主要是一些调用频率较小的一些接口,如驱动装卸载、应鼡初始化等模块不需常驻内存的一些接口实现也可以跟驱动复用空间。

考虑系统架构师的功力块设大了浪费,小了会导致代码切换频繁效率低下既然都是RAM,有时数据可以跟代码段放到同一个块中而没有必要另加一块数据块。当然这些细节需要综合评估并加以详细设計在成本敏感的电子产品中,这些技巧需要努力挖掘发挥

这只是从成本的角度去节省内存资源,有些代码需要常驻内容但其内容并鈈会随着版本的更新而发生变化,如上节所讲操作系统的调度管理等可以考虑将这些代码固化到ROM中。理论上操作系统大部分需要常驻内存的代码都可以固化RAM和ROM同样大小的成本比大概是6:1,因此使用ROM也能大幅降低成本

这是操作系统设计人员务必要考虑的。每个产品都有独囿的功能而底层操作系统具有普适性,在资源紧缺型系统中砍掉不必要的模块是非常明智的。

我们所阐述的系统一般都是封闭系统呮要能高效地实现功能,我们可以任意改动系统中所有的代码例如对于可执行的ELF文件,操作系统如果按标准的流程要解析完ELF文件再加载但不仅需要很多的内存资料,而且也效率低下ELF有关加载和执行最重要的就是.CODE、.DATA、.CONST、.BSS等段信息,我们完全可以离线抽取出来生成一个新嘚简单的定制文件格式操作系统只需解析这个简单的文件就可以了。这样做不仅节省内存也能节省外存储空间,更能实现高效加载的目的

这个需要平时的积累。例如在变量的排列方面,我们都知道编译器会考虑对齐明显,以下第一种定义需要的内存比第二种要大

好的算法一般会是轻巧的,效率高的

编译时选择优化级别高的,这样生成代码大小有有大规模的减小

如arm里面选thumb指令,mips选择mips16e这是由體系结构所决定的,体系结构也是为了考虑节省代码空间资源而设计了16位的指令模式而这些CPU的字长往往是32位。这种方式能减少30%左右的代碼量

每个线程都会有自己的栈,而每个线程的栈都应该根据其线程的调用深度来具体设定像UCOS就有一个栈使用率的任务,我们不妨借用這种思路来看看某个线程最终的栈深度

设定独立的中断栈,可以避免每个任务栈都要给中断预留栈空间

扁平的函数调用方法用栈一般偠比纵向的函数调用小。嵌入式开发有时为了效率和资源不应该把代码分块分得太细,函数一大摞既增加代码量和栈,也降低运行效率

十一、定制链接脚本,合理规划内存空间

例如我们规划空间时往往代码段和数据段分开,但实际的代码段可能又用不完这时就可鉯把一部分变量定位到代码段之后。这样能够尽可能地利用完内存而不留“碎片”。

}

我要回帖

更多关于 自己做产品 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信