GCC C++ (ARM) and const pointer to struct field

自作多情 提交于 2019-12-05 15:59:28

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.

Freddie Chopin

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++.

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

  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.

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 */

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)

...

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.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!