How to force the C compiler to allocate SRAM absolute addr

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

How do you force the AVR-GCC C compiler to allocated an array to an absolute address in SRAM?

when defining variables as:
uint16_t wave_buf[0x1800] __attribute__ ((section (".data"))); // (0x1800) 6144 samples at 16-bits (total 12288 bytes)

and it is the first variable defined in my C code but the compiler decided on its own to allocated other variables before this one. You would think since it's the first defined, it would have address 0x800100 (realy 0x100) in internal SRAM. (I'm using a atmega1284p with 16k SRAM).

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

It certainly isn't the first time we have this question asked here. If you do your searches right you will find the previous discussions (and in those you will also find that it boggles many people WHY you want to do this, and when those reasons are reviled alternative solutions to the problem are also often given).

If you REALLY need a variable at a fixed absolute address the solution often given is to put it into it's own memory section, and then control the placement of that section through tinkering with the linking script.

Quote:

You would think since it's the first defined, it would have address 0x800100

No, I would not. If I want absolute control of what machine code is generated I use assembler. Using a C compiler I give up some of that control to in return for ease of programming, portability and optimization. The compiler is free to arrange variables in any way it sees fit as long as it preserves the semantics of the C programming language. E.g. if the processor performs advantageously if certain types of variables are aligned in specific ways it can arrange the allocation so that this happens with the minimum memory waste.

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

Last Edited: Sun. Apr 3, 2011 - 11:47 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You were so close:

uint16_t wave_buf[0x1800] __attribute__ ((section (".mydata")));

then:

LDFLAGS += -Wl,-section-start=.mydata=0x80NNNN

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

I tried:
uint16_t wave_buf[0x1808] __attribute__ ((section (".wave_data")));
and then in the linker options under the project->Configuration options->Custom options->Linker options, i added the statement:
-Wl,-section-start=.wave_data=0x800100
and i get an error (when I compile)
c:/program files/atmel/winavr-20100110/bin/../lib/gcc/avr/4.3.3/../../../../avr/bin/ld.exe: section .bss [0080010c -> 0080054f] overlaps section .wave_data [00800100 -> 0080310f]

so it appears the linker will not allow me to put variables anywhere I choose in SRAM. Any ideas?

I do not know where you would put:
LDFLAGS += -Wl,-section-start.wave_data=0x800100
The compiler doesn't like it in my C code.

I tried:
$ avr-gcc -Wl,-section-start=.wave_data=0x800100
in my C code but the compiler doesn't like it and returns error.

I'm assuming it's valid to put the:
-Wl,-section-start.wave_data=0x800100
statement in the linker options because it doesn't seem to be valid anywhere else.

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

Well you cannot crash into addresses the linker is already using for .data and .bss, obviously when you choose to take on some of the functions of the linker you have to agree to not tread on each others toes! On modern AVRs 0x800100 is the very start of SRAM and even on the old ones it was 0x800060 allowing just 0xA0 (160) bytes for the existing .data and .bss. If this is a table that needs to be placed on an address that is a multiple of 256 so you can use single register increments then start at the END of RAM and make room for it there - you will also have to modify the CRT to initialise SP beneath it.

BTW I assume this is a mega1284 as it's the ONLY model of AVR with sufficient SRAM to accomodate uint16_t array[0x1808] - that is 12,304 bytes!

EDIT: sorry, should have read more closely - so it is 1284 then ;-)

Here's a quick test:

#include  

uint16_t wave_buf[0x1800] __attribute__ ((section (".mydata"))); 
uint8_t this_in_data[100] = { 1,2,3,4,5,6,7,8};
uint8_t this_in_bss[240];

int main(void){ 
  while (1) 
  { 
  } 
} 
Linking: test.elf
avr-gcc -mmcu=atmega1284p -I. -gdwarf-2 -DF_CPU=8000000UL -Os -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -Wall -Wstrict-prototypes --save-temps -fverbose-asm -msize -Wa,-adhlns=test.o  -std=gnu99 -MMD -MP -MF .dep/test
.elf.d test.o --output test.elf -Wl,-Map=test.map,--cref     -lm -Wl,-q -Wl,-section-start=.mydata=0x800300

