giovedì 29 agosto 2013

FPGA: Verifica della memoria AL422B tramite SignalTap II Logic Analyzer

Precedentemente abbiamo parlato del chip di memoria AL422B, vediamo oggi come verificarne in prima battuta il corretto funzionamento tramite un design su logiche programmabili che simuli una scrittura di dati veloce ed una lettura più lenta dalla memoria FIFO.

Nel nostro design avremo diversi domini di clock tutti derivati dal clock principale generato dall’oscillatore a 50 MHz presente sulla scheda che confineremo in distinte entità per evitare problematiche assai poco piacevoli e suddividere in parti più piccole il tutto.

Possono infatti nascere problemi durante l’analisi e sintesi con processi sensibili contemporaneamente a più fronti di salita all’interno di macchine a stati (es. costrutto VHDL case).

schematic

Abbiamo già parlato dell’entità SyncReset in precedenti articoli, riassumendo brevemente serve per rendere sincrona la de-asserzione del segnale reset cercando così di evitare stati metastabili.

L’entità AL422B_Reader simula la lettura dei dati e gestisce il reset della scrittura dei dati implementando una semplice macchina a stati scandita dal clock rck a 0.25 MHz (simulando ad esempio la lettura da parte di un lento microcontrollore) generato a partire dal clock a 50 MHz.

state machine

Al reset la macchina a stati parte dallo stato wait_state in cui vengono attesi 0.1ms per inizializzare il chip AL422B come descritto nel relativo datasheet prima di passare allo stato reset_write_state in cui viene riavviata la scrittura da parte dell’entità AL422B_Writer. Dopodiché si passa allo stato reset_read_state in cui viene resettato l’indice di lettura interno alla memoria per iniziare nello stato process_state la lettura dalla prima locazione di memoria. Una volta letto il valore 192 (scelto arbitrariamente) la lettura è ultimata e si passa nuovamente allo stato reset_write_state.

Sotto riportato un estratto della macchina a stati AL422B_Reader (alla fine dell’articolo il progetto è interamente scaricabile):

fstate

In cui è possibile notare la gestire dei segnali REn, WEn, RRSTn, writer_reset. In particolare è da notare che al reset della lettura della memoria tramite RRSTn il segnale REn è portato al livello logico alto (lettura disabilitata), come consigliato dall’ultima versione del datasheet e dall’articolo precedente.

Passiamo all’entità AL422B_Writer così definita:

entity writer 

Analizziamo solamente il processo write_proc tralasciando la generazione del clock da 25 MHz ottenuta tramite un contatore. Il processo si occupa di scrivere sulla memoria FIFO tramite il bus ad 8 bit data_in i numeri progressivi da 0 a 192 per poi fermarsi fino alla riattivazione col segnale di reset che fa ripartire il conteggio.

writer_impl

La verifica del codice tramite la sola simulazione richiede l’emulazione del chip AL422B, eseguibile ad esempio tramite dei processi fifo_write e fifo_read come

testbench emulate

Che però andrebbero migliorati per imitare il perfetto comportamento del chip. Seguiamo in questo caso, anziché concentrarci sull’emulazione, una strada alternativa: utilizzeremo un analizzatori di stati logici per verificare a “runtime” il nostro progetto.

Quartus II include l’utilissimo tool SignalTap II Logic Analyzer (richiede l’attivazione del talkback, vedi precedente articolo per l’abilitazione) che permette di esaminare in tempo reale il comportamento dei segnali interni senza utilizzare pin aggiuntivi ma tramite l’interfaccia JTAG. Il circuito esterno composto dal chip AL422B con relativi condensatori bulk e di disaccoppiamento dovrà naturalmente essere collegato alla FPGA (con GND in comune) durante il debug con SignalTap.

diagram

Avremo quindi gratuitamente un analizzatori di stati logici virtuale con tanto di trigger, l’unico prezzo da pagare sarà in termini di risorse (LEs e memoria) sulla FPGA in quanto verrà caricato assieme al nostro design. Le risorse utilizzate dipenderanno dal numero di funzionalità e di campioni da memorizzare

Creiamo innanzitutto un nuovo file SignalTap tramite il menù File / New scegliendo SignalTap II Logic Analyzer File

new

Apparirà l’interfaccia grafica del programma

signaltap

Come prima cosa tramite il pulsante Setup scegliamo il nostro programmatore JTAG, dopodiché indichiamo tramite il pulsante sfoglia (i puntini ) sulla destra nella finestra Signal Configuration il segnale di clock che servirà a SignalTap per campionare gli altri segnali. Assicuriamoci che sia il segnale a frequenza maggiore per evitare la perdita di eventi, l’analisi delle tempistiche di Quartus riporterà la frequenza massima campionabile.

