Adjusting the linker for use of the EEPROM on SAM M0 devices

There’s an interesting “thought” left out of application notes and driver when it comes to using the “EEPROM” on a SAM Cortex M0 device like the SAMD21 or SAMD20.

You have to modify the linker to keep your program safe!

What do I mean?

Flash Organization

The basic flash memory is like this (using 256kb flash as an example):

|-------------- 0x00000
|
|
|
|  FLASH
|
|
|
|
|-------------- ‭0x40000‬

However “EEPROM” uses flash space from the bottom up! So if we allocated 16K of EEPROM by changing the fuse bytem we’ll get:

|-------------- 0x00000
|
|
|
|  FLASH
|
|
|
|-------------- 0x3C000
| EEPROM
|-------------- ‭0x40000‬

So what’s wrong with that? It should work right?

Yes and no!

If you are using the ASF EEProm driver or even write your own, it’s perfectly fine to read and write the EEPROM space pictured above.

The problem comes from the fact the default provided linkers extend the full memory range as flash space no matter what your code is doing.

The compiler thinks its ok to use 0x00000 all the way up to 0x40000 to shove code in. So if you are really pushing the memory limits on smaller sized parts, you’ll be pushing into that “EEPROM” space you reserved. Generally you will be safe as GCC for ARM tends to keep code compact and bunched up (unlike Microchip’s XC8 compilers which allocate flash space as if a handless man was driving a bus.)

However, nothing good will happen if your code ends up being pushed into the EEPROM region by compiler. It will compile just fine and program just fine. It’s once you start running firmware and reading/writing EEPROM you are in for potential misbehavior.

Linker

The solution is simply adjusting the linker script.

In an ASF project it’s typically located here:

\src\ASF\sam0\utils\linker_scripts\samd21\gcc

which will end in a *.ld extension.

The single section to look at is at the top such as this for a SAMD21E16 which has 64KB of flash and 8K of RAM.

MEMORY
{
  rom    (rx)  : ORIGIN = 0x00000000, LENGTH = 0x00010000
  ram    (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00002000
}

The ORIGIN is the starting address of the ROM and RAM sections respectively which the rest of the linker uses for allocating code to and the LENGTH is the same of these sections. These addresses and sizes are in bytes.

So if we used an allocation of 8KB for EEPROM which is 0x2000 bytes, the LENGTH for ROM should have that amount subtracted as so:

MEMORY
{
 /* Adjusted LENGTH for 8KB EEPROM */
  rom    (rx)  : ORIGIN = 0x00000000, LENGTH = 0x00008000
  ram    (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00002000
}

Another option is to define an EEPROM section that will reserve the space but because the rest of the linker does not use it, it will remain unused by the compiler and it leaves a documenting footprint.

/* Memory Spaces Definitions */
<div class="prism-sourcecode">
<pre class="line-numbers language-linker" ><code class="language-linker">MEMORY
{
  rom    (rx)  : ORIGIN = 0x00000000, LENGTH = 0x00008000
  eep    (rw)  : ORIGIN = 0x00008000, LENGTH = 0x00002000
  ram    (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00002000
}</code></pre>

</div>
comments powered by Disqus