Author Topic: How to allocate a variable at a given absolute address in memory (with GCC)  (Read 19301 times)

0 Members and 1 Guest are viewing this topic.

Offline legacyTopic starter

  • Super Contributor
  • ***
  • !
  • Posts: 4415
  • Country: ch
Code: [Select]
typedef struct
{
    …
} myData_t;

export myData_t myData;


Problems
  • G/AS has a completely different implementation about dot org, if you happen to be good at low level programming, then, within { Microsoft/PC, Motorola/Freescale, IBM/PC }, you can appreciate that GNU dot org simply does different things
  • G/CC does not support the following (even if they are used by clever compilers, including Green Hills, ADB and Keil)

Code: [Select]
/*
 * Not Working, Not Supported by G/CC
 *
 * myData_t myData _at_ myData_ADDR;
 * myData_t myData  @   myData_ADDR;
 * myData_t myData __attribute__((at(myData_ADDR)));
 */


".org" does not work as expected from Motorola AS

Code: [Select]
/*
 * Not working with G/AS
 */
        .section .data
        .globl myData
        .org 0x90000000
myData:
      .size myData, myData_SIZE
      .zero   myData_SIZE // fill with 0x00


The ".org 0xXXXX" directive at the beginning actually says:
  • move the location counter from 0xYYYY to 0xXXXX
  • pad the in-between area with 0x00s <---- evil, troubles come, if used, one will be reported a large block being copied



edit:
title changed into "allocate"
« Last Edit: January 16, 2016, 01:32:27 pm by legacy »
 

Offline legacyTopic starter

  • Super Contributor
  • ***
  • !
  • Posts: 4415
  • Country: ch
Re: How to place a variable at a given absolute address in memory (with GCC)
« Reply #1 on: January 16, 2016, 12:58:33 pm »
memory begins at 0x9000.0000, so I modified the linker script
.section .data begins now at 0x9000.0000 + sizeof(my_data_t), say size=0x200, it begins at 0x9000.0200

in this way I can allocate with a trick

mydata.s
Code: [Select]
.section .data
        .globl myData
        .set myData, 0x90000000


mydata.h
Code: [Select]
typedef struct
{
    uint8_t data[0x200];
} myData_t;

public myData_t myData;


Code: [Select]
0x9000.0000 mydata
0x9000.01xx ..
0x9000.0200 section data

ideas ? advices ?
 

Offline Kalvin

  • Super Contributor
  • ***
  • Posts: 2145
  • Country: fi
  • Embedded SW/HW.
Re: How to place a variable at a given absolute address in memory (with GCC)
« Reply #2 on: January 16, 2016, 01:07:02 pm »
Or you can use a constant pointer to access the data.
 

Offline Kalvin

  • Super Contributor
  • ***
  • Posts: 2145
  • Country: fi
  • Embedded SW/HW.
 

Offline legacyTopic starter

  • Super Contributor
  • ***
  • !
  • Posts: 4415
  • Country: ch
Re: How to place a variable at a given absolute address in memory (with GCC)
« Reply #4 on: January 16, 2016, 01:26:15 pm »
Or you can use a constant pointer to access the data.

this does't allocate the variable, I mean, I still need to use a part of the above ugly trick
more specifically: I still need to set the beginning of .section .data to 0x9000.0200
 

Offline legacyTopic starter

  • Super Contributor
  • ***
  • !
  • Posts: 4415
  • Country: ch
Re: How to place a variable at a given absolute address in memory (with GCC)
« Reply #5 on: January 16, 2016, 01:27:26 pm »
Here is one discussion

I am able to use google, I'd like to hear your opinions, guys
 

Offline Kalvin

  • Super Contributor
  • ***
  • Posts: 2145
  • Country: fi
  • Embedded SW/HW.
Re: How to place a variable at a given absolute address in memory (with GCC)
« Reply #6 on: January 16, 2016, 01:37:11 pm »
Here is one discussion

I am able to use google, I'd like to hear your opinions, guys

My opinion: Using a linker script with sections attributes seems to be most portable way allocate a variable at the specific memory address.
 

Offline Mechatrommer

  • Super Contributor
  • ***
  • Posts: 11630
  • Country: my
  • reassessing directives...
