cancel
Showing results for 
Search instead for 
Did you mean: 

How do I modify Linker Script for reserving one Flash Sector for user data? [STM32H7]

bnguy.1
Associate III

How do you modify the linker script to reserve a sector for user data... and such that this user sector won't get erased when re-programming/bootloading?

The dual-core STM32H7 has TWO 8-sector Banks (0x8000000-0x80FFFFF & 0x8100000-0x81FFFFF).. with linker scripts for *EACH* cpu (STM32H745XIHX_RAM.ld, STM32H745XIHX_FLASH.ld). I assume the CPUs can run from either bank/sector, including execute from the same.. so for now, I've tried modifying just the M7's STM32H745XIHX_FLASH.ld :

MEMORY
{
FLASH   (xr)     : ORIGIN = 0x08000000, LENGTH = 1920K
USER_FLASH (xrw) : ORIGIN = 0x081E0000, LENGTH = 128K   ********
RAM (xrw)        : ORIGIN = 0x20000000, LENGTH = 512K 
RAM_D2 (xrw)     : ORIGIN = 0x30000000, LENGTH = 
RAM_D3 (xrw)     : ORIGIN = 0x38000000, LENGTH = 
ITCMRAM (xrw)    : ORIGIN = 0x00000000, LENGTH = 64K
}
 
 
// Section must be 32-bytes aligned (size of a Flash Word)
  .NVM: ALIGN(0x10)
  {
    . = ALIGN(4);
    _NVM= .;
    . += NVM_SIZE ;
  } >USER_FLASH
 
main.c:
 
// 32-byte structure
typedef struct __packed {
    uint8_t val1;
    uint32_t val2[2];  // 8 bytes
    uint16_t val3;
    uint16_t val4;
    uint32_t val5[4];  // 16 bytes
    uint8_t val6[2];   // 2 bytes
    uint8_t val7;
} stNVM;
 
 
// Declare 32-byte variables inside USER_FLASH , should this be const??
__attribute__((__section__(".USER_FLASH"))) uint8_t NVM_user_data[16];  
extern volatile stNVM _NVM;
 
void test ( void )
{
    // flash 32 bytes at a time.
	HAL_FLASH_Unlock();
 
	// Erase / Write 32-bytes
	FLASH_PageErase(0x081E0000);
	CLEAR_BIT (FLASH->CR, (FLASH_CR_PER));
	HAL_FLASH_Program(TYPEPROGRAM_HALFWORD, 0x081E0000, NVM_user_data);
	CLEAR_BIT (FLASH->CR, (FLASH_CR_PG));
 
	// Erase / Write Next 32-bytes
	FLASH_PageErase(0x081E0000+32);
	CLEAR_BIT (FLASH->CR, (FLASH_CR_PER));
	HAL_FLASH_Program(TYPEPROGRAM_HALFWORD, 0x081E0000+32, _NVM);
	CLEAR_BIT (FLASH->CR, (FLASH_CR_PG));
	HAL_FLASH_Lock();
}

However, not sure if its cache related, or linker script / variable declaration, but my program does not write to flash, and if I use CubeMxProgammer to manually put data in the USER_FLASH space, it will get wiped out when I re-pgrogram the application via the Bootloader.

6 REPLIES 6
alister
Lead

>How do you modify the linker script to reserve a sector for user data...

https://sourceware.org/binutils/docs/ld/index.html

USER_FLASH is already aligned.

Why is stNVM packed? It appears unnecessary. Doesn't the compiler know how to optimally align its members? Sectors are large and only a fraction of USER_FLASH will be used. It's increasing the code size and increasing its execution time.

Why is _NVM volatile? It appears unnecessary. Or is another thread going to erase and re-write it?

SECTIONS
{
  <snip>
  
  NVM (NOLOAD) :
  {
    *(NVM)
  } >USER_FLASH
}
 
 
typedef struct {
    uint8_t val1;
    uint32_t val2[2];  // 8 bytes
    uint16_t val3;
    uint16_t val4;
    uint32_t val5[4];  // 16 bytes
    uint8_t val6[2];   // 2 bytes
    uint8_t val7;
} stNVM;
 
const stNVM _NVM __attribute__ ((section ("NVM")));

>and such that this user sector won't get erased when re-programming/bootloading?

Your code decides when and whether a sector is erased.

You may write protect it too. That'd help protect against your not coding properly and/or circumstances you can't control.

The thing with details that appear unnecessary is a reader would have to assume (a) the developer doesn't know their craft or (b) the developer knows something they don't. If it's the latter, it's good practice to comment why.

Ok, this is a muddled mess

The sectors are LARGE (128K), erased ONCE

FLASH_PageErase()?

Your HAL_FLASH_Program() function looks to try to overwrite the blank data with ITSELF, only writing 16-bits (2-bytes)

