猛一回头,觉得笔记不能断掉,所以趁空好好整理一下思路。

最近做了什么?从9月底到今天约20天三周的时间里,基本就是:熟悉canopen协议栈,熟悉bootstrap,uboot源码及其移植,期间了解了下ARM裸机启动和内部时钟配置,norflash驱动移植。

从哪里说起呢?当然是ARM裸机启动。我觉得这也应该是做嵌入式开发的起点,从裸机启动,程序下载,bootstrap、uboot、os、fs、驱动的移植,到最后是上层应用程序的开发。所以很高兴,我认识到了学习的路线,并且我的工作就是要走遍全程。

(一)AT91SAM9G45上电启动过程

主要参考《U-boot在AT91RM9200上的全线移植分析原始版》第3章1节AT91RM9200的启动方式,《SAM926X 裸奔指南》,《at91sam9g45 datasheet》第11章boot strategies。参考资料都很详细,我这里画一幅图总结一下。

上面的图示已经说的很清楚了,一般裸机第一次上电启动是通过SAM-BA或者DBGU往存储器下载正确的程序,之后可以从其它一些存储器启动,比如片外norflash,nandflash,dataflash等等。我手里有块at91sam9m10g45-ek的开发板,就是从EBI0 CS3上的nandflash启动的,nandflash的空间分布如上图的NVM空间分布,而我的目前项目是要从片外的norflash启动的。就上图我再提几个问题,在后文中都会一一解答:

1.如果BMS=低电平,CPU上电能否直接成功从norflash读取第一条指令?这里norflash的型号有没有限制?

2. Bootstrap这一步是不是必须的?什么情况下可以省略?

3. 将bootstrap和u-boot通过SAM-BA或者DBGU直接下载至片内的SRAM或者SDRAM,能正常执行不?

(二)AT91SAM9G45从norflash启动:

主要是解决如下几个问题

(1) at91sam9g45上电能不能正确的读我们的norflash执行第一条指令?这个norflash的型号支持么?

这是个关键的问题,如果答案是否定的,那么只能改板,选择上电BMS=1从nandflash等启动。从atmel的技术人员那边得到的答案是肯定的;从datasheet上看,检测到BMS=0,片内慢速RC做时钟源,连接在EBI CS0上得norflash被映射到地址0x00000000,开始执行第一条指令,并没有提及norflash的型号,推断是型号无关的;从网络上的资料,解释说norflash芯片都是使用的是统一的接口,读取不需要驱动,而nandflash不同,需要相应驱动才能读写。所以,我们暂且认为从norflash启动是没有问题的。

(2) norflash里面的程序通过什么下载进去?

现在的项目实际情况是,SAM-BA只支持对现有的片内SRAM和DDRAM进行烧写,J-FLASH也是无法检测到使用型号的norflash,手动选择了之后又会提示ID校验错误,无法读写。但是J-LINK连接正常。所以,准备通过SAM-BA烧写代码段1到DDRAM的地址1处,烧写代码段2到DDRAM得地址段2处。其中代码段1实现norflash的烧写,把代码段2拷贝到norflash地址0处,代码段2即为移植好的bootstrap。然后,我们通过J-LINK的命令设置PC跳转到代码段1处执行。在这个过程中有学习到一些内容。

的内部时钟>    主要参考datasheet 24章 时钟源25章 电源管理控制器内容

也是用图总结一下:

上图左侧为原始时钟源3个,右侧为由此产生的4种不同时钟,这4种时钟成为后面产生其他各种系统时钟的时钟源。旁边标注的数字为常见应用的典型值。

上图即为系统中各种时钟的产生过程,具体到如何使产生相应频率的时钟,就是配置相应的寄存器,可以参考PMC最后一节。

的读写>

对存储器的操作,最主要的就是实现读,擦除,写这函数,参考已有的S29GL512S10TF101  norflash的驱动,移植过程主要就是配置SMC相应的寄存器,使满足norflash的读写时序,主要参考芯片的datasheet。以读时序为例:

