电子说
文章中没有提及四线 QSPI Flash,因为一般的四线 QSPI Flash 芯片并没有 DQS 信号引脚。FlexSPI 模块的 DQS 信号当然可以用来连接 Octal Flash、Hyper Flash 上的 DQS 引脚,那么对于四线 QSPI Flash,这个 DQS 信号就没有作用了吗?其实不是的。
我们翻开 i.MXRT1170 的数据手册(Data Sheet)找到 FlexSPI parameters 小节里的如下内容,就很清楚了。FlexSPIn_MCR0[RXCLKSRC] 位对应了三种 DQS 信号源设置:0x0 - Dummy read strobe looped back internally 设置即完全不用 DQS 引脚(可作它用),对应最高 60MHz SDR 访问速度;0x1 - Dummy read strobe looped back through the DQS pad 设置即从 DQS 引脚上回环,因此 DQS 引脚需要悬空,对应最高 133MHz SDR 访问速度;0x3 - Read strobe from memory device DQS pad 设置即接到存储芯片 DQS 引脚上,对应最高 166MHz SDR 访问速度;
• Dummy read strobe generated by FlexSPI controller and looped back internally (FlexSPIn_MCR0[RXCLKSRC] = 0x0)
• Dummy read strobe generated by FlexSPI controller and looped back through the DQS pad (FlexSPIn_MCR0[RXCLKSRC] = 0x1)
• Read strobe provided by memory device and input from DQS pad (FlexSPIn_MCR0[RXCLKSRC] = 0x3)
目前恩智浦已量产的所有 i.MXRT 型号里(RT500/600/1010/1015/1020/1050/1060/1160/1170),大部分的 FlexSPI 引脚组合都是包含 DQS 信号设计的,只有如下两个例外。没有 DQS 信号的引脚组合连 Flash 时,应配置 FlexSPIn_MCR0[RXCLKSRC] 为 0x0 - looped back internally。
对于不含 DQS 信号的 FlexSPI 引脚组合,使用恩智浦相关工具操作连接在其上的 NOR Flash 是需要做一些改动的,我们以 i.MXRT1170-Validation 板卡为例来介绍具体改动。
i.MXRT1170-Validation 板卡是专供恩智浦内部使用的,分为 CPU1/2/3/4 四款,每款的硬件连接不同,其中 CPU2 板卡在 FlexSPI2 2nd PortA 上连接了一颗镁光的 MT25QL128:
我们现在打开 SDK 里的 FlexSPI 例程,这个例程是针对 MIMXRT1170-EVK 板卡写的,在 EVK 上 NOR Flash 默认是连在 FlexSPI1 1st PortA 上的,因此我们需要对例程做一些改动。
例程路径:\\SDK_2.x.x_MIMXRT1170-EVK\\boards\\evkmimxrt1170\\driver_examples\\flexspi\\nor\\polling_transfer\\cm7
首当其冲的改动当然是 pin_mux.c 文件里的 BOARD_InitPins() 函数,需要将 FlexSPI1 1st Pinmux 换成 FlexSPI2 2nd Pinmux:
void BOARD_InitPins(void)
{
CLOCK_EnableClock(kCLOCK_Iomuxc);
//IOMUXC_SetPinMux(IOMUXC_GPIO_SD_B2_05_FLEXSPI1_A_DQS, 1U);
//IOMUXC_SetPinMux(IOMUXC_GPIO_SD_B2_06_FLEXSPI1_A_SS0_B, 1U;
//IOMUXC_SetPinMux(IOMUXC_GPIO_SD_B2_07_FLEXSPI1_A_SCLK, 1U);
//IOMUXC_SetPinMux(IOMUXC_GPIO_SD_B2_08_FLEXSPI1_A_DATA00, 1U);
//IOMUXC_SetPinMux(IOMUXC_GPIO_SD_B2_09_FLEXSPI1_A_DATA01, 1U);
//IOMUXC_SetPinMux(IOMUXC_GPIO_SD_B2_10_FLEXSPI1_A_DATA02, 1U);
//IOMUXC_SetPinMux(IOMUXC_GPIO_SD_B2_11_FLEXSPI1_A_DATA03, 1U);
//IOMUXC_SetPinConfig(IOMUXC_GPIO_SD_B2_05_FLEXSPI1_A_DQS, 0x0AU);
//IOMUXC_SetPinConfig(IOMUXC_GPIO_SD_B2_06_FLEXSPI1_A_SS0_B, 0x0AU);
//IOMUXC_SetPinConfig(IOMUXC_GPIO_SD_B2_07_FLEXSPI1_A_SCLK, 0x0AU);
//IOMUXC_SetPinConfig(IOMUXC_GPIO_SD_B2_08_FLEXSPI1_A_DATA00, 0x0AU);
//IOMUXC_SetPinConfig(IOMUXC_GPIO_SD_B2_09_FLEXSPI1_A_DATA01, 0x0AU);
//IOMUXC_SetPinConfig(IOMUXC_GPIO_SD_B2_10_FLEXSPI1_A_DATA02, 0x0AU);
//IOMUXC_SetPinConfig(IOMUXC_GPIO_SD_B2_11_FLEXSPI1_A_DATA03, 0x0AU);
IOMUXC_SetPinMux(IOMUXC_GPIO_SD_B1_00_FLEXSPI2_A_SS0_B, 1U;
IOMUXC_SetPinMux(IOMUXC_GPIO_SD_B1_01_FLEXSPI2_A_SCLK, 1U);
IOMUXC_SetPinMux(IOMUXC_GPIO_SD_B1_02_FLEXSPI2_A_DATA00, 1U);
IOMUXC_SetPinMux(IOMUXC_GPIO_SD_B1_03_FLEXSPI2_A_DATA01, 1U);
IOMUXC_SetPinMux(IOMUXC_GPIO_SD_B1_04_FLEXSPI2_A_DATA02, 1U);
IOMUXC_SetPinMux(IOMUXC_GPIO_SD_B1_05_FLEXSPI2_A_DATA03, 1U);
IOMUXC_SetPinConfig(IOMUXC_GPIO_SD_B1_00_FLEXSPI2_A_SS0_B, 0x0AU);
IOMUXC_SetPinConfig(IOMUXC_GPIO_SD_B1_01_FLEXSPI2_A_SCLK, 0x0AU);
IOMUXC_SetPinConfig(IOMUXC_GPIO_SD_B1_02_FLEXSPI2_A_DATA00, 0x0AU);
IOMUXC_SetPinConfig(IOMUXC_GPIO_SD_B1_03_FLEXSPI2_A_DATA01, 0x0AU);
IOMUXC_SetPinConfig(IOMUXC_GPIO_SD_B1_04_FLEXSPI2_A_DATA02, 0x0AU);
IOMUXC_SetPinConfig(IOMUXC_GPIO_SD_B1_05_FLEXSPI2_A_DATA03, 0x0AU);
}
然后再改一下 app.h 文件里的宏定义,从 FlexSPI1 换到 FlexSPI2,并相应调整 Flash 属性(EVK 上是 IS25WP128,Validation 板上是 MT25QL128),以及更新 flexspi_clock_init() 函数:
//#define EXAMPLE_FLEXSPI FLEXSPI1
//#define EXAMPLE_FLEXSPI_AMBA_BASE FlexSPI1_AMBA_BASE
//#define EXAMPLE_FLEXSPI_CLOCK kCLOCK_Flexspi1
#define EXAMPLE_FLEXSPI FLEXSPI2
#define EXAMPLE_FLEXSPI_AMBA_BASE FlexSPI2_AMBA_BASE
#define EXAMPLE_FLEXSPI_CLOCK kCLOCK_Flexspi2
static inline void flexspi_clock_init(void)
{
// 在 BOARD_BootClockRUN() 函数里给 FlexSPI 设置的时钟源是 OSC_RC_48M_DIV2 / 2 即 12MHz
// 这里没有再次分频,因此 FlexSPI root clock 就是 12MHz
//CLOCK_SetRootClockDiv(kCLOCK_Root_Flexspi1, 2);
//CLOCK_SetRootClockMux(kCLOCK_Root_Flexspi1, 0);
CLOCK_SetRootClockDiv(kCLOCK_Root_Flexspi2, 2);
CLOCK_SetRootClockMux(kCLOCK_Root_Flexspi2, 0);
}
上面都是大家能意识到的改动,但其实最容易被忽略的改动是 flexspi_nor_flash_ops.c 文件里的 flexspi_nor_flash_init() 函数,config.rxSampleClock 设置必须要改成 kFLEXSPI_ReadSampleClkLoopbackInternally 才行。代码全部改完之后下载运行就可以正常擦写 Flash 了。
void flexspi_nor_flash_init(FLEXSPI_Type *base)
{
// 省略部分代码 ...
flexspi_clock_init();
flexspi_config_t config;
FLEXSPI_GetDefaultConfig(&config);
config.ahbConfig.enableAHBPrefetch = true;
config.ahbConfig.enableAHBBufferable = true;
config.ahbConfig.enableReadAddressOpt = true;
config.ahbConfig.enableAHBCachable = true;
//config.rxSampleClock = kFLEXSPI_ReadSampleClkLoopbackFromDqsPad;
config.rxSampleClock = kFLEXSPI_ReadSampleClkLoopbackInternally;
FLEXSPI_Init(base, &config);
// 省略部分代码 ...
}
现在我们再来用一下 SDK 里的 flashloader 工程,这个应用程序可以与恩智浦专用命令行上位机 blhost.exe 进行交互,工程需要用调试器下载进主芯片内部 RAM 运行
工程路径:\\SDK_2.x.x_MIMXRT1170-EVK\\boards\\evkmimxrt1170\\bootloader_examples\\flashloader\\cm7
flashloader 运行起来之后,使用 blhost 工具按序执行下列命令,也一样能对 Flash 进行擦写:
blhost -u -- get-property 1
# 选中 FlexSPI2
blhost -u -- fill-memory 0x20000000 4 0xcf900002
blhost -u -- configure-memory 9 0x20000000
# 配置 NOR Flash(forced internal DQS)
blhost -u -- fill-memory 0x20000000 4 0xc1000053 # 其中bit[7:4]是关键设置!!!
blhost -u -- fill-memory 0x20000004 4 0x00110000
blhost -u -- configure-memory 9 0x20000000
blhost -u -- get-property 25 9
# 下载包含 IVT 头的 App
blhost -u -- flash-erase-region 0x60000000 0x8000
blhost -u -- fill-memory 0x20000000 4 0xf000000f
blhost -u -- configure-memory 9 0x20000000
blhost -u -- write-memory 0x60001000 ivt_app.bin
上面命令序列里第二条 fill-memory 命令的参数 0xc1000053 是关键,从 flashloader 源代码里看它其实是在设置 serial_nor_config_option_t.option0.B.misc_mode 为 kSerialNorEnhanceMode_InternalLoopback,这个设置对于四线 QSPI Flash 而言就是设 config->memConfig.readSampleClkSrc 为 kFlexSPIReadSampleClk_LoopbackInternally:
enum
{
kSerialNorEnhanceMode_Disabled = 0,
kSerialNorEnhanceMode_0_4_4_Mode = 1,
kSerialNorEnhanceMode_0_8_8_Mode = 2,
kSerialNorEnhanceMode_DataOrderSwapped = 3,
kSerialNorEnhanceMode_2ndPinMux = 4,
kSerialNorEnhanceMode_InternalLoopback = 5,
};
status_t parse_sfdp(uint32_t instance,
flexspi_nor_config_t *config,
jedec_info_table_t *tbl,
serial_nor_config_option_t *option)
{
status_t status = kStatus_InvalidArgument;
do
{
// 省略部分代码...
uint8_t misc_mode = option- >option0.B.misc_mode;
if (misc_mode == kSerialNorEnhanceMode_Disabled)
{
// 省略部分代码...
}
#if FLEXSPI_ENABLE_NO_CMD_MODE_SUPPORT
else if (misc_mode == kSerialNorEnhanceMode_0_4_4_Mode)
{
// 省略部分代码...
}
#endif // FLEXSPI_ENABLE_NO_CMD_MODE_SUPPORT
else if (misc_mode == kSerialNorEnhanceMode_InternalLoopback)
{
config- >memConfig.readSampleClkSrc = kFlexSPIReadSampleClk_LoopbackInternally;
}
else
{
// Do nothing
}
// 省略部分代码...
} while (0);
return status;
}
flashloader 的使用对一般客户来说太复杂了,还是图形化工具 MCUBootUtility 更方便,打开这个工具,按如下配置(主要就是图中蓝框圈起来的 Misc Mode 设置),也可以正常擦写 Flash。熟悉这个工具原理的朋友应该知道它底层依赖得就是 3.2 节里的 flashloader 与 blhost。
最后就是 SDK 里全部例程的 XIP build 都需要一个 FDCB 头,这个头定义在 evkmimxrt1170_flexspi_nor_config.c 文件里,这里也要改一下 readSampleClkSrc 和 serialClkFreq 配置才行。
const flexspi_nor_config_t qspiflash_config = {
.memConfig =
{
.tag = FLEXSPI_CFG_BLK_TAG,
.version = FLEXSPI_CFG_BLK_VERSION,
//.readSampleClkSrc = kFlexSPIReadSampleClk_LoopbackFromDqsPad,
.readSampleClkSrc = kFlexSPIReadSampleClk_LoopbackInternally,
.csHoldTime = 3u,
.csSetupTime = 3u,
.controllerMiscOption = 0x10,
.deviceType = kFlexSpiDeviceType_SerialNOR,
.sflashPadType = kSerialFlash_4Pads,
//.serialClkFreq = kFlexSpiSerialClk_133MHz,
.serialClkFreq = kFlexSpiSerialClk_60MHz,
.sflashA1Size = 16u * 1024u * 1024u,
.lookupTable =
{
// Read LUTs
FLEXSPI_LUT_SEQ(CMD_SDR, FLEXSPI_1PAD, 0xEB, RADDR_SDR, FLEXSPI_4PAD, 0x18),
FLEXSPI_LUT_SEQ(MODE8_SDR, FLEXSPI_4PAD, 0x00, DUMMY_SDR, FLEXSPI_4PAD, 0x04),
FLEXSPI_LUT_SEQ(READ_SDR, FLEXSPI_4PAD, 0x04, 0, 0, 0),
},
},
.pageSize = 256u,
.sectorSize = 4u * 1024u,
.ipcmdSerialClkFreq = 0x1,
.blockSize = 256u * 1024u,
.isUniformBlockSize = false,
};
全部0条评论
快来发表一下你的评论吧 !