Lo standard Amba-LITE – Un modello sintetizzabile di periferiche master e slave

In un sistema AMBA AHB-Lite il master è la periferica responsabile dell’inizio di una qualsiasi transazione, in lettura o scrittura; lo slave è invece oggetto di tale transazione. Il presente articolo descrive in dettaglio maggiore il funzionamento di queste periferiche presentandone modelli VHDL sintetizzabili in logiche programmabili.

Lo standard definisce un protocollo parallelo sincrono caratterizzato da una struttura intrinsecamente pipelined; supporta trasferimenti singoli od a burst con possibilità di lock del possesso del bus. Prevede un meccanismo di hand-shake che consente l’inserzione di un numero di wait-state variabile negli accessi alle periferiche in funzione delle prestazioni di queste; definisce una semplice procedura per la segnalazione degli errori. La figura 1 evidenzia una generica periferica in un sistema basato su bua AMBA AHB-Lite.

Figura 1: la periferica master.

Figura 1: la periferica master.

La seguente tabella elenca i segnali di interfaccia della periferica verso il bus, da un lato, ed il processore  host, dall’altro, e ne riporta una sommaria descrizione. Nella descrizione  VHDL, i diversi segnali di interfaccia che appaiono omogenei tra loro possono essere raggruppati in record; la dichiarazione di questi tipi può essere inclusa in due package ahb e ahbmst_pkg di uso generico. Nel listato 1, ad esempio, è riportata la dichiarazione dei record utilizzati per i segnali del bus AHB in ingresso/uscita dal master.

1. — AHB master inputs
2. type ahb_mst_in_type is record
3. hready: std_ulogic;                       — transfer done
4. hresp : std_logic;                        — response type
5. hrdata: std_logic_vector(31 downto 0);    — read data bus
6. end record;
7.
8.— AHB master outputs
9. type ahb_mst_out_type is record
10. haddr : std_logic_vector(31 downto 0);   — address bus (byte)
11. hwrite: std_ulogic; — read/write
12. hsize : std_logic_vector(2 downto 0);    — transfer size
13. hburst: std_logic_vector(2 downto 0);    — burst type
14. hprot : std_logic_vector(3 downto 0);    — protection control
15. htrans: std_logic_vector(1 downto 0);    — transfer type
16. hlock : std_ulogic; — lock request
17. hwdata: std_logic_vector(31 downto 0);   — write data bus
18. end record;
Listato 1

L’utilizzo di record rende in genere più leggibile e maneggevole il  codice sorgente. I file relativi ai due package unitamente ad un semplice modello del master sono disponibili nella sezione download della rivista; tale modello può essere utilizzato come riferimento per sviluppare soluzioni più complesse. Vediamone gli aspetti principali. Anche in questo caso, i segnali interni al modulo sono raggruppati in un record come mostrato nel listato 2.

1. type reg_type is record
2. ahbo : ahb_mst_out_type;
3. dmao : ahb_dma_out_type;
4. hwdata_i : std_logic_vector(31 downto 0);    — internal register
5. blength : burst_length;                      — burst length
6. aphase : std_logic;                          — address phase in progress
7. dphase : std_logic;                          — data phase in progress
8. burst : std_logic;
9. end record;
Listato 2

Il significato dei diversi segnali, quando non già evidente, sarà chiarito nel seguito; un processo dedicato implementa la logica combinatoria che ne definisce i prossimi valori. Una prima sezione di questo processo evidenziata nel listato 3 serve a descrivere la gestione della fase di indirizzamento dei trasferimenti; il  segnale aphase indica che questa è in corso.