上图是norflash的读时序图;

上图是at91sam9g45的SMC标准读时序图,通过寄存器配置,设置图中6个时间参数值,满足norflash的时序要求即可。

(3)bootstrap的移植?

上面已经完成了代码段1的任务,下面就来实现代码段2,也就是移植好的将要放在norflash 0地址处的代码bootstrap。

既然是移植,那就有个参考。当然其实源码是最好的参考,但是从一个菜鸟的角度思考,自己当时一心只想着把手里的9m10g45开发板上的东西全部移植过来,所以就将这块板子作为参照了。那我就有几个问题需要回答,在9m10g45开发板上,bootstrap的功能是什么?它是在什么时候什么样的环境下执行的?

bootstrap的功能

参考《U-boot在AT91RM9200上的全线移植分析原始版》可知,在9m10g45开发板上,bootstrap放在nandflash的0地址处,该代码主要实现SDRAM的初始化,并把u-boot拷贝到SDRAM的固定地址处,然后跳转到该地址处,开始执行u-boot。可以看到bootstrap的出现主要是考虑到有的CPU片内SRAM空间太小(例如AT91RM9200的只有16K),无法直接装载u-boot镜像,所以需要一个6K左右的bootstrap加载u-boot到SDRAM执行。at91sam9g45片内虽然有64K的SRAM,但是想要装载u-boot还是有点困难。

参考Bootstap v1.6源码,main.c很简单,主要就调用了两个函数hw_init()和load_norflash(IMG_ADDRESS, IMG_SIZE, JUMP_ADDR)。hw_init()函数实现一些硬件的初始化,包括DBGU,时钟,DDRAM。load_XXXflash(IMG_ADDRESS, IMG_SIZE, JUMP_ADDR)函数即从相应的flash(norflash,nandflash或dataflash)中下载u-boot到SDRAM的JUMP_ADDR地址处,然后bootstrap就算完成任务了,跳转到JUMP_ADDR地址。

bootstrap的执行环境

从上面上电启动过程一篇知道,在9m10g45开发板上,上电后检测到BMS=H,则内部ROM被映射到0x00000000处,开始执行ROM内固化的第一段代码boot program。设置堆栈,使能主时钟,初始化C变量,初始化PLLA,然后初始化nandflash,检测到从nandflash启动(如何检测的可以参考datasheet),然后拷贝nandflash里的bootstrap到片内SRAM 0地址处,最后通过SRAM被映射到0x00000000处开始执行bootstrap,CPU的控制权就交给了bootstrap。

这样看来,bootstrap开始执行前,已经做了不少工作,如果要上电从片外的norflash启动,CPU是从0状态启动的,就是说还没有执行任何的代码。这样我们要成功把bootstrap移植到norflash,就要在它之前实现boot program的部分功能,将这段代码放在norflash的0地址处先于bootstrap执行,由它为bootstrap创造一个同样的执行环境。并且,实际上开发板上nandflash里面的bootstrap只是原始数据,bootstrap真正的运行是被拷贝到片内SRAM里面,所以这段代码似乎还要负责将norflash里的bootstrap拷贝到片内SRAM。

问题似乎变得稍微复杂了一些?一方面,目前没有固化在ROM片内的那部分代码的源码,它具体做了多少工作还不知道,只是根据参考资料了解到它的主要功能,另一方面作为一个刚入门的菜鸟而言,对自己写代码是没有一点信心的,因为无论是对ARM处理器还是程序的编译连接执行都了解的不多。使能主时钟,把bootstrap拷贝到片内SRAM去执行,起码这两个功能是要实现的。真的要自己去加入这样一段代码么(呵呵,见笑了,其实实现挺简单的,确实自己的水平还不够)?

