AS32A601型MCU芯片flash模块的擦除和编程

一、引言

在AS32A601中,片内Flash共包含两个存储器,分别为程序存储器(PFlash)和数据存储器(DFlash)。

Flash控制器作为CPU内核与片内Flash之间的桥梁,可用于管理任何主设备对片内Flash进行访问。Flash控制器可对Flash执行读取、编程和擦除操作,并实施写保护和读保护等安全机制。

二、 Flash特性

EFlash存储器

  • PFlash最大支持2MB(包括4个block,即4×512KB)
  • DFlash最大支持512KB(包括1个block)
  • 寿命:≥100,000周期
  • 块(Block)容量:512KB/block
  • 扇区(Sector)容量:4KB/sector
  • 行(Row)容量:512B/row

EFlash控制器

操作列表:

  • 擦除:扇区擦除、块擦除、写保护信息区擦除、全片擦除
  • 编程:行编程,编程最小单位64-bit
  • 读:支持8-bit/16-bit/32-bit/64-bit宽度读数据

安全措施:

  • 支持写保护功能
  • 支持读保护功能
  • 可通过安全密钥临时解锁读保护功能
2.1 EFlash存储空间

表 2.1 EFlash存储器组织结构表

|--------|------|-------|--------------------------|--------------|-------------|-------------|-----|
| 存储区 || 空间 | 地址范围 | 扇区 | 起始地址 | 结束地址 | 大小 |
| PFlash | 主存储区 | 2MB | 0x0100_0000~0x011F_FFFF | sector0 | 0x0100_0000 | 0x0100_0FFF | 4KB |
| PFlash | 主存储区 | 2MB | 0x0100_0000~0x011F_FFFF | sector1 | 0x0100_1000 | 0x0100_1FFF | 4KB |
| PFlash | 主存储区 | 2MB | 0x0100_0000~0x011F_FFFF | sector2 | 0x0100_2000 | 0x0100_2FFF | 4KB |
| PFlash | 主存储区 | 2MB | 0x0100_0000~0x011F_FFFF | ... | | | |
| PFlash | 主存储区 | 2MB | 0x0100_0000~0x011F_FFFF | sector511 | 0x011F_F000 | 0x011F_FFFF | 4KB |
| PFlash | 信息区 | 16KB | 0x0120_0000~0x0120_3FFF | info sector0 | 0x0120_0000 | 0x0120_0FFF | 4KB |
| PFlash | 信息区 | 16KB | 0x0120_0000~0x0120_3FFF | info sector1 | 0x0120_1000 | 0x0120_1FFF | 4KB |
| PFlash | 信息区 | 16KB | 0x0120_0000~0x0120_3FFF | info sector2 | 0x0120_2000 | 0x0120_2FFF | 4KB |
| PFlash | 信息区 | 16KB | 0x0120_0000~0x0120_3FFF | info sector3 | 0x0120_3000 | 0x0120_3FFF | 4KB |
| DFlash | 主存储区 | 512KB | 0x0200_0000~0x0207_FFFF | sector512 | 0x0200_0000 | 0x0200_0FFF | 4KB |
| DFlash | 主存储区 | 512KB | 0x0200_0000~0x0207_FFFF | sector513 | 0x0200_1000 | 0x0200_1FFF | 4KB |
| DFlash | 主存储区 | 512KB | 0x0200_0000~0x0207_FFFF | sector514 | 0x0200_2000 | 0x0200_2FFF | 4KB |
| DFlash | 主存储区 | 512KB | 0x0200_0000~0x0207_FFFF | ... | | | |
| DFlash | 主存储区 | 512KB | 0x0200_0000~0x0207_FFFF | sector639 | 0x0207_F000 | 0x0207_FFFF | 4KB |
| DFlash | 信息区 | 4KB | 0x0208_0000~0x0208_0FFF | info sector4 | 0x0208_0000 | 0x0208_0FFF | 4KB |

2.2 EFlash命令

表 2.2 EFlash命令表

