IPGEN: UN FRAMEWORK PER LA GENERAZIONE AUTOMATICA DI IP-CORE PER FPGA 12 ottobre 2006 P OLITECNICO DI M ILANO FACOLTÀ DI I NGEGNERIA C ORSO DI L AUREA IN I NGEGNERIA I NFORMATICA IPGEN: UN FRAMEWORK PER LA GENERAZIONE AUTOMATICA DI IP-CORE PER FPGA Relatore: Prof. Donatella SCIUTO Correlatore: Ing. Marco Domenico SANTAMBROGIO Tesi di Laurea di: Matteo Murgida Matricola n. 662247 Alessandro Panella Matricola n. 661986 A NNO ACCADEMICO 2005-2006 Love is the dance of Eternity. (Dream Theater) Matteo I know someday you’ll have a beautiful life, I know you’ll be a star... (Pearl Jam) Alessandro Indice Riassunto 3 1 Premesse 7 1.1 Scenario operativo . . . . . . . . . . . . . . . . . . . . . . . . . 7 1.1.1 Progettazione modulare dello hardware . . . . . . . . . . 8 1.1.2 IP-Core . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 1.1.2.1 Definizione . . . . . . . . . . . . . . . . . . . 9 1.1.2.2 Dal core all’IP-Core . . . . . . . . . . . . . . . 10 1.1.3 Sistemi riconfigurabili . . . . . . . . . . . . . . . . . . . 12 1.1.4 Riconfigurabilità dinamica parziale: il flusso di Caronte . 13 1.1.4.1 Architettura hardware di Caronte . . . . . . . . 16 1.1.4.2 Da Caronte a YaRA . . . . . . . . . . . . . . . 17 Strumenti utilizzati . . . . . . . . . . . . . . . . . . . . . . . . . 20 1.2.1 Xilinx EDK . . . . . . . . . . . . . . . . . . . . . . . . 20 1.2.2 Xilinx ISE . . . . . . . . . . . . . . . . . . . . . . . . . 21 1.2.3 MentorGraphics ModelSim . . . . . . . . . . . . . . . . 23 1.2 2 Approcci precedenti 27 2.1 OCP Socket e CoreCreator . . . . . . . . . . . . . . . . . . . . . 27 2.2 Interface Adaptor Logic . . . . . . . . . . . . . . . . . . . . . . . 29 2.3 EDK Create/Import Peripheral Wizard . . . . . . . . . . . . . . . 30 2.4 ImpulseC CoDeveloper . . . . . . . . . . . . . . . . . . . . . . . 31 2.5 Conclusioni . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32 v 3 Infrastrutture di comunicazione 33 3.1 IBM CoreConnect OPB . . . . . . . . . . . . . . . . . . . . . . . 34 3.1.1 OPB PSelect . . . . . . . . . . . . . . . . . . . . . . . . 37 3.1.2 OPB IPIF . . . . . . . . . . . . . . . . . . . . . . . . . . 39 3.1.2.1 I Servizi . . . . . . . . . . . . . . . . . . . . . 40 3.1.2.2 Decodifica degli indirizzi . . . . . . . . . . . . 41 3.1.2.3 IPIC . . . . . . . . . . . . . . . . . . . . . . . 42 3.1.2.4 Modalità di utilizzo . . . . . . . . . . . . . . . 44 Wishbone . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44 3.2.1 Le interfacce . . . . . . . . . . . . . . . . . . . . . . . . 45 3.2.2 Protocollo di comunicazione . . . . . . . . . . . . . . . . 47 3.2.3 Tipi di connessione . . . . . . . . . . . . . . . . . . . . . 48 3.2 4 Metodologia 51 4.1 Motivazioni . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51 4.1.1 IPGen nel flusso di Caronte . . . . . . . . . . . . . . . . 52 Struttura dell’IP-Core generato . . . . . . . . . . . . . . . . . . . 53 4.2.1 Core . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54 4.2.2 Stub . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56 4.2.3 IP-Core . . . . . . . . . . . . . . . . . . . . . . . . . . . 56 Workflow di IPGen . . . . . . . . . . . . . . . . . . . . . . . . . 57 4.3.1 Lettura . . . . . . . . . . . . . . . . . . . . . . . . . . . 57 4.3.2 Scrittura . . . . . . . . . . . . . . . . . . . . . . . . . . . 58 Confronti con gli altri approcci . . . . . . . . . . . . . . . . . . . 59 4.2 4.3 4.4 5 Implementazione 65 5.1 Struttura del programma . . . . . . . . . . . . . . . . . . . . . . 65 5.2 Utilizzo e funzionamento del framework . . . . . . . . . . . . . . 69 5.2.1 Inizializzazione . . . . . . . . . . . . . . . . . . . . . . . 70 5.2.2 Fase di lettura . . . . . . . . . . . . . . . . . . . . . . . . 70 5.2.3 Scelta dell’interfaccia e fase di scrittura . . . . . . . . . . 71 5.2.4 Terminazione . . . . . . . . . . . . . . . . . . . . . . . . 75 5.3 5.4 6 7 Caratteristiche delle descrizioni VHDL generate . . . . . . . . . . 75 5.3.1 Visione d’insieme . . . . . . . . . . . . . . . . . . . . . . 75 5.3.2 Istanziazione dei componenti e mappaggio delle porte . . 78 5.3.3 Processi di lettura e scrittura . . . . . . . . . . . . . . . . 79 5.3.4 Gestione dei generic . . . . . . . . . . . . . . . . . . . . 81 5.3.5 Gestione di porte con dimensione maggiore di 32 bit . . . 83 Come scrivere un core interfacciabile automaticamente . . . . . . 85 5.4.1 Requisiti concettuali . . . . . . . . . . . . . . . . . . . . 86 5.4.2 Requisiti pratici . . . . . . . . . . . . . . . . . . . . . . . 87 Risultati sperimentali 89 6.1 Test di prestazione . . . . . . . . . . . . . . . . . . . . . . . . . 89 6.2 Test di funzionamento . . . . . . . . . . . . . . . . . . . . . . . . 95 Conclusione e sviluppi futuri 99 Bibliografia 101 Ringraziamenti 103 Elenco delle figure 1.1 Struttura di un IP-Core . . . . . . . . . . . . . . . . . . . . . . . 11 1.2 Il flusso di lavoro di Caronte . . . . . . . . . . . . . . . . . . . . 15 1.3 L’architettura hardware di Caronte . . . . . . . . . . . . . . . . . 17 1.4 Collegamento tra un modulo fisso e uno riconfigurabile nell’architettura di Caronte . . . . . . . . . . . . . . . . . . . . . . . . 18 1.5 L’architettura di YaRA . . . . . . . . . . . . . . . . . . . . . . . 19 1.6 Workspace di Project Navigator . . . . . . . . . . . . . . . . . . 22 1.7 Workspace di ModelSim. . . . . . . . . . . . . . . . . . . . . . . 24 2.1 Schema dell’IP-Core secondo l’approccio OCP . . . . . . . . . . 29 3.1 Set di segnali visti all’interfaccia con un componente slave . . . . 35 3.2 Schema dei servizi e dei segnali IPIF . . . . . . . . . . . . . . . . 39 3.3 Le interfacce Wishbone lato master e lato slave . . . . . . . . . . 45 3.4 Schema della connessione “Data Flow” . . . . . . . . . . . . . . 48 3.5 Schema della connessione “Shared Bus Interconnection” . . . . . 49 3.6 Schema della connessione “Crossbar Interconnection” . . . . . . 50 4.1 Flusso di creazione delle HW-SSP . . . . . . . . . . . . . . . . . 54 4.2 Struttura di un core interfacciato con OPB . . . . . . . . . . . . . 55 4.3 Struttura di un core interfacciato con Wishbone . . . . . . . . . . 55 4.4 Flusso operativo del reader . . . . . . . . . . . . . . . . . . . . . 58 4.5 Flusso operativo del writer . . . . . . . . . . . . . . . . . . . . . 59 4.6 Confronto degli IP-Core risultanti utilizzando differenti approcci . 61 ix 5.1 Class diagram di IPGen . . . . . . . . . . . . . . . . . . . . . . . 66 5.2 Esempio dell’output video di IPGen . . . . . . . . . . . . . . . . 72 5.3 Flusso di creazione dei file in uscita . . . . . . . . . . . . . . . . 74 5.4 Sequence diagram dell’esecuzione di IPGen . . . . . . . . . . . . 76 5.5 Diverse strutture di file generati . . . . . . . . . . . . . . . . . . . 78 5.6 Dichiarazione e istanziazione del core di un addizionatore binario a 32 bit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80 5.7 Esempio di processo di lettura dei registri . . . . . . . . . . . . . 81 5.8 Esempio di processo di scrittura dei registri . . . . . . . . . . . . 82 5.9 Processo di lettura per un core avente una uscita da 128 bit . . . . 83 5.10 Processo di scrittura per un core avente un ingresso da 96 bit . . . 85 6.1 Occupazione percentuale di overhead introdotto da IPGen . . . . . 92 6.2 Occupazione dello stub generato per i “dummy core” . . . . . . . 94 6.3 Simulazione di un IP-Core con interfaccia PSelect . . . . . . . . . 95 6.4 Simulazione di un IP-Core con interfaccia OPB IPIF . . . . . . . 96 6.5 Simulazione di un IP-Core con interfaccia Wishbone . . . . . . . 96 Riassunto L’obiettivo di questo lavoro di tesi è stato lo sviluppo di un framework, denominato IPGen, in grado di creare automaticamente l’interfaccia di comunicazione per core scritti in linguaggio VHDL. L’interfacciamento viene realizzato incapsulando il core ricevuto in input in una struttura, chiamata IP-Core, che contenga la logica necessaria affinchè esso possa dialogare correttamente con gli altri componenti del sistema nel quale viene inserito. Il beneficio primario che l’utilizzo di IPGen apporta in fase di progettazione di un componente hardware consiste nel fatto che lo sviluppatore è libero di concentrare interamente i propri sforzi nell’implementazione della logica funzionale, anzichè occupare parte del tempo nella realizzazione della logica di comunicazione, in quanto quest’ultima viene generata automaticamente in modo estremamente rapido. Questa caratteristica permette di incrementare il livello di automatismo nella progettazione di sistemi modulari, ovvero architetture composte da una molteplicità di componenti, ciascuno dei quali svolge una funzionalità specifica ed è in grado di interagire con gli altri moduli attraverso un’opportuna infrastruttura di comunicazione. Tale metodologia di progettazione permette la creazione di sistemi dinamicamente riconfigurabili, tipicamente implementati su dispositivi riprogrammabili come le FPGA. La funzionalità di un sistema di questo tipo può essere modificata mentre esso è operativo, senza alcuna interruzione apparente, intercambiando alcuni dei moduli che lo compongono. Il progetto di ricerca D.R.E.S.D. (Dynamic Reconfigurability in Embedded System Design), condotto all’interno del Laboratorio di Microarchitetture del Po3 Riassunto litecnico di Milano, si occupa dello studio e dello sviluppo di tali architetture, e costituisce il contesto all’interno del quale il software è stato concepito. Caronte è un flusso di progettazione realizzato in seno a D.R.E.S.D. per la creazione di sistemi dinamicamente riconfigurabili. Dal momento che il flusso di Caronte riceve una specifica di alto livello, la quale viene analizzata e partizionata generando i moduli che andranno a comporre l’architettura, è necessario creare in breve tempo le interfacce per tali componenti. È questa la motivazione che ha portato alla realizzazione di IPGen. Nonostante il framework sia stato concepito come parte integrante del flusso di Caronte, esso mantiene inalterata la propria identità di software stand-alone, in quanto il suo utilizzo permette un notevole risparmio di tempo e risorse ogniqualvolta si debba creare un componente hardware. Nel Capitolo 1 sono fornite alcune nozioni di base utili a tracciare un quadro dell’ambito in cui IPGen si colloca. Innanzitutto viene mostrata una panoramica sulla progettazione modulare dello hardware e ne viene descritto il componente fondamentale: l’IP-Core. Dopo aver analizzato i vari tipi di sistemi riconfigurabili, vengono introdotti il flusso di Caronte per la riconfigurabilità dinamica parziale e la sua evoluzione YaRA. Successivamente vengono presentati gli strumenti utilizzati durante lo sviluppo del framework: Xilinx EDK, Xilinx ISE e MentorGraphics ModelSim. Nel Capitolo 2 vengono discussi gli approcci relativi al problema dell’interfacciamento di moduli hardware, fornendo in tal modo importanti termini di paragone con i quali confrontare la metodologia proposta. Nel Capitolo 3 vengono descritte dettagliatamente le interfacce attualmente supportate da IPGen. In primo luogo viene esposto il funzionamento del bus OPB e delle due interfacce che permettono di connettersi ad esso: PSelect ed IPIF. Viene poi presentato il bus Wishbone, componente che sta alla base dell’innovazione apportata da YaRA. Nel Capitolo 4 viene introdotto IPGen spiegando la metodologia che è alla base del suo funzionamento. Dapprima sono esposte le motivazioni che hanno portato allo sviluppo del framework; successivamente viene delineata la struttura 4 Riassunto dell’IP-Core costituente l’output del tool e il flusso di esecuzione che porta alla sua generazione. Il capitolo termina fornendo alcuni confronti con gli approcci esposti nel capitolo 2. Nel Capitolo 5 il framework viene presentato dal punto di vista implementativo, iniziando dalla descrizione delle classi che lo compongono. Nel seguito vengono illustrati l’utilizzo e il funzionamento di IPGen e vengono descritte le caratteristiche dei file da esso generati. Infine sono enunciate alcune convenzioni alle quali il progettista si deve attenere durante la creazione di un core affinché esso sia interfacciabile tramite il programma. Nel Capitolo 6 sono mostrati i risultati sperimentali che validano il lavoro svolto, ottenuti tramite test di prestazione e di funzionamento. Il Capitolo 7 conclude l’elaborato ponendo le basi per sviluppi futuri. 5 Capitolo 1 Premesse IPGen è un software pensato per rendere la creazione di componenti hardware più veloce ed efficiente. Esso è stato concepito per essere integrato in un flusso di sviluppo di sistemi hardware, denominato Caronte [1, 2]. Quest’ultimo permette la progettazione di architetture che siano “dinamicamente riconfigurabili”, la cui funzionalità possa essere cioè modificata a run-time, senza interruzioni nel flusso di lavoro del dispositivo. Al fine di agevolare la comprensione degli argomenti trattati in questo testo si procede in questo capitolo con la descrizione dello scenario nel quale il framework si colloca, fornendo un insieme di nozioni e definizioni utili al lettore. Nella prima parte vine descritto l’ambiente nel quale il software presentato si trova ad operare, cioè quello della riconfigurabilità dinamica, mentre nella seconda parte si descrivono brevemente gli strumenti software utilizzati durante lo svolgimento del lavoro di tesi. 1.1 Scenario operativo Il settore nel quale IPGen è stato concepito è quello dei sistemi hardware embedded, ovvero sistemi elettronici progettati appositamente per una determinata applicazione. Una tipologia particolare di questi è costituita dai sistemi riconfigurabili, generalmente implementati su supporti fisici riprogrammabili, come le FPGA (Field Programmable Gate Array). La funzionalità di ogni singolo dispo7 CAPITOLO 1. PREMESSE sitivo di questo tipo può essere modificata un numero indeterminato di volte, in modo tale da conferire a queste architetture un’elevatissima flessibilità. Uno dei segmenti di ricerca attivi in questo ambito riguarda la potenzialità di questi dispositivi di essere riconfigurati mentre operano, senza alcuna interruzione apparente. Questo tipo di riconfigurazione viene eseguita suddividendo il sistema in blocchi, ognuno dei quali può essere modificato senza interrompere la normale esecuzione degli altri. All’interno di questi blocchi vengono collocati dei moduli, denominati IP-Core, che svolgono il compito per il quale sono stati progettati, secondo i principi della progettazione modulare. Questa sezione descrive in modo dettagliato lo scenario qui accennato, presentando dapprima il metodo di progettazione modulare dello hardware e successivamente il componente fondamentale di questa metodologia, l’IP-Core. Si prosegue con una panoramica sui sistemi riconfigurabili e con la descrizione di Caronte, il flusso di lavoro per la riconfigurabilità dinamica sviluppato all’interno del Laboratorio di Microarchitetture del quale il tool costituisce un passaggio fondamentale. 1.1.1 Progettazione modulare dello hardware La progettazione modulare (Modular-Based Design) [3] è una metodologia di sviluppo che identifica un sistema hardware come un insieme di moduli, ognuno dei quali espleta una determinata funzionalità. Queste componenti, sviluppate separatamente, vengono poi interconnesse mediante un’opportuna infrastruttura di comunicazione, tipicamente un bus o degli allacciamenti “punto a punto”. I vantaggi che questa metodologia comporta sono molteplici: • un sistema può essere sviluppato da un team di individui che lavorano indipendentemente su moduli differenti, i quali vengono assemblati in un secondo momento. Questo lavoro parallelo permette di risparmiare molto tempo e risorse; • permette di modificare solo una parte di un sistema, lasciando il resto inalterato; 8 CAPITOLO 1. PREMESSE • getta le fondamenta per il riutilizzo dello hardware: i moduli creati costituiscono un’entità a sé stante, e possono perciò essere inseriti in sistemi diversi. Esiste una prassi di massima da seguire per ottenere una buona architettura modulare, eccone i punti più significativi: 1. creazione di una architettura di alto livello (top-level design) che contenga le informazioni sui moduli e le modalità di comunicazione; 2. implementazione dei moduli nel linguaggio scelto (di solito VHDL o Verilog), che può essere svolta da persone diverse; 3. assemblaggio dei moduli all’interno del top-level design. Ognuno dei moduli che compongono un sistema di questo tipo viene denominato IP-Core, il quale costituisce l’output di IPGen ed è argomento della sezione seguente. 1.1.2 IP-Core L’obiettivo di IPGen è costruire IP-Core. È dunque estremamente importante conoscere ciò a cui questo termine si riferisce. Viene dapprima fornita una definizione formale del termine, per proseguire con la descrizione della struttura interna di questo “oggetto”. 1.1.2.1 Definizione Il termine “IP-Core” (Intellectual Property-Core) indica [4, 5] un’entità logica riutilizzabile, impiegata sia nella creazione di logiche “statiche” (ASIC) che nelle schede riprogrammabili (come le FPGA, vedere sezione 1.1.3). In altre parole, un IP-Core è un’unità che, all’interno di un sistema hardware più ampio, svolge un determinato compito. Come esprime il termine stesso, un IP-Core è una proprietà intellettuale: vale a dire che esso è una risorsa che può essere posseduta da un 9 CAPITOLO 1. PREMESSE singolo o da una società, oppure può essere messa a disposizione di chiunque la voglia utilizzare (spesso si parla di open cores). Una usuale classificazione degli IP-Core è la seguente: • Hard cores, implementazioni fisiche della progettazione della proprietà intellettuale; • Firm (semi-hard) cores, che contengono informazioni sul proprio posizionamento ma possono essere esportate su varie architetture; • Soft cores, definibili come una rete di porte logiche e registri, implementata spesso in un linguaggio di descrizione dell’hardware come VHDL. Un IP-Core può costituire sia un componente master, per esempio la stessa CPU, oppure un componente slave, che deve essere “utilizzato” da un master1 . Esso può inoltre rappresentare entità come un bus o porta seriale, e quindi costituire una infrastruttura di comunicazione tra altri IP-Core. Nel seguito si farà riferimento soltanto a IP-Core di tipo slave; inoltre, con il termine IP-Core ci si riferirà esclusivamente ai soft cores, cioè quelli che possono essere istanziati su una FPGA. L’analogia tra le componenti della progettazione modulare (sezione 1.1.1) e IP-Core è immediata: gli IP-Core costituiscono infatti i moduli che compongono un sistema hardware progettato modularmente. Nel prossimo paragrafo viene descritta la struttura generale di un IP-Core. 1.1.2.2 Dal core all’IP-Core Con il termine core si intende il “cuore elaborativo” di un componente, cioè la parte di logica interna che ne costituisce la funzionalità vera e propria. Il core è dunque la porzione distintiva e caratteristica che rappresenta la vera e propria 1I dispositivi che possono essere connessi ad un bus si dividono in master e slave. I primi sono componenti attivi, ovvero possono decidere quando iniziare un trasferimento di dati; i secondi sono passivi, in quanto rimangono in attesa di una richiesta di trasferimento [6]. 10 CAPITOLO 1. PREMESSE “proprietà intellettuale” del componente. In realtà, per essere inserito efficacemente all’interno di un sistema più grande, un core deve essere dotato di un’interfaccia verso l’esterno. Questa interfaccia permette ad esso di accedere al canale di comunicazione (per esempio un bus) e dialogare con gli altri componenti dell’architettura. Per questo motivo, con il termine IP-Core d’ora in poi si farà riferimento ad un blocco logico dotato di un’opportuna interfaccia, adatto ad essere inserito in un sistema che adotti l’infrastruttura di comunicazione per il quale esso è stato progettato. In pratica, un IP-Core è costituito da un “guscio” esterno che racchiude il core ed un’opportuna interfaccia di comunicazione, come mostrato in figura 1.1. Spesso un core, quando contenuto in questo tipo di struttura, viene indicato con l’espressione user logic, proprio per rimarcare il fatto che è questa la parte che viene effettivamente implementata dall’utente e ne costituisce una proprietà intellettuale. Figura 1.1: Struttura di un IP-Core Nella prossima sezione vengono introdotti i sistemi riconfigurabili, all’interno dei quali gli IP-Core rivestono un ruolo fondamentale in quanto costituiscono, nella architettura presa in esame, i blocchi che possono essere intercambiati per fornire la capacità al sistema di evolversi. 11 CAPITOLO 1. PREMESSE 1.1.3 Sistemi riconfigurabili Una branca relativamente nuova della progettazione hardware si occupa di sistemi riconfigurabili, ovvero sistemi che, al contrario delle tradizionali architetture ASIC (Application-specific Integrated Circuit), permettono di essere modificati dopo essere stati prodotti. Essi prendono dunque il nome di sistemi riconfigurabili. In questo ambito i dispositivi più diffusi sono le FPGA (Field Programmable Gate Array) che, grazie ad un complesso sistema di porte logiche, possono essere riprogrammate ogni qual volta è necessario far svolgere loro un particolare compito. Non esiste un’unica direzione secondo cui intendere la riconfigurabilità di un sistema; si possono infatti identificare i seguenti parametri: • Riconfigurabilità interna: il flusso di riconfigurazione è gestito da un componente interno alla FPGA da riconfigurare; • Riconfigurabilità esterna: la riconfigurazione è gestita da un componente esterno alla FPGA da riconfigurare. Si può inoltre fare una distinzione tra: • Riconfigurabilità totale: è il caso in cui la FPGA viene interamente riconfigurata; • Riconfigurabilità parziale: secondo questa modalità, soltanto una parte della FPGA viene modificata, mentre l’altra non è interessata dal processo di riconfigurazione. Quest’ultima classificazione necessita di un’ulteriore esplicazione: se si opera su una rete di più FPGA, invece che su di un singolo chip, la riconfigurabilità è totale se si analizza una singola FPGA ma può risultare parziale se il punto di vista è quello della rete nel suo insieme. Su tali basi si aggiunge un ulteriore grado di libertà: tali architetture possono essere riconfigurate anche a run-time, cioè mentre sono operative, senza 12 CAPITOLO 1. PREMESSE interrompere il flusso di lavoro. Tale processo prende il nome di riconfigurabilità dinamica, che si contrappone alla tradizionale riconfigurabilità statica, effettuata soltanto quando la FPGA non è operativa. Risulta evidente che la riconfigurabilità dinamica effettuata su un solo chip deve essere parziale, in modo da non interromperne l’esecuzione. Il tipo di riconfigurazione che viene presa in esame nel seguito è la Riconfigurabilità Dinamica Parziale (Partial Dynamic Reconfiguration). Xilinx introduce [7] due differenti flussi per implementare la riconfigurabilità dinamica parziale: l’approccio module-based e quello difference-based . • Module-based partial reconfiguration. Questo approccio si basa sulla progettazione modulare dello hardware (sezione 1.1.1), e prevede la definizione di moduli riconfigurabili, cioè porzioni delimitate di FPGA la cui dimensione è invariante durante l’esecuzione, che possono essere riconfigurate a run-time senza interferire con il funzionamento degli altri moduli e quindi del sistema complessivo. Per rendere tale procedimento possibile, è necessario definire una architettura di livello “top” (top-level architecture) in cui ogni modulo è visto come una “scatola nera” (black-box). La logica al livello top deve limitarsi alla descrizione delle black box (senza conoscenza del loro contenuto) e di una infrastruttura fissa di comunicazione, i bus macro. • Difference-based partial reconfiguration. Nelle architetture in cui si rende necessario riconfigurare blocchi logici di dimensioni significative, è richiesta la riconfigurazione module-based. Se invece si devono attuare cambiamenti piccoli, per esempio una porta di Input/Output, è consigliabile utilizzare l’approccio difference based. In questi casi, si modifica il file di configurazione (NGC) dove è necessario e si produce un bitstream che contiene solamente le differenze tra la configurazione attuale e quella precedente. 1.1.4 Riconfigurabilità dinamica parziale: il flusso di Caronte In questa sezione viene presentato Caronte, un workflow di sviluppo per architetture riconfigurabili [1, 2] sviluppato all’interno del Laboratorio di Microarchitet13 CAPITOLO 1. PREMESSE ture. Tale metodologia è in parte basata su EDK, presentato nella sezione 1.2.1, e adotta dunque uno strumento già largamente utilizzato nello sviluppo di architetture per FPGA, sfruttandone le caratteristiche che lo rendono adatto alla realizzazione di sistemi riconfigurabili. Caronte intende la riconfigurabilità come ridefinizione di uno o più componenti all’interno dell’architettura, è dunque profondamente legato alla riconfigurazione per moduli (sezione 1.1.3): ogni modulo che viene intercambiato viene denominato blocco riconfigurabile. Il flusso di lavoro di Caronte è mostrato in figura 1.2, e include le seguenti fasi: • HW-SSP Phase. Durante la fase Hardware Static System Photo viene identificato un insieme di sistemi completi ognuno dei quali rappresenta un passo nel processo di riconfigurazione. L’idea è quella di considerare il sistema nel tempo come una sequenza di foto statiche. Queste sono tutte identiche per quanto riguarda la parte statica del sistema, mentre differiscono per il contenuto di almeno una Black Box (vedere sezione 1.1.4.1); • Design Phase. Obiettivo di questa fase è quello di raccogliere tutte le informazioni richieste per il calcolo dei bitstream necessari per implementare la riconfigurabilità su di una FPGA. A tal proposito viene utilizzato EDK, adottando una approccio modular based (sezione 1.1.1), fino a produrre la descrizione dei sistemi in VHDL. Un problema da tenere in considerazione è la natura dinamica dell’architettura realizzata, in particolare per quanto riguarda la comunicazione sia tra i moduli riconfigurabili, sia tra essi e la parte fissa del sistema. Per questo vengono utilizzate delle particolari linee di comunicazione chiamate bus macro, che rimangono inalterate durante la riconfigurazione, e sono dunque comuni a tutte le SSP che compongono l’evoluzione del sistema. Bisogna inoltre determinare a priori la dimensioni di ogni porzione fisica di FPGA destinata ad accogliere i moduli riconfigurabili; • Bitstream Creation Phase. Vengono creati tutti i bitstream necessari per la mappatura fisica dell’architettura su FPGA. Questi vengono chiamati bitstream parziali in quanto non contengono l’informazione sull’intero siste14 CAPITOLO 1. PREMESSE ma, ma solo sulle porzioni che vengono modificate tra due passi successivi, e vengono pertanto calcolati come differenza tra due bitstream completi di configurazione. Figura 1.2: Il flusso di lavoro di Caronte Un problema cruciale è la decisione di quali parti di un sistema hardware coinvolgere nella riconfigurazione e quali invece lasciare fissi sulla FPGA. Bisogna inoltre determinare quali blocchi riconfigurabili andranno in una determinata porzione di FPGA (la partizione del chip deve essere fissata a priori e non può essere modificata) e quali in altre. Si tratta di questioni molto delicate che vanno trattate con particolare attenzione, avendo l’obiettivo di raggiungere il miglior compromesso tra occupazione e prestazioni dell’architettura. 15 CAPITOLO 1. PREMESSE 1.1.4.1 Architettura hardware di Caronte La struttura hardware alla base del funzionamento del flusso di Caronte è suddivisa in due parti: quella fissa e quella riconfigurabile (figura 1.3). La prima è schematicamente rappresentabile come una tradizionale macchina di VonNeumann controllata da un processore anch’esso interno al chip (soft-processor). In questa parte si trovano i seguenti componenti: l’Interrupt Controller, l’ICAP (necessario per leggere da memoria e scrivere nella specifica Black Box i moduli riconfigurabili) e l’IP-Core Manager (IPCM), una sorta di ponte tra il lato software e il lato hardware. Sempre collocato sulla parte fissa si trova il bus di comunicazione, tipicamente della famiglia CoreConnect (vedere il capitolo 3). La parte riconfigurabile, o dinamica, è invece costituita da un insieme di Black Box. EDK definisce un componente come una qualsiasi parte di un sistema, per esempio un bus, una periferica o un processore. Da questo punto di vista anche una Black Box può essere considerata un componente, anche se questo è riduttivo: in realtà una Black Box è una porzione di FPGA di dimensione fissa che può essere completamente riconfigurata senza interferire con il normale funzionamento della restante parte del chip, come se fosse un guscio entro il quale collocare i moduli. Dal punto di vista dell’implementazione essa è costituita da due parti: una interna, e cioè l’IP-Core che costituisce il modulo riconfigurabile, e una esterna, che fornisce l’interconnessione tra la logica interna e i bus macro. La modalità di comunicazione all’interno di uno scenario come quello appena descritto include tre casi: innanzitutto si ha il collegamento tradizionale tra due moduli fissi tramite un bus, tipicamente CoreConnect OPB; inoltre è necessario supportare la connessione tra un blocco fisso e uno riconfigurabile, come pure tra due blocchi entrambi riconfigurabili. Tutto questo considerando il fatto che nella parte dinamica i collegamenti sono realizzati tramite bus macro, necessari per creare un canale stabile che supporti la riconfigurazione delle Black Box. Per avere una corretta visione del quadro è utile immaginare di “percorrere” il collegamento tra un modulo fisso e uno riconfigurabile (figura 1.4): il modulo fisso è dotato di un’interfaccia verso il bus adottato dall’architettura (si considera, senza perdere generalità, OPB). Una volta raggiunto il “confine” tra parte fissa e 16 CAPITOLO 1. PREMESSE Figura 1.3: L’architettura hardware di Caronte riconfigurabile è necessario passare attraverso i bus macro, che fortunatamente, essendo semplicemente delle linee fisiche presenti sulla FPGA non possiedono un protocollo di comunicazione, e possono quindi essere semplicemente allacciati ai segnali OPB. Si deve ora “penetrare” nella Black Box per giungere all’IP-Core: anche qui sarà sufficiente un semplice mappaggio tra bus macro e segnali entranti nell’IP-Core: esso è dotato di un interfaccia OPB, in modo da non richiedere la presenza di un bridge che traduca tra due protocolli di comunicazione differenti. Per quanto riguarda il collegamento di due moduli riconfigurabili il procedimento è analogo a prima, con la differenza che in questo caso il punto di partenza è un IP-Core all’interno di una Black Box, quindi si passa al bus macro e infine ad un’altra Black Box. 1.1.4.2 Da Caronte a YaRA L’architettura proposta, pur essendo valida, soffre di alcune limitazioni, soprattutto se utilizzata in un contesto di riconfigurabilità dinamica. La prima di queste è il fatto che la comunicazione tra moduli fisicamente non contigui sulla FPGA può avvenire solamente se i moduli che si frappongo tra la sorgente e la desti17 CAPITOLO 1. PREMESSE Figura 1.4: Collegamento tra un modulo fisso e uno riconfigurabile nell’architettura di Caronte nazione ritrasmettono i dati inalterati. Questo problema implica pesanti effetti sulla riconfigurazione, dato che se uno dei moduli attraverso il quale il canale di comunicazione deve passare viene riconfigurato, la comunicazione dovrà essere interrotta; ciò rappresenta un collo di bottiglia nelle prestazioni del sistema e limita il campo di applicazione dove Caronte può essere effettivamente utilizzato. Un altro forte limite sta nel fatto che lo standard CoreConnect imposto da IBM prevede che per ogni IP-Core istanziato all’interno di un sistema vengano replicate tutte le linee di bus. In questo modo ogni IP-Core aggiunto occupa ben più dello spazio necessario al suo effettivo funzionamento e ciò impone un vincolo estremamente restrittivo alla scalabilità del sistema, il quale non potrà essere composto da un numero elevato di IP-Core. Per questi motivi è stata creata YaRA (Yet another Reconfigurable Architecture); anche in questo caso il sistema è composto da una parte fissa ed una riconfigurabile ma, a differenza di Caronte, presenta un’infrastruttura di comunicazione diversa. La nuova architettura si presenta come in figura 1.5. La parte fissa è suddivisa a sua volta in due sottosistemi. Il primo di questi adotta un bus di comunicazione di tipo PLB, il quale connette componenti quali un processore PowerPC, un BRAM Controller per controllare l’accesso alle memorie BRAM presenti sulla FPGA e l’ICAP. Il secondo sottosistema utilizza invece un bus OPB, dove sono presenti IP-Core che variano da un particolare sistema all’altro, ma per i quali non è prevista alcuna riconfigurazione (per esempio una porta seriale). Per collegare queste due parti è presente un bridge tra PLB e OPB. La 18 CAPITOLO 1. PREMESSE Figura 1.5: L’architettura di YaRA parte riconfigurabile è dotata di connessioni di tipo Wishbone (sezione 3.2), che è esente dagli svantaggi che emergono utilizzando il bus OPB. Il cuore dell’innovazione nel passaggio da Caronte e YaRA è stata la creazione, all’interno del Laboratorio di Microarchitetture, del componente OPB to Wishbone Bridge che permette il collegamento tra la parte fissa e quella riconfigurabile. I moduli riconfigurabili devono rispettare alcuni requisiti: devono essere compatibili con il protocollo Wishbone e devono occupare lo stesso numero di CLB2 , che deve essere necessariamente un multiplo di 8 ed essere scelto in modo da contenere il più grande dei moduli utilizzati dal sistema nelle sue riconfigurazioni. Queste limitazioni hanno lo scopo di garantire l’intercambiabilità dei moduli per poterli sostituire senza problemi nel processo di riconfigurazione. 2 CLB: Configurable Logic Block. Nell’architettura di una FPGA sono i blocchi logici atomici attraverso i quali viene realizzata la logica combinatoria che si vuole implementare nel dispositivo. Ogni cella è dotata di ingressi dati e di controllo attraverso i quali può essere riprogrammata con la funzione logica desiderata. Il numero di CLB è una delle unità di misura per la stima dell’occupazione di un dato modulo nel chip. 19 CAPITOLO 1. PREMESSE 1.2 Strumenti utilizzati In questa sezione vengono presentati i software che sono stati utilizzati nello svolgimento del lavoro di tesi. Il primo di questi è Xilinx EDK, che permette la creazione di sistemi per FPGA; esso è ampiamente utilizzato all’interno del flusso di Caronte e costituisce uno degli ambienti principali di utilizzo degli IP-Core generati dal IPGen. Successivamente è descritto Xilinx ISE, il quale costituisce uno strumento di sviluppo e sintesi di architetture hardware, utilizzato in questo lavoro per la sintesi degli IP-Core generati. L’ultimo software presentato è MentorGraphics Modelsim, che consente di effettuare simulazioni sui componenti implementati; esso è stato indispensabile per effettuare i test di funzionamento dei componenti creati. 1.2.1 Xilinx EDK Embedded Development Kit (EDK) [8], prodotto da Xilinx, è un framework composto da numerosi tool che permette di creare un sistema embedded completo implementabile su una FPGA. EDK permette infatti di realizzare sia l’architettura hardware del sistema sia la componente software. Dal punto di vista hardware è necessaria la creazione di una piattaforma embedded che consista di uno o più processori e moduli (spesso denominati anche periferiche) collegati ad un bus, adottando perciò un approccio alla progettazione di tipo modulare (sezione 1.1.1); EDK descrive questa piattaforma hardware in un file MHS (Microprocessor Hardware Specification). Dall’altro lato vi è la realizzazione di una piattaforma software, cioè l’insieme di driver per i moduli ed eventualmente di un sistema operativo per l’architettura creata; questo è descritto in un file MSS (Microprocessor Software Specification). L’interfaccia grafica di EDK è XPS (Xilinx Platform Studio), che integra strumenti aventi vario scopo e utilizzo, dalla creazione del sistema hardware alle funzionalità di test e debugging. Con XPS è possibile generare e modificare i file MPS e MHS, creare una visualizzazione schematica dell’architettura realizzata e accedere a numerosi tool che compongono la suite EDK I principali sono: 20 CAPITOLO 1. PREMESSE • Base System Builder (BSB): wizard che permette la facile e veloce creazione di un sistema funzionante, o la realizzazione di un sistema base personalizzabile per progetti più complessi; • Platform Generator (Platgen): permette di tradurre la descrizione della piattaforma software in una netlist HDL che può essere implementata su una FPGA. In particolare invoca XST (mostrato nella sezione seguente) per la sintesi degli IP-Core utilizzati; • Create and Import Peripheral Wizard: vedere sezione 2.3; • Library Generator (Libgen): configura la piattaforma software, comprese le applicazioni che si vogliono utilizzare, gestendo il file MSS. Una caratteristica importante di EDK è che, nel costruire un’architettura collegando insieme vari IP-Core, esso non analizza il contenuto di ciascun componente, ma si interessa solamente dei segnali di Input/Output di ciascuno di essi; in questo modo è possibile creare dei moduli costituiti solamente dal “guscio” esterno, contenenti un parte della logica di comunicazione, che verranno poi “riempiti” successivamente con IP-Core appropriati. È proprio questa considerazione che sta alla base della metodologia di riconfigurabilità dinamica presentata nella sezione 1.1.4. 1.2.2 Xilinx ISE Integrated Software Environment (ISE), anch’esso prodotto da Xilinx, è un insieme di tool che permette di effettuare il design e la sintesi di un’architettura descritta tramite linguaggi come VHDL (Very high speed integrated circuit Hardware Description Language) o Verilog. Nel seguito ci si riferirà sempre a VHDL poiché è il linguaggio riconosciuto dal tool presentato in questa tesi, nonostante gli strumenti Xilinx permettano di utilizzare anche Verilog. L’interfaccia grafica di questo pacchetto di software si chiama Project Navigator, tramite il quale è possibile accedere a tutte le funzionalità implementative offerte da ISE. Come si può vedere in figura 1.6, l’area di lavoro è divisa in quattro 21 CAPITOLO 1. PREMESSE Figura 1.6: Workspace di Project Navigator settori: • un editor di testo (riquadro A), in cui è possibile modificare i file sorgenti del progetto da sintetizzare. Sempre nella stessa finestra possono essere visualizzati i risultati di sintesi (Design Summary); • una finestra (riquadro B) in cui è rappresentata la struttura del progetto, in termini di moduli e librerie utilizzate; • una finestra (riquadro C) da cui si possono lanciare le funzioni; • una shell (riquadro D) dove viene visualizzato il log delle operazioni effettuate dal programma. Le funzioni incluse in ISE sono divise in gruppi, e sono qui di seguito presentate. Il primo gruppo (Design Utilities) comprende alcune utilità come la chiamata al simulatore (in questo caso ModelSim, sezione 1.2.3). Si trovano poi le funzionalità User Constraints, che permettono di impostare dei vincoli prestazionali di spazio 22 CAPITOLO 1. PREMESSE e di tempo al sistema creato. Il gruppo successivo è Synthesize - XST, necessario per compilare e sintetizzare il codice VHDL. Queste funzionalità utilizzano il tool XST [9], che, partendo dal VHDL, crea una netlist descritta in un file NGC, effettuando una mappatura del codice sorgente su una rete logica composta da blocchi combinatori, sequenziali e interconnessioni tra di essi. Il processo di sintesi è estremamente complesso, e ciò è dovuto in buona parte alla ottimizzazioni spaziali (area occupata) e temporali (maximum delay time) effettuate. Come si può vedere nel capitolo 6, queste influenzano in modo determinante le prestazioni del tool oggetto di questa tesi. Sempre nell’ambito di sintesi XST si trova View RTL Schematic, che fornisce una visualizzazione schematica dei blocchi logici che costituiscono il sistema. Un altro gruppo di funzionalità è Implement Design, che mappa la rete risultante dal processo di sintesi su di una FPGA, il cui modello e versione vengono decisi a priori. 1.2.3 MentorGraphics ModelSim ModelSim è uno strumento di editing, simulazione e debug di sistemi hardware descritti in un linguaggio HDL. Esso permette di simulare il comportamento di un’architettura hardware visualizzandone numericamente e graficamente il comportamento. L’interfaccia grafica del software [10] consiste di varie finestre che danno accesso alle varie parti del sistema da simulare e ai numerosi tool disponibili. Si possono effettuare due tipi di simulazione: la simulazione step-by-step, che permette di procedere un’istruzione alla volta direttamente sul codice sorgente, e la simulazione wave, di più alto livello, che procede in base ad un’unità temporale definita dall’utente. Quest’ultima modalità è quella che è stata utilizzata nella fase di test di questo progetto, e nel seguito del documento salvo esplicita specificazione si farà implicitamente riferimento ad essa. In fase di simulazione, l’interfaccia si presenta come in figura 1.7. Le parti principali che la compongono sono: • Workspace (riquadro A): consente un rapido accesso al progetto, permetten23 CAPITOLO 1. PREMESSE Figura 1.7: Workspace di ModelSim. do di visualizzare rapidamente la struttura del sistema, le librerie utilizzabili ed i file VHDL che compongono il progetto; • Objects (riquadro B): mostra il nome e il valore dei segnali del modulo corrente, ossia quello selezionato nella visualizzazione “struttura” del Workspace. Da questo pannello si può forzare il valore dei segnali (solitamente quelli di input) e impostare il segnale di clock, per procedere poi alla simulazione vera e propria; • Wave (riquadro C): permette di visualizzare i risultati della simulazione. Questo pannello è suddiviso a sua volta in tre parti – Pathway Pane: visualizza i segnali e il relativo percorso, partendo dal livello top dell’architettura; – Value Pane: visualizza il valore dei segnali all’istante indicato dal cursore nel Wave pane; – Wave pane: visualizza graficamente l’andamento temporale dei segnali. 24 CAPITOLO 1. PREMESSE • Transcript (riquadro D): tiene traccia della history del flusso del programma e fornisce un’interfaccia da linea di comando. Si può avviare la simulazione direttamente dal Project Navigator di ISE. A questo punto è necessario impostare solitamente alcuni segnali, forzandoli al valore desiderato. Inoltre è necessario configurare, se presente, il segnale di clock. Dopo aver determinato il parametro run length corrispondente alla lunghezza del periodo di simulazione, è possibile procedere, un periodo alla volta, alla simulazione vera e propria. 25 Capitolo 2 Approcci precedenti Prima di iniziare a descrivere il software realizzato, si vogliono presentare brevemente alcuni approcci esistenti al problema dell’interfacciamento dei core. Tale analisi risulta molto utile sotto due punti di vista: innanzitutto essa permette di pervenire ad una migliore comprensione dei benefici apportati da questo lavoro nell’ambito della progettazione modulare; in secondo luogo si ottiene una visione più nitida dello scenario di applicazione, con l’effetto di delineare in modo più chiaro la collocazione del framework, sia in una dimensione “verticale” (nel flusso di design) che “orizzontale” (all’interno dell’architettura). Si inizia con una descrizione di OCP Socket e di Interface Adaptor Logic, standard proposti per le interfacce dei core, per poi passare alla presentazione del tool Import Peripheral Wizard che fa parte di Xilinx EDK (presentato nella sezione 1.2.1). Si prosegue con un accenno al framework CoDeveloper sviluppato da ImpulseC. Considerazioni circa la bontà degli approcci presentati e confronti con IPGen verranno discusse nella sezione 4.4. 2.1 OCP Socket e CoreCreator L’Open Core Protocol (OCP) [11, 12] è un progetto sviluppato inizialmente dall’organizzazione Virtual Socket Interface Alliance (VSIA), e passato successivamente sotto il controllo di OCP International Partnership (OCP-IP). L’obiettivo 27 CAPITOLO 2. APPROCCI PRECEDENTI primario di tale progetto è quello di fornire un supporto condiviso per il riuso degli IP-Cores in sistemi differenti. Il riutilizzo di componenti porta con sé numerosi benefici, primo tra tutti la diminuzione del tempo richiesto nello sviluppo di un sistema hardware, con un ovvio incremento della produttività. L’approccio è basato sulla proposta di uno standard per l’interfacciamento dei core all’interno del sistema ospite. Il Socket OCP è alla base di tale metodologia: si tratta di un componente di interfacciamento molto flessibile, progettato per essere utilizzato sia da moduli di tipo master che di tipo slave. Esso offre un’ampia gamma di servizi allo sviluppatore, che può scegliere di utilizzarli o meno tramite un opportuno file di specifica. Sempre in un’ottica di diffusione su larga scala dello standard proposto, OCP-IP ha sviluppato un bus che si sposa perfettamente con le caratteristiche del Socket OCP. Ovviamente, se si vuole rendere tale componente effettivamente portabile anche su sistemi che utilizzano una infrastruttura di comunicazione differente, è necessario utilizzare un “bus wrapper”, che implementa la connessione tra il particolare mezzo trasmissivo e il Socket OCP. Si avranno dunque numerosi wrapper, uno per ogni infrastruttura di comunicazione. Non è dunque escluso che ci si possa trovare nella condizione di dovere implementare personalmente tale componente, in quanto esso potrebbe non esistere. Seguendo questa metodologia si ottiene dunque un IP-Core stratificato su tre livelli, come mostrato in figura 2.1. Si noti che è proprio il concetto di stratificazione (layering) che sta alla base di questo approccio: essa permette infatti di disaccoppiare in modo preciso la funzionalità logica del core dalla connessione con il sistema. Come detto precedentemente, lo sviluppatore ha la possibilità di scegliere quali servizi offerti dal Socket OCP utilizzare, sulla base delle caratteristiche del proprio componente. Tale scelta viene implementata tramite un file di specifica RTL, che può risultare di difficile implementazione da parte di sviluppatori non esperti di linguaggio RTL. Per ovviare a questo problema OCP fornisce un tool denominato CoreCreator, che permette di creare in modo automatico, dopo aver selezionato le opzioni desiderate, un pacchetto costituente il nuovo IP-Core con Socket OCP integrato. Questo tool permette anche di validare in modo automatico la compatibilità del 28 CAPITOLO 2. APPROCCI PRECEDENTI Figura 2.1: Schema dell’IP-Core secondo l’approccio OCP core con lo standard OCP. 2.2 Interface Adaptor Logic In [13] e [14] viene descritta una metodologia sostanzialmente molto simile a quella mostrata nella precedente sezione. Anche in questo caso l’attenzione è focalizzata sul riutilizzo degli IP-Core, obiettivo raggiungibile mediante una rigida separazione tra interfaccia di comunicazione e logica funzionale. A connettere le due parti si trova un blocco denominato Interface Adaptor Logic (IAL), che ha il pregio, a detta degli autori, di essere molto più leggero del Socket OCP. Anche in questo caso dunque si ha un’architettura strutturata su tre livelli: la connessione con il mezzo di comunicazione (corrispondente al “bus wrapper” di OCP), il blocco IAL e il core che implementa la funzionalità. Il modulo IAL ha la caratteristica di presentare un set molto esiguo di porte per quanto riguarda la connessione “lato core”; meno porte implicano necessariamente meno vincoli da rispettare, e questo è sicuramente un aspetto positivo di questa interfaccia. Ciò nonostante l’interesse per questo approccio sembra essere scemato, in quanto attualmente non si trovano corrispondenze reali per quelli che nei due articoli presi in esame sono presentati come sviluppi futuri, in primis la 29 CAPITOLO 2. APPROCCI PRECEDENTI creazione di “IAL Generator”, uno strumento software che sarebbe stato in grado di generare automaticamente un IP-Core completo di interfaccia IAL. 2.3 EDK Create/Import Peripheral Wizard Uno dei tool che compone la suite EDK (sezione 1.2.1) è Create/Import Peripheral Wizard [8]. L’obiettivo di tale strumento è quello di aiutare lo sviluppatore nel realizzare IP-Core che siano compatibili con EDK. Si compone, come dice il nome, di due parti: la creazione di un nuovo componente (create) e l’importazione di un componente già esistente (import). Per quanto riguarda la creazione di un nuovo componente, il tool offre un generatore automatico del template nel quale porre il codice che descrive la funzionalità dell’IP-Core che si vuole creare. Per template si intende la descrizione dell’IP-Core priva della parte di logica che esprime il comportamento del modulo. Sostanzialmente, riprendendo la figura 1.1, il tool crea automaticamente il file VHDL che contiene l’istanza dell’interfaccia e della “user logic”. Per quanto concerne quest’ultima, viene generato un secondo file VHDL contenente la dichiarazione di entity, mentre la parte implementativa (architecture) è vuota ed è appunto la porzione che va inserita da parte dello sviluppatore. Essendo stato sviluppato nell’ambito di EDK, che adotta come canali di comunicazione i bus della famiglia IBM CoreConnect (sezione 3), il tool permette di utilizzare soltanto due interfacce: OPB IPIF e PLB IPIF. Siccome un’interfaccia di tipo IPIF offre molti servizi, l’utente può scegliere, in fase di creazione del template, quali di questi servizi utilizzare per il proprio modulo. In questo modo vengono automaticamente impostati i parametri relativi a tali servizi all’interno del file VHDL che contiene l’istanza dell’interfaccia. Per quanto riguarda il file predisposto a contenere la logica propria del modulo, esso presenta porte di input/output fisse (IPIC), e i segnali provenienti da queste devono essere opportunamente trattati all’interno della logica dell’utente. La funzione di importazione degli IP-Core, invece, prende in input il codice VHDL già completo che rappresenta un IP-Core e lo correda di alcuni file che lo 30 CAPITOLO 2. APPROCCI PRECEDENTI rendono un componente realmente inseribile in un sistema EDK. Sostanzialmente vengono creati due file: • Peripheral Analyze Order (PAO), che definisce l’ordine dei file HDL necessari alla sintesi del componente; • Microprocessor Peripheral Definition (MPD), nel quale si trovano tutte le porte e i parametri dell’IP-Core importato, in modo che esso sia trattato in modo corretto da EDK. Il Create/Import Peripheral Wizard è dunque un utile strumento nell’ambito di lavoro di EDK, in quanto permette di facilitare e velocizzare il flusso di inserimento di un nuovo modulo all’interno di un sistema. La sua utilità è però molto ridotta al di fuori di tale ambiente, proprio perché si tratta di uno strumento sviluppato ad-hoc. 2.4 ImpulseC CoDeveloper Il framework CoDeveloper [15], prodotto da Impulse Accelerated Technologies, è una complessa piattaforma di sviluppo che permette di gestire la creazione di sistemi misti hardware e software (hardware-software codesign), sintetizzando una specifica di alto livello. Questa specifica è implementata in linguaggio C, utilizzando le librerie ImpulseC fornite con il programma. Durante il processo di sintesi di un componente si può selezionare, tra molte alternative, quale sarà l’architettura ospite, in modo da implementare nel codice VHDL prodotto un’interfaccia opportuna; per esempio si può scegliere un’interfaccia di tipo OPB IPIF (vedere capitolo 3). Il punto di forza di questa metodologia sta nel fatto che, generando essa stessa il codice VHDL da utilizzare come “core”, nel processo di creazione dell’interfaccia dispone di un set di segnali conosciuti che si collegano in modo meccanico all’insieme dei segnali dell’interfaccia. Complessivamente, questo framework è uno strumento molto valido che, se utilizzato in modo opportuno, può produrre un risparmio di tempo notevole nello sviluppo di sistemi integrati hardware e software. 31 CAPITOLO 2. APPROCCI PRECEDENTI 2.5 Conclusioni Dopo avere analizzato le caratteristiche di questi approcci, è opportuno sottolinearne pregi e difetti, in modo da fornire un valido sistema di confronto con la metodologia proposta in questo documento. OCP Socket è uno standard progettato molto dettagliatamente che ha sicuramente buone possibilità di affermarsi, soprattutto perchè è sostenuto da una reale organizzazione che si occupa della sua diffusione. Ciò nonostante, affinchè un core sia effettivamente portabile su sistemi esistenti che non adottano il bus OCP, è necessario introdurre un ulteriore livello di logica (il bus wrapper) che appesantisce il modulo hardware. Oltre a quanto detto lo strumento di generazione automatica che viene proposto, ovvero CoreCreator, è in grado di generare l’interfaccia di comunicazione solamente per la connessione al bus OCP. Interface Adaptor Logic, pur essendo più leggera, soffre degli stessi difetti di OCP Socket, in quanto anche in questo caso è necessario implementare opportuni bus wrapper per l’effettiva portabilità dei componenti. Inoltre tale progetto sembra essere stato abbandonato, siccome non si trovano riferimenti a sviluppi ulteriori. EDK Create/Import Peripheral Wizard è un tool molto utile per importare core in un sistema EDK, ma non offre una reale soluzione per l’interfacciamento di core già esistenti, in quanto si limita a fornire un template per le interfacce OPB IPIF e PLB IPIF. Infine, Impulse CoDeveloper rappresenta un framework molto valido per quanto riguarda la sintesi di sistemi a partire da specifiche di alto livello, ed ha il pregio di supportare numerose infrastrutture di comunicazione. Ciò nonostante, la funzionalità che offre l’interfacciamento automatico è applicabile solamente ai core generati dal software a partire dalla specifica, i quali presentano un insieme di porte sempre identico. Poiché si il set di sagnali da gestire è conosciuto a priori, la creazione dell’interfaccia risulta essere un compito meccanico. 32 Capitolo 3 Infrastrutture di comunicazione Il software presentato in questo lavoro di tesi ha la finalità di interfacciare i core dati in input con il resto del sistema nel quale si troveranno ad operare. Per questo motivo prima di descrivere il framework vengono illustrate le infrastrutture di comunicazione che IPGen supporta ad oggi. La prima architettura che viene presentata utilizza il bus OPB (On-chip Peripheral Bus) assieme alle interfacce implementate da Xilinx per rendere più semplice l’interfacciamento dei core con il bus; queste sono due e si chiamano PSelect e IP-Core Interface (IPIF) nella versione OPB. Successivamente viene data una breve descrizione del bus Wishbone, il quale fornisce la stessa funzionalità del precedente, ma a differenza di questo non presenta delle interfacce già sviluppate da terzi per facilitarne l’uso. Esso fornisce infatti un protocollo di facile utilizzo, e una logica per gestire la comunicazione molto meno complessa da implementare. Sono state scelte queste particolari architetture in quanto, come visto nella sezione 1.1.4, OPB viene utilizzato nel flusso di Caronte, mentre Wishbone in quello di YaRA. Questo permette al software di essere adottato fin da subito come parte integrante di questi due flussi, mostrandone le potenzialità in un contesto che mira al maggiore automatismo ottenibile in fase di progettazione hardware. Nel capitolo 5 viene spiegato come IPGen possa essere ampliato al fine di contemplare sempre più infrastrutture. 33 CAPITOLO 3. INFRASTRUTTURE DI COMUNICAZIONE 3.1 IBM CoreConnect OPB La progettazione modulare dello hardware, sezione 1.1.1, permette di ottenere sistemi composti da tanti moduli ognuno con funzionalità proprie e ben distinte. Un sistema nel quale tutti questi componenti vengono integrati all’interno dello stesso chip viene chiamato System-on-Chip (SoC) [16]. In uno scenario come questo sorge l’esigenza di una infrastruttura di comunicazione che sia efficiente e permetta in modo relativamente semplice di connettere tra di loro componenti le cui funzionalità sono state sviluppate separatamente le une dalle altre. CoreConnect [17] è un’architettura realizzata da IBM composta da tre bus e dai loro protocolli di comunicazione, che ha lo scopo di fornire un’infrastruttura di comunicazione tra i vari moduli di un SoC. I tre bus che compongono l’architettura sono: • Processor Local Bus (PLB): bus a 64 bit pensato per dispositivi che necessitano di prestazioni elevate e bassa latenza, utilizzato prevalentemente per connessioni dirette al processore, che può essere PowerPC o Microblaze; • On-Chip Peripheral Bus (OPB): bus a 32 bit utilizzato per sgravare il PLB da una parte del traffico dati e per evitare colli di bottiglia nel sistema. Viene utilizzato comunemente per applicazioni con minori necessita prestazionali. • Device Control Register (DCR): bus sussidiario utile a ridurre il carico dei due bus precedenti. Il bus supportato da IPGen ed utilizzato in Caronte è OPB. Le caratteristiche fondamentali di questo bus sono [18, 19]: • Un protocollo completamente sincrono con 32 bit dedicati al bus dati e altrettanti per il bus indirizzi; • Dimensione del bus dinamica per permettere trasmissioni di dati inferiori a 32 bit (byte steering); • Un protocollo che consente l’invio di segmenti sequenziali di dati (burst); • Supporto per più di un OPB Master contemporaneamente. 34 CAPITOLO 3. INFRASTRUTTURE DI COMUNICAZIONE Figura 3.1: Set di segnali visti all’interfaccia con un componente slave In figura 3.1 è mostrato il set di segnali visti all’interfaccia di un componente di tipo slave, per i quali il framework garantisce l’interfacciamento. Si precisa che tali segnali sono da considerarsi attivi alti e, secondo la specifica del bus, devono essere campionati sul fronte di salita del clock. Vengono ora spiegate le funzionalità di questi segnali (tra parentesi quadre verrà indicata la larghezza in bit dei segnali). • Segnali in ingresso nel componente. – OPB_Clk [1]: segnale di clock; – OPB_Rst [1]: segnale di reset; – OPB_Abus [32]: bus indirizzi, contiene l’indirizzo del registro sul quale si vuole effettuare un’operazione di lettura/scrittura; l’indirizzo presente sul bus è da considerarsi valido solo se il segnale OPB_select è attivo; 35 CAPITOLO 3. INFRASTRUTTURE DI COMUNICAZIONE – OPB_BE [4]: segnale Byte Enable che permette la trasmissione di particolari byte di dati presi da un registro, quando non è necessaria la trasmissione dell’intero registro; – OPB_DBus [32]: bus dati in scrittura comandato dal master, il contenuto del bus rimande valido finché il master non riceve il segnale di acknowledgement di scrittura avvenuta con successo; – OPB_RNW [1]: segnale Read Not Write, ha lo scopo di indicare la direzione del trasferimento dati in corso; se il segnale è posto a 1 il Master sta compiendo un’operazione di lettura sullo slave, al contrario se è posto a 0 è in corso un’operazione di scrittura; – OPB_Select [1]: segnale di select, indica che vi è un trasferimento in corso lungo la linea dati; inoltre valida il contenuto dei segnali OPB_ABus, OPB_BE, OPB_RNW e OPB_seqAddr. Rimane attivo finchè il master non riceve un segnale di acknowledgement o di transfer retry; – OPB_seqAddr [1]: segnale di trasferimento sequenziale, indica allo slave che il trasferimento successivo avverrà nella stessa direzione e sull’indirizzo successivo all’attuale. Questo segnale non implica l’utilizzo del protocollo “burst”. Questa modalità di comunicazione, che non verrà approfondita in questo testo, è utile per l’invio in cicli di clock successivi di uno stesso dato più grosso della capacità del bus e quindi spezzato in pacchetti più piccoli. • Segnali uscenti dal componente (il prefisso “<Sln>_” indica l’appartenenza del segnale ad un generico slave): – <Sln>_DBus [32]: bus dati in lettura comandato dallo Slave, il contenuto rimane valido finchè il segnale <Sln>_xferAck non viene sollevato e riportato a 0; – <Sln>_xferAck [1]: segnale OPB Transfer Acknowledge, sollevato dallo slave per indicare il completamento di un’operazione di comunicazione tra il master e lo slave. Esso rimane attivo per un ciclo di 36 CAPITOLO 3. INFRASTRUTTURE DI COMUNICAZIONE clock per ogni operazione da notificare. Nel caso di un’operazione di scrittura significa che lo slave ha correttamente accettato il dato, nel caso di lettura significa che lo slave ha posto sul bus dati in uscita il dato richiesto dal master. Inoltre questo segnale deve essere attivato non oltre 16 cicli di clock dal momento in cui OPB_select viene posto a 1, altrimenti questo ritardo verrà interpretato come timeout; – <Sln>_retry [1]: segnale di retry, viene utilizzato per indicare una situazione in cui lo slave temporaneamente non può portare a termine un’operazione I/O richiesta dal master. È un segnale molto utile nella prevenzione di possibili deadlock nell’arbitraggio del bus; – <Sln>_toutSup [1]: segnale con il quale viene indicata l’impossibilità di effettuare un’operazione richiesta dal master prima dello scadere dei 16 cicli di clock dall’attivazione di OPB_select. Il segnale deve rimanere a 1 finchè l’operazione prorogata non viene completata; – <Sln>_errAck [1]: segnale utilizzato per notificare al master un errore di lettura o scrittura. Oltre ai segnali elencati, possono essere utilizzati dei segnali opzionali che dipendono dalla funzionalità specifica del modulo, un esempio di tali segnali sono quelli di interrupt. 3.1.1 OPB PSelect La prima interfaccia implementata da Xilinx che viene descritta è OPB PSelect. Essa consiste in un unico file (pselect.vhd) che svolge la sola funzione di Address Decoding Logic, ovvero gestisce semplicemente la codifica degli indirizzi corrispondenti ai registri del componente dove il Master del bus andrà a leggere o scrivere. Per questo motivo viene utilizzata assieme a core che non hanno esigenze particolari quali gestione di interrupt e di registri con dimensione differente da 32 bit. Si spiega il comportamento del’interfaccia PSelect analizzando i suoi generic, ovvero le costanti che possono essere impostate dal progettista secondo le esi37 CAPITOLO 3. INFRASTRUTTURE DI COMUNICAZIONE genze della funzionalità del core, e i suoi segnali di I/O. I generic di pselect.vhd sono: • C_AB: generic di tipo intero che identifica il numero dei bit più significativi del bus indirizzi; questi bit sono identificativi del modulo slave mentre i rimanenti corrispondono ai registri presenti all’interno di uno slave. Da ciò si deduce che aumentando il numero dei bit significativi si diminuisce lo spazio da dedicare ai registri interni. • C_AW: generic di tipo intero corrispondente alla larghezza dal bus indirizzi in numero di bit. Questo valore per OPB deve essere sempre uguale a 32; • C_BAR: generic di tipo std_logic_vector che indica il base address del componente, se i bit dell’indirizzo chiamato dal master identificati dal C_AB corrispondono ai bit del C_BAR, anch’essi identificati dal C_AB, allora significa che il master ha chiamato proprio il componente al quale la pselect appartiene, in questo caso viene posto ad 1 il segnale PS. Di seguito vengono descritti i segnali: • A: segnale di input avente la stessa larghezza del bus indirizzi (C_AW), questo segnale dovrà essere connesso al segnale OPB_ABus; • Avalid: segnale in input da un bit che dovrà essere connesso con il segnale OPB_select; • PS: segnale in output da un bit chiamato Peripheral Select, per la descrizione vedere il generico C_BAR. Se il componente da interfacciare richiede una maggiore varietà e complessità di servizi offerti, questa logica di decodifica indirizzi risulterà scomoda ed incompleta, pertanto si dovrà ricorrere OPB IPIF, descritta nella prossima sezione. 38 CAPITOLO 3. INFRASTRUTTURE DI COMUNICAZIONE Figura 3.2: Schema dei servizi e dei segnali IPIF 3.1.2 OPB IPIF Xilinx, oltre a PSelect, ha sviluppato OPB IPIF (IP-Core Interface) [20], un’interfaccia più completa che offre una codifica degli indirizzi maggiormente comoda e funzionale, e molti servizi aggiuntivi che PSelect non fornisce. In figura 3.2 si può vedere uno schema che identifica i vari servizi di cui IPIF è composta, oltre ai suoi vari segnali sia lato core che lato bus. Mediante l’uso dei generic è data possibilità allo sviluppatore di attivare o disattivare una parte di questi servizi a seconda delle necessità dell’IP-Core che si vuole interfacciare; in questo caso il valore di tali generic è assegnato in modo automatico da parte di IPGen. La parte dell’interfaccia che rappresenta l’insieme dei segnali direttamente connessi al core viene chiamata IP-Core InterConnect (IPIC). 39 CAPITOLO 3. INFRASTRUTTURE DI COMUNICAZIONE 3.1.2.1 I Servizi Prima di analizzare in dettaglio il meccanismo di codifica degli indirizzi ed il significato dei vari segnali IPIC, viene fornita una panoramica dei vari moduli che compongono l’interfaccia. L’elemento base di IPIF è lo Slave Attachment; esso è infatti il responsabile della mediazione diretta tra il bus e IPIC, gestisce il protocollo di comunicazione con OPB e fornisce le porte di I/O per tutti i segnali indispensabili alla comunicazione con il bus. Un servizio opzionale interno allo Slave Attachment è il Burst Support, il quale permette di utilizzare una modalità di trasferimento dati che consente velocità elevate con accesso a indirizzi sequenziali. Un secondo componente è il Byte Steering, che permette la trasmissione in entrambe le direzioni di dati con una larghezza inferiore di quella del bus (32 bit). Grazie a questo componente possono essere letti o scritti solo alcuni byte dei registri interessati grazie alla sua capacità di instradare questi blocchi nelle corrette byte lanes del bus. Oltre a questi due servizi, presenti in ogni configurazione di IPIF, vi sono i servizi opzionali. Fa parte di questo gruppo il servizio Hardware/Software Reset. Esso è aggiuntivo rispetto al normale evento di reset sollevato quando il segnale OPB_Rst viene reso attivo, infatti permette un reset dell’IP-Core eseguito dal processore via software, scrivendo un particolare valore chiave all’interno del registro dedicato a questo particolare servizio. Inoltre H/S Reset è accompagnato dall’istanziazione di un particolare registro chiamato MIR (Module Information Register) contenente le informazioni indicanti la configurazione di default del modulo; tramite questo registro l’IP-Core può essere riportato in uno stato di partenza “sicuro” prestabilito dal progettista. Un’altra funzionalità che può essere attivata riguarda la gestione degli interrupt. L’Interrupt Service Controller mette a disposizione un componente che è in grado di ricevere le varie richieste di interrupt, sia da parte del core che dall’interfaccia stessa, e di collassarle in un unico segnale che verrà portato a OPB. Infine vi sono i servizi di Write FIFO e Read FIFO. Questi vengono utilizzati 40 CAPITOLO 3. INFRASTRUTTURE DI COMUNICAZIONE quando l’IP-Core necessita di “bufferizzare” i dati, e possono essere attivati indipendentemente l’uno dall’altro a seconda delle direzioni (lettura/scrittura) nelle quali vi è la necessità di un buffer. IPGen è in grado di sfruttare l’address decoding di IPIF insieme a tutte le sue funzioni di base; tra i possibili sviluppi futuri del software vi è il pieno supporto ai restanti servizi di IPIF. Si noti comunque che alcuni di questi servizi vengono implementati automaticamente da porzioni di logica generate dal software. Per esempio esso è in grado di gestire segnali con dimensioni maggiori di 32 bit tramite un particolare protocollo descritto dettagliatamente nella sezione 5.3.5. Il motivo di queste scelte è da ricercare nel fatto che spesso i servizi messi a disposizione da IPIF comportano un overhead non trascurabile e la gestione di essi risulta in molti casi molto macchinosa. 3.1.2.2 Decodifica degli indirizzi IPIF gode di un sistema di decodifica degli indirizzi che rende molto agevole l’identificazione dei vari registri all’interno di un core per operazioni di lettura e scrittura. Poiché l’interfaccia deve essere flessibile e potersi integrare a qualsiasi tipo di core, questa decodifica avviene raggruppando i vari registri interni al core in Address Ranges ognuno dei quali ha delle proprietà ben definite che coinvolgono tutti i registri appartenenti ad uno stesso range. IPIF è in grado di trattare adeguatamente i diversi “range” poiché questi vengono definiti in apposite Address Range Definitions. Queste vengono dichiarate all’interno di un set di cinque array di generic caratterizzati dal prefisso “C_ARD”: • C_ARD_ID_ARRAY: array che associa un numero intero di identificazione ad ogni address range; • C_ARD_ADDR_RANGE_ARRAY: in questo array vengono dichiarati, a coppie, il base address e l’high address di ogni address range; • C_ARD_DWIDTH_ARRAY: qui viene specificata la dimensione dei singoli registri appartenenti allo stesso address range, ovviamente tutti i registri di uno stesso address range avranno la stessa dimensione. Grazie a 41 CAPITOLO 3. INFRASTRUTTURE DI COMUNICAZIONE queste informazioni IPIF sarà in grado di operare byte steering su registri di dimensione inferiore a quella del bus OPB; • C_ARD_NUM_CE_ARRAY: la codifica di indirizzi di IPIF permette di assegnare un Chip Enable (CE) per ogni registro di un address range. Questo rappresenta una caratteristica molto utile nell’interfacciamento di un core in quanto, come descritto nel prossimo paragrafo, permette una gestione dei registri più semplice e intuitiva; • C_ARD_DEPENDENT_PROPS_ARRAY: in questo array vengono dichiarate proprietà dei registri che dipendo dal particolare tipo di address range di appartenenza. È importante sottolineare che l’impostazione di questi parametri all’interno di IPIF viene svolta in automatico da IPGen. Il risultato di questa decodifica viene propagato al core attraverso alcuni dei segnali uscenti da IPIC, che viene descritto nel paragrafo seguente. 3.1.2.3 IPIC IPIC è la parte dell’interfaccia rivolta verso il core; attraverso i suoi molteplici segnali permette al core di comunicare con l’interfaccia e quindi con il bus. Di seguito sono mostrati brevemente i segnali più importanti. • Segnali in uscita da IPIC: – Bus2IP_Clk: segnale che trasmette il clock di sistema dal bus al core; – Bus2IP_Reset: segnale che trasmette al core il segnale di reset di sistema; – Bus2IP_Data: bus dati in scrittura sui registri del core, la grandezza è variabile e dipende da come è stata impostata l’interfaccia; – Bus2IP_Addr: di grandezza 32 bit, è una copia esatta dell’indirizzo che arriva ad IPIF tramite il bus indirizzi di OPB; 42 CAPITOLO 3. INFRASTRUTTURE DI COMUNICAZIONE – Bus2IP_RNW: questo segnale identifica se un’operazione è di scrittura (0) o di lettura (1); – Bus2IP_CS: la grandezza di questo segnale dipende dal numero di voci presenti nell’array di generic C_ARD_ID_ARRAY ed ha la funzione di abilitare un address range per permettere di effettuare operazioni su di esso. – Bus2IP_RdCE: la grandezza in questo caso rappresenta il numero totale di registri di tutto il dispositivo; vi sarà al più un 1 e rappresenta il registro sul quale verrà effettuata l’operazione di lettura; – Bus2IP_WrCE: identico al precedente, utilizzato per le operazioni di scrittura; • Segnali in ingresso ad IPIC: – IP2Bus_IntrEvent: segnale con grandezza dipendente dal numero di interrupt che sono stati dichiarati, tramite un apposito generic, e che l’Interrupt Service Controller è stato eventualmente predisposto a gestire; – IP2Bus_Data: bus dati in lettura dei registri del core, la dimensione viene fissata a priori nel momento in cui viene configurata l’interfaccia; – IP2Bus_Ack: segnale di acknowledge, per una transazione di scrittura, il dato viene considerato correttamente scritto quando il clock è sul fronte di salita e questo segnale è attivo; – IP2Bus_Retry: questo segnale è reso attivo quando l’IP-Core non è stato in grado di completare un’operazione e chiede che essa venga riproposta da parte del Master; – IP2Bus_Error: segnale che, se posto ad 1, indica un errore verificatosi durante l’operazione corrente; 43 CAPITOLO 3. INFRASTRUTTURE DI COMUNICAZIONE Oltre ai segnali sopra elencati vi sono set di segnali per la gestione dei registri Read FIFO e Write FIFO e per il supporto del protocollo di Burst, questi segnali ed i relativi servizi non sono attualmente supportati da IPGen e rientrano nei possibili sviluppi futuri del tool. 3.1.2.4 Modalità di utilizzo IPIF, ed in particolare la sua logica di decodifica degli indirizzi, può essere sfruttata dal progettista seguendo due modelli o modalità di utilizzo. Il primo di questi modelli si chiama Register Model. Il progettista opta per questo modo d’uso quando vuole utilizzare appieno la logica di decodifica offerta dall’interfaccia e prevede, come descritto sopra, l’assegnamento ad ogni registro interno al core di un bit di Chip Enable (CE) che lo identificherà univocamente. In questo caso le operazioni sul core vengono pilotate dai segnali Bus2IP_CE, Bus2IP_RdCE e Bus2IP_WrCE a seconda della tipologia di registro utilizzato in un dato momento. Vi possono essere registri di sola lettura, di sola scrittura o entrambe. IPGen è stato programmato per fare uso di questa modalità per la gestione dei registri. La seconda modalità si chiama Memory Model o SRAM Model. In questo caso si fa uso della codifica tramite Chip Select (CS); l’informazione data da tale segnale, che identifica gli address range e non più i singoli registri, deve essere accompagnata dall’informazione sul tipo di operazione da effettuare (lettura/scrittura) e dai bit di indirizzo per identificare il punto nell’address range dove effettuare l’operazione. Questo secondo modello è più di “basso livello” e, pur avendo il pregio di lasciare più libertà d’azione nello spazio degli indirizzi, si presta meno ad essere automatizzato rispetto al precedente, quindi il suo utilizzo nel framework è stato scartato prediligendo il register model. 3.2 Wishbone Viene analizzata ora il secondo tipo di infrastruttura, costituita dal bus Wishbone e dai suoi protocolli [21]. Questo bus è stato implementato da Silicore per fornire un canale di comunicazione ai vari moduli di un SoC, e così come il precedente ha 44 CAPITOLO 3. INFRASTRUTTURE DI COMUNICAZIONE Figura 3.3: Le interfacce Wishbone lato master e lato slave il grande pregio di essere di dominio pubblico e di poter quindi essere utilizzato nel design di circuiti integrati senza l’obbligo di pagarne la licenza d’uso. Un altro punto di forza di Wishbone sta nella sua semplicità d’uso, infatti non sono state sviluppate delle interfacce dedicate, come per OPB, proprio perché non ve ne è la necessità. E’ stato definito da Silicore uno standard per l’utilizzo di questo bus che descrive in dettaglio il modo in cui i componenti devono essere interfacciati, il protocollo per lo scambio di informazioni tra master e slave, e quattro tipologie di modalità di interconnessione tra master e slave. Vengono dapprima descritti i segnali di interfaccia del master e dello slave e a seguire le varie tipologie di connessione. 3.2.1 Le interfacce Vi sono due tipologie di interfaccia, una per il componente master ed una per lo slave, queste devono essere adottate da tutti i componenti del sistema che fa uso di Wishbone. Le due interfacce sono molto simili la tra di loro, dato che si differenziano solamente per la direzione dei segnali che le compongono. In figura 3.3 sono indicati i vari segnali; con il prefisso “_I” vengono indicati i segnali entranti, mentre con “_O” quelli uscenti. Di seguito vengono descritti questi segnali dal punto di vista del master: 45 CAPITOLO 3. INFRASTRUTTURE DI COMUNICAZIONE • RST_I: segnale di reset del sistema, se sollevato indica al componente di ritornare al suo stato iniziale; • CLK_I: clock di sistema; • ADR_O: rappresenta il bus indirizzi di Wishbone; può avere dimensione variabile fino ad un massimo di 32 bit e viene utilizzato dal master per indicare al sistema il registro di un particolare slave; • DAT_I: bus dati da 32 bit, in ingresso; • DAT_O: bus dati da 32 bit, in uscita; • WE_O: segnale Write Enable, indica se l’operazione corrente è di scrittura o di lettura. Segnale alto indica scrittura da parte del master su un registro di uno slave, mentre segnale basso indica lettura da registro. • SEL_O: indica quali dei byte che compongono il bus dati sono da considerarsi validi. Poiché il bus dati ha grandezza 4 byte, questo segnale è costituito da 4 bit, uno per ogni byte selezionabile; • STB_O: segnale sollevato indica che tutti i segnali di controllo sono pronti e stabili. Da questo momento può iniziare una fase di lettura o scrittura; • ACK_I: indica la fine di un processo completato correttamente da parte dello slave; • ERR_I: indica un malfunzionamento dello slave con conseguente esito negativo di un’operazione; • RTY_I: se posto ad 1 lo slave non è temporaneamente disponibile, il master dovrà ricontattarlo in un secondo momento; • CYC_O: indica l’inizio di un ciclo di controllo da parte del master. Non viene fornito l’elenco dei segnali slave poiché è identico a meno dei versi di I/O. Fanno eccezione a questa regola i segnali di clock e reset, essendo entranti per entrambe le interfacce. 46 CAPITOLO 3. INFRASTRUTTURE DI COMUNICAZIONE 3.2.2 Protocollo di comunicazione I segnali sopra citati vengo utilizzati seguendo un protocollo di comunicazione, di questo protocollo vengono mostrate le parti fondamentali, ovvero i cicli di lettura da slave e scrittura su slave. • Ciclo di lettura: 1. Il master pone ad 1 il segnale CYC_O, in questo modo prende il controllo del bus; 2. Pone sul bus indirizzi ADR_O l’indirizzo dello slave interessato dal ciclo di lettura, contemporaneamente pone a 0 il segnale WE_O; 3. Solleva STB_O ad indicare che tutti i segnali sono correttamente impostati e stabili, l’operazione di lettura può avere inizio; 4. Lo slave indirizzato precedentemente controlla che i segnali CYC_I e STB_I siano alti, scrive sul bus dati in uscita DAT_O il dato richiesto, che verrà letto dal master dal proprio ingresso DAT_I; 5. Lo slave innalza ACK_O; 6. Quando il master rileva su ACK_I il segnale di acknowledgement durante il seguente fronte di salita del clock legge il dato presente su DAT_I; 7. Il master abbassa CYC_O e STB_O; 8. Quando lo slave rileva il passaggio a 0 dei segnali abbassati al punto precedente, pone il proprio ACK_O a 0. • Ciclo di scrittura: 1. Il master pone ad 1 il segnale CYC_O, in questo modo prende il controllo del bus; 2. Pone sul bus indirizzi ADR_O l’indirizzo dello slave interessato dal ciclo di scrittura, contemporaneamente pone a 1 il segnale WE_O e scrive il dato da trasmettere su DAT_O; 47 CAPITOLO 3. INFRASTRUTTURE DI COMUNICAZIONE Figura 3.4: Schema della connessione “Data Flow” 3. Solleva STB_O ad indicare che tutti i segnali sono correttamente impostati e stabili, l’operazione di scrittura può avere inizio; 4. Lo slave indirizzato aspetta che i segnali CYC_I e STB_I vengano alzati, successivamente legge il dato su DAT_I; 5. A questo punto lo slave pone ACK_O ad 1; 6. Riconosciuto l’ack, il master tiene il dato sul bus dati fino al successivo fronte di salita del clock; 7. Il master abbassa CYC_O e STB_O; 8. Quando lo slave rileva il passaggio a 0 dei segnali CYC_O e STB_O, abbassa il proprio segnale di ack. 3.2.3 Tipi di connessione I vari componenti di un SoC possono essere connessi, tramite Wishbone, seguendo quattro modalità di connessione. La scelta tra le varie tipologie dipende dalla complessità del sistema e dagli IP-Core presenti al suo interno. Segue una breve descrizione di questi tipi di connessione in ordine di complessità. • Point to Point: questa è la connessione più semplice e consiste in un collegamento punto a punto tra un solo master con un solo slave. La semplicità di questo sistema fa si che non sia richiesta nè un logica di decodifica degli indirizzi nè un meccanismo di collision detection sul bus. 48 CAPITOLO 3. INFRASTRUTTURE DI COMUNICAZIONE Figura 3.5: Schema della connessione “Shared Bus Interconnection” • Data Flow: sfrutta il principio della connessione punto a punto precedentemente illustrato, ma in questo caso è consentito collegare una serie di IP-Core contenenti una coppia Master-Slave in cascata tra di loro. Questa configurazione, mostrata in figura 3.4, è molto utile in applicazioni dove un flusso di informazioni deve subire molte elaborazioni successive, ognuna delle quali necessita dell’output della precedente. In questo caso vi è la necessità di una semplice logica di decodifica di indirizzi per indirizzare i dati al componente corretto. • Shared Bus Interconnection: questo scenario rispecchia la configurazione più tipica di interconnessione, ovvero il bus condiviso tra i vari componenti (figura 3.5), ovviamente è obbligatoria la presenza di una logica di decodifica di indirizzi e di un sistema di collision detection. • Crossbar Interconnection: si tratta della tipologia più complessa di connessione. Anziché avere un unico bus condiviso, permette la comunicazione tra differenti coppie master-slave contemporaneamente tramite una rete di 49 CAPITOLO 3. INFRASTRUTTURE DI COMUNICAZIONE Figura 3.6: Schema della connessione “Crossbar Interconnection” connessioni tra componenti e degli switch che fisicamente allacciano e sganciano le linee di comunicazione tra di loro. Una possibile combinazione viene mostrata in figura 3.6. Tra le varie alternative proposte, il framework fa uso della Shared Bus Interconnection. Per sfruttare la condivisione del bus, l’interfaccia generata da IPGen contiene una logica di decodifica degli indirizzi che permette di rilevare la volontà del master di effettuare un’operazione sull’IP-Core, oltre ad identificarne un particolare registro. L’architettura che fa uso di questo tipo di connessione deve avere uno spazio di indirizzamento ad 8 bit di cui: i primi 3 da sinistra costituiscono il Core Address mentre i restanti 5 compongono il Register Address. Da ciò si deduce che potranno essere istanziati un massimo di 8 core nel sistema e ciascun core avrà non più di 32 registri. La logica di decodifica confronta il Core Address con i 3 bit più significativi del base address dell’IP-Core, se questi coincidono significa che il master vuole interagire con l’IP-Core. A questo punto viene identificato il corretto registro confrontando i bit del Register Address con gli indirizzi assegnati a ciascuna porta del core. 50 Capitolo 4 Metodologia Questo capitolo ha la finalità di fornire una descrizione di IPGen dal punto di vista metodologico. Nel paragrafo seguente vengono spiegate le motivazioni che hanno spinto alla creazione di un software per l’interfacciamento automatico, assieme agli obiettivi preposti alla sua realizzazione. Successivamente è mostrato come IPGen si possa collocare in un contesto più ampio, costituito dal workflow di Caronte (sezione 1.1.4). Dopo aver descritto in dettaglio la struttura degli IPCore generati, illustrandone i vari componenti, viene descritto il procedimento metodico che il programma compie per passare dall’input dell’utente al risultato finale. Il capitolo si conclude con un confronto tra la metodologia proposta da questo lavoro e quelle adottate dagli approcci precedenti (sezione 2) al problema dell’interfacciamento automatico, evidenziando differenze, analogie e possibili integrazioni. 4.1 Motivazioni Il processo di realizzazione di un sistema hardware richiede lo sviluppo di molti prototipi prima di poter arrivare ad un risultato soddisfacente e quindi ad un sistema che può essere messo sul mercato. La fase di progettazione quindi può far si che il time-to-market del prodotto si allunghi notevolmente, con un conseguente ritardo per chi lo dovrà commercializzare o utilizzare per attività di ricerca. Buona 51 CAPITOLO 4. METODOLOGIA parte del tempo impiegato nello sviluppo di un prototipo deriva dal problema di far comunicare i componenti costituenti il sistema complessivo tra di loro mediante un’opportuna infrastruttura di comunicazione, per esempio un bus. L’interfacciamento dei moduli è una fase molto ripetitiva e poco creativa per il progettista, il quale ha come principale obiettivo lo sviluppo di un componente in quanto risolutore di un problema o comunque esecutore di una preposta funzionalità. Da questo punto di vista l’interfacciamento risulta essere un problema con il quale ci si deve per forza confrontare, ma costituisce un collo di bottiglia nel processo di sviluppo di un sistema hardware. Essendo esso però molto ripetitivo è un’operazione che può essere automatizzata. Ogni singolo componente di un’architettura è un IP-Core nel quale, come visto nella sezione 1.1.2.2, possono essere ben distinte la parte funzionale, il core, e la parte costituente l’interfaccia verso il resto del sistema. Se ci fosse un meccanismo che, dato il core, riuscisse a creare l’interfaccia in modo automatico, il tempo speso da parte del progettista nell’implementazione di questa verrebbe azzerato. Questo è l’intento di IPGen. Grazie a questo strumento il progettista può implementare la sua funzionalità senza curarsi della tipologia di architettura nella quale andrà ad inserirsi il suo componente finale. In questo modo viene aumentato il grado di riusabilità di un core, in quanto esso potrà essere inserito e riutilizzato in una molteplicità di sistemi differenti. Esso rimarrà sempre inalterato, sarà la parte relativa all’interfacciamento ad essere rigenerata ogni volta in modo automatico dal software, in base alle necessità e alle scelte progettuali riguardanti l’architettura ospite. Tutto questo senza perdite di tempo da parte del progettista. 4.1.1 IPGen nel flusso di Caronte IPGen ha un ruolo fondamentale nel flusso di Caronte, essendo necessario durante la fase di generazione delle Foto Statiche (HW-SSP, vedere sezione 1.1.4). Questo processo può essere completamente automatizzato tramite due fasi, descritte in figura 4.1: 52 CAPITOLO 4. METODOLOGIA • IPGen Phase. In questa fase viene utilizzato IPGen, che prende i singoli core e li trasforma in IP-Core aggiungendovi l’interfaccia opportuna e rendendoli quindi pronti ad essere inseriti in una Static Photo. • EDK System Creator Phase. EDK System Creator [22] è uno strumento sviluppato all’interno del Politecnico di Milano che permette di inserire un IP-Core in un’architettura compatibile con EDK in modo automatico. Questo programma riceve in ingresso l’IP-Core da inserire e il sistema nel quale verrà integrato, assieme ad un file di configurazione contenente gli indirizzi fisici che determinano l’allocazione dell’IP-Core sulla FPGA. Fondamentalmente EDK System Creator esegue due operazioni: – importa l’IP-Core settando tutti i parametri in modo tale che esso possa essere trattato come un componente EDK; – inserisce l’IP-Core nell’architettura ricevuta in input dal programma. In questo caso il sistema ospite è costituito da un’architettura come quella descritta nella sezione 1.1.4.1, le cui black box sono completamente vuote, denominato EDK general system. Grazie ad un opportuno file di configurazione contenente le informazioni sulle varie HW-SSP da generare, è possibile rendere la creazione di queste ultime completamente automatica. Come si può notare l’uso congiunto di IPGen e EDK System Creator permette di ottenere un rilevante risparmio di tempo grazie ad un elevato automatismo dell’intero processo. 4.2 Struttura dell’IP-Core generato Nonostante la struttura di un IP-Core generato da IPGen cambi a seconda del tipo di bus con il quale esso viene interfacciato, il principio base comune è quello di incapsulare il core dato in input dall’utente all’interno di un componente più grande che lo istanzi assieme alla logica di interfacciamento. 53 CAPITOLO 4. METODOLOGIA Figura 4.1: Flusso di creazione delle HW-SSP Come si può osservare da un confronto tra le figure 4.2 e 4.3, poiché OPB richiede l’istanziazione di un’interfaccia che è un vero e proprio componente diverso rispetto al core di partenza, vi saranno due livelli di incapsulamento nell’IPCore generato in output, mentre per quanto riguarda il bus Wishbone ne avremo soltanto uno. Viene ora fornita una descrizione di queste due diverse strutture partendo dal livello più basso fino ad arrivare al top level. 4.2.1 Core Esso è l’input fornito dall’utente ad IPGen e rappresenta il cuore del componente, ovvero la funzionalità che esso svolge. Nel core si trovano i registri e la logica atti ad espletare la funzionalità propria del componente, senza riferimenti all’infrastruttura di comunicazione. Proprio per questo esso è la parte riusabile dell’IP-Core in quanto è indipendente dal sistema nel quale verrà inserito. Per quanto detto non vi è alcuna differenza strutturale tra i core in figura 4.2 e 4.3. 54 CAPITOLO 4. METODOLOGIA Figura 4.2: Struttura di un core interfacciato con OPB Figura 4.3: Struttura di un core interfacciato con Wishbone 55 CAPITOLO 4. METODOLOGIA 4.2.2 Stub Questo componente viene generato separatamente dalla struttura dell’IP-Core solo in caso di interfacciamento OPB; nel caso Wishbone le funzionalità presenti in questo livello vengono integrate all’interno del top level. La necessità di questo componente aggiuntivo nasce dal fatto che la connessione con OPB richiede l’uso di un’interfaccia complessa che è un componente a parte rispetto alla logica contenuta nel core. Lo Stub permette al core e all’interfaccia di comunicare correttamente; in particolare esso istanzia il core e fornisce la logica che consente di sfruttare i segnali IPIC (vedi sezione 3.1.2.3) per effettuare le procedure di scrittura, lettura, sollevamento di interrupt ed elaborazione dei segnali di acknowledgement. Esso si comporta come un demultiplexer per i segnali provenienti dal sistema verso il core e come un multiplexer per i segnali diretti nel verso opposto; questo comportamento viene ottenuto mappando opportunamente le linee dati in ingresso/uscita sulle porte del core utilizzando i segnali derivanti dalla decodifica degli indirizzi attuata dall’interfaccia, ottenendo in questo modo un corretto indirizzamento nei vari registri. 4.2.3 IP-Core Con il termine IP-Core si intende il top level di un componente interfacciato; esso rappresenta ciò che viene visto da parte del sistema. Per i motivi visti in precedenza, è diverso a seconda del bus adottato dal sistema nel quale dovrà essere inserito. Nel caso di bus OPB è infatti necessaria l’istanziazione di una delle due interfacce CoreConnect descritte nella sezione 3.1 e dello stub. I segnali provenienti dal bus sono in contatto con una di queste due interfacce, che tramite i loro servizi forniscono allo Stub le informazioni necessarie a compiere tutte le operazioni sul core. Nell’interfacciamento con bus Wishbone invece il top level integra tutte le funzioni che nel caso precedente sono incluse nello Stub; questo è possibile poiché, 56 CAPITOLO 4. METODOLOGIA come descritto nella sezione 3.2.3, questo tipo di collegamento non ha bisogno di una logica di decodifica particolarmente elaborata e viene quindi integrata direttamente da IPGen senza necessità di componenti esterni. In definitiva, la descrizione di un IP-Core in questo secondo tipo di interfacciamento conterrà la definizione di tutte le porte che si rivolgono verso il sistema, l’istanziazione del core dato in input dall’utente, la logica di decodifica degli indirizzi necessaria ad attuare i processi di lettura e scrittura, e il mappaggio dei segnali tra core e bus. 4.3 Workflow di IPGen Per generare un IP-Core partendo dalla funzionalità descrittà nel core, IPGen percorre un workflow diviso in due fasi principali; esse sono sequenziali e sono (in ordine di esecuzione) la fase di lettura e quella di scrittura. La prima di esse si occupa di ricevere e scansionare il core dato in input dall’utente, mentre la seconda si occupa della generazione dei file che descrivono l’IP-Core in output. Come verrà visto più in dettaglio nella sezione 5.1, ognuna di queste fasi viene svolta da un componente del framework dedicato: avremo quindi il reader per la fase di lettura ed il writer per quella di scrittura. 4.3.1 Lettura In figura 4.4 è schematizzato il flusso della fase di lettura eseguita dal “reader” all’inizio dell’esecuzione di IPGen. Questo componente, in base al percorso del file VHDL dato in input, dopo averne verificato l’esistenza, dà inizio ad una procedura chiamata clean phase. Essa viene eseguita da un modulo sussidiario al reader chiamato cleaner, il quale legge la dichiarazione della entity del core e la pulisce da eventuali commenti VHDL che potrebbero generare problemi di parsing nella fase successiva. Il reader quindi prosegue il suo flusso lavorando sulla dichiarazione pulita precedentemente, effettuandone il parsing e, nel caso in cui riconosca una dichiarazione ben formata, crea la lista dei segnali descritti in essa. La stessa operazione viene eseguita nella sezione di dichiarazione dei generic, in cui viene creata una lista contenente tutte le informazioni relative ad essi. A questo 57 CAPITOLO 4. METODOLOGIA Figura 4.4: Flusso operativo del reader punto viene richiesto all’utente quale modalità di interfacciamento desidera per il suo core; avvenuta questa scelta il programma ha tutte le informazioni necessarie alla generazione dell’IP-Core. Se i passi descritti fino ad ora terminano senza errori, il reader passa il controllo del flusso di esecuzione e le liste precedentemente salvate al “writer” predisposto a generare l’interfaccia selezionata. 4.3.2 Scrittura Il tipo di writer che entra in funzione a questo punto dipende dal tipo di interfaccia selezionato dall’utente. Sono stati infatti implementati tre writer separati, ognuno dedicato alla generazione di una delle interfacce descritte. Pur essendo componenti a sé stanti, seguono lo stesso tipo di workflow. Il writer legge le informazioni elaborate nella fase precedente: il path del core, le liste dei segnali e dei generic, e il nome della entity specificata nel core; in base a questi dati crea un file 58 CAPITOLO 4. METODOLOGIA Figura 4.5: Flusso operativo del writer VHDL per ogni livello della struttura dell’IP-Core da generare così come è stata descritta in precedenza. Tutti i dettagli implementativi di questa fase vengono trattati nel capitolo 5. 4.4 Confronti con gli altri approcci Avendo mostrato la metodologia proposta da questa tesi, è possibile a questo punto effettuare dei confronti tra la nostra soluzione e quelle descritte nel capitolo 59 CAPITOLO 4. METODOLOGIA 2. Vengono presi inizialmente in considerazione i primi due approcci, cioè OCP Socket e IAL, come già detto molto simili tra loro. Il primo punto fondamentale da analizzare è l’intento che sta alla base delle diverse metodologie: si può dire che l’obiettivo primario di questi due approcci è il riutilizzo degli IP-Core, tramite il quale è possibile creare delle librerie di moduli utilizzabili in applicazioni e contesti architetturali differenti, con il risultato di velocizzare il flusso di lavoro per lo sviluppo di sistemi hardware e disporre di IP-Core solidi e funzionanti. Per quanto riguarda IPGen, l’obiettivo primario è invece quello di velocizzare la creazione degli IP-Core, soprattutto nell’ambito della riconfigurabilità dinamica. Il risultato che si spera di ottenere è quello di un flusso completamente automatizzato che, partendo da specifiche di alto livello, produca un sistema dinamicamente riconfigurabile passando tramite il workflow di Caronte (sezione 1.1.4). All’interno di questo processo IPGen è necessario per generare in un tempo brevissimo e in modo completamente automatizzato gli IP-Core necessari. In ogni caso, il software sviluppato è utilizzabile con ottimi risultati anche in un’ottica di riutilizzo degli IP-Core, in quanto esso è in grado di generare interfacce differenti per core che sono generali, non essendo dipendenti dall’architettura che li ospita. Dopo avere mostrato questa lieve discrepanza di intenti, si può notare che tutti questi approcci adottano un metodo che si basa sulla stessa considerazione: per ottenere un core che sia portabile su sistemi differenti è necessario stratificare la struttura dell’IP-Core. La stratificazione implica infatti un disaccoppiamento tra il core e l’architettura ospitante. Proprio in quest’ottica è possibile definire due tipologie di componenti: core-centric, i quali sono indipendenti dall’infrastruttura di comunicazione, e bus-centric, nei quali il collegamento con un particolare mezzo trasmissivo è integrato e spesso impossibile da isolare e sostituire. Ovviamente è la prima categoria di core che i diversi approcci utilizzano come punto di partenza per raggiungere i propri obiettivi. Da un punto di vista metodologico, si vede chiaramente come IPGen si collochi ad un livello architetturale differente rispetto a OCP e IAL. Infatti, mentre il primo si occupa di generare un IP-Core che opera mediante interfacce già esistenti e diffuse, i secondi propongono uno standard di interfaccia nuovo, da diffondere 60 CAPITOLO 4. METODOLOGIA il più possibile. Adottando però un’interfaccia comune, è necessario adottare dei convertitori logici che colleghino in modo opportuno i segnali di comunicazione dell’IP-Core con quelli molto spesso diversi del bus: tali componenti vengono chiamati bus wrapper. Si perviene quindi alla seguente considerazione: se per allacciare un IP-Core ad una particolare infrastruttura di comunicazione si rende obbligatorio nella nostra metodologia implementare un writer opportuno (se non già esistente), mentre negli altri due approcci è necessario scrivere un bus wrapper. Nonostante questa apparente equità, emerge a questo punto un dato a favore di IPGen: esso è infatti in grado di generare automaticamente lo stub, il quale contiene logica che non costituisce un overhead, in quanto comunque necessaria alla comunicazione con un bus; gli altri due approcci introducono invece un componente aggiuntivo, il bus wrapper, che appesantisce necessariamente l’IP-Core risultante. Quanto spiegato si può vedere chiaramente in figura 4.6. Inoltre la funzione svolta dallo stub deve essere implementata manualmente dallo sviluppatore adottando una della altre due metodologie, incrementando quindi il tempo di creazione del core. Figura 4.6: Confronto degli IP-Core risultanti utilizzando differenti approcci 61 CAPITOLO 4. METODOLOGIA Dato che, come è stato appena mostrato, IPGen e OCP/IAL operano a livelli differenti, si può addirittura considerare una potenziale integrazione tra i due differenti approcci: è infatti possibile pensare di creare un writer per IPGen che adotta, per esempio, il Socket OCP. Viene ora preso in considerazione EDK Create/Import Peripheral Wizard. Come già detto, quest’ultimo opera solamente con le interfacce OPB IPIF e PLB IPIF, in quanto è orientato ad interagire con EDK, che adotta i bus della famiglia CoreConnect. Questo strumento possiede dunque una ristretta area di applicazione. Inoltre, esso non si occupa in nessun modo dell’interfacciamento di core già esistenti con uno specifico bus, poiché l’unica funzione significativa che offre è quella di generare un template vuoto nel quale collocare manualmente ed in un secondo momento la descrizione VHDL della funzionalità. Quello che invece IPGen fa è generare l’interfaccia prendendo in ingresso il core, fornendo in uscita un prodotto completo. Anche in questo caso si può delineare in modo semplice un’integrazione tra le due metodologie: un core generato automaticamente dal framework con interfaccia OPB IPIF può essere importato in un sistema EDK tramite la funzionalità “import” fornita da EDK Create/Import Peripheral Wizard. Impulse CoDeveloper offre la possibilità di generare l’interfaccia per collegare il componente sintetizzato a una varietà di architetture molto vasta. In tal senso esso si dimostra uno strumento molto versatile. Ciò nonostante, esso lavora, come detto in precedenza, su moduli hardware che esso stesso genera, sintetizzandoli a partire da una specifica C di alto livello. In generale dunque, il valore di questo software non è da ricercare tanto nell’interfacciamento dei moduli, operazione che a questo punto risulta quasi meccanica e banale, quanto nella capacità di sintetizzare una specifica di alto livello in un sistema integrato hardware e software in un tempo breve e in modo semplice per l’utente. Ovviamente una limitazione ineliminabile di questa metodologia risiede nel fatto che l’utente deve apprendere l’utilizzo delle librerie ImpulseC per scrivere il codice della specifica. Volendo effettuare un confronto con IPGen, si può affermare che quest’ultimo è stato progettato tenendo conto di un core in ingresso che sia veramente “generico”, ossia non presenti necessariamente un determinato set di porte. Questo perché, pur essendo 62 CAPITOLO 4. METODOLOGIA stato pensato come componente integrante di un flusso che prende in ingresso una specifica di alto livello e genera un sistema dinamicamente riconfigurabile, esso mantiene comunque intatta la propria identità di software stand-alone. 63 Capitolo 5 Implementazione Questo capitolo è dedicato alla descrizione degli aspetti implementativi di IPGen, ossia il modo in cui la metodologia descritta nel capitolo 4 viene messa in pratica. Per una migliore comprensione, si è deciso di organizzare la trattazione in quattro sezioni. Si inizia con la descrizione della struttura del programma, ossia la presentazione del framework dal punto di vista dell’ingegneria del software. Successivamente è mostrato il funzionamento del software, cioè l’interazione con l’utente e i passi compiuti per generare il codice VHDL in uscita. Proprio quest’ultimo è l’argomento della sezione 5.3, nella quale si descrive in dettaglio l’output prodotto dal programma. L’ultima sezione presenta alcune linee guida da seguire per realizzare un core VHDL che sia interfacciabile con successo tramite IPGen. 5.1 Struttura del programma La struttura software di IPGen ricalca praticamente in tutto la metodologia sulla quale si fonda, descritta nel precedente capitolo. Il programma è stato implementato con il linguaggio C++. L’impiego di tale linguaggio ha permesso di conferire una struttura modulare al software, la quale è la chiave dell’espandibilità del software: sarà sufficiente aggiungere una nuova classe per espanderne la funzionalità. Viene quindi analizzata l’organizzazione delle classi che compongono IPGen, il cui class diagram è mostrato in figura 5.1. 65 CAPITOLO 5. IMPLEMENTAZIONE Figura 5.1: Class diagram di IPGen Il software possiede innanzitutto una classe Main, adibita all’inizializzazione del programma, alla creazione delle istanze di classi necessarie e alla chiamata dei relativi metodi. La classe Signal definisce il tipo “segnale”, indispensabile affinché il reader possa creare un lista contenente i segnali di input/output del core per potere poi implementare un’opportuna interfaccia. La classe possiede molti metodi qui di seguito elencati e brevemente spiegati: • in(). Restituisce “true” se il segnale costituisce un ingresso del core; • out(). Restituisce “true” se il segnale costituisce una uscita del core; • vector(). Restituisce “true” se il segnale è di tipo std_logic_vector; • name(). Restituisce il nome del segnale; • type(). Restituisce il tipo del segnale (std_logic oppure std_logic_vector); • direction(). Restituisce la direzione del segnale (“in” oppure “out”); 66 CAPITOLO 5. IMPLEMENTAZIONE • initial(). Nel caso in cui il segnale sia di tipo std_logic_vector, restituisce l’indice iniziale del vettore; • final(). Nel caso in cui il segnale sia di tipo std_logic_vector, restituisce l’indice finale del vettore; • length(). Nel caso in cui il segnale sia di tipo std_logic_vector, restituisce la lunghezza del vettore; • to(). Nel caso in cui il segnale sia di tipo std_logic_vector, restituisce la direzione di ordinamento del vettore (to oppure downto). Si potrebbe dire che alcuni tra i metodi elencati siano ridondanti in quanto sembrano fornire la stessa informazione. In effetti è così, ma l’apparente ridondanza è giustificata dalla diversità dell’ambito in cui tali metodi vengono invocati. Per esempio, se in una espressione condizionale è necessario verificare il fatto che il segnale sia un vettore, è molto più comodo utilizzare il metodo vector(), il quale restituisce una variabile booleana, che invocare il metodo type() ed effettuare un confronto sulla stringa ottenuta. Se invece è richiesto di scrivere sul file destinazione il tipo di segnale, sarà molto comodo invocare type() rispetto a verificare il tipo tramite il metodo vector(). Per quanto concerne la classe Generic, la quale descrive appunto il tipo “generic”, ossia un parametro della descrizione VHDL, tale oggetto possiede degli attributi che possono essere ottenuti invocando i seguenti metodi: • name(). Restituisce il nome del generic; • type(). Restituisce il tipo del generic; • def(). Restituisce, se assegnato, il valore di default del generic. Verranno ora presentate le classi che rappresentano il cuore dell’applicazione. La classe Reader costituisce l’oggetto adibito a leggere il file VHDL la cui locazione è fornita in ingresso e a riconoscerne sostanzialmente la dichiarazione di entity, costruendo una lista di segnali e una lista di generic. Sono presenti i seguenti metodi: 67 CAPITOLO 5. IMPLEMENTAZIONE • read(). Tramite questa funzione viene avviato il processo di lettura; • getList(). Restituisce la lista di Signal costruita dal metodo read(); • getListg(). Restituisce la lista di Generic costruita dal metodo read(); • getComponent(). Restituisce il nome del componente, cioè quello che appare nella dichiarazione di entity. La classe Writer rappresenta l’oggetto che effettua la creazione e la scrittura dei file VHDL in uscita. Come si vede nel diagramma, non vi è un’unica classe Writer: è infatti presente una classe per ogni interfaccia supportata dal framework. Al momento il programma è in grado di generare core con tre differenti tipi di interfaccia (vedere sezione 3): OPB PSelect, OPB IPIF e Wishbone. Ognuna di questa classi è figlia di un’unica classe astratta, denominata appunto Writer, in modo da mantenere una struttura identica e permettere in questo modo una maggiore espandibilità tramite l’aggiunta di Writer per nuove interfacce. Vi sono dunque tre classi: PSelect_Writer, IPIF_Writer e Wishbone_Writer, ognuna delle quali definisce per “override” il metodo write() della classe madre. Si noti invece che la classe Reader è unica, e non cambia passando da un tipo di interfaccia ad un altro, in quanto il processo di lettura del file in ingresso e di costruzione delle liste è identico qualunque sia il tipo di infrastruttura di comunicazione che si vuole adottare. In sintesi, se si vuole aggiungere supporto per una nuova interfaccia, è sufficiente scrivere un’opportuna classe _Writer e aggiungere la scelta di tale interfaccia come opzione all’interno della classe Main. L’ultima classe che si trova in figura è Cleaner. Essa fornisce due importanti metodi per la gestione dei file VHDL, che sono: • CleanComments(std:string Component). Questo metodo è necessario per pulire il file VHDL in ingresso da commenti che possono risultare molto fastidiosi in fase di lettura della entity. Per esempio tra due linee che contengono dichiarazioni di porte potrebbe trovarsi un commento: questo, se non identificato come tale ed eliminato, potrebbe essere interpretato come una dichiarazione di porta generando una palese situazione di errore. Il 68 CAPITOLO 5. IMPLEMENTAZIONE metodo CleanComment opera nel seguente modo: apre il file VHDL identificato dalla stringa in ingresso e ne scansiona la entity in cerca di commenti, producendo in uscita una stringa contenente la stessa dichiarazione di entity priva di eventuali commenti. Si noti che non è necessario fornire in uscita tutto il contenuto del file VHDL, in quanto la sezione che interessa al reader è solamente quella in cui si trovano la descrizione delle porte e degli eventuali generic. • CleanGenerics(). Anche questo metodo opera sul file VHDL che descrive il core. Si occupa dell’identificazione dei generic presenti e provvede a cancellarne l’eventuale valore di default. Questa operazione è necessaria poichè, se il valore di default venisse lasciato indicato nel core, esso genererebbe una discrepanza di valori con il dato impostato dall’esterno. È molto importante sottolineare il fatto che questo metodo non sovrascrive il file originale ma crea un ulteriore file contenente il codice senza i valori di dafault dei generic. Questo perché in tal modo il core fornito dall’utente viene preservato completamente intatto. Nella prossima sezione verrà descritto come queste classi e i relativi metodi vengono utilizzati nel flusso di lavoro di IPGen. 5.2 Utilizzo e funzionamento del framework IPGen non è dotato per il momento di interfaccia grafica (GUI), ed è quindi accessibile solamente da riga di comando. Il motivo per il quale non è stata data la priorità allo sviluppo di una GUI per il tool è semplice: esso è nato come componente del flusso di lavoro di Caronte e dunque viene invocato direttamente da tale framework, senza bisogno di una interfaccia grafica indipendente. L’implementazione di questa è comunque in cantiere per sviluppi futuri, in quanto l’utilità del framework si è dimostrata notevole anche al di fuori dell’ambito nativo, al punto che esso potrebbe essere visto come un software a sé stante. 69 CAPITOLO 5. IMPLEMENTAZIONE Il flusso di esecuzione del software è stato suddiviso per chiarezza in quattro fasi, che sono: • inizializzazione; • fase di lettura; • scelta dell’interfaccia e fase di scrittura; • terminazione. Ognuna di queste sarà descritta dettagliatamente nei prossimi paragrafi. 5.2.1 Inizializzazione Per invocare IPGen l’utente digita la stringa ./ipgen seguita dal percorso del file VHDL che descrive il core da interfacciare. Se tale percorso non viene fornito, o è inesistente, viene generato immediatamente un messaggio di errore. Se invece il file indicato esiste, viene creato un oggetto di tipo Reader e uno di tipo Cleaner, del quale viene invocato il metodo cleanComments, che apre il file in esame, e genera una stringa contenente la entity del core priva di eventuali commenti. Tale stringa viene poi passata al Reader invocando su di esso il metodo read. Si noti che a questo punto si è preferito utilizzare una stringa, piuttosto che un ulteriore file, soprattutto per motivi di efficienza: è infatti noto che un accesso alla RAM, possibile se si utilizza una stringa, è molto meno dispendioso che un accesso su disco, necessario in caso fosse stato utilizzato un file di passaggio. 5.2.2 Fase di lettura A questo punto è il Reader a controllare l’esecuzione: esso analizza la stringa in ingresso in cerca della parola chiave “entity”, che indica l’inizio delle dichiarazioni di interesse. Una volta trovata sonda la presenza di eventuali generic, verificando la presenza della parola chiave “generic”, a cui seguono le dichiarazioni di questi parametri. Se essa è presente il Reader procede riga per riga, inserendo in un’opportuna lista di oggetti di tipo Generic i parametri identificati, sicuro sul fatto che 70 CAPITOLO 5. IMPLEMENTAZIONE non possano esserci dei commenti che lo conducano a una condizione di errore. In un file VHDL i generic possono essere presenti o meno. Al contrario un core deve per forza possedere delle porte di input/output, e quindi il Reader deve trovare la parola chiave “port”, che precede la dichiarazione di tali segnali. Se questa non viene identificata il Reader si blocca e genera un messaggio di errore. Se invece il file è corretto le linee seguenti vengono scansionate e i segnali identificati vengono inseriti in una lista di oggetti di tipo Signal. Durante questo processo il Reader scrive a video il nome e le caratteristiche di tutte le porte identificate, offrendo così all’utente la possibilità di verificarne la correttezza. Una volta conclusa la lettura della stringa fornita dal Cleaner, il controllo passa di nuovo al main il quale stampa a video le dimensioni complessive (in byte) degli ingressi e delle uscite del core, invocando sul Reader i metodi getSpaceIn e getSpaceOut. In figura 5.2 è mostrato un esempio dell’output video di IPGen. In questo caso il core fornito è un addizionatore avente oltre ai segnali di clock, di interrupt e di reset, due operandi di 32 bit in ingresso e una uscita, sempre di 32 bit. 5.2.3 Scelta dell’interfaccia e fase di scrittura A questo punto dell’esecuzione viene chiesto all’utente di scegliere il tipo di interfaccia da integrare nell’IP-Core. Al momento il software supporta tre tipi di interfaccia: OPB PSelect, OPB IPIF e Wishbone. Nel caso particolare in cui venga scelta l’interfaccia verso il bus Wishbone, viene chiesto all’utente di indicare il numero di bit che vanno a costituire l’indirizzo che identifica l’IP-Core all’interno del sistema (vedere capitolo 3), corrispondente a tre nel caso di architettura YaRA. Una volta effettuata questa eventuale scelta, viene creata un’istanza del particolare writer in base alla preferenza dell’utente e su di esso si invoca il metodo write, che prende in ingresso il Reader e il Cleaner precedentemente creati. In questa sezione non verranno trattati singolarmente i tre tipi di Writer, in quanto il flusso di esecuzione è sostanzialmente lo stesso. Si può suddividere l’esecuzione del metodo write in tre fasi: inizializzazione, creazione dello stub e creazione dell’IP-Core. 71 CAPITOLO 5. IMPLEMENTAZIONE Figura 5.2: Esempio dell’output video di IPGen 72 CAPITOLO 5. IMPLEMENTAZIONE • Per “inizializzazione” si intendono una serie di operazioni che precedono la scrittura vera e propria: innanzitutto viene invocata la funzione cleanGenerics(). Essa analizza il file VHDL che descrive il core e produce in uscita un nuovo file, identico a quello letto ma privo dell’eventuale valore di default dei generic, qualora essi fossero presenti. Nella sezione 5.3.4 verrà spiegato nel dettaglio il motivo della necessità di tale operazione. Successivamente vengono caricate la lista di segnali e la lista di generic, invocando sul reader i metodi getList e getListg, le quali vengono salvate in due opportune variabili. L’ultima tra le operazioni di inizializzazione, necessaria se è stata selezionato l’interfaccimento con IPIF, è il calcolo della lunghezza del vettore di Chip Enable (CE, vedere sezione 3.1.2). • A questo punto tutto è pronto per iniziare la scrittura del file VHDL che descrive lo stub. Questo è composto da una struttura fissa, nella quale si innestano alcune parti variabili, che cambiano a seconda del core da interfacciare. La tecnica impiegata è stata dunque quella di creare una costante contenente una stringa che corrisponde alla struttura fissa. Questa viene caricata in memoria dal metodo write, che la legge carattere per carattere e simultaneamente scrive i caratteri letti nel file VHDL in uscita. All’interno della stringa, laddove vi è la necessità di inserire una parte di descrizione che dipende dal particolare core in ingresso, è presente un identificatore, “#”, seguito da un carattere. Quando, durante la lettura della stringa, il Writer incontra tale identificatore, il flusso di esecuzione entra in un costrutto di tipo case. A seconda del carattere letto dopo “#”, viene scritta nel file in uscita un’opportuna sezione, per esempio la dichiarazione o l’istanziazione del core, utilizzando le informazioni che pervengono in larga parte dalle liste provenienti dal Reader. Una volta eseguite le istruzioni corrispondenti all’opzione selezionata all’interno del costrutto case, il Writer riprende normalmente il ciclo di lettura da stringa e scrittura su file, fino alla successiva occorrenza dell’identificatore “#”. La scrittura dello stub termina ovviamente quando viene raggiunta la fine della stringa. In figura 5.3 è mostrato schematicamente il flusso di esecuzione corrispondente alla tecnica appena 73 CAPITOLO 5. IMPLEMENTAZIONE descritta. Figura 5.3: Flusso di creazione dei file in uscita • Il file VHDL che descrive l’IP-Core, ossia la top-architecture del componente interfacciato, viene creato in modo del tutto analogo allo stub. Si utilizza infatti un’altra stringa costante, che contiene la parte fissa del codice che descrive l’IP-Core. Anche in questo caso è necessario creare delle parti dipendenti dal core da interfacciare, e quindi è presente un costrutto case che funziona allo stesso modo di quello descritto nel precedente paragrafo. Si tenga però presente che per generare un’interfaccia verso il bus Wishbone non è necessario creare questo secondo file, in quanto lo stub, che a questo punto verrà chiamato esso stesso IP-Core, presenta come porte di ingresso e uscita esattamente i segnali del bus Wishbone. Quindi tale IP-Core non utilizza un’interfaccia che costituisce un modulo da istanziare (come avviene 74 CAPITOLO 5. IMPLEMENTAZIONE invece per PSelect o IPIF), e lo stub svolge tutte le funzioni richieste ai fini di supportare un corretto protocollo di comunicazione. 5.2.4 Terminazione Se non vengono riscontrati errori, il processo di scrittura si conclude e il controllo passa di nuovo al main, il quale termina l’esecuzione di IPGen. L’intero processo di esecuzione del software (nel caso si adotti un’interfaccia “da istanziare”, per esempio IPIF, e quindi rispettando la struttura classica dell’IP-Core come presentata in figura 1.1) è sinteticamente mostrato nel sequence diagram UML di figura 5.4. In questo diagramma, le etichette collegate da frecce tratteggiate al termine dell’esecuzione dei metodi cleanGenerics e write indicano la creazione dei file il cui nome è riportato all’interno dell’etichetta stessa. 5.3 Caratteristiche delle descrizioni VHDL generate In questa sezione verranno presentate le caratteristiche delle descrizioni VHDL prodotte da IPGen. Alcune di queste caratteristiche sono comuni a tutte le interfacce, mentre altre si differenziano a seconda della tipologia dell’infrastruttura di comunicazione selezionata dall’utente. La trattazione è suddivisa in ambiti funzionali; per ognuno di questi si mostreranno le eventuali differenze presenti tra i tre diversi tipi di interfacce. 5.3.1 Visione d’insieme In generale, i file VHDL prodotti da IPGen rispecchiano la struttura dell’IPCore come mostrata in figura 4.2. L’output del programma è infatti solitamente composto da tre file: • La descrizione VHDL del core, denominata nomecomponente_core.vhd, dove “nomecomponente” va sostituito con il nome reale del componente 75 CAPITOLO 5. IMPLEMENTAZIONE Figura 5.4: Sequence diagram dell’esecuzione di IPGen che viene interfacciato. Questo file è sostanzialmente uguale a quello che viene fornito dall’utente, dal quale si differenzia soltanto per l’assenza del valore di default di eventuali generic. Il motivo di tale scelta è spiegato nella sezione 5.3.4. • La descrizione VHDL dello stub, denominata nomecomponente_stub.vhd. Questo file rappresenta, come dice il nome, lo stub dell’IP-Core generato, ossia la parte di logica che permette la comunicazione tra il core e l’interfaccia. • La descrizione VHDL dell’IP-Core, denominata nomecomponente_IPCore.vhd. Questo file descrive l’IP-Core nel suo complesso. Esso è la top architecture del componente, indicando con questo termine la parte di logica più ester76 CAPITOLO 5. IMPLEMENTAZIONE na, cioè quella che viene vista dal bus, all’interno della quale sono istanziate le parti che compongono l’IP-Core. Volendo utilizzare l’IP-Core, tutto ciò che importa è il set di segnali che questo file presenta come porte di ingresso e uscita; sono infatti questi segnali, e solo questi, che rientrano nel file Microprocessor Peripheral Definition (MPD, vedere sezione 2.3) utilizzato da EDK per gestire correttamente un IP-Core che si vuole inserire in un sistema. Il file che rappresenta la vera e propria interfaccia di comunicazione non è presente tra gli output del software, in quanto, come già scritto, IPGen si avvale di interfacce già esistenti e diffuse. Per citare un esempio, i file che descrivono le interfacce OPB IPIF e OPB PSelect, fanno parte della libreria di core fornita da Xilinx con il framework EDK. La struttura dei file sopra illustrata non è però sempre identica, in quanto esistono delle situazioni che potremmo definire “degeneri”. È questo il caso dell’interfaccia verso il bus Wishbone: interfacciando un core verso questo bus, infatti, non è necessario introdurre un ulteriore componente (l’interfaccia di cui si è scritto prima) tra lo stub e il bus, in quanto i segnali di ingresso e uscita dello stub coincidono già con i segnali del bus Wishbone. Tutta la logica necessaria per implementare un collegamento che permetta la corretta comunicazione dell’IP-Core con gli altri componenti del sistema è contenuta nello stub, in quanto non sono richiesti ulteriori mappaggi tra segnali. Per questi motivi lo stub viene a coincidere con l’IP-Core stesso, e quindi si avranno solamente due file in uscita: nomecomponente_core.vhd e nomecomponente_IPCore.vhd. In figura 5.5 è mostrata sia la struttura generale che quella che è stata definita “degenere”. Si descrive ora brevemente il funzionamento dell’IP-Core generato facendo riferimento alla struttura dell’IP-Core generale. I segnali del bus si collegano, in fase di “placement” del componente all’interno di un sistema, alle omonime porte presenti in ingresso e in uscita dall’IP-Core. Si noti che la dichiarazione delle porte dell’IP-Core non cambia a seconda del core interfacciato, in quanto esse corrispondono ai segnali del bus adottato. Questi vengono filtrati da quella che è stata chiamata interfaccia, che li elabora per generare un set di segnali solitamente più semplice da gestire, offrendo spesso una varietà di servizi, come quelli visti nel 77 CAPITOLO 5. IMPLEMENTAZIONE Figura 5.5: Diverse strutture di file generati caso di OPB IPIF (sezione 3.1.2). I segnali generati dall’interfaccia costituiscono le porte dello stub, il quale viene istanziato all’interno dell’IP-Core e svolge le funzionalità descritte nel capitolo 4. All’interno di questo componente, a sua volta, è istanziato il core, ossia la funzionalità implementata dall’utente con il proprio set particolare di porte. Nelle seguenti sezioni sarà fornita una descrizione più dettagliata del funzionamento descritto. 5.3.2 Istanziazione dei componenti e mappaggio delle porte Come affermato nel paragrafo 5.3.1, all’interno dell’IP-Core vengono istanziati l’interfaccia e lo stub. Nel caso di infrastrutture complesse, come OPB IPIF, il mappaggio delle porte dell’interfaccia è abbastanza complicato; trattandosi comunque di una parte di logica fissa, cioè non dipendente dal particolare core, esso non deve essere effettuato ogni volta. Inoltre l’interfaccia suddetta presenta numerosi parametri da impostare, i quali determinano l’utilizzo o meno di un determinato servizio offerto. Ad eccezione della decodifica degli infirizzi, al momento si è scelto di non adottare alcun servizio opzionale implementato in OPB IPIF, poiché questi comportano spesso un overhead di occupazione non ridotto, il che andrebbe ad aumentare l’area occupata e il tempo necessario alla riconfigurazione del componente nell’ambito di un’architettura quale Caronte. Nel futuro si potrebbe attrezzare IPGen del supporto necessario a offrire all’utente la possi78 CAPITOLO 5. IMPLEMENTAZIONE bilità di utilizzare o meno un determinato servizio. Per quanto riguarda lo stub, anch’esso presenta, per ogni singolo tipo di interfaccia, un set di porte standard, che non varia (nel caso di OPB IPIF esso è composto dai segnali IPIC). Quindi l’istanziazione di esso risulta semplice e soprattutto fissa. Diversamente da quanto appena scritto riguardo l’istanziazione dell’interfaccia e dllo stub, l’inserimento del core all’interno dello stub è più problematico, in quanto non si conoscono a priori le porte del core. Queste, come descritto nella sezione 5.2, vengono salvate dal reader in un’opportuna lista, utilizzata dal writer per effettuare la dichiarazione del core nello stub. Per ogni porta del core diversa dal segnale di clock, di reset e di interrupt (vedere sezione 5.4.2), viene creato nello stub un corrispondente registro denominato register_nomesegnale, dove “nomesegnale” è ovviamente il nome attribuito alla particolare porta, avente tipo e dimensione del segnale del core corrispondente. Sono tali registri che vengono collegati alle porte del core in fase di istanziazione dello stesso, come si vede nell’esempio di figura 5.6. La gestione di questi registri all’interno dello stub è l’argomento della prossima sezione. 5.3.3 Processi di lettura e scrittura I registri creati all’interno dello stub devono essere opportunamente gestiti. Infatti, come precedentemente spiegato, lo stub deve svolgere una funzione di demultiplexer verso l’interno (dalla linea dati del bus ai vari registri) e multiplexer verso l’esterno (dai registri alla linea dati del bus). Queste funzionalità vengono svolte da due processi implementati nello stub: il processo di lettura, denominato reg_read_process, e il processo di scrittura, denominato reg_write_process. Adottando la terminologia usata nell’ambito dei componenti logici sopra citati, si può dire che il segnale di selezione corrisponde alla linea indirizzi del bus, o a un suo derivato. Utilizzando OPB IPIF, per esempio, si dispone dei due vettori di selezione Bus2IP_RdCE e Bus2IP_WrCE (vedere sezione 3.1.2), che fungono da surrogati della linea indirizzi, ma ne permettono un’interpretazione più intuitiva. Nel seguito si farà riferimento senza perdere generalità al caso OPB IPIF. 79 CAPITOLO 5. IMPLEMENTAZIONE Figura 5.6: Dichiarazione e istanziazione del core di un addizionatore binario a 32 bit Il processo di lettura è costituito sostanzialmente da un costrutto di tipo case, il quale viene attivato ad ogni fronte di salita del clock. Quando un bit del vettore read_reg_select, che corrisponde all’ingresso Bus2IP_RdCE, è posto a ’1’, significa che è necessario porre sul bus la corrispondente uscita del core, rappresentata all’interno dello stub dal registro a cui è mappata. Nel caso particolare in cui l’uscita che si vuole leggere è costituita da un segnale di tipo std_logic, tale valore viene posto per convenzione sul bit meno significativo (Least Significant Bit LSB) del bus dati, cioè quello più a destra. Se il segnale è di tipo std_logic_vector ed ha lunghezza inferiore a 32 bit, che è la dimensione del bus dati nei tre tipi di interfaccia supportati, anche in questo caso, per convenzione, il valore viene posto sui bit più a destra della linea dati del bus. Il caso di segnali con dimensione maggiore di 32 bit è più complesso e viene esposto nella sezione 5.3.5. Un esempio chiarificatore è mostrato in figura 5.7. 80 CAPITOLO 5. IMPLEMENTAZIONE Figura 5.7: Esempio di processo di lettura dei registri Il processo di scrittura è molto simile a quello di lettura. Anche in questo caso si ha un costrutto case il cui segnale di selezione è reg_write_select, corrispondente all’ingresso Bus2IP_WrCE. In modo analogo a quanto detto per il processo di lettura, i registri aventi lunghezza minore di 32 bit prelevano il proprio valore dai bit meno significativi del bus dati. Nel processo di scrittura, è presente anche la parte riguardante il reset dei registri in ingresso. Semplicemente, si controlla all’inizio del processo se l’ingresso Bus2IP_Reset è attivo, e, in caso affermativo, si procede ponendo a zero il contenuto dei registri di scrittura dello stub. Un esempio è mostrato in figura 5.8. 5.3.4 Gestione dei generic Se nel core sono presenti dei generic, essi possono possedere un valore di default. Tale valore, se lasciato indicato nel core, potrebbe essere fonte di errori o problemi di interpretazione. Infatti, dato che un generic è un parametro che si può impostare 81 CAPITOLO 5. IMPLEMENTAZIONE Figura 5.8: Esempio di processo di scrittura dei registri quando l’IP-Core viene collocato in un sistema (per esempio tramite EDK), il framework deve essere in grado di propagare verso l’esterno, dal punto di vista della consueta struttura del componente, tale parametro; in questo modo il generic è impostabile agendo sul file che descrive l’IP-Core. Ora, impostando dall’esterno un valore per un determinato generic, potrebbe darsi che esso sia differente dal valore di default indicato nel core. Per ovviare a questo problema, IPGen sfrutta un meccanismo molto semplice: salva una lista dei generic in fase di lettura, e in fase di scrittura effettua due mappaggi consecutivi per ognuno di essi prima dal core allo stub, e poi dallo stub all’IP-Core. Inoltre esso genera, come scritto in precedenza, un file uguale al core, ma privo dei valori di default di eventuali generic; tali valori vengono poi indicati ragionevolmente nella dichiarazione dello stesso generic a livello di IP82 CAPITOLO 5. IMPLEMENTAZIONE Core. Sembrerebbe inutilmente dispendioso creare una copia quasi identica di un file, ma effettivamente questa scelta si rivela sensata: grazie ad essa infatti il file originale viene preservato in tutto e per tutto, rendendo possibile il suo riutilizzo in un altro ambito. 5.3.5 Gestione di porte con dimensione maggiore di 32 bit Figura 5.9: Processo di lettura per un core avente una uscita da 128 bit Per consentire ad IPGen di essere applicabile ad una maggiore varietà di core, si è deciso di implementare un supporto per i segnali di tipo std_logic_vector aventi dimensione maggiore della linea dati del bus, che in tutti e tre i casi di interesse corrisponde a 32 bit. Dopo attente analisi, è stato adottato il seguente meccanismo di comunicazione: ogni segnale “grande” viene segmentato in registri di dimensione uguale a 32 bit, ad eccezione dell’ultimo, che può avere una dimensione compresa tra 1 e 32 bit, in quanto la lunghezza segnali non è necessariamente multipla di 32. Ad ogni segmento viene associato un indirizzo; per accedere in lettura o scrittura a ciascun pezzo del segnale, il driver che gestisce l’IP-Core deve quindi fornire l’indirizzo preciso del registro corrispondente. Per quanto riguarda il processo di lettura, esso non si discosta molto da quanto presentato nella sezione 5.3.3. In figura 5.9 si può vedere un esempio di processo di lettura per un core avente un segnale di lunghezza 128 bit. La questione è nettamente più complessa per quanto riguarda la scrittura di 83 CAPITOLO 5. IMPLEMENTAZIONE tali segnali. Questo perché se un core possiede un ingresso, per esempio, di 64 bit, esso si aspetta certamente di riceverlo tutto in una volta, e non scaglionato in segmenti da 32 bit. Per rispettare questa condizione IPGen crea all’interno dello stub, per ogni ingresso del core più grande di 32 bit, i seguenti segnali, il cui utilizzo viene spiegato successivamente: • il consueto registro, denominato register_nomesegnale; • un segnale di tipo std_logic_vector, denominato buffer_nomesegnale, avente dimensione pari a quella dell’ingresso del core; • un segnale di tipo intero, denominato count_nomesegnale; • un segnale di tipo std_logic, denominato ready_nomesegnale. Nel processo di scrittura, in presenza di un segnale “grande”, viene generata una sorta di macchina a stati, la quale pone al driver il vincolo di rispettare obbligatoriamente la sequenzialità nella trasmissione del segnale, la quale può avvenire anche in modo non consecutivo. In pratica, all’indirizzo di ogni segmento del segnale viene assegnato un numero intero partendo da 0; se, quando si riceve l’nesimo segmento, il contatore count_nomesegnale ha un valore pari alla posizione del segmento all’interno del segnale, cioè n, il valore in ingresso viene correttamente copiato nella giusta posizione all’interno di buffer_nomesegnale. In caso contrario il contatore viene annullato e il buffer posto interamente a ’0’, effettuando perciò un reset della comunicazione. Quando l’ultimo segmento del segnale viene correttamente ricevuto, viene posto al valore ’1’ il flag ready_nomesegnale, in modo che un opportuno controllore possa, nel ciclo di clock successivo, copiare l’intero contenuto del buffer in registro_nomesegnale e riazzerare il flag. Un esempio di questa operazione è mostrato in figura 5.10, nella quale si considera un segnale di lunghezza pari a 96 bit. Il meccanismo appena descritto non è al momento stato implementato per quanto riguarda l’interfaccia OPB PSelect, mentre è supportato utilizzando OPB IPIF e Wishbone. 84 CAPITOLO 5. IMPLEMENTAZIONE Figura 5.10: Processo di scrittura per un core avente un ingresso da 96 bit 5.4 Come scrivere un core interfacciabile automaticamente In questa sezione vengono analizzati quelli che sono i requisiti che un core deve possedere, affinché la creazione del relativo IP-Core tramite IPGen possa avvenire con successo. Questi requisiti si dividono in due gruppi: • requisiti concettuali, la cui inosservanza non genera errori nel flusso di esecuzione di IPGen; 85 CAPITOLO 5. IMPLEMENTAZIONE • requisiti pratici, riconosciuti dal framework, la cui inosservanza blocca effettivamente l’esecuzione dello stesso. 5.4.1 Requisiti concettuali Si tratta in realtà di un unico principio fondamentale da tenere a mente, e non di una serie di regole da seguire. Sostanzialmente, si può dire che lo sviluppatore del core, cioè colui che ne scrive la descrizione in VHDL, deve occuparsi solamente della funzionalità svolta dal componente che sta implementando, senza curarsi, in nessun modo, della connessione di questo con il sistema in cui verrà inserito. In altre parole, tutta e sola la funzionalità reale che il componente svolge va descritta nel core, lasciando ad altri il compito di occuparsi del collegamento con una particolare infrastruttura di comunicazione. Portando il discorso ad un livello più pratico, si può affermare che l’insieme degli ingressi e delle uscite del core non deve contenere elementi in qualche modo riconducibili al collegamento dell’IPCore nel sistema, in quanto la logica necessaria a tale scopo viene interamente creata da IPGen. Viene presentato un esempio per esplicare meglio quanto si intende dire: si prenda il caso in cui si voglia realizzare un addizionatore binario con operandi di 32 bit. In tal caso, è ovvio che gli ingressi saranno costituiti dagli addendi, e da eventuali segnali di clock (se il componente è sincrono) e di reset. Le uscite saranno invece il risultato e eventualmente un interrupt, utilizzabile ad esempio per segnalare un overflow. Come si vede, non si fa in nessun modo riferimento alla linea dati del bus o agli indirizzi dei registri che permettono di effettuare operazioni di lettura scrittura sul componente. Questo è possibile poiché è lo stub, generato automaticamente, che si occupa di questioni quali lo smistamento degli operandi in ingresso, provenienti da un’unica linea dati del bus, o la lettura del risultato in uscita, il quale va posto sulla stessa linea dati. Se un core contenente porzioni di logica necessari alla comunicazione con il sistema viene dato in ingresso ad IPGen, si ottiene un prodotto non sintatticamente scorretto, ma contenente inutili e dannose ridondanze. Un tale IP-Core risulta necessariamente inutilizzabile, sia a causa dei ritardi introdotti da inutili livelli di comunicazione, sia perché non si assicura un corretto collegamento tra ingressi del 86 CAPITOLO 5. IMPLEMENTAZIONE core e segnali provenienti dal bus. Per citare un caso, si consideri un core avente un segnale in ingresso che rappresenta l’indirizzo posto sul bus. Tale segnale non verrebbe in nessun modo riconosciuto e verrebbe collegato, come un ingresso “normale”, alla linea dati del bus, anziché a quella degli indirizzi. Il requisito fondamentale qui discusso non pone in realtà alcun vincolo allo sviluppatore, che anzi si trova sgravato dalla necessità di implementare una porzione di logica generabile automaticamente. Si noti inoltre che questo requisito costituisce la base per il riuso degli IP-Core, in quanto un componente che non contiene accenni al sistema in cui verrà posto è necessariamente “universale”. 5.4.2 Requisiti pratici Si passa ora all’analisi dei requisiti cosiddetti “pratici”. In primo luogo un core può possedere due tipologie di porte corrispondenti due gruppi di segnali: • Segnali custom: sono segnali propri della funzionalità implementata, come, ad esempio, gli ingressi di una funzione; • Segnali standard: sono porte solitamente presenti in tutti i core implementati. Svolgono funzioni standard (da qui il nome) come il segnale di clock, quello di reset e quello di interrupt. Solitamente a questo tipo di segnali vengono assegnate linee dedicate del bus. Le porte appartenenti al secondo tipo devono essere gestite in modo specifico dallo stub, per permettere un collegamento corretto con le corrispondenti linee del bus. Sarebbe infatti un errore, per esempio, non riconoscere come tale il segnale di clock e collegarlo alla linea dati del bus, come se fosse un segnale custom, in quanto il bus possiede una linea separata per la trasmissione di questo particolare segnale. Quindi, affinché il writer sia in grado riconoscere tali segnali, è necessario introdurre delle condizioni di nomenclatura, elencate qui di seguito: • Il segnale di clock deve essere denominato “clk”; • Il segnale di reset deve essere denominato “reset”; 87 CAPITOLO 5. IMPLEMENTAZIONE • Il segnale di interrupt deve essere denominato “intr”. Oltre a queste convenzioni, ci sono altre due regole da seguire. IPGen gestisce solamente core aventi porte di tipo std_logic e std_logic_vector; altri tipi non sono supportati. Se un core possiede al suo interno segnali di altro tipo, spetta allo sviluppatore implementare la logica di conversione dal tipo in esame e uno dei due tipi prima menzionati. Siccome ciò avviene in realtà in pochi casi, poiché solitamente il livello di astrazione utilizzato scrivendo codice VHDL è piuttosto basso, sarebbe ingiustificatamente oneroso scrivere una parte di software che generi in automatico la logica per effettuare queste conversioni, considerando anche che l’insieme dei tipi utilizzabili nella dichiarazione di porte e segnali interni è potenzialmente infinito. Infine, il software non supporta al momento dichiarazioni del tipo: port1, port2 : std_logic; Tale dichiarazione va sviluppata nel modo consueto: port1 : std_logic; port2 : std_logic; Non vi sono altri vincoli da rispettare per scrivere un core gestibile da IPGen. 88 Capitolo 6 Risultati sperimentali Sono stati effettuati numerosi test con l’obiettivo di validare in pratica IPGen. Questi test sono stati condotti con due strumenti: ISE Project Navigator e ModelSim, entrambi presentati nella sezione 1.2. Essi si possono suddividere in due categorie: test di prestazione e di funzionamento. Nei primi sono stati valutati parametri quali il tempo di esecuzione del software e l’area occupata sulla FPGA dagli IP-Core generati, mentre i secondi hanno come obiettivo la dimostrazione dell’effettivo funzionamento delle soluzioni adottate. 6.1 Test di prestazione Per valutare le prestazioni di IPGen e degli IP-Core prodotti, si è scelto di focalizzare l’attenzione sull’interfaccia OPB IPIF. Questa è una scelta che non toglie assolutamente generalità al discorso. Anzi, addirittura potremmo identificare tale interfaccia come il “caso pessimo” in quanto, come si evince dal capitolo 3, essa è l’infrastruttura di comunicazione più complessa da gestire e che comporta un maggiore overhead in termini di area occupata sulla FPGA. Sono stati attentamente analizzati due parametri: • Il tempo di esecuzione del software, espresso in millisecondi, che descrive la bontà del software; 89 CAPITOLO 6. RISULTATI SPERIMENTALI • L’area occupata dall’IP-Core sulla FPGA, espressa in numero di slice, che esprime una caratteristica non del software, bensì del codice VHDL generato. In una prima sessione sono stati utilizzati otto core, partendo da componenti molto semplici e leggeri a core piuttosto complessi. Essi sono qui di seguito elencati: • Un semplice Counter, che conta da 0 al valore in ingresso n; • Un Adder con operandi da 32 bit con controllo di overflow; • Uno Squarer, che calcola il quadrato del numero binario in ingresso; • Un contatore di frequenza; • Un lettore hardware di file con filesystem di tipo FAT16; • Una Division Unit; • Una Unità Aritmetico Logica (ALU); • Una Floating Point Unit (FPU), che esegue operazione su numeri binari espressi in floating point. All’interno della tabella 6.1 sono mostrati i risultati ottenuti nell’ambiente di sviluppo ISE Project Navigator, simulando la collocazione degli IP-Core su una FPGA Xilinx VirtexIIPro xc2vp7, con l’unica eccezione della FPU, per la quale, a causa della notevole dimensione, è stata virtualmente utilizzata una VirtexIIPro xc2vp30. In primo luogo è stata effettuata la sintesi con XST del core preso singolarmente. Successivamente, è stato sintetizzato lo stub, e infine l’IP-Core. Prima di analizzare i dati mostrati in tabella è necessario fare una premessa sul funzionamento di XST. Come descritto in [23], esso effettua in fase di sintesi di un IP-Core importanti ottimizzazioni, con l’obiettivo di raggiungere un compromesso tra la massimizzazione della frequenza di utilizzo e la minimizzazione dell’area occupata. Tale processo si basa fondamentalmente su due operazioni: la duplicazione 90 CAPITOLO 6. RISULTATI SPERIMENTALI Counter Adder Squarer F. Counter FFR-Fat16 Div Unit ALU FPU Core (# slice) 26 35 63 85 168 317 997 5394 Stub (# slice) 19 37 26 29 91 39 48 -123 Core+Stub (# slice) 45 72 89 114 259 356 1045 5271 IPIF (# slice) 39 62 24 35 80 60 17 153 IP-Core (# slice) 84 134 113 149 339 416 1065 5424 Overhead (# slice) 58 99 50 64 171 111 68 30 69,05 73,88 44,22 42,95 50,44 26,68 6,38 0,01 33 96 11 34 42 80 140 115 53 60 55 59 64 60 67 59 Overhead % Dimensione delle porte (bit) Tempo (ms) Tabella 6.1: Risultato dei test sugli otto core utilizzati di registri (per minimizzare il “tempo di attraversamento” e quindi aumentare la frequenza di utilizzo), e l’eliminazione di registri ridondanti o inutilizzati (per minimizzare l’area occupata). A livello teorico, è ragionevole supporre che l’interfaccia IPIF necessiti di una quantità di area costante per tutti gli IP-Core; in realtà, proprio a causa delle ottimizzazioni operate dallo strumento di sintesi, questo non si verifica. Ciò nonostante, l’area occupata dalla IPIF non mostra alcun trend particolare, quindi si può escludere che essa dipenda in qualche modo dalla dimensione o dalla complessità del core interfacciato. È altresì intuitivo aspettarsi che, al crescere della dimensione del core istanziato, l’occupazione percentuale di overhead introdotto da IPGen, relativa alla dimensione totale dell’IP-Core, decresce in modo netto, come è mostrato in figura 6.1. Dalla tabella emerge un dato senz’altro “strano”: la dimensione dello stub insieme al core è in un caso minore di quella del core stesso; ancora una volta questo è un effetto del processo di ottimizzazione operato da XST. Viene valutato ora il tempo di esecuzione del software; per ottenere una misura il più reale possibile, tale dato è costituito dalla media di dieci tempi ottenuti da altrettante esecuzioni del framework per ogni core. Si nota facilmente che esso è indipendente dalla dimensione e dalle porte dei core e risulta con buona approssimazione costante. 91 CAPITOLO 6. RISULTATI SPERIMENTALI Figura 6.1: Occupazione percentuale di overhead introdotto da IPGen Successivamente è stata condotta una seconda sessione di test. Sono stati utilizzati, in qualità di core da dare in ingresso a IPGen, dei dummy core opportunamente implementati. Per dummy core si intende una descrizione VHDL contenente, nella parte architetturale, il livello minimo di logica necessaria a far sì che le porte dichiarate nella entity siano effettivamente utilizzate. Molto spesso si tratta di un semplice assegnamento tra uscite e ingressi. Anche se questi non descrivono funzionalità utili e reali, offrono in fase di test numerosi vantaggi: infatti, si possono condurre delle prove “mirate” a verificare l’efficacia o meno del framework in una data situazione, semplicemente scrivendo un dummy core adeguato. Inoltre, possedendo una logica molto semplice (addirittura inesistente nella quasi totalità dei casi), essi sono meno soggetti alle pesanti ottimizzazioni operate in fase di sintesi da XST. Il fatto che un dummy core abbia di per sé un’occupazione di area quasi sempre nulla non rappresenta un risvolto negativo: infatti è stato mostrato nella precedente sessione che i parametri considerati non dipendono in alcun modo dalla dimensione del core. Tra le misure considerate non appare la dimensione del92 CAPITOLO 6. RISULTATI SPERIMENTALI l’interfaccia IPIF: tale scelta deriva sia da una considerazione teorica già espressa sopra, cioè che l’occupazione della IPIF dovrebbe essere costante, sia dal risultato pratico ottenuto precedentemente, ossia la mancanza di correlazione tra complessità del core e occupazione dell’interfaccia. Ci si riferirà quindi solamente allo stub, la cui dimensione dovrebbe ragionevolmente cambiare al variare del numero e della larghezza dei segnali di ingresso e uscita dal core. Per valutare a fondo le prestazioni di IPGen, sono stati creati nove dummy core. I risultati dei test si possono vedere nella tabella 6.2, mentre in figura 6.2 si può osservare l’andamento dell’occupazione (espresso in numero di slice) dello stub al variare del dummy core considerato. Per esprimere i dati di interesse di ciascun componente, cioè le porte di ingresso e di uscita, è stata adottata la seguente simbologia: il primo numero rappresenta la quantità delle porte e il numero tra parentesi indica la dimensione in bit del segnale. Per esempio, “2 x (64)” significa che il core ha (in ingresso o in uscita) due porte di dimensione pari a 64 bit. dummy01 dummy02 dummy03 dummy04 dummy05 dummy06 dummy07 dummy08 dummy09 Porte in Porte out Tempo (ms) Core (slice) Stub (slice) 1 x (1) 1 x (32) 2 x (1) 2 x (32) 4 x (32) 2 x (64) 1 x (128) 1 x (64) 3 x (64) 1 x (1) 1 x (32) 2 x (1) 2 x (32) 4 x (32) 2 x (64) 1 x (128) 3 x (64) 1 x (64) 42,7 44,2 42,9 45,7 45,9 44,3 55,3 44,2 58,3 0 0 0 0 0 0 0 0 37 2 37 4 69 107 196 193 115 252 Tabella 6.2: Risultato dei test sui “dummy core” Vengono ora fatte alcune considerazioni sui risultati ottenuti. Innanzitutto si può notare che l’overhead di logica costituito dallo stub è molto basso quando gli ingressi e le uscite del core sono segnali costituiti da un singolo bit. Si nota anche che, al raddoppiare del numero di porte di dimensione 32 bit (si considerino i core dummy02, dummy04 e dummy05), l’occupazione dello stub risulta anch’essa quasi raddoppiata. Un altro risultato rilevante é il seguente: osservando i componenti 93 CAPITOLO 6. RISULTATI SPERIMENTALI Figura 6.2: Occupazione dello stub generato per i “dummy core” dummy05 e dummy06 si vede che, a parità di dimensione complessiva in bit sia degli ingressi che delle uscite, la dimensione dello stub è molto maggiore nel caso in cui si utilizzino segnali più grandi di 32 bit. Tale risultato è frutto del meccanismo particolare implementato per la gestione di questi segnali, soprattutto quelli in ingresso (si veda la sezione 5.3.5). Proprio per verificare questa affermazione, sono stati creati dummy08 e dummy09, che utilizzano porte da 64 bit. Il primo presenta un segnale in scrittura e tre in lettura, mentre il secondo ne è il duale, possedendo tre ingressi e una sola uscita. Lo stub di dummy08 ha un’occupazione che, come era lecito aspettarsi, è minore di quello di dummy06 e maggiore di dummy05. La dimensione dello stub di dummy09 è invece maggiore di quella di tutti gli altri essendo l’unico core che presenta bene tre ingressi di dimensione 64 bit. Per quanto riguarda i tempi di esecuzione, essi sono pressoché costanti, anche se rivelano un leggero trend crescente all’aumentare del numero e della dimensione dei porte di ingresso dei core. 94 CAPITOLO 6. RISULTATI SPERIMENTALI 6.2 Test di funzionamento Con l’intento di validare la metodologia proposta, è stato necessario effettuare alcune simulazioni del funzionamento degli IP-Core generati dal framework, condotte utilizzando ModelSim (sezione 1.2.3). Il core scelto per verificare il corretto funzionamento dei componenti generati è un addizionatore a 32 bit, il quale possiede due ingressi, gli addendi, e un’uscita, il risultato. Sono necessarie quindi due operazioni di scrittura e una di lettura al fine di compiere un ciclo completo di applicazione del modulo. Sono stati simulati i tre IP-Core generati con le interfacce supportate. Figura 6.3: Simulazione di un IP-Core con interfaccia PSelect In primo luogo viene considerato l’IP-Core che integra l’interfaccia PSelect sul bus OPB, la cui simulazione è mostrata nel grafico temporale in figura 6.3, dove i segnali che rappresentano dati o indirizzi sono espressi per chiarezza in codifica esadecimale. Il valore iniziale dell’indirizzo presente sul bus corrisponde al base address assegnato al componente in fase di istanziazione nel sistema. in quanto il primo registro è posizionato all’interno dello spazio di indirizzamento dell’IP-Core in posizione zero. In tal modo, essendo l’ingresso opb_rnw posto a zero (indicando così un’operazione di scrittura), il contenuto della linea dati entrante viene copiato nel registro corrispondente al primo addendo. In modo identico viene successivamente scritto il valore del secondo operando, il cui registro si trova all’indirizzo 4. A questo punto il core computa la somma che viene letta dal master, innalzando il segnale opb_rnw e ponendo sulla linea indirizzi il valore 0, che corrisponde in fase di lettura al registro contenente il risultato. 95 CAPITOLO 6. RISULTATI SPERIMENTALI Figura 6.4: Simulazione di un IP-Core con interfaccia OPB IPIF La simulazione dell’IP-Core interfacciato tramite OPB IPIF è raffigurata in figura 6.4; il procedimento di scrittura/lettura è sostanzialmente simile a quello di PSelect. Si noti che in questo caso lo stub, per gestire i registri, utilizza i vettori di CE forniti da IPIF. Inoltre, durante la fase di lettura del risultato, si vede che esso viene posto sul segnale IPIC IP2Bus_data sul fronte di discesa del clock; questo è necessario poiché, se tale valore venisse scritto durante il fronte di salita, la IPIF non farebbe in tempo a elaborarlo e verrebbe perso in tal modo un intero ciclo di lettura, corrispondente generalmente a tre cicli di clock. Figura 6.5: Simulazione di un IP-Core con interfaccia Wishbone L’interfacciamento con Wishbone (figura 6.5) si dimostra meno complesso dei precedenti. In questo caso il valore della linea indirizzi del bus è espressa in codifica binaria, in quanto composta da soli 8 bit. Dopo aver impostato il valore dei parametri necessari a inizializzare la comunicazione (secondo il protocollo de96 CAPITOLO 6. RISULTATI SPERIMENTALI scritto nella sezione 3.2), si procede con la fase di scrittura dei registri in ingresso, innalzando il segnale we_i e ponendo a “011” il valore dei primi tre bit del segnale adr_i, che corrispondente al numero identificativo dell’IP-Core all’interno del sistema. Assegnando in successione i valori “00000” e “00001” ai cinque bit meno significativi di adr_i, il valore presente nel bus dati viene copiato nei registri corrispondenti agli ingressi del core. Per leggere la somma, il master abbassa we_i e scrive l’indirizzo opportuno, in modo tale che il risultato il risultato venga copiato sulla linea dati in uscita dall’IP-Core. 97 Capitolo 7 Conclusione e sviluppi futuri Durate la fase di progettazione di un sistema embedded, la realizzazione delle interfacce dei componenti che devono dialogare tra di loro mediante un’opportuna infrastruttura di comunicazione è la parte meno creativa e più meccanica di tutto il processo. Questo lavoro di tesi è stato concepito per sgravare il progettista da questo lavoro ripetitivo, migliorando il time-to-market del dispositivo prodotto e dando la possibilità all’utente di focalizzare la propria attenzione sulla parte realmente funzionale della propria architettura: il core. IPGen, per come è stato concepito, ha una struttura interna fortemente modularizzata (sezione 5.1). Questo aspetto permette al framework di essere espanso senza la necessità di modificare porzioni consistenti di codice già scritto. In futuro potranno essere implementati nuovi writer al fine di supportare un maggior numero di bus assieme alle loro interfacce. Come mostrato nel capitolo 6, IPGen è uno strumento già funzionante con discrete prestazioni. Si può dire con tutta sicurezza che esso rappresenta una vera e propria novità all’interno del settore della progettazione di IP-Core. Infatti, come si può vedere nel capitolo 2 e nella sezione 4.4, non esiste un altro strumento che attualmente offra funzionalità analoghe. Inoltre, sebbene IPGen è stato pensato per essere impiegato all’interno di un flusso di riconfigurabilità dinamica, esso è in realtà un software indipendente, che può essere utilizzato ogniqualvolta si debba creare un IP-Core. Ovviamente, l’ampiezza del campo di utilizzo del software 99 CAPITOLO 7. CONCLUSIONE E SVILUPPI FUTURI dipende dal numero delle interfacce supportate. Allo stato attuale, esso supporta il bus OPB con interfacce PSelect, IPIF e Wishbone. L’interfaccia OPB PSelect viene sfruttata dal software utilizzando tutte le funzionalità che essa mette a disposizione. Ciò non vale per IPIF, della quale IPGen sfrutta le capacità di decodifica degli indirizzi, ma non i servizi aggiuntivi quali Interrupt Service Controller e registri FIFO. Il bus Wishbone viene supportato in modalità Point-to-Point e Shared Bus, mentre le ulteriori tipologie di connessione non vengono utilizzate. Quindi, sebbene IPGen garantisca un interfacciamento sia con OPB che con Wishbone, possibili sviluppi comprendono, oltre all’aggiunta del supporto a altre eventuali interfacce, l’ampliamento e l’irrobustimento del supporto a questi bus. Al fine di allargare il campo di applicazione del framework, è in fase di studio l’implementazione del supporto per l’interfacciamento di IP-Core di tipo master, e in particolare di componenti con accesso diretto alla memoria (DMA). 100 Bibliografia [1] Alberto Donato, Fabrizio Ferrandi, Massimo Redaelli, Marco D. Santambrogio, and Donatella Sciuto. Caronte: A complete methodology for the implementation of partially dynamically self-reconfiguring systems on fpga platforms. In FCCM, pages 321–322, 2005. [2] Fabrizio Ferrandi, Marco D. Santambrogio, and Donatella Sciuto. A design methodology for dynamic reconfiguration: The caronte architecture. In IPDPS, 2005. [3] Xilinx Inc. Development System Reference Guide. Xilinx, Inc., 2001. [4] Semiconductor intellectual property core. Wikipedia, the free encyclopedia. [5] IP core. Whatis.com, the leading IT encyclopedia and learning center. [6] Andrew S. Tanenbaum. Structured Computer Organization - 4th Edition. Prentice Hall, 1999. [7] Xilinx Inc. Two flows for Partial Reconfiguration: Module Based or Difference Based. Technical Report XAPP290, Xilinx Inc., September 2004. [8] Xilinx Inc. Embedded System Tools Reference Manual. Xilinx Inc., 2005. [9] Xilinx Inc. XST User Guide. Xilinx Inc., 2005. [10] MentorGraphics. ModelSim GUI Reference. MentorGraphics, 2004. [11] OCP-IP Association. The importance of Sockets in SOC Design. 101 [12] OCP-IP Association. Open Core Protocol Specification 2.1. [13] Tien-Lung Lee and Neil W. Bergmann. An Interface Methodology for Retargettable Fpga Peripherals. In Engineering of Reconfigurable Systems and Algorithms, pages 167–173, 2003. [14] Tien-Lung Lee and Neil W. Bergmann. Interfacing Methodologies for IP Re-use in Reconfigurable System-On-Chip. Microelectronics: Design, Technology and Packaging, Vol. 5274, 2004. [15] Impulse Accelerated Technologies. Impilse CoDeveloper User Guide. Impulse Accelerated Technologies, 2006. [16] System-on-chip. Wikipedia, the free encyclopedia. [17] IBM corporation. The CoreConnect Bus Architecture, white paper. International Business Machines Corporation., 2004. [18] IBM Corporation. On-Chip Peripheral Bus, Architecture Specification version 2.1. [19] Xilinx Inc. Designing Custom OPB Slave Peripherals for MicroBlaze. Xilinx, Inc., 2002. [20] Xilinx Inc. OPB IPIF (v3.01a). Xilinx, Inc., 2004. [21] Silicore OPENCORES.ORG. WISHBONE system-on-chip (SoC) inter- connection architecture for portable IP cores. September 2002. Revision: B.3. [22] F. Ferrandi, G. Ferrara, R. Palazzo, V. Rana, and M. D. Santambrogio. Vhdl to fpga automatic ipcore generation: A case study on xilinx design flow. In 20th IEEE International Parallel and Distributed Processing Symposium (IPDPS’06) - Reconfigurable Architecture Workshop - RAW, 2005. [23] Hitesh Patel. Synthesis and Implementation Strategies to Accelerate Design Performance. Xilinx White Paper 229, 2005. 102 Ringraziamenti È stata dura, ma ce l’abbiamo fatta. Sono passati tre anni dall’inizio di questo percorso, e abbiamo finalmente raggiunto la prima tappa fondamentale. Tre lunghi anni che hanno comportato molte fatiche e preoccupazioni, ma non solo; fortunatamente abbiamo incontrato tanta gente, tanti amici che hanno contribuito a rendere questo cammino molto ma molto più sereno e divertente: Alessio, Cesco, Malex, Marco, Paco, Pasca, Ste, Supremo e tutti gli altri con cui abbiamo trascorso questo periodo. Il primo ringraziamento va a loro. Questa tesi non sarebbe mai esistita se non ci fosse stata una persona a istruirci, spronarci e motivarci. Un grazie di cuore a Santa!!!, che ci ha fatto capire come lavorando in gruppo si possano ottenere grandi risultati. A proposito di gruppo, ci teniamo a ringraziare tutti i ragazzi di DRESD, in particolare Matteo e Vincenzo per l’aiuto che ci hanno dato. Matteo Innanzitutto un grazie immenso alla mia famiglia, grazie per il sostegno che mi dà ogni giorno e grazie per tutti i principi che mi ha insegnato nel corso di questi 22 anni. Questa tesi è il mio ringraziamento per tutto questo. Voglio ringraziare tanto, infinitamente, colei che mi è vicina da due anni, che mi ha aiutato, consolato, spronato nei momenti difficili, grazie a lei sono diventato migliore. Ste, ti voglio bene. 103 E adesso è arrivato il momento di ringraziare un uomo che è venuto dalle Valli per allietare e rendere produttiva ogni mia giornata! Con Alessandro ho condiviso esperienze di studio, di lavoro, di festa, di estremo divertimento. Grazie per aver condiviso con me queste esperienze di vita. Infine voglio ringraziare tutti i miei amici, soprattutto perchè nonostante le mie sparizioni per (anche lunghi) periodi di tempo, mi fanno comunque sentire sempre la loro amicizia ed ogni volta che ci ritroviamo siamo sempre legatissimi, come se trascorressimo assieme ogni ora del nostro tempo. Alessandro Ho tante persone da ringraziare... inizio da coloro che mi hanno supportato e sopportato per tutto questo tempo: i miei genitori. Grazie veramente di cuore per tutto quello che avete fatto e fate per me. Restando in ambito familiare, è doveroso ringraziare il bassista più virtuoso della Valle Seriana: mio fratello Massimo, colui che mi ha avvicinato alla Musica, compagna fidata di sempre. E poi, come dimenticare la Compagnia (sì, con la C maiuscola)? Grazie per tutti i week-end "shalli" che abbiamo passato insieme, non sapete quanto abbiano contribuito al raggiungimento di questo risultato. E inoltre vorrei ringraziare ma veramente dibbruno il mio egregio collega Matteo, validissimo compagno di studi e soprattutto grande amico. Infine desidero ringraziare la persona meravigliosa che mi sta vicino da quattro anni e che mi ha reso migliore: grazie amore mio, grazie di tutto. 104 Tesi scritta in LATEX 2ε Stampata il 21 Settembre 2006