所幸Bootstrap v1.6的源码比较简单(当然啦,因为它生成的镜像大小不能超过十几K),条理也比较清楚ctrl0_gnu.S------------>main.c------------>ctrl0_gnu.S。我们不妨先浏览一下源码(代码的注释可以很好的帮助我们理解功能)。

我发现在ctrl0_gnu.S里面,主要是初始化程序堆栈,重定位到SRAM,设置使能主时钟,初始化一些段,跳转到main.c,继而初始化SDRAM,拷贝u-boot到SDRAM,跳转到SDRAM(我只是走马观花的理了一下流程,有些功能怎么实现的为什么需要我还不清楚)。可以看到,在bootstrap主要功能实现(main.c)前执行的ctrl0_gnu.S也做了一些工作,其中就包括使能主时钟,如果上面所说的“重定位到SRAM”能实现把bootstrap拷贝到片内SRAM去执行(为什么会有这种猜测?因为这段代码的注释是在使用norflash的情况下条件编译的,而且代码的注释是“When running from NOR, we must relocate to SRAM prior to resetting the clocks and SMC timings”,这段代码确实是一段拷贝功能),那么就不需要我们去给bootstrap创造和9m10g45开发板上一样的运行环境,它自己就给自己创造了。

我们为什么必须把bootstrap拷贝到SRAM执行?程序是可以直接在norflash上运行的,不考虑执行速度的要求,我们不拷贝直接将源码编译生成的镜像文件下载到norfalsh执行可以不?这里又需要多考虑一点。解答这个问题又学到了不少东西:程序的加载地址,链接地址,运行地址的作用(是的,在这之前我竟然不知道这些东东,尽然不知道链接脚本,之前写好程序直接gcc生成可执行文件就运行了。当然,我现在的理解也很肤浅,以后好好补课。)我们写好的.c程序通过编译汇编(编译器汇编器)生成相应的二进制目标代码,然而这时程序中用到的一些地址(比如函数的跳转地址,标号代表的地址)还只是相对地址,相对于第一条代码的偏移量,我们还需要一个链接器,该链接器通过链接脚本.lds指定的参数得到.c程序数据段,代码段等各部分的链接地址(暂不考虑动态链接),通过链接地址就计算出了程序中代码的绝对地址(相当于链接地址给了一个基地址,将相对偏移量换算成绝对地址),然后链接得到代码各个段(section)的在内存中的分布,我们就可以下载到相应内存中去执行了。举个例子,假设链接脚本里面指定了代码段(.text)的地址是A,然而我们下载的时候加载到了内存地址B处,那么遇到代码中的地址跳转指令要跳到A offset(这是链接的时候计算出来的),然而实际要执行的代码可能在B offset处,那么程序就“跑飞”了。那么,是不是加载地址必须等于链接地址?也不是的,例如如果代码里面没有跳转指令或者说没有用到绝对地址(相对跳转),这就是所谓的地址无关代码(无论加载到哪里,都可以执行)。

加载地址:程序实际在内存中存储的地址,也就是被下载到内存的地址。一般通过设置下载工具实现。

链接地址:链接器规定的程序的运行地址。程序中的绝对地址都是在链接过程中通过该地址计算出来的,一般代码的运行地址(执行时候的地址)应该等于该链接地址,除非地址无关的代码。可能等于加载地址,也可能不等于加载地址。一般在链接脚本中指定。

运行地址:程序实际运行时候所处的地址。可能等于加载地址(这时一般加载地址等于链接地址等于运行地址,直接在存储程序的地方flash,rom执行,这样执行速度比较慢),也可能等于链接地址(加载地址可以不同于链接地址,该代码是被运行于它之前的代码从加载地址处拷贝到该地址处的,这是常见的情况,代码存放在flash,rom中,执行在sram,sdram中),也有可能既不等于加载地址也不等于链接地址(除非是地址无关代码——在哪里都能正常执行,否则无法正常执行)。

