0%

flexspi启动方式

i.MX RT1052 flexspi启动方式

0、关键字

i.MX RT1052

keil 5

分散加载

外部SDRAM

1、Program image

RN5C0H.png

(IMXRT1050RM,8.7.1)

2、IVT(Image Vector Table)

对于FlexSPI NOR而言,偏移为固定0x1000字节,可从下面的bin文件看出。IVT包含程序入口,DCD数据,以及其它一些boot期间会用到的数据。

RN53hq.png

编译生成的bin文件,从0x1000开始就是IVT数据,大小32字节。

RN5RDe.png

从上图可以看出,entry地址为0x60002000,定位到该地址处,如下图所示。

RN5LDg.png

从0x60002000地址开始的首先是栈指针,指向0x80080000。接下来的0x600024F9才是Reset_Handler的地址,如下述map文件所示。

1
2
3
4
5
6
7
8
9
10
Reset_Handler           0x600024f9   Thumb Code    56  startup_mimxrt1052.o(.text)
NMI_Handler 0x60002531 Thumb Code 2 startup_mimxrt1052.o(.text)
HardFault_Handler 0x60002533 Thumb Code 2 startup_mimxrt1052.o(.text)
MemManage_Handler 0x60002535 Thumb Code 2 startup_mimxrt1052.o(.text)
BusFault_Handler 0x60002537 Thumb Code 2 startup_mimxrt1052.o(.text)
UsageFault_Handler 0x60002539 Thumb Code 2 startup_mimxrt1052.o(.text)
SVC_Handler 0x6000253b Thumb Code 2 startup_mimxrt1052.o(.text)
DebugMon_Handler 0x6000253d Thumb Code 2 startup_mimxrt1052.o(.text)
DMA0_DMA16_IRQHandler 0x60002543 Thumb Code 4 startup_mimxrt1052.o(.text)
DMA1_DMA17_IRQHandler 0x60002547 Thumb Code 4 startup_mimxrt1052.o(.text)

下面的部分是用于生成IVT相关部分的代码。定义段时使用attribute((section(“.boot_hdr.ivt”)))。

fsl_flexspi_nor_boot.h文件

1
2
3
4
#define DCD_ADDRESS           dcd_data
#define BOOT_DATA_ADDRESS &boot_data
#define CSF_ADDRESS 0
#define IVT_RSVD (uint32_t)(0x00000000)

fsl_flexspi_nor_boot.c文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include "fsl_flexspi_nor_boot.h"

__attribute__((section(".boot_hdr.ivt")))

/*************************************
* IVT Data
*************************************/
const ivt image_vector_table = {
IVT_HEADER, /* IVT Header */
IMAGE_ENTRY_ADDRESS, /* Image Entry Function */
IVT_RSVD, /* Reserved = 0 */
(uint32_t)DCD_ADDRESS, /* Address where DCD information is stored */
(uint32_t)BOOT_DATA_ADDRESS, /* Address where BOOT Data Structure is stored */
(uint32_t)&image_vector_table, /* Pointer to IVT Self (absolute address */
(uint32_t)CSF_ADDRESS, /* Address where CSF file is stored */
IVT_RSVD /* Reserved = 0 */
};

__attribute__((section(".boot_hdr.boot_data")))

/*************************************
* Boot Data
*************************************/
const BOOT_DATA_T boot_data = {
FLASH_BASE, /* boot start location */
FLASH_SIZE, /* size */
PLUGIN_FLAG, /* Plugin flag*/
0xFFFFFFFF /* empty - extra data word */
};
#endif

3、FlexSPI接口配置

The ROM expects the 512-byte FlexSPI NOR configuration parameters as explained in next section to be present at offset 0 in Serial NOR flash. The ROM reads these configuration parameters using the read command specified by BOOT_CFG2[2:0] with Serial clock operating at 30 MHz.

In the second step, ROM configures FlexSPI interface with the parameters provided in configuration block read from Serial NOR flash and starts the boot procedure. Refer to Table 8-17 for details regarding FlexSPI configuration parameters and to the FlexSPI NOR boot flow chart for detailed boot flow chart of FlexSPI NOR.

