Bus AXI

AXI: Nozioni di base.

Le parti interne del SoC ZYNQ7000 sono tra loro interconnesse tramite il bus AXI. Questo è l’evoluzione del predecessore AMBA.  La funzionalità di base è quella di interconnettere in maniera bidirezionale la sezione PS (processor unit oppure programmable system) con la PL (programmable Logic) appoggiandosi a dei registri configurabili in indirizzo e in lunghezza.

Abbiamo predisposto un applicativo, da compilare in ambiente Linux, utilizzabile come template per ogni possibile futura applicazione, in linguaggio C, compilabile direttamente nel sistema operativo della Redpitaya.  -> axi_write     Per compilare copiamo il file sorgente nel file sytem del sistema SoC, ad esempio usando WinSCP oppure PuTTy.

compilazione

Il compilatore gcc è in linea quindi basterà fornire, come nell’immagine, il comando di compilazione  gcc -o (con “-o” si intende “generando un file in output”) segue il nome del file compilato seguito dal nome del file sorgente completo di estensione.

Per mandare in esecuzione il file compilato basterà digitare ./My_Axi_write

lancio

Attenzione: quando creiamo una nuova cartella all’interno del file system della Pitaya potrebbe verificarsi che i programmi compilati, ad esempio AXI_write, copiati in questa cartella non siano lanciabili. Questo è dovuto alla mancanza dei necessari permessi di esecuzione, cosa piuttosto normale in ambiente Linux. La soluzione è semplice, all’errore indicato sotto:

-bash: ./axi_write: Permission denied

rispondiamo come segue:  in primo luogo lanciamo il comando “ls -la” per vedere la situazione dei permessi attuali.  Poi rendiamoci proprietari del file, e quindi in grado di lanciarlo tramite il comando:

chmod u+x axi_write

Successivamente, digitando ls, dovremmo vedere che il file axi_write è mostrato in verde, quindi è diventato eseguibile.

 

Nell’esempio è stato lanciato senza parametrizzazione, al solo scopo di vedere se il sistema operativo del sistema SoC lo esegue. In effetti viene ritornata la richiesta di lancio fornendo parametri, ovvero una stringa condizionata interna al sorgente C, e precisamente contenuta nel corpo del dell’ IF che testa il numero minimo di registri passati:

if(argc<3) {
printf(“usage: %s address size [d1 d2 … ]\n”,argv[0]);
return 1;
}

La scrittura dei parametri avviene da un indirizzo base, detto access point o memory mapped, che sarà definito all’atto della creazione del wrapper, per un numero di parametri definiti all’atto della creazione del device AXI o AXI lite, ad esempio 4.

architettura

Come notiamo dallo schema a blocchi che rappresenta l’architettura interna dello Xilinx Zynq7000, esistono due sezioni AXI, una indicata con Master e una con Slave.

Le sezioni MGP0 e MGP1 sono le porte master dei core ARM verso la sezione FPGA, salvo diversa indicazione definite a 32 bit, mentre le HPx (high performance n) interconnettono, potenzialmente a 64 bit, la sezione FPGA ai core o via bypass DMA alla DDR3.

Consideriamo un design complesso contenente un elevato numero di IP block, questi debbano scambiare tra loro dati ad alta velocità. Ognuno di questi IP hanno un sorgente VHDL oppure Verilog, che ne descrive l’architettura e in behaviural interno, ma dovranno per forza di cose essere equipaggiati con un secondo sorgente che ne descrive le regole di comunicazione tramite il bus AXI, in definitiva vedremo sempre almeno due file sorgenti.

Ogni AXI link (connessione) contiene necessariamente due parti, un AXI master e un AXI slave.

  • La sezione AXI Master è quella che inizializza le transizioni, sia queste in lettura o in scrittura.
  • La sezione AXI slave è quella che risponde alle transizioni su richiesta del master.

Il flusso dei dati è bidirezionale nel bus a seconda che il master abbia inizializzato un transizione di scrittura o di lettura.

Tipi di AXI interface.

Esistono due tipi di interfacce AXI.

  1. interfaccia AXI memory mapped.
  2. interfaccia AXI streaming.

La seconda è una versione ridotta della prima.

Consideriamo per prima la AXI memory mapped. Abbiamo disponibili due set di segnali una per la lettura e una per la scrittura. Infatti sono coinvolti diverse situazioni e sequenze.

