基于uboot的2410调试平台的实现

发布时间:2010-4-1 17:31    发布者:lelee007
关键词: uboot , 2410
--免烧写nand flash & 不用仿真

哈哈,平台!

名字起的有点大,列位看官莫笑。这年头,就那么回事。

如果内容对您有什么用或者帮助,鄙人甚感欣慰;如果没什么大用,首先,您水平肯定在鄙人之上,其次,就当是看闲书了,虽然笔者笔风不是那么诙谐。

其实这个东西对于熟悉ARM的人来说,真不是什么难事,太小case了。确实,笔者自己也感觉不过是雕虫小技而已。但是为什么要写出来了?!因为笔者也是从51转到ARM上来的,开始的时候对这个问题困惑了很久,而且就是找不到答案。同时总结出一个问题,不要迷信网络!网上并非要什么有什么!!!RMB,很明显无法从网上找到。如果从网上实在找不到答案,那么潜心研究研究,可能比网上“盲目”乱找收获更大。这里之所以给“盲目”二字打上个小引号是因为,这里的盲目并非指您不知道要找什么而去找,而是指您不知道网上到底存不存在您要找的东东,就比如笔者最近总是时不时在网上找关于X11R7.5下ATI显卡驱动的解决方案,其实AMD官方都没给出答案,民间又怎么折腾了?如果没有,您还要浪费时间跟那死找,..........不过经过笔者本次撰述,您就可以在网上找到答案了,KAKA~~~~~~~

其实要笔者写点东西也确实难,笔者自己也知道废话多,但是木有办法,真正内容就那么点,不扯点废话,湊不齐篇章。

废话到此为止,下面言归正传。

关于这个东东,开始的时候,笔者是因为木有钱买仿真机,而且被一遍一遍的烧写nand flash折腾的很烦躁,因为nand flash的烧写速度并不像下载到SRAM或者SDRAM里边那么快。而且相当nand的寿命有限,烧写有风险,每次都是heart hard-beating下完成的,生怕nand挂了或者CPU挂了,sigh......生亦何哀,死亦何苦。有痛如斯,夫复何求?!无奈当时对于ARM的MMU还不是很熟悉,而且当时是一边上班一边业余折腾,遇到问题了就有点躁。痛定思痛,长痛不如短痛!咬着牙花了一晚上把MMU看了两遍,结果发现有好几种配置方式,让人抓狂哇!哈哈,想想当时真的很傻很天真,就因为有多种配置方式,段式,页式,页式还分个粗细,就不知道到底该用哪个更合适,后来想到linux下用哪个方式咱就用哪个方式,然后抱着这个想法去看linux内核代码,结果不了了之--没看明白,HOHO~~~~~~~~不过后来是在一个关于ARM MMU的例程中找到了定心丸,就用段式映射,这个最简单!当时还不知道看SAMSUNG的代码,很多代码都是网上杂七杂八搜罗过来的。原理弄明白,方案定下来之后,事情就好办多了,一步一步实施就是了,无非是代码出问题了再调试。

废话是不是有点多?不过笔者应该是把事情的背景都交代的一清二楚了。接下来就是具体的方案了,请各位看官务必擦亮眼睛,精彩不容错过!

原理其实是这样的,首先移植一个可以用的uboot,至少要包含tftp和go命令,然后将其烧到nand flash里边,每次系统上电的时候能顺利运行uboot;然后我们将编译链接好的目标代码通过uboot下载到SDRAM里边,再从uboot里边go到我们自己的程序去运行。
实施过程中遇到的几个问题如下:

1、代码的存储位置和运行位置的问题
2、中断向量表的位置问题
3、中断入口配置

第一个问题中关于两个位置的问题,这应该是连接器要处理的,这个问题不是这里的阐述重点,有兴趣的可以参考《arm学习报告》系列文档,里边基本讲的非常详细,而且不像GNU Ld那么长篇大论。虽然这个问题不是咱的重点,但是多少对咱是有影响的,不然.............讲讲到底怎么影响咱的是正事,废话就不扯了,嘿嘿,因为,废话已经扯了很多了,GAGA~~~~~~~~~。因为从原理上来看,我们自己编写的程序用这种方式来调试的话,就不可能再放到0地址开始,让系统自动加载了,因此存储地址和运行地址都不能直接用默认的0了,这个地址需要我们在链接脚本里边亲自指定一下。为了节省大家时间,笔者在尼度给俩例子吧,一个是源代码里边的链接脚本文件,一个是链接脚本的书写规则。

