Vorrei contare il numero di istruzioni per ciclo eseguite su un ARM Cortex-M4 (o cortex-M3) processore.ARM M4 istruzioni per ciclo (IPC) contatori
Quello che è necessario è: numero di istruzioni eseguite (in fase di esecuzione) del codice che ho voglia di profilo e numero di cicli che il codice necessario per l'esecuzione.
1 - Numero di cicli
Utilizzare il contatore ciclo è abbastanza facile e semplice.
volatile unsigned int *DWT_CYCCNT ;
volatile unsigned int *DWT_CONTROL ;
volatile unsigned int *SCB_DEMCR ;
void reset_timer(){
DWT_CYCCNT = (int *)0xE0001004; //address of the register
DWT_CONTROL = (int *)0xE0001000; //address of the register
SCB_DEMCR = (int *)0xE000EDFC; //address of the register
*SCB_DEMCR = *SCB_DEMCR | 0x01000000;
*DWT_CYCCNT = 0; // reset the counter
*DWT_CONTROL = 0;
}
void start_timer(){
*DWT_CONTROL = *DWT_CONTROL | 1 ; // enable the counter
}
void stop_timer(){
*DWT_CONTROL = *DWT_CONTROL | 0 ; // disable the counter
}
unsigned int getCycles(){
return *DWT_CYCCNT;
}
main(){
....
reset_timer(); //reset timer
start_timer(); //start timer
//Code to profile
...
myFunction();
...
stop_timer(); //stop timer
numCycles = getCycles(); //read number of cycles
...
}
2 - numero di istruzioni
ho trovato navigando in internet per contare il numero di istruzioni eseguite dal ARM Cortex-M3 e Cortex-M4 (link) po 'di documentazione:
# instructions = CYCCNT - CPICNT - EXCCNT - SLEEPCNT - LSUCNT + FOLDCNT
I registri menzionati sono documentati here (da pagina 11-13) e questi sono gli indirizzi di memoria per accedervi:
DWT_CYCCNT = 0xE0001004
DWT_CONTROL = 0xE0001000
SCB_DEMCR = 0xE000EDFC
DWT_CPICNT = 0xE0001008
DWT_EXCCNT = 0xE000100C
DWT_SLEEPCNT = 0xE0001010
DWT_LSUCNT = 0xE0001014
DWT_FOLDCNT = 0xE0001018
Il registro DWT_CONTROL viene utilizzato per abilitare i contatori, in particolare il contatore di cicli come documentato here.
Ma quando ho cercato di mettere tutti insieme per contare il numero di istruzioni eseguite per ciclo non ci sono riuscito.
Here c'è una piccola guida su come usarli da gdb.
Ciò che non è facile è che alcuni registri sono registri a 8 bit (DWT_CPICNT, DWT_EXCCNT, DWT_SLEEPCNT, DWT_LSUCNT, DWT_FOLDCNT) e quando si eccedono attivano un evento. Non ho trovato un modo per raccogliere quell'evento. Non ci sono snippet di codice che spiega come farlo o routine di interrupt adatte a questo.
Sembra inoltre che utilizzando watchpoints da gdb sugli indirizzi di tali registri non funziona. gdb non è in grado di fermarsi quando i registri cambiano valore. Per esempio. on DWT_LSUCNT:
(gdb) watch *0xE0001014
Aggiornamento: Ho trovato questo project su GitHub spiega come utilizzare le unità DWT, ITM e ETM. Ma non ho controllato se funziona! Pubblicherò aggiornamenti.
Qualche idea su come usarli?
Grazie!
Forse troppo ovvio, ma si chiama sempre reset_timer() prima che qualsiasi altra funzione sia eseguita, corretta? Potresti postare il codice chiamante, come esempio minimo? – Lundin
Suggerirei di dichiarare registri come '#define DWT_CYCCNT (* (volatile uint32_t *) 0xE0001004ul)'. – Lundin
Gli eventi non eseguono il debug di eventi che potrebbero attivare un'eccezione di debug monitor? – Notlikethat