uint8_t NVM_user_data[16]; // this is 16 bytes, not 32

Linker script and __attribute__ stuff here is confused . The section would want to be NOLOAD so you don't write content into it.

Perhaps tell the linker not to use the memory, and then it won't use it?

You've shrunk the memory, this is all you need to keep the linker out of it

FLASH (xr) : ORIGIN = 0x08000000, LENGTH = 1920K

Consider using a POINTER with an casted assignment to the address you're using?

extern volatile stNVM *pNVM = (stNVM *)0x081E0000;

printf("%d\n", pNVM->val1);

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
// ADDR_FLASH_SECTOR_7_BANK2 0x081E0000
 
{
  FLASH_EraseInitTypeDef EraseInitStruct = {0};
  uint32_t SECTORError = 0;
 
  HAL_FLASH_Unlock();
 
  /* Fill EraseInit structure*/
  EraseInitStruct.TypeErase     = FLASH_TYPEERASE_SECTORS;
  EraseInitStruct.VoltageRange  = FLASH_VOLTAGE_RANGE_3;
  EraseInitStruct.Banks         = FLASH_BANK_2;
  EraseInitStruct.Sector        = FLASH_SECTOR_7;
  EraseInitStruct.NbSectors     = 1;
 
  if (HAL_FLASHEx_Erase(&EraseInitStruct, &SECTORError) == HAL_OK)
  {
// Success
  }
 
  HAL_FLASH_Lock();
}

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..

Thanks, I understand the 128KB sector size is huge compared to the few bytes of data I need to store.. but the only other place would be use the OPTION BYTE area, byte I rather not mess with that for fear of bricking. I'll need to write to erase/flash quite frequently , hopefully the 10k number of write cycles won't be an issue.

I fixed the test code to erase only once, I've also expanded FLASH_PageErase to show what it's doing.. and simplified the source buffer, to just be an array of 32-bit data. It seems like it's writing now, but still seems there's a pesky issue with the source pointer math.

uint32_t ulDATA_TO_WRITE [ 128 ];
 
void test ( void )
{
	static FLASH_EraseInitTypeDef EraseInitStruct;
	uint32_t SECTORError;
	int sofar=0;
	
	HAL_FLASH_Unlock();
 
	// Erase 
	uint32_t StartSector = GetSector(0x081E0000);
	uint32_t EndSectorAddress = StartSectorAddress + numberofwords*4;
	uint32_t EndSector = GetSector(EndSectorAddress);
	EraseInitStruct.TypeErase     = FLASH_TYPEERASE_SECTORS;
	EraseInitStruct.VoltageRange  = FLASH_VOLTAGE_RANGE_3;
	EraseInitStruct.Sector        = StartSector;
	EraseInitStruct.NbSectors     = (EndSector - StartSector) + 1;
	HAL_FLASHEx_Erase(&EraseInitStruct, &SECTORError);
	//CLEAR_BIT (FLASH->CR, (FLASH_CR_PER));   //  Flush needed DCRST / ICRST?  
	
	// Write
	int numberofwords = (strlen(ulDATA_TO_WRITE)/4) + ((strlen(ulDATA_TO_WRITE) % 4) != 0);
	while (sofar<numberofwords)
	{
		 if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_FLASHWORD, StartSectorAddress, ulDATA_TO_WRITE[sofar]) == HAL_OK)
		 {
			 StartSectorAddress += 4; // 2 // 8
			 sofar++;
		 }
	}
	//CLEAR_BIT (FLASH->CR, (FLASH_CR_PG));
	HAL_FLASH_Lock();
}

:

"The thing with details that appear unnecessary is a reader would have to assume (a) the developer doesn't know their craft or (b) the developer knows something they don't. If it's the latter, it's good practice to comment why."

Unfortunately, it's the former.. I don't have much experience, so my code is based on what I understand from the datasheet, and somewhat related questions I could find on the forum.

The NOLOAD appear to take.. however, if I call HAL_FLASH_Program with the desired flash address , can this linker section be removed all together? I.e. the linker section / NOLOAD is needed only if I want to map a variable directly to flash?

Is there an issue with writing to the same 32-byte flash word repeatedly with the same data?

I'm using a 32-byte buffer, initialized with 0xFFs

uint32_t buffer [ 8 ]  = {
	0xFFFFFFFFL , 0xFFFFFFFFL , 0xFFFFFFFFL , 0xFFFFFFFFL , 0xFFFFFFFFL , 0xFFFFFFFFL , 0xFFFFFFFFL , 0xFFFFFFFFL};

so every time I want to write to flash, I use the next index.. so essentially, I'm always writing the same 32-bytes except for the 32-bit word I'm writing.

It's seems random, but every couple writes or so, I'll get a hard fault when verifying the data in the flash.