Flash(闪存)是一种可擦除的只读存储器,按照实现方式和运行特性Flash一般还会分为NOR和NAND两种。其中NOR Flash支持随机地址的读取方式,在读取操作上类似于RAM,比较适合程序的直接读取运行,而NAND Flash读取是基于页的方式,一般无法随机读取。在MCU中,Flash需要支持程序和数据的存储,所以实现方式上也都是NOR Flash。
基本特性YTM32系列MCU中Flash的控制是通过EFM(Embedded Flash Module)控制的,这里以ME0x系列MCU为例,EFM模块支持如下的一些功能:
512KB * 2 的程序存储区域,带有ECC功能,Sector大小为2K256KB 单独的数据存储块,带有ECC功能,Sector大小为1K4KB的NVR区域,带有ECC功能,Sector大小为1K支持Flash按区域(16KB单位)的写保护功能支持调试器禁用支持Flash命令执行结束和异常中断支持Block Swap的OTA升级功能支持ECC错误地址记录和单比特、多比特中断支持OTP(One Time Program,一次可编程)区域支持HCU 密钥存储(可擦写,不可读取)支持块擦除、扇区(sector)擦除和整个chip的擦除操作写入页大小为8BytesFlash的基本术语约定为了便于理解,这里整理一下Flash使用中常用的一些基本术语:
PFlash,程序Flash,实际也可以保存数据,属于基于应用场景的一种约定名称DFlash,数据Flash,实际也可以运行程序,同样属于基于应用场景的一种约定名称Block,Flash块,表示物理上的一个Flash块,不同物理块的flash可以支持Read While Write(RWW)特性Sector,扇区,这个是Flash擦除的最小单位,属于Flash的物理特性,软件无法修改Page,Flash 编程的最小单位,同样属于Flash物理特性,软件无法修改RWW,Read While Write,指的是Flash在运行擦除或者编程操作时候支持Flash的读取操作ECC校验,纠错算法的一种,可以纠正单比特错误,检测多比特错误Memory Map定义这里以ME0x为例,芯片系统存储的memory map如下
NameStart AddressEnd AddressSize(KB)BlockProtectPFlash00x0000_00000x0007_FFFF5120ADDR_PROT0PFlash10x0008_00000x000F_FFFF5121ADDR_PROT1DFlash0x0010_00000x0013_FFFF2562ADDR_PROT2AES_NVR0x1000_00000x1000_03FF10No Read + Customer KeyOTP_NVR0x1001_00000x1001_03FF10No EraseBOOT_NVR0x1002_00000x1002_03FF11SWAP CMD onlyCUS_NVR0x1003_00000x1003_03FF11Customer Key上述表格中PFlash0和PFlash1是用于存储用户程序的,当使用OTA功能的时候,这两个Block可以通过物理地址的重映射互换,实现应用升级。
AES_NVR主要用于保存HCU中使用的密钥,该部分占用1KB空间,可以实现32*256Bit的密钥存储。该部分区域支持用户的Program和Erase,但是不支持读取操作,可以保证密钥的安全。当HCU需要使用密钥的时候,软件可以直接调用Flash的命令将需要用到的Key直接load到HCU中使用,整个过程软件只能选择key而不能读取或者修改key。注意program和erase AES key区域需要用customer key解锁。
OTP_NVR该部分可以用于保存一次可编程数据,OTP(One Time Program)区域的特点是只能一次program,不支持擦写和重新program。应用中可以在该部分保存一些产品ID信息或者其它不能希望后续修改的信息。注意该部分的读取并没有限制。
BOOT_NVR这部分用来保存和OTA升级相关的数据,软件应该避免对该区域进行操作,需要使用OTA Swap功能的时候,软件需要发送SWAP命令实现Flash的Block Swap。
CUS_NVR这部分区域包含Flash擦写保护配置和Debugger禁用Tag,用户可以向该区域的特定地址program特定值来实现对Flash的擦写保护和调试端口禁用。CUS_NVR的剩余部分是用户可以自由使用的,对CUS_NVR的读写操作需要先通过写入customer key来解锁。
Flash的读写擦操作读取、写入和擦除是Flash的基本操作,Flash的特性是擦除之后bit变成1,写入操作是将相应的bit改写成0,不过因为有ECC的限制,YTM32系列MCU都不支持Reprogram,也就是不支持Flash 页数据有非1情况下的页编程,EFM模块在执行Program命令之前会先从Flash中读取页数据并验证数据为全1,验证失败则该页无法编程。
另外Flash的物理块同一个时间只能执行读取、写入或者擦除的任一操作,这就限制了我们在对一个物理块(Block)进行写入或者擦除操作时候不能读取Flash内容。这也意味着Flash在执行写入或者擦除的时候,处理器不能从Flash中继续读取程序执行代码,否则EFM模块会直接产生bus error(M0+中对应为hardfault)。这也是L系列MCU在进行Flash操作(包括DFlash)的时候必须关闭中断的原因。
在ME0x MCU中总共有3个物理Block,在使用过程中,按照上述原则,处理器在不能对同一个block同时进行读写操作,所以典型的使用方式有如下几种:
PFlash0和PFlash1用于保存程序和运行数据,DFlash用于保存Bootloader程序和模拟EEPROM的代码,这样在boot模式下,软件可以直接对两块PFlash进行读写操作而不必禁用系统中断;而在应用软件运行过程中,软件可以直接操作模拟EEPROM区域而不必禁用系统中断。在上述配置的基础上使用OTA功能,PFlash0和PFlash1分别保存一套程序代码,通过SWAP指令决定下次复位之后从哪个block启动。当使用OTA功能的时候,当前Block运行的程序可以直接对另一个Block的Flash进行擦写操作而不用关闭中断,因为这个时候软件是不会跳转到另外一个block运行的,当然如果软件在操作另外一个block的时候,同时对这个block进行读操作也是不允许的。当前EFM设计中,为了方便软件使用,对于Flash的编程操作是直接将数据写入Flash对应的地址(数据都需要4字节对齐),EFM模块会从写入操作捕获写入操作的地址和数据信息。在这种设计下,软件对Flash地址的写操作并不会产生错误,这个和Flash不支持直接写操作是有一定冲突的,所以严格来说软件需要通过MPU模块对不需要编程的Flash进行禁止写操作保护。注意Flash的写保护操作只能保护Flash内容不被擦除和重新编程,处理器直接对这些地址进行写操作的时候,EFM模块并不会报错,只有软件Launch了扇区擦写(Sector Erase)或者页编程(Program)命令的时候,EFM模块才会检查当前区域时候被保护,如果为保护区域,EFM会abort命令并返回access error错误。
Flash command流程对于Flash的操作都是基于Command来操作的,ME0x支持的command列表如下:
CodeDescriptionNeed Address0x02Program 64 bitsY0x03Program 64bits and read back verifyY0x10Sector eraseY0x11Sector erase and verifyY0x12Erase block (only main array)Y0x13Erase block and then verify (only main array)Y0x1EErase chipN0x20Load AES KeyN (Y in MD1)0x30Boot SwapN0x40Program NVRY0x41Erase NVRY0x42Read NVRY以Flash Program为例,Program一个64 bits并verify的command流程如下:
确认EFM的Prescaler参数设置正确跳转到RAM中执行代码,并根据需要决定是否需要关闭全局中断读取EFM_STS寄存器,判断当前EFM是否正在执行命令向需要PROGRAM的flash地址写入64bit长度数据写0xfd9573f5 到EFM_CMD_UNLOCK解锁FLASH command写0x03到EFM_CMD开始执行command轮询读取EFM_STS寄存器等待命令执行结束读取EFM_STS验证command执行结果EFM寄存器介绍EFM对Flash的所有操作都是通过寄存器接口实现的,这里对EFM寄存器的主要域做一个简单介绍。
CTRL寄存器CTRL寄存器用于配置EFM操作的基础配置,比如低功耗模式是否关闭Flash,选择HCU的KEY,EFM模块的时钟基准,是否开启数据预取加速,数据读取的等待周期和各种中断源的开关。
AES_KEY_SEL用于选择load到HCU中AES key的ID,这个操作后续都换成Flash 命令操作了,仅在ME0x中有该域。
PRESCALER是Flash操作的一个基础分频,软件初始化的时候需要将PRESCALER设置为主频/2MHz,比如Core时钟为120MHz,那么$PRESCALER=120MHz/2MHz=60$,EFM模块会根据这个值来产生program和erase的时间,如果这个值设置不准确(主要是偏小)可能会导致Flash编程或者擦除异常。SDK中在时钟初始化的时候自动适配该值。
RWS是Flash读操作的等待周期,ME0x中Flash的最高频率是40MHz,当主频为120MHz时候,$RWS=(120MHz/40MHz)-1=2$,RWS设置过大会导致系统性能下降,过小则会导致Flash读取数据异常。RWS通过时钟的.flashDiv = *SCU_SYS_CLK_DIV_BY_3 配置,SCU_SYS_CLK_DIV_BY_3* 表示3分频,RWS=2。
STS寄存器STS寄存器保存了Flash操作的状态信息,包含Flash当前是否在执行命令,当前的启动Block,Flash命令的执行结果和错误原因以及ECC错误的标志位。
CMD寄存器软件通过向CMD写入命令来启动flash的相关操作,为了保护CMD不被意外触发,对CMD操作之前需要向CMD_UNLOCK寄存器写入0xfd9573f5来解锁,解锁后软件必须立即写入CMD而不能进行其他寄存器访问,否则CMD会产生Access Error。
TIMING1和TIMING2寄存器这两个寄存器是用来做Flash命令的时序微调,当CTRL_PRESCALER域正确配置时,这两个寄存器保持复位值就可以了,错误的修改这两个值会导致Flash擦写异常。
NVR_ADDR和NVR_DATA这一组寄存器用来传递NVR操作的地址和数据信息。
ADDR_PROT寄存器ADDR_PROT是一组寄存器,ME0x中是3个32位的寄存器,每个寄存器保护一个block,ADDR_PROT的所有bit只能由1写0,bit为1表示对应区域是未保护的,为0则表示对应区域不能进行擦除和写入操作,ADDR_PROT上电时会自动从CFG_NVR的特定地址载入。
ADDR_PROT0:定义了Block0(PFlash0)的保护区域,每个bit保护16KB。
ADDR_PROT1:定义了Block1(PFlash1)的保护区域,每个bit保护16KB。
ADDR_PROT0:定义了Block0(DFlash)的保护区域,每个bit保护8KB。
ECC_ERR_ADDR寄存器该寄存器保存了发生ECC错误的地址,软件可以配合ECC错误的STS标志实现对Flash ECC错误的定位。
Flash操作时间软件应用中对于Flash的编程和擦写的时间一般比较敏感,这里将Flash一些基本操作的时间列举如下:
操作M系列L系列Sector Erase16ms4.5msBlock Erase16ms35msChip Erase16ms35msPage Program45us50us因为Flash操作时间和Prescaler和TIMING寄存器配置有关,上述表格的数据是在Prescaler正确配置和TIMING保持默认值下的数据。
M系列擦除的时间都是16ms,Flash array支持多扇区同时擦写,所以整个block甚至整个芯片的擦除时间都是16ms,当采用erase retry的擦除方式时,EFM会按照800us的周期对flash进行擦除尝试,这种方式下擦写的时间是800us~16ms。Erase retry仅支持单个sector的擦写。
L系列因为只有一个Block的缘故,Block Erase和Chip Erase并没有什么区别,也不支持erase retry功能,另外L系列的Dflash实际和Pflash属于相同的block,所以对Dflash进行擦写操作时候,也不支持对PFlash进行读操作。因此用Dflash模拟EEPROM的时候还是需要禁用系统中断的。
调试禁用和Flash保护为了保护用户的软件代码,ME0x系列支持通过Flash禁用芯片调试端口,这个过程是在芯片初始化过程中通过硬件实现的。同样为了避免程序在运行过程中对Flash进行意外操作,Flash同样支持基于地址的擦写保护,应用程序可以根据实际应用需求限制。
在YTM32B1ME0x中,CUS_NVR的起始地址是0x1001_0000U ,这部分NVR区域中数据定义如下:
Region(32bits)AddressDescriptionDebugger Disable Tag 00x10010000TAG=0x5a5a5a5a Disable debuggerDebugger Disable Tag 10x10010004TAG=0x5a5a5a5a Disable debuggerPROT0 Tag0x10010008TAG=0x5a5a5a5aPROT0 Value0x1001000CADDR_PROT0 Init valuePROT1 Tag0x10010010TAG=0x5a5a5a5aPROT1 Value0x10010014ADDR_PROT1 Init valuePROT2 Tag0x10010018TAG=0x5a5a5a5aPROT2 Value0x1001001CADDR_PROT2 Init valueUser NVR User can use other left space因为NVR区域的页大小也是8 Bytes(64bits),实际单次至少写入两个words。比如当希望在上电后禁用调试端口,那么只需要向0x10010000U地址写入0x5a5a5a5a, 0x5a5a5a5a,那么下次芯片复位之后调试端口默认就不会开启了。
PROT相应的tag和value也是一起编程的,当tag匹配的时候,下次上电对应的value就会自动load到相应的寄存器实现Flash的保护。
HCU KEY区域ME0x芯片内置HCU加速模块,可以实现硬件的AES/SHA/SM4运算加速,FLASH模块支持存储32组长度为256bit的AES KEY,软件在使用这些key的时候可以直接将KEY load到HCU模块,这个过程中软件无法读取KEY的值,可以保证KEY的安全性。
在ME0x中,HCU的KEY是通过EFM_CTRL_AES_KEY_SEL域来选择然后发Flash命令读取的,不过后续项目中将这个操作统一成标准Flash command,用户直接给出KEY存储的地址,然后直接通过命令load。因为KEY的load本身也是一个Flash操作,所以执行该命令的时候同样不支持RWW操作,软件需要关闭中断,并在RAM中执行该命令。
NVR操作注意事项因为NVR区域和block有一定的对应关系,并且block swap还会改动这个对应关系,除非明确软件和NVR不在一个block,否则对于NVR区域的操作还是需要关中断和在RAM中执行。
对于NVR区域的擦除和写入操作的时间与Main Array一致。
最后更新于 2023-06-17 00:30:24 并被添加「」标签,已有 11336 位童鞋阅读过。
本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可。