目前主流的Uboot启动模式分为三级启动:

  1. 上电开机后,芯片Boot Rom的代码会被加载到内部的SRam运行,由于通常SRam的内存很小(几十K左右),这就决定了Boot Rom中的代码是做不了太多的事情的,通常厂商会在这部分固化的代码中 完成MMC,NAND,NOR等存储介质的初始化,并尝试从存储介质中读取SPL的代码;
  2. SPL代码执行阶段会初始化串口,DRAM等外设,并从FLASH中读取完整的UBOOT代码到DRAM中;
  3. UBOOT执行,SPL将UBOOT的完整代码加载到RAM后,会跳转到UBOOT的代码段中执行。

大致启动过程如下(32位ARM):

arch\arm\lib\crt0.S

...
bl  board_init_f
...
ldr pc, =board_init_r  /* this is auto-relocated! */
...

board_init_f会完成串口以及DRAM的初始化,以及必要的板级设备初始化

arch\arm\mach-sunxi\board.c

void board_init_f(ulong dummy)
{spl_init();//串口初始化preloader_console_init();#ifdef CONFIG_SPL_I2C_SUPPORT/* Needed early by sunxi_board_init if PMU is enabled */i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE);
#endif//必要的板级设备以及DRAM初始化sunxi_board_init();//内存测试
#ifdef CONFIG_SPL_BUILDspl_mem_test();
#endif
}

common\spl\spl.c

