Inizializzare i micro TMS570LS

In questo articolo della rubrica Firmware Reload trattiamo le linee guida per una corretta procedura di start-up dei micro TMS570LS di TI.

TMS570LS è la famiglia di microcontrollori per applicazioni safety-critical di Texas Instruments. Progettata per rispettare i requisiti ISO 26262 ASIL-D e IEC 61508 SIL-3, si basa su un’architettura (Figura 1) dual core con due CPU ARM Cortex-R4F operanti in lockstep, fino a 3 Mbyte di memoria flash embedded con controllo ECC, due ADC ridondati a 8 canali e 12 bit di risoluzione, porte di connessione Ethernet, FlexRay, CAN, LIN, SPI, controller DMA e timer con funzionalità PWM e Input Capture.

Figura 1: l’architettura dei micro TMS570LS di Texas Instrument.

Figura 1: l’architettura dei micro TMS570LS di Texas Instruments

I dispositivi TMS570LS risultano ideali per numerose applicazioni in ambito automotive (come i sistemi ABS, ESC ed ESP, gli inverter HEV/EV per i veicoli ibridi, i dispositivi di assistenza alla guida) ma trovano impiego anche in settori diversi, come quello della gestione della potenza, dei sistemi avionici e aerospaziali, dei veicoli fuori strada. Considerata la complessità hardware del dispositivo e la rilevanza dal punto di vista della sicurezza delle funzionalità implementate, è facile comprendere come rivesta importanza enorme nel caso dei TMS570LS implementare una corretta procedura d’inizializzazione delle risorse e delle periferiche presenti a bordo prima di avviare l’applicazione utente. Un interessante Application Report di TI (per i dettagli si veda al punto [1] dei riferimenti) descrive la procedura suggerita e fornisce l’infrastruttura per la stesura del relativo codice sorgente per la propria applicazione. Di seguito rivediamo brevemente i passi principali da seguire per l’inizializzazione del microcontrollore; l’attenzione è rivolta principalmente ai dettagli implementativi, in quanto tale aspetto e la descrizione del codice non sono trattati approfonditamente nell’Application Report citato, che si concentra piuttosto sui diversi passi della procedura a livello generico. La procedura è implementata dalla funzione _c_int00() inclusa nel file sys_startup_recommended.c. L’esempio è configurato in modo che la CPU esegua un salto a tale funzione dopo ogni reset. Un estratto della funzione _c_int00() è riportato nel Listato 1.

