2011-02-10 17 views
5

Ho alcuni dati costanti che voglio memorizzare in ROM poiché ne esiste una buona quantità e sto lavorando con un dispositivo embedded ARM7 con vincoli di memoria. Sto cercando di farlo usando strutture che simile a questa:Memorizzare le strutture nella ROM sul 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 
} 

che ho poi creare e inizializzare come globali:

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

Tuttavia, questo mangia un po 'di RAM, nonostante la const all'inizio. Più specificamente, aumenta significativamente la quantità di dati RW e alla fine fa bloccare il dispositivo se vengono create abbastanza istanze.

Sto usando uVision e il compilatore ARM, insieme al kernel RTX in tempo reale.

Qualcuno sa perché questo non funziona o conosce un modo migliore per archiviare dati eterogenei strutturati nella ROM?

Aggiornamento

Grazie a tutti per le vostre risposte e le mie scuse per non tornare a voi ragazzi in precedenza. Quindi ecco il punteggio finora e alcune osservazioni aggiuntive da parte mia.

Purtroppo, __attribute__ ha un effetto zero su RAM vs ROM e lo stesso vale per static const. Non ho ancora avuto il tempo di provare la via dell'assemblaggio.

I miei colleghi e io abbiamo scoperto un comportamento più insolito, però.

In primo luogo, devo notare che, per semplicità, non ho menzionato che la mia struttura objdef contiene un campo const void*. Il campo viene talvolta assegnato un valore da una tabella stringa definita come

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 è nella ROM come previsto. Ed ecco il kicker: le istanze di objdef vengono inserite nella ROM fino a quando uno dei valori in string_table viene assegnato a quel campo const void*. Successivamente l'istanza della struct viene spostata nella RAM.

Ma quando string_table viene modificato in

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 

quelle istanze di objdef sono collocati in ROM nonostante ciò const void* assigment. Non ho idea del perché questo dovrebbe avere importanza.

Sto iniziando a sospettare che Dan abbia ragione e che la nostra configurazione sia incasinata da qualche parte.

+0

Provare a dichiarare come 'const statico'. Ho notato che alcuni compilatori copieranno i dati dalla ROM allo stack quando viene dichiarato come 'const'; l'accesso diretto alla ROM si verifica quando viene applicato 'static const'. –

+0

C++ 'const' non significa" fisicamente di sola lettura ". Puoi avere const_cast, per una cosa .. –

+0

So che puoi sempre eseguire un const_cast, ma mi sono imbattuto in un certo numero di risorse sul web che suggerivano di usare 'const' per ottenere i dati memorizzati nella ROM. A parte questo, ho imparato attraverso l'esperienza che 'const' a volte ma non sempre ha questo effetto. Ovviamente non sono del tutto chiaro quando o perché. :) – dandan78

risposta

1

Si può sempre provare a utilizzare il linguaggio assembly.

Inserire le informazioni utilizzando le dichiarazioni DATA e pubblicare (rendere pubblici) gli indirizzi iniziali dei dati.

Nella mia esperienza, i dati di sola lettura di grandi dimensioni sono stati dichiarati in un file di origine come static const. Una semplice funzione globale all'interno del file sorgente restituirebbe l'indirizzo dei dati.

+0

Proverò entrambi i suggerimenti per prima cosa al mattino. Grazie. – dandan78

+0

La sezione .data è normalmente una lettura dei dati di scrittura, quindi verrà sempre caricata nella ram – doron

1

Suppongo che tu abbia uno scatterfile che separa le tue sezioni RAM e ROM.Quello che vuoi fare è specificare la tua struttura con un attributo per quale sezione sarà posizionata, o metterla nel proprio file oggetto e quindi specificare che nella sezione vuoi che sia nello scatterfile.

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

Il C "const" parola chiave in realtà non causa il compilatore di mettere qualcosa nel testo o sezione const. Permette solo al compilatore di avvisarti dei tentativi di modificarlo. È perfettamente valido per ottenere un puntatore a un oggetto const, convertirlo in un non-const e scriverlo, e il compilatore deve supportarlo.

+0

Sì, ho uno scatterfile e credo che sia impostato correttamente. Lo ricontrollerò e proverò anche la tua soluzione. Grazie. – dandan78

+1

non è "perfettamente valido". È un comportamento indefinito e un compilatore ha tutto il diritto di inserire una variabile 'const' nella ROM. – MSalters

1

Se stai facendo cose su ARM probabilmente stai utilizzando il formato binario ELF. I file ELF contengono un numero di sezioni, ma i dati costanti dovrebbero trovarsi nelle sezioni .rodata o .text del file binario ELF. Dovresti poterlo verificare con l'utilità GNU readelf o con l'utilità RVCT fromelf.