L’analizzatore logico campionerà i dati ad ogni fronte di salita, Altera raccomanda l’utilizzo di un segnale globale non gated di clock sincrono ai segnali da acquisire.

Premuto il pulsante sfoglia apparirà una finestra Node Finder che permetterà di scegliere un nodo dal nostro design. Scegliamo pre-synthesis nelle opzioni per visualizzare i nomi prima della sintesi e portiamo il pin clock nei nodi selezionati tramite un doppio click o selezionandolo e premendo il pulsante >.

node_finder

I segnali pre-sintesi elencati esistono dopo l’elaborazione del design ma prima di ogni ottimizzazione di sintesi, se non si sta utilizzando la compilazione incrementale aggiungere solamente questa tipologia di segnali.

Sempre nella finestra Signal Configuration è possibile scegliere il numero di campioni da visualizzare al trigger impostando Sample depth, nell’Instance Manager in alto apparirà il corrispondente utilizzo di risorse e sarà possibile capire se il nostro dispositivo sarà in grado di contenere l’analizzatore con le impostazioni scelte risparmiandoci così eventualmente una compilazione con esito negativo.

signaltap_2

Nell’immagine sopra la FPGA utilizzata dispone solamente di 26 blocchi di memoria di medie dimensioni, coi nodi selezionati da monitorare ed una profondità di 2K campioni (altre impostazioni di default) verranno utilizzati 15 blocchi (circa 56k bits) ed 828 LEs.

Nell’area centrale della finestra (nella scheda Setup) è possibile aggiungere tramite doppio-click i nodi da visualizzare e configurare eventuali trigger. Scegliendo Basic AND il trigger verrà valutato se tutte le condizioni impostate saranno verificate mentre scegliendo Basic OR basterà una sola delle condizioni per scatenare il trigger. Sono disponibili inoltre opzioni avanzati di Trigger che non vedremo in questo articolo.

Prima di iniziare il monitoraggio sarà necessario ricompilare il progetto e caricare nuovamente sulla FPGA il nostro design dopodiché tramite il pulsante Run Analysis (cerchiato in rosso nell’immagine seguente) al verificarsi di trigger sarà possibile visionare i nodi monitorati.

instance manager

Impostando ad esempio un trigger su data_in 5 allo scatenarsi dell’evento nella scheda data sarà visionabile l’andamento dei segnali

signaltap_3

Il programma permette di impostare la visualizzazione di bus di segnali con codifiche predefinite (es. unsigned decimal, Hexadecimal, etc..) o tramite tabelle mnemoniche (utile per visualizzare gli stati) e contiene altre funzioni che lasciamo scoprire al lettore.

Analizziamo adesso tramite questo potente strumento il nostro design:

1.reset pressed_2

Alla pressione del pulsante di reset lo stato iniziale passa a wait sul fronte di salita di rck

3.wait-reset_write_2

Lo stato dopo 0.1ms passa a reset_write, il puntatore di scrittura della memoria viene azzerato tramite writer_reset che nell’entità AL422B_Writer porta wrstn al livello logico basso per attivare il reset (in questo caso già al livello basso). La scrittura viene abilitata e finito il reset potrà iniziare.

4.reset_write-read_2

Al fronte di rck successivo lo stato passa a reset_read per l’azzeramento del puntatore di lettura della memoria tramite il segnale rrstn. Il segnale wrstn sincronizzato col dominio di clock wck viene portato al livello logico alto tramite il segnale writer_reset, sincronizzato col dominio di clock rck. Inizia la scrittura progressiva dei numeri nella memoria tramite il bus data_in. Il primo fronte di salite wck dopo il reset non comporta la memorizzazione del numero, il numero 0 non sarà quindi scritto in memoria (vedi datasheet AL422-15 Write Cycle Timing)

7.full_2

Al fronte di rck successivo lo stato passa a process, il segnale rrstn di reset di lettura della memoria viene disabilitato ed inizia la lettura della memoria dopo il ciclo disabilitato (vedi datasheet AL422-14 Read Cycle Timing). In data_out è possibile vedere i dati correttamente letti dalla memoria che iniziano dal valore 1 e progrediscono fino al valore 192 dopodiché la macchina a stati passa a reset_write

8.full

Nello stato reset_write il bus data_out assume il valore 193 in quanto la disabilitazione della lettura tramite il segnale ren ha effetto dal ciclo successivo (vedi datasheet AL422-14 Read Cycle Timing). Come visto dal diagramma di stato lo stato passa poi nuovamente a reset_read e a process ripetendo la scrittura / lettura della memoria

Dopo un’ispezione manuale è possibile constare che in prima approssimazione il chip AL422B funziona correttamente e sarà possibile utilizzarlo con maggiore sicurezza anche con microcontrollori lenti senza scomodare facilmente un analizzatore di stati logici reale.

