PE2 Sxediasmos kai epeksigisi vasikwn kuklwmatwn - ellak-monades-aristeias/HDL-HELP GitHub Wiki
Για την επεξήγηση της μεθοδολογίας προγραμματισμού σε VHDL θα παρουσιαστεί σε αυτή την ενότητα ο σχεδιασμός δύο πρακτικών υποκυκλωμάτων για την επικοινωνία του FPGA με τα περιφερειακά της πλακέτας. Τα υποκυκλώματα αυτά περιλαμβάνουν τον ελεγκτή για την επικοινωνία με ένα πληκτρολόγιο ακολουθώντας το πρωτόκολλο PS/2 και τον ελεγκτή για τη σύνδεση με μια οθόνη ακολουθώντας το πρωτόκολλο VGA. Για μεγαλύτερη ευκολία ο σχεδιασμός θα στηριχτεί στα αντίστοιχα components των δύο ελεγκτών που βρίσκονται αναρτημένα στο αποθετήριο κώδικα της Μονάδα Αριστείας ΕΛ/ΛΑΚ Ιωαννίνων (Ioannina FOSS Unit of Excellence) και τους παρακάτω συνδέσμους.
Για το πληκτρολόγιο μέσω PS/2:
https://github.com/ioa-maellak/FPGA_controllers_and_more/tree/master/keyboard
Για την οθόνη μέσω VGA:
https://github.com/ioa-maellak/FPGA_controllers_and_more/tree/master/vga
Τα παραπάνω αρχεία τροποποιήθηκαν κατάλληλα για τις ανάγκες του συγκεκριμένου έργου. Οι τελικές εκδόσεις των αρχείων βρίσκονται αναρτημένες στο αποθετήριο του έργου.
Βασικές πληροφορίες σχετικά με το πρωτόκολλο VGA και τον τρόπο λειτουργίας δίνονται στο αποθετήριο κώδικα και το αρχείο readme. Στην ενότητα αυτή η έμφαση δίνεται στη μεταφορά του πρωτοκόλλου σε VHDL και όχι στην επεξήγηση του τρόπου λειτουργίας και επικοινωνίας μεταξύ των συσκευών.
Στην αρχή κάθε VHDL αρχείου θα πρέπει να δηλωθούν οι βιβλιοθήκες που απαιτούνται για τη σύνθεση του προς σχεδίαση κυκλώματος. Όπως αναφέρθηκε και στην προηγούμενη ενότητα, λόγω της προτυποποίησης της γλώσσας από την IEEE η βασική βιβλιοθήκη είναι η IEEE library. Έπειτα ακολουθεί η δήλωση των αντίστοιχων προτυποποιημένων συναρτήσεων που θα χρησιμοποιηθούν. Ενδεικτικά 3 κατηγορίες περιλαμβάνονται στον ακόλουθο κώδικα που καλύπτουν τις βασικότερες ανάγκες βασικών κυκλωμάτων, όπως και αυτά που περιλαμβάνονται στον συγκεκριμένο οδηγό.
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use ieee.std_logic_arith.all;
Οι δύο κυριότεροι τύποι σημάτων είναι η δήλωση απλού καναλιού ενός δυαδικού ψηφίου, std_logic (1-bit channel) και η δήλωση ενός διαύλου παράλληλων καλωδίων με n ψηφίο, std_logic_vector (n-bit channel) ανάλογα με τις απαιτήσεις του κυκλώματος.
Για περισσότερες πληροφορίες σχετικά με τις βιβλιοθήκες και τις συναρτήσεις, που μπορούν να χρησιμοποιούνται κατά τη σχεδίαση εφόσον δηλωθούν, δίνονται οι παρακάτω σύνδεσμοι:http://www.arl.wustl.edu/projects/fpx/class/resources/Libraries%20and%20Packages%20in%20VHDL.htm
https://www.cs.sfu.ca/~ggbaker/reference/std_logic/
http://www.csee.umbc.edu/portal/help/VHDL/stdpkg.html
Το πρωταρχικό στοιχείο κάθε κυκλώματος είναι η δήλωση της οντότητας που περιλαμβάνει τα σήματα εισόδου και εξόδου για το προς σχεδίαση κύκλωμα.
Τα σήματα εισόδου περιλαμβάνουν το κανάλι του ενιαίου ρολογιού του κυκλώματος (clk), ενός σήματος για την επαναφορά του κυκλώματος στην αρχική του κατάσταση (rst) κατά τη λειτουργία του και 3 σήματα για την αποθήκευση δεδομένων στην προσωρινή μνήμη του ελεγκτή:
(α) τη διεύθυνση της μνήμης (vgaAddress), σε αντιστοιχία 1 διεύθυνση ανά pixel της εικόνας, π.χ. 640x480=307.200 pixel => 307.200 διευθύνσεις μνήμης => 19 ψηφία εύρος διευθυνσιοδότησης (2^19=524.288) => vgaAddress : in std_logic_vector(18 downto 0),
(β) τα δεδομένα χρωματικής πληροφορίας (vgaData) που αντιστοιχούν στη θέση μνήμης, θεωρώντας 3 χρώματα * 8 ψηφία/χρώμα = 24 ψηφία συνολικά => vgaData : in std_logic_vector(23 downto 0), και
(γ) ένα σήμα εγκυρότητας των δεδομένων (validData) για τον έλεγχο των εγγραφών στην προσωρινή μνήμη => validData : in std_logic.
Για τον ελεγκτή της οθόνης τα σήματα εξόδου περιλαμβάνουν τη χρωματική πληροφορία (red, green, blue) για κάθε pixel σε μορφή RGB, τα σήματα οριζόντιου και κάθετου συγχρονισμού (hsync : out std_logic; & vsync : out std_logic;) του πρωτοκόλλου VGA και (ανάλογα με το μοντέλο της πλακέτας, όχι σε όλα) το ρολόι χρονισμού (vgaCLK : out std_logic;) στα 25 MHz για ανάλυση 640x480, 60Hz (βλ. Πίνακας 1). Τα σήματα των 3 χρωμάτων στο συγκεκριμένο παράδειγμα έχουν εύρος 8 ψηφίων, red/green/blue : out std_logic_vector(7 downto 0);, αν και μερικά μοντέλα της εκπαιδευτικής σειράς πλακετών της Altera υποστηρίζουν λιγότερα (συνήθως 4) δίνοντας μικρότερο βάθος χρώματος (2^4 vs. 2^8 για κάθε χρωματικό κανάλι).
Μετά τη δήλωση της οντότητας του κυκλώματος, σειρά έχει ο σχεδιασμός της αρχιτεκτονικής του και της δήλωσης των εσωτερικών σημάτων και υποκυκλωμάτων, εφόσον αυτά υπάρχουν. Στην περίπτωση του ελεγκτή VGA θα χρησιμοποιηθεί μια ενδιάμεση μνήμη RAM που θα περιέχει όλη τη χρωματική πληροφορία της εικόνας που προβάλλεται στην οθόνη. Η μνήμη αυτή από τη μια πλευρά πρέπει να διαβάζεται συνεχώς με ρυθμό 640x480x60Hz = 18.432.000 διευθύνσεις ανά δευτερόλεπτο όπως επιτάσσει το πρωτόκολλο VGA. Από την άλλη πλευρά η εγγραφή της μπορεί να γίνεται με όποιο ρυθμό ορίζει η εφαρμογή που θέλει να φτιάξει ο χρήστης.
Για τη δημιουργία της μνήμης η Altera παρέχει έναν εύχρηστο οδηγό με γραφικό περιβάλλον μέσα από το Quartus II (Tools > MegaWizard Plug-In Manager). Ένα σχετικό tutorial δίνεται στον παρακάτω σύνδεσμο
https://www.youtube.com/watch?v=1nhTDOpY5gU
Μετά τη δημιουργία του αρχείου ‘vgaBuffer.vhd’ η οντότητα του υποκυκλώματος της μνήμης θα πρέπει να δηλωθεί ως συστατικό “component” του κυκλώματος του ελεγκτή VGA για να μπορεί να γίνει η σύνθεσή του στη συνέχεια. Η χρήση των components είναι ένας πολύ καλός τρόπος για να σπάσει η σχεδίαση μεγάλων κυκλωμάτων σε μικρότερα υποσυστήματα, ώστε εκτός των άλλων και για πρακτικούς λόγους, να αποφεύγεται η δημιουργία ενός μεγάλου αρχείου σχεδίασης.
Σχήμα 1. Ένα παράδειγμα σχεδίασης με τη χρήση προσχεδιασμένων υποκυκλωμάτων (components) (http://fpgacenter.com/VHDL/terminology.php).
Πρακτικά το μόνο που μεταβάλλεται για τη δήλωση του υποκυκλώματος είναι η λέξη “entity” με τη λέξη “component” και το “end vgaBuffer” σε “end component”:
Στο αρχείο του υποκυκλώματος “vgabuffer.vhd” ως:
entity vgabuffer is
port(clock : in std_logic := ‘1’;
data : in std_logic_vector (23 downto 0);
rdaddress : in std_logic_vector (18 downto 0);
wraddress : in std_logic_vector (18 downto 0);
wren : in std_logic :=’0’;
q : out std_logic_vector (23 downto 0) );
end vgabuffer;
Ενώ η δήλωση στο αρχείο του κυκλώματος του ελεγκτή “vga_controller.vhd” ως:
component vgabuffer is
port(clock : in std_logic;
data : in std_logic_vector (23 downto 0);
rdaddress : in std_logic_vector (18 downto 0);
wraddress : in std_logic_vector (18 downto 0);
wren : in std_logic;
q : out std_logic_vector (23 downto 0) );
end component;
Η ανάθεση των επιθυμητών σημάτων διασύνδεσης του υποκυκλώματος με το κυρίως κύκλωμα γίνεται παρακάτω.
Η δήλωση των σημάτων γίνεται όπως και στην οντότητα με τη χρήση της δεσμευμένης για αυτό το σκοπό λέξης “signal” να προηγείται πάντα της ονομασίας του σήματος. Ο τύπος του σήματος (std_logic, std_logic_vector, κλπ) πρέπει να δηλώνεται επίσης αλλά σε αντίθεση με τα σήματα της οντότητας, δεν γίνεται διαχωρισμός σε εισόδου και εξόδου αφού όλα χρησιμοποιούνται για την εσωτερική καλωδίωση του κυκλώματος. Τα σήματα μπορούν να δηλώνονται ένα προς ένα ή συγκεντρωτικά, διαχωριζόμενα με ‘ , ’ αν είναι του ίδιου τύπου και εύρους. Για παράδειγμα, τα σήματα ‘hcount’ και ’vcount’ μπορούν να δηλωθούν ως
signal hcount : std_logic_vector(9 downto 0);
signal vcount : std_logic_vector(9 downto 0);
ή ως
signal hcount, vcount : std_logic_vector(9 downto 0);
Η επιλογή είναι καθαρά θέμα προτίμησης του προγραμματιστή.
Μια ειδική περίπτωση δήλωσης σημάτων εμφανίζεται για τη χρήση τους σε Μηχανές Πεπερασμένων Καταστάσεων (Finite-State Machine, FSM), όπου δηλώνουν την τρέχουσα κατάσταση του συστήματος. Η FSM είναι ένα ακολουθιακό κύκλωμα που εναλλάσσεται μέσα από μια σειρά καταστάσεων ανάλογα με την τιμή κάποιων σημάτων ελέγχου. Η δήλωση των καταστάσεων γίνεται με τη χρήση της δεσμευμένης για αυτό το σκοπό λέξης “type”, ενώ πρέπει να ακολουθείται από τα ονόματα των καταστάσεων (π.χ. εδώ ‘s1’, ‘s2’). Στην πραγματικότητα οι καταστάσεις μεταφράζονται σε δυαδικά κυκλώματα ελέγχου και η ονοματολόγια τους γίνεται για ευκολία του προγραμματιστή.
type state_type is (s1, s2);
signal state : state_type;
Έπειτα έχοντας ορίσει το “state_type” μπορεί να γίνει ανάθεση σημάτων (εδώ το σήμα “state”) που θα έχουν τις ιδιότητες που οριστήκαν παραπάνω, δηλαδή να εναλλάσσονται μεταξύ ‘s1’ και ‘s2’.
Η διαδικασία δήλωσης των εσωτερικών υποσυστημάτων και σημάτων πρέπει να προηγείται της έναρξης της περιγραφής της αρχιτεκτονικής του κυκλώματος, ώστε να μπορεί να γίνει σωστά η ανάλυση και η σύνθεσή του. Σε αντίθετη περίπτωση το Quartus θα βγάλει μήνυμα λάθους.
Η έναρξη της αρχιτεκτονικής δηλώνεται με τη δεσμευμένη λέξη “begin”. Από αυτό το σημείο αρχίζει ο σχεδιασμός της αρχιτεκτονικής του κυκλώματος.
Το υποκύκλωμα της μνήμης που δηλώθηκε προηγουμένως θα πρέπει και να αρχικοποιηθεί κατά τη σχεδίαση του κυκλώματος ώστε να γίνει μέρος του και να διασυνδεθεί κατάλληλα με τα υπόλοιπα στοιχεία του με τη χρήση εσωτερικών σημάτων που θα πρέπει να δηλωθούν αντίστοιχα παραπάνω. Για λόγους ευκολίας προτείνεται η αρχικοποίηση να γίνεται στην αρχή της αρχιτεκτονικής, αν και αυτό είναι επίσης μια προσωπική απόφαση του προγραμματιστή ανάλογα με τις προτιμήσεις του.
Τα components θα πρέπει να αρχικοποιούνται με μια μοναδική ονοματολόγια πριν το όνομα της οντότητας του υποκυκλώματος που έχει δηλωθεί προηγουμένως (π.χ. comp1 : entityName1, comp2 : entity_name2, κτλ.). Η έκφραση “port map” χρησιμοποιείται για να δηλώσει την αντιστοίχιση μεταξύ εισόδων-εξόδων του υποκυκλώματος και των εσωτερικών σημάτων του κυκλώματος που το εσωκλείει. Για μεγαλύτερη ευκολία μέσα στην παρένθεση του “port map” μπορούν να μπουν μόνο τα εσωτερικά σήματα, αρκεί αυτά να τοποθετηθούν με την αντίστοιχη σειρά προτεραιότητας που εμφανίζονται στην οντότητα του υποκυκλώματος και να είναι του ίδιου τύπου και εύρους με αυτά. Το όνομα του σήματος οντότητας του υποκυκλώματος και του αντίστοιχου του εξωτερικού κυκλώματος μπορεί να είναι το ίδιο. Έτσι στην αρχικοποίηση του παραδείγματος το σήμα “clk” χρησιμοποιείται για να ενώσει το ρολόι του κυκλώματος με το αντίστοιχο του υποκυκλώματος της μνήμης.
comp1 : vgaBuffer port map (clk, dataForVGA, vgaBuffer_rdaddress, addressForVGA, validData, vgaBuffer_dataOut);
Τα σήματα που δεν έχουν δηλωθεί ως σήματα εισόδου στην αρχή της οντότητας πρέπει να δηλωθούν ως εσωτερικά σήματα (βλ. Part 4: Δήλωση εσωτερικών σημάτων). Εδώ πλέον έχει γίνει η διασύνδεση του υποκυκλώματος της μνήμης vgabuffer με το κυρίως κύκλωμα του ελεγκτή μέσα από τα σήματα που δηλώνονται με το “port map”.
Μια ακόμα εκκρεμότητα αποτελεί ο σχεδιασμός της λειτουργίας της FSM που δηλώθηκε προηγουμένως. Για να αναγνωριστεί η επιθυμία του προγραμματιστεί να ορίσει μια FSM από το Quartus, ώστε να γίνει σωστά η σύνθεσή της, θα πρέπει να πληρούνται κάποιοι κανόνες. Ως πρακτική καλού προγραμματισμού σε VHDL, προτείνεται και από την Altera η ακόλουθη δομή των δύο process όπως φαίνεται στον παρακάτω σύνδεσμο:http://quartushelp.altera.com/15.0/mergedProjects/hdl/vhdl/vhdl_pro_state_machines.htm
Η πρώτη process δηλώνει ρητά αφενός την εναλλαγή των καταστάσεων μόνο σε κάθε θετική ακμή του ρολογιού (εφόσον ικανοποιούνται οι συνθήκες ελέγχου) και αφετέρου τη σειρά της ακολουθίας καταστάσεων (από ‘s0’ σε ‘s1’, από ‘s1’ σε ‘s2‘ και από ‘s2’ σε ‘s0’). Η εναλλαγή θα μπορούσε να γίνεται προς οποιαδήποτε συνδυασμό καταστάσεων (π.χ. από ‘s1’ σε ‘s2’ αν ικανοποιείται μια συνθήκη και πίσω σε ‘s0’ αν ικανοποιείται κάποια άλλη) Το σήμα “state” είναι αυτό που περιέχει κάθε χρονική στιγμή την τρέχουσα κατάσταση της FSM. Επίσης, η κατάσταση ‘s0’ δηλώνεται ως η αρχική του συστήματος μέσω της συνθήκες ελέγχου για “reset”.
Η δεύτερη process δηλώνει την τιμή κάποιων σημάτων ανάλογα με την τρέχουσα κατάσταση του συστήματος της FSM. Αυτή η process ολοκληρώνει την FSM ως προς τη λειτουργία της, δηλαδή του ελέγχου διάφορων σημάτων ανάλογα με την τρέχουσα κατάσταση του συστήματος. Αυτή η απευθείας σύνδεση μεταξύ της τιμής ενός σήματος ανάλογα με μια κατάσταση είναι εξαιρετικά χρήσιμη στο σχεδιασμό ψηφιακών κυκλωμάτων.
Στο παράδειγμα του ελεγκτή VGA, ο σκοπός της FSM είναι απλώς να δημιουργήσει ένα ρολόι 25 MHz από το φιξαρισμένο εσωτερικό ρολόι των 50 MHz, που είναι διαθέσιμο στα περισσότερα μοντέλα FPGA. Αν και υπάρχουν εναλλακτικοί τρόποι για την αλλαγή της συχνότητας του ρολογιού, στα πλαίσια του εκπαιδευτικού προσανατολισμού αυτού του έργου, το συγκεκριμένο παράδειγμα λόγω της απλότητάς του μπορεί να δείξει αποτελεσματικά τη λειτουργία και το σχεδιασμό μιας FSM.
Όπως και στο παράδειγμα της Altera, η πρώτη process δηλώνει την εναλλαγή των καταστάσεων από ‘s1’ σε ‘s2’ (when s1 => state <= s2) και από ‘s2’ σε ‘s1’ (when s2 => state <= s1), σε κάθε θετική ακμή του ρολογιού. Επίσης, η κατάσταση ‘s1’ δηλώνεται ως αρχική του συστήματος σε περίπτωση επαναφοράς μέσω του σήματος “rst” (if rst='0' then state <= s1). Ο έλεγχος κάποιας συνθήκης για την εναλλαγή των καταστάσεων δεν απαραίτητος, καθώς η μετάβαση εξορισμού θα πρέπει να γίνεται σε κάθε ακμή του ρολογιού για να προκύψει ο επιθυμητός διπλασιασμός της περιόδου του ρολογιού για τον υποδιπλασιασμό της συχνότητας. Η δεύτερη process δηλώνει την τιμή του σήματος “enable” που για μια περίοδο του ρολογιού “clk” των 50MHz παραμένει στην κατάσταση ‘s1’ και στην τιμή του λογικού ‘1’ (when s1 => enable <= '1'), ενώ για την επόμενη περίοδο του ρολογιού “clk” παραμένει στην κατάσταση ‘s2’ και στην τιμή του λογικού ‘0’ (when s2 => enable <= '0').
Σχήμα 2. Δημιουργία ρολογιού μισής συχνότητας αλλάζοντας κατάσταση σε κάθε θετική ακμή του αρχικού ρολογιού.
Το πρωτόκολλο λειτουργίας VGA επιβάλει από τη συσκευή (σε αυτή την περίπτωση ο ελεγκτής του FPGA) να αποστέλλει δύο σήματα για τον έλεγχο του συγχρονισμού των προβαλλόμενων pixel. Ο έλεγχος αυτός περιλαμβάνει τον οριζόντιο συγχρονισμό για την αλλαγή της γραμμής των pixel και τον κάθετο συγχρονισμό για την ανανέωση της εικόνας όταν όλα τα Pixel έχουν προβληθεί και την επιστροφή στην πάνω αριστερή γωνία της εικόνας για να ξεκινήσει το επόμενο frame. Τα σήματα αυτά πρέπει να έχουν συγκεκριμένες τιμές για συγκεκριμένες χρονικές περιόδους ώστε να γίνονται σωστά οι αλλαγές και να προβάλλεται η εικόνα με επιτυχία στην οθόνη. Για το λόγο αυτό χρησιμοποιούνται οι μετρητές “hcount” και “vcount”. Ο πρώτος αυξάνεται κατά ένα (hcount <= hcount + 1;) κάθε φορά που αλλάζει η κατάσταση του ρολογιού 25MHz, δηλαδή όταν το enable πάει στο λογικό ‘1’, για να δείξει ότι έχει προβληθεί ένα pixel στην οθόνη και να μπορεί να προσδιορίζεται το τρέχον ενεργό pixel. Ο δεύτερος αυξάνεται κάθε φορά που έχει ολοκληρωθεί η προβολή μιας γραμμής pixel και η οθόνη ξεκινάει να προβάλει την επόμενη (if hcount=799 then vcount <= vcount + 1;) για να μπορεί να προσδιορίζεται η τρέχουσα ενεργή γραμμή pixel.
Το σήμα του οριζόντιου συγχρονισμού “sigHsync” πρέπει να έχει την τιμή του λογικού ‘1’ σε όλη τη διάρκεια της προβολής των pixel και να πέφτει στο λογικό ‘0’ για 96 κύκλους ρολογιού (if hcount>655 and hcount<752 then, Sync Pulse Πίνακας 1), 16 κύκλους μετά το τελευταίο ενεργό Pixel της εικόνας στο hcount=639 (elsif (hcount>639 and hcount<656), Front Porch Πίνακας 1) και 48 κύκλους πριν αρχίσει το επόμενο ενεργό Pixel της δεύτερης γραμμής Pixel της οθόνης (or (hcount>751 and hcount<800), Back Porch Πίνακας 1). Στο τέλος κάθε γραμμής 800 Pixel, ο “hcount” μηδενίζεται για να ξεκινήσει ο συγχρονισμός της επόμενης γραμμής Pixel της εικόνας. Οι συνθήκες ελέγχου του μετρητή “hcount” όλων των φάσεων (Front Porch, Sync Pulse, Back Porch) και η αντίστοιχη τιμή του σήματος “sigHsync”, δείχνονται αναλυτικά στο παράδειγμα του ελεγκτή για λόγους αντιστοίχισης με τα νούμερα του Πίνακα 1.
Αντίστοιχα για το σήμα του κάθετου συγχρονισμού “sigVsync” πρέπει να έχει την τιμή του λογικού ‘1’ σε όλη τη διάρκεια της προβολής των 480 γραμμών pixel και να πέφτει στο λογικό ‘0’ για χρόνο ίσο με 2 γραμμές Pixel (if vcount>490 and vcount<493 then) ή 2*800=1600 κύκλους ρολογιού (Sync Pulse Πίνακας 1), 11 γραμμές (elsif (vcount>479 and vcount<491)) ή 11*800=8800 κύκλους μετά το τελευταίο ενεργό Pixel της τελευταίας γραμμής (Front Porch Πίνακας
- και 31 γραμμές (or (vcount>492 and vcount<524)) 31*800=24.800 κύκλους ρολογιού πριν αρχίσει το επόμενο ενεργό Pixel του επόμενου frame της οθόνης (Back Porch Πίνακας 1). Στο τέλος των 524 γραμμών pixel (elsif vcount=524 then), ο μετρητής “vcount” μηδενίζεται για να ξεκινήσει ο συγχρονισμός του επόμενου frame της εικόνας. Όπως και πριν, οι συνθήκες ελέγχου του μετρητή “vcount” όλων των φάσεων (Front Porch, Sync Pulse, Back Porch) και η αντίστοιχη τιμή του σήματος “sigVsync”, δείχνονται αναλυτικά στο παράδειγμα του ελεγκτή για λόγους αντιστοίχισης με τα νούμερα του Πίνακα 1.
Σχήμα 3. Γραφική αναπαράσταση της ζώνης ενεργών pixel και των ενδιάμεσων διαστημάτων χρονισμού της οθόνης (http://www.pyroelectro.com/projects/masochists_video_card/vga_theory.html).
Η συχνότητα λειτουργίας του σήματος “enable” για το ρολόι χρονισμού, οι τιμές των ενεργών Pixel και οι αντίστοιχες χρονικές τιμές ενεργοποίησης των σημάτων συγχρονισμού “sigHsync” και “sigVsync” για διάφορες αναλύσεις και ρυθμούς ανανέωσης δίνονται στον Πίνακα 1.
Πίνακας 1. Οι τιμές χρονισμού για διάφορες αναλύσεις οθόνης.
Έχοντας εξασφαλίσει το συγχρονισμό με την οθόνη σε συμφωνία με αυτά που επιτάσσει το πρωτόκολλο VGA, το επόμενο βήμα είναι να προβληθούν τα περιεχόμενα της μνήμης vgaBuffer στην οθόνη με τη σωστή σειρά, ώστε να σχηματιστεί η αντίστοιχη εικόνα. Άρα για όσο διάστημα οι μετρητές “hcount” και “vcount” βρίσκονται εντός της ζώνης των ενεργών pixel της οθόνης η διεύθυνση της μνήμης (vgaBuffer) θα πρέπει να μεταβάλλεται αντίστοιχα. Σε αυτό το παράδειγμα τα περιεχόμενα της μνήμης περιέχουν την πληροφορία των pixel σειριακά όπως αυτά προβάλλονται και στην οθόνη (οι πρώτες 640 διευθύνσεις τα pixel της πρώτης γραμμής, οι επόμενες 640 της δεύτερης, κτλ.). Για αυτή τη διαδοχική ανάγνωση της μνήμης χρησιμοποιείται ο έλεγχος (if ( (hcount<640) and (vcount<480) ) then). Έχοντας εξασφαλίσει ότι κατά την επανέναρξη του κυκλώματος η διεύθυνση αρχικοποιείται στο μηδέν (if rst='0' then vgaBuffer_rdaddress <= (others => '0');) όπως και η προβολή του πάνω αριστερά pixel, η διαδοχική αύξηση της διεύθυνσης μνήμης για όσο διάστημα βρισκόμαστε στην ενεργή ζώνη θα προβάλει τα περιεχόμενα στην οθόνη.
Μόλις ολοκληρωθεί η προβολή ενός frame στην οθόνη (elsif ( (hcount=640) and (vcount=480) ) then) η διεύθυνση της μνήμης επιστρέφει στην τιμή μηδέν για να ξεκινήσει η ανάγνωσή της εκ νου από την αρχή για την προβολή του επόμενου frame. Η δεύτερη process συνδέει την έξοδο δεδομένων της μνήμης, που περιέχει τη χρωματική πληροφορία για κάθε pixel, στα αντίστοιχα χρωματικά κανάλια εξόδου “red”, “green”, “blue”. Τα 8 λιγότερα σημαντικά ψηφία των περιεχομένων της μνήμης vgaBuffer αντιστοιχίζονται στο κόκκινο κανάλι (red <= vgaBuffer_dataOut(7 downto 0);), τα επόμενα 8 λιγότερα σημαντικά ψηφία των περιεχομένων της μνήμης vgaBuffer αντιστοιχίζονται στο κόκκινο κανάλι (green <= vgaBuffer_dataOut(15 downto 8);) και τα 8 πλέον σημαντικά ψηφία των περιεχομένων της μνήμης vgaBuffer αντιστοιχίζονται στο μπλε κανάλι (blue <= vgaBuffer_dataOut(23 downto 16);). Για τα χρονικά διαστήματα εκτός της ενεργής ζώνης προβολής των pixel (οριζόντια και κάθετα Front Porch+Sync Pulse+Back Porch ), οι τιμές των σημάτων των τριών χρωματικών καναλιών επιστρέφουν στο μηδέν.
Βασικές πληροφορίες σχετικά με το πρωτόκολλο PS/2 και τον τρόπο λειτουργίας δίνονται στο αποθετήριο κώδικα και το αρχείο readme. Όπως και στην περίπτωση του ελεγκτή VGA, στην ενότητα αυτή δίνεται έμφαση στη μεταφορά του πρωτοκόλλου σε VHDL. Τα πρώτα κομμάτια του κώδικα (1-3) έχουν την ίδια λογική με τα αντίστοιχα του ελεγκτή VGA που αναλύθηκαν παραπάνω και γι’ αυτό το λόγο δεν αναφέρονται και εδώ.
Είναι πολύ σύνηθες τα κυκλώματα να πρέπει να αντιδράσουν σε μια εξωτερική μεταβολή ή σε κάποιο σήμα ελέγχου. Υπάρχουν πολλά είδη σεναρίων όπου ένα σήμα που παράγεται από ένα μέρος του κυκλώματος πρέπει να ανιχνευθεί από ένα άλλο μέρος ή ένα διαφορετικό κύκλωμα το οποίο λειτουργεί υπό διαφορετικό ρολόι χρονισμού. Στο συγκεκριμένο παράδειγμα του ελεγκτή PS/2, είναι μια εξωτερική είσοδος δεδομένων που ενημερώνει το υπόλοιπο τμήμα του κυκλώματος, εσωτερικά του FPGA, ότι έχει ανιχνεύσει το πάτημα ενός πλήκτρου. Το πρόβλημα είναι ότι το πληκτρολόγιο στέλνει σειριακά τα δεδομένα με ένα ρυθμό της τάξης των 5 kHz, ενώ ο ελεγκτής εντός του FPGA λειτουργεί σε πολύ υψηλότερη συχνότητα (π.χ. 50 MHz). Πρακτικά αυτό συναντάται όταν το ένα κύκλωμα «μιλάει» πολύ αργά σε ένα κύκλωμα που «ακούει» πολύ γρήγορα για να μπορέσουν να επικοινωνήσουν. Σε όρους κυκλωμάτων αυτό σημαίνει ότι τα ίδια δεδομένα θα διαβάζονται επανειλημμένα ως καινούρια από το πιο γρήγορο κύκλωμα αν έχει σχεδιαστεί απλά σε κάθε κύκλο του δικού του ρολογιού να λαμβάνει ότι βρίσκει στην είσοδό του. Επίσης ασυγχρόνιστα συστήματα ενδέχεται να παρουσιάζουν σφάλματα εγκυρότητας δεδομένων στους καταχωρητές, καθώς εκτός από την ακμή του ρολογιού απαιτείται και ένας πρόσθετος χρόνος για να σταθεροποιηθεί η τιμή τους στο λογικό ‘1’ ή ‘0’.
Ο συγχρονισμός γίνεται με τη χρήση flip-flop που είναι χρονισμένα με το ρολόι του κυκλώματος που παραλαμβάνει την πληροφορία. Σαν στόχο το κύκλωμα των καταχωρητών έχει να ανιχνεύσει μια αλλαγή στην κατάσταση του σήματος εισόδου. Αυτό μπορεί να γίνει εύκολα με την διαδοχική διάδοση του σήματος μέσα από μια αλυσίδα τουλάχιστον δύο καταχωρητών (shift registers) και τον έλεγχο της διαφορετικότητας της τιμής του σήματος πριν και μετά τον τελευταίο καταχωρητή. Η διαδικασία με τρεις καταχωρητές φαίνεται στο Σχήμα 4.
Σχήμα 4. Το κύκλωμα συγχρονισμού και ανίχνευσης αλλαγής κατάστασης.
Το πρώτο στάδιο δέχεται την είσοδο του κυκλώματος του πληκτρολογίου, ώστε να εξασφαλίσει ότι από εκεί και έπειτα οι αλλαγές θα γίνονται στο χρονισμό του FPGA. Τα δύο επόμενα στάδια χρησιμοποιούνται συγκρίνοντας τις εξόδους C1 και C2 για να διαπιστωθεί η μετάβαση από το λογικό ‘0’ στο λογικό ‘1’ (και αντίστροφα) στο σήμα εισόδου. Ο έλεγχος μπορεί να δώσει δύο σήματα εξόδου που να δείχνουν την άνοδο (‘0’ σε ‘1’) ή την πτώση (‘1’ σε ‘0’), όπως φαίνεται στο Σχήμα 4, ή ένα σήμα ανίχνευσης της αλλαγής κατάστασης γενικά. Στον ελεγκτή του πληκτρολογίου, πριν αποσταλούν τα δεδομένα αναγνώρισης του πλήκτρου, το πρωτόκολλο PS/2 ορίζει τη μετάβαση του σήματος “kClk” από το ‘1’ στο ‘0’. Για αυτό επιλέγεται η συνθήκη ελέγχου kClk_Delay2 and (not kClk_Delay1), η οποία δίνει στο σήμα “Fclk” την τιμή ΄1΄ μόνο όταν η παλιά τιμή (C2) είναι ΄1΄ και η καινούρια (C1) είναι ‘0’, δηλαδή ανίχνευση μετάβασης σε χαμηλό δυναμικό.
Στο παράδειγμα του ελεγκτή πληκτρολογίου η FSM έχει τρεις καταστάσεις: (α) την κατάσταση “start”, όπου το σύστημα βρίσκεται σε κατάσταση αναμονής μέχρι την πυροδότηση του “Fclk”, (β) την κατάσταση “data” που διαδέχεται την κατάσταση “start” και καταχωρεί τον κωδικό του πλήκτρου που αποστέλλεται και (γ) την κατάσταση “ready” που διαδέχεται την κατάσταση “data” και ειδοποιεί ότι η σειριακή λήψη του κωδικού του πλήκτρου έχει ολοκληρωθεί μετά από 8 κύκλους του ρολογιού του πληκτρολογίου (if counter=8 then state <= ready). Το κύκλωμα αρχικοποιείται στην κατάσταση “start”.
Η παραπάνω συμπεριφορά της FSM δηλώνεται από τα σήματα “enable” και “full”. Το πρώτο παίρνει την τιμή ΄1΄ μόνο στην κατάσταση “data” ώστε να δηλωθεί ότι το πληκτρολόγιο είναι έτοιμο να στείλει τον κωδικό του πλήκτρου, ενώ το δεύτερο παίρνει την τιμή ΄1΄ μόνο στην κατάσταση “ready” ώστε να δηλωθεί ότι ολοκληρώθηκε η διαδικασία λήψης του κωδικού, με σκοπό η τιμή που λήφθηκε να προωθηθεί στο κύκλωμα του FPGA που θα εξετάσει ποιο πλήκτρο πατήθηκε και να αντιδράσει αναλόγως με τη σχεδίαση. Και τα δύο σήματα έχουν την τιμή ΄0΄στην αρχική κατάσταση αναμονής “start”.
Έχοντας εξασφαλίσει με την FSM ότι κατά την κατάσταση “ready” (if enable='1' then) σε κάθε ακμή του ρολογιού “kClk” (if Fclk='1' then) θα αποστέλλεται σειριακά από το κανάλι “kData” και μια νέα τιμή του μοναδικού κωδικού 8 ψηφίων κάθε πλήκτρου, τα πεδία του σήματος “button” υφίστανται κάθε φορά μια δεξιά μετατόπιση. Η διαδικασία της μετατόπισης ολοκληρώνεται όταν ο μετρητής “counter” πάρει την τιμή 8, δηλαδή μετά από 9 επαναλήψεις (για να γίνει λήψη και του parity bit) όπου και η κατάσταση της FSM αλλάζει σε “ready” και το σήμα “full” παίρνει την τιμή ‘1’. Χρησιμοποιώντας ως ένδειξη το σήμα “full” (if full='1' then), στην επόμενη process τα περιεχόμενα του “button” μεταφέρονται στην έξοδο του ελεγκτή (keyboard_data <= button(7 downto 0)).