|------|----------------|------|----------------------------------------|
| 操作类型 | 命令 | ID | 描述 |
| 擦除 | 扇区擦除 | 0x01 | 按扇区擦除PFlash或DFlash的主存储区 |
| 擦除 | 块擦除 | 0x02 | 擦除指定Block的主存储区 |
| 擦除 | 写保护信息扇区擦除 | 0x03 | 擦除写保护信息所在扇区 |
| 擦除 | 全片擦除 | 0x04 | 擦除PFlash的DFlash全部区域 |
| 编程 | 行编程 | 0x10 | 按照64-bit对齐的方式编程PFlash或DFlash,一次最大可编程一行 |
| 验证 | Security Key验证 | 0x20 | 根据指定的安全密钥验证是否和存储在信息区的密钥匹配 |

三、 应用说明

为了防止用户误操作EFlash,配置EFlash寄存器前首先需要解锁,否则无法写EFlash寄存器,解锁方式参考如下步骤1所描述。

为了保证EFlash正常工作,务必在系统时钟初始化完成后解锁EFlash,然后配置EFLASH_CNFG配置寄存器的CLKFRQ位,具体配置请参考该寄存器位说明。

EFlash命令操作流程均相同,操作步骤如下:

  1. 通过向EFLASH_KEY解锁寄存器写入正确的解锁密钥(依次写入0x01020304和0x0A0B0C0D)解锁EFlash;
  2. 通过检测EFLASH_STATE状态寄存器的BUSY位,判断当前是否有命令正在执行。如果有命令正在执行(BUSY=1)则等待当前命令执行完成后(BUSY=0或FINISH=1),再执行步骤3;
  3. 配置EFlash相关配置寄存器以及EFLASH_CMD命令ID寄存器,然后配置EFLASH_START命令触发寄存器的START位为1,触发EFlash命令执行。后续章节不同的命令仅针对该步骤做详细描述,其他通用的步骤不再做描述;
  4. 通过检测EFLASH_STATE状态寄存器的FINISH位来判断该命令是否执行完成,检测OPERR位、WPERR位等状态位来判断该命令执行是否发生错误;
  5. 配置EFLASH_STATE状态寄存器,清除相关状态位;
  6. 操作完EFlash寄存器后,可通过向EFLASH_KEY解锁寄存器写入非正确密钥即可锁定EFlash。
3.1擦除

AS32A601驱动库提供了扇区擦除和块擦除两个函数,本文档就扇区擦除函数详细讲解。

扇区擦除命令能把指定地址所在的扇区的数据全部擦除,该命令的配置步骤如下:

  1. 配置扇区擦除地址到EFLASH_ADDR地址寄存器,该地址需要8字节对齐,否则触发扇区擦除命令的执行时,会产生操作错误(即EFLASH_STATE寄存器的OPERR位置1);

  2. 配置扇区擦除命令(0x01)到EFLASH_CMD命令ID寄存器;

  3. 配置EFLASH_START寄存器的START位为1,触发扇区擦除命令的执行。

    FLASHStatus_TypeDef FLASH_EraseSector(uint32_t Addr, uint32_t Size)
    {
    FLASHStatus_TypeDef ret = FLASH_SUCCESS;
    uint32_t sectorsize = 0x00;
    uint32_t tempsize = Size;

    复制代码
     /* Check the parameters */  
     assert_param((Addr % 0x08) == 0);  
     assert_param(Size > 0);  
    
     /* Check if command is completed by BUSY */  
     if(FLASH_GetFlagStatus(FLASH_FLAG_BUSY) == SET)  
     {  
         return FLASH_BUSY;  
     }  
    
     /* Determine the flash object of the operation */  
     if ((Addr >= P_FLASH_Main_BASE) && ((Addr + tempsize) <= (P_FLASH_Main_BASE + PFLASH_MAIN_SIZE)))   
     {  
         sectorsize = (uint32_t)PFLASH_SECTOR_SIZE;  
     }  
     else if ((Addr >= D_FLASH_Main_BASE) && ((Addr + tempsize) <= (D_FLASH_Main_BASE + DFLASH_MAIN_SIZE)))  
     {  
         sectorsize = (uint32_t)DFLASH_SECTOR_SIZE;  
     }  
     else  
     {  
         ret = FLASH_ERROR;  
         tempsize = 0U;  
         sectorsize = 0U;  
     }  
    
     while ((tempsize > 0U) && (FLASH_SUCCESS == ret))   
     {  
         /* Set flash sector erase command */  
         FLASH_SetAddress(Addr);  
         FLASH_SetCommand(FLASH_Cmd_SectorErase);  
    
    
         ret = FLASH_StartCmd();  
         /* Start command */  
         if (ret == FLASH_ERROR)   
         {  
             return FLASH_ERROR;  
         }  
         else if (ret == FLASH_BUSY)  
         {  
             return FLASH_BUSY;  
         }  
         else  
         {  
             /* Nothing to do */  
         }  
    
         /* Update size and address */  
         tempsize -= sectorsize;  
         Addr += sectorsize;  
     }  
    
     return ret;  

    }