<</span>说明一下,上面的认识和举的例子可能有许多不当之处,只是为了易于理解,而且我就是这样理解的。但是要细究,还是需要花点时间看看这方面的书籍>

我们可以在bootstrap源码相应目录下找到makefile,board---->at91sam9m10g45ek---->nandflash---->Makefile里面:

# Link Address and Top_of_Memory

LINK_ADDR=0x300000

TOP_OF_MEMORY=0x304000

此处即设置了链接地址,对应的正是片内SRAM的首地址。然后在链接的过程,通过指定参数-Ttext来告诉链接器该链接地址:

LDFLAGS =-T $(BOOTSTRAP_PATH)/elf32-littlearm.lds -Ttext $(LINK_ADDR)

现在,那么我们是不是可以改变这个链接地址为norflash的地址,bootstrap就不用拷贝进SRAM中执行了?是的。到此,我们要为norflash上的bootstrap创造的运行环境已经解决,其实回头一看,没多少东西,但是我却感觉学到不少知识。我们还有一个问题没有解决,ctrl0_gnu.S中重定位到SRAM这段代码是什么功能?那么我们来分析一下这段代码。


接下来的代码就实现了将_stext到_edata部分的代码拷贝到LINK_ADDR指向的地址处,然后继续执行_setup_clocks段代码。怎么实现的?关键是这一句:ldrcc   r2, [r1], #4。寄存器间接寻址。把r1寄存器的内容(=0)作为地址,该地址处的连续4字节拷贝到寄存器r2中 。r1是0,该0地址是相对地址,相对于程序的运行地址的,因为存储在norflash中,运行前也没有被拷贝到别处,所以就是当前PC值。所以这段代码就实现了自拷贝的功能,就是说如果定义了norflash,bootstrap会将自己拷贝到SRAM(地址0x300000)中去。你可能已经发现了,这段代码的运行地址不等于链接器指定的链接地址,对,ctrl0_gnu.S里这段代码之前的部分都是地址无关的。拷贝完成后,跳转到_setup_clocks标号处的代码段,而标号在链接前都是个偏移量,经过链接得到绝对地址,所以这个跳转已经跳转到了LINK_ADDR offset去运行了。这一段理解很重要,也很艰难,不过理解之后的喜悦也是难以言表的。之后会发现像u-boot,kernel等好多的源码中汇编部分都有自拷贝功能代码。(为什么?自拷贝也是一种实现代码运行地址无关的方法呀,不管代码加载到在哪里,我都将自己拷贝到链接地址去执行。)

bootstrap的移植过程

有了上面理解的基础,移植过程变得简单。依程序为主线,主要修改board---->at91sam9m10g45ek----> at91sam9m10g45ek.c(需要实现void norflash_hw_init(void)函数,参考at91cap9adk知道就是上篇说到的配置SMC的相关寄存器值满足norflash的读写时序),board---->at91sam9m10g45ek----> norflash----> at91sam9m10g45ek.h,board---->at91sam9m10g45ek----> norflash----> Makefile。主要是修改一些参数值,只要清楚了他们代表的意思,就很容易了。

最后编译后生成的镜像下载到norflash的0地址处,上电片外引导,成功启动,调试串口输出打印信息。

(最后提一下,移植过程中,SDRAM的初始化部分没有考虑,因为开发板和项目所使用的SDRAM型号一样,且连接方式相同。不过,完整的移植是要考虑这部分的,主要也是实现at91sam9m10g45ek.c中的void ddramc_hw_init(void)函数,配置相关寄存器满足SDRAM时序)

启动的方式

对于S3C2440而言,启动的方式有两种,一是Nor Flash方式启动,二是Nand Flash方式启动。

使用Nor Flash方式启动

Nor Flash的地址范围如下

0x0000.0000—0x0800.0000 (2M Nor Flash)

片内的BootSRAM地址被置为

0x4000.0000—0x4000.DFFF (4K BootSRAM)

