一.预览
本文主要对IMX6Q的启动过程进行分析,主要参考文档来自NXP官方文档IMX6DQRM.pdf,使用的uboot为NXP官方提供uboot-imx,使用的开发板为迅为iTOP-i.MX6。NXP官方文档IMX6DQRM.pdf对启动过程的描述在第8章,本文主要参考第8章。
二.关键词
IVT image vector table
DCD device configuration data
三.启动过程
imx6q上电复位后,从芯片内部ROM开始执行。内部ROM包含代码,可以 引导启动。这个内部ROM代码首先会检查BOOT_MODE[1:0]来确定引导模式,而BOOT_MODE[1:0]状态由芯片的BOOT_MODE的管脚确定,在iTOP-i.MX6开发板上已经固定为10,即内部引导。
BOOT_MODE[1:0] Boot Type
00 Boot From Fuses
01 Serial Downloader
10 Internal Boot
11 Reserved
在确定了引导模式之后,接下来就确定引导设备(uboot的存储位置)。在iTOP-i.MX6开发板上可通过拨码开关设置引导设备,这里拨码开关实际是利用imx6q芯片的管脚来设置引导设备,具体设置方法请看IMX6DQRM.pdf的8.5节。iTOP-i.MX6开发板可以设置从eMMC(板上flash)或SD卡为引导设备,这里设置eMMC引导设备。
在确定了引导设备eMMC之后,内部ROM代码会去eMMC上读取镜像,我们的镜像(uboot,内核,文件系统)都存储在eMMC上。在eMMC上首先存储的是uboot。在uboot-imx的源文件有board/freescale/mx6q_topeet/flash_header.S文件(mx6q_topeet对应iTOP-i.MX6开发板,在board/freescale/mx6q_sabresd/flash_header.S也有此文件),再看board/freescale/u-boot.lds,可以发现如下,flash_header.S代码是存储在start.S代码的前面。
.text :
{
/* WARNING - the following is hand-optimized to fit within */
/* the sector layout of our flash chips! XXX FIXME XXX */
board/freescale/mx6q_topeet/flash_header.o (.text.flasheader)
cpu/arm_cortexa8/start.o
board/freescale/mx6q_topeet/libmx6q_topeet.a (.text)
lib_arm/libarm.a (.text)
net/libnet.a (.text)
drivers/mtd/libmtd.a (.text)
drivers/mmc/libmmc.a (.text)
. = DEFINED(env_offset) ? env_offset : .;
common/env_embedded.o(.text)
*(.text)
}
在其他arm开发板上uboot都是从cpu/arm_cortexa8/start.S开始运行,这里flash_header.S存储在start.S之前,会在start.S之前运行是怎么回事呢?
要回答这个问题,我们可以看官方文档IMX6DQRM.pdf的第8.6节。在内部ROM代码读取镜像时,会先读取IVT(镜像向量表)、boot data、DCD(设备配置数据)。flash_header.S内的代码就是IVT、boot data和DCD。在flash_header.S内先有0x400字节的偏移(这个偏移量是IMX6DQRM.pdf的第8.6.1小节有说明,eMMC启动是偏移0x400字节),然后是IVT、boot data和DCD的描述。
内部ROM代码先偏移0x400字节读取IVT,再根据IVT的内容找到boot data和DCD,实际IVT保存了boot data和DCD的地址。内部ROM代码读取并执行上述内容后,知道了uboot镜像(start.S开始的)的起始地址和长度,对DDR进行了初始化设置,接下来就可以导入uboot镜像。
整个过程:上电复位->内部ROM代码->芯片基本初始化(时钟,缓存等)->确定引导模式->确定引导设备->从引导设备上偏移一定地址后读取IVT->在IVT内找到boot data和DCD->根据boot data确定uboot的起始地址和长度->根据DCD设置相关外设,包括DDR内存->导入uboot。
IVT、boot data和DCD的数据结构在官方文档IMX6DQRM.pdf的第8.6节有详细说明。
四. flash_header.S注释
/* 这里仅有flash_header.S的部分代码,详细请看源文件 */
#include <config.h>
#include <asm/arch/mx6.h>
#ifdef CONFIG_FLASH_HEADER
#ifndef CONFIG_FLASH_HEADER_OFFSET
# error "Must define the offset of flash header"
#endif
/* 数据变大端宏定义 */
#define CPU_2_BE_32(l) \
((((l) & 0x000000FF) << 24) | \
(((l) & 0x0000FF00) << 8) | \
(((l) & 0x00FF0000) >> 8) | \
(((l) & 0xFF000000) >> 24))
/* DCD数据写入宏定义 */
#define MXC_DCD_ITEM(i, addr, val) \
dcd_node_##i: \
.word CPU_2_BE_32(addr) ; \
.word CPU_2_BE_32(val) ; \
.section ".text.flasheader", "x"
b _start
.org CONFIG_FLASH_HEADER_OFFSET /*在eMMC中IVT偏移0x400字节*/
/* IVT 数据结构 */
ivt_header: .word 0x402000D1 /* Tag=0xD1, Len=0x0020, Ver=0x40 */
app_code_jump_v: .word _start /* 从uboot开始执行的第一条指令的绝对地址 */
reserv1: .word 0x0
dcd_ptr: .word dcd_hdr /* DCD的绝对地址 */
boot_data_ptr: .word boot_data /* boot data 的绝对地址 */
self_ptr: .word ivt_header /* IVT 的绝对地址 */
#ifdef CONFIG_SECURE_BOOT
app_code_csf: .word __hab_data /* CSF文件的绝对地址,与安全boot相关,这里没有 */
#else
app_code_csf: .word 0x0
#endif
reserv2: .word 0x0
/* Boot data structure */
boot_data: .word TEXT_BASE /* Absolute address of the image */
#ifdef CONFIG_SECURE_BOOT
image_len: .word __hab_data_end - TEXT_BASE + CONFIG_FLASH_HEADER_OFFSET
#else
image_len: .word _end_of_copy - TEXT_BASE + CONFIG_FLASH_HEADER_OFFSET
#endif
plugin: .word 0x0
/* DCD */
#if defined CONFIG_MX6DL_DDR3
#if defined CONFIG_DDR_32BIT
/* DCD(device configuration data) header*/
dcd_hdr: .word 0x40E001D2 /* Tag=0xD2, Len=59*8 + 4 + 4, Ver=0x40 */
/* Write data command */
write_dcd_cmd: .word 0x04DC01CC /* Tag=0xCC, Len=59*8 + 4, Param=0x04 */
/* IOMUX Control (IOMUXC) registers */
MXC_DCD_ITEM(1, IOMUXC_BASE_ADDR + 0x774, 0x000C0000)
......
MXC_DCD_ITEM(26, IOMUXC_BASE_ADDR + 0x47c, 0x00000030)
/* MMDC(Multi-mode DDR controller) register set */
MXC_DCD_ITEM(27, MMDC_P0_BASE_ADDR + 0x800, 0xA1390003)
......
MXC_DCD_ITEM(59, MMDC_P0_BASE_ADDR + 0x01c, 0x00000000)