SECTIONS {
.text
0x30004000
:
{
head.o
clock.o
init.o
led.o
serial.o
timer0.o
mmu.o
interrupt.o
main.o
}
}

这个是链接脚本,其中的 0x30004000地址前面的text是指如下内容全是文本段。关于文本段如果您不想看别的资料,就简单的理解成是代码段吧。实际也是代码的运行地址,更确切的说是运行地址的开始,就是我们目标代码的入口地址。链接以后,程序在执行时的一些相对跳转中,这个地址就是个基地址了。如果在把程序从别的介质加载到运行内存(SDRAM)时,地址发生了错误,有些程序就无法正常执行,这就是位置相关和位置无关代码的区别。

下面是我注释的一个lds文件的书写规则,估计大多数看官都能很快看明白,当然,能把lds的书写规则系统的研究研究,绝对是大有裨益。

链接脚本规则.JPG

可能以上内容讲的不够详细,但是木有办法,如果这些东西对于您理解这些东西来说还不够的话,那么强烈建议您认真阅读下《arm学习报告》系列文档,共有3篇,《arm学习报告001》、《arm学习报告002》、《arm学习报告003》,因为那个里边对这个分析的是比较到位的,而且篇幅并不大,绝对值得品味的,笔者也就不再好意思再搁这废话了。

之所以讲以上关于链接的问题,就是因为我们的程序最后不可能放到0地址,然系统一上电就自动去运行,而是要放到SDRAM里边去,然后从uboot里边go过去。

如果是一个非常简单的程序,不涉及中断的,那么只讲讲上面的内容,加上笔者推荐的几篇文档,差不多足够了。但是这样玩起来就太没意思了,只够点个流水灯而已!如果加上中断,那差不多就把ARM的体系全弄明白了吧。接下来就切入ARM的中断。
ARM的中断体系实际上也不复杂,向量中断一共就那么8个,reset一个,几乎是个CPU都会有这玩意,就像男人的撒尿工具。然后就是什么未定义的一个,软中断,预取终止,数据终止,中间还有个未使用的,后面就生外部中断和快中断了。这8个里边我们用到最多的也就是reset、外部中断这两个,连快中断都比较少用。

reset就是一个入口,CPU在上电的时候先找她!如果您有什么工作希望CPU在上电之后就做的话,您也找她!

外部中断的入口用处是非常强大的,因为一切外部中断源都要经过他。2410上的外部中断实际上是这样安排的,首先在系统的向量中断中安排了外部中断这一级,然后在外部中断中又安排了下一级的中断表,这一级就不再是向量式的了,但是这一级的中断入口都是隔4个字节放置一个,即每个入口的地址用4个字节来描述。这就是2410的二级中断表。第二级的中断表其实每个地址也是固定对应一个中断源的。算了,还是废话少说,上代码。

_start:
@ 0x00: 中断向量表并非从0地址开始放置,因为我们使用的直接SDRAM调试时,中断入口是需要通过MMU来映射的
@Reset:
b
Reset
@直接在SDRAM中调试的话,实际是不使用Reset的,因此一Reset,硬件系统将从nand flash中读取uboot来执行,所以此处实际是个空语句处理
@ 0x04: Undefined instruction exception
HandleUndef:
b
HandleUndef
@ 0x08: Software interrupt exception
HandleSWI:
b
HandleSWI
@ 0x0c: Prefetch Abort (Instruction Fetch Memory Abort)
HandlePrefetchAbort:
b
HandlePrefetchAbort
@ 0x10: Data Access Memory Abort
HandleDataAbort:
b
HandleDataAbort
@ 0x14: Not used
HandleNotUsed:
b
HandleNotUsed
@ 0x18: IRQ(Interrupt Request) exception
ldr
pc,HandleIRQAddr
@
ldr
pc,=HandleIRQ
@ 0x1c: FIQ(Fast Interrupt Request) exception
HandleFIQ:
b
HandleFIQ

这几行是最基本的,不用讲也该明白。

lelee-debug.tar

29.51 KB, 下载积分: 积分 -1

本文地址:https://www.eechina.com/thread-10096-1-1.html     【打印本页】