My opinion: Using a linker script with sections attributes seems to be most portable way allocate a variable at the specific memory address.
i agree but i also believe this is the devil details of C/C++ where portability rule breaks, where giant conditional #ifdefined branches is needed...
since the op mentioned __attribute__(at(**)) didnt work, the target hardware may not be supporting it. he may try different variant specific to the hardware he's using. for eg, i saw different variant for avr chip where __attribute__(io(**)) is used... but other hardwares are not supporting the syntax...
https://gcc.gnu.org/onlinedocs/gcc/AVR-Variable-Attributes.html#AVR-Variable-Attributes
from... https://gcc.gnu.org/onlinedocs/gcc/Variable-Attributes.html#Variable-Attributes
which turned out from... http://lmgtfy.com/?q=how+to+gcc+variable+specific+location
« Last Edit: January 16, 2016, 02:26:17 pm by Mechatrommer »
Nature: Evolution and the Illusion of Randomness (Stephen L. Talbott): Its now indisputable that... organisms “expertise” contextualizes its genome, and its nonsense to say that these powers are under the control of the genome being contextualized - Barbara McClintock
 

Offline nctnico

  • Super Contributor
  • ***
  • Posts: 26906
  • Country: nl
    • NCT Developments
I always create a section in the link file and put the variables and/or functions in that section using the attribute keyword.
There are small lies, big lies and then there is what is on the screen of your oscilloscope.
 

Offline legacyTopic starter

  • Super Contributor
  • ***
  • !
  • Posts: 4415
  • Country: ch
so, something like:
  • adding a special section ".my" into the linker script
  • redefining borders of data section in linker script (now it begins down of 0x200 byte)
  • using: myData_t volatile myData __attribute__((section (".my"))) __attribute__((aligned (32)));
 

Offline Jeroen3

  • Super Contributor
  • ***
  • Posts: 4078
  • Country: nl
  • Embedded Engineer
    • jeroen3.nl
Option 1:
-Put your variables/arrays in a separate myData.C file. Add const or volatile as you see fit. For flash, you'd need const. For peripherals, you'd need volatile.
-Create a header file with extern declarations to use them elsewhere in the code.
-Tell the linker that your want myData.o in a specific region.

