Cleaning Out Unwanted Memory Sections for ARM Cortex-M

 In embedded C, Embedded Systems

Sometimes we want to individualize a piece of hardware.  We don’t want to recompile the whole program (because then it’s hard to verify that it is the same program), and we don’t want to rewrite everything in memory.  How do you do that in C?  Depending on the processor you’re using it is as simple as telling it to put the data structure at a given address, compile and write.  When working with the ARM Cortex-M, and the GNU compiler, it’s not that simple.  I found that out when trying to write 24 bytes to a specific memory location.

What we’re writing

Here’s the genericized data structure:

typedef struct {
    uint32_t structVersion;     //ID to tell which type of struct this is.
    uint32_t member1;
    uint32_t member2;
    uint32_t member3;
    uint16_t member4;
    uint8_t member5;
    uint8_t member6;
    uint8_t member7;
} Provisioning;

How to get it in the Right Place

There’re a couple ways to do this, but the way I chose was to create a specific section of memory for this, called provision.  To put my data structure in that memory location, I created it in its own .c file and prefaced it with

__attribute__ ((section("." "provision")))
:

Your header files may have a macro to make this easier.

__SECTION_SIMPLE(provision) Provisioning provisioning = {0};

Include your header file that has the structure definition and that’s it.  You’ll probably want to do a better job at defining the structure.

The second half of it is creating the memory location to make sure that you don’t overwrite other areas of memory.  To do this, you need to see how your flash memory is designed in your chip.  The one used here, an NXP LCP5528, which has 512 byte sectors, which are the smallest region of memory that can be cleared.  This means that to write one byte in the bank, the whole bank needs to be cleared.  I had space to spare, so I defined the provision region, in the MEMORY section of the linker file, to be 1 kB as so:

PROVISION_FLASH (r) : ORIGIN = 0x7fc00, LENGTH = 0x400 /* 1K bytes*/
 

I made sure to remove this from the larger memory section I carved this out of.  Please note that the memory is listed as r, which means that it can be read by the program.

Keeping What you don’t Use

The next challenge is to create the section and make sure it keeps the data, since the compiler knows it’s never accessed in this project. To do this we create the .provision section.

.provision : ALIGN(4)
{
    provisioning_start = .;
    *(.provision*)
    KEEP(*(.provision*))
    . = ALIGN(4);
} > PROVISION_FLASH
PROVIDE(end = .);

The KEEP statement is important because the compiler will throw out the data structure otherwise because it’s not referenced in this project.  In the main program, we just need to create the symbol in its linker file as so:

provisioning_start =  0x7fc00 ; /* 1K bytes */

Discarding What you don’t Want

This is the fun part.  After getting the data structure and linker file ready, my first compile had kilobytes of data in it, and it was all over the memory map.  Here’s the linker output:

section             size         addr
.text                 80            0
.init                  4           80
.fini                  4           84
.data                476    536870912
.data_RAM2             0     67108864
.data_RAM3             0   1074790400
.data_RAM4             0    537133056
.bss                 180    536871388
.text              15636           88
.provision            24       523264
.uninit_RESERVED       0    536870912
.noinit_RAM2           0     67108864
.noinit_RAM3           0   1074790400
.noinit_RAM4           0    537133056
.noinit                0    536871568
.heap               4096    536871568
.heap2stackfill     4096    536875664
.stack                 0    537063424
.ARM.attributes       54            0
.comment              73            0
.debug_frame        5288            0
.debug_info          347            0
.debug_abbrev        149            0
.debug_aranges        24            0
.debug_line          185            0
.debug_str           768            0
Total              31476

We need to get rid of the extra stuff because it will overwrite other memory locations.

Looking at the linker documentation, we find that there are standard libraries that we can get rid of.  To do this we need to give the linker the following options, -nostartfiles, -nodefaultlibs, -nostdlib, -nolibc, and -s.  This helps a lot.

Linker OptionsTotal Size (bytes)
Starting31476
-nostartfiles31466
-nostartfiles, -nodefaultlibs31466
-nostartfiles, -nodefaultlibs, -nostdlib31466
-nostartfiles, -nodefaultlibs, -nostdlib, -nolibc31466
-nostartfiles, -nodefaultlibs, -nostdlib, -nolibc, -s24705
Everything and removing -lc23349
Everything and removing -lc, -g, -lrdimon23349
Everything and removing -lc, -g, -lrdimon, -u _printf_float8419

That was about as far as I could go with command line options to the linker.  Here’s what the linker output now looks like:

section            size         addr
.text                80            0
.data                 0    536870912
.data_RAM2            0     67108864
.data_RAM3            0   1074790400
.data_RAM4            0    537133056
.bss                  0    536870912
.text                 0           80
.provision           24       523264
.uninit_RESERVED      0    536870912
.noinit_RAM2          0     67108864
.noinit_RAM3          0   1074790400
.noinit_RAM4          0    537133056
.noinit               0    536870912
.heap              4096    536870912
.heap2stackfill    4096    536875008
.stack                0    537063424
.comment             73            0
.ARM.attributes      58            0
Total              8419

As you can see, there are not nearly as many sections in there, but there are some we still need to remove.  To do this, we go back to the actual linker file.

In the linker file, we define the

.heap
and
.heap2stackfill
sections.  If we remove those from the linker file, most of the memory is freed.  Looking at the .hex file generated, we are not actually writing those addresses because they are runtime SRAM.

What we are writing is the vector table as the first block of memory:

  /* MAIN TEXT SECTION */
.text : ALIGN(4)
{
    FILL(0xff)
    __vectors_start__ = ABSOLUTE(.) ;
    /* Global Section Table */
    . = ALIGN(4) ;
    __section_table_start = .;
    __data_section_table = .;
    LONG(LOADADDR(.data));
    LONG(    ADDR(.data));
    LONG(  SIZEOF(.data));
    LONG(LOADADDR(.data_RAM2));
    LONG(    ADDR(.data_RAM2));
    LONG(  SIZEOF(.data_RAM2));
    LONG(LOADADDR(.data_RAM3));
    LONG(    ADDR(.data_RAM3));
    LONG(  SIZEOF(.data_RAM3));
    LONG(LOADADDR(.data_RAM4));
    LONG(    ADDR(.data_RAM4));
    LONG(  SIZEOF(.data_RAM4));
    __data_section_table_end = .;
    __bss_section_table = .;
    LONG(    ADDR(.bss));
    LONG(  SIZEOF(.bss));
    LONG(    ADDR(.bss_RAM2));
    LONG(  SIZEOF(.bss_RAM2));
    LONG(    ADDR(.bss_RAM3));
    LONG(  SIZEOF(.bss_RAM3));
    LONG(    ADDR(.bss_RAM4));
    LONG(  SIZEOF(.bss_RAM4));
    __bss_section_table_end = .;
    __section_table_end = . ;
    /* End of Global Section Table */

    *(.after_vectors*)

} > PROGRAM_FLASH

If we get rid of that section, we are now down to just writing the data structure when we look at the .hex file.

What’s with all the Other Stuff?

If we want to remove the other sections we can remove most of them by removing their definitions in the linker file.  There are a few sections that doesn’t work for.  For those we need to

/DISCARD/
them.  Put the
/DISCARD/
section at the end of your
SECTIONS
entries in the linker file and then list all the sections that you want dropped.  This looks like:

/DISCARD/ :
{
    *(.comment*)
    *(.ARM*)
}

Putting it all together, here’s the linker output:

section      size     addr
.provision     24   523264
Total          24

Leave a Comment

Contact Us

We're not around right now. But you can send us an email and we'll get back to you, asap.

Start typing and press Enter to search