2011-02-10 9 views
5

Tengo algunos datos constantes que quiero almacenar en la ROM, ya que hay una buena cantidad y estoy trabajando con un dispositivo ARM7 incrustado de memoria. Estoy tratando de hacer esto utilizando las estructuras que se parecen algo como esto:almacenando estructuras en la ROM en el dispositivo ARM

struct objdef 
{ 
    int x; 
    int y; 
    bool (*function_ptr)(int); 
    some_other_struct * const struct_array; // array of similar structures 
    const void* vp; // previously ommittted to shorten code 
} 

que luego crear e inicializar como globales:

const objdef def_instance = { 2, 3, function, array, NULL }; 

Sin embargo, esto consume un poco de memoria RAM a pesar de la const al principio. Más específicamente, aumenta significativamente la cantidad de datos RW y, finalmente, hace que el dispositivo se bloquee si se crean suficientes instancias.

Estoy usando uVision y el compilador ARM, junto con el núcleo RTX en tiempo real.

¿Alguien sabe por qué esto no funciona o sabe una mejor manera de almacenar datos heterogéneos estructurados en la ROM?

actualización

Gracias a todos por sus respuestas y mis disculpas por no volver a estar con ustedes antes. Así que aquí está la puntuación hasta el momento y algunas observaciones adicionales de mi parte.

Lamentablemente, __attribute__ tiene un efecto nulo en la RAM frente a la ROM y lo mismo ocurre con static const. Todavía no he tenido tiempo de probar la ruta de ensamblaje.

Mis compañeros de trabajo y yo hemos descubierto un comportamiento más inusual.

En primer lugar, debo señalar que por simplicidad no mencioné que mi estructura objdef contiene un campo const void*. El campo a veces se le asigna un valor de una tabla de cadenas definido como

char const * const string_table [ROWS][COLS] = 
{ 
    { "row1_1", "row1_2", "row1_3" }, 
    { "row2_1", "row2_2", "row2_3" }, 
... 
} 

const objdef def_instance = { 2, 3, function, array, NULL };//->ROM 
const objdef def_instance = { 2, 3, function, array, string_table[0][0] };//->RAM 

string_table es en la ROM como se esperaba. Y aquí está el truco: las instancias de objdef se ponen en la ROM hasta que uno de los valores en string_table se asigna a ese campo const void*. Después de eso, la instancia de struct se mueve a la RAM.

Pero cuando se cambia a string_table

char const string_table [ROWS][COLS][MAX_CHARS] = 
{ 
    { "row1_1", "row1_2", "row1_3" }, 
    { "row2_1", "row2_2", "row2_3" }, 
... 
} 

const objdef def_instance = { 2, 3,function, array, NULL };//->ROM 
const objdef def_instance = { 2, 3, function, array, string_table[0][0] };//->ROM 

esas instancias de objdef se colocan en la memoria ROM a pesar de que const void* assigment. No tengo idea de por qué esto debería importar.

Estoy empezando a sospechar que Dan tiene razón y que nuestra configuración está arruinada en alguna parte.

+0

Intenta declarar como 'const' estático. Noté que algunos compiladores copiarán los datos de la ROM para apilarlos cuando solo se declaran como 'const'; el acceso directo a la ROM ocurre cuando se aplica 'const estático '. –

+0

C++ 'const' no significa" físicamente solo lectura ". Puede tener const_cast, por un lado. –

+0

Sé que siempre puede realizar un const_cast, pero me encontré con una serie de recursos en la web que sugerían usar 'const' para obtener datos almacenados en la ROM. Aparte de esto, he aprendido a través de la experiencia que 'const' algunas veces pero no siempre tiene este efecto. Obviamente, no tengo muy claro cuándo ni por qué. :) – dandan78

Respuesta

1

Siempre puede intentar usar el lenguaje ensamblador.

Ponga la información usando las declaraciones DATA y publique (publique) las direcciones iniciales de los datos.

En mi experiencia, los grandes datos de solo lectura se declararon en un archivo fuente como static const. Una función global simple dentro del archivo fuente devolvería la dirección de los datos.

+0

Voy a probar ambas sugerencias a primera hora de la mañana. Gracias. – dandan78

+0

La sección .data normalmente es de lectura de datos, por lo que siempre se cargará en ram – doron

1

Supongo que tiene un scatterfile que separa sus secciones de RAM y ROM.Lo que quiere hacer es especificar su estructura con un atributo para qué sección se colocará, o colocarla en su propio archivo de objeto y luego especificar eso en la sección que desea que esté en el scatterfile.

__attribute__((section("ROM"))) const objdef def_instance = { 2, 3, function, array }; 

El C "const" palabra clave en realidad no hacen que el compilador para poner algo en el texto o sección const. Solo le permite al compilador advertirle sobre los intentos de modificarlo. Es perfectamente válido para obtener un puntero a un objeto const, convertirlo en un non-const y escribir en él, y el compilador debe ser compatible con eso.

