一、引言
在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命令操作流程均相同,操作步骤如下:
- 通过向EFLASH_KEY解锁寄存器写入正确的解锁密钥(依次写入0x01020304和0x0A0B0C0D)解锁EFlash;
- 通过检测EFLASH_STATE状态寄存器的BUSY位,判断当前是否有命令正在执行。如果有命令正在执行(BUSY=1)则等待当前命令执行完成后(BUSY=0或FINISH=1),再执行步骤3;
- 配置EFlash相关配置寄存器以及EFLASH_CMD命令ID寄存器,然后配置EFLASH_START命令触发寄存器的START位为1,触发EFlash命令执行。后续章节不同的命令仅针对该步骤做详细描述,其他通用的步骤不再做描述;
- 通过检测EFLASH_STATE状态寄存器的FINISH位来判断该命令是否执行完成,检测OPERR位、WPERR位等状态位来判断该命令执行是否发生错误;
- 配置EFLASH_STATE状态寄存器,清除相关状态位;
- 操作完EFlash寄存器后,可通过向EFLASH_KEY解锁寄存器写入非正确密钥即可锁定EFlash。
3.1擦除
AS32A601驱动库提供了扇区擦除和块擦除两个函数,本文档就扇区擦除函数详细讲解。
扇区擦除命令能把指定地址所在的扇区的数据全部擦除,该命令的配置步骤如下:
-
配置扇区擦除地址到EFLASH_ADDR地址寄存器,该地址需要8字节对齐,否则触发扇区擦除命令的执行时,会产生操作错误(即EFLASH_STATE寄存器的OPERR位置1);
-
配置扇区擦除命令(0x01)到EFLASH_CMD命令ID寄存器;
-
配置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)。该命令的配置步骤如下:
-
配置编程地址到EFLASH_ADDR寄存器,该地址需要8字节对齐,否则触发编程命令的执行时,会产生操作错误(即EFLASH_STATE寄存器的OPERR位置1);
-
配置编程长度到EFLASH_LEN寄存器,该位的最小长度为1(即64-bit),最大长度为64(即EFlash一行的数据长度);
-
配置编程命令(0x10)到EFLASH_CMD寄存器;
-
配置编程数据低32-bit到EFLASH_DATA0寄存器;
-
配置编程数据高32-bit到EFLASH_DATA1寄存器;
-
若编程长度大于1,则需要继续执行步骤4,、5,直至将编程数据全部写入;
-
配置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功能,相关功能如下:
- ECC 1-bit错误自动纠正,且会将EFLASH_STATE寄存器的ECC1ERR位置1;
- ECC 2-bit错误无法纠正,且会将EFLASH_STATE寄存器的ECC2ERR位置1,并记录发生该错误的地址,存入EFLASH_ECC_ADDR寄存器中;
- 若ECC错误中断使能(即EFLASH_CNFG寄存器的ECC2EIE位置1),则当检测到ECC发生2-bit错误时,会产生ECC中断,并将EFLASH_STATE的ECC2ERR位置1,建议在中断中写1清除该中断标记。
若用户出现编程或擦除失败,请关注EFLASH_STATE寄存器,是否ECC错误置1,调用clearflag函数清楚标志位后再对EFLASH操作