(IMXRT1050RM)

evkbimxrt1050_flexspi_nor_config.c文件,其中具体的含义暂时不去细究,这里只是作为流程介绍。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
//对nor flash配置
#include "evkbimxrt1050_flexspi_nor_config.h"

__attribute__((section(".boot_hdr.conf")))

const flexspi_nor_config_t qspiflash_config = {
.memConfig =
{
.tag = FLEXSPI_CFG_BLK_TAG,
.version = FLEXSPI_CFG_BLK_VERSION,
.readSampleClkSrc = kFlexSPIReadSampleClk_LoopbackFromDqsPad,//lxl
.csHoldTime = 3u,
.csSetupTime = 3u,
.columnAddressWidth = 0u,//lxl
.configCmdEnable = 0u,
// Enable SDR mode, Wordaddassable, Safe configuration, Differential clock
.controllerMiscOption = 0u,//lxl
.deviceType = kFlexSpiDeviceType_SerialNOR,
.sflashPadType = kSerialFlash_4Pads,//lxl
.serialClkFreq = kFlexSpiSerialClk_133MHz,
.lutCustomSeqEnable = 0u,
.sflashA1Size = 8u * 1024u * 1024u, //8MB //lxl
.lookupTable =
{
// Fast read sequence
[0] = FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0xEB, RADDR_SDR, FLEXSPI_4PAD, 0x18),
[1] = FLEXSPI_LUT_SEQ(DUMMY_SDR, FLEXSPI_4PAD, 0x06, READ_SDR, FLEXSPI_4PAD, 0x02),
[2] = FLEXSPI_LUT_SEQ(STOP, 0, 0, STOP, 0, 0),
[3] = FLEXSPI_LUT_SEQ(STOP, 0, 0, STOP, 0, 0),
},
},
};

RNICvT.png

该部分数据长度为512字节。

4、DCD(Device Configuration Data)

Upon reset, the chip uses the default register values for all peripherals in the system.However, these settings typically are not ideal for achieving the optimal system performance and there are even some peripherals that must be configured before they can be used.

The DCD is a configuration information contained in the program image (external to the ROM) that the ROM interprets to configure various peripherals on the chip.For example, some components (such as SDRAM) require some sequence of register programming as a part of the configuration before it is ready to be used. The DCD feature can be used to program the SEMC register to the optimal settings.

The ROM determines the location of the DCD table based on the information located in the Image Vector Table (IVT). See Image Vector Table and Boot Data for more details. The DCD table shown below is a big-endian byte array of the allowable DCD commands. The maximum size of the DCD is limited to 1768B.

(摘自IMXRT1050RM 8.7.2)

DCD区域也有具体的格式,可参考IMXRT1050RM文档。

RNIVa9.png

RNIlrD.png

该格式简单理解为:首先一个寄存器地址,之后以该寄存器的设置值,从下述代码dcd_data定义处也可以看出,之后依次按照这样的顺序处理。

evkbimxrt1050_sdram_ini_dcd.c文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include "evkbimxrt1050_sdram_ini_dcd.h"

__attribute__((section(".boot_hdr.dcd_data")))

/*************************************
* DCD Data
*************************************/
const uint8_t dcd_data[] = {
/*0000*/ DCD_TAG_HEADER,
0x04,
0x30,
0x41,
0xCC,
0x03,
0xAC,
0x04,
0x40,
0x0F,
0xC0,
0x68,
0xFF,
0xFF,
0xFF,
0xFF,
//...
};

5、分散加载文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
//m_flash_config域起始地址,定义在SPI FLASH里面(8MB)
#define m_flash_config_start 0x60000000
#define m_flash_config_size 0x00001000 //m_flash_config域大小

#define m_ivt_start 0x60001000 //m_ivt域起始地址,定义在SPI FLASH里面(8MB)
#define m_ivt_size 0x00001000 //m_ivt域大小

//m_interrupts域起始地址,定义在SPI FLASH里面(8MB)
#define m_interrupts_start 0x60002000
#define m_interrupts_size 0x00000400 //m_interrupts域大小

