Pregunta

En un sistema integrado, tenemos una configuración que nos permite leer datos arbitrarios a través de una interfaz de línea de comandos con fines de diagnóstico. Para la mayoría de los datos, esto funciona bien, utilizamos memcpy () para copiar los datos en la dirección solicitada y enviarlos de vuelta a través de una conexión en serie.

Sin embargo, para los registros de hardware de 16 bits, memcpy () causa algunos problemas. Si intento acceder a un registro de hardware de 16 bits utilizando dos accesos de 8 bits, el byte de orden superior no se lee correctamente.

¿Alguien ha encontrado este problema? Soy un tipo de 'alto nivel' (C # / Java / Python / Ruby) que se está acercando al hardware y este es un territorio extraño.

¿Cuál es la mejor manera de lidiar con esto? Veo cierta información, específicamente, una publicación algo confusa [para mí] aquí . El autor de esta publicación tiene exactamente el mismo problema que yo pero odio implementar una solución sin comprender completamente lo que estoy haciendo.

Cualquier luz que pueda arrojar sobre este tema es muy apreciada. Gracias!

¿Fue útil?

Solución

Además de lo que Eddie dijo , normalmente necesita usar un puntero volátil para leer un registro de hardware (suponiendo un registro mapeado de memoria, que no es el caso para todos los sistemas, pero parece que es cierto para el suyo). Algo así como:

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

Aquí hay una serie de artículos de Dan Saks que deberían brindarle casi todo lo que necesita saber para poder utilizar efectivamente los registros mapeados en memoria en C / C ++:

Otros consejos

Cada registro en este hardware está expuesto como una matriz de dos bytes, el primer elemento está alineado en un límite de dos bytes (su dirección es par). memcpy () ejecuta un ciclo y copia un byte en cada iteración, por lo que copia de estos registros de esta manera (todos los bucles desenrollados, char es un byte):

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

Sin embargo, la segunda línea funciona incorrectamente por algunas razones específicas de hardware. Si copia dos bytes a la vez en lugar de uno a la vez, se hace de esta manera (el int corto es dos bytes):

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

Aquí copia dos bytes en una operación y el primer byte está alineado uniformemente. Como no hay una copia separada de una dirección extrañamente alineada, funciona.

La memoria modificada verifica si las direcciones están alineadas correctamente y copia en fragmentos de bytes de remolque si lo están.

Si necesita acceso a registros de hardware de un tamaño específico, entonces tiene dos opciones:

  • Comprenda cómo su compilador de C genera código para que pueda usar el tipo entero apropiado para acceder a la memoria, o
  • Incruste algún ensamblado para hacer el acceso con el byte correcto o el tamaño de palabra.

La lectura de registros de hardware puede tener efectos secundarios, dependiendo del registro y su función, por supuesto, por lo que es importante acceder a los registros de hardware con el acceso de tamaño adecuado para que pueda leer todo el registro de una vez.

Por lo general, es suficiente usar un tipo entero que sea del mismo tamaño que su registro. En la mayoría compiladores, un corto es de 16 bits.

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

Creo que todos los detalles están contenidos en ese hilo que publicaste, así que intentaré desglosarlo un poco;

Específicamente;

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.

Entonces, el problema aquí es que los registros de hardware solo informan el valor correcto si sus valores se leen en una sola lectura de 16 bits. Esto sería equivalente a hacerlo;

uint16 value = *(regAddress);

Esto lee desde la dirección en el registro de valores utilizando una sola lectura de 16 bytes. Por otro lado, tiene memcpy que está copiando datos de un solo byte a la vez. Algo así;

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

Por lo tanto, esto hace que los registros se lean 8 bits (1 byte) a la vez, lo que da como resultado que los valores no sean válidos.

La solución publicada en ese hilo es usar una versión de memcpy que copiará los datos usando lecturas de 16 bits siempre que el origen y el destino estén alineados a6 bits.

¿Qué necesitas saber? Ya has encontrado una publicación separada que lo explica. Aparentemente, la documentación de la CPU requiere que se accede a los registros de hardware de 16 bits con lecturas y escrituras de 16 bits, pero su implementación de memcpy utiliza lecturas / escrituras de 8 bits. Entonces no trabajan juntos.

La solución es simplemente no usar memcpy para acceder a este registro. En su lugar, escriba su propia rutina que copie valores de 16 bits.

No estoy seguro de cuál es exactamente la pregunta: creo que la publicación tiene la solución correcta. Como dijo, el problema es que la rutina memcpy () estándar lee un byte a la vez, lo que no funciona correctamente para los registros de hardware mapeados en memoria. Esa es una limitación del procesador: simplemente no hay forma de obtener un valor válido leyendo un byte a la vez.

La solución sugerida es escribir su propia memcpy () que solo funciona en direcciones alineadas con palabras y lee palabras de 16 bits a la vez. Esto es bastante sencillo: el enlace proporciona una versión c y una versión de ensamblaje. El único inconveniente es asegurarse de que siempre haga las copias de 16 bits de una dirección válida alineada. Puede hacerlo de 2 maneras: use comandos de enlace o pragmas para asegurarse de que las cosas estén alineadas, o agregue un caso especial para el byte adicional al frente de un búfer sin alinear.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top