1. — Address Phase
2. — end of data phase. no further transfer in burst mode
3. if (ahbi.hready=’1’ and r.burst=’0’) then
4. v.aphase := ‘0’; end if;
5. if start=’1’ then
6. v.aphase:= ‘1’; end if;
7. — ctrl during address phase
8. if start=’1’ then
9. v.ahbo.hwrite:=dmai.write;
10. v.ahbo.hsize :=dmai.size;
11. v.ahbo.hburst:=dmai.burst;
12. end if;
13. if (ahbi.hready=’1’ and r.burst=’0’) then          — end data phase
14. v.ahbo.htrans:=htrans_idle; end if;
15. if (ahbi.hready=’1’ and r.burst=’1’) then          — burst in progress
16. v.ahbo.htrans:=htrans_seq; end if;
17. if start=’1’ then                                  — single transfer or first transfer of a burst
18. v.ahbo.htrans:=htrans_nonseq;
19. end if;
20. if ahbi.hresp=hresp_error then                     — in case of error :
21. v.ahbo.htrans:=htrans_idle;                        — abort current transfer
22. v.burst :=’0’;                                     — end burst (if in progress)
23. end if;
24. — drive address lines
25. if (r.aphase=’1’ and ahbi.hready=’1’ and r.burst=’1’) then
26. — end of data phase. burst in progress
27. v.ahbo.haddr:=incr_addr(r.ahbo.haddr,r.ahbo.hburst,r.ahbo.hsize); end if;
28. if start=’1’ then                                   — starting address
29. v.ahbo.haddr:=dmai.addr; end if;
Listato 3

Viene resettato (riga 4) se la fase può terminare - in quanto il dispositivo  slave indirizzato ha asserito la linea hready - e se non sono previsti altri trasferimenti di una transazione a burst; viene invece asserito (riga 6) quando viene istruita una richiesta di trasferimento da parte dell’interfaccia host. In accordo a questa, sono pilotati (riga 9-11) i segnali di controllo hwrite, hsize ed hburst del bus AHB. La linea htrans viene pilotata invece, come previsto dallo standard:

➤ in idle se (riga 14) la fase dati termina e non sono previsti ulteriori trasferimenti in una transazione a burst o se (riga 21) la periferica indirizzata chiude il  trasferimento con errore;

➤ in non_seq (riga 18) nei trasferimenti singoli o nella prima fase di indirizzamento di un trasferimento a burst;

➤ in seq (riga 16) nei trasferimenti successivi al primo nel caso di burst. Il bus indirizzi, infine, assume il valore indicato all’atto della richiesta di trasferimento (riga 29) in trasferimenti singoli o nel primo di un burst; nei successivi viene invece incrementato automaticamente (riga 27).

La funzione incr_addr calcola opportunamente l’incremento in base al tipo di burst istruito (incrementale o wrapping) ed alla dimensione in byte del singolo trasferimento. Il listato 4 evidenzia la sezione del processo combinatorio che descrive la fase dati.

1. — Data phase
2.   if ahbi.hready=’1’ then
3.    v.dphase := r.aphase; end if;
4.   — drive data bus during data_phase
5.   if (ahbi.hready=’1’) then — start of new data phase
6.    v.ahbo.hwdata:= v.hwdata_i; — drive new data
7.    v.hwdata_i := dmai.wdata; — latch input data for burst
8.   end if;
Listato 4

Questa ha inizio quando la fase di indirizzamento termina; in questo caso (riga 3) viene asserita la flag dphase. Nel caso di operazioni di scrittura, quindi, il valore della linea dati in ingresso viene latchato (riga 6) in un registro interno ed il precedente contenuto di questo viene riportato (riga 7) sul bus AHB; tale logica è conseguenza della natura pipelined del bus. L’ultima parte del processo combinatorio, infine, genera i segnali di interfaccia verso il processore  host. Come si vede, il segnale busy viene asserito (riga 2) durante tutta la durata del trasferimento. Se il trasferimento termina in presenza di un errore, questo (riga 4) viene segnalato al processore. Diversamente, nel caso di una richiesta di accesso in scrittura, in modalità a burst (riga 10), al termine di ogni fase di indirizzamento viene asserito il segnale rd richiedendo  il pre-fetch del dato della prossima transazione. Nel caso invece di un accesso in lettura, al termine di ogni fase dati, il valore letto viene riportato in uscita (riga 7) e viene asserito il segnale  wr (riga 16) La figura 2 mostra la simulazione del modello descritto nel caso di un accesso a burst di tipo wrapping, con singoli tra sferimenti a byte e terminato in presenza di un errore da parte della periferica slave indirizzata.

