最近在支持一个i.MX RT1170欧美客户,客户项目里选用了来自Micron的四线NOR Flash - MT25QL256ABA8E12-0AAT作为启动设备,一般读写倒是没有问题,但是在 Segger J-Flash下烧写遇到了特定区域内校验失败的问题。
从本人过往丰富的Flash支持经验来看,亚太区客户一般选用ISSI(芯成)/Winbond(华邦)/MXIC(旺宏)/GigaDevices(兆易创新) 的Flash比较多,个人对这些厂商Flash可以说是门清了。这个欧美客户选用的是不太熟的Micron(镁光)产品,借着这个问题,正好带大家一起稍微深入地了解下Micron Flash产品。
一、引出客户问题
首先是复现下客户的问题,找块MIMXRT1170-EVK开发板,将板载其他厂商Flash换成这颗MT25QL256ABA8E12-0AAT(因为是T-PBGA24封装,所以需要放到原来的 OctalFlash位置——U21),然后将SDK_2.11.1_MIMXRT1170-EVKoardsevkmimxrt1170driver_examplesflexspi orpolling_transfer例程稍作适配性修改,主要是将customLUT里的命令表按Micron数据手册命令表做调整(全用了四字节地址命令),然后跑了一下例程发现基本的Flash读写擦操作没有问题(默认操作的是0x14000处的Sector),这表明硬件修改没有问题。
const uint32_t customLUT[CUSTOM_LUT_LENGTH] = { /* Fast read quad mode - SDR */ [4 * NOR_CMD_LUT_SEQ_IDX_READ_FAST_QUAD] = FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0xEC, kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_4PAD, 0x20), [4 * NOR_CMD_LUT_SEQ_IDX_READ_FAST_QUAD + 1] = FLEXSPI_LUT_SEQ(kFLEXSPI_Command_DUMMY_SDR, kFLEXSPI_4PAD, 0x0a, kFLEXSPI_Command_READ_SDR, kFLEXSPI_4PAD, 0x04), /* Erase Sector */ [4 * NOR_CMD_LUT_SEQ_IDX_ERASESECTOR] = FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0xDC, kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_1PAD, 0x20), /* Page Program - quad mode */ [4 * NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM_QUAD] = FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x34, kFLEXSPI_Command_RADDR_SDR, kFLEXSPI_1PAD, 0x20), [4 * NOR_CMD_LUT_SEQ_IDX_PAGEPROGRAM_QUAD + 1] = FLEXSPI_LUT_SEQ(kFLEXSPI_Command_WRITE_SDR, kFLEXSPI_4PAD, 0x04, kFLEXSPI_Command_STOP, kFLEXSPI_1PAD, 0x00), };
接下来就是按客户操作流程来复现Segger J-Flash烧写校验失败问题,客户其实是尝试烧写全部32MB数据来查看J-Flash及其配套下载算法能否适用这颗Flash,这里就用《超级下载算法RT-UFL v1.0》,经过测试,确实复现了客户的问题。
经过反复测试,定位了问题是这颗Micron32MB的Flash前3/4 区域(0x0 - 0x17FFFFF)是没问题的,但是在后1/4区域(0x1800000 - 1FFFFFF)均会出现校验错误(J-Flash软件里看擦写操作是能进行的,但后面发现其实根本没有正常擦写)。
二、Micron Flash有什么不同?
在分析客户问题之前,我们先来简单认识一下这颗Micron NOR Flash,经过浏览Micron的官网以及这颗Flash的数据手册,发现它确实跟其他厂商的NOR Flash设计有点区别。
首先是Flash容量,其他厂商一般都是能够提供从512Kb到2Gb全范围的Flash产品,但是Micron串行NOR Flash最小容量就是128Mb,果然是国际Memory大厂,设计就是豪横。
但是从Flash作为XIP启动设备角度而言,128Mb其实挺多的,普通的嵌入式项目没有这么大的代码存储需求。
其次是NOR Flash里的高频问题 《QE bit 设计》,一般Flash的IO2/3引脚复用功能都是通过内部状态寄存器里的QE位来控制,QE关闭则IO2/3是RESET#/HOLD#/WP#功能:如果QE开启则IO2/3用于数据传输(这种情况下才可以用Quad I/O相关命令)。
然而Micron Flash根本就没有QE位控制,IO2/3功能主要靠当前命令类型来决定:如果是Single SPI或者Dual I/O SPI命令,则IO2/3是RESET#/HOLD#/WP#功能;如果是Quad I/O SPI命令,则IO2/3 用于传输数据。
其它设计上的区别就不再详细展开了,等用到具体功能查看数据手册再去了解对比。
三、找到问题原因
现在来分析客户问题,Flash的后1/4区域在J-Flash下校验错误,那我们先修改 polling_transfer例程去操作0x1800000之后的Sector,发现确实跑不过。
如果不是Flash介质问题,也不是读写擦命令问题,那只能有一种解释,那就是 Flash里这个区域被保护了,Flash里是有非易失寄存器可以设置软件保护的,但是默认应该是全部区域不保护,而第一小节里我们先跑了polling_transfer例程验证 Flash读写,那大概率这个例程里有修改Flash内部寄存器操作,经过排查定位到了flexspi_nor_enable_quad_mode() 函数。
#define FLASH_QUAD_ENABLE 0x40U const uint32_t customLUT[CUSTOM_LUT_LENGTH] = { /* Enable Quad mode */ [4 * NOR_CMD_LUT_SEQ_IDX_WRITESTATUSREG] = FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x01, kFLEXSPI_Command_WRITE_SDR, kFLEXSPI_1PAD, 0x04), }; int main(void) { // 代码省略 /* Enter quad mode. */ status = flexspi_nor_enable_quad_mode(EXAMPLE_FLEXSPI); if (status != kStatus_Success) { return status; } // 代码省略
第二小节介绍里我们知道Micron Flash是没有QE位设计的,因此 flexspi_nor_enable_quad_mode() 函数在这里是多余的,这个函数是将0x40写入到了命令标号为0x01的Status Register(这个操作适用于ISSI Flash),我们在数据手册里找到这个寄存器定义,发现被置位的bit 6是块保护控制位BP[3:0]里的最高位,并且 BP[3:0]设置是非易失性的(断电不丢失)。
再进一步往下找BP[3:0]设置与Flash空间对应关系,发现4'b1000设置就是保护后1/4区域里的所有block,至今似乎真相大白了。
为了验证这个发现,我们需要将StatusRegister重设为0x00,然后再用J-Flash烧写一次,这时候校验失败问题消失了,一切恢复正常。
一点小体会
回顾这个故事,如果事先不用polling_transfer例程去操作一次Flash,或者即使跑了例程但事先意识到了Micron Flash没有QE设计而删除flexspi_nor_enable_quad_mode() 函数,也就无法复现客户问题了,这也是本次故事里最神奇的地方,我和客户犯了同样的失误,也许这就是缘分?
全部0条评论
快来发表一下你的评论吧 !