Option 2:
- Tell the linker to create an empty region, or simply don't tell the linker to use the region exists all.
- Use (#defined) pointers to create a method of accessing the region. Also be aware if you need const and volatile depending on the memory location and expected behavior.
This is how you access peripheral registers. See your "mymcu.h" file where the registers are defined what you need to do for the pointer part.
 

Online AndyC_772

  • Super Contributor
  • ***
  • Posts: 4228
  • Country: gb
  • Professional design engineer
    • Cawte Engineering | Reliable Electronics
I'd go with creating extra sections in the linker script. Normally I don't care so much that a variable goes in a very particular location, but that it goes somewhere within a section that's treated differently to "normal" RAM space. More often than not, there are several variables which need each kind of special treatment, and I don't want to have to specify each address individually.

I might, for example, have sections which are:
- "normal"
- not initialised to zero on reboot
- periodically copied to Flash, and restored at power up
- write protected
- included within a checksum
- used for message passing between bootloader and application

... and so on.

Offline legacyTopic starter

  • Super Contributor
  • ***
  • !
  • Posts: 4415
  • Country: ch
Option 2:
- Tell the linker to create an empty region, or simply don't tell the linker to use the region exists all.
- Use (#defined) pointers to create a method of accessing the region

that is what I have written above
 

Offline legacyTopic starter

  • Super Contributor
  • ***
  • !
  • Posts: 4415
  • Country: ch
Code: [Select]
memory space:

0x9000.0000 section data
0x9000.01xx ..
0x9000.02xx ..
before

Code: [Select]
memory space:

0x9000.0000 section my <--- mydata, size=0x200
0x9000.01xx ..
0x9000.0200 section data
after



myData.S
Code: [Select]
.section .data
        .data
        .globl myData

        .set myData, 0x9000000
        .set size, 0x200

myData.h
Code: [Select]
typedef struct
{
    uint32_t data[0x200];
} volatile my_Data_t;

public my_Data_t my_Data;

myData.c
Code: [Select]
#include "mydata.h"

my_Data_t my_Data;
« Last Edit: January 16, 2016, 10:12:08 pm by legacy »
 

Offline legacyTopic starter

  • Super Contributor
  • ***
  • !
  • Posts: 4415
  • Country: ch
Code: [Select]
#define DataRegister_ADDR 0x90000000
typedef struct
{

} myData_t;
typedef myData_t*  p_myRegister_t;

p_Data_t volatile p_Data = (p_Data_t) (Data_ADDR);

but I do not like, too much
 

Offline andyturk

  • Frequent Contributor
  • **
  • Posts: 895
  • Country: us
I'd much rather put specific location info in C/C++ source than burying it in the linker file. I've done it both ways, but explaining to your team that critical information lives in a strange file that most of them have never seen before is not ideal. Usually, the placement of these variables is important, and requires documentation. Put that doc in the linker file too?

Plus, if you're talking about peripheral registers, you'll probably end up having lots of those and the locations won't be contiguous. That means you'll need to have lots of symbols exported from the linker. Put that stuff in normal source code! Here's how I do it (in C++):

Code: [Select]
struct MyPeripheral {
  uint32_t foo;
  uint32_t bar;
  ...
};

// my_periph is a machine register. see page 1234 of the datasheet
volatile MyPeripheral& my_periph {*reinterpret_cast<MyPeripheral *>(0x90000000)};
 

Offline ale500

  • Frequent Contributor
  • **
  • Posts: 415
In your linker script you define a section at the right physical address with the right size. In the code you assign the variable(s) to the section with the __atribute__ attribute :)

Code: [Select]
int8_t schwarzes_pixel __attribute__ ((section (".lram128k")))=0;       // für DMA

Code: [Select]
MEMORY
{
  FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1024K
  RAM (xrw) : ORIGIN = 0x10000000, LENGTH = 64K
  LRAM128K(xrw) : ORIGIN = 0x20000000, LENGTH = 128K
  MEMORY_B1 (rx) : ORIGIN = 0x60000000, LENGTH = 0K
}
...
  /* LRAM128K section, data must be located here explicitly */
  /* Example: extern int array[128] __attribute__ ((section (".lram128k"))); */
  .memory_b1_text :
  {
    *(.lram128k) /* data */
  } >LRAM128K

Map file says:

Code: [Select]
.memory_b1_text
                0x0000000020000000    0x1fa6c
 *(.mb1text)
 *(.mb1text*)
 *(.mb1rodata)
 *(.mb1rodata*)
 *(.lram128k)
 .lram128k      0x0000000020000000    0x1fa6c cce0b7ns.ltrans0.ltrans.o


There are ways to specify the location in the source file using for instance #pragma but that is very compiler specific and not portable. I've been burned by those because the better (expected) way is in the linker file.
« Last Edit: January 16, 2016, 04:23:33 pm by ale500 »
 

Offline rsjsouza

  • Super Contributor
  • ***
  • Posts: 5986
  • Country: us
  • Eternally curious
    • Vbe - vídeo blog eletrônico
I haven't seen a way to allocate data at specific addresses without using either an implementation-specific feature or, in case of GCC and some specific compilers, without modifying the linker script. I particularly think the linker approach is more elegant.
Vbe - vídeo blog eletrônico http://videos.vbeletronico.com

Oh, the "whys" of the datasheets... The information is there not to be an axiomatic truth, but instead each speck of data must be slowly inhaled while carefully performing a deep search inside oneself to find the true metaphysical sense...
 

Offline Kalvin

  • Super Contributor
  • ***
  • Posts: 2145
  • Country: fi
  • Embedded SW/HW.
You can also add a comment pointing to the linker file into the source code where you declare the external variable. Then the reader understands to peek into the linker file for more specific information like the absolute address etc. if needed.
 

Offline bson

  • Supporter
  • ****
  • Posts: 2269
  • Country: us
I'd much rather put specific location info in C/C++ source than burying it in the linker file. I've done it both ways, but explaining to your team that critical information lives in a strange file that most of them have never seen before is not ideal. Usually, the placement of these variables is important, and requires documentation. Put that doc in the linker file too?

Plus, if you're talking about peripheral registers, you'll probably end up having lots of those and the locations won't be contiguous. That means you'll need to have lots of symbols exported from the linker. Put that stuff in normal source code! Here's how I do it (in C++):

Code: [Select]
struct MyPeripheral {
  uint32_t foo;
  uint32_t bar;
  ...
};

// my_periph is a machine register. see page 1234 of the datasheet
volatile MyPeripheral& my_periph {*reinterpret_cast<MyPeripheral *>(0x90000000)};

This is how I do it too - the address still has to be stored someplace, even if an immediate inline constant in the instruction stream.  So it needs to be fetched since the peripheral space can't be relative addressed, no matter what.  Using a C++ reference here is important, because it tells the compiler it can't be changed after assignment (initialization) so is effectively a constant pointer (not to be confused with pointer-to-const) within its scope.  The volatile of course refers to the referenced, not the reference.  Without proper care or casual use of C pointers (not C++ references) easily results in excessive loading of the base address and the compiler can't treat it as a constant and can't move it out of loops.  It can even prevent proper inlining.
« Last Edit: January 16, 2016, 09:51:05 pm by bson »
 

Offline miguelvp

  • Super Contributor
  • ***
  • Posts: 5550
  • Country: us
In my ongoing PSoC tutorial I needed my DMA frame buffer at a specific place so I did the following:

Used the following flags to declare a section:
-Wl,--section-start=.ram2=0x20000000

Then use it as this:
uint8 dframe[VGA_Y_BYTES][VGA_X_BYTES] __attribute__ ((aligned(),section(".ram2")));

But the proper way is to declare the section in the custom linker file then use the section the same way so other sections don't overlap.
Since I know and I'm monitoring the compile report out I didn't bother.

https://www.eevblog.com/forum/projects/no-bitbanging-necessary-or-how-to-drive-a-vga-monitor-on-a-psoc-5lp-programmabl/msg825368/#msg825368
 

Offline westfw

  • Super Contributor
  • ***
  • Posts: 4199
  • Country: us
With the gnu tools, positioning code or data at particular addresses is done during link, rather than during compile.
You can use separate sections for "special" segments, combined either with a mysterious custom linker map, or a link command with mysterious --section-start options.
Macros are recommended (IMO) in the C code, for portability's sake:
Code: [Select]
uint8 RAM2VAR(dframe[VGA_Y_BYTES][VGA_X_BYTES]);
   rather than
uint8 dframe[VGA_Y_BYTES][VGA_X_BYTES] __attribute__ ((aligned(),section(".ram2")));

 
 

Offline miguelvp

  • Super Contributor
  • ***
  • Posts: 5550
  • Country: us
With the gnu tools, positioning code or data at particular addresses is done during link, rather than during compile.
You can use separate sections for "special" segments, combined either with a mysterious custom linker map, or a link command with mysterious --section-start options.
Macros are recommended (IMO) in the C code, for portability's sake:
Code: [Select]
uint8 RAM2VAR(dframe[VGA_Y_BYTES][VGA_X_BYTES]);
   rather than
uint8 dframe[VGA_Y_BYTES][VGA_X_BYTES] __attribute__ ((aligned(),section(".ram2")));

 
This will never be portable, the PSoC5LP has two separate 32KB buffers, one at the end of the code space and the other at the beginning of the sram space.

The buffer in question is accessible by a DMA channel that sends the bytes to be displayed in the screen, if it was on the same section as the buffer where the CPU writes the buffer then there would be memory contention and that would be displayed in the screen. Actually just accessing any variable in the code space will stall that DMA transfer.

While that DMA channel accesses the separate upper 32KB buffer the CPU is free to fill the lower 32KB buffer and/or use variables that reside in the code memory space.
At the end of the DMA buffer to screen and during the vertical sync, a 2nd DMA is used to copy the CPU buffer to the DMA buffer.

As for a macro, well I would have to define it then, but since that's a tutorial for that specific hardware and not portable at all, then I rather leave it explicitly coded for clarity on where that buffer resides.

If I was to actually polish it, I would use the mysterious custom linker scripts and reducing the .ram section to 32KB
Not sure why they are so mysterious but to each their own :)

