完成最初u-boot的编写u-boot_laifu文件夹有如下文件:
start.S(一定记得大写,大写的.S能够被GCC预编译)
start.lds
main.c main.h type.h
Makefile(记得Makefile中)
放进虚拟街编译中途遇到各种问题,编译器版本不对以及Vim编辑器中出现^M:
编译器版本原来是4.3.3,改成3.4.5版本,试过多种方法,最终办法如下:
在/etc/profile中添加export PATH=路径:#PATH(到bin为止即可)
然后控制台输入arm-linux-gcc -v检查一下;
Vim编辑器中出现^M的问题,我前面的博客中有解决方法。
排除以上错误,居然报以下错误,经过修改代码测试,极有可能是这个版本的编译器不支持移位指令
上面的移位指令都是出现在start.S中nand代码搬移中,初步想法用除法或乘法指令替代,经过考虑不太合适,
所以还是用汇编调用一个nand代码搬移函数比较靠谱,于是乎明天将这部分汇编代码用一个子函数实现,
今天有点累了,看看新闻回去睡jiao。。。明天从电脑桌面u-boot_laifu开始
按照昨天所想,将nand代码搬移部分用C实现,起初我将这个函数放在main.c中,函数代码如下:(宏定义在main.h中)
#define ULCON0 (*((volatile _u32 *)0x50000000)) /* 琛屾帶鍒?*/
#define UCON0 (*((volatile _u32 *)0x50000004)) /* 涓插彛鎺у埗 */
#define UFCON0 (*((volatile _u32 *)0x50000008)) /* FIFO妯″紡鎺у埗 */
#define UMCON0 (*((volatile _u32 *)0x5000000C)) /* 妯″紡鎺у埗 */
#define UBRDIV0 (*((volatile _u32 *)0x50000028)) /* 娉㈢壒鐜囪缃?*/
#define UTXH0 (*((volatile u8 *)0x50000020)) /* 鍙戦€佸瓧鑺傜紦鍐?*/
#define URXH0 (*((volatile u8 *)0x50000024)) /* 鎺ュ彈瀛楄妭缂撳啿 */
#define UTRSTAT0 (*((volatile _u32 *)0x50000010)) /* 鍙戦€佺姸鎬?*/
int nand_move_read(unsigned long start_addr, unsigned char *buf, int size)
{
int i, j,k;
char dat;
if ((start_addr & 2047) || (size & 2047))
{
return -1; /* 鍦板潃鎴栭暱搴︿笉瀵归綈 */
}
NFCONT&= ~(1<<1);
for(i=start_addr; i < (start_addr + size);)
{
/* Check Bad Block */
int col, page;
col = i & 2047;
page = i / 2048;
/* 鍙戝嚭READ0鍛戒护 */
NFCMMD = 0x00;
NFADDR = 5;
for(j=0; j<10; j++);
NFADDR = 8;
for(j=0; j<10; j++);
NFADDR = page & 0xff; /* Row Address A12~A19 */
for(j=0; j<10; j++);
NFADDR = (page >> 8) & 0xff; /* Row Address A20~A27 */
for(j=0; j<10; j++);
NFADDR = (page >> 16) & 0x03; /* Row Address A28~A29 */
for(j=0; j<10; j++);
NFCMMD = 0x30;
while(!(NFSTAT & 1))
for(k=0; k<10; k++);
dat = NFDATA;
/* 鍙栨秷鐗囬€変俊鍙?*/
if(dat != 0xff)
i += 131072; // 1 Block = 2048*64= 131072
/* Read Page */
/* 閫変腑鑺墖 */
NFCONT |= (1<<1);
/* 鍙戝嚭READ0鍛戒护 */
NFCMMD = 0x00;
/* Write Address */
col = i& 2047;
page = i/ 2048;
NFADDR = col & 0xff; /* Column Address A0~A7 */
for(i=0; i<10; i++);
NFADDR = (col >> 8) & 0x0f; /* Column Address A8~A11 */
for(i=0; i<10; i++);
NFADDR = page & 0xff; /* Row Address A12~A19 */
for(i=0; i<10; i++);
NFADDR = (page >> 8) & 0xff; /* Row Address A20~A27 */
for(i=0; i<10; i++);
NFADDR = (page >> 16) & 0x03; /* Row Address A28~A29 */
for(i=0; i<10; i++);
NFCMMD = 0x30;
while(!(NFSTAT & 1))
for(k=0;k<10; k++);
for(j=0; j < 2048; j++, i++)
{
*buf = NFDATA;
buf++;
}
}
}
编译报错_u32找不到,于是我就在标准u_boot中找到一个type.h的头文件,放入u_boot_laifu文件夹,错误解决;
后面感觉start.S调用的初始化代码放在main.c中结构有点乱,于是新建一个low_init.c文件存放这个函数,并且对start.lds修改如下:
.text :
{
start.o (.text)
<span style="color:#ff0000;">low_init.o (.text)
</span> * (.text)
}
make boot后生成.bin文件加载到开发板NAND中,然后从NAND启动,串口无打印信息;于是我用观察LED显示来进行调试,在各处添加对LED的操作,发现程序在start.S中按照正常顺序走,但是在main.c函数最开始添加LED操作,LED显示有时正确有时不正确;查看boot.dis反汇编文件
程序跳到33d00xxx(没做记录只能XXX了)不是main程序开始地址标号,于是思考ldr pc,=dara_main指令正确性,实在看不懂怎就乱跳了(往后可以深入思考该指令),于是改成b dara_main,再看boot.dis文件显示能够正常跳到主函数,于是进行LED测试,结果只是比前面正确的几率打了一些,但是有时LED显示还是不正确。
在以上操作的同时我还发现一个让我费解的问题
以上反汇编对应汇编指令是
ldr r0,=0x0 /* code in nand flash or nor flash begin addr */
ldr r1,=_code_start /* code in sdmem begin addr */
ldr r2,=_bss_start /* code size */
ldr r3,=_code_start
sub r2,r2,r3
beq _is_nand_start
感觉在start.lds中定义的_code_start等段首尾地址冒似不是什么0x33d00288之类的,接下来我去查看标准u-boot的引用方法,改成如下:
start.lds中一个下划线的都改成两个下划线变量,进行编译,无错误,下载到开发板还是没能解决,看看反汇编文件没什么变化(这个问题值得思考)。
想着用Keil MDK或者AXD去调试u-boot,我之前有用过AXD去调试的,但是除了要生成u-boot.bin还有生成一个u-boot文件,我去标准代码中Makefile寻找u-boot生成的方法,由于太复杂了,完全没想法;网上有一种MDK的方法,但是不太好弄;到了饭点了,打算吃完饭再来。当我饭后回来,用LED灯测试程序是否到达main函数,LED却显示正确(经过多次开关电源,验证时正确的),真心无语了;
上述调试过后,程序能到达main函数,但是串口无法正确输出;。。。实验室蚊子太多了,回去洗澡去。。。明天从串口的初始化开始测。
串口调试开始,检查串口初始化函数
对照裸奔程序串口的初始化函数进行初始化,Jlink仿真裸奔程序得到UBRDIV0中PCLK=50000000,加载程序仍无正确输出;发现没有检查GPH的初始化,更改后还是不对;
仔细想了想,发现前面测试的时候程序有跑飞现象,于是在初始化里面加入LED代码测试程序走向,结果程序并没有到达初始化程序,极有可能是堆栈没设置正确;于是回去
查看start.S中堆栈的设置,发现在堆栈设置前,调用了一次C函数
所以我把堆栈的设置剪切到这次C函数调用的前面,加载镜像后,显示正确
上面的ab我用的是uart_put_char(u8 c)函数逐个显示,我接下来加入以下函数进行字符串显示
void uart_puts(char *str){
while(*str)
uart_put_char(*str++);
}
但是显示不对,我去查看裸跑的字符串输出函数
void Uart_Printf(char *fmt,...)
{
va_list ap;
char string[256];
va_start(ap,fmt);
vsprintf(string,fmt,ap);
Uart_SendString(string);
va_end(ap);
}
用到了链表操作,我的uart_puts(char *str)只是等同Uart_SendString(string);函数的功能
void Uart_SendString(char *pt)
{
while(*pt)
Uart_SendByte(*pt++);
}
所以我猜测,很可能是因为没有对字符串地址区域进行分配.
加入都字符的操作文件common.c和common.h,直接使用里面的printf()函数打印字符串,报错误:对ARM的求于和除法不支持
int modfun(int value,int weight) //value mod weight
{
while(value >= weight)
value -= weight;
return value;
}
int divfun(int value,int weight) //value / weight
{
int ret = 0;
while(value >= weight)
{
value -= weight;
ret++;
}
return ret;
}
void itoa(int value,char *buf){
if((value >> 31) & 0x1){ /* neg num */
*buf++ = '-';
value = ~value + 1;
}
char tmp_buf[10] = {0}, *tbp = tmp_buf;
do{
// *tbp++ = ('0' + (char)(<span style="color:#ff0000;">value % 10</span>));
*tbp++ = ('0' + (char)(modfun(value,10)));
// <span style="color:#ff0000;">value /= 10;
</span> value = divfun(value,10);
}while(value);
while(tmp_buf != tbp--)
*buf++ = *tbp;
}
以上程序是进行修改后的modfun()和divfun()是我自己添加的用来替代红色部分代码,编译后通过,下载到板子,程序跑飞,将LED显示放在堆栈初始化之前运行正确,但是只要放在堆栈初始化之后LED就闪烁,经过多次试探,结果发现把 b dara_main 改成 ldr pc ,=dara_main就不闪了,一直跑到ldr pc ,=dara_main前程序是按照正常路线下去,但是用LED测试放在dara_mian()函数的第一句却不能执行,应该是ldr pc ,=dara_main没能正常跳到dara_mian(),但是boot.dis显示是能跳到的;无奈之下将common.c和common.h去掉,加载程序,还是显示不对,将ldr pc ,=dara_main改成b dara_main后,串口能正确输出;
(注:ldr pc =dara_main可以改写成如下:
ldr pc, _start_armboot
_start_armboot: .word dara_main)
突然对地址定位代码有了新的认识
adr r0, _start /* r0 <- current position of code */
ldr r1, _TEXT_BASE /* test if we run from flash or RAM */
cmp r0, r1 /* don't reloc during debug */
beq clear_bss
ldr r2, _armboot_start
ldr r3, _bss_start
sub r2, r3, r2 /* r2 <- size of armboot */
以上代码来自TQ标准u-boot,ldr r2, _armboot_start与ldr r3, _bss_start这两个地址值应该是根据start.lds中以下红色部分决定,关于adr和ldr这两个指令比较复杂,可以参考我转载的另一篇博客,专门讲解着两个指令。
ENTRY(_start)
SECTIONS
{
<span style="color:#ff0000;">. = 0x00000000;
</span>
. = ALIGN(4);
总结以上现象,肯定是加了common.c后程序跑飞了,可能是对于函数编译或者是调用导致堆栈问题,不能跳到主函数入口;还有可能是因为增加common.c后镜像超过4KB,而且NAND代码搬移没能成功的问题体现出来;
接下来,查看boot.dis反汇编文件,dara_main函数的起始地址如下:
0x33d01164-0x33d00000=0x1164大于4KB,我把common.c代码屏蔽掉一部分,再编译
0xc7c小于4KB,编译加载,串口显示正确,以上分析,极有可能是NAND代码没有正确搬移或者搬移后没能正确跳转。。。
折腾一天了,在网上查找硬件调试方法,先是想用MDK调试u-boot,不太可行;然后想用GDB更加折腾;想想以前用的AXD看看怎么样,找到以前那个教程(我前面的博客中有教程),谁知道一运行就可以调试了,连命令都不用输入;记得以前要输几个命令加载才行,原来是我里面已经有代码了,打开AXD就直接跳到第一条指令;真的是忙活了半天,都快到放弃的地步了,柳暗花明又来了一春,而且还支持断点调试(爽啊);
调的过程中发现一个奇怪的现象,如下图,是我给一个区域的控制寄存器赋值得到如下地址内容重复出现的现象:
每隔0X00000040重复一次,一直延续到0x48ffffff(如下图),很明显能知道原因是局部译码引起的地址重叠,所以S3C2440A内部控制器应该是局部译码。
接着往下调试,发现程序在以下代码执行了return -1;由于我没有对size处理导致的,但是该够之后还是不行
继续一步步调试,发现程序在从0x00000000拷贝到0x33d00000时,0x33d00000有数据一个一个过来,但是和0x00000000开始的数据不一致,如下图(0x00000000的数据忘记截图了)
而后笔者调试TQ的u-boot进行比较,如下图所示,发现拷贝时成功的
以上就是程序问题之所在,下一步该对搬移函数进行调试,不知不觉又犯困了(每次有点小发现后的习惯),明天continue。。。
在实验室堵了一个早上,单步单步的调,按照时序一步一步来,没找到时序有错误,但是就是读取搬移数据的时候就是错误的数据,于是我就先把TQ的u-boot和我的u-boot调试过程中的存储器和时钟电源的控制寄存器值进行比较如下(上面是我的u-boot,下面是TQ的u-boot)一模一样:
存储器控制寄存器:
时钟电源控制寄存器:
下图是在nand赋值地址之前nand控制寄存器的值(调试的过程中没有发现时序的不对):
没辙了!!!怕是CP15可能有影响,后面吧CP15的初始化也改成和TQ的u-boot一样,还是不出结果,好吧,只好用它的nand搬移函数了,程序一改掉之后马上又输出了;
其中改的时候报了一个语言性的错误error:macro names must be identifiers和error:syntax error before “typedef”,我的错误语句如下:
typedef unsigned char u8
typedef unsigned short u16
typedef unsigned int u32
typedef unsigned long long u64
语句后面没加分号报错,加了之后就对了,typedef是关键字不是宏。
ab/r/n************************************************************
* *
* Kiss By Dara! *
* *
* 鈹屸攢鈹? 鈹€鈹? *
* 銆€鈹?鈹?/ / *
* 銆€鈹?鈹? / *
* 銆€鈹?/ /-鈹攢鈹? *
* 銆€鈹?鈹?| 鈹?鈹? Dara_Boot V1.0 *
* 鈹屸敶鈹€鈹粹攢鈹?鈹樷攢鈹? *
* 鈹?鈹屸攢鈹€鈹? 鈹? *
* 鈹斺攼 鈹屸敇 *
* 銆€鈹斺攼 鈹屸敇 *
* 銆€ 锛糭 _/ *
* *
* by czysocket_dara *
************************************************************
以上是我串口字符串输出的结构,英文是正确的,乱码部分是我摆的一个“剪刀手”的造型,由于可能是GCC库不完善导致一些符号不能显示,总归是可以输出字符串。
至于为什么我的搬移函数就是不能输出,现在不想花时间去调试了,其实是有方法可以一定可以调出来,只是个时间问题而已(从它的搬移函数上一句一句的改成之前不出结果的代码,一定要一句一句的改,改到哪一句时从有输出变成没有输出了,错误的源头就找到了,不过我不想这么折腾了,以后什么时候心血来潮了可以试试)。
至此,u-boot第一阶段的代码就告一段落了。其实已经好困了,无奈还要写一篇关于党的“实践先锋”文摘,硬着顶完回去睡觉吧!!!