文档主要是通过看英文手册并进行翻译总结得到的。术语尽可能也中文文档一致,但有几个因中文文档翻译有误(一词多义没翻译好)就自己翻译了。而如Secure-only这样翻译了也不好看的就不保留英文了。
本笔记是基于[4]继续写的,故一些概念就没有再重复了。
若要看基本功能例程,请直接拉到最后。并说明了ST固件库只提供的基本功能API调用,可作为学习寄存器的参考,若需要写高性能、针对具体场景的驱动程序,则需要自己进行修改。
最基本的理解:Flash用来存代码,RAM用来存数据(如堆,栈,全局变量,静态变量)。
补充说明:由于Flash的非易失性,以及内存接口单元提供的电路逻辑,Flash可以用来存储敏感的信息(sensitive imformation)。
a、同时(simultaneous)操作:两块Bank,允许独立访问,互补干涉。
b、读/写缓冲区,支持连续的读/写访问( 单次访问(single accesses)和突发访问(burst accesses)).
c、由于AXI总线接口的存在,不仅允许32bits、16bits and bits 访问,也允许双字节(64bits)读操作。
下图为STM32H743的Flash架构图
如果时序太严,内存将不能正确操作;太松,编程速度将受限。时序约束通过设置CPU等待特定的Flash时钟周期(Number of wait states)和编程延时(Programming delay)来实现。(这部分与stm32其他系列类似)
注:Flash使用的时钟是微控器系统时钟(sys_ck)或者说AXI总线时钟。这个约束也是CPU动态调整内核频率时需要考虑的。
闪存接口逻辑单元与闪存的数据线位宽为256bits,即一个闪存字。写操作只允许以闪存字为单位进行,但由于AXI总线的位宽为64bits,于是需要一个闪存字缓冲区。写操作步骤简单说如下:
每个闪存字都与10bits的错误纠正码(Error Correcting Code,ECC)绑定,写闪存时伴随生成。开启ECC功能后,进行读闪存操作时,才会检验闪存字中的数据是否正确。具体设置方式见手册。
注:读/写队列的操作是总线与闪存接口逻辑单元间进行的,终端用户无需考虑。
编程平行机制(programming parallellism):由PSIZE1/2设置,默认一字节写。它与峰值电流和执行效率的权衡关系有关。
可以全擦除(mass erase),或对bank、section进行擦除。擦除前需注意错误状态和保护区域从属状况。具体见手册和HAL_FLASHEx_Erase()函数。这部分可以说是Flash中最复杂的了,等需要时再看吧。
若DMEP==1,PCROP区域将在RDP等级从1变0时擦除。若DMES==1,Secure-only区域将在RDP等级从1变0时擦除。(这一点在手册中反反复复的出现!)
接收到读操作后,闪存中的闪存字被读到读缓冲区,总线再从缓冲区中取特定的字节。若下次读和上次读操作属于同一个闪存字中的内存,那么后一次读操作将直接从缓冲区获取数据。
闪存的存储空间可分为三类:
a\第一类选项字集合包含当前选项字的值。这些寄存器的名字以“_CUR”结尾,并且是只读的。在非易失存储发生上电复位、从standby模式唤醒或选项字修改操作发生时,这类寄存器的值将被自动导入。
b\第二类选项字集合是可修改的。这些寄存器的名字以“PRG”结尾。虽然这些选项字可修改,并必须是解锁后才能修改。修改过程请在手册中搜索“Option byte modification sequence ”
全局设置的读保护不仅保护Flash,也保护Backup SRAM 和 RTC backup registers.
下表仅写了理解的部分,不理解的部分没写。
Level 0 | 如果没有其他保护机制的存在,对Flash内存的读、写和擦除操作的是可行的。不能调试。 |
Level 1 |
为默认等级 入侵原:从RAM启动,从系统闪存启动,有调试器连接。 当有入侵发生时,错误标志位会置位。 |
Level 2 |
除了SWAP位外的所有选项字的位都不能被修改;不能调试;程序不能从RAM启动; 不用从意法公司提供的bootloader启动。 |
带RDP不为Level2的前提下,WPRS每个区域(sector)可被独立的设置为写保护,并且出厂默认设置每个sector都为没有写保护。请注意,WRPSn1/2为0时才是使能保护。
由于Secure-only area和proprietary code readout protection area很类似,故放在一起讲。
专有代码读出保护PCROP区或安全区若使能,那么设置的最小区域为16个闪存字(flash word),最大为整个内存块(bank)。出厂默认设置为:安全区使能,区间[0x00,0xFF]*flash_word;PCROP区域未使能。
错误地写操作将被忽略,错误地读操作将读到0,不会才产生总线错误,但错误标志位会被置位。
Keil不具有代码保护区域设置的功能,若要有这个功能,可能需要用户自己写个bootloader协议,做个上位机软件,感觉挺复杂的。
特殊保护区域要使能的默认条件是区域开始闪存字序号值小于等于区域结束闪存字序号值。由手册中“Table 17. Option byte organization”值两个区域都没有使能。
本质:只可执行(executable-only),CPU可以从这个区域取指令,但不可以在这个区域读数据,擦除数据。
待:PCROP中的指令若要取本区域文本池中的数据是否可行?
应用:可有效地保护第三方软件的知识产权。
属性:
1、当执行本区域代码时,调试事件将被忽略。
2、除非:RDP降级且DMEP==1,否则正常情况下不可擦除。
2、只有CPU能访问,而且CPU只能获取(fetch)指令来执行。
安全访问模式:可以选择复位后进入安全访问模式的BootLoader,在该模式下可以访问Secure-only area并进行一些安全性操作,在操作完成后,可跳转到用户存储器并运行用户程序(而标准模式下的BootLoader不具有跳转功能)。
Bit 21 SECURITY: Security enable option configuration bit
The SECURITY option bit enables the secure access mode of the device during an option byte change. The change will be taken into account at next reset (next boot sequence). Once it is enabled, the security feature can be disabled if no areas are protected by PCROP or Secure access mode. If there are secure-only or PCROP protected areas, perform a level regression (from level 1 to 0) and set all the bits to unprotect secure-only areas and PCROP areas.
Secure-only area 本质:当SECURITY选项位为1时,表明程序正在执行安全应用程序,即处于安全访问模式(secure access mode)。在该模式下,Secure-only区域才能访问。
应用:安全程序更新,经典的安全启动(或自举)库,第三方安全库。
属性:
1、当执行本区域代码时,调试事件将被忽略。
2、除非:RDP降级且DMES==1,否则正常情况下不可擦除。
3、只用ST安全库或用户安全应用程序可以访问。
这些错误可 FLASH_CR1/2 register使能中断事件,多个中断时间公用一个中断线,对应的中断信号称为flassh_it。从“.s”文件知,中断句柄为FLASH_IRQHandler。
操作错误 |
OPERR |
当 Flash 在写操作或擦除操作期间检测到错误。 此错误可能是由于循环问题导致的不正确的非易失性内存行为或由于系统重置导致之前的修改操作停止而导致的。(不是很明白它的意思) |
写保护错误 |
WRPERR |
|
读保护错误 |
RDPERR |
保护的区域为:安全(secure-only)区域或RDP保护区域、 当尝试读这些区域的值时,RDPERR将被置位。 当尝试执行这个区域的代码时,将会触发总线错误。 |
读安全(区)错误 |
RDSERR |
(不是很确定) 应该是针对安全区域设置的,读保护错误包含了读安全错误 |
选项字改变错误 |
OPTCHANGEERR |
略 |
闪烁错误 Strobe error |
STRBERR |
当用户应用程序多次向写缓冲区中写入同一字节时,会通过 硬件将此位置 1。 |
不连续错误 inconsistency error |
INCERR |
在前一次写操作完成前又尝试进行新的写操作。 注:错误发生时,请按手册中的方式处理。状态位必须由软件清零。 ??应该与包突发有关 wrap burst |
编程顺序错误 |
PGSERR |
情形1:若在下一次写操作前,没有清除不连续错误,则会触发该错误。 情形2:在进行写操作时,控制写缓冲区的标志位PG1/2没使能(默认失能)。 状态位必须由软件清零。 |
用于错误纠正的编码错误 Error correction code error |
SNECCERR DBECCERR |
单位错误可以纠正,返回正确结果,SNECCERR被置位。 两位错误不可纠正,可发现,DNECCERR被置位,并且产生总线错误。 注:有FLASH_CRCCR1/2寄存器知,默认不进行CRC校验。 |
杂项硬件错误 |
|
情形1-AXI系统总线:当全局设置读保护RDP为Level1时,进行了与该设置错误的访问。与安全区域保护冲突。 情形2-AHB设置总线:开解锁时的错误 |
错误状态测试代码:
void Flash_Error_Test(void)
{
char *p1 = (char*)0x8100000;//User Flash area
*p1 = 0x0A; // raise PGSERR
__DSB();
Debug_Printf("@0x%x\r\n",FLASH->SR2);
if(__HAL_FLASH_GET_FLAG_BANK2(FLASH_FLAG_PGSERR_BANK2))
Debug_Printf("@WRPERR\r\n");
__HAL_FLASH_CLEAR_FLAG_BANK2(FLASH_FLAG_PGSERR_BANK2);
p1 = (char*)0x1FF40000;//System Flash area
*p1 = 0x0A; // raise PGSERR
__DSB();
Debug_Printf("@0x%x\r\n",FLASH->SR2);
if(__HAL_FLASH_GET_FLAG_BANK2(FLASH_FLAG_PGSERR_BANK2))
Debug_Printf("@WRPERR\r\n");
__HAL_FLASH_CLEAR_FLAG_BANK2(FLASH_FLAG_PGSERR_BANK2);
FLASH->OPTSR_PRG = 0x0A;
__DSB();
Debug_Printf("@0x%x\r\n",FLASH->SR2);
}
注:闪存设置保护层面的并不会产生错误,所有上面的PGSERR错误仅为没有使能写缓冲区。
擦除和写测试代码:
void Flash_Write_Test(void)
{
char *p1 = (char*)0x8100000;
uint32_t sectERR;
static uint8_t flashWordData[256] = {'f','l','a','s','h'};
FLASH_EraseInitTypeDef EraseInit =
{
.TypeErase = FLASH_TYPEERASE_SECTORS,
.Banks = FLASH_BANK_2,
.Sector = FLASH_SECTOR_0,
.NbSectors = 1,
.VoltageRange = FLASH_VOLTAGE_RANGE_1
};
HAL_FLASHEx_Unlock_Bank2();
HAL_FLASHEx_Erase(&EraseInit,§ERR);
Debug_Printf("after erase:0x%x\r\n",*p1);
HAL_FLASH_Program(FLASH_TYPEPROGRAM_FLASHWORD,0x8100000,(uint64_t) flashWordData);
Debug_Printf("Writing result :%s\r\n",p1);
HAL_FLASHEx_Erase(&EraseInit,§ERR);
Debug_Printf("after erase:0x%x\r\n",*p1);
HAL_FLASHEx_Lock_Bank2();
}
注:打印函数使用非屏蔽DMA方式发送,波特率为460800。ST固件库中没有强制写功能,而且完全没有用上架构上的队列功能,好处是不会出现闪烁错误和不连续错误,详见FLASH_WaitForLastOperation()函数和SR寄存器的第0~2位。
测试结果:可以看出擦除/写Flash是一个很慢的过程,真正应用时应使用非阻塞方式。
[16:12:27.989]收←◆after erase:0xff
[16:12:29.875]收←◆Writing result :flash
after erase:0xff
参考资料
[1] RM0433 Reference manual :STM32H742, STM32H743/753 and STM32H750 Value line advanced Arm®-based 32-bit MCUs
[2] AN2606 Application note :STM32 microcontroller system memory boot mode
[3] AN3155 Application note : USART protocol used in the STM32 bootloader
[4]STM32H743:程序的启动 https://blog.csdn.net/NoDistanceY/article/details/104400204