Edit: Plus I was answering the OP question on how to place a variable at a given absolute address in memory (with GCC)
Edit: If he was using keil I would have said to use _at_ but he already knew that.

Edit: BTW my .map reports the following:
.ram2           0x20000000     0x7530
 .ram2          0x20000000     0x7530 .\CortexM3\ARM_GCC_493\Debug\main.o
                0x20000000                dframe

for the 30,000 byte DMA buffer.
« Last Edit: January 17, 2016, 06:41:46 am by miguelvp »
 

Offline westfw

  • Super Contributor
  • ***
  • Posts: 4199
  • Country: us
If it's a single buffer, you might as well write a special "malloc" function that returns a cast constant, and modify the linker script just so that it knows it can't use that memory any more.

 

Offline miguelvp

  • Super Contributor
  • ***
  • Posts: 5550
  • Country: us
Not the point, the OP might want to have more than one absolute address, maybe memory mapped I/O ports? don't know.

He could add as many sections of many lengths on specific addresses on the linker script and declare them in code accordingly.
In my case this was the easiest way for such a long write-up, where I want to focus into the task at hand.

I mean: 800x600@60Hz with 60fps update time (every 16.666ms) from the CPU on a small Cortex M3 MCU with at least at least 636,942.7 CPU cycles (or 15.924 ms) per frame left to modify that buffer for buttery smooth graphics, why would I want non portable special mallocs when I can do non portable memory sections?
Specially since the frame buffers eat up 60,000 bytes out of the 64KB SRAM? Not that matters because I have plenty of program space on the Flash Ram, but anyways.

