1、获取源码。git clone https://github.com/Lichee-Pi/u-boot.git -b v3s-spi-experimental。
ps:下载慢的解决办法:将uboot源码导入到码云官网(https://gitee.com/),再git该官网下相应的链接地址。
2、编译得到u-boot-sunxi-with-spl.bin。
(1)make ARCH=arm CROSS_COMPILE=(/usr/local/arm/arm-2009q3(根据自身环境路径))/bin/arm-none-linux-gnueabi- (空格)LicheePi_Zero_480x272LCD_defconfig
(2)make ARCH=arm CROSS_COMPILE=(/usr/local/arm/arm-2009q3(根据自身环境路径))/bin/arm-none-linux-gnueabi- 2>&1 | tee build.log
3、启动设备
(1)SD卡启动。参考:https://blog.csdn.net/qq_40860568/article/details/96474001
(2)nor flash启动。
1)以上用的源码v3s-spi-experimental设备树已加入spi0以及make menuconfig中也选择了Device Drivers ‣ SPI Flash Support,不过需要根据自身情况选择相应的spi flash厂商,本人用的是XT25Q128,选择Winbond SPI flash support,其他选项默认就好。
2)因为用的spi flash是XT25Q128,所以还需要参考其作相应改动:https://whycan.com/t_4159.html。
3)需要安装sunxi-fel工具烧写进spi flash。
>git clone -b spi-rebase https://github.com/Icenowy/sunxi-tools.git
>进入工具目录执行make && sudo make install
ps:如果出现:fel_lib.c:26:20: fatal error: libusb.h: No such file or directory,那需要安装libusb:sudo apt-get install libusb-1.0-0-dev
4)使芯片进入FEL模式。
>在spi flash的cs引脚处焊条引线;
>上电或复位时,将引线接地便可进入FEL模式;
>进入FLE模式后再断开引线和地的连接。
5)烧录命令:sunxi-fel -p spiflash-write 0 u-boot-sunxi-with-spl.bin
4、启动信息分析
(1)个人初体验。
以本人以前学习朱老师的课程中的uboot分析可知:soc(s5pV210)是自身固定了一段代码,该代码会读取uboot的前16k(BL1)去执行,而在这16k的代码会做的工作:将完整的uboot.bin复制到内存中,然后再长跳转到完整的uboot.bin去执行。
然而刚开始看到v3s的uboot启动信息就非常困惑。首先一眼看去,看到打印信息有一部分是重复的:
CPU: Allwinner V3s (SUN8I 1681)
Model: Lichee Pi Zero
DRAM: 64 MiB
MMC: SUNXI SD/MMC: 0
SF: Detected xt25q128 with page size 256 Bytes, erase size 4 KiB, total 16 MiB
Setting up a 480x272 lcd console (overscan 0x0)
dotclock: 10000kHz = 10000kHz: (1 * 3MHz * 20) / 6
In: serial@01c28000
Out: serial@01c28000
Err: serial@01c28000
还以为是重定位造成的(在没长跳转之前执行了一遍,在跳转后又执行了这些代码一遍),当时就以为这是v3s的uboot和之前s5pV210的不一样吧。于是就追进源码去看,居然发现有两个board_init_f();其是通过CONFIG_SPL_BUILD宏定义条件编译的,刚开始以为整个uboot源码就只会编译和执行其中的一段代码,但是对照打印信息怎么都不对劲,因为最开头的打印信息:
U-Boot SPL 2017.01-rc2-00073-gdd6e874-dirty (Feb 17 2021 - 16:57:21)
DRAM: 64 MiB
Trying to boot from sunxi SPI
也打印了出来,在源码中这些打印的信息是在定义了CONFIG_SPL_BUILD宏才执行的啊,而如果定义了这个宏,那CPU: Allwinner V3s (SUN8I 1681)这些信息就不会被打印啊,再加上CPU: Allwinner V3s (SUN8I 1681)这些信息还出现了重复打印,这到底是怎么个重定位法跳来跳去执行的...
(2)几经折腾以及大胆猜测得到以下结论。
1)首先把那些重复打印信息先解决(这个是在最后才能发现的,现在因为知道了直接拿到第一点说明)。print_pre_console_buffer()【\common\console.c】就是因为该语句才导致出现信息的重复打印的(真能折腾死)。
2)上面说的board_init_f()函数出现了两个,其实两个都执行了,都在同一个uboot源码中,两个相同的函数居然能编译过,真神奇。其实它是被编译成两个不同的bin的了。
3)个人的分析过程(其中大多是猜的,错的指出即可)
/**************************附录1**************************/
RGB灯的汇编测试程序:
ldr r5, =0X1
ldr r6, =0X01C208D8
str r5, [r6]
ldr r5, =0X0
ldr r6, =0X01C208E8
str r5, [r6]
b delay_later
delay:
ldr r7, = 19999999
ldr r8, = 0
delay_loop:
sub r7,r7,#1
cmp r7,r8
bne delay_loop
mov pc,lr
delay_later:
bl delay
ldr r5, =0X1
ldr r6, =0X01C208E8
str r5, [r6]
(1)在“bl board_init_f”【\arch\arm\lib\crt0.S】紧后
加了LED闪烁测试:发现LED闪烁了两次。
猜测:
uboot有两个bin文件,一个是定义了CONFIG_SPL_BUILD宏的SPL的bin文件,
另一个则是没有定义的完整uboot的bin文件;SPL的bin文件是将/common/spl/spl.c
编译进去的,而完整uboot的bin文件则是没有将其编译进去,故两者调用的
board_init_f和board_init_r是不一样的。
(2)在“ldr lr, =board_init_r”【\arch\arm\lib\crt0.S】前
加了LED慢闪;在“ldr pc, =board_init_r”【\arch\arm\lib\crt0.S】前
加了LED快闪:发现LED先是慢闪一下,后再快闪一下。
猜测:
在SPL的bin文件运行时是通过短跳转到board_init_r,
而在完整uboot的bin文件运行时是通过长跳转到board_init_r。
(3)在“b relocate_code”【\arch\arm\lib\crt0.S】前
加了LED闪烁测试:发现只闪烁了一次。
猜测:
只有在完整uboot的bin文件中才会调用relocate_code进行代码重定位,
从中可推断SPL的bin文件运行只是初始化了DDR和判断从哪里启动等,并没
有实现重定位。
总结:
u-boot-sunxi-with-spl.bin文件中的最前面代码是sunxi-spl.bin【\spl】,
其主要作用是初始化了DDR和判断从哪里启动等,最后便跳转到u-boot.bin中运行,
期间是通过CONFIG_SPL_BUILD等宏来区分board_init_f、board_init_r等函数,
代码重定位是在u-boot.bin中实现。
/**************************附录1**************************/
/**************************附录2-sunxi-spl.bin**************************/
(1)_start【\arch\arm\lib\vectors.S】
reset:
>reset【\arch\arm\cpu\armv7\start.S】
lowlevel_init:
>>lowlevel_init【\arch\arm\cpu\armv7\lowlevel_init.S】
设置栈
s_init();
>>>s_init()【\arch\arm\mach-sunxi\board.c】
clock_init();
timer_init();
gpio_init();
i2c_init_board();
eth_init_board();
_main:
>>_main
(2)_main【\arch\arm\lib\crt0.S】
设置栈
board_init_f_alloc_reserve();
board_init_f_init_reserve();
board_init_f();
>board_init_f()【\arch\arm\mach-sunxi\board.c】
spl_init();
preloader_console_init();
>>preloader_console_init()【\common\spl\spl.c】
serial_init();
/*U-Boot SPL 2017.01-rc2-00057-g32ab180-dirty*/
sunxi_board_init();
>>sunxi_board_init()【\board\sunxi\board.c】
printf("DRAM:");
ramsize = sunxi_dram_init();
printf(" %d MiB\n", (int)(ramsize >> 20));
(3)board_init_r【\common\spl\spl.c】
board_boot_order(spl_boot_list);
boot_from_devices(&spl_image, spl_boot_list,ARRAY_SIZE(spl_boot_list));
spl_board_prepare_for_boot();
jump_to_image_no_args(&spl_image);
/**************************附录2-sunxi-spl.bin**************************/
/**************************附录3-u-boot.bin**************************/
(1)board_init_f()【\common\board_f.c】
initf_malloc();
initf_dm();
mark_bootstage();
timer_init();
init_baud_rate();
>init_baud_rate()【\common\board_f.c】
CONFIG_BAUDRATE【\include\configs\sunxi-common.h】
serial_init();
console_init_f();
display_options(); /*U-Boot 2017.01-rc2-00057-g32ab180-dirty*/
display_text_info(); /*show debugging info if required*/
print_cpuinfo(); /*CPU: Allwinner V3s (SUN8I 1681)*/
show_board_info(); /*Model: Lichee Pi Zero*/
INIT_FUNC_WATCHDOG_INIT
INIT_FUNC_WATCHDOG_RESET
init_func_i2c();
init_func_spi();
announce_dram_init();
dram_init(); /*DRAM: 64 MiB*/
INIT_FUNC_WATCHDOG_RESET
setup_dest_addr();
>setup_dest_addr【\common\board_f.c】
/*
* Now that we have DRAM mapped and working, we can
* relocate the code and continue running from DRAM.
*
* Reserve memory at end of RAM for (top down in that order):
* - area that won't get touched by U-Boot and Linux (optional)
* - kernel log buffer
* - protected RAM
* - LCD framebuffer
* - monitor code
* - board info struct
*/
gd->ram_size = board_reserve_ram_top(gd->ram_size);
gd->ram_top = CONFIG_SYS_SDRAM_BASE;
gd->ram_top += get_effective_memsize();
gd->ram_top = board_get_usable_ram_top(gd->mon_len);
gd->relocaddr = gd->ram_top; /*gd->relocaddr = 0X43E00000*/
reserve_round_4k();
gd->relocaddr &= ~(4096 - 1); /*gd->relocaddr = 0X43E00000*/
reserve_mmu();
/*gd->relocaddr = 0X43DF0000*/
reserve_uboot();
/*gd->relocaddr = 0X43D60000*/
reserve_malloc();
reserve_board();
setup_machine(); /* board id for Linux() */
>CONFIG_MACH_TYPE【\include\configs\sun4i.h】
#define CONFIG_MACH_TYPE (4104|((CONFIG_MACH_TYPE_COMPAT_REV) << 28))
reserve_global_data();
reserve_fdt();
reserve_arch()
reserve_stacks();
setup_dram_config();
show_dram_config();
INIT_FUNC_WATCHDOG_RESET
setup_reloc(); /*重定位准备*/
gd->relocaddr = 0X43D60000;
gd->reloc_off = gd->relocaddr - CONFIG_SYS_TEXT_BASE;
/*gd->reloc_off = 0XF60000*/
(2)board_init_r【\common\board_r.c】
initr_reloc();
initr_caches();
initr_reloc_global_data();
initr_malloc();
initr_dm(); /*dm表示driver model?*/
initr_bootstage();
board_init(); /*Setup chipselects*/
stdio_init_tables();
initr_serial();
INIT_FUNC_WATCHDOG_RESET
#ifndef CONFIG_SYS_NO_FLASH
initr_flash();
#endif
INIT_FUNC_WATCHDOG_RESET
initr_mmc(); /*MMC: SUNXI SD/MMC: 0*/
initr_env();
>initr_env()【\common\board_r.c】
env_relocate();
>>env_relocate()【\common\env_common.c】
env_relocate_spec();
>>>env_relocate_spec()【\common\env_sf.c 2】
/*Invalid bus 0 (err=-19)*/
/***Warning-spi_flash_probe()..failed ...*/
spi_flash_probe();
>>>>spi_flash_probe()【\driver\mtd\spi\sf-uclass.c】
spi_flash_probe_bus_cs();
原因:\arch\arm\dts\sun8i-v3s-licheepi-zero.dts处没配置好spi0
INIT_FUNC_WATCHDOG_RESET
stdio_add_devices();
>stdio_add_devices【\common\stdio.c】
drv_video_init();
>>drv_video_init【\drivers\video\cfb_console.c】
cfg_video_init();
>>>cfg_video_init【\drivers\video\cfb_console.c】
video_hw_init();
>>>>video_hw_init【\drivers\video\Sunxi_display.c】
//配置信息
/*
CONFIG_VIDEO_LCD_MODE=
"x:480,y:272,depth:18,pclk_khz:10000,
le:42,ri:8,up:11,lo:4,hs:1,vs:1,
sync:3,vmode:0"
*/
char *lcd_mode = CONFIG_VIDEO_LCD_MODE;
/*Setting up a 480x272 lcd console (overscan 0x0)*/
sunxi_mode_set();
/*dotclock: 10000kHz = 10000kHz: (1 * 3MHz * 20) / 6*/
video_console_address = video_logo();
>>>>video_logo()【\drivers\video\cfb_console.c】
/*logo图片的更换可参考:http://zero.lichee.pro/%E7%B3%BB%E7%BB%9F%E5%BC%80%E5%8F%91/uboot_logo.html*/
//logo的宽
VIDEO_LOGO_WIDTH
//logo的高
VIDEO_LOGO_HEIGHT
//logo的x坐标
video_logo_xpos
//logo的y坐标
video_logo_ypos
//分辨率-列
VIDEO_VISIBLE_COLS
//分辨率-行
VIDEO_VISIBLE_ROWS
initr_jumptable();
console_init_r(); /*CONFIG_SYS_CONSOLE_IS_IN_ENV is not set*/
>console_init_r【\common\console.c 2】
stdio_print_current_devices();
>>stdio_print_current_devices()【\common\console.c】
/*
In: serial@01c28000
Out: serial@01c28000
Err: serial@01c28000
*/
print_pre_console_buffer();
>>print_pre_console_buffer()【\common\console.c】
/*
经过实践验证,如下信息:
CPU: Allwinner V3s (SUN8I 1681)
Model: Lichee Pi Zero
...
重复打印便是出现在此函数中
*/
INIT_FUNC_WATCHDOG_RESET
interrupt_init();
initr_enable_interrupts();
initr_ethaddr();
INIT_FUNC_WATCHDOG_RESET
initr_net();
/**************************附录3-u-boot.bin**************************/
后面本人将会将tftp加载内核以及设备树到内存,并通过NFS文件系统的方式启动内核的思路总结发表
离线