本站部分文章为转载或网友发布,目的在于传递和分享信息,并不代表本网赞同其观点和对其真实性负责;文章版权归原作者及原出处所有,如涉及作品内容、版权和其它问题,我们将根据著作权人的要求,第一时间更正或删除。
lelee007 发表于 2010-4-1 17:31:32
接下来就是咱们的二级中断表了。

        HandleEINT0:
       .long      0
       HandleEINT1:
       .long      0
       HandleEINT2:
       .long      0
       HandleEINT3:
       .long      0
       HandleEINT4_7:
       .long      0
       HandleEINT8_23:
       .long      0
       HandleRSV6:
       .long      0
       HandleBATFLT:
       .long      0
       HandleTICK:
       .long      0
       HandleWDT:
       .long      0
       HandleTIMER0:
@   .long      Timer0_Handle
       .long      0
       HandleTIMER1:
       .long      0
       HandleTIMER2:
       .long      0
       HandleTIMER3:
       .long      0
       HandleTIMER4:
       .long      0
       HandleUART2:
       .long      0
       HandleLCD:
       .long      0
       HandleDMA0:
       .long      0
       HandleDMA1:
       .long      0
       HandleDMA2:
       .long      0
       HandleDMA3:
       .long      0
       HandleMMC:
       .long      0
       HandleSPI0:
       .long      0
       HandleUART1:
       .long      0
       HandleRSV24:
       .long      0
       HandleUSBD:
       .long      0
       HandleUSBH:
       .long      0
       HandleIIC:
       .long      0
       HandleUART0:
       .long      0
       HandleSPI1:
       .long      0
       HandleRTC:
       .long      0
       HandleADC:
       .long      0

这个表实际上仅仅是声明了一个long型的量,先占上4个字节,具体的内容将由我们自己来填写。填写什么内容好了?!当然是对应的中断的处理函数了。从标号可以看出来,这些是用来处理中断的,你说不填写中断处理函数,填什么了?!至于中断函数怎么填,这个是C语言的问题了。

光有这俩表是远远不够的。因为还有很多问题没解决。首先,表放到那儿?其次,中断来了怎么找这个表?其实这俩问题合起来也算是一个问题,系统如果知道表在哪儿了,那肯定能找到;但是也不一定就真的只是一个问题,如果你放的位置不正确,那么就算系统知道在哪儿,他如果跑不过去,那也不算找到了!

表放哪儿的问题很好解决,2410手册里有讲,向量中断表只有两种放法,低地址和高地址!低地址就是从0开始存放,高地址就是从0XFFFF0000开始存放。这个是通过配置协处理器寄存器实现的。有两种放法,有两种放法?有两种放法?!倒塌!!!哪种放法好?哪种更合适?到底怎么放好啊~~~~~~~~~~~~~别急,看需求,看习惯。如果你不启用MMU,高端是没法用的,因为你的系统肯定不会在高端地址处配置一个ROM。那就只能用低端了呗。如此以来,可以把表放到2410的小石头里边去了。看来不开启MMU也挺好,就那么一种选择,省去不少事。不过笔者这里要介绍的是开启MMU之后的用法。当然开启MMU之后就比这个灵活多了,您想用高端用高端,想用低端用低端。只需要将您选定的地址映射到物理内存中就O了,比如咱想放低端,就是0开始的地址,那咱用MMU把逻辑0地址给映射到SDRAM的0X30F00000,那么在程序启动之后就把中断表copy到0X30F00000处,然后配置MMU映射表,最后开启MMU,O了!放高端的话,以此类推。当然具体地址咱可以随便定,但是也不能太随便了,免得自己给自己找麻烦。MMU映射表的代码可以在附件中找到,就是MMU.c中。Copy中断表的代码这里可以列出来

void       copy_vectors_to_high()//by lelee,用于SDRAM调试的时候,copy中断向量表,实际是从SDRAM里copy,而不再是从nand copy 了

{
       unsigned int i = 0;      
       for(i = 0;i < 128;i++){
              (*(unsigned int *)(0x33ff0000 + (i << 2)))=(*(unsigned int *)(0x30004000 + (i << 2)));
       }
}

就这么简单,中断表搞定了。

最后一个问题就是中断入口的配置问题了。这里是外部中断扩展出来的真正的外部中断源的入口配置问题,这个很关键。中断处理流程请查找2410手册,不过这里可以简略介绍,省去您找手册的麻烦。

