Cleaning Out Unwanted Memory Sections for ARM Cortex-M

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

Of embedded black boxes

Over the last few months I\’ve been working on projects using PIC microcontrollers. At first I had a rather negative view of the PIC processors. Since I started coding on them, I have come to realize that it is the compiler that I have issues with much more than the hardware. The compiler we\’ve been using by CCS is designed to get projects up and running quickly by abstracting the hardware from the coder. So, instead of directly writing to the registers, you call one of their functions. We\’ve found this works well when it is a mature processor and when we\’re not trying to do too many things at once. Where we ran into lots of frustration was with new processors. We thought we had the code written correctly, yet it wasn\’t working. Now instead of just having our code and hardware errata to look at, I also had to figure out what the CCS code was doing… which means slogging through the disassembled code and looking up the mnemonics for the different assembly operations.

Now, you need to understand where I come from. I\’m a Professional Engineer. When I vouch for something, I\’m saying that it works. When I have the black box of a intrinsic compiler functions, it makes me a little less sure about what is occurring. That\’s one of the reasons why I like the MSP430. TI does a very good job of letting me know what\’s going on with the chip. There\’s not much left to the imagination when using it. The analog functions are well documented and there are good code examples. The Stellaris line of ARM Cortex-M3 chips tries to make both camps happy. It has good documentation of things, but it also comes with a large driver library that encapsulates many of the functions for an engineer to rapidly program on it.

My beef with embedded processor abstraction through libraries or intrinsic functions is that it usually doesn\’t keep up with the latest processors and that it is too easy to do something that modifies something else and then you don\’t know where the problem is being generated because the functions work separately or in a different order. Oh, and they take more memory, on memory constrained processors. What\’s your experience?

Capacitive Touch Sensing on the MSP430

One of the projects I\’m working on involves using capacitive touch sensing (CTS) on the TI MSP430.  TI has been pushing their touch sensing capabilities recently and has even released a library that helps in implementing touch sensing on the MSP430.  I decided to give it a try.  The short story is that there is a lot on TI\’s site and it is easy to get confused as to what you need.  See below for some of my journey getting it going.Continue reading

A Valentine Example for Blinking Lights

For Valentine\’s Day my son needed to make a \”mailbox\” for his kindergarten class. He & his mother made a rocket out of an old oat container. Since this was a family project, I decided that my contribution would be lighting it up. My son was quite excited when I told him that I was putting lights on it and it would have a computer to control them. Being sharp as a tack, he quickly asked, \”Daddy, where will the keyboard go?\” I had to explain that it would be a little computer without a keyboard, just a button. He was still excited.

Continue reading

The Never Ending Conversion

When using the ADC10 in an MSP430 with multiple conversions, the ADC10BUSY bit will stay on even after the conversions are done. Trying to be a good little programmer and checking to make sure the ADC is done will leave you hanging… forever. The conversions must be done for the data transfer and the data transfer controller (DTC) will set the ADC10 interrupt after it is done, therefore, the conversions must be done if the interrupt is run.