2015-09-16 15 views
10

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!

+1

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

+3

Suggerirei di dichiarare registri come '#define DWT_CYCCNT (* (volatile uint32_t *) 0xE0001004ul)'. – Lundin

+0

Gli eventi non eseguono il debug di eventi che potrebbero attivare un'eccezione di debug monitor? – Notlikethat

risposta

1

Non ho idea di come utilizzare i registri nel modo in cui si desidera utilizzarli. Ma ecco come mi occupo dei cicli di misurazione.

Assicurarsi di abilitare il contatore al SysTick Control and Status Register. Con le intestazioni appropriate, si dovrebbe avere accesso ai registri SysTick come una struttura.

Misurare il numero di cicli eseguiti dalla funzione contatore. Questo viene successivamente sottratto da qualsiasi misura.

SysTick->VAL = 0; // set 0 
    // Measure delay on measurement 
    __disable_irq(); 
    a = (uint32_t) SysTick->VAL; 
    //... measuring zero instructions 
    b = (uint32_t) SysTick->VAL; 
    __enable_irq(); 
    measure_delay = a - b; 

Ora misurare una funzione.

SysTick->VAL = 0; 
__disable_irq(); 
a = (uint32_t) SysTick->VAL; 

//Assuming this function doesn't require interruptions 

// INSERT CODE TO BE PROFILED 
function_to_be_examined(); 

b = (uint32_t) SysTick->VAL; 
__enable_irq(); 
cycles_profiled_code = a - b - measure_delay; 

Spero che sia d'aiuto.

+0

Si noti che questo misura con granularità molto grossolana, dal momento che il SysTick "normalmente" è impostato su overflow ad es. 1ms. Il codice nella domanda misura i conteggi esatti del ciclo. – Darhuuk

+0

Ben notato. Corretta. Approssimativamente, se la velocità del processore è 100 MHz, 1 ms consentirebbe di misurare circa 100.000 cicli. Questo metodo è preciso per le funzioni che richiedono decine di migliaia di cicli. – Toani

2

L'esempio di codice fornito ha un problema nella cancellazione del bit di abilitazione. Si dovrebbe azzerare il bit cantare 'E' non e 'O':

*DWT_CONTROL = *DWT_CONTROL & 0xFFFFFFFE ; // disable the counter by clearing the enable bit 
1

Penso che se si vuole misurare i cicli di precisione, utilizzando il debugger è una buona scelta. il Keil-MDK potrebbe accumulare il registro di stato e non traboccare. il risultato nel debugger è lo stesso del risultato utilizzando DWT.

se si desidera misurare gli altri valori, ad esempio FOLDCNT, utilizzando la traccia in Keil-MDK -> Debug -> Impostazione -> Traccia -> Abilita traccia.

Con ciò, durante il debug, in Trace Windows si sceglie l'evento di traccia, il valore di quei 8 bit di registro potrebbe essere raccolto e aggiunto insieme da Keil.

Sembra un po 'stupido ma non so come raccogliere l'evento di overflow, penso che questo evento possa essere inviato solo a ITM, perché sia ​​il DWT che l'ITM sono singoli componenti fuori dal programma. se vogliamo raccogliere l'evento nel programma del cliente, l'azione collettiva dovrà effettuare l'accuratezza del risultato.

ITM? ETM? CoreSight? DWT? AHB?