void board_init_r(gd_t *dummy1, ulong dummy2)
{u32 spl_boot_list[] = {BOOT_DEVICE_NONE,BOOT_DEVICE_NONE,BOOT_DEVICE_NONE,BOOT_DEVICE_NONE,BOOT_DEVICE_NONE,};struct spl_image_info spl_image;debug(">>spl:board_init_r()\n");...//选择BOOT设备(MMC,NAND,NOR等)board_boot_order(spl_boot_list);//从FLASH介质中读取UBOOT完整代码段到DRAMif (boot_from_devices(&spl_image, spl_boot_list,ARRAY_SIZE(spl_boot_list))) {puts("SPL: failed to boot from all boot devices\n");hang();}spl_image.entry_point |= 0x1;//根据读取的代码段,确认代码段是UBOOT还是KERNELswitch (spl_image.os) {case IH_OS_U_BOOT:debug("Jumping to U-Boot\n");break;case IH_OS_LINUX:debug("Jumping to Linux\n");spl_fixup_fdt();spl_board_prepare_for_linux();jump_to_image_linux(&spl_image);default:debug("Unsupported OS image.. Jumping nevertheless..\n");}...//跳转到UBOOT执行debug("loaded - jumping to U-Boot...\n");spl_board_prepare_for_boot();jump_to_image_no_args(&spl_image);

这个章节再关注下board_boot_order这个函数

common\spl\spl.c

__weak void board_boot_order(u32 *spl_boot_list)
{spl_boot_list[0] = spl_boot_device();
}

arch\arm\mach-sunxi\board.c

uint32_t sunxi_get_boot_device(void)
{int boot_source;/** When booting from the SD card or NAND memory, the "eGON.BT0"* signature is expected to be found in memory at the address 0x0004* (see the "mksunxiboot" tool, which generates this header).** When booting in the FEL mode over USB, this signature is patched in* memory and replaced with something else by the 'fel' tool. This other* signature is selected in such a way, that it can't be present in a* valid bootable SD card image (because the BROM would refuse to* execute the SPL in this case).** This checks for the signature and if it is not found returns to* the FEL code in the BROM to wait and receive the main u-boot* binary over USB. If it is found, it determines where SPL was* read from.*/if (!is_boot0_magic(SPL_ADDR + 4)) /* eGON.BT0 */return BOOT_DEVICE_BOARD;boot_source = readb(SPL_ADDR + 0x28);printf("boot source %d\n", boot_source);    switch (boot_source) {case SUNXI_BOOTED_FROM_MMC0:return BOOT_DEVICE_MMC1;case SUNXI_BOOTED_FROM_NAND:return BOOT_DEVICE_NAND;case SUNXI_BOOTED_FROM_MMC2:return BOOT_DEVICE_MMC2;case SUNXI_BOOTED_FROM_SPI:return BOOT_DEVICE_SPI;}panic("Unknown boot source %d\n", boot_source);return -1;        /* Never reached */
}
u32 spl_boot_device(void)
{return sunxi_get_boot_device();
}

如上就是BOOT的FLASH设备选择过程。
需要注意的是要将sunxi_spi_spl.c编译进SPL中

common/spl/Makefile/

+obj-y += sunxi_spi_spl.o

代码如下:

/** Copyright (C) 2016 Siarhei Siamashka <siarhei.siamashka@gmail.com>** SPDX-License-Identifier: GPL-2.0+*/#include <common.h>
#include <spl.h>
#include <asm/gpio.h>
#include <asm/io.h>
#include <libfdt.h>#ifdef CONFIG_SPL_OS_BOOT
#error CONFIG_SPL_OS_BOOT is not supported yet
#endif/** This is a very simple U-Boot image loading implementation, trying to* replicate what the boot ROM is doing when loading the SPL. Because we* know the exact pins where the SPI Flash is connected and also know* that the Read Data Bytes (03h) command is supported, the hardware* configuration is very simple and we don't need the extra flexibility* of the SPI framework. Moreover, we rely on the default settings of* the SPI controler hardware registers and only adjust what needs to* be changed. This is good for the code size and this implementation* adds less than 400 bytes to the SPL.** There are two variants of the SPI controller in Allwinner SoCs:* A10/A13/A20 (sun4i variant) and everything else (sun6i variant).* Both of them are supported.** The pin mixing part is SoC specific and only A10/A13/A20/H3/A64 are* supported at the moment.*//*****************************************************************************/
/* SUN4I variant of the SPI controller                                       */
/*****************************************************************************/#define SUN4I_SPI0_CCTL             (0x01C05000 + 0x1C)
#define SUN4I_SPI0_CTL              (0x01C05000 + 0x08)
#define SUN4I_SPI0_RX               (0x01C05000 + 0x00)
#define SUN4I_SPI0_TX               (0x01C05000 + 0x04)
#define SUN4I_SPI0_FIFO_STA         (0x01C05000 + 0x28)
#define SUN4I_SPI0_BC               (0x01C05000 + 0x20)
#define SUN4I_SPI0_TC               (0x01C05000 + 0x24)#define SUN4I_CTL_ENABLE            BIT(0)
#define SUN4I_CTL_MASTER            BIT(1)
#define SUN4I_CTL_TF_RST            BIT(8)
#define SUN4I_CTL_RF_RST            BIT(9)
#define SUN4I_CTL_XCH               BIT(10)/*****************************************************************************/
/* SUN6I variant of the SPI controller                                       */
/*****************************************************************************/#define SUN6I_SPI0_CCTL             (0x01C68000 + 0x24)
#define SUN6I_SPI0_GCR              (0x01C68000 + 0x04)
#define SUN6I_SPI0_TCR              (0x01C68000 + 0x08)
#define SUN6I_SPI0_FIFO_STA         (0x01C68000 + 0x1C)
#define SUN6I_SPI0_MBC              (0x01C68000 + 0x30)
#define SUN6I_SPI0_MTC              (0x01C68000 + 0x34)
#define SUN6I_SPI0_BCC              (0x01C68000 + 0x38)
#define SUN6I_SPI0_TXD              (0x01C68000 + 0x200)
#define SUN6I_SPI0_RXD              (0x01C68000 + 0x300)#define SUN6I_CTL_ENABLE            BIT(0)
#define SUN6I_CTL_MASTER            BIT(1)
#define SUN6I_CTL_SRST              BIT(31)
#define SUN6I_TCR_XCH               BIT(31)/*****************************************************************************/#define CCM_AHB_GATING0             (0x01C20000 + 0x60)
#define CCM_SPI0_CLK                (0x01C20000 + 0xA0)
#define SUN6I_BUS_SOFT_RST_REG0     (0x01C20000 + 0x2C0)#define AHB_RESET_SPI0_SHIFT        20
#define AHB_GATE_OFFSET_SPI0        20#define SPI0_CLK_DIV_BY_2           0x1000
#define SPI0_CLK_DIV_BY_4           0x1001#define _DEBUG    1
#define CONFIG_SYS_SPI_U_BOOT_OFFS 0x8000/*****************************************************************************//** Allwinner A10/A20 SoCs were using pins PC0,PC1,PC2,PC23 for booting* from SPI Flash, everything else is using pins PC0,PC1,PC2,PC3.*/
static void spi0_pinmux_setup(unsigned int pin_function)
{unsigned int pin;for (pin = SUNXI_GPC(0); pin <= SUNXI_GPC(2); pin++)sunxi_gpio_set_cfgpin(pin, pin_function);if (IS_ENABLED(CONFIG_MACH_SUN4I) || IS_ENABLED(CONFIG_MACH_SUN7I))sunxi_gpio_set_cfgpin(SUNXI_GPC(23), pin_function);elsesunxi_gpio_set_cfgpin(SUNXI_GPC(3), pin_function);
}/** Setup 6 MHz from OSC24M (because the BROM is doing the same).*/
static void spi0_enable_clock(void)
{/* Deassert SPI0 reset on SUN6I */if (IS_ENABLED(CONFIG_SUNXI_GEN_SUN6I))setbits_le32(SUN6I_BUS_SOFT_RST_REG0,(1 << AHB_RESET_SPI0_SHIFT));/* Open the SPI0 gate */setbits_le32(CCM_AHB_GATING0, (1 << AHB_GATE_OFFSET_SPI0));/* Divide by 4 */writel(SPI0_CLK_DIV_BY_4, IS_ENABLED(CONFIG_SUNXI_GEN_SUN6I) ?SUN6I_SPI0_CCTL : SUN4I_SPI0_CCTL);/* 24MHz from OSC24M */writel((1 << 31), CCM_SPI0_CLK);if (IS_ENABLED(CONFIG_SUNXI_GEN_SUN6I)) {/* Enable SPI in the master mode and do a soft reset */setbits_le32(SUN6I_SPI0_GCR, SUN6I_CTL_MASTER |SUN6I_CTL_ENABLE |SUN6I_CTL_SRST);/* Wait for completion */while (readl(SUN6I_SPI0_GCR) & SUN6I_CTL_SRST);} else {/* Enable SPI in the master mode and reset FIFO */setbits_le32(SUN4I_SPI0_CTL, SUN4I_CTL_MASTER |SUN4I_CTL_ENABLE |SUN4I_CTL_TF_RST |SUN4I_CTL_RF_RST);}
}static void spi0_disable_clock(void)
{/* Disable the SPI0 controller */if (IS_ENABLED(CONFIG_SUNXI_GEN_SUN6I))clrbits_le32(SUN6I_SPI0_GCR, SUN6I_CTL_MASTER |SUN6I_CTL_ENABLE);elseclrbits_le32(SUN4I_SPI0_CTL, SUN4I_CTL_MASTER |SUN4I_CTL_ENABLE);/* Disable the SPI0 clock */writel(0, CCM_SPI0_CLK);/* Close the SPI0 gate */clrbits_le32(CCM_AHB_GATING0, (1 << AHB_GATE_OFFSET_SPI0));/* Assert SPI0 reset on SUN6I */if (IS_ENABLED(CONFIG_SUNXI_GEN_SUN6I))clrbits_le32(SUN6I_BUS_SOFT_RST_REG0,(1 << AHB_RESET_SPI0_SHIFT));
}static void spi0_init(void)
{unsigned int pin_function = SUNXI_GPC_SPI0;if (IS_ENABLED(CONFIG_MACH_SUN50I))pin_function = SUN50I_GPC_SPI0;spi0_pinmux_setup(pin_function);spi0_enable_clock();
}static void spi0_deinit(void)
{/* New SoCs can disable pins, older could only set them as input */unsigned int pin_function = SUNXI_GPIO_INPUT;if (IS_ENABLED(CONFIG_SUNXI_GEN_SUN6I))pin_function = SUNXI_GPIO_DISABLE;spi0_disable_clock();spi0_pinmux_setup(pin_function);
}/*****************************************************************************/#define SPI_READ_MAX_SIZE 60 /* FIFO size, minus 4 bytes of the header */static void sunxi_spi0_read_data(u8 *buf, u32 addr, u32 bufsize,ulong spi_ctl_reg,ulong spi_ctl_xch_bitmask,ulong spi_fifo_reg,ulong spi_tx_reg,ulong spi_rx_reg,ulong spi_bc_reg,ulong spi_tc_reg,ulong spi_bcc_reg)
{writel(4 + bufsize, spi_bc_reg); /* Burst counter (total bytes) */writel(4, spi_tc_reg);           /* Transfer counter (bytes to send) */if (spi_bcc_reg)writel(4, spi_bcc_reg);  /* SUN6I also needs this *//* Send the Read Data Bytes (03h) command header */writeb(0x03, spi_tx_reg);writeb((u8)(addr >> 16), spi_tx_reg);writeb((u8)(addr >> 8), spi_tx_reg);writeb((u8)(addr), spi_tx_reg);/* Start the data transfer */setbits_le32(spi_ctl_reg, spi_ctl_xch_bitmask);/* Wait until everything is received in the RX FIFO */while ((readl(spi_fifo_reg) & 0x7F) < 4 + bufsize);/* Skip 4 bytes */readl(spi_rx_reg);/* Read the data */while (bufsize-- > 0)*buf++ = readb(spi_rx_reg);/* tSHSL time is up to 100 ns in various SPI flash datasheets */udelay(1);
}static void spi0_read_data(void *buf, u32 addr, u32 len)
{u8 *buf8 = buf;u32 chunk_len;while (len > 0) {chunk_len = len;if (chunk_len > SPI_READ_MAX_SIZE)chunk_len = SPI_READ_MAX_SIZE;if (IS_ENABLED(CONFIG_SUNXI_GEN_SUN6I)) {sunxi_spi0_read_data(buf8, addr, chunk_len,SUN6I_SPI0_TCR,SUN6I_TCR_XCH,SUN6I_SPI0_FIFO_STA,SUN6I_SPI0_TXD,SUN6I_SPI0_RXD,SUN6I_SPI0_MBC,SUN6I_SPI0_MTC,SUN6I_SPI0_BCC);} else {sunxi_spi0_read_data(buf8, addr, chunk_len,SUN4I_SPI0_CTL,SUN4I_CTL_XCH,SUN4I_SPI0_FIFO_STA,SUN4I_SPI0_TX,SUN4I_SPI0_RX,SUN4I_SPI0_BC,SUN4I_SPI0_TC,0);}len  -= chunk_len;buf8 += chunk_len;addr += chunk_len;}
}static ulong spi_load_read(struct spl_load_info *load, ulong sector,ulong count, void *buf)
{spi0_read_data(buf, sector, count);return count;
}/*****************************************************************************/static int spl_spi_load_image(struct spl_image_info *spl_image,struct spl_boot_device *bootdev)
{int ret = 0;struct image_header *header;header = (struct image_header *)(CONFIG_SYS_TEXT_BASE);spi0_init();spi0_read_data((void *)header, CONFIG_SYS_SPI_U_BOOT_OFFS, 0x40);if (IS_ENABLED(CONFIG_SPL_LOAD_FIT) &&image_get_magic(header) == FDT_MAGIC) {struct spl_load_info load;debug("Found FIT image\n");load.dev = NULL;load.priv = NULL;load.filename = NULL;load.bl_len = 1;load.read = spi_load_read;ret = spl_load_simple_fit(spl_image, &load,CONFIG_SYS_SPI_U_BOOT_OFFS, header);} else {ret = spl_parse_image_header(spl_image, header);if (ret)return ret;spi0_read_data((void *)spl_image->load_addr,CONFIG_SYS_SPI_U_BOOT_OFFS, spl_image->size);}spi0_deinit();return ret;
}
/* Use priorty 0 to override the default if it happens to be linked in */
SPL_LOAD_IMAGE_METHOD("Jon SPI", 0, BOOT_DEVICE_SPI, spl_spi_load_image);

SPL_LOAD_IMAGE_METHOD("Jon SPI", 0, BOOT_DEVICE_SPI, spl_spi_load_image);
如上是SPI BOOT的入口项;另外需要特别留意CONFIG_SYS_SPI_U_BOOT_OFFS,这个是配置的UBOOT代码的偏移地址,如何确定这个偏移是多少呢?这个需要看u-boot-spl.lds文件配置的是多大了,如下:

u-boot/arch/arm/cpu/armv7/sunxi/u-boot-spl.lds

MEMORY { .sram : ORIGIN = CONFIG_SPL_TEXT_BASE,\LENGTH = CONFIG_SPL_MAX_SIZE }
MEMORY { .sdram : ORIGIN = CONFIG_SPL_BSS_START_ADDR, \LENGTH = CONFIG_SPL_BSS_MAX_SIZE }

CONFIG_SYS_SPI_U_BOOT_OFFS=CONFIG_SPL_TEXT_BASE+CONFIG_SPL_MAX_SIZE
其实还可以直接看SPL目录的生成文件:

u-boot/spl/u-boot-spl.lds

MEMORY { .sram : ORIGIN = 0x60, LENGTH = 0x7fa0 }
MEMORY { .sdram : ORIGIN = 0x4ff80000, LENGTH = 0x00080000 }

拔出MMC后启动LOG如下:

U-Boot Jon SPL 2017.11 (Apr 30 2019 - 15:46:34)
DRAM: 256 MiB(408MHz)
CPU Freq: 408MHz
memory test: 1
Pattern 55aa  Writing...Reading...OK
>>spl:board_init_r()
boot source 3
flag : 0xffffffff
Trying to boot from Jon SPI
Boot device: untest, may occur error
Jon,spl_parse_image_header
0x27,
0x05,0x19,0x56,0x98,0x4a,0xd2,0x7b,0x5c,0xc7,0xfd,
0x77,0x00,0x04,0xf6,0x2c,0x4a,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x34,0x15,0xab,0xdd,0x11,0x02,0x05,
0x00,0x55,0x2d,0x42,0x6f,0x6f,0x74,0x20,0x32,0x30,
0x31,0x37,0x2e,0x31,0x31,0x20,0x66,0x6f,0x72,0x20,
0x73,0x75,0x6e,0x78,0x69,0x20,0x62,0x6f,0x61,0x72,
0x64,0x00,0x00,
spl: payload image: *s load addr: 0x20 size: 1241514016
loader->name : Jon SPI
Jumping to U-Boot
SPL malloc() used lx bytes (0 KB)
loaded - jumping to U-Boot...
image entry point: 0xU-Boot 2017.11 (Apr 30 2019 - 15:46:34 +0800) Allwinner TechnologyCPU:   Allwinner H3 (SUN8I 1680)
Model: FriendlyElec NanoPi H3
DRAM:  256 MiB
Detecting eMMC...
No MMC device available
No MMC device available
eMMC not exist
CPU Freq: 1008MHz
MMC:   SUNXI SD/MMC: 0, SUNXI SD/MMC: 1
Card did not respond to voltage select!
mmc_init: -95, time 22
*** Warning - MMC init failed, using default environmentERROR: unsupported boot mmc 3
### ERROR ### Please RESET the board ###

很明显SPL已经能够正常从SPI中BOOT了

Uboot SPL的Boot模式选择(从MMC切换到SPI启动)相关推荐

  1. BOOT模式选择启动、Error -6311 PRSC modulefailedtowritetoa register。

    BOOT电路硬件设计(已验证) 1.使用的目的: 程序烧写到主芯片的flash中,断电上电后,程序不能自己运行,所以,就需要使用boot模式电路进行选择. 2.下图,是DSP数据手册中的boot引脚G ...

  2. STM32的三种BOOT模式

    文章目录 一.三种BOOT模式的对比 1.内部 FLASH 启动方式 2.内部 SRAM 启动方式 3.系统存储器启动方式 二.BOOT模式下代码运行后所在地址分析 1.从内部 FLASH 启动 2. ...

  3. STM32三种BOOT模式

    探究STM32三种BOOT模式 简介 一.三种BOOT模式比较 二.开发BOOT模式选择 三.实验项目 (1)实验要求 (2)实验过程 a.STM32内置的Flash启动--最常用 b.从系统存储器启 ...

  4. STM32中BOOT模式配置的作用

    学习了一段时间stm32单片机, 一直没有搞明白这个Boot 引脚的作用,经过找资料,才算搞明白boot设置, 所谓的启动, 就是我们在程序下载完成后, 重新启动芯片时, SYSCLK的第四个上升沿, ...

  5. IMXRT 的Boot模式

    本篇文章参考NXP技术博客整理总结,为个人学习笔记,如有错误欢迎在评论区指正,谢谢. NXP的IMXRT系列单片机,血统来自i.mx处理器系列,除RT1064外,均没有内置flash,因此启动的boo ...

  6. STM32三种BOOT模式介绍

    一.三种BOOT模式介绍 所谓启动,一般来说就是指我们下好程序后,重启芯片时,SYSCLK的第4个上升沿,BOOT引脚的值将被锁存.用户可以通过设置BOOT1和BOOT0引脚的状态,来选择在复位后的启 ...

  7. STM32 BOOT模式配置以及作用

    一.三种BOOT模式介绍 所谓启动,一般来说就是指我们下好程序后,重启芯片时,SYSCLK的第4个上升沿,BOOT引脚的值将被锁存.用户可以通过设置BOOT1和BOOT0引脚的状态,来选择在复位后的启 ...

  8. 计算机boot进入u盘启动,戴尔服务器怎么进入u盘启动模式 选择oneshotbios

    导读:谈到模式,大家应该都了解,有人问dell服务器怎么看raid,当然了,还有人想问戴尔笔记本u盘启动快捷键,这到底怎么回事呢?其实dell服务器bios呢,下面是小编为大家整理的戴尔服务器怎么进入 ...

  9. TI Sitara AM335x系统之AM335x uboot spl分析

    本文转载自nidetech的Blog 芯片到uboot启动流程     ROM → SPL→ uboot.img 简介     在335x 中ROM code是第一级的bootlader.mpu上电后 ...

最新文章

  1. Memcached 缓存系统的-介绍、安装以及应用
  2. python简单入门_Python简明入门教程
  3. mysql笔记03 查询性能优化
  4. Python四大金刚
  5. SQL:如何用一个sql统计出全校男生个数、女生个数以及总人数
  6. JQuery播放器代理--IE下支持wma格式
  7. 手机学linux软件下载,Linux标准教科书下载
  8. abb机器人goto指令用法_ABB机器人指令对照---中文
  9. java基础—对象转型
  10. Learning SQL2
  11. lsblk命令 – 查看系统的磁盘
  12. 机器人周志_机器人制造基础学习日志
  13. FAT32文件操作系统(主要针对SD卡)
  14. 详解高级前端面试常问的EventLoop
  15. 计算机网络(三)—— 数据链路层(7):MAC地址、IP地址以及数据链路层使用的ARP协议
  16. 领导说“辛苦啦”,下属怎么回答?这三种话术,给你职场加分
  17. 2020北大信科计算机考研公示,2020年北京大学信息科学技术学院硕士研究生拟录取名单.pdf...
  18. delphi获取外网IP
  19. 猿辅导python编程课网课怎么样_猿辅导网课怎么样,一个过来人经历告诉你
  20. windows环境下netcat的安装及使用

热门文章

  1. input、textarea等输入框输入中文时,拼音在输入框内会触发input事件的问题
  2. 关于Debug.Log的一点儿小知识
  3. Android Studio中Git更新本地的远程(remote)branch列表?
  4. 尹孝孫 楸溪 門前刺紙欲生毛
  5. IE浏览器退役,终究还是被市场淘汰!
  6. android中小火箭动态喷射动画的简单实现
  7. 飞腾国产处理器FT-2000/4在麒麟Kylin系统下I2C-Tools工具使用
  8. 处理器架构 (十五) 国产cpu芯片与架构
  9. xshell是什么软件
  10. CentOS7 云服务器(ECS)挂载磁盘教程(简明完整梳理版)