2
\$\begingroup\$

I am trying to create a 2K flash memory region in which user defined const uint32_t will be stored. The project is built in Keil uVision. The microcontroller has 64K of flash memory. The idea is to allocate 2K of flash memory at the end of the total memory that is available. I have edited the original uVision generated scatter (.sct) file to include an additional execute region of size 2K. I reduced the load region LR_IROM1 from 64K down to 62K.

  ER_IROM1 0x08000000 0x0000F800  {  ; main execution region, reduced by 2KB
   *.o (RESET, +First)
   *(InRoot$$Sections)
   .ANY (+RO)
   .ANY (+XO)
  }
  
  ER_PARAMETER_FLASH 0x0800F800 0x00000800  {  ; 2KB region for parameters
   *(.parameter_section)
   .ANY (+RO)
  }

  RW_IRAM1 0x20000000 0x00009000  {  ; RW data
   .ANY (+RW +ZI)
  }
}

In the main program, the flash data is generated with some random uint32_t constants as follows:

__attribute__((section(".parameter_section"))) const uint32_t my_flash_data[4] = {0xDEADBEEF, 0xCAFEBABE, 0xBAADF00D, 0xFACEB00C};

To ensure that this data is referenced, in the main loop the following is put:

uint32_t value = my_flash_data[0];  

The build process does not return any errors. However, looking at the .map file Image Symbol Table, it appears that this data is written just after the main program data and it happens to be the last entry:

Execution Region ER_PARAMETER_FLASH (Exec base: 0x0800f800, Load base: 0x08001aa0, Size: 0x00000010, Max: 0x00000800, ABSOLUTE)

Exec Addr    Load Addr    Size         Type   Attr      Idx    E Section Name        Object

0x0800f800   0x08001aa0   0x00000010   Data   RO           86    .parameter_section  main.o

However the load address is not the same as the execution address. This does however make sense, since the actual data is stored at that memory address, but that is not what I wanted to achieve.

The my_flash_data address should have been 0x0800f800.

Is there a way to fix this? Could there be another load region created for this memory .sct file instead? If so, what are the changes that need to be implemented?

I have tried to change the load region from 0x08000000 0x0000F800 to 0x08000000 0x00010000, but it seems that it has no effect on it.

\$\endgroup\$

1 Answer 1

3
\$\begingroup\$

Linker scripts are difficult. Good you have found the map file.
But, I think you have made several mistakes.

  • Don't put ANY in parameter flash region. You want control over what gets put there, ANY is not control.
  • You have shrunk your LR, why? This region is getting programmed. Then the scatter loading program copies stuff to their given ER region positions. You can't copy to flash. You want the LR and ER to match to avoid copy.
  • Your uint32_t value is not (+RO) read only. Use correct C type qualifiers, at least const it.

I suggest:

  • Avoid absolute linker attributes, they're not reliable, and never portable. (conflicts with unit testing)
  • Put your parameter object (correctly qualified!) in a separate C file and refer that object in the scatter file.
  • You may even want to create a separate load region for it if you want to do post-processing on it (eg: signing binaries).

Example:

LR_PARAMETER_FLASH 0x0800F800 0x00000800  {                         
  PARAMETER_FLASH 0x0800F800 0x00000800  {
    parameter_section.o (+RO)
  }
}

You cannot have overlapping LR, do shrink the other one with this approach.

I do not recall if this will throw an error if you try to put anything other that +RO in this file, please try, I think it might. (this is a good thing, you don't want other stuff in the parameter sector)

See also my previous answer:
https://electronics.stackexchange.com/a/357236/148363


In reply to comment, good questions:

The memory containing these constants is now significantly spaced apart from the main program in the flash memory. Could this lead to any compromises in program runtime speed, particularly with flash access?

  • reading, there is a penalty on reading because it won't be prefetched or cached. I'm not familiar with th STM32G0, but generally flash is multiple words wide and reads pages ahead/caches. Because of wait states and the instruction pipelining. Complex specific architecture stuff.
    So, unless you read this value a lot in your program there won't be any measurable difference. However, if you do read it a lot, creating a local copy in a register first can help. But this goes into program micro-optimization, off topic here.
  • writing, if most of the unused flash is filled with zero's or your programmer is dumb, it will write all flash sectors after erase. Even the unused ones. This takes time. You can speed this up by shrinking the load regions. You then must use addressed artifact files (eg: hex, axf) otherwise the linker will just generate filler for you. Eg: with .bin files.
    if I recall correctly, keil has an "program only used sectors" checkbox, try it.
\$\endgroup\$
3
  • \$\begingroup\$ thank you for the reply, i applied your suggested modifications and it does appear to work correctly now. I will attach the final code and .sct, .map snippets later so that other could benefit from it. \$\endgroup\$
    – rbe
    Commented Aug 12 at 9:21
  • \$\begingroup\$ I have another question, though. The memory containing these constants is now significantly spaced apart from the main program in the flash memory. Could this lead to any compromises in program runtime speed, particularly with flash access? The total program size is only 6 kB, so the flash contents will be stored within the first few pages (2K each). However, the newly created data is located in the last page of the memory map. \$\endgroup\$
    – rbe
    Commented Aug 12 at 9:28
  • \$\begingroup\$ @rbe valid questions, see my changes \$\endgroup\$
    – Jeroen3
    Commented Aug 12 at 15:45

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.