由于可以在Nor Flash直接运行代码,因此BootSRAM被映射到别的地址上去,可作为其他用途。

程序映像直接存放到NOR FLASH里面,中断向量表存放在0x0000.0000开始的8×4大小的空间中。

中断产生时,PC被置为相对应的向量地址。如上电或者按Reset键时,PC直接置为0x00,从NOR FLASH的0x00处开始执行。

使用NAND Flash方式启动

此时,片内的BootSRAM地址被置为

0x0000.0000—0x0800.0000 (4K BootSRAM)

NAND Flash地址接NFCE

由于NAND Flash中不能运行代码,因此必须复制到内存之中再运行。

程序映像存放在NAND FLASH中,中断向量表位置在程序映像的最前面。由于NAND FLASH不能运行代码,系统上电或者Reset的时候,内置的NAND FLASH 将访问控制接口,并将中断向量表和引导代码自动加载到内部SRAM(此时该SRAM 定位于起始地址空间0x00000000,容量为4KB),并且置PC值为0x00运行程序(这一切是有芯片内部的硬件逻辑完成的)。之后,SRAM 中的引导程序将操作系统镜像加载到SDRAM 中,操作系统就能够在SDRAM 中运行。启动完毕,4KB 的启动SRAM 就可以用于其他用途。

中断向量表的设置

ARM中的异常与中断总共有7种

按响应优先级从高到低

按中断向量表顺序

复位

复位

数据中止

未定义指令中断

FIQ

SWI

IRQ

预取指令中止

预取指令中止

数据中止异常

未定义指令、SWI

IRQ

 

FIQ

ARM要求中断向量表必须放置在从0地址开始,连续8×4字节的空间内(ARM720T和ARM9、ARM10也支持从0xFFFF0000开始的高地址向量表),各异常和中断向量在向量表中的位置如下

地址

中断

0x00

Reset

0x04

Undef

0x08

SWI

0x0C

Prefetch Abort

0x10

Data Abort

0x14

(Reserved)

0x18

IRQ

0x2C

FIQ

当中断产生时ARM处理器强制把PC指针置为中断向量表中相对应的向量地址。因为每个中断向量在向量表中只有一个字节的存储空间,只能存放一条指令,所以通常存放跳转指令,使程序跳转到存储器的其他地方,再执行中断处理。

中断向量表的实现程序通常如下

AREA Boot,CODE,READONLY

ENTRY

B Reset_Handler ; Reset_Handler is a label

B Undef_Handler

B SWI_Handler

B PreAbort_Handler

B DataAbort_Handler

B ;for reserved interrupt, stop here

B IRQ_Handler

B FIQ_Handler

其中的关键字ENTRY是指定编译器保留这段代码,因为编译器可能会认为这段代码是冗余代码,将其优化。链接的时候要确保这段代码被连接到0地址处,并且 作为整个程序的入口点(ENTRY并非总是用来设置程序的入口点的,所以通常需要在链接选项里面显式的设置程序入口点)。