In generale durante una sessione di lettura e scrittura sono coinvolti i seguenti cinque segnali che occupano il canale di trasmissione:

Read transaction (ciclo di lettura).

  1. Read data
  2. Read Address

Write transaction (ciclo scrittura)

  1. Write Address
  2. Write Data
  3. Write Response

Nel caso di interfaccia di tipo AXI streaming dovremmo definire la dimensione dei dati da spedire il burst lenght e altre variabili non necessarie nel memory mapped.

è possibile connettere una AXI memory mapped a una AXI streaming con opportuni accorgimenti di design.

L’unità DMA.

Nello schema a blocchi che mostra l’architettura interna dello ZYNQ notiamo la sezione DMA controller, detta anche DMA engine, direttamente sotto la gestione della sezione ARM solo per quanto riguarda lo stato e lo stop dell’allocazione.  L’unità DMA può creare un canale di comunicazione diretta tra la logica programmata in FPGA e le DDR3. Una volta avviato lo streaming di dati, ad esempio provenienti da convertitori veloci (hardware) collegati a specifici GPIO della PL, la sezione ARM si dedica a altro e la sua attenzione verrà richiesta solo quando necessario tramite segnali gestiti dall’IP GIC (generic interrupt controller). In questo caso gli specifici GPIO saranno parte della sezione indicata con AXI master collegata all’interfaccia ACP (accelerator currency port), a 64 bit, facente parte del protocollo AXI. L’informazione necessaria sarà il burst lenght e la velocità di transizione.

Definizione di un IP di lettura e scrittura da AXI.

Si consideri l’IP direct register_out_0 descritto in un paragrafo precedente. questi ha inizialmente il seguente aspetto.

direct_register_out0

Vogliamo aggiungere delle porte in input, costituite sempre da registri a 8 bit. Identifichiamo nel codice VHDL il punto in cui dichiarare i due nuovi registri.

dichiarazione_reg_in

Vengono dichiarate le nuove porte con il nome reg_in0 e reg_in1 come vettori standard logic.  Notiamo che il codice VHDL in Vivado riserva la posizione corretta per la dichiarazione nell’area commentata .– Users to add ports here

Nelle righe successive sono definiti i registri del protocollo AXI, e vanno lasciati inalterati. Notiamo, scorrendo in basso, la dichiarazione dei 4 registri per il protocollo AXI lite il cui codice è stato automaticamente generato quando per via grafica abbiamo creato il nuovo IP come periferica AXI lite e abbiamo definito numero di registri 4.

registri AXI lite 4

Ora si aggiungono i due registri in lettura, nella sezione di descrizione dell’architettura e quindi dei segnali coinvolti ricordando che l’assegnazione di stati a segnali avviene con il costrutto <=

assegna_registri AXI lite in lettura

Al salvataggio, nell’area del design sources non dovranno essere segnalati errori, nell’immagine sottostante non vi è nulla di evidenziato in rosso da Vivado.

corretto

in questo momento l’IP non è più coerente con il design di partenza e quindi va aggiornata tutta la struttura.  La cosa è segnalata nella finestra del block design.

refresh

Agendo su “refresh IP Catalog” compare, nella parte bassa la finestra dell’IP status.

refresh raccomandation

Nella colonna “Recommendation” è segnalata la necessità di eseguire l’Upgrade dell’IP. Quindi agiamo, ottenendo questa reazione da Vivado.

refresh raccomandation 1

Agendo su “Rerun” si ottiene:

refresh raccomandation 2

Fino a questo momento non vedremo comparire le nuove porte nell’IP perché non collegabili al livello superiore della gerarchia dei blocchi.

Aggiunta delle porte al TOP level.

Portiamoci nel codice VHDL del TOP level del modulo IP che stiamo modificando, e ripetiamo la definizione delle porte.

top_level

Vi sono tre posizioni in cui intervenire sul codice.

  1. definire nell’entity del top level i nuovi port .
  2. definire nell’architecture del top level i components.
  3. definire nel port map del top level in modo che il wrapper possa agganciarsi.

Definizione delle nuove porte nella sezione entity del top module

Si deve procedere per passi dichiarando le porte e il loro comportamento nei vari livelli del design. Innanzitutto portiamoci nel sorgente che descrive l’architettura del nuovo IP. A questo livello si giunge dal block design completo e facendo tasto destro-> edit and pakage IP sul blocco interessato.