Scarica il progetto analizzato nell’articolo

Nota: Il progetto scaricabile utilizza la scheda di sviluppo EP2C5 Mini Board, cambiare il dispositivo ed i pin con le impostazioni della scheda di sviluppo utilizzata

lunedì 12 agosto 2013

Introduzione ad ASF (sesta parte) – USB

Indice degli articoli su ASF

Microcontrollori come AVR UC3 ed XMega hanno in alcuni modelli un modulo hardware per la comunicazione USB, la scrittura del software per gestire il protocollo non è però banale e molto articolata, la libreria ASF ci viene in aiuto tramite uno stack USB che può risparmiarci molti mesi di lavoro permettendo di focalizzarsi su cosa rende unica la nostra applicazione.

xmega_usb

Non analizziamo in questo articolo nel dettaglio tutto lo stack USB che offre molteplici possibilità ma vediamo in modo semplice come sostituire la vecchia comunicazione seriale con la più moderna USB presente su tutti i dispositivi di ultima generazione.

Utilizzeremo la classe USB CDC (Communication Devices Class), non una classe del mondo della programmazione ad oggetti ma un insieme di specifiche a cui attenerci che ci esonera dallo scrivere uno specifico driver per l’Host USB (il sistema operativo lato pc ad esempio) in quanto già presente per generici dispositivi USB CDC ed utilizzabile sotto Windows con un semplice file .inf. La comunicazione verrà semplicemente vista come una porta seriale virtuale utilizzabile con semplicità da un software sul sistema operativo.

Esistono anche altre classi USB ed è possibile crearne personalizzate, sotto una tabella riassuntiva (non completa) per rendere meglio l’idea di cosa possono offrire alcune classi USB ed alcuni vantaggi/svantaggi.

usb_class

Lo stack USB di Atmel è composto da tre moduli: UDC, UDI ed UDD

usb_stack

UDC (USB Device Controller) fornisce un’astrazione di alto livello del dispositivo USB, presenta metodi come udc_start(), udc_stop() per avviare e fermare lo stack USB, udc_attach(void), udc_detach() per collegare il dispositivo al bus quando la tensione è idonea e scollegarlo dal bus rimuovendo le resistenze interne di pull-up sulle linee D- e D+.

UDI (USB Device Interface) il modulo è distinto per ogni classe di dispositivo che si vuole utilizzare, utilizzeremo UDI for CDC che offre metodi per controllare la linea seriale virtuale e gestire il trasferimento dati

UDD (USB Device Driver) fornisce un’astrazione di basso livello del dispositivo USB, gestisce eventi provenienti dall’hardware come interruzioni chiamando a sua volta funzioni dei moduli UDC e UDI, non utilizzeremo direttamente questo modulo.

Il funzionamento dello stack USB si basa sulle interruzioni, questa soluzione garantisce bassa latenza e non richiede cicli di attesa, bisogna porre però particolare attenzione. Le interruzioni USB hanno bassa priorità e possono essere bloccate da interruzioni con priorità maggiore o sezioni critiche di codice.

Le specifiche USB impongono un tempo massimo di 2ms per assegnare l’indirizzo USB, procedura di basso livello che si svolge durante l’enumerazione, al collegamento del dispositivo.

Gli host USB usano poi un timeout, non previsto dalle specifiche USB, per rilevare e resettare un dispositivo che non risponde ai messaggi di controllo, sotto una tabella di esempio dei tempi di timeout

timeout
CBW: Command Block Wrapper, ZLP: Zero Lenght Packet, CSW: Command Status Wrapper

Tralasciando le varie sigle che si riferiscono a meccanismi di basso livello del protocollo USB di cui non parleremo in questo articolo, è possibile notare che per avere compatibilità con i principali sistemi operativi la MCU deve rispondere entro 5 secondi a delle richieste particolari ricevute tramite USB.

L’utilizzo delle funzionalità di Debug (es. Debug con AVR Dragon) può interferire con il funzionamento del modulo hardware USB del microcontrollore, con la modalità USB OCD break mode disabilitata eventuali breakpoint saranno immediatamente soddisfatti ma il comportamento USB sarà imprevedibile mentre con la modalità abilitata le transazioni USB verranno completate prima di fermare l’esecuzione del programma e il micro non accetterà ulteriori transazioni e risponderà con un messaggio NACK ad eventuali richieste dell’host.

Nota: Seppur non specificato nel datasheet o nel manuale del programmatore la modalità USB OCD break mode è automaticamente configurata dal debugger e non richiede intervento manuale. Fonte: supporto tecnico Atmel

Le funzioni più interessanti sono nel modulo UDI con singola interfaccia CDC, sotto riportate per comodità:

udi_cdc

