32F417 - best way to program the FLASH from RAM based code - Page 1
Forum section: electronics / general

author: peter-h
author_rank: Super Contributor
timestamp: July 21, 2021, 08:36:08 pm
content: After thishttps://www.eevblog.com/forum/microcontrollers/how-to-create-elf-file-which-contains-the-normal-prog-plus-a-relocatable-block/msg3611627/#msg3611627I am ready for the next challengeGoogling around, a lot of people get the RM and do it by going directly to the hardware. This bypasses the usually bloated ST libraries, but does it give you anything? The process will be totally time-limited by the FLASH writing speed. The only reason I can see to reinvent the wheel is to avoid bugs in the ST libs.My loader will be extracting the data from an SPI serial FLASH. I have plenty of RAM; certainly enough to write a 16k block in one go.Which ST function would people here recommend?

author: peter-h
author_rank: Super Contributor
timestamp: August 02, 2021, 02:37:39 pm
content: I can see a number of functions provided by STCode:/* Program operations */static void FLASH_Program_DoubleWord(uint32_t Address, uint64_t Data); // needs external VPPstatic void FLASH_Program_Word(uint32_t Address, uint32_t Data); // this one is for normal 3.3V usestatic void FLASH_Program_HalfWord(uint32_t Address, uint16_t Data); // for 2.1V to 3.6Vstatic void FLASH_Program_Byte(uint32_t Address, uint8_t Data); // for 1.8V to 3.6V.static void FLASH_SetErrorCode(void);void FLASH_Erase_Sector(uint32_t Sector, uint8_t VoltageRange)Fairly obviously you use FLASH_Erase_Sector to erase the block (size varies; 16k is the smallest) and then you write that block.The program funcs appear to be non-blocking e.g.Code:static void FLASH_Program_Word(uint32_t Address, uint32_t Data){/* Check the parameters */assert_param(IS_FLASH_ADDRESS(Address));/* If the previous operation is completed, proceed to program the new data */CLEAR_BIT(FLASH->CR, FLASH_CR_PSIZE);FLASH->CR |= FLASH_PSIZE_WORD;FLASH->CR |= FLASH_CR_PG;*(__IO uint32_t*)Address = Data;}so one needs to wait for each one to complete, withCode:HAL_StatusTypeDef FLASH_WaitForLastOperation(uint32_t Timeout){uint32_t tickstart = 0U;/* Clear Error Code */pFlash.ErrorCode = HAL_FLASH_ERROR_NONE;/* Wait for the FLASH operation to complete by polling on BUSY flag to be reset.Even if the FLASH operation fails, the BUSY flag will be reset and an errorflag will be set *//* Get tick */tickstart = HAL_GetTick();while(__HAL_FLASH_GET_FLAG(FLASH_FLAG_BSY) != RESET){if(Timeout != HAL_MAX_DELAY){if((Timeout == 0U)||((HAL_GetTick() - tickstart ) > Timeout)){return HAL_TIMEOUT;}}}/* Check FLASH End of Operation flag */if (__HAL_FLASH_GET_FLAG(FLASH_FLAG_EOP) != RESET){/* Clear FLASH End of Operation pending bit */__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP);}#if defined(FLASH_SR_RDERR)if(__HAL_FLASH_GET_FLAG((FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | FLASH_FLAG_PGAERR | \FLASH_FLAG_PGPERR | FLASH_FLAG_PGSERR | FLASH_FLAG_RDERR)) != RESET)#elseif(__HAL_FLASH_GET_FLAG((FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | FLASH_FLAG_PGAERR | \FLASH_FLAG_PGPERR | FLASH_FLAG_PGSERR)) != RESET)#endif /* FLASH_SR_RDERR */{/*Save the error code*/FLASH_SetErrorCode();return HAL_ERROR;}/* If there is no error flag set */return HAL_OK;}which needs to be heavily chopped down because in the boot loader I have no interrupt servicing so timeouts can't really be implemented. Can the programming really fail, without the device having a defective silicon?

