Domanda:
applicazione bootloader sul controller del braccio
Nama
2016-09-14 17:01:11 UTC
view on stackexchange narkive permalink

Sto usando un microcontrollore con processore ARM Cortex M4 e sto sviluppando un'applicazione bootloader su di esso.

  1. Compilatore: compilatore GCC- GNU ARM
  2. Dimensione flash del codice : 1 MB
  3. Interfaccia per il download del codice dell'applicazione: USB CDC Classe ACM
  4. Un software applicativo sul PC per trasferire il file binario del codice dell'applicazione su MCU o USB
  5. I hanno due codici: a. codice principale (codice di avvio), b. Codice dell'applicazione
  6. Nel codice dell'applicazione, viene scritta la funzione run_my_app ().
  7. Il flusso del codice principale è il seguente:

      reset- > inizializza MCU inizializza stato avvio USB = CLEAR; while (1) {aspetta il comando da USB: scrivi / cancella if (stato di avvio == BOOT_COMPLETE) {chiama un nome di funzione run_my_app () dal codice dell'applicazione; }} se si riceve il comando di cancellazione: cancella il codice dell'applicazione in una posizione flash 0x0020000 se si riceve il comando di scrittura: scrive il codice dell'applicazione nella posizione flash 0x0020000 dopo il completamento della scrittura riuscita, stato di avvio == BOOT_COMPLETE;  

La mia domanda è come dovrei mappare la funzione run_my_app () dal codice dell'applicazione al codice di avvio? Come posso ottenere l'indirizzo della funzione run_my_code () in modo che usando il puntatore alla funzione posso chiamare quella funzione?

Ciao a tutti, devo modificare il punto di ingresso del codice dell'applicazione nella posizione 0x0020000 in flash dalla posizione 0x0000000 utilizzando lo script del linker. Il file di script del linker attualmente generato è:

  MEMORY {FLASH (rx): ORIGIN = 0x00000000, LENGTH = 0x0100000 / * 1M * / RAM (rwx): ORIGIN = 0x20000000, LENGTH = 0x0030000 / * 192K * / DATA_FLASH (rx): ORIGIN = 0x40100000, LENGTH = 0x0004000 / * 16K * / QSPI_FLASH (rx): ORIGIN = 0x60000000, LENGTH = 0x4000000 / * 64M, Modifica nella sezione QSPI di seguito anche * /} ENTRY (Reset_Handler) SEZIONI {.text: {__ROM_Start =.;
/ * Anche se la tabella vettoriale non è lunga 256 voci (1KB), allochiamo ancora quello spazio * perché i registri ROM sono all'indirizzo 0x400 e c'è pochissimo spazio * nel mezzo. * / KEEP (* (. Vectors)) __Vectors_End =.; __Vectors_Size = __Vectors_End - __Vectors; __end__ =.; / * I registri ROM iniziano all'indirizzo 0x00000400 * /. = __ROM_Start + 0x400; KEEP (* (. Rom_registers *)) / * Riservare 0x100 byte di spazio per i registri ROM. * /. = __ROM_Start + 0x500; * (. text *) KEEP (* (. init)) KEEP (* (. fini)) / * .ctors * / * crtbegin.o (.ctors) * crtbegin? .o (.ctors) * (EXCLUDE_FILE (* crtend? .o * crtend.o) .ctors) * (SORT (.ctors. *)) * (. ctors) / * .dtors * / * crtbegin.o (.dtors) * crtbegin? .o (.dtors) * (EXCLUDE_FILE (* crtend? .O * crtend.o) .dtors) * (SORT (.dtors. *)) * (. Dtors) * (. Rodata *) KEEP (* (. Eh_frame *)) __ROM_End =. ; } > FLASH = 0xFF ............. ..........  

Aggiunta di alcuni dettagli alla mia implementazione:

Una struttura "CodeInfoStruct" è definita sia nel codice di avvio che in quello dell'applicazione.

  struct CodeInfoStruct {uint32_t RunCodeFunctionAddress;};  

Nel codice dell'applicazione:

Qui, un'istanza della struttura viene creata in una posizione fissa "0x0020100" e l'indirizzo della funzione "run_my_app" è memorizzato lì.

  const struct CodeInfoStruct CodeInformation __attribute __ ((section (". ARM .__ at_0x0020100"))); const struct CodeInfoStruct CodeInformation = {(uint32_t) run_my_app};  

Inoltre, nel file linker, la ROM l'indirizzo iniziale viene modificato in 0x0020000:

  MEMORY {FLASH (rx): ORIGIN = 0x00020000, LENGTH = 0x0100000 / * 1M * / RAM (rwx): ORIGIN = 0x20000000, LENGTH = 0x0030000 / * 192K * / DATA_FLASH (rx): ORIGIN = 0x40100000, LENGTH = 0x0004000 / * 16K * / QSPI_FLASH (rx): ORIGIN = 0x60000000, LUNGHEZZA = 0x4000000 / * 64 M, Modifica anche nella sezione QSPI sotto * /}  

Nel codice di avvio:

il puntatore alla struttura di "COdeInfoStruct" è definito.

  struct CodeInfoStruct * pCodeInfo = 0x0020100;  

E poi, la funzione 'run_my_code' viene chiamata dal codice di avvio come segue:

  void (* trgt_fnc) (void); trgt_fnc = (void (*) ()) (pCodeInfo->RunCodeFunctionAddress); trgt_fnc ();

Ma ottengo il valore zero in questo "trgt_fnc".È un modo corretto per condividere l'indirizzo della funzione run_my codice?Cosa c'è che non va qui?

Dato che attualmente hai una tabella vettoriale all'inizio del firmware del payload, puoi semplicemente leggere l'indirizzo di ingresso dalla seconda parola di quello.Rifletti sul se / come l'hardware verrà puntato a questo per rendere attivi quei vettori e se vuoi che il tuo bootloader inizializzi il puntatore dello stack al valore fornito con la tabella vettoriale come avrebbe fatto l'hardware se fosse avviato, ose si desidera che il firmware del payload erediti lo stack del bootloader che potrebbe non corrispondere alle ipotesi a cui era collegato.
@ChrisStratton: Grazie per il tuo commento.Non riesco a capire l'affermazione che hai fatto: "puoi semplicemente leggere l'indirizzo di entrata dalla seconda parola".Perché seconda parola?
Ciao, sono ancora bloccato solo qui.Dal codice di avvio, come posso chiamare una funzione scritta nel codice dell'applicazione?Qualcuno può fornire uno snippet di codice o un semplice esempio?Eventuali suggerimenti o commenti sono i benvenuti.:)
Dovresti avere qualcosa come una struttura dati a un indirizzo previsto che puoi leggere per trovare l'indirizzo della funzione.Dovresti avere il minor numero possibile di chiamate distinte, idealmente solo una senza risposta.Come accennato in precedenza potresti essere in grado di utilizzare la tabella vettoriale.SO non incoraggia le richieste di esempi di codice, in definitiva questo è un motivo vicino.
La tabella vettoriale ARM si trova all'inizio dell'immagine flash binaria per l'applicazione.È necessario passare dalla tabella vettoriale al nuovo indirizzo flash dell'applicazione (VTOR).È possibile caricare lo Stack Pointer dal primo indirizzo e il PC dal secondo indirizzo.Non era una cosa semplice da far funzionare.Come hai già scoperto, devi cambiare l'indirizzo di origine per la tua applicazione, ma potresti anche dover cambiare l'indirizzo di origine in alcuni file di intestazione di avvio nella tua applicazione.Ho scoperto spiacevolmente che la mia tabella vettoriale veniva sovrascritta dal codice di avvio GCC / ARM!
@Daniel: Grazie per i tuoi suggerimenti.In realtà non devo eseguire l'intero codice dell'applicazione.Dal codice di avvio, voglio solo chiamare una funzione scritta nel codice dell'applicazione.Quindi come si fa?
Quando lo scoprirai fammi sapere!
Immagino che non ci siano problemi a saltare in bit casuali di codice, purché non faccia riferimento a valori globali o statici?non so.Perché solo chiamare il tuo indirizzo come puntatore a funzione non funziona?
@Daniel: La funzione che voglio chiamare dal codice dell'applicazione non contiene alcuna variabile.È solo un'inizializzazione periferica del timer, nient'altro.La mia domanda è: come posso ottenere la posizione / l'indirizzo esatto di questa funzione?così posso chiamarlo usando il puntatore a funzione.Come posso condividere questo indirizzo tra il codice di avvio e quello di esecuzione?
Come è stato spiegato molte volte, dovrai inserire le informazioni sulla funzione (o la funzione stessa) in un luogo noto.In generale, * non dovresti * chiamare solo una parte casuale di codice, dovresti invocare l'intera cosa.Ma se vuoi, metti l'indirizzo in una posizione nota, proprio come fa la tabella vettoriale, probabilmente usando il modo in cui il tuo progetto riempie la tabella vettoriale come esempio.Oppure inseriscilo in un vettore inutilizzato * nella * tabella vettoriale.Indipendentemente dal fatto che lo usi letteralmente o lo duplichi solo, * studia la tabella vettoriale * poiché ti mostrerà come raggiungere il tuo obiettivo.
Assicurati di avere davvero una buona ragione per farlo.A volte è necessario il codice dell'applicazione per tornare al bootloader per fare qualcosa di basso livello (come scrivere un flag "sporco"), ma la chiamata in avanti nel codice dell'applicazione per attività banali sembra chiedere problemi.
Tre risposte:
Arsenal
2016-09-14 19:45:19 UTC
view on stackexchange narkive permalink

Il modo in cui lo facciamo in uno dei nostri prodotti è così (il codice è basato sul compilatore IAR, quindi i pragmi potrebbero differire per te):

Abbiamo definito una struttura che contiene informazioni importanti dell'applicazione part (che si chiama mainpart nei nostri progetti):

  #pragma pack (push, 1) struct softwareRevision_t {uint8_t VersionSystem; uint8_t VersionFunction; uint8_t VersionError; uint8_t VersionCustomer; uint8_t BuildNumber; }; # pragma pack (pop) #pragma pack (push, 1) struct mainInfoStruct_t {softwareRevision_t softwareRevision; uint32_t startFunctionAddress; riserva uint8_t [27]; uint32_t bootControl; }; # pragma pack (pop)  

Questo contiene tra le altre cose uint32_t startFunctionAddress . Ora, affinché questa struttura sia utile, devi fare diverse cose:

  1. Inizializzala con valori significativi nell'applicazione
  2. Rendila const in modo che possa essere posizionato in flash
  3. Dì al linker di posizionare la struttura sempre nella stessa identica posizione ogni volta che compili il tuo progetto
  4. condividi la posizione e la struttura tra l'applicazione e la parte di avvio

Nella parte dell'applicazione:

Per condividere la posizione, abbiamo creato un file di intestazione che è incluso in entrambe le parti con una semplice definizione:

  #define MAIN_INFO_MEMORY_POSITION (0x0807FFD4)  

In un file .cpp della nostra applicazione creiamo un istanza della struttura che viene posizionata di conseguenza:

  #pragma location = MAIN_INFO_MEMORY_POSITIONextern __root const mainInfoStruct_t mainInfoStruct = {{1U, // mainInfo.softwareRevision.VersionSystem 0U, // mainInfo.softwareRevision.VersionFunction 0U, // mainInfo.softwareRevision.VersionError 0U, // mainInfo.softwareRevision.VersionCustomer 0U // mainInfo.softwareRevision.BuildNumber},
reinterpret_cast<uint32_t> ((&_start)), // mainInfo.startFunctionAddress {0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U , 0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U}, // reserve 0xF0F0FFFFU // mainInfo.bootControl};  

Qui accadono diverse cose: #pragma location = MAIN_INFO_MEMORY_POSITION dice al linker dove posizionare la struttura successiva.

Il __root dice al nostro linker che questo simbolo deve essere mantenuto, anche se non è referenziato dal codice dell'applicazione (cosa che non è). Deve essere const altrimenti non verrebbe posizionato in flash (se non finisce in flash prova ad aggiungere un statico).

Quindi con questo abbiamo fatto tutto nell'applicazione.


Nella parte di avvio:

La parte di avvio ha bisogno di accedere alle informazioni sulla parte dell'applicazione struttura. Abbiamo racchiuso una classe attorno a tutta questa cosa, ma fondamentalmente un puntatore sarà sufficiente:

  volatile mainInfoStruct_t * pMainInfo = reinterpret_cast<mainInfoStruct_t * > (MAIN_INFO_MEMORY_POSITION); > 
Usiamo volatile qui per assicurarci che il compilatore non ottimizzi l'accesso al flash away. Con questo ora puoi saltare nel codice dell'applicazione con un brutto cast:
  (reinterpret_cast<void (*) (void) > (pMainInfo->startFunctionAddress)) ();  

Note:

Nella nostra applicazione eseguiamo un controllo CRC su tutta la parte dell'applicazione che include mainInfoStruct, quindi non saltiamo nel nirvana è qualcosa è andato storto con l'aggiornamento.

La prima cosa che facciamo nella nostra applicazioneèimpostare lo stackpointer al valore con cui dovrebbe iniziare (puoi ottenerlo dal file di configurazione del linker o dal tuo vettore di interrupt tavolo). Altrimenti potresti finire con un overflow dello stack e danneggiare gli altri tuoi dati.

Questo non è un esempio minimale, ma potrebbe anche dare qualche suggerimento su ciò che potrebbe essere utile in una struttura condivisa.

Usiamo C ++, ci scusiamo per il reinterpret_cast s.

Ciao Arsenal, grazie per i tuoi preziosi suggerimenti.Va bene che il compilatore e il linguaggio di programmazione che hai usato siano diversi.Volevo solo la logica.Lo userò per la mia applicazione.:)
Come modificare l'indirizzo del punto iniziale del codice dell'applicazione in una posizione specifica?
@Nama con l'approccio sopra, non è necessario correggere l'indirizzo di partenza in una posizione specifica.Quando si inizializza la struttura, si inserisce l'indirizzo della funzione, il linker risolverà quell'indirizzo e ogni volta che si compila il progetto l'indirizzo verrà correttamente memorizzato nella struttura.Altrimenti, di solito puoi semplicemente usare un `#pragma location = xyz` davanti alla funzione di avvio, quindi verrà posizionato lì.
L'assegnazione di posizioni fisse sarà diversa con la toolchain GCC del richiedente, in genere eseguita tramite sezioni nella mappa del linker.
@ChrisStratton Non ho mai usato GCC, quindi non posso dire come farlo lì, immagino che ci sarebbe un meccanismo simile per posizionarlo all'inizio di una sezione o di un indirizzo definito.
Nama
2016-09-28 14:32:17 UTC
view on stackexchange narkive permalink

Grazie per i tuoi sforzi ...

Come ho affermato nella mia domanda, ho creato un'istanza di una struttura in una posizione fissa 0x0020100. utilizzando il seguente metodo.

  const struct CodeInfoStruct CodeInformation __attribute __ ((section (". ARM .__ at_0x0020100")));
 

Ma ho osservato che la struttura non è memorizzata in questa particolare posizione. Quindi, l'accesso all'indirizzo richiesto del puntatore a funzione che è stato memorizzato in questa struttura in questa posizione era sbagliato. La logica di accesso al codice dell'applicazione dal codice di avvio funziona perfettamente.

Il modo corretto per scrivere la struttura (qualsiasi dato / variabile) in una posizione specifica è il seguente:

  struct CodeInfoStruct __attribute __ ((section (".ArrSectioninflash"))) CodeInformation =
{
    &my_run_code
};
 

E aggiungi la sezione "ArrSectioninflash" nel file del linker come segue:

  .myBufBlock 0x00021000:
{
            KEEP (* (. ArrSectioninflash))
} > FLASH
 
pjc50
2016-09-14 19:36:37 UTC
view on stackexchange narkive permalink

Due possibili approcci:

  • Rendi il file scaricato un binario ELF e analizza le sue intestazioni per determinare il "punto di ingresso" o un simbolo specifico.

  • Basta far saltare il bootloader a un punto fisso specifico nel file scaricato, di solito l'inizio.Scrivi uno script del linker per mettere il punto di ingresso nel posto giusto.(Di solito questo finisce come un file "bin").

Ciao pjc50, grazie per la tua risposta.Non capisco il primo punto che hai menzionato.Puoi elaborarlo?
Come modificare l'indirizzo del punto iniziale del codice dell'applicazione in una posizione specifica?


Questa domanda e risposta è stata tradotta automaticamente dalla lingua inglese. Il contenuto originale è disponibile su stackexchange, che ringraziamo per la licenza cc by-sa 3.0 con cui è distribuito.
Loading...