关于ARMloader的一些心得.
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
关于ARM loader的一些心得
最近的项目里需要用KS8695这块MPU,它是ARM922T内核的,有cache,也有MMU,RAM和ROM需要外扩,最大都支持到64M,足以跑起一个完整功能的linux核.虽然这MPU主频最高只有166Mhz,相比三星的S3C2410等性能不是很强,但有内置的硬件WAN口和LAN口,构建网络路由器非常方便.
考虑到各种原因,最主要是实时性响应和速度响应的原因,决定不上linux操作系统,当作普通单片机以单一任务不断循环的方式实现,这就涉及到了loader的问题.由于专用于网络,普及率不大,这块MPU的网上资料不多,随开发板赠送的loader是uboot,uboot 是引导linux内核的,功能多,但要在linux环境下编译和调试,比较麻烦.我习惯用的工具是ADS,因此决定用ADS的汇编实现一个简单功能的loader(以下提到的代码语法全部是针对ADS环境的,而且仅是KS8695这块芯片,对于Keil for ARM或GCC环境或其他ARM芯片需要做别的考虑,但可以参考下面的作法和思路).
玩ARM的朋友,少不免要接触loader,但因为loader涉及到很多跟硬件有关的很抽象的东西,看起来比较复杂和难理解。希望下面说的这些能帮助有兴趣的朋友更好理解。
下面由简单到复杂来说说这个loader。
先想想,这个loader该有哪些功能呢?很明显,最重要的是,它必须最后能调用C语言里的Main函数,因为我不想整个项目都用汇编写,毕竟我不是自虐狂.而很明显,如果你要调用函数,必须进行入栈,出栈这些保护现场的工作,否则程序就会乱套.那保护现场工作需要什么?当然就是需要RAM来保存现场.对于KS8695,它没有内置可随意使用的RAM(实际上,它有特殊的小容量的RAM,但那是给网卡这些做硬件数据缓冲,用户是不能随意调用的,顺便提一下,ARM芯片很多,不同的厂家有不同的硬件配置,譬如很多低端应用的ARM芯片其实有内置的SRAM,譬如菲利浦的LPC系列),因此,就得使用外扩的RAM了.开发板上用的是
SDRAM,SDRAM的本身的特性决定,需要定时刷新的支持,KS8695有内置的SDRAM控制器,只要往某个专用的寄存器写入一些与硬件参数有关的数值,该SDRAM控制器就能自动产生各种读写SDRAM的时序信号来驱动SDRAM。然后用户程序只需用str和ldr指令就能读写SDRAM的内容.
OK,现在有点头绪了,loader至少要完成这两个功能:
只要这两部做完,其实就可以调用C里的main函数,然后由main函数再调用各种功能的子函数,以后就爱咋滴就咋滴了.
咋一看,似乎这两步做完,就天下太平了,真的就这么简单吗?
未必,别忘了,几乎所有的CPU都带有中断触发机制.一个稍能完成点实用功能的项目,你想完全避开中断处理这一部分是完全不可能的,也不现实,你必须按你的需要来编写各种各样的中断服务程序,处理各种各样的中断触发(至于中断触发对CPU来说是必需的吗?中断有什么好处?中断时CPU的硬件会做什么动作?...这些就不想罗嗦,有兴趣的可以参考51系列的中断原理作为入门理解).编写中断服务程序,可以在C语言里实现,不要在loader里实现,loader里要实现的,是要建立中断向量表.中断向量表是一个有点专业的名词,请自行查入门资料.
这里说说为什么要建立中断向量表.在51单片机里,是不用自己建立中断向量表的,这是因为标准51核的中断,本身就是一个向量中断(自行查资料),自身硬件里就已经有内置的中断向量表,没必要自己去建立.就是说,当某个中断触发时,51核的硬件会自动把PC置成相应的固定的地址入口来执行相应的中断处理程序,不同的中断触发时,会有不同的地址入口,一一对应,俗称向量中断.但
KS8695不是这样(很多arm芯片没中断向量表,但有些arm芯片同时支持向量中断和非向量中断两种模式,可以按需要设定),它没内置的中断向量表,当中断触发时,无论什么中断,入口地址只有一个,你必须在这个入口程序里读取某些中断寄存器的内容,来判断究竟是发生了哪个中断,然后通过查表,查自己建立的中断向量表,来调用相应的中断处理程序.
现在看来,loader复杂一点点了,归纳一下,现在变成有三个功能需要而且是必须要完成了:
完成了这三步,似乎又天下太平可以一马平川了,真的是这样吗?不是的.下面还有一个功能必须要实现:变量的初始化.
变量的初始化是个什么样的概念呢?举个简单的例子,如果你程序里有个变量这样定义(这是不可能避免的吧):
uchar i=5;
main()
{
}
就是说,i这个变量,初始值是5,具体反映到硬件上面,就是说,i这个变量所对应的内存单元,它在相应程序要执行之前,这个内存单元里保存的数值应该是5.否则,这个内存单元保存的是上电时硬件随机产生的不确定数值.
自己来做变量初始化的工作,其实不是必要的,而仅仅是当使用的编译器是ADS,才是必要的.如果使用的是其他编译器,有可能不用做这步.譬如Keil的编译器在链接成最终代码时,会自动添加一段变量初始化的代码,而GCC编译器记得也是这样.现在用的编译器是ADS。
因此,很遗憾,loader又得复杂一点点了,新的列表如下:
这四步做完,确实可以一马平川了,但如果你想独步天下,还需要做得更好.作为一个编程人员,通常都是完美主义者,偏执狂,所追求的是无止境的效率和自由,我想很少有人会拒绝耍一些提高程序效率的手段吧?
于是下面可以再多了一个步骤,但这个功能对KS8695来说其实不是必需的(因为KS8695用的是NOR flash,允许单字节随机访问,代码可以在NOR flash里执行,但对于别的ARM芯片,如果用的是NAND flash,由于不支持单字节读以及随机地址访问,则必须把代码拷贝到RAM里,从RAM里执行),只是让我们的代码跑得更快.这个功能是:把代码在放在RAM里执行.