Flash(闪存)是一种可擦除的只读存储器,按照实现方式和运行特性Flash一般还会分为NOR和NAND两种。其中NOR Flash支持随机地址的读取方式,在读取操作上类似于RAM,比较适合程序的直接读取运行,而NAND Flash读取是基于页的方式,一般无法随机读取。在MCU中,Flash需要支持程序和数据的存储,所以实现方式上也都是NOR Flash。
YTM32系列MCU中Flash的控制是通过EFM(Embedded Flash Module)控制的,这里以ME0x系列MCU为例,EFM模块支持如下的一些功能:
为了便于理解,这里整理一下Flash使用中常用的一些基本术语:
这里以ME0x为例,芯片系统存储的memory map如下
Name | Start Address | End Address | Size(KB) | Block | Protect |
---|---|---|---|---|---|
PFlash0 | 0x0000_0000 | 0x0007_FFFF | 512 | 0 | ADDR_PROT0 |
PFlash1 | 0x0008_0000 | 0x000F_FFFF | 512 | 1 | ADDR_PROT1 |
DFlash | 0x0010_0000 | 0x0013_FFFF | 256 | 2 | ADDR_PROT2 |
AES_NVR | 0x1000_0000 | 0x1000_03FF | 1 | 0 | No Read + Customer Key |
OTP_NVR | 0x1001_0000 | 0x1001_03FF | 1 | 0 | No Erase |
BOOT_NVR | 0x1002_0000 | 0x1002_03FF | 1 | 1 | SWAP CMD only |
CUS_NVR | 0x1003_0000 | 0x1003_03FF | 1 | 1 | Customer 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的特性是擦除之后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同时进行读写操作,所以典型的使用方式有如下几种:
当前EFM设计中,为了方便软件使用,对于Flash的编程操作是直接将数据写入Flash对应的地址(数据都需要4字节对齐),EFM模块会从写入操作捕获写入操作的地址和数据信息。在这种设计下,软件对Flash地址的写操作并不会产生错误,这个和Flash不支持直接写操作是有一定冲突的,所以严格来说软件需要通过MPU模块对不需要编程的Flash进行禁止写操作保护。注意Flash的写保护操作只能保护Flash内容不被擦除和重新编程,处理器直接对这些地址进行写操作的时候,EFM模块并不会报错,只有软件Launch了扇区擦写(Sector Erase)或者页编程(Program)命令的时候,EFM模块才会检查当前区域时候被保护,如果为保护区域,EFM会abort命令并返回access error错误。
对于Flash的操作都是基于Command来操作的,ME0x支持的command列表如下:
Code | Description | Need Address |
---|---|---|
0x02 | Program 64 bits | Y |
0x03 | Program 64bits and read back verify | Y |
0x10 | Sector erase | Y |
0x11 | Sector erase and verify | Y |
0x12 | Erase block (only main array) | |
Y | ||
0x13 | Erase block and then verify (only main array) | Y |
0x1E | Erase chip | |
N | ||
0x20 | Load AES Key | |
N (Y in MD1) | ||
0x30 | Boot Swap | |
N | ||
0x40 | Program NVR | |
Y | ||
0x41 | Erase NVR | |
Y | ||
0x42 | Read NVR | Y |
以Flash Program为例,Program一个64 bits并verify的command流程如下:
0xfd9573f5
到EFM_CMD_UNLOCK
解锁FLASH commandEFM对Flash的所有操作都是通过寄存器接口实现的,这里对EFM寄存器的主要域做一个简单介绍。
CTRL寄存器用于配置EFM操作的基础配置,比如低功耗模式是否关闭Flash,选择HCU的KEY,EFM模块的时钟基准,是否开启数据预取加速,数据读取的等待周期和各种中断源的开关。
EFM CTRL寄存器
AES_KEY_SEL
用于选择load到HCU中AES key的ID,这个操作后续都换成Flash 命令操作了,仅在ME0x中有该域。
PRESCALER
是Flash操作的一个基础分频,软件初始化的时候需要将PRESCALER设置为主频/2MHz,比如Core时钟为120MHz,那么,EFM模块会根据这个值来产生program和erase的时间,如果这个值设置不准确(主要是偏小)可能会导致Flash编程或者擦除异常。SDK中在时钟初始化的时候自动适配该值。
RWS
是Flash读操作的等待周期,ME0x中Flash的最高频率是40MHz,当主频为120MHz时候,,RWS设置过大会导致系统性能下降,过小则会导致Flash读取数据异常。RWS通过时钟的.flashDiv = *SCU_SYS_CLK_DIV_BY_3
* 配置,SCU_SYS_CLK_DIV_BY_3
表示3分频,RWS=2。
STS寄存器保存了Flash操作的状态信息,包含Flash当前是否在执行命令,当前的启动Block,Flash命令的执行结果和错误原因以及ECC错误的标志位。
软件通过向CMD写入命令来启动flash的相关操作,为了保护CMD不被意外触发,对CMD操作之前需要向CMD_UNLOCK
寄存器写入0xfd9573f5
来解锁,解锁后软件必须立即写入CMD而不能进行其他寄存器访问,否则CMD会产生Access Error。
这两个寄存器是用来做Flash命令的时序微调,当CTRL_PRESCALER
域正确配置时,这两个寄存器保持复位值就可以了,错误的修改这两个值会导致Flash擦写异常。
这一组寄存器用来传递NVR操作的地址和数据信息。
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错误的地址,软件可以配合ECC错误的STS标志实现对Flash ECC错误的定位。
软件应用中对于Flash的编程和擦写的时间一般比较敏感,这里将Flash一些基本操作的时间列举如下:
操作 | M系列 | L系列 |
---|---|---|
Sector Erase | 16ms | 4.5ms |
Block Erase | 16ms | 35ms |
Chip Erase | 16ms | 35ms |
Page Program | 45us | 50us |
因为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的时候还是需要禁用系统中断的。
为了保护用户的软件代码,ME0x系列支持通过Flash禁用芯片调试端口,这个过程是在芯片初始化过程中通过硬件实现的。同样为了避免程序在运行过程中对Flash进行意外操作,Flash同样支持基于地址的擦写保护,应用程序可以根据实际应用需求限制。
在YTM32B1ME0x中,CUS_NVR
的起始地址是0x1001_0000U
,这部分NVR区域中数据定义如下:
Region(32bits) | Address | Description |
---|---|---|
Debugger Disable Tag 0 | 0x10010000 | TAG=0x5a5a5a5a Disable debugger |
Debugger Disable Tag 1 | 0x10010004 | TAG=0x5a5a5a5a Disable debugger |
PROT0 Tag | 0x10010008 | TAG=0x5a5a5a5a |
PROT0 Value | 0x1001000C | ADDR_PROT0 Init value |
PROT1 Tag | 0x10010010 | TAG=0x5a5a5a5a |
PROT1 Value | 0x10010014 | ADDR_PROT1 Init value |
PROT2 Tag | 0x10010018 | TAG=0x5a5a5a5a |
PROT2 Value | 0x1001001C | ADDR_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的保护。
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区域和block有一定的对应关系,并且block swap还会改动这个对应关系,除非明确软件和NVR不在一个block,否则对于NVR区域的操作还是需要关中断和在RAM中执行。
对于NVR区域的擦除和写入操作的时间与Main Array一致。
全部0条评论
快来发表一下你的评论吧 !