views:

992

answers:

7

Let's say there is a simple test code

typedef struct
{
    int first;
    int second;
    int third;
} type_t;

#define ADDRESS 0x12345678

#define REGISTER ((type_t*)ADDRESS)

const int data = (int)(&REGISTER->second)*2;

int main(void)
{
    volatile int data_copy;

    data_copy = data;

    while(1) {};
}

Which is compiled in CodeSourcery G++ (gcc 4.3.2) for bare metal ARM. It also has a very standard linker script.

When compiled in C (as main.c) the object "data" goes into Flash, as expected. When compiled in C++ (as main.cpp) this object goes into RAM, and additional code is added which does nothing more than copy the value from Flash to RAM (the value is already calculated, just copy!). So the compiler can calculate the address, but somehow doesn't want to "just use it". The root of the problem is the multiplication of the address - without "*2" multiplication both versions work as expected - "data" is placed in Flash. Also - when "data" is declared as:

const int data = (int)(REGISTER)*2;

also everything is fine.

All files for C and C++ compilation are identical, the only difference is the call to compiler - g++ for main.cpp, gcc for main.c (with differences in the level of warnings, and c++ has RTTI and exceptions disabled).

Is there any easy and elegant way to overcome this "C++ problem"? I do require such operations for creating const arrays of addresses of bits in bitband region of Cortex-M3. Is this a bug, or maybe that is some strange limitation of the C++ compiler?

I know that I can create data objects in "C" files and just "extern"-include them in C++, but that's not very elegant [;

Thank you for all help!

+1  A: 
  1. You shouldn't be multiplying pointers.
  2. You shouldn't be storing pointers as "int".

I'm sure there is a legal C++ way to do what you want, but I can't fathom what that is from the code you've posted.

Andrew Medico
Trust me - multiplying pointers is exactly what I want to do. Don't mind storing that as an "int", that's not the point here.Remember - I'm using a bare-metal ARM core. There is a "perippheral region" in the address space, and a bit-band alias of that region. Any bit in the "peripheral region" is mapped to to one uin32_t in the bit-band region. The formula to calculate the address in the bit-band region is this:(0x42000000+(((unsigned long)address)-0x40000000)*32+(bit)*4)As the registers for STM32 in the header are organized in structs I face the problem described above.
Freddie Chopin
That expression really isn't multiplying pointers - it's multiplying the difference between two addresses (which is a plain number). I would suggest rewriting it as something like "(uint32_t*)0x42000000 + (addr - (uint32_t*)0x40000000)*32 + bit*4".
Andrew Medico
Re.2.: Yes, but I tried to provide the simpliest test case possible. Rewriting the macro in the way you propose doesn't change anything, as somehow C compiler knows the address of the field, and C++ "thinks he doesn't know that address"... That's really strange [; As I wrote in the question - when I use a plain address - everything is fine, the problems start when I want to use the address of a field of a struct.
Freddie Chopin
Freddie Chopin: As a general advice, in the future if you ask a question and you know you're doing dodgy things but you know what you're doing and why, make sure to mention that in the question, otherwise people will - quite correctly - identify that as an issue.
DrJokepu
A: 

EDIT:

You say you want to multiply pointers, but that is very likely wrong. Keep in mind: (int)(REGISTER) * 2 will equal (int)(0x12345678 * 2) which equals 0x2468ACF0 probably not what you want. You probably want: REGISTER + sizeof(int) * 2 which is 2 integers past the last byte of the struct.

Original Answer:

This looks like a failed attempt to do the "struct hack" (The example uses c99 style, but it works just fine in C89 too, just have to have an array of 1 as last element). Probably what you want is something like this:

typedef struct {
    int first;
    int second;
    int third;
    unsigned char data[1]; /* we ignore the length really */
} type_t;

type_t *p = (type_t *)0x12345678;

p->data[9]; /* legal if you **know** that there is at least 10 bytes under where data is */

The common usage of this is for malloc allocated structures like this:

type_t *p = malloc((sizeof(int) * 3) + 20) /* allocate for the struct with 20 bytes for its data portion */
Evan Teran
Re. EDIT: the value I expect from the code posted in the question is 0x2468acf8.
Freddie Chopin
well then you are still 8 off in your calculations...
Evan Teran
+2  A: 

You have several problems. Why are you taking an address, converting it to an integer, and multiplying by 2? You should never by multiplying addresses, or storing addresses as ints. The only arithmetic you should ever do with pointers is to subtract them (to obtain an offset), or to add a pointer with an offset to get another pointer.

In C++, the rules for global value initialization are a little more lax than in C. In C, values are required to be initialized to compile-time constants; as a result, the compiler can place const global values in read-only memory. In C++, values can be initialized to expressions which aren't necessarily compile-time constants, and the compiler is permitted to generate code at runtime to calculate the initial value. This initialization code is called before entry into main(), akin to constructors for global objects.