Figura 2: esempio di funzionamento della periferica master

Figura 2: esempio di funzionamento della periferica master

Un’interfaccia slave

La figura 3 evidenza una periferica slave in un sistema basato bus AMBA AHBLite; nella precedente tabella 1 è stata fornita una breve descrizione di tutti questi segnali di interfaccia verso il bus (considerati come ingressi al master) ad eccezione della linea HSel.

Figura 3: la periferica slave.

Figura 3: la periferica slave.

 

Tabella 1: Segnali di ingresso/uscita di una periferica master AHB-Lite.

Tabella 1: Segnali di ingresso/uscita di una periferica master AHB-Lite.

Questa serve a selezionare la periferica come oggetto della transazione corrente; il segnale  viene generato dalla matrice di connessione, come avremo modo di vedere in seguito. Ogni dispositivo slave, quindi, deve rispondere alla trasferimento in corso solo se la propria linea di selezione è attiva. Dal sito web della rivista è possibile scaricare un modello VHDL che include un semplice template per descrivere una periferica slave; vediamone in dettaglio alcune caratteristiche. Il  modello utilizza una serie di segnali interni raccolti in un record di tipo internal come evidenziato nel listato 6.

1. type internal is record
2. cnt : waitstate_cnt;                     — wait state counter
3. active : std_logic;                      — data phase flag
4. error : std_logic;                       — error flasg
5. addr : std_logic_vector(31 downto 0);    — AHB address
6. write : std_logic;                       — AHB write ctrl
7. size : std_logic_vector(2 downto 0);     — AHB data transfer size
8. end record;
Listato 6

I segnali  active e ed error, in particolare, servono rispettivamente ad indicare che la fase del trasferimento è in corso ed a segnalare un qualche tipo di errore, come nel caso di un accesso in scrittura ad un registro a sola lettura. I segnali  addr, write e size invece sono utilizzati per memorizzare al termine della fase dati l’indirizzo e lo stato dei segnali di controllo della transazione; sono resi necessari dalla natura pipelined del bus AMBA AHB-Lite. Il modello della periferica proposto, come al solito, si basa su un processo combinatorio che descrive le equazioni di prossimo stato e prossima uscita, ed uno registrato che inferisce, invece, i flip-flop. Il processo combinatorio definisce inizialmente dei valori di default per i segnali  e le variabili interne al fine di evitare che siano inferiti latch durante la fase di sintesi (listato 7).

1. slvo_v.hready   := ‘1’;
2. slvo_v.hresp    := HRESP_OKAY;
3. slvo_v.hrdata   := (others => ‘0’);
Listato 7

Quindi include tre diverse sezioni che servono a descrivere, nell’ordine, la gestione della fase dati, il controllo della fase di indirizzamento del trasferimento e la logica della applicazione  specifica.  Il listato 8 evidenzia in particolare  il codice per la gestione della fase dati.

1. — Data Phase
2. if (r.active=’1’) then                                — data phase in progress
3. if slvi.hready=’1’ then                               — data phase will terminate
4. v.active :=’0’;
5. v.cnt :=0;
6. end if;
7. if r.cnt<tcnt then                                     — to extend data phase
8. v.cnt :=v.cnt+1;
9. slvo_v.hready :=’0’;
10. end if;
11. if r.error=’1’ then                                    — two cycle error response
12. slvo_v.hready:= ‘0’;
13. slvo_v.hresp :=HRESP_ERROR;
14. end if;
15. if (slvo_r.hready=’0’ and slvo_r.hresp=HRESP_ERROR) then
16. slvo_v.hready:=’1’;                                     — terminate data phase
17. slvo_v.hresp :=HRESP_ERROR;
18. end if;
19. if slvo_r.hresp=HRESP_ERROR then
20. v.error :=’0’;                                           — release error flag
21. end if;
22. end if;
Listato 8

