Framework and debug
Transcript
Framework and debug
64 ESECUZIONE DI APPLICAZIONI SU SISTEMI EMBEDDED MIPS-based: FRAMEWORK UTILIZZATO PER COMPILAZIONE E SIMULAZIONE 65 ESECUZIONE DI APPLICAZIONI SU SISTEMI EMBEDDED MIPS-based FRAMEWORK DI SVILUPPO (I) c_code.c mips_gcc.exe -o c_code.asm -O2 -fno-delayed-branch -S c_code.c c compiler c_code.asm boot.asm link instructions.asm c_code.asm boot.asm merge instructions.asm mips_asm instructions.asm instructions.hex instructions.hex assembler Sono necessari 2 file sorgenti (c_code.c e boot.asm), vengono creati 2 file intermedi (c_code.asm e instructions.asm) per ottenere un file di codifica delle istruzioni (instructions.hex). Vengono utilizzati un compilatore c (gcc 2.95.3), un assembler (ad hoc) ed una piccola utility che fa il merge L’utility merge si limita ad unire i file assembly mettendo le istruzioni di ciascun file in sequenza in un unica file 66 ESECUZIONE DI APPLICAZIONI SU SISTEMI EMBEDDED MIPS-based FRAMEWORK DI SVILUPPO (II) Utilizzando la seguente struttura di file e cartelle La cartella principale contiene le 2 sottocartelle HEX e VERILOG e il file del progetto con allegata la sottocartella work La cartella HEX contiene i file sorgente (mips_S1.c e mips_S1_boot.asm), il file batch che esegue tutti i task (compila.bat), il file che codifica l'inizializzazione della la memoria istruzioni e le sottocartelle GCC e ASM I file eseguibili dell'assembler sono nella sottocartella HEX\ASM I file eseguibili del compilatore sono nella sottocartella HEX\GCC La cartella VERILOG contiene il modello verilog del sistema 67 ESECUZIONE DI APPLICAZIONI SU SISTEMI EMBEDDED MIPS-based FRAMEWORK DI SVILUPPO (III): file compila.bat echi off set NOME_APPLICAZIONE=MIPS_s1 cd HEX copy %NOME_APPLICAZIONE%.c .\GCC\c_code.c copy %NOME_APPLICAZIONE%_boot.asm .\ASM\boot.asm cd GCC mips_gcc.exe -o c_code.asm -O2 -fno-delayed-branch -S c_code.c pause copy c_code.asm ..\ASM\ cd ..\ASM copy /B boot.asm + c_code.asm instructions.asm copy instructions.asm ..\ @bash -rcfile mipsprofile mipsAsm.sh %1 %2 instructions.asm instructions.hex pause copy instructions.hex ..\ cd ..\ type instructions.hex pause del .\GCC\c_code.c del .\GCC\c_code.asm .... 68 SIMULAZIONE 1: ASSEGNAMENTO DI VALORI AD UN VETTORE 69 ESECUZIONE DI APPLICAZIONI SU SISTEMI EMBEDDED MIPS-based La memoria utilizzata (unica per dati ed istruzioni) è di 0x3000=12888 locazioni. Lo spazio riservato alle istruzioni è di 0x1000=4096 locazioni locazione 0x2FFC void _main( void ) { int a[2]; int i; int *pointer; pointer = (int*) 0x1000; STACK WriteEnable Signal global clk MIPS-mc reset System for( i=0; i<2; i++) a[i] = i; *pointer = a[0]+a[1]; } Instructions / Data Buses INSTRUCTIONS / DATA MEMORY Addresses Bus MAM locazione 0x1000 ISTRUZIONI locazione 0x0000 ESECUZIONE DI APPLICAZIONI SU SISTEMI EMBEDDED MIPS-based J RESET_HANDLER RESET_HANDLER: ; routine di inizializzazione addi $29,$0,0x3000; ; inizializza lo stack pointer jal _main ; avvia l'esecuzione del main ; La simulazione termina con il blocco del PC stop: j stop _main: ;# vars= 16, regs= 0/0, args= 0, extra= 0 subu $sp,$sp,16 li $3,1 addu $2,$sp,4 $L6: sw addu addu bgez lw lw #nop addu sw addu j .end .. $3,0($2) $2,$2,-4 $3,$3,-1 $3,$L6 $2,0($sp) $3,4($sp) $2,$2,$3 $2,4096 $sp,$sp,16 $31 _main inizializzazione e salto al main alloca la memoria per il frame di attivazione del main. La dim minima è di 16 locazioni. inizializza i registri che utilizzerà come indici esegue il loop, usa $2 come puntatore alla locazione dell'elemento del vettore utilizzato ed $3 come indice per il conteggio. Calcola il valore degli elementi del vettore e li salva nello stack carica gli elementi del vettore dallo stack, calcola la somma e la salva nella MAM dealloca la memoria (frame di attivazione del main) e ripristina il PC 70 71 SIMULAZIONE 2: CHIAMATA A FUNZIONE 72 ESECUZIONE DI APPLICAZIONI SU SISTEMI EMBEDDED MIPS-based La memoria (unica per dati ed istruzioni) utilizzata è di 0x3000=12888 locazioni. Lo spazio riservato alle istruzioni è di 0x1000=4096 locazioni #define dim 4 void _main( void ) { int a[dim]; int i, *max; for( i=0; i<dim; i++) a[i] = i; max = (int*) 0x1000; *max = max_func(a,dim); } int max_func(int* vector, int size) { int max,i; max = 0; for( i=0; i<size; i++) if (vector[i] > max) max = vector[i]; return (max); } 73 SIMULAZIONE 3: MEMORIA AUTOMATICA E MEMORIA ALLOCATA MANUALMENTE 74 ESECUZIONE DI APPLICAZIONI SU SISTEMI EMBEDDED MIPS-based La memoria (unica per dati ed istruzioni) utilizzata è di 0x3000=12288 locazioni. Lo spazio riservato alle istruzioni è di 0x1000=4096 locazioni #define size 4 void _main( void ) { int *dinamic_v; int automatic_v[size]; int i; for( i=0; i<size; i++) automatic_v[i] = 10+i; dinamic_v = (int*) 0x1000; for( i=0; i<size; i++) dinamic_v[i] = automatic_v[i]; } Architetture dei Sistemi Embedded 2007/08 S.M. Carta 75 ESECUZIONE DI APPLICAZIONI SU SISTEMI EMBEDDED MIPS-based: DEBUG DELLE APPLICAZIONI ESECUZIONE DI APPLICAZIONI SU SISTEMI EMBEDDED MIPS-based76 DEBUG DI SOFTWARE EMBEDDED Il debug di software per sistemi embedded presenta delle difficoltà aggiuntive rispetto al debug di sw per piattaforma PC. E’ spesso impossibile definire le condizioni al contorno del SW in maniera puramente SW (gestione delle condizioni di interrupt, della mappa della memoria che è legata alla tipologia del sistema, ecc.). E’ indispensabile un sistema di sviluppo di tipo HW-SW che consenta al SW embedded di interagire in tempo reale con l’HW di contorno al processore. Nel caso di sistemi embedded di tipo SoB (System on Board) la fase finale dello sviluppo SW è basate su sistemi di In-Circuit Emulation (ICE). Nel caso di sistemi embedded di tipo SoC (System on Chip) si usa l’evoluzione dei sistemi ICE. Si parla di sistemi embedded ICE I sistemi ICE sono basati su un dispositivo hardware (emulatore) che implementa la funzionalità del processore (e può quindi venire inserito nel sistema) ma in aggiunta manda in output tutta una serie di informazioni sul suo funzionamento interno (valore dei registri, valori sui bus e sulle porte di I/O, ecc.). L'emulatore è collegato ad una stazione di debug (un PC o una workstation) che può memorizzare tutte le informazioni utili per il debug Il programmatore può monitorare le informazioni relative all'esecuzione, in modo da rilevare e correggere eventuali (!) malfunzionamenti ESECUZIONE DI APPLICAZIONI SU SISTEMI EMBEDDED MIPS-based77 DEBUG DI SOFTWARE EMBEDDED Nel nostro caso, abbiamo a disposizione un simulatore verilog che emula il funzionamento del processore e di tutto il sistema (quindi un emulatore completo. Utilizzeremo una tecnica che sfrutta l’inserimento di routine C dedicate che cooperano con processi verilog ad-hoc situati nel testbench. Anche i sistemi ICE funzionano con tecniche di questo tipo Utilizzeremo degli strumenti (breakpoints) inseribili a livello di codice c che consentono di stoppare la simulazione e di visualizzare : Il valore assunto da segnali, bus, porte di moduli, registri, ecc. (controllo a livello HW) Il contenuto dei registri interni del processore, delle locazioni mappate in memoria, dei segnali di interrupt, e dei segnali di I/O (controllo a livello FW) Il valore assunto dalle variabili, dalle locazioni mappate in memoria, dai segnali di interrupt, e dai segnali di I/O (controllo a livello SW) ESECUZIONE DI APPLICAZIONI SU SISTEMI EMBEDDED MIPS-based78 DEBUG DI SOFTWARE EMBEDDED: BREAKPONTS L'inserimento di un breakpoint consente di stoppare la simulazione Questa funzionalità è stata implementata tramite una funzione c che accoppiata ad un processo verilog nel testbench che stoppa la simulazione eseguendo una istruzione $stop. La funzione c (denominata breakpoint()) scrive il valore 0x1 sulla locazione 0x7FFF. La scrittura non ha alcun esito poichè non vi è alcuna locazione di memoria a quell'indirizzo (E' vero per il nostro sistema. Per altri sistemi si possono usare altre locazioni o altri tipi di tecniche) Il processo dedicato esegue la funzione stop ogni volta che si verifica questo tentativo di scrittura C LEVEL - descrizione della routine void breakpoint(void) { int *p_out; p_out = (int*) 0x7FFF ; *p_out = 0x1 ; } C LEVEL - FRAMMENTO DI CODICE ... breakpoint(); ... VERILOG LEVEL - PROCESSO DEDICATO PER IL BREAKPOINT always @(posedge clk) if ( (addr_bus == (32'h7FFF)) && (MemWrite == 1)) $stop; ESECUZIONE DI APPLICAZIONI SU SISTEMI EMBEDDED MIPS-based79 DEBUG DI SOFTWARE EMBEDDED: WATCH FIRMWARE I sistemi di sviluppo dei compilatori mettono a disposizione la possibilità di visualizzare il contenuto di registri interni, locazioni della memoria, ecc. Questa funzionalità è stata implementata tramite una funzione c che accoppiata ad un processo verilog nel testbench che esegue una serie di display e stoppa la simulazione eseguendo una istruzione $stop. La funzione c (denominata watch_fw()) scrive il valore 0x2 sulla locazione 0x7FFF. La scrittura non ha alcun esito poichè non vi è alcuna locazione di memoria a quell'indirizzo (..) Il processo dedicato esegue la funzione stop e visualizza le locazioni ed i registri di interesse (da definire di volta in volta) ogni volta che si verifica questo tentativo di scrittura C LEVEL - descrizione della routine void watch_fw(void) { int *p_out; p_out = (int*) 0x7FFF ; *p_out = 0x2 ; } C LEVEL - FRAMMENTO DI CODICE ... watch_fw(); ... VERILOG LEVEL - PROCESSO DEDICATO PER IL BREAKPOINT always @(posedge clk) if ( (addr_bus == (32'h7FFF)) && (MemWrite == 1)) begin $display(" $1 = 0x%x =%d",MIPS.REGFILE.data[1], MIPS.REGFILE.data[1]); $display(" $2 = 0x%x =%d",MIPS.REGFILE.data[2], MIPS.REGFILE.data[2]); $stop; end ESECUZIONE DI APPLICAZIONI SU SISTEMI EMBEDDED MIPS-based80 DEBUG DI SOFTWARE EMBEDDED: WATCH SOFTWARE I sistemi di sviluppo dei compilatori mettono a disposizione la possibilità di visualizzare il assunto dalle variabili nel corso dell'esecuzione. Questa funzionalità è stata implementata tramite una funzione c che accoppiata ad un processo verilog nel testbench che esegue un display e stoppa la simulazione eseguendo una istruzione $stop. La funzione c (denominata watch_sw()) scrive il valore della variabile sulla locazione 0x7FF0 codice della variabile. La scrittura non ha alcun esito poichè non vi è alcuna locazione di memoria a quell'indirizzo (..) Il processo dedicato esegue la funzione stop e visualizza il valore della variabile di interesse (da definire di volta in volta) ogni volta che si verifica questo tentativo di scrittura C LEVEL - descrizione della routine void watch_sw(int var, int var_code) { int *p_out; p_out = (int*) (0x7FF0 - var_code); *p_out = var; } C LEVEL - FRAMMENTO DI CODICE (p.e. variabile con codice 3) ... watch_sw(pippo,3); ... watch_sw(pluto,2); VERILOG LEVEL - PROCESSO DEDICATO PER LA VISUALIZZAZIONE DELLA VAR CON CODICE 3 always @(posedge clk) if ( (addr_bus == (32'h7FF0 - 3 )) && (MemWrite == 1)) begin $display(" var #3 = 0x%x =%d", proc_dout_bus, proc_dout_bus); $stop; end ESECUZIONE DI APPLICAZIONI SU SISTEMI EMBEDDED MIPS-based81 DEBUG DI SOFTWARE EMBEDDED Quelli illustrati sono solo 3 possibili esempi di tecniche utilizzabili per il debug in un sistema di emulazione come il nostro E' possibile mischiare le modalità di visualizzazione, di stop della simulazione, il salvataggio delle grandezze temporanee della simulazione di interesse su file, associare alle grandezze il tempo di simulazione o altre grandezze stesse ecc. Disponendo di un modello del processore a basso livello ed avendo la possibilità di visualizzare qualunque segnale, siamo simo infatti in grado di monitorare qualunque grandezza in qualunque condizione o insieme di condizioni ci interessi. Utilizzando sistemi ICE reali le possibilità sono più limitate. In questi casi comunque si tratta di debuggare solo l'applicazione e al limite le periferiche, mai il processore stesso. Il sistema da noi adottato ha permesso il debug anzitutto del processore 82 SIMULAZIONE 4: CALCOLO DEL MASSIMO CON INSERIMENTO DI FUNZIONE DI DEBUG PER IL MONITORAGGIO DEL MASSIMO TEMPORANEO 83 ESECUZIONE DI APPLICAZIONI SU SISTEMI EMBEDDED MIPS-based La memoria (unica per dati ed istruzioni) utilizzata è di 0x3000=12888 locazioni. Lo spazio riservato alle istruzioni è di 0x1000=4096 locazioni #define size 16 void _main( void ) { int a[size]; int i, *max; dim = 4; for( i=0; i<dim; i++) a[i] = (i-5)*(i-5); max = (int*) 0x1000; *max = max_func(a,dim); } void watch_sw(int var, int var_code) { int *p_out; p_out = (int*) (0x7FF0 - var_code); *p_out = var; } int max_func(int* vector, int dim) { int max,i; max = 0; for( i=0; i<dim; i++) { if (vector[i] > max) { max = vector[i]; watch_sw(max, 1)} } return (max); }