YTM32系列MCU Flash模块功能详解

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大小为2K
  • 256KB 单独的数据存储块,带有ECC功能,Sector大小为1K
  • 4KB的NVR区域,带有ECC功能,Sector大小为1K
  • 支持Flash按区域(16KB单位)的写保护功能
  • 支持调试器禁用
  • 支持Flash命令执行结束和异常中断
  • 支持Block Swap的OTA升级功能
  • 支持ECC错误地址记录和单比特、多比特中断
  • 支持OTP(One Time Program,一次可编程)区域
  • 支持HCU 密钥存储(可擦写,不可读取)
  • 支持块擦除、扇区(sector)擦除和整个chip的擦除操作
  • 写入页大小为8Bytes

Flash的基本术语约定

为了便于理解,这里整理一下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)BlockProtect
PFlash00x0000_00000x0007_FFFF5120ADDR_PROT0
PFlash10x0008_00000x000F_FFFF5121ADDR_PROT1
DFlash0x0010_00000x0013_FFFF2562ADDR_PROT2
AES_NVR0x1000_00000x1000_03FF10No Read + Customer Key
OTP_NVR0x1001_00000x1001_03FF10No Erase
BOOT_NVR0x1002_00000x1002_03FF11SWAP CMD only
CUS_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同时进行读写操作,所以典型的使用方式有如下几种:

  1. PFlash0和PFlash1用于保存程序和运行数据,DFlash用于保存Bootloader程序和模拟EEPROM的代码,这样在boot模式下,软件可以直接对两块PFlash进行读写操作而不必禁用系统中断;而在应用软件运行过程中,软件可以直接操作模拟EEPROM区域而不必禁用系统中断。
  2. 在上述配置的基础上使用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 Address
0x02Program 64 bitsY
0x03Program 64bits and read back verifyY
0x10Sector eraseY
0x11Sector erase and verifyY
0x12Erase block (only main array)
Y
0x13Erase block and then verify (only main array)Y
0x1EErase chip
N
0x20Load AES Key
N (Y in MD1)
0x30Boot Swap
N
0x40Program NVR
Y
0x41Erase NVR
Y
0x42Read NVRY

以Flash Program为例,Program一个64 bits并verify的command流程如下:

  1. 确认EFM的Prescaler参数设置正确
  2. 跳转到RAM中执行代码,并根据需要决定是否需要关闭全局中断
  3. 读取EFM_STS寄存器,判断当前EFM是否正在执行命令
  4. 向需要PROGRAM的flash地址写入64bit长度数据
  5. 0xfd9573f5EFM_CMD_UNLOCK解锁FLASH command
  6. 写0x03到EFM_CMD开始执行command
  7. 轮询读取EFM_STS寄存器等待命令执行结束
  8. 读取EFM_STS验证command执行结果

EFM寄存器介绍

EFM对Flash的所有操作都是通过寄存器接口实现的,这里对EFM寄存器的主要域做一个简单介绍。

CTRL寄存器

CTRL寄存器用于配置EFM操作的基础配置,比如低功耗模式是否关闭Flash,选择HCU的KEY,EFM模块的时钟基准,是否开启数据预取加速,数据读取的等待周期和各种中断源的开关。

EFM CTRL寄存器

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.5ms
Block Erase16ms35ms
Chip Erase16ms35ms
Page 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)AddressDescription
Debugger Disable Tag 00x10010000TAG=0x5a5a5a5a Disable debugger
Debugger Disable Tag 10x10010004TAG=0x5a5a5a5a Disable debugger
PROT0 Tag0x10010008TAG=0x5a5a5a5a
PROT0 Value0x1001000CADDR_PROT0 Init value
PROT1 Tag0x10010010TAG=0x5a5a5a5a
PROT1 Value0x10010014ADDR_PROT1 Init value
PROT2 Tag0x10010018TAG=0x5a5a5a5a
PROT2 Value0x1001001CADDR_PROT2 Init value
User 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一致。


本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可。

发表新评论