+0

Sí, tengo un scatterfile y creo que está configurado correctamente. Lo verificaré dos veces y probaré tu solución también. Gracias. – dandan78

+1

no es "perfectamente válido". Es un comportamiento indefinido, y un compilador tiene todo el derecho de colocar una variable 'const' en la ROM. – MSalters

1

Si está haciendo cosas en ARM, probablemente esté utilizando el formato binario ELF. Los archivos ELF contienen varias secciones pero los datos constantes deben encontrar su camino en las secciones .rodata o .text del binario ELF. Debería poder verificar esto con la utilidad GNU readelf o la utilidad RVCT fromelf.

Ahora suponiendo que los símbolos se encuentran en la parte correcta del archivo elf, ahora necesita saber cómo funciona el cargador RTX. Tampoco hay ninguna razón por la que las instancias no puedan compartir la misma memoria de solo lectura, pero esto dependerá del cargador. Si el ejecutable está almacenado en la ROM, puede ejecutarse in situ, pero aún puede cargarse en la RAM. Esto también depende del cargador.

+0

Estoy usando uVision y el compilador ARM en Windows, y la instalación de Cygwin no es algo que me gustaría hacer en mi máquina de desarrollo. Acabo de comprobar mi directorio de salida y no hay .elf. Lo que sí tengo es un contenedor, que puedo cargar directamente desde uVision o usando un gestor de arranque personalizado que está en el dispositivo. – dandan78

2

Su pensamiento es correcto y razonable. He usado Keil/uVision (esto fue v3, ¿tal vez hace 3 años?) Y siempre funcionó como lo esperabas, es decir, puso los datos de const en flash/ROM.

Sospecho que su configuración/script del enlazador. Intentaré volver a mi trabajo anterior & para ver cómo lo configuré. No tuve que agregar las directivas #pragma o __attribute__, acabo de tener el lugar .const & .text en flash/ROM. Configuré el mapa de configuración/memoria del enlazador hace bastante tiempo, así que desafortunadamente, mi recuerdo no es muy reciente.

(yo soy un poco confundido por las personas que están hablando de fundición & punteros const, etc ... Usted no pidió nada de eso & pareces entender como "const" funciona. Desea colocar el datos inicializados en flash/ROM para guardar RAM (no ROM-> copia de RAM al inicio), sin mencionar una ligera aceleración en el arranque, ¿no? No está preguntando si es posible cambiarlo o lo que sea ...)

EDIT/UPDATE:

acabo de notar el último campo en su estructura (const) es un some_other_struct * const (puntero constante a un some_other_ estructura). Es posible que desee intentar convertirlo en un puntero (constante) a constante some_other_struct [some_other_struct const * const] (suponiendo que lo que señala es realmente constante). En ese caso, podría funcionar. No recuerdo los detalles (¿ves un tema aquí?), Pero esto empieza a parecerme familiar. Incluso si su puntero no es un elemento constante, y no puede hacerlo, intente cambiar la definición de estructura & inicializándola con un puntero para const y simplemente vea si eso lo coloca en la ROM. Aunque lo tiene como un puntero const y no puede cambiar una vez que se construye la estructura, parece recordar algo en el que si el destino no es también const, el vinculador no cree que pueda inicializarse completamente en el momento del enlace. & difiere la inicialización a cuando se ejecuta el código de inicio C de tiempo de ejecución, incl. la copia ROM a RAM de la memoria RW inicializada.

+0

Gracias, ya probé el puntero const para acercar el tipo de enfoque también, pero sin suerte. Sin embargo, se ha observado otro comportamiento extraño. Mira mi actualización a la pregunta cuando puedas. – dandan78

0

Un ejemplo completo hubiera sido mejor.Si tomo algo como esto:

 
typedef struct 
{ 
    char a; 
    char b; 
} some_other_struct; 

struct objdef 
{ 
    int x; 
    int y; 
    const some_other_struct * struct_array; 
}; 

typedef struct 
{ 
    int x; 
    int y; 
    const some_other_struct * struct_array; 
} tobjdef; 


const some_other_struct def_other = {4,5}; 
const struct objdef def_instance = { 2, 3, &def_other}; 
const tobjdef tdef_instance = { 2, 3, &def_other}; 

unsigned int read_write=7; 

y compilarlo con la última CodeSourcery Lite

 
arm-none-linux-gnueabi-gcc -S struct.c 

consigo

 
    .arch armv5te 
    .fpu softvfp 
    .eabi_attribute 20, 1 
    .eabi_attribute 21, 1 
    .eabi_attribute 23, 3 
    .eabi_attribute 24, 1 
    .eabi_attribute 25, 1 
    .eabi_attribute 26, 2 
    .eabi_attribute 30, 6 
    .eabi_attribute 18, 4 
    .file "struct.c" 
    .global def_other 
    .section .rodata 
    .align 2 
    .type def_other, %object 
    .size def_other, 2 
