py32f0-template
py32f0-template copied to clipboard
Unlocking and erasing protected flash on PY32 chips
Hi,
I am experimenting with flash protection on PY32 chips with the following program:
#include <string.h>
#include "main.h"
#include "py32f0xx_bsp_clock.h"
#include "py32f0xx_bsp_printf.h"
static void APP_GPIOConfig(void);
static void APP_FlashSetOptionBytes(void)
{
FLASH_OBProgramInitTypeDef OBInitCfg;
LL_FLASH_Unlock();
LL_FLASH_OB_Unlock();
OBInitCfg.OptionType = OPTIONBYTE_USER;
OBInitCfg.USERType = OB_USER_BOR_EN | OB_USER_BOR_LEV | OB_USER_IWDG_SW | OB_USER_WWDG_SW | OB_USER_NRST_MODE | OB_USER_nBOOT1;
OBInitCfg.USERConfig = OB_BOR_DISABLE | OB_BOR_LEVEL_3p1_3p2 | OB_IWDG_SW | OB_WWDG_SW | OB_RESET_MODE_RESET | OB_BOOT1_SYSTEM;
LL_FLASH_OBProgram(&OBInitCfg);
LL_FLASH_OB_RDP_LevelConfig(OB_RDP_LEVEL_1);
LL_FLASH_Lock();
LL_FLASH_OB_Lock();
/* Reload option bytes */
LL_FLASH_OB_Launch();
}
int main(void)
{
/* Set clock = 8MHz */
BSP_RCC_HSI_8MConfig();
/* Enable peripheral clock */
LL_IOP_GRP1_EnableClock(LL_IOP_GRP1_PERIPH_GPIOA | LL_IOP_GRP1_PERIPH_GPIOB);
BSP_USART_Config(115200);
printf("SPI Demo: nRF24L01 Wireless\r\nClock: %ld\r\n", SystemCoreClock);
APP_GPIOConfig();
LL_mDelay(7000);
uint32_t rdplvl = READ_BIT(FLASH->OPTR, FLASH_OPTR_RDP);
if (rdplvl == OB_RDP_LEVEL_0) {
APP_FlashSetOptionBytes();
}
else
{
printf("RESET has been configurated as RESET\r\n");
}
while (1)
{
printf("nRF24L01 check: error\r\n");
LL_mDelay(2000);
}
}
static void APP_GPIOConfig(void)
{
LL_GPIO_InitTypeDef GPIO_InitStruct;
// PA6 CSN
LL_GPIO_SetPinMode(GPIOA, LL_GPIO_PIN_6, LL_GPIO_MODE_OUTPUT);
// PA5 CE
LL_GPIO_SetPinMode(GPIOA, LL_GPIO_PIN_5, LL_GPIO_MODE_OUTPUT);
/* PA4 as input */
GPIO_InitStruct.Pin = LL_GPIO_PIN_4;
GPIO_InitStruct.Mode = LL_GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = LL_GPIO_PULL_UP;
LL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
void APP_ErrorHandler(void)
{
while (1);
}
#ifdef USE_FULL_ASSERT
void assert_failed(uint8_t *file, uint32_t line)
{
while (1);
}
#endif /* USE_FULL_ASSERT */
After power-cycling the PY32 chip (FY32F003 in SOP-8 package), I can no longer erase the chip using J-Link.
Is there a known method to Unlock and Erase the protected flash memory on PY32 chips?
https://dzone.com/articles/unlocking-and-erasing-flash is a good read on this topic but I didn't find a method which works for PY32 chips yet.
Thanks for the help.
In David's post he mentioned
After setting RDP1, I could no longer program or erase it with J-Link, I had to use PUYA ISP, setting BOOT=1 to enter the serial bootloader and running "Clear chip".
I guess you are in the same situation, but the problem is that SOP8(and SOP16) package has no BOOT pin so you cannot use PuyaISP to reset chip.
If JLink can still detect and connect to the chip, maybe you can use the init and exit steps to unlock and change the option bytes as described in this post
Instead using PuyaISP everytime, I added a erase function triggered by special sequence of keys.
static void rdp_unlock(void){
const FLASH_OBProgramInitTypeDef RDP_Reset = {
.OptionType = OPTIONBYTE_RDP | OPTIONBYTE_USER,
.USERConfig = OB_BOR_DISABLE | OB_BOR_LEVEL_3p1_3p2| OB_IWDG_SW | OB_WWDG_SW | OB_RESET_MODE_RESET | OB_BOOT1_SYSTEM, // Default OPT value
.RDPLevel = OB_RDP_LEVEL_0,
};
LL_FLASH_Unlock();
LL_FLASH_OB_Unlock();
LL_FLASH_OBProgram((FLASH_OBProgramInitTypeDef*)&RDP_Reset);
LL_FLASH_OB_Launch();
while(1); // We shouldn't get here
}
After LL_OB_Launch() the MCU will be instantly erased (Flash erase is forced by hardware when clearing RDP setting).
The RDP will be still active because it's only loaded at boot from the Option bytes, so it needs a power cycle to reload the new setting, then you'll be able to program it again.
I managed to brick few mcus after disabling the bootloader (OB_BOOT1_SRAM) while having a bug in the unlock sequence. But it still can be done, as in this mode BOOT0=1 boots into SRAM.
Modify the linker script so everything is loaded into RAM: (Remove .txt extension) py32f030x6.ld.txt
Run this code:
int main(void)
{
WRITE_REG(FLASH->KEYR, FLASH_KEY1);
WRITE_REG(FLASH->KEYR, FLASH_KEY2);
WRITE_REG(FLASH->OPTKEYR, FLASH_OPTKEY1);
WRITE_REG(FLASH->OPTKEYR, FLASH_OPTKEY2);
FLASH->OPTR = OB_BOR_DISABLE | OB_BOR_LEVEL_3p1_3p2 | OB_IWDG_SW | OB_WWDG_SW | OB_RESET_MODE_RESET | OB_BOOT1_SYSTEM | OB_RDP_LEVEL_0; // Default OPT value
FLASH->CR|=FLASH_CR_OPTSTRT;
FLASH->CR|=FLASH_CR_EOPIE;
*((__IO uint32_t *)(0x40022080))=0xff;
SET_BIT(FLASH->CR, FLASH_CR_OBL_LAUNCH);
while (1){}
}
Compile and run, RDP will be cleared, forcing flash erase, and you'll recover your 11 cents back 😆
This will work in any BOOT1 mode (SYSTEM or SRAM), so it can be used to clear RDP without needing PuyaISP. I simply made a dedicated project for this, called "Clear RDP from SRAM boot".