第1行,Size参数单位为字节,固定为4096,与sector大小相等。第9行检查地址是否8字节对齐,要求余数必须为0,即地址必须是8的倍数。第33行,循环条件:当还有字节要擦除(tempsize > 0U)且操作状态为成功(FLASH_SUCCESS == ret) 。第36-38行,调用FLASH_SetAddress()函数设置当前要擦除的扇区地址,调用FLASH_SetCommand()函数设置命令为扇区擦除。

总结:在使用扇区擦除函数前,用户需要解锁flash,使用后给flash上锁,size参数应固定为4096。

3.2编程函数

编程命令用于编程数据到EFlash中,最小的编程单位为64-bit。需要注意的是:根据EFlash的要求,单次编程操作不允许跨行编程(即,每次编程只允许对EFlash的一行进行编程数据),EFlash一行的容量为512B(即64*64bit)。该命令的配置步骤如下:

  1. 配置编程地址到EFLASH_ADDR寄存器,该地址需要8字节对齐,否则触发编程命令的执行时,会产生操作错误(即EFLASH_STATE寄存器的OPERR位置1);

  2. 配置编程长度到EFLASH_LEN寄存器,该位的最小长度为1(即64-bit),最大长度为64(即EFlash一行的数据长度);

  3. 配置编程命令(0x10)到EFLASH_CMD寄存器;

  4. 配置编程数据低32-bit到EFLASH_DATA0寄存器;

  5. 配置编程数据高32-bit到EFLASH_DATA1寄存器;

  6. 若编程长度大于1,则需要继续执行步骤4,、5,直至将编程数据全部写入;

  7. 配置EFLASH_START寄存器的START位为1,触发编程命令的执行。

    FLASHStatus_TypeDef FLASH_ProgramRom(uint32_t Addr, uint32_t Size, const uint32_t *Data)
    {
    FLASHStatus_TypeDef ret = FLASH_SUCCESS;
    uint32_t addrcount = 0x00, sizetemp = 0x00;
    uint32_t romsize = 0x00;

    复制代码
     /* Check the parameters */  
     assert_param((0 < Size) && ((Size % EFLASH_WRITE_UNIT_SIZE) == 0));  
     assert_param(NULL != Data);  
    
     /* Check if command is completed by BUSY */  
     if(FLASH_GetFlagStatus(FLASH_FLAG_BUSY) == SET)  
     {  
         return FLASH_BUSY;  
     }  
    
     /* Check address */  
     if ((Addr >= P_FLASH_Main_BASE) && ((Addr + Size) <= (P_FLASH_Main_BASE + PFLASH_MAIN_SIZE)))   
     {  
         romsize = PFLASH_ROM_SIZE;  
     }   
     else if ((Addr >= D_FLASH_Main_BASE) && ((Addr + Size) <= (D_FLASH_Main_BASE + DFLASH_MAIN_SIZE)))  
     {  
         romsize = DFLASH_ROM_SIZE;  
     }  
     else  
     {  
         ret = FLASH_ERROR;  
     }  
    
     /* Judge size and feed write */  
     addrcount = Size / romsize;  
     if(0 != (Size % romsize))  
     {  
         addrcount += 1;  
     }  
     else  
     {  
         /* Nothing to do */  
     }  
    
     while ((FLASH_SUCCESS == ret) && (addrcount > 0U))   
     {  
         if (Size >= romsize)   
         {  
             sizetemp  = romsize;  
         }   
         else   
         {  
             sizetemp  = Size;  
         }  
         /* Set address and data length */  
                     
                 EFLASH->AR = Addr;  
         FLASH_SetAddress(Addr);  
         FLASH_SetDataLength(sizetemp / EFLASH_WRITE_UNIT_SIZE);  
    
         /* Set rom program command */  
         FLASH_SetCommand(FLASH_Cmd_Program);  
    
         while ((sizetemp > 0U) && (FLASH_SUCCESS == ret))   
         {  
             /* Write 64 bit data */  
             FLASH_SetLowData(Data[0]);  
             FLASH_SetHighData(Data[1]);  
    
             /* Update data adddress*/  
             Data += 2;  
             sizetemp -= EFLASH_WRITE_UNIT_SIZE;  
         }  
    
         ret = FLASH_StartCmd();  
    
         /* Start command */  
         if (ret == FLASH_ERROR)   
         {  
             return FLASH_ERROR;  
         }  
         else if (ret == FLASH_BUSY)  
         {  
             return FLASH_BUSY;  
         }  
         else  
         {  
             /* Nothing to do */  
         }  
    
         /* Update adddress*/  
         Addr += romsize;  
         addrcount--;  
         Size -= romsize;  
     }  
    
     return ret;  

    }