Builds without error. The linker places this_in_data[] first in .data at 0x0100. this_in_bss[] within .bss is next at 0x164 and extends to 0x254. I then just arbitrarily picked 0x0300 to leave a bit of room and be on a boundary. Here's the .map data:

 .data          0x00800100       0x64 test.o
                0x00800100                this_in_data
 .data          0x00800164        0x0 c:/winavr-20100110/bin/../lib/gcc/avr/4.3.3/avr51\libgcc.a(_exit.o)
 .data          0x00800164        0x0 c:/winavr-20100110/bin/../lib/gcc/avr/4.3.3/avr51\libgcc.a(_clear_bss.o)
 *(.data*)
 *(.rodata)
 *(.rodata*)
 *(.gnu.linkonce.d*)
                0x00800164                . = ALIGN (0x2)
                0x00800164                _edata = .
                0x00800164                PROVIDE (__data_end, .)

.bss            0x00800164       0xf0
                0x00800164                PROVIDE (__bss_start, .)
 *(.bss)
 .bss           0x00800164        0x0 c:/winavr-20100110/bin/../lib/gcc/avr/4.3.3/../../../../avr/lib/avr51/crtm1284p.o
 .bss           0x00800164        0x0 test.o
 .bss           0x00800164        0x0 c:/winavr-20100110/bin/../lib/gcc/avr/4.3.3/avr51\libgcc.a(_exit.o)
 .bss           0x00800164        0x0 c:/winavr-20100110/bin/../lib/gcc/avr/4.3.3/avr51\libgcc.a(_clear_bss.o)
 *(.bss*)
 *(COMMON)
 COMMON         0x00800164       0xf0 test.o
                0x00800164                this_in_bss
                0x00800254                PROVIDE (__bss_end, .)

also:

.mydata         0x00800300     0x3000
 .mydata        0x00800300     0x3000 test.o
                0x00800300                wave_buf

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

Also, the LDFLAG line goes in the MAKEFILE of Winavr when using PN. That's why you had trouble in where to place it.

1) Studio 4.18 build 716 (SP3)
2) WinAvr 20100110
3) PN, all on Doze XP... For Now
A) Avr Dragon ver. 1
B) Avr MKII ISP, 2009 model
C) MKII JTAGICE ver. 1

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

JohanEkdahl wrote:

It certainly isn't the first time we have this question asked here. If you do your searches right you will find the previous discussions (and in those you will also find that it boggles many people WHY you want to do this, and when those reasons are reviled alternative solutions to the problem are also often given).

Surely there's people other than me who have memory-mapped peripherals, that need to be allocated at specific places in RAM? For example, I have an external FPGA that runs as a coprocessor on one of my designs, which has a large array of shared memory inside it that needs to be accessed by the AVR. Placing that array at a known address makes this easier than guessing...

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

Quote:

when those reasons are reviled

As this thread shows, those reasons do indeed often seem to be "reviled" after they are revealed.

Lee

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

mvdw wrote:
Placing that array at a known address makes this easier than guessing...
An alternative to using linker directives is to define a constant pointer to the array having the predetermined address. For example,
uint8_t *const myArray = (uint8_t *)0x1800;

void foo(void)
{
    uint16_t i;

    for (i = 0; i < 100; i++)
        myArray[i] = i;
}

int main(void)
{
    foo();
    myArray[23] = 0x55;
    for (;;)
    {
    }
}

The optimizer is smart enough to use the pointer's value as a constant in the generated code. The only downside I can see is that it needlessly assigns a RAM location to hold the constant pointer even though it is never used. You can eliminate this artifact by defining and initializing the pointer within each function where it is used, e.g.

#define MY_ARRAY_ADDR    (uint8_t *)0x1800

void foo(void)
{
    uint8_t *const myArray = MY_ARRAY_ADDR;
    uint16_t i;

    for (i = 0; i < 100; i++)
        myArray[i] = i;
}

int main(void)
{
    uint8_t *const myArray = MY_ARRAY_ADDR;
    foo();
    myArray[23] = 0x55;
    for (;;)
    {
    }
}

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