Ora assumendo che i simboli si trovino nella parte corretta del file elf, ora è necessario scoprire come fa il caricatore RTX a svolgere il proprio lavoro. Non vi è inoltre alcuna ragione per cui le istanze non possano condividere la stessa memoria di sola lettura, ma ciò dipenderà dal caricatore. Se l'eseguibile è memorizzato nella rom, può essere eseguito sul posto ma può ancora essere caricato nella RAM. Questo dipende anche dal caricatore.

+0

Sto usando uVision e il compilatore ARM su Windows e installare Cygwin non è qualcosa che mi piacerebbe fare sul mio computer di sviluppo. Ho appena controllato la mia directory di output e non c'è nessuno. Quello che ho è un cestino, che posso caricare direttamente da uVision o usando un bootloader personalizzato che si trova sul dispositivo. – dandan78

2

Il tuo pensiero è corretto e ragionevole. Ho usato Keil/uVision (questo era v3, forse 3 anni fa?) E ha sempre funzionato come te lo aspettavi, cioè ha messo i dati const in flash/ROM.

Sospetto la configurazione/script del linker. Proverò a tornare al mio vecchio lavoro & vedere come l'ho configurato. Non ho dovuto aggiungere le direttive #pragma o __attribute__, ho appena avuto posto .const & .text in flash/ROM. Ho impostato la configurazione del linker/mappa della memoria un po 'di tempo fa, quindi purtroppo il mio richiamo non è molto recente.

(io sono un po 'confuso da persone che stanno parlando di fusione & puntatori const, ecc ... Lei non ha chiesto nulla & ti sembra di capire come "Const" funziona. Si desidera inserire il dati inizializzati in flash/ROM per salvare la RAM (non ROM-> copia RAM all'avvio), per non parlare di una leggera accelerazione all'avvio, giusto? Non stai chiedendo se è possibile cambiarlo o qualsiasi altra cosa ...)

EDIT/UPDATE:

ho appena notato l'ultimo campo nella vostra (const) struct è una some_other_struct * const (costante puntatore ad una some_other_ struct). Potresti provare a impostarlo come puntatore (costante) su una costante _other_struct [some_other_struct const * const] (assumendo che ciò a cui punta sia effettivamente costante). In quel caso potrebbe funzionare. Non ricordo le specifiche (vedi un tema qui?), Ma questo sta cominciando a sembrare familiare. Anche se il tuo puntatore non è un oggetto const, e non puoi farlo, prova a cambiare la definizione di struct & inizializzandolo con un puntatore a const e vedi se questo lo fa cadere nella ROM. Anche se lo hai come puntatore const e non può cambiare una volta che la struttura è stata creata, mi sembra di ricordare qualcosa in cui, se il target non è anche const, il linker non pensa che possa essere completamente inizializzato al momento del collegamento & riporta l'inizializzazione a quando viene eseguito il codice di avvio runtime C, incl. la copia da ROM a RAM della memoria RW inizializzata.

+0

Grazie, ho già provato anche il puntatore const per l'approccio const, ma senza fortuna. Tuttavia, è stato osservato un altro comportamento strano. Controlla il mio aggiornamento alla domanda quando puoi. – dandan78

0

Un esempio completo sarebbe stato il migliore.Se prendo qualcosa di simile:

 
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; 

e compilarlo con l'ultima lite CodeSourcery

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

ottengo

 
    .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 sezione contrassegnata come .rodata, che vorrei assumere è desiderato Quindi spetta allo script linker assicurarsi che i dati ro siano inseriti in rom. E nota la variabile read_write dopo il passaggio da .rodata a .data che è in lettura/scrittura.

Quindi, per rendere questo un binario completo e vedere se viene collocato nella ROM o RAM (.text o .data) allora

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 

Poi

 
# 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 

E otteniamo

 

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 

E quello ha raggiunto il risultato desiderato. La variabile read_write è in ram, le structs sono nella rom. È necessario assicurarsi che entrambe le dichiarazioni const siano nel posto giusto, il compilatore non fornisce alcun avvertimento sull'opportunità di inserire un const su un puntatore su un'altra struttura che non può determinare al momento della compilazione come const e anche con tutto ciò che ottiene lo script del linker (se ne usi uno) per funzionare come desiderato può richiedere un certo sforzo. per esempio questo sembra funzionare:

 

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

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

+0

L'invio dell'intera sezione di codice sarebbe stato un espediente per la maggior parte delle persone, motivo per cui ho fornito una versione condensata che mostrava lo stesso problema. A parte questo, credo che il tuo esempio sia basato sulla toolchain GNU. Sto usando quello fornito da ARM. – dandan78

+0

Il mio male, scusa, non ho visto il riferimento del compilatore di bracci. Quale compilatore di bracci? Keil? RVCT? o il vecchio ADS o quello precedente non ricordo il suo nome (SBT?). Almeno nei giorni dell'ADS il supporto tecnico di ARM era molto buono (necessario per ottenere qualcosa per i soldi pagati per quegli strumenti). hanno fornito qualche intuizione? –

Problemi correlati