author: peter-h
author_rank: Super Contributor
timestamp: August 08, 2021, 09:54:25 pm
content: I have finished my boot loader and here is some code which somebody may find handy. It is of course based on the HAL code but is stripped down, because a lot of the error conditions are probably impossible unless the silicon is defective, and in any case there is no way to deal with most of them in a typical embedded system.Some basic routines:Code:/**** @param Sector FLASH sector to erase 0-11* Device voltage range is 2.7V to 3.6V, and the operation will be done by word (32-bit)* @retval None* Waits for previous operation to complete.**/static void L_FLASH_Erase_Sector(uint32_t Sector){if (Sector<=11){while(__HAL_FLASH_GET_FLAG(FLASH_FLAG_BSY) != RESET);CLEAR_BIT(FLASH->CR, FLASH_CR_PSIZE);// Clears bits 9,8FLASH->CR |= FLASH_PSIZE_WORD;// sets them to 10CLEAR_BIT(FLASH->CR, FLASH_CR_SNB);// Clears bits 6-3FLASH->CR |= FLASH_CR_SER | (Sector << 3);// SER=1 & load sector # into 6-3FLASH->CR |= FLASH_CR_STRT;}}/*** @brief Locks the FLASH control register access* @retval*/static void L_HAL_FLASH_Lock(void){// Wait for any flash operation to finishwhile(__HAL_FLASH_GET_FLAG(FLASH_FLAG_BSY) != RESET);// Set the LOCK Bit to lock the FLASH Registers accessFLASH->CR |= FLASH_CR_LOCK;}/*** @brief Unlock the FLASH control register access* Issues the unlok codes*/static void L_HAL_FLASH_Unlock(void){// Wait for any flash operation to finishwhile(__HAL_FLASH_GET_FLAG(FLASH_FLAG_BSY) != RESET);// Write the two unlock codesWRITE_REG(FLASH->KEYR, FLASH_KEY1);WRITE_REG(FLASH->KEYR, FLASH_KEY2);}/*** @brief Program word (32-bit) at a specified address.* @note This function must be used when the device voltage range is from* 2.7V to 3.6V.** @note If an erase and a program operations are requested simultaneously,* the erase operation is performed before the program one.** @param Address specifies the address to be programmed.* @param Data specifies the data to be programmed.* @retval None* Waits for previous operation to finish**/static void L_FLASH_Program_Word(uint32_t Address, uint32_t Data){// wait for any previous op to finishwhile(__HAL_FLASH_GET_FLAG(FLASH_FLAG_BSY) != RESET);// clear program size bitsCLEAR_BIT(FLASH->CR, FLASH_CR_PSIZE);// reload program size bitsFLASH->CR |= FLASH_PSIZE_WORD;// enable programmingFLASH->CR |= FLASH_CR_PG;// write the data in*(volatile uint32_t*)Address = Data;}I start the boot loader with a bit which tests the uppermost flash sector (sector 11, size 128k) by erasing it, verifying the erasure, programming it with varying data, and verifying that, before proceeding with the restCode:// ===== Make sure we can erase and program sector 11 - the top one =====// If that works, the rest should work :)uint32_t error=0;uint32_t data;uint32_t address;// Erase sectorL_HAL_FLASH_Unlock();L_FLASH_Erase_Sector(11);L_HAL_FLASH_Lock();// Check it is all FFsfor (address=0x080e0000; address<0x080fffff; address+=4){data=*(volatile uint32_t*)address;if ( data != 0xffffffff )error++;}// Erase sector againL_HAL_FLASH_Unlock();L_FLASH_Erase_Sector(11);// Fill it with datafor (uint32_t i=0; i<(128*1024); i+=4){L_FLASH_Program_Word(i+0x080e0000, i);}// Probably always best to lock the flash again before reading itL_HAL_FLASH_Lock();// Check the data we have just writtendata=0;for (address=0x080e0000; address<0x080fffff; address+=4){if ( (*(volatile uint32_t*)address) != data )error++;data+=4;}// If any errors, set up a code on LEDs and allow override with a buttonif (error!=0)and here is another piece of codeCode:// Erase whole 1MB except boot block// This is done in one go, rather than inside the loop below, to avoid complicated// coding around the variable size sectors (16k to 128k). Exec time 9 seconds.L_HAL_FLASH_Unlock();for (uint32_t i=2;i<=11;i++){L_FLASH_Erase_Sector(i);}L_HAL_FLASH_Lock();// Check it is all FFserror=0;for (address=0x08080000; address<0x080fffff; address+=4){data=*(volatile uint32_t*)address;if ( data != 0xffffffff )error++;}pagebase=4100+64;// 4100-5119 factory image in serial FLASH pagescpubase=0x08008000;// base of CPU FLASH, after boot block// Program entire image in one go// Loop which always does the whole 512k (16 32k blocks) except block 0for (uint32_t block32k=1; block32k<16; block32k++)// 1-15{// Read each 32k block into buffer1buffer1idx=0;for ( uint32_t page=pagebase; page<(pagebase+64); page++ ){AT45dbxx_ReadPage(&buffer1,512,page*512); // reads 512 bytes at a time, from serial FLASH address of page*512buffer1idx+=512;}// Program 32k blockL_HAL_FLASH_Unlock();for (uint32_t i=0; i<(32*1024); i+=4){uint32_t data=buffer1|(buffer1[i+1]<<8)|(buffer1[i+2]<<16)|(buffer1[i+3]<<24);L_FLASH_Program_Word(i+cpubase, data);}L_HAL_FLASH_Lock();// Verify 32k block against buffer1for (uint32_t i=0; i<(32*1024); i+=4){uint32_t data=buffer1|(buffer1[i+1]<<8)|(buffer1[i+2]<<16)|(buffer1[i+3]<<24);if ( (*(volatile uint32_t*) (i+cpubase)) != data ) error++;}cpubase+=(32*1024);pagebase+=64;block32k++;}attempt++;}while ( (attempt<NUM_ATTEMPTS) && (error!=0) );I am allowing up to 5 attempts but in reality I never saw any errors.You can see above that erasing most of the 1MB FLASH takes 9 seconds. Erasing and programming the two bottom blocks (2x16k) takes 0.6 second (I measured that carefully) which extrapolates to 15-20 seconds for erasing and writing the whole 1MB. That includes reading the data from a serial FLASH over SPI running at 21mbps, so the raw time would be nearer to 15s. One could perhaps speed this up a little by interleaving data fetching with the FLASH programming cycle (the above code is blocking so it waits).