Gestione della memoria
Transcript
Gestione della memoria
44 FRAMEWORK PER L'ESECUZIONE DI APPLICAZIONI ASSEMBLY SUL MODELLO VERILOG DI UN SISTEMA DI ELABORAZIONE BASATO SUL PROCESSORE eMIPS-sc 45 MODELLO VERILOG DEL PROCESSORE MODELLO VERILOG DEL SISTEMA COMPLETO La gerarchia del modello verilog del sistema è la seguente: testbench.v (istanza testbench) instructions_ROM.v (istanza InstrROM) data_RAM.v (istanza DataRAM) eMIPS_sc.v (istanza MIPS) pc_reg.v (istanza PC) pc_adder.v (istanza NPC) .... 46 FRAMEWORK DI SVILUPPO (I) MIPS ASSEMBLER file instructions.hex MEMORIA ISTRUZIONI (MODELLO VERILOG) DATI INIZIALI CON CONVERSIONE IN UN FORMATO LEGGIBILE DAL SIMULATORE VERILOG (READMEMH) INSTRUCTIONS BUS INSTR ADDRESSES DATA MIPS (MODELLO VERILOG) BUS DATA ADDRESSES BUS BUS CONVERSIONE IN UN FORMATO LEGGIBILE DAL SIMULATORE VERILOG (READMEMH) CODICE ASSEMBLY MEMORIA DATI (MODELLO VERILOG) file data_RAM.hex SIMULATORE VERILOG (MODELSIM) FRAMEWORK DI SVILUPPO (II) STRUTTURA DI FILE E DIRECTORY eMIPS-sc_S3 S1.mpf compila.bat eMIPS_S3.asm HEX instructions.hex data_ram.hex ASM file eseguibili dell'assembler..... VERILOG testbench_eMIPS_S3.v Memories eMIPS-sc 47 48 ESECUZIONE DI APPLICAZIONI SU SISTEMI EMBEDDED MIPS-based SPECIFICA MISTA C-ASSEMBLY, UTILIZZO DELLA MEMORIA A BASSO LIVELLO, CONVENZIONI DI CHIAMATA A SUBROUTINE INTERFACCIA HW/SW L’utilizzo di programmi scritti in linguaggi ad alto livello (c/c++) sul processore di un sistema embedded richiede la specifica di una serie di informazioni supplementari (dimensione della memoria, partizionamento e mappa della memoria, routine di boot, routine di interrupt, ecc.) che non possono essere definite a livello di software c stesso, ma vanno definite a livello assembly-sistema operativo. Si rende necessaria l’integrazione di specifiche scritte a livelli diversi: c e assembly Si rende inoltre necessario la conoscenza della strategia usata dal compilatore c utilizzato (nel nostro caso il gcc 2.95.3) per la gestione a basso livello della memoria 49 INTERFACCIA HW/SW Per fissare i primi concetti legati al funzionamento del compilatore e all'utilizzo dei tool di compilazione semplificati (compilatore + assembler) sviluppati per questo corso, utilizzeremo un semplice sistema processorememoria senza periferiche La microarchitettura MIPS che utilizzeremo è la MIPS-mc. L'architettura di memoria di Von Neumann: vi è una sola memoria condivisa da dati ed istruzioni WriteEnable Signal global clk MIPS-mc Instructions / Data Buses reset System Addresses Bus INSTRUCTIONS / DATA MEMORY 50 51 GESTIONE DELLA MEMORIA PER I SISTEMI MONOPROGRAMMATI IN MODALITA’ REALE INTERFACCIA HW/SW CONVENZIONI PER LA GESTIONE DELLA MEMORIA 52 INTERFACCIA HW/SW UTILIZZO DELLA MEMORIA (VERSIONE SEMPLIFICATA) (I) Il compilatore utilizza la memoria dividendola in due parti: lo Stack e lo Heap. Lo stack è la porzione di memoria gestita automaticamente dal processore, inizia a partire dalle locazioni con indirizzo più alto e ha una dimensione variabile in funzione dello spazio di cui ha bisogno il programma. Lo heap è invece la zona di memoria gestita dinamicamente, con le funzioni malloc e calloc. Lo Heap inizia dalle locazioni di memoria più basse e si riempie verso locazioni di indirizzo più elevato. Non avendo a disposizione le funzioni di libreria malloc e calloc allocheremo i dati in memoria in maniera esplicita tramite i puntatori. Utilizzeremo inoltre una zona di memoria per il salvataggio dei dati relativi alle routine di interrupt o ad altre routine speciali. Chiameremo la memoria utilizzata in questo modo MEMORIA ALLOCATA MANUALMENTE (MAM) In un sistema di Von Neumann, la parte della memoria di indirizzi più bassi, da zero in poi, viene utilizzata per le istruzioni. 53 54 INTERFACCIA HW/SW UTILIZZO DELLA MEMORIA (VERSIONE SEMPLIFICATA) (II) A partire dagli indirizzi di memoria più alti sta la funzione main, il compilatore alloca ricorsivamente lo spazio per le procedure allocando e disallocando zone di memoria. Ogni procedura ha a disposizione una zona di memoria privata definita frame di attivazione. Lo spazio dei frame di attivazione è lo stack La memoria di tipo dinamico viene allocata a partire dagli indirizzi bassi e si espande verso quelli alti, a partire da un certo indirizzo in poi. La MAM si può allocare o fra lo heap (sotto lo heap) e la memoria istruzioni (sopra la mem istruzioni) oppure sopra lo stack Per sistemi a memoria condivisa fra dati ed istruzioni la parte più bassa della memoria è dedicata alle istruzioni. TOP OF MEMORY main FP M.A.M. MAIN DATA FUNC 1 DATA MEMORIA DATI GESTITA DAL COMPILATORE FUNC 2 DATA HEAP M.A.M. SPAZIO ISTRUZIONI locazione 0 STACK: MEMORIA AUTOMATICA ALLOCATA DAL COMPILATORE TEXT HEAP: MEMORIA DINAMICA ALLOCATA DAL COMPILATORE TRAMITE MALLOC O CALLOC INTERFACCIA HW/SW MECCANISMO DI CHIAMATA DI PROCEDURA Un opportuno sottoinsieme delle istruzioni dell'IS MIPS viene utilizzato per supportare la chiamata di una procedura (funzione), ed una volta terminata la sua esecuzione, il ritorno alla istruzione successiva alla chiamata Ogni volta che deve essere convertita in assembly una chiamata a funzione viene utilizzata una istruzione che forza il salto all'indirizzo della prima istruzione della procedura, e simultaneamente salva quello dell'istruzione successiva nel registro $31. Tale istruzione è jal label_procedura Ogni procedura deve terminare con una istruzione che forza il valore del PC all'indirizzo della istruzione successiva a quella di chiamata, cioè all'indirizzo salvato in $31. Tale istruzione è jr $31 Per gestire la possibilità che una funzione ne chiami a sua volta un'altra, sovrascrivendo l'indirizzo di ritorno in $31, è necessario che questo venga salvato in memoria E' necessaria una convenzione che definisca come salvare in memoria l'indirizzo di ritorno nonchè i parametri passati alla procedura, i dati locali, ecc. 55 56 INTERFACCIA HW-SW GESTIONE DELLO STACK (I) Quando viene eseguita una funzione viene riservata ad essa una porzione dello stack detta frame. La dimensione del frame dipende dal numero di dati relativi alla funzione che è necessario scrivere in memoria FRAME$30POINTER e può essere anche nulla se non ci sono dati da memorizzare. Le informazioni da memorizzare per ogni frame sono: parametri della funzione invocata oltre il quarto (i primi quattro parametri sono salvati nei registri da $4 a $7) indirizzo di ritorno dati locali della funzione registri temporanei che verranno sovrascritti durante l'esecuzione della funzione $29 STACK POINTER PARAMETRO N PARAMETRO 5 PARAMETRI DELLA FUNZIONE (oltre il quarto) FP DELLA FUNZ CHIAMANTE INDIRIZZO DI RITORNO LOCAL DATA 1 DATI LOCALI DELLA FUNZIONE LOCAL DATA N VALORE SALVATO DI $8 VALORE SALVATO DI $n VALORE SALVATO DEI REGISTRI UTILIZZATI DURANTE LA FUNZIONE 57 INTERFACCIA HW-SW CONVENZIONE DI CHIAMATA DI PROCEDURA PER MIPS Architetture dei Sistemi Embedded 2007/08 S.M. Carta 58 INTERFACCIA HW-SW GESTIONE DELLO STACK (II) ESEMPIO void _main( void ) { int a[2]; int i; int *pointer; pointer = (int*) 0x1000; for( i=0; i<2; i++) a[i] = i; *pointer = a[0]+a[1]; } INTERFACCIA HW-SW GESTIONE DELLO STACK (III) ESEMPIO alloca la memoria per il frame di attivazione del main. La dim minima è di 16 locazioni. 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 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 59 INTERFACCIA HW-SW GESTIONE DELLO STACK (IV) Il frame viene gestito attraverso due registri che sono utilizzati come puntatori: lo Stack Pointer ($sp), che punta all’indirizzo della locazione più bassa del frame, e il Frame Pointer ($fp), che punta alla locazione più alta. Mentre il primo è indispensabile il secondo può in certi casi non essere utilizzato a seconda del compilatore, del livello di ottimizzazione utilizzato e del programma in esecuzione. Lo stack pointer serve da riferimento per accedere alle locazioni del frame: per esempio una scrittura su una locazione del frame verrà specificata con: sw $31,20($sp) Le informazioni sul numero di dati da salvare in memoria sono forniti dal compilatore come commenti nel programma assembly in uscita. #vars indica il numero di locazioni utilizzate per memorizzare le variabili locali. Se non sono dichiarati arrays questo numero è nullo. #regs indica il numero di registri che devono essere salvati in memoria. In genere non ce ne sono se non viene invocata una funzione. #args indica il numero di locazioni destinate ai parametri di una funzione invocata. Se non viene invocata nessuna funzione questo numero è zero ma se viene invocata una funzione questo numero è sempre maggiore o uguale a 16, anche se la funzione invocata non ha parametri. 60 61 INTERFACCIA HW-SW GESTIONE DELLO STACK (V) All’inizio dell’esecuzione del programma lo Stack Pointer punta all’indirizzo successivo a quello più alto in memoria, perché lo stack deve occupare la memoria a partire dagli indirizzi più alti. Le prime istruzioni prodotte dal compilatore riguardano la gestione automatica di memoria e registri. Ogni funzione, se ha bisogno di dati da memorizzare, inizia con l’istruzione che fa scendere lo Stack Pointer in modo da allocare lo spazio sufficiente. Seguono eventualmente i salvataggi dei registri che saranno sovrascritti. La chiamata a funzione inizia sempre con un istruzione jal che porta il processore ad eseguire la prima istruzione della funzione chiamata. A questo punto viene creato un altro frame e così via. Alla fine di ogni funzione l’eventuale valore restituito viene scritto sul registro $2 o $3, vengono ripristinati i registri sovrascritti con i valori salvati in memoria e viene deallocato il frame riportando lo stack pointer alla posizione che aveva prima della chiamata a funzione. Con l’istruzione Jr $ra si torna alla funzione chiamante. 62 INTERFACCIA HW-SW SPECIFICA C E BOOT FILE Anche il main viene gestito come una funzione qualunque. Ma come viene effettuata la chiamata al main? Nel caso di Sistema Operativo, vi saranno un assembler ed un linker che si occupano di gestire la cosa in maniera trasparente per il programmatore Nel nostro caso vogliamo che tutto sia gestito in maniera esplicita, non vi è un SO e neppure un vero linker. La chiamata del main viene eseguita tramite una specifica assembly separata codificata in un file detto file di boot Tale file verrà unito alla traduzione assembly della specifica c dando vita ad una specifica assembly completa, su singolo file, che può essere tradotta in linguaggio macchina (cioè nel file di codifica delle locazioni di memoria dedicate alle istruzioni) INTERFACCIA HW-SW FILE DI BOOT Il file di boot che utilizzeremo inizialmente si occupa esclusivamente delle cose essenziali, vedremo in seguito delle specifiche più complete La prima istruzione salta sempre alla routine di inizializzazione del sistema, che in questo caso consiste solo nell'inizializzazione dello stack pointer Una volta inizializzato il sistema viene eseguita la chiamata del main Quando si vuole che lo stack allocato automaticamente dal compilatore si estenda a partire dalle locazioni di indirizzo massimo della memoria, il valore di inizializzazione dello stack pointer coincide con la dimensione della memoria J RESET_HANDLER RESET_HANDLER: ; routine di inizializzazione del sistema li $29,0x3000; ; inizializzazione dello stack pointer ; fine della routine di inizializzazione jal _main ; avvia l'esecuzione del programma principale ; La simulazione termina con il blocco del PC stop: j stop 63