AT91SAM9G45上电启动和bootstrap移植过程

来源:互联网 发布:java构造方法是什么 编辑:程序博客网 时间:2024/05/21 10:08

出处:http://blog.sina.com.cn/s/blog_686ced250102vyml.html


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

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

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

(一)AT91SAM9G45上电启动过程

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

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

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

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

 

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

(二)AT91SAM9G45norflash启动:

主要是解决如下几个问题

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

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

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

现在的项目实际情况是,SAM-BA只支持对现有的片内SRAMDDRAM进行烧写,J-FLASH也是无法检测到使用型号的norflash,手动选择了之后又会提示ID校验错误,无法读写。但是J-LINK连接正常。所以,准备通过SAM-BA烧写代码段1DDRAM的地址1处,烧写代码段2DDRAM得地址段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的读时序图;

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

3bootstrap的移植?

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

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

bootstrap的功能

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

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

bootstrap的执行环境

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

这样看来,bootstrap开始执行前,已经做了不少工作,如果要上电从片外的norflash启动,CPU是从0状态启动的,就是说还没有执行任何的代码。这样我们要成功把bootstrap移植到norflash,就要在它之前实现boot program的部分功能,将这段代码放在norflash0地址处先于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-bootSDRAM,跳转到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源码相应目录下找到makefileboard---->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 r10,该0地址是相对地址,相对于程序的运行地址的,因为存储在norflash中,运行前也没有被拷贝到别处,所以就是当前PC值。所以这段代码就实现了自拷贝的功能,就是说如果定义了norflashbootstrap会将自己拷贝到SRAM(地址0x300000)中去。你可能已经发现了,这段代码的运行地址不等于链接器指定的链接地址,对,ctrl0_gnu.S里这段代码之前的部分都是地址无关的。拷贝完成后,跳转到_setup_clocks标号处的代码段,而标号在链接前都是个偏移量,经过链接得到绝对地址,所以这个跳转已经跳转到了LINK_ADDR offset去运行了。这一段理解很重要,也很艰难,不过理解之后的喜悦也是难以言表的。之后会发现像u-bootkernel等好多的源码中汇编部分都有自拷贝功能代码。(为什么?自拷贝也是一种实现代码运行地址无关的方法呀,不管代码加载到在哪里,我都将自己拷贝到链接地址去执行。)



bootstrap的移植过程

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

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

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


阅读全文
'); })();
0 0
原创粉丝点击
热门IT博客
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 各大网盘都关停怎么办 在路上捡了10万怎么办 上眼皮薄又松弛怎么办 孩子爱说谎怎么办12了 动铁耳机进水了怎么办 成人牙齿地包天怎么办 钉钉密码忘记了怎么办 钉钉忘记密码了怎么办 钉钉密码忘了怎么办 安卓系统中毒了怎么办 5岁晚上咳嗽厉害怎么办 微博账号被盗了怎么办 老爸对妈妈家暴怎么办 产后腰疼的厉害怎么办 生完孩子有痔疮怎么办 生完孩子后腰痛怎么办 生完小孩后腰疼怎么办 生完孩子腰酸疼怎么办 经常抱孩子腰疼怎么办 生完孩子后腰疼怎么办 被蚊虫叮咬后痒怎么办 蹲式厕所堵了怎么办 马桶堵了怎么办小窍门 锁眼被502堵了怎么办 锁眼被钥匙堵了怎么办 锁眼被堵了502怎么办 开滴滴被罚款了怎么办 野外被毒蛇咬了怎么办 在野外被蚊子咬怎么办 两个门牙摔断了怎么办 主持是搭档忘词怎么办 上学忘了穿胸罩怎么办 游戏充值被骗了怎么办 戒烟后又复吸了怎么办 6s摄像头进灰怎么办 苹果se镜头进灰怎么办 苹果6摄像头进灰怎么办 苹果手机进灰了怎么办 苹果6镜头进灰怎么办 3岁宝宝牙齿腐蚀怎么办 i.o.i解散周洁琼怎么办