2010-07-21 15 views
8

Estoy trabajando en un proyecto integrado (objetivo de PowerPC, compilador de Codewarrior Freescale Metrowerks) donde los registros se mapean en memoria y se definen en bonitos campos de bits para facilitar la manipulación de las marcas de bits individuales.¿Qué hace el compilador de C con bitfields?

Por el momento, estamos utilizando esta característica para borrar indicadores de interrupción y controlar la transferencia de datos. Aunque todavía no he notado ningún error, tenía curiosidad de saber si esto es seguro. ¿Hay alguna manera de usar de forma segura los campos de bits, o tengo que ajustar cada uno en DISABLE_INTERRUPTS ... ENABLE_INTERRUPTS?

Para aclarar: la cabecera suministrado con el micro tiene campos como

union { 
     vuint16_t R; 
     struct { 
      vuint16_t MTM:1;  /* message buffer transmission mode */ 
      vuint16_t CHNLA:1;  /* channel assignement */ 
      vuint16_t CHNLB:1;  /* channel assignement */ 
      vuint16_t CCFE:1;  /* cycle counter filter enable */ 
      vuint16_t CCFMSK:6;  /* cycle counter filter mask */ 
      vuint16_t CCFVAL:6;  /* cycle counter filter value */ 
     } B; 
    } MBCCFR; 

Asumo estableciendo un bit en un campo de bits no es atómica. ¿Es esta una suposición correcta? ¿Qué tipo de código realmente genera el compilador para los campos de bits? Realizar la máscara yo mismo usando el campo R (crudo) podría hacer que sea más fácil recordar que la operación no es atómica (es fácil olvidar que una asignación como CAN_A.IMASK1.B.BUF00M = 1 no es atómica).

Su consejo es apreciado.

+1

Estoy en lo correcto al pensar que el '' V' en vuint16_t' significa "volátil"? – JAB

+0

Sí, los tipos son volátiles ya que son todas las ubicaciones de registro mapeadas en memoria. También hay instrucciones de alineación que dejé para mantener las cosas cortas. –

+0

Evite los campos de bits y use la máscara y el desplazamiento en su lugar. – starblue

Respuesta

3

La atomicidad depende del objetivo y del compilador. AVR-GCC, por ejemplo, intenta detectar el acceso a los bits y, si es posible, emitir un conjunto de bits o instrucciones claras. Compruebe la salida del ensamblador para estar seguro ...

EDIT: Aquí es un recurso para obtener instrucciones atómicas sobre PowerPC directamente de la boca del caballo:

http://www.ibm.com/developerworks/library/pa-atom/

+0

Cuando puede, pero no todas las direcciones admiten esto, y esto no se puede hacer a menos que la dirección se conozca en tiempo de compilación. – nategoose

+0

@nategoose Sí, es mejor deletrearlo como tú que implicar como lo hice. –

3

Es correcto suponer que el establecimiento de campos de bits no es atómico. El estándar C no es particularmente claro sobre cómo deberían implementarse los campos de bits y varios compiladores realizan varias formas de ellos.

Si realmente solo le interesan la arquitectura y el compilador de destino, desmonte algún código de objeto.

En general, su código logrará el resultado deseado pero será mucho menos eficiente que el código utilizando macros y cambios. Dicho esto, probablemente sea más fácil usar tus campos de bits si no te importa el rendimiento aquí.

Siempre se puede escribir una función de contenedor setter para los bits que es atómica, si le preocupa que los codificadores futuros (incluido usted) se confundan.

0

Depende totalmente de la arquitectura y el compilador si las operaciones de campo de bits son atómicas o no. Mi experiencia personal dice: no uses bitfields si no tienes que hacerlo.

3

Sí, su suposición es correcta, en el sentido de que no puede asumir la atomicidad. En una plataforma específica, puede obtenerla como un extra, pero no puede confiar en ella en ningún caso.

Básicamente, el compilador realiza enmascaramiento y cosas para usted. Él podría aprovechar los casos de esquina o instrucciones especiales. Si está interesado en la eficiencia, busque en el ensamblador que su compilador produce con eso, por lo general es bastante instructivo. Como regla general, diría que los compiladores modernos producen código que es tan eficiente como lo sería un esfuerzo de programación medio. El twiddeling real de bits profundos para tu compilador específico podría quizás ganarte algunos ciclos.

0

estoy bastante seguro de que el PowerPC esto no es atómico, pero si su objetivo es un sistema único núcleo a continuación, puedes:

