每次碰上问题,都要花上很长时间去寻找答案。从上周五开始到今天,除去周末两天回家,我一直被一个blamed bug困扰。其实这个问题不能叫做bug,只是因为碰到的bug太多叫习惯了,应该称之为a mistake更贴切些。
调试了两天依然没个结果,昨天憋不住了,只好向各大论坛还有QQ群求助,却许久无人应答,无果。心不甘,于是继续奋斗ing。
具体症状如我在论坛中所述:
bootloader已经能正常工作,但是当加载内核映像时就没反应了。
连“Uncompressing Linux...”都没有出现。
一些设置:
内核2.6.14
make的是zImage
ZTEXTADDR 0x00010000
ZREALADDR 0x0C008000
ZBSSADDR 0x0C400000
把decompress_kernel改为putstr("test");后仍然不能打印出字符。
不知问题出在哪里?或者给个思路?
在这之前已经查阅了许多文章,知道了可能的原因(找不到原文出处):
在 boot loader 程序的设计与实现中,没有什么能够比从串口终端正确地收到打印信息能更令人激动了。此外,向串口终端打印信息也是一个非常重要而又有效的调试手段。但是,我们经常会碰到串口终端显示乱码或根本没有显示的问题。造成这个问题主要有两种原因:(1) boot loader 对串口的初始化设置不正确。(2) 运行在 host 端的终端仿真程序对串口的设置不正确,这包括:波特率、奇偶校验、数据位和停止位等方面的设置。此外,有时也会碰到这样的问题,那就是:在 boot loader 的运行过程中我们可以正确地向串口终端输出信息,但当 boot loader 启动内核后却无法看到内核的启动输出信息。对这一问题的原因可以从以下几个方面来考虑:
(1) 首先请确认你的内核在编译时配置了对串口终端的支持,并配置了正确的串口驱动程序。
(2) 你的 boot loader 对串口的初始化设置可能会和内核对串口的初始化设置不一致。此外,对于诸如 s3c44b0x 这样的 CPU,CPU 时钟频率的设置也会影响串口,因此如果 boot loader 和内核对其 CPU 时钟频率的设置不一致,也会使串口终端无法正确显示信息。
(3) 最后,还要确认 boot loader 所用的内核基地址必须和内核映像在编译时所用的运行基地址一致,尤其是对于 uClinux 而言。假设你的内核映像在编译时用的基地址是 0xc0008000,但你的 boot loader 却将它加载到 0xc0010000 处去执行,那么内核映像当然不能正确地执行了。 在bootloader的运行过程中已经可以正确地向串口终端输出信息,但是我的情况并不是其中所述的任何一种:
(1) 内核配置时已经设置了CONFIG_SERIAL_S3C44B0X和CONFIG_SERIAL_S3C44B0X_CONSOLE,即内核已支持串口端。
(2) 因为bootloader中已经能正常使用串口,所以内核中就没有再对串口进行初始化设置,因此不存在设置不一致的问题。而CPU的时钟频率我统一设置为60MHz。
(3) 内核尚未解压,因此该问题不用考虑。
冥思苦想……仍找不出症结所在。
把bootloader中的打印函数移到内核中,命名为test_serial,并设置head.S中start的第一句为bl test_serial,但是仍然没有字符出现。
接着分析了bootloader中串口的写入地址:
if(data=='\n') { while(!(rUTRSTAT0 & 0x2)); Delay(10); //because the slow response of hyper_terminal WrUTXH0('\r'); } while(!(rUTRSTAT0 & 0x2)); //Wait until THR is empty. Delay(5); WrUTXH0(data);其中WrUTXH0宏定义为:
#define WrUTXH0(ch) (*(volatile unsigned char *)(0x1d00023))=(unsigned char)(ch)即写入地址为0x01d00023。
而在内核中写入的地址:
文件linux-2.6.x/include/asm/arch/uncompress.c:
12 static int s3c44b0x_putc(char c) 13 { 14 while (!(SYSREG_GET(S3C44B0X_UTRSTAT0) & 0x2)); 15 SYSREG_SETB(S3C44B0X_UTXH0, c); 16 if(c == '\n') 17 s3c44b0x_putc('\r'); 18 }S3C44B0X_UTXH0在文件linux-2.6.x/include/asm/arch/s3c44b0x.h中定义:
287 #ifdef CONFIG_CPU_BIG_ENDIAN 288 #define S3C44B0X_UTXH0 0x01d00023 289 #define S3C44B0X_URXH0 0x01d00027 290 #else 291 #define S3C44B0X_UTXH0 0x01d00020 292 #define S3C44B0X_URXH0 0x01d00024 293 #endif没找到CONFIG_CPU_BIG_ENDIAN,应该是LITTLE ENDIAN,发现地址和bootloader中不一样:0x01d00020。
这就是问题所在吗?
好吧,那咱就把地址改为0x01d00023再瞧瞧:
287 #ifdef CONFIG_CPU_BIG_ENDIAN 288 #define S3C44B0X_UTXH0 0x01d00023 289 #define S3C44B0X_URXH0 0x01d00027 290 #else 291 #define S3C44B0X_UTXH0 0x01d00023 292 #define S3C44B0X_URXH0 0x01d00027 293 #endif可惜还是没有任何反应,顿时傻掉……
将要放弃前试了最后一招,对比了2.4.x中的串口写入函数:
15 static int s3c44b0_putc(char c) 16 { 17 CSR_WRITE(DEBUG_TX_BUFF_BASE, c); 18 while(!(CSR_READ(DEBUG_CHK_STAT_BASE) & DEBUG_TX_DONE_CHECK_BIT)); 19 20 if(c == '\n') 21 s3c44b0_putc('\r'); 22 }DEBUG_TX_BUFF_BASE在linux-2.4.x/include/asm/arch/hardware.h中定义:
#define DEBUG_TX_BUFF_BASE (Base_Addr+0x104020) #define DEBUG_RX_BUFF_BASE (Base_Addr+0x104024) #define DEBUG_UARTLCON_BASE (Base_Addr+0x104000) #define DEBUG_UARTCONT_BASE (Base_Addr+0x104004) #define DEBUG_UARTBRD_BASE (Base_Addr+0x104028) #define DEBUG_CHK_STAT_BASE (Base_Addr+0x104010)其中Base_Addr为(0x1c00000),故DEBUG_TX_BUFF_BASE值为0x01d04020。发现它和2.6.x中的地址一致,于是把linux-2.6.x/include/asm/arch/uncompress.c中串口写入函数改为:
12 static int s3c44b0x_putc(char c) 13 { 14 while (!(SYSREG_GET(S3C44B0X_UTRSTAT1) & 0x2)); 15 SYSREG_SETB(S3C44B0X_UTXH1, c); 16 if(c == '\n') 17 s3c44b0x_putc('\r'); 18 }
make后终于看到那行激动人心的字符:
Uncompressing Linux............................. done, booting the kernel.虽然没有成功启动内核,但总算是跳过了这一坎,往前进了一步。
庆幸自己没有放弃……
来源:http://blog.iyi.cn/hily/archives/2006/09/post_13.html