赞
踩
最近将1046CPU的boot从2017 11版本 移入到201 07的版本中,移植内容主要增加了部分单板相关的私有驱动, 目前,已经可以编译出u-boot.bin,移植过程中主要解决了一些未定义、重定义的变量和函数。
满心期待的将boot烧录到版本中,重启后凉凉了,发现串口没有任何输出。一时间懵逼了,不清楚该怎么办了,咨询了身边的几个同事,基本都没有遇到过无打印的情况,当然也都给了一些建议,逐步去尝试,最后发现问题所在,并将过程做总结,以备忘记。
由于1046CPU的boot前512位时是rcw使用的,如果这一部分不对的话,那么CPU无法启动,非常的关键,还好有移植前和移植后的boot文件,直接beyond compare对比,发现大不一样,最后跟踪编译流程,发现移植到的分支对rcw有处理,直接删掉这一步。这里也没有什么好写的,跟踪编译脚本即可。
对于串口没有输出的情况,调试起来是让人崩溃的,好在板子上有LED灯是通过GPIO控制的,移植点灯接口,init_sequence_f中的如下串口初始化相关函数已经走完了。
init_baud_rate, /* initialze baudrate settings */
serial_init, /* serial communications setup */
console_init_f, /* stage 1 init of console */
bsp_run_led_off, -----灯灭。之前init_sequence_f最开始的地方会点亮。
display_options, --正常情况下,此函数会打印出boot版本信息。
display_text_info,
经过查看配置生产的文件.config autoconf.h等文件,发现波特率配置宏定义 为115200,正确的。
该CPU的串口驱动相关文件serial_ns16500.c,serial.c,ns16500.c三个文件,通过查看配置宏以及编译生产的中间文件,确认正确。
####端口是否正确
如下,宏定义CONFIG_CONS_INDEX 是1,我也不知道是否正确,同样对比发现移植前的代码也是1的。基本可以确定正确。
__weak struct serial_device *default_serial_console(void) { #if CONFIG_CONS_INDEX == 1 return &eserial1_device; #elif CONFIG_CONS_INDEX == 2 return &eserial2_device; #elif CONFIG_CONS_INDEX == 3 return &eserial3_device; #elif CONFIG_CONS_INDEX == 4 return &eserial4_device; #elif CONFIG_CONS_INDEX == 5 return &eserial5_device; #elif CONFIG_CONS_INDEX == 6 return &eserial6_device; #else #error "Bad CONFIG_CONS_INDEX." #endif }
由于2017 11月的boot文件相对与201707的boot差别时间不是太久,但依然怀疑是不是存在问题。
由于串口初始化前的代码也不是很多,所以逐个对比分析了文件start.S cache.S lowerinit.S等arch\arm\cpu\armv8下的代码,差别不大,有差别的地方也基本得到了排除。
同时,对串口驱动相关的几个文件也做了对比,排除。
咨询了身边的同事,有的同事让量串口信号,感觉意义不大,毕竟板子是正常的,只是换了boot出现了问题。加之操作起来麻烦,也就没有搞。
串口相对而言,是一个比较简单的低速设备,协议也比较简单,输入输出对于uboot而言,无非就是读写寄存器,而且nor flash和串口初始化之后,理应正常的,不会对其他的ip有依赖(此时代码没有运行于ddr)。阅读了串口的驱动,发现串口驱动里面有封装的打印函数,见serial_ns165500.c。如下函数
serial_puts_dev(unsigned int dev_index,const char *s)
{
_serial_puts(s,dev_index);
}
本质上,uboot的printf puts debug等函数最终都要调用到这里,进行打印。
所以,将这个函数放开了,改成全局的,直接加入到init_sequence_f中尝试看能否打印。
编译版本,上板子调试,发现可以打印出来。正常了串口没有问题。
另一个同事说,需要在include/common.h中将DEBUG宏定义一下。即#define DEBUG,通过查看代码,发现该DEBUG只会影响debug接口的打印,理应不会影响到printf,而且我们原来的代码分支也没有定义这个宏。问题是printf无法打印。但是定义了该宏后,发现串口打印正常了,printf也正常,这就让我费解了。逻辑上根本解释不通。
再者说DEBUG宏放开后,打印会非常非常多,我还是不希望通过这个方式解决问题。所以,狠下心来去看printf debug puts打印接口的实现。发现最后都是调用到了puts函数。
printf的源码位于vsprintf.c中,
int printf(const char *fmt, ...) { va_list args; uint i; char printbuffer[CONFIG_SYS_PBSIZE]; va_start(args, fmt); /* * For this to work, printbuffer must be larger than * anything we ever want to print. */ i = vscnprintf(printbuffer, sizeof(printbuffer), fmt, args); va_end(args); /* Handle error */ if (i <= 0) return i; /* Print the string */ puts(printbuffer); 最后调用了puts return i; }
继续跟puts的实现,位于console.c中。
void puts(const char *s) { #ifdef CONFIG_DEBUG_UART if (!gd || !(gd->flags & GD_FLG_SERIAL_READY)) { while (*s) { int ch = *s++; printch(ch); } return; } #endif if (!gd) return; #ifdef CONFIG_CONSOLE_RECORD if ((gd->flags & GD_FLG_RECORD) && gd->console_out.start) membuff_put(&gd->console_out, s, strlen(s)); #endif #ifdef CONFIG_SILENT_CONSOLE if (gd->flags & GD_FLG_SILENT) return; #endif #ifdef CONFIG_DISABLE_CONSOLE if (gd->flags & GD_FLG_DISABLE_CONSOLE) return; #endif if (!gd->have_console) return pre_console_puts(s); if (gd->flags & GD_FLG_DEVINIT) { ####发现这一段代码备DEBUG宏控制起来了。。 /* Send to the standard output */ fputs(stdout, s); } else { /* Send directly to the handler */ pre_console_puts(s); serial_puts(s); } }
我代码合入的分支,puts函数实现被修改了,当DEBUG没有定义时,不走最后的串口输出函数,定义时才会走,至此该问题定位。。。
回顾定位过程,还是得到了不少同事的提示的,得到了很多有价值的线索。整错过程基本就是实验排除法,不挺的改代码、编译boot,上板子看结果。因为没有打印,看不到信息,除此之外,貌似没有更好的办法。
定位过程的笔记:
目前,已排查的工作如下:
1. 对比编译后的arch目录下的中间文件,两边是一致的。可以判断出和架构相关的配置是没有问题的。
1046CPU对应的串口驱动涉及到三个文件:serial.c ns16550.c serial_ns16550.c,
ns16550.c存在几行代码的不同,但是已经对比,两份代码加错误做实验,发现不一致的地方没有编译到。
serial_ns16550.c 两边代码完全一致。
serial.c有几行不一致,已经排除。
对比arch目录下关键的启动代码
start.S完全一致。
cache.S不一致,但差别主要集中在 汇编函数多了.pushsection .text.__asm_invalidate_icache_all, “ax” popsection
transition.S 差别同cache.S。
cpu.c 对比结果:
arch_early_init_r 函数实现中,201711的代码多加了 config_core_prefetch。
650行多了函数 efi_reset_system。
755行dram_init_banksize 函数存在少许不同,但只是一个打印。串口初始化时该函数应该还没有走到。
lowlevel.S基本相同,secondary_switch_to_el1函数存在几行汇编代码不一样。
soc.c 差别较大。多数都集中在USB,没有发现有何和串口相关的。
配置方面的修改:对比.config文件
1.相比较于201711,增加了CONFIG_TINY_UBOOT配置,解决未定义的问题。该宏只是在main.c应用了,不会影响串口打印。
2.相比于201711,少了两个宏CONFIG_SPL_SYS_MALLOC_F_LEN=0x400 CONFIG_TPL_SYS_MALLOC_F_LEN=0x400,代码中没有用到这个宏。
3.未发现串口相关配置的不同。
串口相关配置:
波特率:115200已确认。
__weak struct serial_device *default_serial_console(void)
{
#if CONFIG_CONS_INDEX == 1 该值两边一致,u-boot.cfg显示为1。代码加编译错误,也表明是这里。
return &eserial1_device;
}
已做实验:
1.按照建议,定义宏, #define CONFIG_NOUSB_OMIT_USB_ERRATUM,无效。
2.已尝试使用puts看能否打印出来,结果是打印不出来。
3.建议common.h加宏定义 #define DEBUG,但是通过查看代码,这个宏定义只是使用debug进行打印时才会起到控制作用。
4.
init_sequence_r
initr_serial
serial_initialize
ns16550_serial_initialize
serial_register(&eserial1_device);
最终结论:添加DEBUG宏定义,发现debug函数和puts函数可以打印出信息了。最终原因原来是这个。至于为什么,需要研究debug和puts的源码了。
另外,那么201711也是没有定义DEBUG的,为什么是好的呢?只能怀疑是201711初始化过程中没有异常,后续串口某个地方被打开了。
定位过程:
error error error bsp_run_led_off begain
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbberror error error bsp_run_led_off end
initcall sequence 0000000040079058 failed at call 000000004000cf9c (err=32768)
U-Boot 2017.11-dirty for (Feb 25 2020 - 16:41:18 +0800)
SoC: LS1046AE Rev1.0 (0x87070010)
Clock Configuration:
CPU0(A72):1800 MHz CPU1(A72):1800 MHz CPU2(A72):1800 MHz
去除的DEBUG
=> reset
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbError, wrong i2c adapter 1074432448 max 4 possible
果然,问题已定位,是那个DEBUG没有定义引起的。如果这个DEBUG宏没有定义的话,那么uboot代码初期是debug函数和puts函数均不会打印出来,
但是还是可以通过串口驱动直接打印的。 如serial_ns16550.c中的 serial_putc_dev(1, 'b');
技术支持给的建议:
1. 是否存在写CPLD导致挂死。
2. 串口的tx buf强制写入内容试试看。串口驱动,或者直接写寄存器。
3. 串口的打印,不需要其他外设初始化的,nor flash和串口初始化完即可。
4. 是否串口内容没有来得及打印,CPU就挂掉了。
依然悬而未决的问题是 DEBUG,为什么会 影响到串口的打印呢?
当DEBUG定义时,puts、debug、printf可以正常输出打印,但是又存在一个问题,为啥acpc和SFUQ的不开这个宏定义,也可以正常打印呢?
所以接下来,对puts debug printf的实现进行梳理:
#define debug(fmt, args…) debug_cond(_DEBUG, fmt, ##args)
#define debug_cond(cond, fmt, args…)
do {
if (cond)
printf(pr_fmt(fmt), ##args);
} while (0)
printf的实现:
lib/vsprintf.c
int printf(const char *fmt, ...)
{
va_list args;
uint i;
char printbuffer[CONFIG_SYS_PBSIZE];
va_start(args, fmt);
/*
* For this to work, printbuffer must be larger than
* anything we ever want to print.
*/
i = vscnprintf(printbuffer, sizeof(printbuffer), fmt, args);
va_end(args);
/* Print the string */
puts(printbuffer);
return i;
}
void puts(const char *s) common/console.c
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。