Aggiungiamo prima di tutto al nostro progetto HelloASF creato negli articoli precedenti il modulo USB Device tramite il menù ASF / ASF Wizard come visto negli articoli precedenti per gli altri moduli, scegliamo la variante cdc_stdio dal menù a discesa come mostrato in figura e premiamo Apply 

asf_module

cdc_stdio fornisce al nostro progetto un’interfaccia Standard I/O (stdio) che implementa dietro le quinte la classe USB CDC permettendo così di utilizzare metodi come printf(), scanf(), getc(), puts(), etc.. le funzioni del modulo UDI for CDC rimangono però estremamente utili per inviare e ricevere byte o per attendere la ricezione di dati dall’host USB.

Verrà automaticamente aggiunto al nostro progetto tutto quello che serve per utilizzare l’USB con la classe CDC tramite stdio, notiamo dall’ASF Explorer l’aggiunta dei vari moduli dello stack USB.

asf_explorer

La prima operazione sarà adesso impostare il clock del modulo USB, nel file conf_clock.h eliminiamo i commenti ai #define relativi al clock usb come nell’immagine seguente

usb_clk

Per il modulo USB verrà quindi utilizzato l’oscillatore RC interno da 32 MHz con la calibrazione di fabbrica a 48 MHz, non sarà però utilizzabile direttamente per la CPU se perlomeno si vuole rimanere entro le specifiche raccomandate (32 MHz) ed evitare l’overclock della CPU.

Una possibilità è utilizzare i prescaler per ridurre la frequenze di clock a 24 MHz, è però anche possibile utilizzare un oscillatore esterno o l’oscillatore RC interno da 2 MHz e tramite il PLL moltiplicare per 16 la frequenza ed ottenere così 32 MHz, seguiremo quest’ultima strada:

clock_pll

Il file conf_usb.h contiene le impostazioni di configurazione per l’USB CDC, è obbligatorio modificarne il Vendor ID con un identificativo rilasciato dietro pagamento dal consorzio USB per applicazioni commerciali.

Per utilizzare stdio_usb è necessario impostare alcuni #define a funzioni del modulo stdio_usb come mostrato di seguito

USB_conf

Da notare l’ #include finale al file <stdio_usb.h> per poter risolvere correttamente le funzioni aggiunte.

Dal file è possibile specificare tramite USB_DEVICE_ATTR se l’alimentazione è prelevata dal bus USB oppure no ed un eventuale risveglio remoto del dispositivo.

Nota per i dispositivi alimentati indipendentemente dal bus USB: E’ necessario un modo per rilevare lo stato della connessione USB, la scheda EWS utilizzata come è possibile vedere dallo schema sul pin PA7 presenta un divisore di tensione 1:3 per questo scopo, 5v saranno rilevati circa come 1,67 e sarà possibile misurarli senza problemi tramite l’ADC per esempio. Sarà poi necessario modificare la funzione stdio_usb_init() nel file stdio_usb.c mostrata di seguito

stdio_usb_init

Sostituendo il valore true del parametro di stdio_usb_vbus_event() con l’effettivo valore del bus USB.

Alimentando continuamente da bus USB il micro questa operazione non sarà però necessaria.

Le impostazioni di comunicazione infine permettono di impostare il data rate e gli altri parametri tipici di una comunicazione seriale che devono coincidere col programma sull’host usb.

Modifichiamo adesso il file main.c col seguente codice

main_code

Dopo l’inizializzazione del servizio sysclk notiamo che vengono abilitati gli interrupts, passaggio fondamentale dato che tutto lo stack USB si basa su essi per il funzionamento.

Con l’istruzione stdio_usb_init() inizializziamo il modulo ASF stdio_usb che si occuperò di avviare lo stack USB ed impostare stdin ed stdout all’implementazione USB CDC.

All’interno del ciclo while è stata utilizzata semplicemente l’istruzione printf() per inviare tramite USB una stringa di testo alla porta seriale virtuale.

Il micro è pronto, non rimane adesso che modificare il file .inf che il Wizard ha aggiunto al progetto con le informazioni corrette sul nostro dispositivo (VID, PID) e che dovremo indicare a Windows per l’installazione del driver. Nel progetto allegato, scaricabile alla fine dell’articolo è possibile trovare un esempio di come modificare il file.

Nell’immagine seguente il terminale PuTTY collegato al micro XMega.

terminale

Anche per oggi è tutto, la libreria ASF permette di nascondere le complessità dietro al protocollo USB e ci fornisce strumenti semplici da utilizzare per concentrarci sulla nostra applicazione e scambiare senza problemi dati ad esempio tra micro e pc. Spesso il supporto USB è un fattore rilevante e permette di abbattere il BOM (Bill of Materials) evitando l’utilizzo di un IC dedicato.

Scarica il progetto dell’articolo