def_other: 
    .byte 4 
    .byte 5 
    .global def_instance 
    .align 2 
    .type def_instance, %object 
    .size def_instance, 12 
def_instance: 
    .word 2 
    .word 3 
    .word def_other 
    .global tdef_instance 
    .align 2 
    .type tdef_instance, %object 
    .size tdef_instance, 12 
tdef_instance: 
    .word 2 
    .word 3 
    .word def_other 
    .global read_write 
    .data 
    .align 2 
    .type read_write, %object 
    .size read_write, 4 
read_write: 
    .word 7 
    .ident "GCC: (Sourcery G++ Lite 2010.09-50) 4.5.1" 
    .section .note.GNU-stack,"",%progbits 

Con la sección marcada como .rodata, que yo supongo es deseado. Luego, depende del script del enlazador asegurarse de que los datos ro se pongan en rom. Y tenga en cuenta que la variable read_write es después de cambiar de .rodata a .data que es de lectura/escritura.

Así que para hacer esto un binario completo y ver si se coloca en la memoria ROM o RAM (.text o .data) entonces

start.s

 

.globl _start 
_start: 
    b reset 
    b hang 
    b hang 
    b hang 

    b hang 
    b hang 
    b hang 
    b hang 

reset: 
hang: b hang 

Entonces

 
# arm-none-linux-gnueabi-gcc -c -o struct.o struct.c 
# arm-none-linux-gnueabi-as -o start.o start.s 
# arm-none-linux-gnueabi-ld -Ttext=0 -Tdata=0x1000 start.o struct.o -o struct.elf 
# arm-none-linux-gnueabi-objdump -D struct.elf > struct.list 

Y obtenemos

 

Disassembly of section .text: 

00000000 <_start>: 
    0: ea000006 b 20 <reset> 
    4: ea000008 b 2c <hang> 
    8: ea000007 b 2c <hang> 
    c: ea000006 b 2c <hang> 
    10: ea000005 b 2c <hang> 
    14: ea000004 b 2c <hang> 
    18: ea000003 b 2c <hang> 
    1c: ea000002 b 2c <hang> 

00000020 <reset>: 
    20: e59f0008 ldr r0, [pc, #8] ; 30 <hang+0x4> 
    24: e5901000 ldr r1, [r0] 
    28: e5801000 str r1, [r0] 

0000002c <hang>: 
    2c: eafffffe b 2c <hang> 
    30: 00001000 andeq r1, r0, r0 

Disassembly of section .data: 

00001000 <read_write>: 
    1000: 00000007 andeq r0, r0, r7 

Disassembly of section .rodata: 

00000034 <def_other>: 
    34: 00000504 andeq r0, r0, r4, lsl #10 

00000038 <def_instance>: 
    38: 00000002 andeq r0, r0, r2 
    3c: 00000003 andeq r0, r0, r3 
    40: 00000034 andeq r0, r0, r4, lsr r0 

00000044 <tdef_instance>: 
    44: 00000002 andeq r0, r0, r2 
    48: 00000003 andeq r0, r0, r3 
    4c: 00000034 andeq r0, r0, r4, lsr r0 

Y eso logró el resultado deseado. La variable read_write está en ram, las estructuras están en la rom. Necesito asegurarme de que tanto las declaraciones de const están en los lugares correctos, el compilador no da advertencias sobre decir poner un const en algún puntero a otra estructura que no puede determinar en tiempo de compilación como un const, e incluso con todo eso obteniendo la secuencia de comandos del enlazador (si usa uno) para funcionar como lo desee puede requerir un poco de esfuerzo. por ejemplo, este parece funcionar:

 

MEMORY 
{ 
    bob(RX) : ORIGIN = 0x0000000, LENGTH = 0x8000 
    ted(WAIL) : ORIGIN = 0x2000000, LENGTH = 0x8000 
} 

SECTIONS 
{ 
    .text : { *(.text*) } > bob 
    .data : { *(.data*) } > ted 
} 

+0

Publicar toda la sección de código habría sido una decepción para la mayoría de las personas, por lo que di una versión condensada que presentaba el mismo problema. Aparte de eso, creo que su ejemplo se basa en la cadena de herramientas GNU. Estoy usando el proporcionado por ARM. – dandan78

+0

Lo siento, lo siento, no vi la referencia del compilador del brazo. ¿Qué compilador de brazo? Keil? RVCT? o el ADS anterior o el anterior, no recuerdo su nombre (¿SBT?). Al menos en los días de ADS, el soporte técnico de ARM era muy bueno (se necesitaba obtener algo por el dinero pagado por esas herramientas). ¿proporcionaron alguna idea? –

Cuestiones relacionadas