Took the shorter route to get the point across after a humongous amount of documenting the steps, don't really need more steps :)
« Last Edit: January 17, 2016, 08:25:14 am by miguelvp »
 

Offline legacyTopic starter

  • Super Contributor
  • ***
  • !
  • Posts: 4415
  • Country: ch
memory mapped I/O ports?

ops, I had forgotten to say: yes  :-+

edit: yes, I also need them
« Last Edit: January 18, 2016, 10:12:31 am by legacy »
 

Offline bson

  • Supporter
  • ****
  • Posts: 2269
  • Country: us
You can of course declare symbols extern volatile in your code, then define them in the linker script.  Kind of cumbersome though.
 

Offline Lukas

  • Frequent Contributor
  • **
  • Posts: 412
  • Country: de
    • carrotIndustries.net
memory mapped I/O ports?

ops, I had forgotten to say: yes  :-+
Ok, then you don't need all the section funny business :
Code: [Select]
#define myData ((myData_t *) 0xbabaf001)
That's how people do in the ARM header files.
 

Offline westfw

  • Super Contributor
  • ***
  • Posts: 4199
  • Country: us
Quote
You can of course declare symbols extern volatile in your code, then define them in the linker script.
Microchip is (was?) defining peripheral base addresses at link time for PIC32, rather than the more common constant pointers in .h files.
It made for some interesting "missed optimizations."   IIRC, they claim this does a better job at making binary-distribution libraries possible.
 

Offline legacyTopic starter

  • Super Contributor
  • ***
  • !
  • Posts: 4415
  • Country: ch
Ok, then you don't need all the section funny business :

I need it when I need to allocate variables which have to be used in shared ram, my project has both these two needs
  • I/O (e.g. a quadri port lan, each port is memory mapped, but the whole device is mapped into a special section called IO_ports, so it's not memory, even if handled as memory, e.g. load/store instruction, without cache), I prefer to have symbols defined in assembly, called BSP_IO.S, their interfaces are declared in BSP_IO.h, while BSP_IO.c defines IO_methods
  • shared ram, shared between two CPUs, it comes with things that must stay at a given absolute address in memory, I like the linker script method, but I'd rather use a special trick: I have defined symbols in BSP_shared_ram.S (say "absolute addresses", which are known since the beginning, it's good from the documentation point of view), their interface (and sizes) in BSP_shared_ram.h, and their implementation in BSP_shared_ram.c

edit:
I am also going to implement the other approach (pure linker script) in order to compare them from the "error prone" point of view
« Last Edit: January 18, 2016, 11:22:40 am by legacy »
 

Offline legacyTopic starter

  • Super Contributor
  • ***
  • !
  • Posts: 4415
  • Country: ch
If it's a single buffer, you might as well write a special "malloc" function that returns a cast constant, and modify the linker script just so that it knows it can't use that memory any more.

that looks nice for my SMP project, nice idea :D
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf