Question

Disons que il y a un code de test simple

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) {};
}

Ce qui est compilé dans CodeSourcery G ++ (gcc 4.3.2) pour ARM en métal nu. Il a également un script éditeur de liens très standard.

Lorsque compilé en C (comme main.c) l'objet « données » va dans Flash, comme prévu. Lorsque compilé en C ++ (comme main.cpp) cet objet passe en RAM, et un code supplémentaire est ajouté qui ne fait que copier la valeur de Flash à la RAM (la valeur est déjà calculée, il suffit de copier!). Ainsi, le compilateur peut calculer l'adresse, mais ne veut pas en quelque sorte « juste utiliser » La racine du problème est la multiplication de l'adresse - sans. « * 2 » multiplication deux versions fonctionnent comme prévu - « données - données « est déclaré comme » est placé en Flash aussi quand. »:

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

aussi tout va bien.

Tous les fichiers pour C et C ++ compilation sont identiques, la seule différence est l'appel compilateur - g ++ pour main.cpp, gcc pour main.c (avec des différences dans le niveau des avertissements, et c ++ a RTTI et exceptions désactivé) .

Yat-il un moyen facile et élégant pour surmonter ce « C ++ problème »? Je ne demande ces opérations pour créer des matrices const d'adresses de bits dans la région de BitBand de Cortex-M3. Est-ce un bug, ou peut-être qui est une limitation étrange du compilateur C?

Je sais que je peux créer des objets de données dans « C » fichiers et juste « extern » les -include en C ++, mais ce n'est pas très élégant [;

Merci pour toute aide!

Était-ce utile?

La solution 2

La bonne solution est d'utiliser le offsetof () de la macro tête stddef.h.

En fait ceci:

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

doit être remplacé par

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

Et puis l'objet est placé en Flash à la fois pour C et C ++.

Autres conseils

Vous avez plusieurs problèmes. Pourquoi prenez-vous une adresse, la conversion à un entier, et en multipliant par 2? Vous ne devriez jamais en multipliant les adresses ou le stockage des adresses comme ints. L'arithmétique ne vous devriez jamais faire avec des pointeurs est de les soustraire (pour obtenir un décalage), ou d'ajouter un pointeur avec un décalage pour obtenir un autre pointeur.

En C ++, les règles pour l'initialisation de valeur mondiale sont un peu plus laxiste que dans C. En C, les valeurs doivent être initialisé à des constantes de compilation; en conséquence, le compilateur peut placer des valeurs globales const dans la mémoire en lecture seule. En C ++, les valeurs peuvent être initialisés à des expressions qui sont la compilation pas nécessairement constantes, et le compilateur est autorisé à générer du code à l'exécution pour le calcul de la valeur initiale. Ce code d'initialisation est appelé avant l'entrée en main(), semblable aux constructeurs pour les objets globaux.

Puisque vous travaillez avec des adresses constantes, et vous savez combien grande est un int sur votre plate-forme (je suppose 32 bits), vous devez simplement faire quelque chose comme ceci:

Ensuite, votre utilisation du mot-clé volatile est complètement faux. volatile dit au compilateur de ne pas enregistrer une variable dans un registre - il doit être rechargé de la mémoire à chaque fois qu'il est lu, et il doit être entièrement écrit en mémoire chaque fois qu'il est écrit. Déclarant la data_copy variable locale comme volatile est pratiquement inutile, sauf si vous attendez un autre thread ou un gestionnaire de signal pour commencer à le modifier de façon inattendue (très douteux). En outre, data_copy est juste une copie de l'adresse, pas le contenu du registre vous essayez de lire.

Qu'est-ce que vous devrait être en train de faire est de déclarer REGISTER comme pointeur sur un volatile - qui est l'un des objectifs explicites de volatile, pour accéder à des registres mappés en mémoire. Voici ce que votre code devrait ressembler à:

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

fait en sorte que quand vous faites des choses comme ceci:

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

Il fait toujours la bonne chose: une écriture de 1 à 0x12345678, une écriture de 2 à 0x12345678, une lecture de 0x1234567c, et une lecture de 0x2468acf8. Le mot-clé volatile veille à ce que les lectures et écritures se produisent toujours, et ils ne sont pas loin optimisés. Ne supprime pas le compilateur la première écriture à REGISTER.first, qui serait redondante si elle était une variable régulière

EDIT

En réponse à votre commentaire, voir la réponse de Andrew Medico à votre commentaire - vous vraiment multiplier le différence entre deux pointeurs par 2, ce qui est correct. Il suffit de faire attention à vos types de données. Je ne ai jamais parlé aussi quelque chose au sujet d'un noyau.

Vous pouvez obtenir gcc pour mettre les variables dans une section de données spécifiques à la section attribut :

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

Utilisez le nom de la section appropriée. Si vous n'êtes pas sûr de ce qui est, prenez le fichier objet généré par le compilateur C (qui vous avez dit qu'il met dans la section appropriée), exécutez nm sur elle, et voir ce que la section du compilateur C placé.

Avez-vous pris un coup d'œil à la Attributs variables , peut-être bien "section" vous aider dans la mise en place de la variable.

  1. Vous ne devriez pas multiplierez pointeurs.
  2. Vous ne devriez pas stocker des pointeurs comme "int".

Je suis sûr qu'il ya un moyen de faire ce que C ++ juridique que vous voulez, mais je ne peux pas comprendre ce qui est du code que vous avez posté.

EDIT :

Vous dites que vous voulez multiplier les pointeurs, mais qui est très probablement tort. Gardez à l'esprit: (int)(REGISTER) * 2 sera égale (int)(0x12345678 * 2) ce qui équivaut 0x2468ACF0 probablement pas ce que vous voulez. Vous voulez sans doute: REGISTER + sizeof(int) * 2 qui est 2 entiers passé le dernier octet du struct

.

Réponse d'origine:

Cela ressemble à une tentative avortée de faire le « pirater struct »(L'exemple utilise le style de C99, mais il fonctionne très bien en C89 aussi, suffit d'avoir un tableau de 1 dernier élément). Probablement ce que vous voulez est quelque chose comme ceci:

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

L'usage courant est de structures attribuées malloc comme ceci:

type_t *p = malloc((sizeof(int) * 3) + 20) /* allocate for the struct with 20 bytes for its data portion */

Je dirais que pour un accès plus sûr pour la lecture périphérique écrit que vous devez simplement utiliser la volatilité et l'offset définit définit. Jetant un adresse périphérique en tant que struct ne donne aucune sorte d'alignement ou de décalage garanties.

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

...

Si je comprends bien, votre objectif global est résumé dans ce paragraphe:

  

J'imposent de telles opérations pour créer des matrices const d'adresses de bits dans la région de BitBand de Cortex-M3. Est-ce un bug, ou peut-être qui est une limitation étrange du compilateur C?

J'utilise le compilateur Keil pour STM32, et l'un des exemples qui vient avec elle contient des macros pour le réglage et la compensation de bits bagués 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)))

Si ces exactement ne sont pas ce que vous cherchez, je pense qu'ils peuvent être modifiés pour répondre à vos besoins.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top