HandleIRQ:
       sub  lr, lr, #4         @计算返回地址
       stmdb    sp!,        { r0-r12,lr }  @保存使用到的寄存器
@added from a-nan
       sub  sp,sp,#4        @堆栈指针减4,空出一个单元,以便后面放入PC需要的字 reserved for PC
       stmfd     sp!,{r8-r9}           @r8,r9入栈,此时存储r8,r9的两个堆栈单元下面的一个单元是空的
@   ldr   r9,=INTOFFSET       @INTOFFSET寄存器中存放中断源号,标明是哪个中断,CHIP规定死的,相当于查表。
       ldr   r9,=0x4A000014
       ldr   r9,[r9]           @
@   ldr   r8,=HandleEINT0      @入口可以随便放,与中断向量表没有关系
       ldr   r8,=0xffff0024
       add r8,r8,r9,lsl #2       @中断号乘以4,然后加上HandleEINT0,得到的将是该中断的入口
       ldr   r8,[r8]
       str   r8,[sp,#8]             @把得到的中断向量的内容(timer0Handler的地址)放入最开始空出的堆栈单元,由于这个单元在r8,r9下面,所以位置是sp+8
       ldr   lr,    =int_return   @设置返回地址      
      ldmfd    sp!,{r8-r9,pc}      @把堆栈顶部的三个单元分别出栈到r8,r9,和pc,此时pc会跳转到中断向量里存储的地址,也就是timer0Handler
@added from a-nan
@   ldr   lr,    =int_return   @设置返回地址      
@   ldr   pc, =Timer0_Handle @调用中断处理函数,在interrupt.c中
int_return:
       ldmia     sp!,        { r0-r12,pc }^      @中断返回, ^表示将spsr的值复制到cpsr

我的解释看明白了没有?嘿嘿

实际上外部中断是用向量中断里的一个中断来扩展的,然后有用于扩展的寄存器,比如 INTOFFSET,里边的10进制数表明了是顺序为多少的中断发生了,外部中断来了之后,先读该寄存器,然后判断往哪儿跳。跳的过程就是先把 INTOFFSET读到r9里边去,然后把二级表的开始地址读到r8里边去,接着将r9里边的值乘以4再加上r8里边的值,结果还是放到r8中,r8中的值就是我们最后要跳转的目的地址,然后压栈,跳转就是靠弹栈实现的,将r8的值弹到pc里边去,下一条指令的时候,就跳到r8的值指定的地址进行取指译码之类的操作了。后面的几条指令就是设置中断返回地址了。

最后再废话一点点,关于二级表的位置。void     copy_vectors_to_high()这个函数里边实现了将向量中断表和二级中断表一起copy到高端地址。在copy的时候,MMU木有开启,中断也给屏蔽了,所以看代码可以看出,copy过程中使用的地址都是物理地址。二级表的填充就很easy了,对应的各个表项地址都声明了对应的名字,由预定义完成的,如何填写,这也是C语言的问题。不赘述。由于二级表可以在程序中随意配置,所以也可以叫动态配置的二级中断表。比如拿代码中的timer0来作例子说事,我们可以在初始化函数中这个样子:

void Timer0_init()
{
       TCFG0 = 119;     //Prescaler0 = 119              
       TCFG1 = 0x03;   //Select MUX input for PWM Timer0:divider=16
//     TCNTB0 = 3125;       //0.5秒钟触发一次中断
//     TCNTB0 = 13020;     //0.5秒钟触发一次中断
       TCNTB0 = 26040;     //0.5秒钟触发一次中断
//     TCNTB0 = 3255;
//     TCON |=  (1<<1);     //Timer 0 manual update
       TCON = 0x02;
       TCON = 0x09;    /*Timer 0 auto reload on
                       Timer 0 output inverter off
                       清"Timer 0 manual update"
                       Timer 0 start */
//     ISR_TIMER0 + 4 = (unsigned int)Timer0_Handle;
       ISR_TIMER0 = (unsigned int)Timer0_Handle;
}

然后在 Timer0_Handle()函数中再将 ISR_TIMER0的内容给换了,比如:

void Timer0_Handle()
{
       /*   
       if(INTOFFSET == 10){
              GPBDAT = ~(GPBDAT & (0xf << 7));
       //     timer0_flag = !timer0_flag;
       }
       */
       if (timer0_flag == 1)
              {
                     timer0_flag = 0;
              }
       else
              {
                     timer0_flag = 1;
                     ISR_TIMER0 = (unsigned int)Timer0_Handle001;//动态配置中断服务程序入口
              }

       if(timer0_flag){
              //wait(100000);
              //led_red_on();
              led_grn_on();
              //wait(100000);
              led_red_off();
              //led_grn_on();
              printk("This is Timer0_Handle!!!\n\r");
       }
       else{
              led_red_on();
              led_grn_off();
       }
       //清中断
       SRCPND = 1 << INTOFFSET;
       INTPND = INTPND;      
}

差不多,就这么着,可劲的折腾吧。

最后总结一下,其实很简单,如果对编译链接理解透了,同时对MMU和2410的中断表的放置熟悉了,就足够折腾出这玩意了。有了这个,咱调试起来就方便了,新增加的程序,或者其他要使用的中断,加进来就是了。程序编译完了,通过uboot DOWN到SDRAM的0x30004000地址处,这个是链接地址,程序的入口在这里,入了口之后,此地址之前的16K空间是预备后来初始化MMU时存放映射表的。因为要16K的空间存放映射表,所以程序入口选择放在SDRAM的16K地址之后开始存放。SDRAM的开始地址是0X30000000。DOWN完之后再从uboot里边go 0x30004000,好了,接下来就不再是运行uboot了,运行的就是咱自己的程序了,跟uboot半点关系都木有了。这么下来,是不是发现很简单?!确实是雕虫小技。

附件中是相关代码,其中有readme.txt,里边有告诉怎么操作。如果感兴趣又没全看明白的,给俺发邮件,最好是搁公社里边白话。
lelee007 发表于 2010-4-1 17:31:58
晕翻,插入的图片位置不对头
huizijingxin 发表于 2010-4-1 17:33:05
丽丽gg牛
忘情天书 发表于 2010-4-1 17:58:49
赞一个!
f.luo 发表于 2010-4-1 18:04:00
丫丫的,原本以为3月都在几大牛人都现身了,我四月再冒泡泡,现在看来亏了!再等等!
lelee007 发表于 2010-4-1 19:19:13
有一点东西大意了,代码里边关于LED控制的GPIO

哈哈,如果板子上的配置跟俺不一样的,可得把代码仔细改改了
dddg 发表于 2010-4-1 19:27:25
   这个语文水平,有待提高...  还是肉皮语文好一些.
lelee007 发表于 2010-4-1 20:10:55
咱是抛砖引玉
linux_Ultra 发表于 2010-4-1 20:28:03
对于三星24xx这个分支uboot 一直都不用mmu和caches吧?
龙龙 发表于 2010-4-1 20:29:30
这么有技术含量的东西也放灌水菜园里~~拆迁办的呢!!!赶紧的!!{:4_83:}
f.luo 发表于 2010-4-1 20:40:35
嗯,等待拆迁办的出现。
lelee007 发表于 2010-4-1 20:42:44
uboot中,MMU是没开启,cache那部分的代码没研究过
admin 发表于 2010-4-2 10:07:28
这是lele的参赛文章?
lelee007 发表于 2010-4-2 10:51:34
恩啦,哈哈,写的比较糙,见笑了
老郭 发表于 2010-4-2 14:56:35
昨天没看到。
俺把你那些没有的空行删掉了,这样更紧凑些。图片也插到地方了。
很不错,和阿南、RP的风格不一样
lelee007 发表于 2010-4-2 15:33:21
娃哈哈,还是老郭识货哇,嘿嘿

每段的缩进没了
lelee007 发表于 2010-4-3 16:23:50
跟rp说的过了泼水节似的
f.luo 发表于 2010-4-3 16:54:35
放假嘛,大多去玩了。
lelee007 发表于 2010-4-3 16:59:28
哈哈哈哈

看看放假之后还有木有人过来翻
12下一页
您需要登录后才可以发表评论 登录 | 立即注册

厂商推荐

相关视频

关于我们  -  服务条款  -  使用指南  -  站点地图  -  友情链接  -  联系我们
电子工程网 © 版权所有   京ICP备16069177号 | 京公网安备11010502021702
快速回复 返回顶部 返回列表