Specifying the beginning of the .noinit section

Go To Last Post
6 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I am writing an application that uses a battery-backed-up external RAM for non-volatile storage of frequently-changed parameters. We had the heap and all other variables located in internal RAM, and we literally #defined the locations of the specific variables we wanted to live in external RAM.

Up until recently, the majority of the 32k RAM chip was sitting idle, so I'd like to move all variables and the heap to external RAM as well. The problem is, we need to ensure that those specific non-volatile variables continue to live in the same location as before -- at the very beginning of the external RAM -- and that they continue to behave in a non-volatile fashion. I gather that placing these variables in the .noinit section can achieve this.

From what I have read of the discussion of memory sections from the avr-libc FAQ, it appears that the .data section comes first, followed by the .bss section, with the .noinit section coming at the very end of it all, just before the heap.

This concerns me -- if any additional variables are added to or removed from the .data or .bss sections in future revisions, then our non-volatile variables will change locations.

Is it possible to force the .noinit section to the very beginning of external RAM, and place the .data, .bss, and heap to be located further along? If not, can I declare my own memory section, separate from the .noinit section, starting at the beginning of external RAM, before any of the .data or .bss sections, and forced to be non-initialized at start-up?

- Luke

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

It should be possible. You can specify the beginning of the .noinit
section using -Wl,--section-start=.noinit=0x80xxx, but I don't know
how .data and .bss will be placed in that case. If all else fails,
you need to customize the linker script.

Jörg Wunsch

Please don't send me PMs, use email if you want to approach me personally.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello,

Looking at the standard linker script it seems to appear also a problem with dynamically allocated data.

  /* Global data not cleared after reset.  */
  .noinit  SIZEOF(.bss) + ADDR(.bss) :
  {
     PROVIDE (__noinit_start = .) ;
    *(.noinit*)
     PROVIDE (__noinit_end = .) ;
     _end = . ;
     PROVIDE (__heap_start = .) ;
  }  > data

_end & __heap_start are defined with the assumption of .noinit section placed at the end of the data area.

To avoid many headaches I should go directly to a custom linker script. It's very clean, no need to count section bytes after every change, and easy (swap order and move two lines to the appropiate place)

Regards,

Carlos.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I probably don't understand enough about linker scripts to do everything I have to. Try as I might, I can only get the .data section to start at either 0x800100 or at a hard-coded location via a command-line option to the linker. I can't find a way to configure the .data section to begin immediately after another section, rather than at an (apparently) fixed location.

For that matter, I can't seem to figure out where the linker is told to start the .data section at 0x800100 by default -- by my reading of the linker script, I would have expected it to start at 0x800060. (Starting at 0x800100 on the mega128 is a good thing, I know, because addresses below that actually are extended as I/O registers. But, how does the linker know about that? It doesn't show up anywhere in the linker script, as far as I can tell.)

Just modifying the linker script, the .data section simply won't move around. I can explicitly place it at a specific location by modifying the makefile to add a linker option such as

-Wl,-Tdata=0x808000

to force the .data section to a location that I have manually calculated to come after my special .noinit section. But that's no good, since there's always the possibility that I might want to add additional variables to the .noinit section someday, and I want a solution that will move the sections that follow automatially when the preceeding section grows.

As a temporary solution, I have left the .data and .bss sections in internal RAM, in their default locations. I have moved only the .noinit section to external RAM, and the heap still begins at the end of .noinit. I have replaced my #defined von-volatile variables with declared variables that live in the .noinit section. So, I at least have the heap in External RAM, and positioned such that it can never overwrite my special variables.

Perhaps I'll leave it this way -- there's still about 2 K of stack space left in internal RAM, and I've still managed to free up quite a bit more heap space, which was the original motivator for the move in the first place.

Thanks for the suggestions,
Luke

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello,

You are right. The section .data remains at the same address, and apparently can be moved only to absolute addresses. A different aproach is to remap .data contents to another section (in the example .qdata). The linker will then insert an empty .data section. Try to replace the "data" region sections entries (.data, .bss, and .noinit) in the linker script with the following:

.noinit 0x808000 :
  {
     PROVIDE (__noinit_start = .) ;
    *(.noinit*)
     PROVIDE (__noinit_end = .) ;
  }  > data

  .qdata SIZEOF(.noinit) + ADDR(.noinit) : AT (ADDR (.text) + SIZEOF (.text))
  {
     PROVIDE (__data_start = .) ;
    *(.data)
    *(.gnu.linkonce.d*)
    . = ALIGN(2);
     _edata = . ;
     PROVIDE (__data_end = .) ;
  }  > data
   __data_load_start = LOADADDR(.qdata);
   __data_load_end = __data_load_start + SIZEOF(.qdata);

  .bss  SIZEOF(.qdata) + ADDR(.qdata) :
  {
     PROVIDE (__bss_start = .) ;
    *(.bss)
    *(COMMON)
     PROVIDE (__bss_end = .) ;
     _end = . ;
     PROVIDE (__heap_start = .) ;
  }  > data

This is not as clean as I would like but it's enough simple :-(

Regards,

Carlos.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I have attached the linker script that I use to order the RAM thusly:

.noinit
.data
.bss

It is based on avr5.x as supplied with avr-gcc 3.4.3 so you can do a diff to see what changed. That should give you an idea how to make scripts for various combinations.

You must, however, make sure that the final data section provides _heap_start. That is, it has to have the line below at it's end.

     PROVIDE (__heap_start = .) ;

I initially tried using the --section-start link option and didn't have much luck. Oddly, the various sections were often overlapping. Perhaps I wasn't doing it right but it seems to me that using the linker script is the simplest way to go.

As an aside, in the linker script there is a section that begins with the word "MEMORY". In that section you'll find a line that begins with "text" that tells the linker how large the code space is. The avr5.x linker script has the value set to 128K. If you change the value to match your device, the linker will generate an error message if the code size exceeds the available space.

Attachment(s): 

Don Kinzer
ZBasic Microcontrollers
http://www.zbasic.net