Dopo aver presentato la scheda di sviluppo a basso costo per FPGA della famiglia IGLOO di ACTEL/Microsemi, sperimentiamo un primo progetto dove utilizziamo un controllo di tastiera per la generazione di toni.
Per incominciare a sperimentare progetti utilizzando l’AGL-NANO-KIT, completo di sorgenti in linguaggio Verilog. Gli strumenti necessari al test, insieme al KIT, sono una tastiera a matrice e un semplice buzzer. La tastiera richiesta originariamente è una matrice sei righe per tre colonne, per un totale di diciotto tasti, in grado di gestire non solo i toni, ma anche altre funzioni. Per provare la sezione toni sarà comunque sufficiente anche una semplice e più facilmente reperibile tastiera 4x4.
Struttura del progetto: schema a blocchi
Con l’ambiente libero 8.5 regolarmente installato, se avete scaricato il progetto, è sufficiente, dopo aver lanciato libero 8.5, selezionare menu Project > Open project e con un doppio click su file di progetto PRJ aprirlo (figura 1).
Si noti che essendo già compilato correttamente, le icone di sintesi e Place and Route sono colorate in verde. Il progetto come schema a blocchi è invece presentato in figura 2.
Qui possiamo evidenziare il PLL che utilizza il clock di scheda a 20 MHz, la sezione di controllo della tastiera a matrice (Keyscan e shift register), oltre che del blocco generatore toni. A schema si evidenzia anche la presenza di un blocco di ricezione trasmissione dati di tipo asincrono a 19200 BAUD. Pur essendo presente in progetto, questa sezione non sarà approfondita, per motivi di spazio. Tramite un pin (nel nostro progetto il pin 20) è possibile attivare la generazione dei toni e anche di melodie, usando dei comandi in arrivo dal PC, in alternativa ai comandi di tastiera. Nel nostro caso poiché il pin 20 è stato posizionato in modalità HW, tutti i comandi possibili potranno arrivare solo dalla tastiera. I pin da utilizzare per questo progetto sono decisamente pochi: sei pin per la scansione delle righe di tastiera, tre pin per la lettura delle colonne, pin di reset (attivo basso), pin di clock, pin di selezione fra modalità SW o HW, output audio oltre che dei pin RX e TX della sezione seriale.
Top level del progetto
La figura 3 ci introduce alla parte codice sorgente del progetto. Il top del progetto è il Top_tone_ip.
In questo file sono dichiarati il modulo Tone_ip.v (che è il progetto vero e proprio) e i moduli per la gestione del clock di sistema. Sempre guardando la figura 3 osserviamo che esistono tre moduli, come struttura gerarchica, per la gestione del clock di ingresso: clk_by_2.v, divideby5.v e PLL_20_10. Sia il clk_by_2.v che il divideby5.v sono due semplici file verilog, dove il clock in arrivo dall’esterno viene diviso per due o per cinque. Il PLL_20_10 è invece una IP creata direttamente nell’ambiente di sviluppo, che ha configurato il PLL disponibile sul chip Actel. Cliccando infatti due volte sull’icona PLL_20_10, si apre automaticamente la videata di configurazione grafica della periferica (figura 4).
Qui è possibile inserire la frequenza di ingresso come valore (per noi 20 MHz) e scegliere se la sorgente è un pin di tipo Globale ad alto fan out (Hard-wired I/O), un ingresso standard / External I/O, oppure in arrivo da una net interna del progetto. Scelto l’ingresso, si definisce il valore della frequenza di uscita, che nel nostro caso è 10 MHz. Va fatto notare che questo PLL è in grado di genere fino a tre frequenze, partendo da un’unica sorgente (uscite GLA, GLB e GLC) e le uscite supplementari, se selezionate, attivano i relativi campi, dove inserire le frequenze desiderate. I valori in blu sono le frequenze che realmente il PLL è in grado di generare; ricordiamo infatti che i registri di moltiplicazione e divisione sono unici per tutte e tre le frequenze generabili. Nel progetto la frequenza da 10 MHz, che viene usata dal modulo tone_ip.v, è quella generata tramite il PLL.
Controllo della tastiera
Proseguendo nell’analisi del progetto, troviamo il modulo tone_ip.v, nel quale i vari moduli sottostanti vengono dichiarati e collegati. Il controllo della tastiera a matrice viene sviluppato dal modulo Keyscan.v. Questo modulo provvede alla generazione dei segnali di scansione per le sei righe della tastiera e all’acquisizione del tasto premuto, controllando i tre ingressi delle colonne, infine consente il relativo salvataggio su uno shift register a 18 bit. Il listato 1 riporta una parte di codice, che provvede alla scansione.
// contatore di righe per scandire ed acquisire always@(posedge SYS_CLK or negedge reset_l) begin if(~reset_l) begin row <= 3’b000; end else if (re_kb_clk) // rising edge drive row data begin if(row == 3’b111) row <= 3’b000; else row <= row + 1; end end // Funzione per driving delle uscite tastiera righe tipo - ONE HOT function [5:0] drive_key_scan; input [2:0] row; begin case(row) 3’b000: drive_key_scan = 6’b000000;// scan inactive 3’b001: drive_key_scan = 6’b000000;// scan inactive 3’b010: drive_key_scan = 6’b000001;// scan active row 0 3’b011: drive_key_scan = 6’b000010;// scan active row 1 3’b100: drive_key_scan = 6’b000100;// scan active row 2 3’b101: drive_key_scan = 6’b001000;// scan active row 3 3’b110: drive_key_scan = 6’b010000;// scan active row 4 3’b111: drive_key_scan = 6’b100000;// scan active row 5 default: drive_key_scan = 6’b000000;// scan inactive endcase end endfunction // copia valori drive_key_scan in uscita sui pin assign KEYSCAN_OUT = drive_key_scan(row); // legge lo stato delle colonne e le copia nello shift register a 18 bit always@(posedge SYS_CLK or negedge reset_l) begin if(~reset_l) begin shift_reg <= 18’h00000; keyscan_data <= 18’h00000; end else if (fe_kb_clk) // falling edge capture col data begin if (enable_ks) begin if(row == 3’b111) keyscan_data[17:0] <= {KEYSCAN_IN[2], KEYSCAN_IN[1],KEYSCAN_IN[0],shift_reg[17:3]}; else shift_reg[17:0] <= {KEYSCAN_IN[2], KEYSCAN_IN[1],KEYSCAN_IN[0],shift_reg[17:3]}; end end end
Listato 1 |
Dal codice osserviamo che la funzione è sviluppata semplicemente codificando il valore row binario a tre bit, nella corrispondenza di tipo ONE HOT e copiandolo sui pin di uscita KEYSCAN_OUT. Il valore row è a sua volta gestito da un semplice contatore, che viene incrementato alla velocità del clock di tastiera, che è 19,5 KHz, tale valore entra nel modulo Keyscan.v ed è generato dal modulo clk_gen.v. L’acquisizione dei valori di colonna viene eseguita attraverso una funzione, che testando il valore di row decide se caricare e shiftare il registro shift_reg[17:0] (valore in corso), oppure, accorgendosi che si è arrivati al massimo numero di righe con row = 111, concludere l’attività, copiando il valore sul registro keyscan_data, che è lo stato dei 18 tasti disponibili. Una volta acquisito il valore keyscan_data passa attraverso il modulo debounce.v, che provvede alla funzione di antirimbalzo. Tale funzione è molto semplice ed è una rilettura del valore d’ingresso con aggiornamento del valore di uscita, solo in presenza di cambiamento di stato confermato alla seconda rilettura.
Generazione toni
La generazione dei toni è affidata al modulo tone_generator.v. Questo modulo utilizzando un PWM con variazione in durata e frequenza, permette la generazione dei toni, che per la gestione da tastiera sono coincidenti con la classica scala musicale. Come anticipato all’inizio dell’articolo il progetto, che è una estrapolazione di un sistema HMI (human machine interface) presenta anche la possibilità di ricevere via seriale comandi e valori per la generazione di toni. Pur non approfondendo la sezione seriale (che richiederebbe la spiegazione di tutto il protocollo comandi in arrivo dal PC), dobbiamo evidenziare che il modulo di generazione toni può ricevere comandi sia dal modulo di ricezione seriale serial.v, sia in HW dalla tastiera di sistema. La scelta fra le due opzioni è dettata dallo stato del pin HW_SW, che per le nostre prove deve essere posizionato allo stato logico alto (HW). Questa selezione viene fatta nel modulo tone_ip.v (listato 2).
// contatore di righe per scandire ed acquisire always@(posedge SYS_CLK or negedge reset_l) begin if(~reset_l) begin row <= 3’b000; end else if (re_kb_clk) // rising edge drive row data begin if(row == 3’b111) row <= 3’b000; else row <= row + 1; end end // Funzione per driving delle uscite tastiera righe tipo - ONE HOT function [5:0] drive_key_scan; input [2:0] row; begin case(row) 3’b000: drive_key_scan = 6’b000000;// scan inactive 3’b001: drive_key_scan = 6’b000000;// scan inactive 3’b010: drive_key_scan = 6’b000001;// scan active row 0 3’b011: drive_key_scan = 6’b000010;// scan active row 1 3’b100: drive_key_scan = 6’b000100;// scan active row 2 3’b101: drive_key_scan = 6’b001000;// scan active row 3 3’b110: drive_key_scan = 6’b010000;// scan active row 4 3’b111: drive_key_scan = 6’b100000;// scan active row 5 default: drive_key_scan = 6’b000000;// scan inactive endcase end endfunction // copia valori drive_key_scan in uscita sui pin assign KEYSCAN_OUT = drive_key_scan(row); // legge lo stato delle colonne e le copia nello shift register a 18 bit always@(posedge SYS_CLK or negedge reset_l) begin if(~reset_l) begin shift_reg <= 18’h00000; keyscan_data <= 18’h00000; end else if (fe_kb_clk) // falling edge capture col data begin if (enable_ks) begin if(row == 3’b111) keyscan_data[17:0] <= {KEYSCAN_IN[2], KEYSCAN_IN[1],KEYSCAN_IN[0],shift_reg[17:3]}; else shift_reg[17:0] <= {KEYSCAN_IN[2], KEYSCAN_IN[1],KEYSCAN_IN[0],shift_reg[17:3]}; end end end
Listato 2 |
Qui vengono caricati in maniera fissa i valori in formato HEX e come si vede per la gestione da tastiera, l’unica variabile è il periodo, che cambia a secondo del tono, mentre durata e duty cicle sono fissati al medesimo valore. Questi parametri sono invece gestibili dinamicamente attraverso la sezione seriale. L’abilitazione dei toni è legata alla presenza di tasto premuto sui tasti da 10 a 18. Il registro Key_detect viene utilizzato come operatore per il case, dove in base al suo valore viene assegnato al registro hw_buz_freq l’espressione esadecimale corrispondente alla nota prescelta. Key_detect è la copia dello stato dei tasti 10-18, mentre hw_tone_en è la funzione OR dei medesimi presi in uscita dal modulo antirimbalzo. I tasti da 1 a 9 non sono utilizzati in questa sezione del progetto.
Implementazione pratica
Dopo aver brevemente analizzato la struttura del progetto, passiamo alla parte di prova. Come specificato in apertura, per la sperimentazione del progetto, oltre allo schedino KIT di prova, serve una tastiera e un buzzer. Poiché è molto più probabile avere in laboratorio una classica tastiera di tipo a matrice 4x4, abbiamo implementato il progetto utilizzando solo quattro delle righe scandite dal keypad controller. Inoltre, come abbiamo visto nel codice esposto, soltanto nove tasti concorrono nella generazione dei toni. Pertanto su una tastiera 4x4 avremo tre righe di tasti attivi e per ogni riga tre tasti su quattro operativi. Lo schema è quindi quello visualizzato in figura 6, dove abbiamo riportato i due connettori J7 e J9 presenti sul nano KIT e il collegamento della tastiera e del buzzer. Scegliendo dal menu Project > Settings… potete accedere alla scelta del componente della famiglia IGLOO da utilizzare. Eseguito tale cambiamento potrete, senza alcuna altra modifica, lanciare la sintesi cliccando sull’icona Synplify di Libero 8.5 (figura 1) e una volta conclusa lanciare il place e route utilizzando l’icona Designer. Il sw di Designer permette la configurazione fisica del componente sia come pin sia come selezione dei livelli logici sui banchi di I/O, oltre a moltissime altre caratterizzazioni e verifiche (dalle timing come prestazioni, ai consumi di corrente). L’interfaccia utente di Designer si presenta quindi come in figura 5.
In questa fase, se utilizzerete la copia del progetto presa dal web Actel, dovrete per forza modificare la scelta del componente selezionando dal menu Tools > Device selection... e scegliere AGLN250V2Z package 100 pin. Una volta compilato dovrete piazzare i pin selezionando l’icona I/O attribute editor e il risultato finale sarà molto simile a quanto in figura 6.
Si noti l’inserimento delle resistenze di pull down sui segnali, che corrispondono alle tre colonne o ingressi della FPGA in arrivo dalla tastiera stessa. Posizionati i pin non resta che lanciare il layout (utilizzando la medesima icona) e generare il file di programmazione. Generando questo file, ricordatevi di attivare la creazione del file nome_progetto.stp oltre che del *.Pdb. Conclusa questa sezione e dopo averla salvata sarà sufficiente tornare all’ambiente libero 8.5 e lanciare il Sw di programmazione del chip selezionando l’icona FlashPro. Prima di quest’ultima operazione ricordiamo di collegare alla porta USB del PC la chiavetta HW di programmazione fornita con il Kit.