AT91SAM9G45上电启动和bootstrap移植过程相关推荐

  1. U-Boot 之三 U-Boot 源码文件解析及移植过程详解

      在之前的博文 Linux 之八 完整嵌入式 Linux 环境介绍及搭建说明 中我们说了要一步步搭建整个嵌入式 Linux 运行环境.我所使用的硬件平台及整个要搭建的嵌入式 Linux 环境见博文 ...

  2. RTL8821CS移植过程记录

    RK3308B+RTL8821CS移植 在rk3308b平台移植rtl8821cs,rlt8821cs是wifi+bt一体的模组,主要记录下移植过程中需要注意的地方 移植驱动 将rtl8821cs的驱 ...

  3. 全志h3linux移植教程,全志H3启动分析,移植主线UBOOT

    全志H3启动分析,移植主线UBOOT 参考资源 启动流程 因为使用的是外扩SD卡,因此主要参考了这部分内容:Bootable SD card SD卡Layout如下: start sector siz ...

  4. 嵌入式linux python移植过程_嵌入式linux项目开发(一)——BOA移植

    嵌入式linux项目开发(一)--BOA移植 项目目标:使用BOA.CGIC.SQLite搭建嵌入式web服务器 一.嵌入式web服务器BOA简介 在嵌入式设备的管理与交互中,基于Web方式的应用成为 ...

  5. Arm芯片上电启动流程剖解

    关键字: stepping stone:(可以看做是Internal RAM) IROM (Internal ROM):固化在CPU内部ROM里的一段代码,它的运行叫做BL0. IRAM: 因为IRO ...

  6. omapl138移植uboot系列之启动TI官方移植的Linux内核(启动内核第一篇)

    启动TI官方移植的Linux内核 和编译uboot一样,Linux内核的编译也是三板斧,首先修改Makefile中的ARCH和CROSS_COMPILE,然后找到你要编译的板卡执行make xx_de ...

  7. IOT-OS之RT-Thread(二)--- CPU架构与BSP移植过程

    文章目录 一.RT-Thread内核简介 二.RT-Thread CPU架构移植 2.1 Cortex-M CPU 架构简介 2.2 RT-Thread 中断机制 2.3 CPU 架构移植 三.RT- ...

  8. NDK/JNI demo ( 五 ) ORB_SLAM2在Android上的移植过程

    Android平台搭建和NDK环境配置 Android移植基础 NDK是集成的Android中调用C++代码的工具包,核心是JNI(Java Native Interface)技术,具体这里略过不表. ...

  9. 随机森林中的Bootstrap抽样是有放回抽样么?Bootstrap抽样过程描述一下

    随机森林中的Bootstrap抽样是有放回抽样么?Bootstrap抽样过程描述一下 Bootstrap方法:每当选中一个元组,这个元组同样也可能再次被选中并再次添加到训练集中.例如,想象一台从训练集 ...

最新文章

  1. 提示内存不足,其它服务不能运行
  2. python import caffe失败的可能原因
  3. SQL Server 6.5 如何升级到SQL Server 2000—以前原创(二)
  4. Python装饰器与面向切面编程
  5. VTK:Utilities之DenseArrayRange
  6. 使用腾讯开发平台获取QQ用户数据资料
  7. bzoj3732-Network【Kruskal重构树模板】
  8. OpenShift 4 - 在集群节点用crictl对Pod/Image/Container进行操作
  9. 计算机表格乘法表,教你用Excel制作乘法表,方法奉上
  10. 在学习时,遇到in module ssbuild. File is included in 4 contexts
  11. md5加密算法使用流程
  12. 复联3观影指南丨漫威宇宙里的AI黑科技
  13. 达梦数据库基础优化分享
  14. 椭球面的c语言方程,椭球面
  15. 用EasySysprep封装Win7系统
  16. IDM+百度网盘提速下载复活版
  17. 操作系统P/V操作(V操作中的典型理解偏差)
  18. 穆迪收购风险与合规情报、数据和软件领域的领导者RDC
  19. GIONEE A1 金立A1 root 刷机包 GIONEE SWW1609_0201 mt6755
  20. 天池O2O优惠券使用预测

热门文章

  1. PostgreSQL与es的数据同步方案调研
  2. 图像美妆算法---自动祛斑算法研究
  3. Mac开发狮子连弹第一弹
  4. python+django上传图片到微信
  5. Invoke类型的已垃圾回收委托进行了回调
  6. python 扯线木偶_GitHub - huma008/puppet: 简单的股票程序化交易系统。核心模块基于同花顺和通达信金融终端。用户交流群:624585416...
  7. Java项目中数据库从mysql 5.7更换到mysql8.0.18版本
  8. 【burpsuite安全练兵场-服务端7】访问控制漏洞和权限提升-11个实验(全)
  9. Android代码故事第一回,平均间隔的按钮
  10. android 高仿华为充电动画