void update_reg_from_isr(unsigned * reg_addr, unsigned set, unsigned clear, unsigned toggle) { 
    unsigned reg = *reg_addr; 
    reg |= set; 
    reg &= ~clear; 
    reg ^= toggle; 
    *reg_addr = reg; 
} 

void update_reg(unsigned * reg_addr, unsigned set, unsigned clear, unsigned toggle) { 
    interrupts_block(); 
    update_reg_from_isr(reg_addr, set, clear, toggle); 
    interrupts_enable(); 
} 

No recuerdo si el manejador de interrupciones de PowerPC son interrumpibles, pero si lo son, solo debes usar la segunda versión siempre.

Si su objetivo es un sistema multiprocesador, debe hacer bloqueos (spinlocks, que deshabilitan las interrupciones en el procesador local y luego esperar que otros procesadores finalicen con el bloqueo) que protegen el acceso a cosas como registros de hardware y adquieren los bloqueos necesarios antes de acceder al registro, y luego suelte los bloqueos inmediatamente después de que haya terminado de actualizar el registro (o registros).

Leí una vez cómo implementar bloqueos en powerpc: implicó decirle al procesador que mire el bus de memoria para cierta dirección mientras hacía algunas operaciones y luego volver a verificar al final de esas operaciones para ver si la dirección del reloj había sido escrito por otro núcleo. Si no lo hubiera hecho, su operación fue exitosa; si lo hubiera hecho, ha tenido que rehacer la operación. Esto estaba en un documento escrito para compiladores, bibliotecas y desarrolladores de sistemas operativos. No recuerdo dónde lo encontré (probablemente en algún lugar de IBM.com), pero un poco de cacería debería subirlo. Probablemente también tenga información sobre cómo hacer movimientos de bits atómicos.

+0

Guau, eso suena como una receta para el interbloqueo: El procesador A inicia la sección crítica, mira la dirección. El procesador B inicia la sección crítica, busca la dirección. El procesador A termina de escribir. El procesador B termina de escribir. El procesador A busca en la dirección. ¡Ha cambiado! Procesador A reescribe datos. El procesador B busca la dirección. ¡Ha cambiado! El procesador B reescribe datos. Continuar en bucle infinito. (Disculpa por el formato. Los comentarios no parecen gustarle) – nmichaels

+0

@Nathon: Es un poco más complicado y simple que eso, pero ha pasado un tiempo desde que leí la documentación. Realmente había algo que evitaba que fuera una situación de punto muerto, pero también permitía más flexibilidad que el enfoque de instrucción atómica única utilizado en otras arquitecturas. Pudo haber sido que la escritura final en la RAM no sucedió si otro procesador había escrito en esa dirección después de que este procesador comenzara a verla. – nategoose

+1

Se lo conoce como "carga vinculada/almacenar condicional" en algunas arquitecturas. PPC lo llama 'lwarx' (palabra de carga y reserva indexada) y' stwcx' (palabra de tienda condicional indexada (y registro)). La tienda está condicionada a que nadie más haya escrito en la memoria; un procesador siempre ganará. Además, la "reserva" se borra a través de los cambios de contexto. De cualquier manera, no desea hacerlo en los campos de bits de hardware ... –

3

creo que el uso de campos de bits para modelar los registros de hardware no es una buena idea.

Tanto la forma en que los compiladores manejan los campos de bits está definida por la implementación (incluyendo cómo se manejan los campos que abarcan límites de byte o palabra, problemas de endiabilidad y cómo se implementan, configuran y eliminan los bits). Consulte C/C++: Force Bit Field Order and Alignment

Para verificar que los accesos al registro se estén manejando de la forma en que podría esperarlos o necesitarlos, tendría que estudiar cuidadosamente los documentos del compilador y/o mirar el código emitido. Supongo que si los encabezados suministrados con el conjunto de herramientas del microprocesador los usan, se puede suponer que la mayoría de mis preocupaciones están resueltas. Sin embargo, supongo que el acceso atómico no es necesariamente ...

Creo que es mejor manejar este tipo de accesos a nivel de bits de registros de hardware que usan funciones (o macros, si se debe) que realizan lecturas explícitas/modificar/escribir operaciones con la máscara de bits que necesita, si eso es lo que su procesador requiere.

Esas funciones podrían ser modificados para arquitecturas que soportan accesos a nivel de bits atómicas (como "poco bandeo" del ARM Cortex M3 direccionamiento). No sé si el PowerPC es compatible con algo como esto: el M3 es el único procesador con el que he tratado que lo admite de forma general. E incluso las bandas de bits del M3 admiten accesos de 1 bit; si se trata de un campo de 6 bits de ancho, debe volver al escenario de lectura/modificación/escritura.

Cuestiones relacionadas