Since you're working with constants addresses, and you know how big an int is on your platform (I'm assuming 32 bits), you should just do something like this:

Next, your use of the volatile keyword is completely wrong. volatile says to the compiler not to save a variable in a register -- it should be reloaded from memory each time it is read, and it should be fully written to memory every time it is written. Declaring the local variable data_copy as volatile is practically useless, unless you expect another thread or a signal handler to start modifying it unexpectedly (highly doubtful). Further, data_copy is just a copy of the address, not the contents of the register you're trying to read.

What you should be doing is declaring REGISTER as a pointer to a volatile -- that is one of the express purposes of volatile, for accessing memory-mapped registers. Here's what your code should look like:

#define REGISTER (*(volatile type_t *)ADDRESS)
#define DATA (*(const volatile int *)((ADDRESS+4)*2))

This makes it so that when you do things like this:

REGISTER.first = 1;
REGISTER.first = 2;
int x = REGISTER.second;
int y = DATA;

It always does the proper thing: a write of 1 to 0x12345678, a write of 2 to 0x12345678, a read from 0x1234567c, and a read from 0x2468acf8. The volatile keyword ensures that those reads and writes always happen, and they don't get optimized away: the compiler will not remove the first write to REGISTER.first, which would be redundant if it were a regular variable.

EDIT

In response to your comment, see Andrew Medico's response to your comment -- you're really multiplying the difference between two pointers by 2, which is ok. Just be careful about your data types. I also never mentioned anything about a kernel.

You can get gcc to put variables in a specific data section with the section attribute:

const volatile int *data __attribute__((section("FLASH")) = /* whatever */;

Use the proper section name. If you're not sure what that is, take the object file generated by the C compiler (which you said puts it in the proper section), run nm on it, and see what section the C compiler put it in.

Adam Rosenfield
ehh... BARE METAL ppl! There is no kernel, and I really HAVE TO multiply pointers and store the address in any way, I know what and why I doing. Take a look at the comment to the first answer. The volatile "data_copy" is there just so that compiler wouldn't remove unused "data". Just stick to the main question - "how to place an multiplied address of a struct field in flash".
Freddie Chopin
Placing the var in .text section explicitly doesn't solve the problem. A value of "0" is placed in flash (that's the "data" object), and the "initializer function" is still there trying to "copy" the value from flash to... flash...I know that I want to multiply the difference of two addresses, but that's not important - really that's the simpliest test case, not bloated with other useless stuff, so please just ignore that "it's stupid to multiply pointers", because that's not the point here.
Freddie Chopin
+1  A: 

Have you taken a look at the gcc Variable Attributes, perhaps "section" well help you in the placement of the variable.

jcopenha
A: 

I would say for safest access for peripheral read writes you should just use volatile defines and offset defines. Casting a peripheral address as a struct does not give any sort of alignment or offset guarantees.

#define UART_BASE_REG ((volatile uint32_t*)0x12341234)
#define UART_STATUS_REG (UART_BASE_REG + 1)

...

ThePosey
Still, you are not asking the main question. I know what you have written, but there is a problem - the header with all registers is already created, and I really don't feel like editing a 600kB of text...
Freddie Chopin
+1  A: 

The right solution is using the offsetof() macro from the stddef.h header.

Basically this:

const int data = (int)(&REGISTER->second)*2;

has to be replaced with

#include <stddef.h>
const int data = (int)(REGISTER + offsetof(type_t,second))*2;

And then the object is placed in Flash both for C and C++.

Freddie Chopin
A: 

If I understand correctly, your overall aim is summarised in this paragraph:

I do require such operations for creating const arrays of addresses of bits in bitband region of Cortex-M3. Is this a bug, or maybe that is some strange limitation of the C++ compiler?

I use the Keil compiler for STM32, and one of the examples that comes with it contains macros for setting and clearing bit-banded bits:

#define RAM_BASE       0x20000000
#define RAM_BB_BASE    0x22000000

#define  Var_ResetBit_BB(VarAddr, BitNumber)    \
 (*(vu32 *) (RAM_BB_BASE | ((VarAddr - RAM_BASE) << 5) | ((BitNumber) << 2)) = 0)

#define Var_SetBit_BB(VarAddr, BitNumber)       \
 (*(vu32 *) (RAM_BB_BASE | ((VarAddr - RAM_BASE) << 5) | ((BitNumber) << 2)) = 1)

#define Var_GetBit_BB(VarAddr, BitNumber)       \
 (*(vu32 *) (RAM_BB_BASE | ((VarAddr - RAM_BASE) << 5) | ((BitNumber) << 2)))

If these exactly aren't what you're looking for, I imagine they can be modified to suit your needs.

Steve Melnikoff
Freddie Chopin