u-boot201707调试LS1046CPU的板子时串口无打印

简介

最近将1046CPU的boot从2017 11版本 移入到201 07的版本中,移植内容主要增加了部分单板相关的私有驱动, 目前,已经可以编译出u-boot.bin,移植过程中主要解决了一些未定义、重定义的变量和函数。
满心期待的将boot烧录到版本中,重启后凉凉了,发现串口没有任何输出。一时间懵逼了,不清楚该怎么办了,咨询了身边的几个同事,基本都没有遇到过无打印的情况,当然也都给了一些建议,逐步去尝试,最后发现问题所在,并将过程做总结,以备忘记。

定位过程

对比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中尝试看能否打印。
编译版本,上板子调试,发现可以打印出来。正常了串口没有问题。

加DEBUG宏

另一个同事说,需要在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目录下的中间文件,两边是一致的。可以判断出和架构相关的配置是没有问题的。
  1. 1046CPU对应的串口驱动涉及到三个文件:serial.c ns16550.c serial_ns16550.c,

    ns16550.c存在几行代码的不同,但是已经对比,两份代码加错误做实验,发现不一致的地方没有编译到。
    serial_ns16550.c 两边代码完全一致。
    serial.c有几行不一致,已经排除。

  2. 对比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初始化过程中没有异常,后续串口某个地方被打开了。

定位过程:

initcall: 000000004000cf9c

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)

ERROR ### Please RESET the board

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

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值