SCALETTA LEZIONE SERT E14 -- 17 gennaio 2013 1 Programmazione degli ingressi e uscite digitali 1.1 Consultazione del manuale della scheda (sez. 4.2) 1.1.1 Venti pin DIO (Digital I/O) su due morsettiere chiamate "DIO1" e "LCD" 1.1.2 La morsettiera "LCD" puo' essere utilizzata per collegare un LCD oppure come 11 DIO indipendenti, piu' "Power" (5V) e "Ground" 1.1.3 La morsettiera "DIO1" puo' essere utilizzata come interfaccia SPI oppure come 9 DIO indipendenti, piu' "Power" (3.3V) e "Ground" 1.1.4 Il paragrafo relativo alla morsettiera "DIO2" non e' congruente con l'hardware presente effettivamente sulla scheda 1.1.5 Tutte le linee DIO sono individualmente programmabili come ingresso o come uscita 1.1.6 Ciascuna linea DIO ha due bit di controllo: uno per la direzione (ingresso o uscita), ed uno per il dato (valore della linea) 1.1.7 Quasi tutte le linee DIO (17 su 20) possono essere utilizzate come linee di IRQ per la CPU 1.2 Configurazione della morsettiera "DIO1" +---+---+---+---+---+---+---+---+ | 2 | 4 | 6 | 8 |10 |12 |14 |16 | +---+---+---+---+---+---+---+---+ | 1 | 3 | 5 | 7 | 9 |11 |13 |15 | +---+---+---+---+---+---+---+---+ o <-- marker del pin1 1.3 Mappatura dei pin DIO +---+---+---+---+---+---+---+---+ |GND|---|---|D_8|---|---|---|3.3| +---+---+---+---+---+---+---+---+ |D_0|D_1|D_2|D_3|D_4|D_5|D_6|D_7| +---+---+---+---+---+---+---+---+ o <-- marker del pin1 1.4 Registri di controllo della morsettiera DIO1 1.4.1 Registro di dati per DIO0-DIO7 all'indirizzo 0x80840004 1.4.2 Registro direzione per DIO0-DIO7 all'indirizzo 0x80840014 1.4.3 Registro di dati per DIO8 all'indirizzo 0x80840030 (bit 1) 1.4.4 Registro direzione per DIO8 all'indirizzo 0x80840034 (bit 1) 1.5 Inserire i dati nel file ts7250.h: +----------------------------------------------------------+ |static const unsigned int DIO1_DR1 = 0x80840004 / 4; | |static const unsigned int DIO1_DDR1 = 0x80840014 / 4; | |static const unsigned int DIO1_DR2 = 0x80840030 / 4; | |static const unsigned int DIO1_DDR2 = 0x80840034 / 4; | |#define dio_setout(n) _iomem_high(DIO1_DDR1, (1UL<<(n))) | |#define dio_setin(n) _iomem_low(DIO1_DDR1, (1UL<<(n))) | |#define dio_high(n) _iomem_high(DIO1_DR1, (1UL<<(n))) | |#define dio_low(n) _iomem_low(DIO1_DR1, (1UL<<(n))) | |#define dio_toggle(n) _iomem_toggle(DIO1_DR1,(1UL<<(n))) | |#define dio_setout8() _iomem_high(DIO1_DDR2, 2UL) | |#define dio_setin8() _iomem_low(DIO1_DDR2, 2UL) | |#define dio_high8() _iomem_high(DIO1_DR2, 2UL) | |#define dio_low8() _iomem_low(DIO1_DR2, 2UL) | |#define dio_toggle8() _iomem_toggle(DIO1_DR2,2UL) | +----------------------------------------------------------+ 1.6 Definire tre task per far ciclare DIO0, DIO1 e DIO3 in main.c: +----------------------------------------------------------+ |void dio_cycle(void *arg) | |{ | | int dio = (int) arg; | | if (dio < 8) | | dio_toggle(dio); | | else | | dio_toggle8(); | |} | |[...] | |dio_setout(0); | |if (create_task(dio_cycle, (void *) 0, HZ/7, 2, -HZ/7, | | "dio0_cycle") == -1) { | | puts("ERROR: cannot create task dio0_cycle\n"); | | _greenpanic(); | |} | |dio_setout(1); | |dio_setout(2); | |dio_low(2); | |if (create_task(dio_cycle, (void *) 1, HZ/3, 2, -HZ/3, | | "dio1_cycle") == -1) { | | puts("ERROR: cannot create task dio1_cycle\n"); | | _greenpanic(); | |} | |dio_setout(3); | |dio_setout(5); | |dio_low(5); | |if (create_task(dio_cycle, (void *) 3, HZ/2, 2, -HZ/2, | | "dio3_cycle") == -1) { | | puts("ERROR: cannot create task dio3_cycle\n"); | | _greenpanic(); | |} | +----------------------------------------------------------+ 2 Uso di un oscilloscopio per analizzare i segnali digitali 2.1 Collegare la sonda dell'oscilloscopio ai pin 13 (CH1) e 15 (ground) della morsettiera DIO1 (DIO6,DIO7) 2.2 Visualizzare il segnale presente sui pin: 50HZ circa, forma d'onda periodica ed irregolare 2.3 Impostare in main() DIO6 e DIO7 come uscite 2.4 Visualizzare il segnale: essenzialmente 0V (+/- 100mV) 2.5 Impostare in main() DIO6 ad alto e DIO7 a basso 2.6 Visualizzare il segnale: essenzialmente +3.3V (+/- 100mV) 2.7 Creare un task per generare un'onda quadra di 4 HZ su DIO6 +--------------------------------------------------------------+ |dio_setout(6); | |dio_setout(7); | |dio_high(6); | |dio_low(7); | |if (create_task(dio_cycle, (void *) 6, HZ/8, 2, -HZ/8, | | "dio6_cycle") == -1) { | | puts("ERROR: cannot create task dio6_cycle\n"); | | _greenpanic(); | |} | +--------------------------------------------------------------+ 2.8 Visualizzare il segnale 3 Analisi del segnale del tick periodico 3.1 Modificare main() per togliere il task dio6_cycle 3.2 Modficare isr_tick(): +-----------------------------+ |static void isr_tick(void) | |{ | | iomem[TEOI] = 1UL; | | dio_toggle(6); | | ++ticks; | | check_periodic_tasks(); | |} | +-----------------------------+ 3.3 Visualizzare il segnale generato: onda quadra a 32.05 HZ (periodo di 31.20 ms) [esecuzione con comando RedBoot "go", vedi dopo] 3.3.1 La frequenza di 64 HZ corrisponde al semi-periodo 15.625 ms) 3.4 Valutazione del jitter del tick periodico 3.4.1 Impostare trigger "normale", sul fronte di salita 3.4.2 Impostare scala dei tempi, div = 250 ns 3.4.3 Spostare il trigger all'indietro fino a visualizzare il fronte di discesa 3.4.3 Impostare persistenza del display a infinito 3.4.4 Misurare tramite il cursore l'ampiezza dell'area in cui cade il fronte di discesa 3.4.5 Risultato: larghezza dell'area (jitter) di 1.540 microsec nanosec. 3.5 Possibili cause del jitter: 3.5.1 Cache dei dati (D-Cache) 3.5.2 Cache delle istruzioni (I-Cache) 3.5.3 Memory Management Unit (MMU) 3.6 Consultare il manuale dell'architettura ARM920T (sezione 2.3.5, pag. 36 (2-12)) per scoprire come si abilitano o disabilitano le cache 3.6.1 L'istruzione "mrc p15,0,Rd,c1,c0,0" legge un registro di controllo con i bit che abilitano cache e MMU 3.6.2 Scrivere la funzione read_caching_state() in ts7250.h per leggere questo registro di controllo: +---------------------------------------------------+ |static inline int read_caching_state(void) | |{ | | u32 value; | | __asm__ __volatile__("mrc p15,0,%0,c1,c0,0\n" | | : "=r" (value)); | | return value; | |} | +---------------------------------------------------+ 3.6.3 Scrivere la funzione print_caching_state() in main.c per controllare lo stato delle cache e della MMU: +----------------------------------------------------+ |static void print_caching_state(void) | |{ | | int value = read_caching_state(); | | puts("I-Cache "); | | puts(value & (1<<12) ? "enabled" : "disabled"); | | puts(", D-Cache "); | | puts(value & 2 ? "enabled" : "disabled"); | | puts(", MMU "); | | puts(value & 1 ? "enabled\n" : "disabled\n"); | |} | +----------------------------------------------------+ 3.6.4 Lanciare l'esecuzione con il comando RedBoot "go": il risultato e' "I-Cache disabled, D-Cache enabled, MMU enabled" 3.7 Ripetere la misurazione eseguendo l'ambiente SERT con il comando RedBoot "exec" invece del comando "go" 3.7.1 In questo caso lo stato delle cache e' il seguente: "I-Cache disabled, D-Cache enabled, MMU disabled" 3.7.2 La larghezza dell'area (jitter) e' di 1.480 microsec. 3.7.3 La differenza e' dovuta al fatto che il comando "go" esegue con la MMU attivata, mentre il comando "exec" la disattiva 3.7.4 In ogni caso la cache dei dati rimane attiva (tra l'altro la documentazione del processore sconsiglia di abilitare la D-Cache se la MMU e' disabilitata) 3.8 Ripetere la misurazione eseguendo l'ambiente SERT con il comando RedBoot "go -c" invece del comando "go" 3.8.1 In questo caso lo stato delle cache e' il seguente: "I-Cache enabled, D-Cache enabled, MMU enabled" 3.8.2 Risultato: larghezza dell'area (jitter) di 66 nanosec. 3.9 Implementare un meccanismo per disabilitare la D-Cache nel caso in cui la MMU e' disabilitata 3.9.1 Scrivere la funzione disable_dcache() in ts7250.h +-------------------------------------------------------+ |static inline void disable_dcache(void) | |{ | | u32 value = read_caching_state(); | | value &= ~2; | | __asm__ __volatile__("mcr p15,0,%0,c1,c0,0\n" : : | | "r" (value)); | |} | +-------------------------------------------------------+ 3.9.2 Scrivere ed invocare la funzione init_cpu_caches() in init.c: +----------------------------------------+ |static void init_cpu_caches(void) | |{ | | int value = read_caching_state(); | | if (!(value & 1) && (value & 2)) | | disable_dcache(); | |} | +----------------------------------------+ 3.10 Ripetere la misurazione con il comando RedBoot "exec" 3.10.1 In questo caso lo stato delle cache e' il seguente: "I-Cache disabled, D-Cache disabled, MMU disabled" 3.10.2 Risultato: larghezza dell'area (jitter) di 1.480 microsec 4 Analisi delle prestazioni dello scheduler a task interrompibili 4.1 Modificare isr_tick() per rimuovere dio_toggle(6) 4.2 Modificare main() per reintrodurre il task dio6_cycle con periodo di 1 tick e priorita' -1 (massima) 4.3 Analisi del jitter con cache attivate ("go -c"): jitter 750 nanosec 4.4 Analisi del jitter con cache disattivate ("exec"): jitter 24.2 microsec, jitter 12.1 microsec 4.5 Modificare main() per impostare la priorita' di dio6_cycle a -100 (minima) 4.7 Analisi del jitter con cache attivate ("go -c"): jitter 40.4 microsec 4.8 Analisi del jitter con cache disattivate ("exec"): jitter 630 microsec 5 Analisi del WCET di una funzione 5.1 Modificare main() per rimuovere il task dio6_cycle 5.2 Scrittura della funzione factor_ticks(): +--------------------------------------------------------------+ |const unsigned long small_primes[] = | | { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, | | 53, 59, 61, 67, 71, 73, 79, 83, 89, 97 }; | |void factor_ticks(void) | |{ | | unsigned long v = ticks; | | int i; | | putu(v); | | puts(": "); | | i = 0; | | while (v > 1 && i < 25) { | | if (!(v % small_primes[i])) { | | putu(small_primes[i]); | | putc(' '); | | v /= small_primes[i]; | | continue; | | } | | ++i; | | } | | if (v > 1) { | | putc('['); | | putu(v); | | putc(']'); | | } | | puts("\n"); | |} | +--------------------------------------------------------------+ 5.3 Creare un task periodico con job factor_ticks(), periodo 17 e priorita' -17 5.4 Provare la funzione 5.5 Aggiungere dio_high(6) all'inizio della funzione e dio_low(6) al termine 5.6 Misurazione del WCET con cache abilitate ("go -c"): 1.75 millisec 5.7 Misurazione del WCET con cache disabilitate ("exec"): 2.03 millisec 5.8 Ma... nel tempo e' compreso il tempo d'esecuzione dei task a priorita' maggiore 5.8.1 Si dovrebbe fare il test alzando la priorita' del task al livello massimo 5.8.2 In questo modo si comprende l'overhead dello scheduler e si esclude il tempo impiegato dagli altri task 5.8.3 In questo caso specifico non ci sono differenze significative poiche' questo task e' molto piu' lungo di quelli di frequenza maggiore