void _c_int00()
{
_coreEnableVfp_(); /* Enable VFP Unit */
_coreInitRegisters_(); /* Initialize Core Registers*/
flashWREG->FEDACCTRL1 = 0x000A060A; /* Enable response to ECC errors indicated
by CPU for accesses to flash */
/* Enable CPU Event Export */
…
/* Enable CPU ECC checking for ATCM (flash accesses) */
…
/* Reset handler */
if (systemREG1->SYSESR & 0x8000) /* check for power-on reset condition */
{…}
…
else if (systemREG1->SYSESR & 0x20) /* Reset caused due to CPU reset. */
{
if ((stcREG->STCSCSCR & 0xF) == 0xA) /* check if this was an stc-
SelfCheck run */
{
if ((stcREG->STCGSTAT & 0x3) != 0x3) /* check if the self-test fail bit
is set */
{ stcSelfCheckFail(); // STC self-check has failed }
else /* STC self-check has passed */
{ …
cpuSelfTest(); /* Start CPU Self-Test */
}
}
else if ((stcREG->STCGSTAT & 0x1) == 0x1) // CPU reset caused by
CPU self-test completion
{
if ((stcREG->STCGSTAT & 0x2) == 0x2) // Self-Test Fail flag is set
…
else // CPU self-test completed successfully
{ afterSTC(); // Continue start-up sequence after CPU STC completed }
}
else // CPU reset caused by software writing to CPU RESET bit
{…}
/* Check if there were ESM group3 errors during power-up.
if (esmREG->ESTATUS1[2])
{ while(1); }
else
{
setupPLL();/* Configure PLL control registers and enable PLLs. */
efcCheck();/* Run eFuse controller start-up checks and start eFuse controller
ECC self-test. /
periphInit();/* Enable clocks to peripherals and release peripheral reset
*/
muxInit();/* Configure device-level multiplexing and I/O multiplexing */
…
stcSelfCheck();
}
}
Listato 1

La prima istruzione _coreEnableVfp_() abilita il coprocessore FPU, disabilitato per default; si tenga presente che se il coprocessore è disabilitato e l’applicazione utente esegue un’istruzione floating-point, il sistema genera un’eccezione indefinita. La seconda istruzione _coreInitRegisters_() inizializza, invece, i registri delle CPU. Ciò è reso necessario dal fatto che lo stato dei registri è indefinito al power-on ma i due core operano in lock-step, con un modulo CCM-R4 che ne compara le uscite. In corrispondenza di una chiamata a funzione, le CPU copiano i registri interni nello stack; quindi, alla prima chiamata a funzione, essendo i registri non necessariamente inizializzati per default allo stesso valore, come si diceva, nella copia di questi sullo stack il modulo CCM-R4 riconoscerebbe un errore di sistema. Le funzioni appena citate sono scritte direttamente in linguaggio asm e disponibile nel file sorgente sys_core.asm. Vi è poi una serie di passi (non riportati nel Listato 1) che consentono invece di abilitare, rispettivamente, la risposta agli errori ECC nel modulo di controllo della memoria Flash e il meccanismo di segnalazione degli eventi da parte della CPU (tale meccanismo è usato ad esempio dai moduli Flash e RAM per catturare gli eventi ECC). Quindi troviamo l’handler della condizione di reset, implementato come una serie di istruzione di tipo if…else.

Viene controllato, in particolare, il registro di stato delle eccezioni per identificare la causa di reset e definire per ognuna le azioni appropriate. Al power-on, ad esempio, viene eseguito il primo ramo dell’istruzione if; l’unica azione eseguita dal codice di riferimento è il clear del registro delle eccezioni ma è possibile introdurre istruzioni specifiche della propria applicazione. Dopo di ciò, il sistema esce dall’handler e riprende la normale procedura di inizializzazione. La successiva istruzione eseguita (riga 34) è la verifica di eventuali errori di tipo ESM group3 che potrebbero verificarsi, ad esempio, durante la lettura dalla flash OTP. In questo caso il sistema è in uno stato “non safe” e quindi non è sicuro eseguire la propria applicazione; la procedura prevede che la CPU stalli in un ciclo infinito. Dal punto di vista hardware, l’errore è segnalato dall’asserzione della linea nError che può quindi essere usata per implementare le opportune azioni di recovery. In assenza di errori, invece, sono eseguiti gli altri passi della procedura di inizializzazione, tra i quali i principali sono:

  • configurazione del PLL e sua abilitazione;
  • verifica dello stato del controller eFuse; vi è una protezione di tipo SECDEC (Single-Bit Error Correction, Double-Bit Error Detection) dei valori letti in autoload;
  • abilitazione del clock alle periferiche;
  • configurazione del multiplexing a livello dispositivo e sulle porte di I/O;
  • configurazione del controller della Flash, impostando i wait-state in funzione della frequenza di clock corrente della CPU.

Infine, viene chiamata la funzione stcSelfCheck() che implementa un controllo diagnostico del controller del self-test della CPU. Al termine di tale funzione, viene asserito il reset della CPU e la funzione _c_int00() è eseguita quindi nuovamente. Diversamente dal caso precedente, tuttavia, l’handler del reset che vi è incluso, come abbiamo visto, riconoscerà una nuova circostanza (il fatto ovvero che il reset è dovuto al check del controller del self-test) ed eseguirà quindi il ramo corrispondente dell’istruzione if. In questo è inclusa (riga 22) la chiamata alla funzione cpu- SelfTest(void), il cui codice è riportato per semplicità nel Listato 2; la funzione avvia il PBIST del sistema forzando quindi la CPU in idle. Come in precedenza, al termine del self-test viene generata una nuova condizione di reset e rieseguita la funzione _c_int00().

void cpuSelfTest(void)
{
volatile int i = 0;
stcREG->STCGCR0 = 0x00180001; // Run all 24 test intervals starting
from interval 0
for (i=0; i<16; i++); // wait for 16 VBUS clock cycles at least
stcREG->STCGCR1 = 0xA; // Enable self-test
asm(“WFI”); // Idle the CPU so that the self-test can start
asm(“NOP”);
asm(“NOP”);
}
}
}
Listato 2