#define m_text_start 0x60002400 //m_text域起始地址,定义在SPI FLASH里面(8MB)
#define m_text_size 0x1F7FDC00 //m_text域大小

//#define m_data_start 0x20200000 //m_data域起始地址,定义在OCRAM里面(256KB)
#define m_data_start 0x80000000 //m_data域起始地址,定义在SDRAM里面
#define m_data_size 0x00080000 //m_data域大小

//m_ncache域起始地址,定义在SDRAM里面(5M开始,总共27MB),必须初始化SDRAM,才能使用此域
#define m_ncache_start 0x80500000
#define m_ncache_size 0x00200000 //m_ncache域大小

#if (defined(__stack_size__))
#define Stack_Size __stack_size__
#else
#define Stack_Size 0x0800
#endif

#if (defined(__heap_size__))
#define Heap_Size __heap_size__
#else
#define Heap_Size 0x0000
#endif

LR_m_rom_config m_flash_config_start m_flash_config_size { ; load region size_region
RW_m_config_text m_flash_config_start m_flash_config_size { ; load address = execution address
* (.boot_hdr.conf, +FIRST)
}
}

LR_m_rom_ivt m_ivt_start m_ivt_size { ; load region size_region
RW_m_ivt_text m_ivt_start m_ivt_size { ; load address = execution address
* (.boot_hdr.ivt, +FIRST)
* (.boot_hdr.boot_data)
* (.boot_hdr.dcd_data)
}
}

ER_m_text m_text_start m_text_size {
ER_m_text m_text_start m_text_size { ; load address = execution address
* (InRoot$$Sections)
.ANY (+RO)
}
RW_m_data m_data_start m_data_size-Stack_Size-Heap_Size { ; RW data
.ANY (+RW +ZI)
*(m_usb_dma_init_data)
*(m_usb_dma_noninit_data)
}
ARM_LIB_HEAP +0 EMPTY Heap_Size { ; Heap region growing up
}
ARM_LIB_STACK m_data_start+m_data_size EMPTY -Stack_Size { ; Stack region growing down
}
RW_m_ncache m_ncache_start m_ncache_size { ; ncache RW data
* (NonCacheable.init)
* (NonCacheable)
}
}

LR_m_interrupts m_interrupts_start m_interrupts_size { //LR_m_interrupts加载域
VECTOR_ROM m_interrupts_start m_interrupts_size { //VECTOR_ROM运行域,起始地址为:m_interrupts_start,大小为:m_interrupts_size
* (RESET,+FIRST) //优先(+FIRST)将RESET(中断向量表)段放这个域,实际上就是把中断向量表拷贝到m_interrupts_start
} //RESET是一个段名,表示中断向量表(在.s文件定义);+FIRST表示时第一个要加载的.
}

分散加载文件中各个段的定义位置分布在多个文件中,上述代码是删除条件编译之后的部分。编译器按照分散加载文件,将各个段放到相应地地址处。

6、编译

如同上述bin文件所示。中间没有数据的部分填充0xFF。

一个需要注意的地方

RNIBqg.png

如上图方框里面的选项,如果是—bin的话,那么会生成4个二进制文件。

RNIIZ4.png

这是因为存在多个加载段的原因,使用-–bincombined选项后,生成一个二进制文件,就是上面看到的bin文件。-–bincombined说明如下(keil帮助文档)。

RNIbJ1.png

7、一句话总结

  1. 上电,根据启动方式选择,内部bootROM按照寄存器默认配置运行,读取Flex Norflash开始部分,重新设置FlexSPI接口。
  2. 读取image,初始化SDRAM。
  3. __main(RW数据复制、解压等,bss清零,堆栈建立等,之前分析过。)
  4. main()

需要说明的是,如分散加载文件所示,RW数据是在外部SDRAM中运行的,所以需要在程序正式运行前配置SDRAM。程序开始运行后,就不能再对SDRAM进行配置了,否则会造成死机。当然也可以在内部OCRAM运行,此时可以将外部SDRAM配置放到main函数中处理。

* 结尾

在研究过程中参考了很多资料,如有侵权,联系删除(aya_lxl@163.com)。