Question

Sur un système intégré, notre configuration nous permet de lire des données arbitraires sur une interface de ligne de commande à des fins de diagnostic. Pour la plupart des données, cela fonctionne correctement. Nous utilisons memcpy () pour copier les données à l'adresse demandée et les renvoyer via une connexion série.

Cependant, pour les registres de matériel 16 bits, memcpy () pose certains problèmes. Si j'essaie d'accéder à un registre matériel 16 bits à l'aide de deux accès 8 bits, l'octet de poids fort ne lit pas correctement.

Quelqu'un at-il rencontré ce problème? Je suis un type "de haut niveau" (C # / Java / Python / Ruby) qui se rapproche du matériel et qui constitue un territoire étranger.

Quelle est la meilleure façon de gérer cela? Je vois des informations, en particulier, un message quelque peu déroutant, ici . L'auteur de cet article a exactement le même problème que moi, mais je n'aime pas mettre en œuvre une solution sans bien comprendre ce que je fais.

Tous les éclaircissements que vous pouvez apporter sur cette question sont les bienvenus. Merci!

Était-ce utile?

La solution

En plus de ce que a déclaré Eddie , vous avez généralement devez utiliser un pointeur volatile pour lire un registre matériel (en supposant un registre mappé en mémoire, ce qui n’est pas le cas pour tous les systèmes, mais cela ressemble à ce qui est vrai pour le vôtre). Quelque chose comme:

// using types from stdint.h to ensure particular size values
// most systems that access hardware registers will have typedefs
// for something similar (for 16-bit values might be uint16_t, INT16U,
// or something)

uint16_t volatile* pReg = (int16_t volatile*) 0x1234abcd;  // whatever the reg address is

uint16_t val = *pReg;  // read the 16-bit wide register

Voici une série d’articles de Dan Saks qui devraient vous donner à peu près tout ce que vous devez savoir pour pouvoir utiliser efficacement les registres mappés en mémoire en C / C ++:

Autres conseils

Chaque registre de ce matériel est exposé sous la forme d'un tableau de deux octets. Le premier élément est aligné sur une limite de deux octets (son adresse est paire). memcpy () exécute un cycle et copie un octet à chaque itération. Il copie donc de ces registres de cette manière (toutes les boucles sont déroulées, char est un octet):

*((char*)target) = *((char*)register);// evenly aligned - address is always even
*((char*)target + 1) = *((char*)register + 1);//oddly aligned - address is always odd

Cependant, la deuxième ligne ne fonctionne pas correctement pour certaines raisons spécifiques au matériel. Si vous copiez deux octets à la fois au lieu d'un à la fois, la procédure est la suivante (short int est deux octets):

*((short int*)target) = *((short*)register;// evenly aligned

Ici, vous copiez deux octets en une seule opération et le premier octet est aligné de manière égale. Comme il n’ya pas de copie séparée à partir d’une adresse étrangement alignée, cela fonctionne.

Le memcpy modifié vérifie si les adresses sont bien alignées et les copie en morceaux d'octets de remorquage si elles le sont.

Si vous avez besoin d'accéder à des registres de matériel d'une taille spécifique, vous avez le choix entre:

  • Comprenez comment votre compilateur C génère du code afin que vous puissiez utiliser le type entier approprié pour accéder à la mémoire, ou
  • Intégrez un assemblage pour effectuer l'accès avec l'octet ou la taille de mot appropriés.

La lecture des registres de matériel peut avoir des effets secondaires, en fonction du registre et de sa fonction, bien entendu. Il est donc important d’accéder aux registres de matériel avec un accès de taille appropriée pour pouvoir lire l’ensemble du registre en une fois.

Généralement, il suffit d’utiliser un type entier de la même taille que votre registre. Sur la plupart des compilateurs, un court est de 16 bits.

void wordcpy(short *dest, const short *src, size_t bytecount)
{
    int i;
    for (i = 0;  i < bytecount/2;  ++i)
        *dest++ = *src++;
}

Je pense que tous les détails sont contenus dans le fil que vous avez posté, alors je vais essayer de le décomposer un peu;

Spécifiquement;

If you access a 16-bit hardware register using two 8-bit
accesses, the high-order byte doesn't read correctly (it
always read as 0xFF for me). This is fair enough since
TI's docs state that 16-bit hardware registers must be
read and written using 16-bit-wide instructions, and
normally would be, unless you're using memcpy() to
read them.

Le problème ici est que les registres matériels ne signalent la valeur correcte que si leurs valeurs sont lues en une seule lecture 16 bits. Ce serait équivalent à faire;

uint16 value = *(regAddress);

Ceci lit l’adresse dans le registre de valeurs en utilisant une seule lecture de 16 octets. D'autre part, vous avez memcpy, qui copie des données un octet à la fois. Quelque chose comme;

while (n--)
{
  *(uint8*)pDest++ = *(uint8*)pSource++;
}

Ainsi, les registres sont lus 8 bits à la fois (1 octet), ce qui rend les valeurs non valides.

La solution publiée dans ce fil de discussion consiste à utiliser une version de memcpy permettant de copier les données à l'aide de lectures 16 bits, quelle que soit l'origine de la source et de la destination.

Que devez-vous savoir? Vous avez déjà trouvé un article séparé l'expliquant. Apparemment, la documentation de la CPU requiert que les registres de matériel 16 bits soient accessibles avec des lectures et des écritures 16 bits, mais que votre implémentation de memcpy utilise des lectures / écritures 8 bits. Donc, ils ne travaillent pas ensemble.

La solution consiste simplement à ne pas utiliser memcpy pour accéder à ce registre. A la place, écrivez votre propre routine qui copie les valeurs 16 bits.

Je ne sais pas exactement quelle est la question - je pense que cet article a la bonne solution. Comme vous l'avez dit, le problème est que la routine standard memcpy () lit un octet à la fois, ce qui ne fonctionne pas correctement pour les registres matériels mappés en mémoire. C’est une limitation du processeur: il n’ya tout simplement aucun moyen d’obtenir une valeur valide qui lit un octet à la fois.

La solution suggérée consiste à écrire votre propre memcpy (), qui ne fonctionne que sur les adresses alignées sur les mots et lit les mots de 16 bits à la fois. Ceci est assez simple - le lien donne à la fois une version c et une version assemblée. La seule chose à faire est de vous assurer de toujours faire les copies 16 bits à partir d'une adresse correctement alignée. Vous pouvez le faire de deux manières: soit en utilisant des commandes de l'éditeur de liens ou des pragmas pour vous assurer que les éléments sont alignés, soit en ajoutant un cas spécial pour l'octet supplémentaire situé à l'avant d'un tampon non aligné.

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