In questo caso, il ramo dell’handler di reset (riga 25) eseguito dalla funzione verifica dapprima che il self-test sia andato a buon fine e quindi chiama la funzione afterSTC(). Il codice di questa è anch’esso incluso nel file sys_startup_recommended.c; per convenienza ne è riportato un estratto nel Listato 3.

void afterSTC(void)
{
ccmSelfCheck(); /* Make sure that CCM-R4F is working as expected. */
pbistSelfCheck(); /* Run a diagnostic check on the memory self-test
controller */
pbistRun(0x20, SP_RAMs); /* Run PBIST on CPU RAM. */
...
_memoryInit_(0x1); /* Initialize CPU RAM. */
...
checkFlashECC(); /* Test the CPU ECC mechanism for Flash accesses.*/
flashWREG->FDIAGCTRL = 0x000A0007; // disable flash diagnostic mode
...
if (pbistREG->FSRF0 || pbistREG->FSRF1) // Check if all memories
passed the self-test
{...} else
{ pbistStop(); } /* Disable memory self-test mode */
...
_coreEnableIrqVicOffset_(); /* Enable IRQ offset via the CPU’s VIC
port */
/* Initialize VIM table */
...
/* set IRQ/FIQ priorities for each interrupt request*/
...
/* enable desired interrupts */
...
main();
exit();
}
Listato 3

Alcuni dei passi eseguiti da tale funzione sono:

  • verificare che il modulo CCM-R4F funzioni come atteso, forzando questo in modalità di self-test per controllare che sia in grado di rilevare un disaccordo tra le due CPU (operanti in lock-step in condizioni normali);
  • verificare il corretto funzionamento del modulo di self-test della memoria eseguendo questo sulla ROM on-chip (ovviamente ci si aspetta che restituisca un errore);
  • eseguire il PBIST sulla RAM;
  • auto-inizializzare tutte le memorie on chip;
  • verificare i relativi meccanismi di rilevamento di errore mediante parità;
  • abilitare l’interrupt controller e programmare gli indirizzi per le routine di servizio delle interruzioni, configurando infine le priorità dei diversi canali.

Altre istruzioni non correntemente incluse nell’esempio di riferimento ma che potrebbero facilmente essere introdotte ove l’applicazione specifica faccia uso delle risorse interessate sono le seguenti:

  • configurare e verificare il modulo DCC (Dual Clock Comparator);
  • eseguire il check in background della Flash usando il canale DMA;
  • eseguire il self-test dell’ADC;
  • configurare i permessi di accesso per le periferiche;
  • impostare il modulo RTI per la generazione di interruzioni periodiche, come richiesto dall'applicazione corrente.

Al termine dell’intera procedura la routine afterSTC() chiama infine la funzione main() che conterrà il codice specifico dell’utente. La funzione è descritta nel file sys_main.c; l’esempio di riferimento ne contiene una implementazione banale che accede semplicemente alle porte di I/O del microcontrollore. L’utente può tranquillamente modificarla per eseguire la propria applicazione.

Scarica subito una copia gratis

Scrivi un commento

Seguici anche sul tuo Social Network preferito!

Send this to a friend