Viene dapprima verificato se il trasferimento  in corso deve essere terminato; in questo caso viene rilasciata la flag active e azzerato il contatore di wait-state (riga 4-5). Diversamente  il contatore viene incrementato ad ogni ciclo di clock e la linea hready viene forzata a ‘0’ per estendere la durata dell’accesso (riga 8-9). Se, invece, nell’accesso corrente è stato evidenziato un errore (riga 11), questo viene dapprima segnalato estendendo il  trasferimento per un ciclo di clock; successivamente la transazione viene chiusa (riga 16-17) resettando la flag di error (riga 20). Tale procedura, si ricordi, è espressamente specificata dallo standard AMBA AHB-Lite; a causa della natura pipelined del bus che vede sovrapporsi la fase dati di un accesso e la fase di indirizzamento di quello successivo, infatti, in presenza di errore, e prima di poter terminare la transazione, è necessario che il master cancelli anche il trasferimento seguente forzandone come IDLE il tipo. Il listato 9 descrive invece la fase di indirizzamento. Come si vede, se il dispositivo è selezionato e la fase di indirizzamento può essere terminata, indirizzi e controllo vengono registrati internamente.

1. if (slvi.hsel=’1’ and slvi.hready=’1’) then
2.   — device selected. address phase to be terminated.
3.   v.addr := slvi.haddr; — latch address and ctrl
4.   v.write := slvi.hwrite;
5.   v.size := slvi.hsize;
6.   if slvi.htrans(1)=’1’ then — SEQ or NONSEQ transfer
7.    v.active := ‘1’; end if; — init data phase
8.    slvo_v.hready := ‘0’; end if;
9. end if;
Listato 9

Inoltre se si tratta di una transazione di tipo SEQ o NONSEQ, viene segnalata la fase dati come attiva al ciclo successivo; inoltre se la periferica necessita di wait-state per chiudere il ciclo, il segnale hready viene forzato a ‘0’. L’ultima sezione del modello comprende la logica specifica dell’applicazione. A titolo di esempio riportiamo nel listato 5, il caso di una periferica che implementa:

➤ un registro Reg1_R a 32 bit, all’indirizzo relativo Reg1Addr:=0x0, di tipo read/write ed a cui è possibile accedere in modalità 8, 16 o 32 bit;

➤ un registro Reg2_R a 8 bit, all’indirizzo relativo Reg2Addr=0x5, di tipo a sola lettura.

1. — Output to Host Interface
2.   v.dmao.busy:= v.aphase or v.dphase ;
3.   if (r.dphase=’1’ and ahbi.hresp=’1’ and ahbi.hready=’1’) then
4.    v.dmao.error := ‘1’; else
5.    v.dmao.error := ‘0’;
6.   end if;
7.   v.dmao.rdata := ahbi.hrdata;
8.   v.dmao.rd:=’0’;
9.   v.dmao.wr:=’0’;
10.   if (v.dmao.error=’0’ and v.ahbo.hwrite=’1’) then
11.   if (start=’1’ and v.burst=’1’) then v.dmao.rd := ‘1’; end if;
12.    if (r.aphase=’1’ and ahbi.hready=’1’ and r.burst=’1’) then
13.     v.dmao.rd:=’1’; end if;
14.   end if;
15.   if (v.dmao.error=’0’ and v.ahbo.hwrite=’0’) then
16.    if (r.dphase=’1’ and ahbi.hready=’1’) then v.dmao.wr:=’1’; end if;
17.   end if;
Listato 5

La figura 4 mostra la simulazione della risposta della periferica nel caso di un accesso in lettura all’indirizzo Reg2Addr seguito da un accesso in scrittura all’indirizzo 0x06; non essendo questo registro implementato, la periferica termina la transazione con errore.

Figura 4: esempio di funzionamento della periferica slave.

Figura 4: esempio di funzionamento della periferica slave.

 

 

 

Scarica subito una copia gratis

Scrivi un commento

Seguici anche sul tuo Social Network preferito!

Send this to a friend