第13-17行,检查命令是否因忙状态而完成。第20-31行,检查地址,第一个if:检查是否在主Flash范围内,条件:地址在主Flash基址到结束地址之间,如果满足:设置romsize为主Flash的ROM大小,else if:检查是否在数据Flash范围内,else:地址无效,设置ret为FLASH_ERROR。第68-71行,分别设置低32位数据(Data[0])和高32位数据(Data[1])。第73-74行,指针增加2(64位/8字节),指向下一个64位数据。

总结,最小编程单位为64bit(8字节),因此,Size参数应为8的倍数。

3. 3 ECC

EFlash支持ECC功能,相关功能如下:

  1. ECC 1-bit错误自动纠正,且会将EFLASH_STATE寄存器的ECC1ERR位置1;
  2. ECC 2-bit错误无法纠正,且会将EFLASH_STATE寄存器的ECC2ERR位置1,并记录发生该错误的地址,存入EFLASH_ECC_ADDR寄存器中;
  3. 若ECC错误中断使能(即EFLASH_CNFG寄存器的ECC2EIE位置1),则当检测到ECC发生2-bit错误时,会产生ECC中断,并将EFLASH_STATE的ECC2ERR位置1,建议在中断中写1清除该中断标记。

若用户出现编程或擦除失败,请关注EFLASH_STATE寄存器,是否ECC错误置1,调用clearflag函数清楚标志位后再对EFLASH操作

相关推荐
IT_陈寒1 小时前
【SpringBoot 3.2实战】10倍性能优化的5个冷门技巧,90%开发者都不知道!
前端·人工智能·后端
n***i951 小时前
前端技术的反向演进:去框架化浪潮下的轻量化与原生能力回归
前端
不悔哥2 小时前
路由器特性——网络状态检测
linux·c语言·网络·tcp/ip·智能路由器
幸运小圣2 小时前
defineAsyncComponent【Vue3】
前端·javascript·vue.js
青云交2 小时前
Java 大视界 -- Java 大数据机器学习模型在自然语言处理中的对话系统多轮交互优化与用户体验提升
java·大数据·机器学习·自然语言处理·对话系统·多轮交互
90后小陈老师2 小时前
记录一次Figma订阅被多扣费的教训
java·linux·数据库
ouliten2 小时前
《Linux C编程实战》笔记:socketpair
linux·笔记
一个很帅的帅哥2 小时前
webpack开发极简指南
前端·webpack·node.js
计算机毕设指导62 小时前
基于微信小程序的心理咨询预约系统【源码文末联系】
java·spring boot·mysql·微信小程序·小程序·tomcat·maven