Top_module_source

Si entra nei sorgenti facendo doppio click nel punto evidenziato, e aggiungiamo le due porte in input come vettori logici standard a 8 bit, come evidenziato sotto. In questo momento stiamo aggiungendo alla grafica dell’IP i punti di collegamento (nuovi pin) con il resto del block design. La loro definizione porta già l’informazione su cosa saranno collegabili, per drag and drop, tramite fili o bus, al resto dell’architettura.

new_IP_top_module_PORT_definition

Defininizione nell’architecture del top level dei components.

Nello stesso sorgente, scorriamo più in basso fino ad identificare la sezione architecture. Definiamo la forma interna all’IP dei registri hardware.

top_2

Definizione nel port map del top level in modo che il wrapper possa agganciarsi.

In questa terza sezione dello stesso sorgente VHDL definiamo la direzione del bus che sarà agganciabile ai nuovi pin da parte del resto del block design. La sintassi è intuitiva infatti => per un segnale da portare fuori dall’IP e <= per un segnale da portare dentro all’IP via interfaccia AXI.

top_3

Aggiorniamo l’IP nel catalogo.

top_4

Se le nuove porte compaiono nell’anteprima la strada intrapresa è quella corretta. Clicchiamo su “Merge change from customization GUI Wizard“.

Compaiono le nuove porte:

top_5

Agiamo nella sequenza indicata sotto.

  1. Rewiev and pakage
  2. poi repapakage IP  (pulsante in basso)
  3. upgrade selected

Ecco l’aspetto finale del nuovo IP AXI con i due registri in input e i due in output.

Top_module_IP

Colleghiamo il nuovo IP al resto del design ed eseguiamo nel secondo registro il un “Utility vector logic” IP in modo da poter eseguire nei contenuti dei registri, passati da consol PC via AXI (utilizzando l’applicazione AXI write), almeno una operazione booleana bitwise, ad esempio la negazione bit a bit.

Nel block design, facciamo tasto destro “add new ip” e sulla barra di filtro scriviamo “utility”… verrà segnalata la presenza in libreria anche del nostro Utility vector. Confermiamo con un doppio click.

Dichiarazione nel wrapper delle nuove porte definite nel nuovo IP.

Ora si deve procedere rendendo compatibile il wrapper con le nuove porte esistenti nel IP modificato. Questo avviene agendo in tre punti del codice VHDL.

  1. citare le nuove porte nella sezione entity.
  2. citare le nuove porte nella sezione architecture.
  3. citare le nuove porte nella sezione System_i.

Ecco delle immagini chiarificatrici.

Aggiunta alle sezione entity:

wrapper1

Aggiunta alla sezione architecture.

wrapper2

Aggiunta dei collegamenti delle nuove porte agli IP che nella gererachia risultano in posizione inferiore, ovvero l’IP che abbiamo modificato e vogliamo collegare a questo wrapper.

wrapper3

in alto a destra verrà segnalato che la sintesi e l’implementazione sono “Out of date

wrapper4

Procediamo scrivendo le modifiche nel disco cliccando su “change on disk“.

wrapper5

Nell’istanza dell’editor del nuovo IP agire su Tools -> create and package IP.

wrapper6

Procediamo con “Run Automation” in modo che almeno il bus AXI venga connesso agli altri IP dal sistema, evitando errori.

Poi colleghiamo gli ingressi e uscite del nostro nuovo IP, per trascinamento, come in figura.

new_design

Generazione del Bitstream.

Probabilmente in alto a destra sarà presente la dicitura:

wrapper4

Quindi il Bitstream eventualmente presente non è relativo all’ultima versione del design sviluppato, ad esempio dopo avere aggiunto delle porte al custom IP.   Creiamo nuovamente il bitstream, dalla posizione relativa al system Wrapper quindi del design completo.

bitstream

Agendo su Generate bit stream compare un ultimo warning.

bitstream out of date

Confermiamo per vedere attivarsi la procedura di creazione segnalata da una barra progresso.

generating bitstream out of dateIn alto a destro comparirà la segnalazione di “Running”

running

Questo processo potrà prendere qualche minuto

generating 1

Cliccchiamo su OK, e ci verrà proposta una visone dell’implementazione.

generating 2

In alto a destra e indicato che il bitstream è aggiornato.

generating complete