PE4 Dimiourgia ekpaideutikou kuklwmatos 2 - ellak-monades-aristeias/HDL-HELP GitHub Wiki
Σχεδιασμός κυκλώματος λειτουργίας χρωματισμού οθόνης
Στην προηγούμενη ενότητα (ΠΕ3 – «Δημιουργία εκπαιδευτικού κυκλώματος 1») έγινε η παρουσίαση του πρώτο πλήρους εκπαιδευτικού κυκλώματος αυτού του έργου. Σε αυτή την ενότητα θα σχεδιαστεί ακόμα ένα πλήρες κύκλωμα που θα χρησιμοποιεί τα ίδια δομικά στοιχεία αλλά για διαφορετικό αυτή το φορά σκοπό. Σε αυτή την ενότητα η επιθυμητή λειτουργία που θέλουμε να εκτελείται στο FPGA board είναι η λειτουργία ενός συστήματος που θα δίνει στο χρήστη τη δυνατότητα να χρωματίζει τα pixel της οθόνης κινώντας έναν κέρσορα μέσω του πληκτρολογίου. Ο κέρσορας καταλαμβάνει ένα pixel της οθόνης το οποίο χρωματίζεται μαύρο ώστε να είναι ευδιάκριτος σε σχέση με το άσπρο χρώμα του υπόλοιπου καμβά της οθόνης. Με τα πλήκτρα ‘W’, ‘A’, ‘S’ και ‘D’ ο χρήστης μπορεί να μετακινεί τον κέρσορα πάνω, αριστερά, κάτω και δεξιά αντίστοιχα κατά μια θέση ή ένα pixel. Έπειτα μπορεί με τα πλήκτρα ‘R’, ‘G’ και ‘B’ να χρωματίζει το συγκεκριμένο pixel κόκκινο, πράσινο ή μπλε αντίστοιχα.
Για την επεξήγηση της μεθοδολογίας προγραμματισμού σε VHDL θα παρουσιαστεί σε αυτή την ενότητα ο σχεδιασμός των υποκυκλωμάτων που λειτουργούν ως ενδιάμεσος κρίκος μεταξύ των δύο ελεγκτών (πληκτρολόγιο και οθόνη) για να προσδώσουν στο κύκλωμα την επιθυμητή λειτουργία. Τα αρχεία των ελεγκτών που βρίσκονται στον φάκελο “FPGA controllers” θα τροποποιηθούν κατάλληλα για τις ανάγκες του κάθε παραδείγματος λειτουργίας (εδώ χρωματισμός των pixel της οθόνης). Οι τελικές εκδόσεις όλων των αρχείων βρίσκονται αναρτημένες στο αποθετήριο του έργου στο φάκελο “Ex2: Χρωματισμός οθόνης”.
Αρχείο: “color_pixels.vhd”
Η δήλωση ακολουθεί την ίδια λογική με αυτή που αναλύθηκε σε προηγούμενη ενότητα (ΠΕ2 - Σχεδιασμός και επεξήγηση βασικών κυκλωμάτων).
Τα σήματα εισόδου περιλαμβάνουν το ρολόι του κυκλώματος (clk, clk : in std_logic), το σήμα για την επαναφορά του κυκλώματος στην αρχική του κατάσταση (rst, rst : in std_logic) κατά τη λειτουργία του και τα δύο σήματα εισόδου του ελεγκτή του PS/2 πληκτρολογίου που περιέχουν το κανάλι των δεδομένων του πλήκτρου που πατήθηκε (kData, kData : in std_logic) και το κανάλι χρονισμού (kClk, kClk : in std_logic).
Τα σήματα εξόδου αντίστοιχα περιλαμβάνουν τα τρία χρωματικά κανάλια RGB (red : out std_logic_vector(7 downto 0);, green : out std_logic_vector(7 downto 0);, blue : out std_logic_vector(7 downto 0);) για την προβολή της εικόνας στην οθόνη μέσω του ελεγκτή VGA μαζί με τα σήματα συγχρονισμού (hsync : out std_logic;, vsync : out std_logic;, vgaCLK : out std_logic;, blank : out std_logic;, sync : out std_logic;). Να σημειωθεί ότι για λόγους πληρότητας τα σήματα ελέγχου του ελεγκτή VGA που δηλώνονται εδώ καλύπτουν όλα τα μοντέλα της εκπαιδευτικής σειρά DEX της Altera. Όπως θα φανεί στη συνέχεια, στην περίπτωση του μοντέλου DE1, που χρησιμοποιήθηκε για τον έλεγχο των κυκλωμάτων σε αυτό το έργο, ορισμένα από αυτά τα σήματα εξόδου είναι περιττά. Κατά τη σύνθεση του κυκλώματος το Quartus II θα εμφανίσει σχετικό μήνυμα μη χρήσης όλων των σημάτων αλλά αυτό είναι κάτι που δεν επηρεάζει τη λειτουργικότητα του κυκλώματος.
Σε αυτό το σημείο θα πρέπει να δηλωθεί η χρήση των ελεγκτών του πληκτρολογίου και της οθόνης. Η δήλωση αυτή ακολουθεί την ίδια διαδικασία με αυτή που περιγράφηκε στην Ενότητα 2 («ΠΕ2 – Σχεδιασμός και επεξήγηση βασικών υποκυκλωμάτων»).
Η δήλωση των σημάτων γίνεται όπως και στην οντότητα με τη χρήση της δεσμευμένης για αυτό το σκοπό λέξης “signal” να προηγείται πάντα της ονομασίας του σήματος. Η δήλωση ακολουθεί την ίδια λογική με αυτή που αναλύθηκε στην ενότητα 2 («ΠΕ2 - Σχεδιασμός και επεξήγηση βασικών κυκλωμάτων»). Όλα τα σήματα που πρόκειται να χρησιμοποιηθούν στην εσωτερική δομή της αρχιτεκτονικής του κυκλώματος θα πρέπει να δηλωθούν ρητά εδώ. Η FSM που δηλώνεται αρχικά εδώ και θα οριστεί στη συνέχεια θα χρησιμοποιηθεί για τον έλεγχο της αναγνώρισης του κωδικού του πλήκτρου που αποστέλλεται από το πληκτρολόγιο (type state_type is (s1,s2,s3,s4);).
Τα δύο κυκλώματα των ελεγκτών που δηλώθηκαν στο Part 3, θα πρέπει τώρα να αρχικοποιηθούν στο παρόν κύκλωμα και να δηλωθούν τα εσωτερικά σήματα που θα συνδέονται στα κανάλια εισόδου και εξόδου τους. Το πρώτο κατά σειρά δήλωσης υποκύκλωμα είναι ο ελεγκτής του πληκτρολογίου:
comp1: keyboard_controller port map ( clk, rst, kClk, kData, sig_keyboard_data );.
Ο ελεγκτής δέχεται ως είσοδο τα σήματα του πληκτρολογίου (kClk και kData), το γενικό ρολόι του κυκλώματος χρωματισμού οθόνης (clk) και το σήμα επαναφοράς (rst), ενώ παράγει στην έξοδό του τον κωδικό του πλήκτρου που πατιέται από το χρήστη (sig_keyboard_data).
Το δεύτερο κατά σειρά δήλωσης υποκύκλωμα είναι αυτό του ελεγκτή της οθόνης:
comp2: vga_controller port map ( clk, rst, sig_keyboard_data2, sig_hsync, sig_vsync, sig_vgaCLK, sig_red, sig_green, sig_blue);.
Ο ελεγκτής δέχεται ως είσοδο τον κωδικό του πλήκτρου που πατιέται από το χρήστη (sig_keyboard_data2), το γενικό ρολόι του κυκλώματος χρωματισμού οθόνης (clk) και το σήμα επαναφοράς (rst), ενώ παράγει στην έξοδό του τα τρία χρωματικά κανάλια RGB (sig_red, sig_green, sig_blue) και τα αντίστοιχα σήματα χρονισμού (sig_hsync, sig_vsync, sig_vgaCLK).
Σε ένα PS/2 πληκτρολόγιο η πίεση κάποιου πλήκτρου προσδιορίζεται από έναν κωδικό σάρωσης. Αυτός ο κωδικός συνδέεται με ένα φυσικό κλειδί. Έτσι για παράδειγμα, το αριστερό πλήκτρο Shift και το δεξί πλήκτρο Shift έχουν διαφορετικούς κωδικούς σάρωσης όπως και κάθε γράμμα ή αριθμός του πληκτρολογίου. Όταν πιεστεί ένα πλήκτρο ένας κωδικός σάρωσης “Make” αποστέλλεται στη θύρα PS/2 από το πληκτρολόγιο, ενώ όταν το πλήκτρο αφεθεί ξανά, ένας άλλος κωδικός σάρωσης “Break” αποστέλλεται στη θύρα PS/2.
Για όλα τα γράμματα και ψηφία o κωδικός σάρωσης “Make” είναι ένα μόνο Βyte ενώ ο κωδικός σάρωσης “Break” αποτελείται από δύο Byte (το F0 Hex byte και το ίδιο Byte του “Make”, π.χ. 12 πατώντας και F0,12 αφήνοντας το Left Shift). Οι κωδικοί σάρωσης δεν έχουν καμία σχέση με τους κωδικούς ASCII των βασικών χαρακτήρων. Υπενθυμίζεται ότι ο κώδικας ASCII για ένα κεφαλαίο γράμμα είναι διαφορετικός από τον κώδικα ASCII του αντίστοιχου πεζού γράμματος, ενώ εδώ όχι . Στο επίπεδο διασύνδεσης PS/2, για τη διάκριση ενός κεφαλαίου από ένα πεζό γράμμα θα πρέπει να ελέγχετε εάν έχει πατηθεί το πλήκτρο Shift (ή το πλήκτρο CapsLock) πριν πατηθεί το πλήκτρο γράμματος και πριν αφεθεί ξανά το πλήκτρο Shift.
Για παράδειγμα, για το κεφαλαίο Α θα πατηθεί το πλήκτρο αριστερό Shift, μετά το πλήκτρο Α, ενώ μετά αφήνεται πρώτα το πλήκτρο Α και μετά το πλήκτρο αριστερό Shift. Η παραπάνω διαδικασία θα στείλει τις ακόλουθες τιμές κωδικών “Make” και “Break” (bytes) στη θύρα PS/2:
12 , 1C , F0 1C , F0 12 .
Οι κωδικοί σάρωσης για όλα τα πλήκτρα σε ένα πληκτρολόγιο PS/2 φαίνονται στο ακόλουθο σύνδεσμο http://www.cs.ucr.edu/\~jtarango/cs122a/lab3/lab3\_scancodekeyboard.png.
Το πρόβλημα στην παραπάνω διαδικασία είναι ότι κάθε κωδικός πλήκτρου στέλνεται πρακτικά δύο φορές. Έτσι, μια απλή συνθήκη ελέγχου αναγνώρισης του επιθυμητού κωδικού (π.χ. 12 για το ‘Α’) θα γινόταν αληθής δύο φορές μια κατά την πίεση και μια κατά την αποδέσμευση του πλήκτρου, με αποτέλεσμα η μετακίνηση του κέρσορα να γίνεται κατά δύο θέσεις αριστερά (λόγω ‘Α’ σε αυτό το παράδειγμα) αντί για μια. Για να ξεπεραστεί αυτό το πρόβλημα υλοποιείται η συγκεκριμένη FSM του κυκλώματος.
Η πρώτη process δηλώνει την αλληλουχία αλλαγής των δυνατών καταστάσεων που έχουν προηγουμένως οριστεί στο Part 4 με βάση την ικανοποίηση κάποιων συνθηκών (αν υπάρχουν). Ως αρχική κατάσταση σε αυτό το στάδιο ορίζεται η ‘S1’ (if rst='0' then state <= s1;), όπου το κύκλωμα είναι σε θέση ετοιμότητας για να δεχθεί τον επόμενο κωδικό σάρωσης από τον ελεγκτή του πληκτρολογίου. Έπειτα, όταν το κύκλωμα αποστέλλει κωδικό ο οποίος είναι διαφορετικός από αυτόν που είχε αποσταλεί κατά την τελευταία έγκυρη κατάσταση (αν δεν υπάρχει π.χ. την πρώτη φορά σε σχέση με την τιμή μηδέν) περνάει στην κατάσταση ‘S2’ (when s1 => if sig_keyboard_data/=sig_keyboard_data1 then state <= s2;). Το κύκλωμα παραμένει στην κατάσταση ‘S2’ μόνο για ένα κύκλο ρολογιού μέσα στον οποίο γίνεται αποστολή του κωδικού που διαβάστηκε στον ελεγκτή της οθόνης για να διευκρινιστεί αν πρέπει να αλλάξει κάτι στην οθόνη (βλ. VHDL κώδικας για τον ελεγκτή οθόνης VGA παρακάτω) και μετά μεταβαίνει στην κατάσταση ‘S3’. Επίσης, στην κατάσταση ‘S2’ ο κωδικός του πλήκτρου αποθηκεύεται προσωρινά σε έναν καταχωρητή για μελλοντική χρήση. Στην κατάσταση ‘S3’ το κύκλωμα περιμένει να αδρανοποιηθεί το πληκτρολόγιο για να μεταβεί στην κατάσταση ‘S4’, συμπεριφορά που αναγνωρίζεται με τη μη αποστολή κάποιου κωδικού και την εμφάνιση του λογικού μηδέν στο κανάλι εισόδου δεδομένων (when s3 => if sig_keyboard_data="00000000" then state <= s4;). Τέλος, από την κατάσταση ‘S4’ το κύκλωμα αναμένει να δεχθεί από τον ελεγκτή του πληκτρολογίου τον κωδικό “Break” που θα ταυτίζεται με τα περιεχόμενα του κωδικού “Make” που αποθηκεύτηκε στον καταχωρητή στην κατάσταση ‘S2’ (when s4 => if sig_keyboard_data=sig_keyboard_data1 then state <= s1;). Έχοντας ολοκληρώσει τον κύκλο πίεση-αποδέσμευση πλήκτρου, το κύκλωμα επιστρέφει στην αρχική κατάσταση ‘S1’ όπου αναμένει τον κωδικό του επόμενου πλήκτρου. Όπως πάντα η εναλλαγή των καταστάσεων γίνεται μόνο σε κάθε θετική ακμή του ρολογιού (elsif rising_edge(clk)).
Η δεύτερη process δηλώνει την τιμή του σήματος ελέγχου ‘enable’ ανάλογα με την τρέχουσα κατάσταση του συστήματος της FSM. Το σήμα γίνεται ενεργό μόνο στην κατάσταση ‘S2’ (when s2 => enable <= '1';). Αυτή η process ολοκληρώνει την FSM ως προς τη λειτουργία της, δηλαδή του ελέγχου της πίεσης-αποδέσμευσης πλήκτρων από το χρήστη για τη μετακίνηση του κέρσορα ή το χρωματισμό του pixel.
Σε αυτό το τμήμα της σχεδίασης η πρώτη process δηλώνει την υλοποίηση ενός καταχωρητή για την προσωρινή αποθήκευση του κωδικού ενός νέου πλήκτρου στο σήμα ‘sig_keyboard_data1’, κατά τη διάρκεια που το σύστημα βρίσκεται στην κατάσταση ‘S2’ (if enable='1' then sig_keyboard_data1 <= sig_keyboard_data;). Όπως περιγράφηκε παραπάνω, τα περιεχόμενα του ‘sig_keyboard_data1’ χρησιμοποιούνται τόσο κατά τον έλεγχο της συνθήκης μεταφοράς από την κατάσταση ‘S1’ στην ‘S2’, όσο και για τη μεταφορά από την κατάσταση ‘S4’ πίσω στην ‘S1’.
Η δεύτερη process δηλώνει τη μεταφορά του έγκυρου κωδικού από τον ελεγκτή του πληκτρολογίου ‘sig_keyboard_data’, στον ελεγκτή της οθόνης ‘sig_keyboard_data2’. Η μετακίνηση των δεδομένων γίνεται μόνο όταν το σύστημα βρίσκεται στην κατάσταση ‘S2’ (if enable='1' then sig_keyboard_data2 <= sig_keyboard_data; else sig_keyboard_data2 <= (others => '0');).
Αρχείο: “vga_controller.vhd”
Η λειτουργία του ελεγκτή VGA παρουσιάστηκε με λεπτομέρεια στην ενότητα 2 («ΠΕ2 - Σχεδιασμός και επεξήγηση βασικών κυκλωμάτων»), ενώ ο κώδικας βρίσκεται αντίστοιχα στο αποθετήριο της σελίδας του έργου. Στο παράδειγμα της τρίλιζας που εξετάστηκε στην προηγούμενη ενότητα («ΠΕ3 - Δημιουργία εκπαιδευτικού κυκλώματος 2»), το αρχείο ‘vga_controller_v1.vhd’ τροποποιήθηκε κατάλληλα για να ταιριάξει στις απαιτήσεις του σχεδίου. Το ίδιο θα γίνει και για το δεύτερο εκπαιδευτικό κύκλωμα του χρωματισμού της οθόνης. Οι βασικές αλλαγές στα επιμέρους τμήματα της σχεδίασης σε σχέση με τις προηγούμενες ενότητες εντοπίζονται: (α) στην επαναφορά της χρήσης της ενδιάμεσης μνήμης ‘vgaBuffer’ που δεν χρειαζόταν στο κύκλωμα της τρίλιζας και (β) στην διαφοροποίηση των τμημάτων από το part 8 και κάτω που είναι υπεύθυνα για την προβολή των ενεργών pixel στην οθόνη.
Σε ότι αφορά την ενδιάμεση μνήμη ‘vga_buffer’, αυτή επανέρχεται στο σχεδιασμό του κυκλώματος χρωματισμού της οθόνης καθώς, σε αντίθεση με το κύκλωμα της τρίλιζας, τα περιεχόμενα των pixel πρέπει να ανανεώνονται δυναμικά ανάλογα με την επιλογή του χρήστη. Έτσι επειδή κάθε κίνηση του χρήστη προκαλεί ουσιαστικά μια καινούρια εικόνα προβολής, τα περιεχόμενα των pixel θα πρέπει να αποθηκεύεται ώστε να μπορεί να γίνεται σωστά η προβολή της νέας, εν μέρει αλλαγμένης, εικόνας.
Tα πρώτα τμήματα του κώδικα (1-7) έχουν την ίδια λογική με τα αντίστοιχα του ελεγκτή VGA που αναλύθηκαν στην ενότητα 2 (‘vga_controller_v1.vhd’) και γι’ αυτό το λόγο δεν θα επαναληφθούν και εδώ. Η μοναδική αλλαγή που σημειώνεται σε αυτό το παράδειγμα είναι στο εύρος και τα περιεχόμενα της μνήμης ‘vga_buffer’ που δηλώνονται στο Part 3. Ο αριθμός των ψηφίων των περιεχομένων της κάθε διεύθυνσης μειώνεται στα 3 bit (data : in std_logic_vector (2 downto 0);, q : out std_logic_vector (2 downto 0)), ένα για κάθε χρωματικό κανάλι RGB, ενώ ο αριθμός των ενεργών διευθύνσεων (rdaddress : in std_logic_vector (14 downto 0); wraddress : in std_logic_vector (14 downto 0);) περιορίζεται πλέον σε 19.200 (160x120 pixel). Οι αλλαγές έγιναν για να μπορεί να γίνει επιτυχώς η σύνθεση του κυκλώματος χρωματισμού οθόνης σε μοντέλα που δεν διαθέτουν μεγάλο μέγεθος εσωτερικής μνήμης, όπως το DE1 που χρησιμοποιήθηκε στο συγκεκριμένο έργο. Περισσότερες πληροφορίες για τη σύσταση της μνήμης ‘vga_buffer’ δίνονται εσωτερικά του αρχείου ‘vga_buffer.vhd’.
Προσοχή! Το αρχείο ‘vga_buffer.vhd’ αποτελεί προϊόν του εργαλείου “MegaWizard Plug-In Manager” που χρησιμοποιεί έτοιμες συναρτήσεις της Altera για τη σχεδίαση διαφόρων κυκλωμάτων και οποιαδήποτε παρεμβολή μπορεί να προκαλέσει σφάλμα στη λειτουργία της μνήμης.
Όπως αναφέρθηκε προηγουμένως, επειδή οι περιορισμοί στη διαθέσιμη εσωτερική μνήμη του FPGA σε ορισμένα μοντέλα δεν επιτρέπει την υλοποίηση μιας μνήμης ‘vga_buffer’ με μέγεθος 640x480x3=921.600 bit, το ενεργό παράθυρο προβολής στην οθόνη θα περιοριστεί σε 160x120 (δηλαδή το ¼ του αρχικού 640x480). Το παράθυρο αυτό τοποθετείται στο κέντρο της οθόνης στην πρώτη process (if ( ( hcount>240 and hcount<= 400 ) and ( vcount>180 and vcount<=300 ) ) then) ξεκινώντας στην πάνω αριστερά γωνία από τη θέση (241,180) και καταλήγοντας στην κάτω δεξιά γωνία στη θέση (400,300), με βάση τις τιμές των σημάτων ‘vcount’ και ‘hcount’.
Τα περιεχόμενα εσωτερικά της μνήμης ‘vga_buffer’ οργανώνονται με αύξουσα κατά σειρά προβολής στην οθόνη μορφή, δηλαδή στις διευθύνσεις 0-159 η πρώτη γραμμή της εικόνας, στις διευθύνσεις 160-319 η δεύτερη γραμμή της εικόνας, κτλ. Επομένως, κάθε φορά που το τρέχον pixel βρίσκεται εντός του ενεργού παραθύρου προβολής (ξεκινώντας από τη θέση (241,180)), η τιμή της διεύθυνσης ανάγνωσης της μνήμης ανεβαίνει κατά μια μονάδα:
if enable='1' then
if ( ( hcount>240 and hcount<= 400 ) and ( vcount>180 and vcount<=300 ) ) then
vgaBuffer_rdaddress <= vgaBuffer_rdaddress + 1;.
Μόλις το τρέχον pixel φτάσει στη θέση λήξης του ενεργού παραθύρου (401,300), η διεύθυνση ανάγνωσης της μνήμης μηδενίζεται για να ξεκινήσει ξανά στο επόμενο frame, όταν το τρέχον pixel βρεθεί πάλι εντός του ενεργού παραθύρου προβολής (θέση (241,180):
elsif hcount=401 and vcount=300 then vgaBuffer_rdaddress <= (others => '0');.
Αντίστοιχα για όσο χρονικό διάστημα η προβολή των pixel γίνεται εντός του ενεργού παραθύρου ([240-400]x[180-300]), τα περιεχόμενα της μνήμης ‘vga_buffer’ δίνονται στα τρία χρωματικά κανάλια RGB:
if ( ( hcount>240 and hcount<= 400 ) and ( vcount>180 and vcount<=300 ) ) then
red <= (others => vgaBuffer_dataOut(0));
green <= (others => vgaBuffer_dataOut(1));
blue <= (others => vgaBuffer_dataOut(2));
Σε αντίθετη περίπτωση τα χρωματικά κανάλια παίρνουν την τιμή του λογικού ‘0’ για την προβολή ενός μαύρου πλαισίου περιμετρικά του ενεργού παραθύρου:
red <= (others => '0');
green <= (others => '0');
blue <= (others => '0');
Να σημειωθεί ότι λόγω αρχικοποίησης των περιεχομένων της μνήμης ‘vga_buffer’ με την τιμή “111” σε όλες τις διευθύνσεις μέσω του αρχείου ‘vga_buffer_init.hex’, το αρχικό χρώμα του ενεργού παραθύρου θα είναι άσπρο. Η εισαγωγή χρώματος από το χρήστη ουσιαστικά αλλάζει τα περιεχόμενα της μνήμης από “111” σε “001” για το κόκκινο χρώμα, σε “010” για το πράσινο χρώμα και “100” για το μπλε χρώμα. Κάθε φορά μόνο ένα ψηφίο μπορεί να έχει την τιμή του λογικού ‘1’ καθώς το κύκλωμα υποστηρίζει μόνο τα τρία βασικά χρώματα.
Μια ειδική περίπτωση χειρισμού απαιτείται λόγω της ύπαρξης του μαύρου κέρσορα στην οθόνη για την ένδειξη της τρέχουσας ενεργής θέσης. Όσο ο χρήστης μετακινείται σε pixel που δεν έχει εισάγει κάποιο χρώμα πάνω στο λευκό καμβά (if vgaBuffer_dataOut="111" then), ο κέρσορας θα πρέπει να είναι μαύρος για να είναι ευδιάκριτος:
if (vgaBuffer_rdaddress = pointer) then
if vgaBuffer_dataOut="111" then
red <= (others => '0');
green <= (others => '0');
blue <= (others => '0');.
Όταν όμως ο χρήστης εισάγει κάποιο χρώμα τότε ο κέρσορας θα πρέπει να πάρει το χρώμα που επιλέχθηκε για όσο διάστημα βρίσκεται πάνω στο ζωγραφισμένο pixel, ώστε να μπορεί να δίνεται άμεσα η εικόνα του χρωματισμού του τρέχοντος pixel:
else
red <= (others => vgaBuffer_dataOut(0));
green <= (others => vgaBuffer_dataOut(1));
blue <= (others => vgaBuffer_dataOut(2));.
Σε αυτό το τμήμα του σχεδιασμού στόχος είναι να διαβάζονται τα περιεχόμενα του σήματος ‘keyboard_data*’* που περιέχουν τον κωδικό του πλήκτρου που πιέστηκε ώστε, αν αυτό αντιστοιχεί σε έγκυρη πλήκτρο, να ανανεωθεί η θέση του κέρσορα πάνω στο λευκό καμβά. Στο συγκεκριμένο παράδειγμα επιλέχθηκαν τα πλήκτρα ’W’, ‘Α’, ’S’ και ’D’ για μετακίνηση πάνω, αριστερά, κάτω και δεξιά, αντίστοιχα, κατά μια θέση. Οι κωδικοί για το κάθε πλήκτρο δίνονται στο Σχήμα 1.
Σχήμα 1. Γραφική αναπαράσταση των κωδικών του κάθε πλήκτρου, (http://retired.beyondlogic.org/keyboard/keybrd.htm).
Ανάλογα με την συνθήκη που ενεργοποιείται από τον κωδικό που στέλνεται από τον ελεγκτή του πληκτρολογίου, θα γίνει η ανάθεση τιμών στο σήμα ‘pointer’. Το σήμα ‘pointer’ αποτελείται από 15 ψηφία όσο και το εύρος των διευθύνσεων της μνήμης ‘vga_buffer’ και χρησιμοποιείται για να καθοριστεί η τρέχουσα θέση του κέρσορα. Ο κέρσορας αρχικοποιείται σε κάποια θέση τυχαία πάνω στον καμβά (if rst='0' then pointer <= "000000001100000";) και μπορεί να μετακινηθεί ελεύθερα προς όλες τις κατευθύνσεις από το χρήστη. Λόγω της οργάνωσης των περιεχομένων της μνήμης ‘vga_buffer’ σε σχέση με τα pixel της οθόνης, για τη μετακίνηση:
-
κατά μια θέση δεξιά, η τιμή του ‘pointer’ πρέπει να αυξηθεί κατά μια μονάδα (if keyboard_data="00100011" then pointer <= pointer + 1;),
-
κατά μια θέση αριστερά, η τιμή του ‘pointer’ πρέπει να μειωθεί κατά μια μονάδα (elsif keyboard_data="00011100" then pointer <= pointer - 1;),
-
κατά μια θέση πάνω, η τιμή του ‘pointer’ πρέπει να μειωθεί κατά 160 για να προσπελαστούν όλες οι ενδιάμεσες διευθύνσεις μνήμης και να βρεθεί στην αντίστοιχη στήλη της προηγούμενης γραμμής της οθόνης (elsif keyboard_data="00011101" then pointer <= pointer - 160;). Μια ειδική περίπτωση πρέπει να ληφθεί υπόψιν για την μεταφορά του κέρσορα από την πρώτη γραμμή της οθόνης στην τελευταία αν ο χρήστης επιλέξει να κινηθεί προς τα πάνω. Σε αυτή την περίπτωση θα πρέπει να προσπελαστούν προς τα μπροστά όλες οι διευθύνσεις της μνήμης εκτός από τις τελευταίες 160 (19.200-160=19.040):
elsif keyboard_data="00011101" then
if pointer <= 160 then
pointer <= pointer + 19040;)
- κατά μια θέση κάτω, η τιμή του ‘pointer’ όπως και προηγουμένως θα πρέπει να αυξηθεί κατά 160 για να προσπελαστούν όλες οι ενδιάμεσες διευθύνσεις μνήμης και να βρεθεί στην αντίστοιχη στήλη της επόμενης γραμμής της οθόνης (elsif keyboard_data="00011011" then pointer <= pointer + 160;). Μια ειδική περίπτωση πρέπει να ληφθεί υπόψιν για την μεταφορά του κέρσορα από την τελευταία γραμμή της οθόνης στην πρώτη αν ο χρήστης επιλέξει να κινηθεί προς τα κάτω. Σε αυτή την περίπτωση θα πρέπει να προσπελαστούν προς τα πίσω όλες οι διευθύνσεις της μνήμης εκτός από τις πρώτες 160 (19.200-160=19.040):
elsif keyboard_data="00011011" then
if pointer >= 19040 then
pointer <= pointer - 19040;
Σε αυτό το τμήμα του σχεδιασμού στόχος είναι να διαβάζονται τα περιεχόμενα του σήματος ‘keyboard_data*’* που περιέχουν τον κωδικό του πλήκτρου που πιέστηκε ώστε, αν αυτό αντιστοιχεί σε έγκυρη πλήκτρο, να ανανεωθεί το χρώμα του pixel που δείχνει ο κέρσορας πάνω στο λευκό καμβά. Στο συγκεκριμένο παράδειγμα επιλέχθηκαν τα πλήκτρα ’R’, ‘G’, ’B’ για την εισαγωγή του κόκκινου, πράσινου και μπλε χρώματος, αντίστοιχα. Οι κωδικοί για το κάθε πλήκτρο δίνονται στο Σχήμα 1.
Η εισαγωγή χρώματος από το χρήστη ουσιαστικά αλλάζει τα περιεχόμενα της μνήμης από “111” σε “001” για το κόκκινο χρώμα (if keyboard_data="00101101" then vgaBuffer_dataIn <= "001";), σε “010” για το πράσινο χρώμα (elsif keyboard_data="00110100" then vgaBuffer_dataIn <= "010";) και “100” για το μπλε χρώμα (elsif keyboard_data="00110010" then vgaBuffer_dataIn <= "100";). Η τρέχουσα διεύθυνση της μνήμης ‘vga_buffer’ που θα εισαχθεί η χρωματική πληροφορία λαμβάνεται εύκολα απευθείας από την τιμή του ‘pointer’ (vgaBuffer_wrAddress <= pointer-1;). Το -1 χρησιμοποιείται για το συγχρονισμό μεταξύ ‘vga_buffer’ και οθόνης, γιατί τα περιεχόμενα της μνήμης διαβάζονται και γράφονται κατά 1 κύκλο του ρολογιού πιο αργά λόγω των καταχωρητών που τοποθετούνται υποχρεωτικά στις εισόδους της μνήμης. Τέλος, το σήμα ‘vgaBuffer_wrEnable’ παίρνει την τιμή του λογικού ‘1’ για να δηλώσει την εγκυρότητα των περιεχομένων στην είσοδο της μνήμης, ώστε αυτά να μεταφερθούν στο εσωτερικό της. Το ‘vgaBuffer_wrEnable’ γίνεται ‘1’ (vgaBuffer_wrEnable <= '1';) μόνο αν αναγνωρισθεί κάποιο από τα τρία έγκυρα πλήκτρα χρωματικής πληροφορίας ’R’, ‘G’ ή ’B’.
Αρχείο: “keyboard_controller.vhd”
Ο ελεγκτής του πληκτρολογίου που χρησιμοποιήθηκε σε αυτό το παράδειγμα είναι ίδιος με τον αντίστοιχο που παρουσιάστηκε στην προηγούμενη ενότητα (ΠΕ2 – «Σχεδιασμός και επεξήγηση βασικών υποκυκλωμάτων») και βρίσκεται αναρτημένος στον φάκελο «FPGA controllers/keyboard» του repository.
Αρχείο: “vga_buffer.vhd”
Η δημιουργία της μνήμης γίνεται με τη χρήση του του εργαλείου “MegaWizard Plug-In Manager” που χρησιμοποιεί έτοιμες συναρτήσεις της Altera για τη σχεδίαση κυκλωμάτων. Η υλοποίησή της δείχνεται βήμα προς βήμα στις ακόλουθες εικόνες.
Από την καρτέλα “Tools” επιλέγουμε “MegaWizard Plug-In Manager”.
Στο παράθυρο που ανοίγει την επιλογή “Create a new custom megafunction variation” και μετά Next.
Από την καρτέλα αριστερά του παραθύρου που ανοίγει επιλέγουμε “RAM: 2 PORT” στην κατηγορία “Memory Compiler”. Στη συσκευή πάνω δεξιά επιλέγουμε το μοντέλο που θα τρέξει η συγκεκριμένη μνήμη, εδώ “Cyclone II”. Στο πεδίο στη μέση του παραθύρου επιλέγεται η θέση και το επιθυμητό όνομα του αρχείου της μνήμης, εδώ “C:/vga_buffer”. Πατάμε Next.
Στη νέα καρτέλα δεν χρειάζεται κάποια τροποποίηση και πατάμε Next απευθείας.
Στην επόμενη καρτέλα θέτονται οι διαστάσεις της μνήμης. Ο αριθμός των διευθύνσεων πάνω δεξιά τίθεται **19.200 **(160x120) και ο αριθμός των ψηφίων κάθε διεύθυνσης σε 3. Πατάμε Next.
Στη νέα καρτέλα δεν χρειάζεται κάποια τροποποίηση και πατάμε Next απευθείας.
Στη επόμενη καρτέλα ξετσεκάρουμε την επιλογή “**Read output port(s) ‘q’ **”για να μην δημιουργήσει καταχωρητή στα δεδομένα εξόδου και πατάμε Next.
Στην επόμενη καρτέλα επιλέγεται η μεταφορά των παλιών περιεχομένων της μνήμης σε περίπτωση που ταυτίζεται η διεύθυνση ανάγνωσης και εγγραφής, “Old memory contents appear” και πατάμε Next.
Μετά επιλέγεται η αρχικοποίηση της μνήμης με το αρχείο “vga_buffer_init.hex” που είναι διαθέσιμο στο repository του έργου στον φάκελο «Ex2 - Χρωματισμός οθόνης» και πατάμε Next.
Στη νέα καρτέλα δεν χρειάζεται κάποια τροποποίηση και πατάμε Next απευθείας.
Στην επόμενη και τελευταία καρτέλα ξετσεκάρουμε την επιλογή “vga_buffer.cmp” και πατάμε Finish για την ολοκλήρωση του οδηγού σχεδίασης της μνήμης.
Το αρχείο “vga_buffer.vhd” έχει δημιουργηθεί στη θέση που επιλέχθηκε, εδώ “C:/”.
Έχοντας ολοκληρώσει το σχεδιασμό του κυκλώματος, μπορεί να ξεκινήσει η μεταφορά του στην πλακέτα. Η όλη διαδικασία για τα μοντέλα της Altera, όπως το DE1 που χρησιμοποιήθηκε για τον έλεγχο εδώ, γίνεται μέσω του λογισμικού Quartus ΙΙ. Αρχικά μόνο τα αρχεία με κατάληξη ‘*.vhd’ και το αρχείο αρχικοποίησης ‘vga_buffer_init.hex’ που είναι διαθέσιμα στο φάκελο “Ex2: Χρωματισμός οθόνης” θα πρέπει να τοποθετηθούν σε έναν οποιοδήποτε φάκελο του χρήστη. Έπειτα ακολουθείται η διαδικασία όπως περιγράφηκε βήμα προς βήμα στην ενότητα 1 («ΠΕ1 – Παρουσίαση βασικών εργαλείων σχεδίασης») για τη δημιουργία ενός καινούριου project μέσω του wizard της Altera. Προσοχή χρειάζεται στα ακόλουθα σημειά:
-
Το όνομα του project θα πρέπει να ταυτίζεται με αυτό του τελικού κυκλώματος ‘color_pixels’ για να γίνει σωστά η σύνθεση και το path θα πρέπει να δείχνει τον φάκελο που αποθηκεύτηκαν τα αρχεία.
-
Μετά την επιλογή του ονόματος η επόμενη καρτέλα προτρέπει την εισαγωγή αρχείων στο project και θα πρέπει σε αυτή τη φάση να εισαχθούν όλα τα αρχεία με κατάληξη ‘*.vhd’. Μετά την επιλογή τα αρχεία προστίθενται πατώντας ‘Add’.
-
Στην επιλογή του κατάλληλου μοντέλου FPGA στην αντίστοιχη καρτέλα του wizard (εδώ το μοντέλο Cyclone II EP2C20F484C7).
Μετά την ολοκλήρωση του Wizard ανοίγει το παρακάτω παράθυρο από όπου με διπλό κλικ ανοίγουμε το βασικό αρχείο του κυκλώματος χρωματισμού οθόνης ‘color_pixels’.
Μόλις ανοίξει το αρχείο επιλέγουμε Start Compilation ή πατάμε F5 για να ξεκινήσει ο έλεγχος των αρχείων και η σύνθεση του κυκλώματος όπως φαίνεται στην παρακάτω εικόνα.
Αν όλα γίνουν σωστά όλα τα βήματα του compile θα πάρουν ένα πράσινο τικ και θα εμφανιστεί σχετικό παράθυρο που θα ενημερώνει για την επιτυχή ολοκλήρωση της διαδικασίας, δίνοντας και έναν αριθμό από warnings. Τα πλέον σημαντικά καταχωρούνται την καρτέλα “Critical Warnings” και ενδέχεται να κρύβουν ζητήματα που πρέπει να ληφθούν υπόψιν για να διασφαλιστεί η σωστή λειτουργία του κυκλώματος όταν μεταφερθεί στην πλακέτα.
Στην παρούσα φάση το πρώτο πράγμα που αναμένεται να ζητήσει το Quartus II είναι να αντιστοιχίσουμε τα σήματα εξόδου και εισόδου του FPGA με τα πραγματικά κανάλια που βρίσκονται περιμετρικά του chip και το ενώνουν στο φυσικό χώρο με τα περιφερειακά όπως το πληκτρολόγιο και η οθόνη:
Critical Warning (169085): No exact pin location assignment(s) for Χ pins of Υ total pins.
Ο πιο εύκολος τρόπος για να γίνει αναζήτηση του αρχείου ‘color_pixels.qsf’ που δημιουργείται αυτόματα στον ίδιο φάκελο με το που ολοκληρωθεί η δημιουργία του νέου project. Το όνομα του .qsf αρχείου ταυτίζεται πάντα με το όνομα της οντότητας του project (εδώ color_pixel). Για ευκολία το αρχείο θα πρέπει να αντικατασταθεί με το υπάρχον στο repository του έργου ‘color_pixels.qsf’ και να ξαναγίνει Compile του project για να αναγνωριστούν οι αλλαγές.
Το επόμενο Critical Warning αναμένεται να τονίζει ότι δεν υπάρχει δήλωση σήματος ρολογιού για το συγκεκριμένο project. Αυτό προκύπτει γιατί το Quartus II δεν είναι σε θέση να αναγνωρίσει αυτόματα ότι το σήμα ‘clk’ που χρησιμοποιήθηκε παντού ως σήμα ρολογιού είναι πράγματι το σήμα που πρέπει να θεωρήσει ως ενιαίο ρολόι. Αυτό γίνεται μέσα από τη δήλωση του αρχείου ‘color_pixels.sdc’ το οποίο επίσης είναι διαθέσιμο στο repository και θα πρέπει να τοποθετηθεί στο φάκελο του project.
Critical Warning (332012): Synopsys Design Constraints File file not found: 'xx.sdc'. A Synopsys Design Constraints File is required by the TimeQuest Timing Analyzer to get proper timing constraints. Without it, the Compiler will not properly optimize the design.
Μετά τις παραπάνω αλλαγές το compile αναμένεται να δώσει ένα αποτέλεσμα όπως αυτό που φαίνεται στην παρακάτω εικόνα. Τα 15 pins που δεν έχουν τοποθετηθεί είναι τα 4 πιο σημαντικά ψηφία των καναλιών ‘red’, ‘green’, ‘blue’ που δηλώθηκαν με εύρος 8 bit ενώ απαιτούνται 4 και τα 3 περιττά σήματα χρονισμού του ελεγκτή VGA ‘blank’, ‘sync’ και ‘vgaCLK’. Το συγκεκριμένο warning δεν αποτελεί λόγος ανησυχίας και μπορούμε να συνεχίσουμε με τη μεταφορά του κυκλώματος στην πλακέτα.
Για τον προγραμματισμό της πλακέτας θα πρέπει πρώτα να συνδεθεί μέσω USB με τον υπολογιστή του χρήστη. Επιπλέον, θα πρέπει να συνδεθούν στην πλακέτα και τα απαραίτητα περιφερειακά (πληκτρολόγιο και οθόνη) στις αντίστοιχες υποδοχές. Η τροφοδοσία για το μοντέλο DE1 γίνεται μέσω του USB καλωδίου και δεν απαιτείται επιπρόσθετη παροχή ρεύματος. Η πλακέτα τίθεται σε λειτουργία πατώντας το κόκκινο κουμπί στην αριστερή πλευρά. Μόλις ενεργοποιηθεί, η πλακέτα θα προβάλει μια εικόνα με το σήμα της Altera στην οθόνη που είναι προ-εγκατεστημένη στο εσωτερικό της και θα μείνει αδρανής περιμένοντας τον προγραμματισμό της.
Για να «κατεβάσουμε» το πρόγραμμα από το Quartus ΙΙ στην πλακέτα επιλέγουμε το εικονίδιο του “Programmer” και ανοίγει η ακόλουθη καρτέλα:
Αν δεν έχει αναγνωριστεί αυτόματα, από την επιλογή “Hardware Setup” επιλέγουμε τον οδηγό “USB-Blaster” και επιλέγουμε “Start”. Η μπάρα “Progress” θα ξεκινήσει να γεμίζει καθώς τα δεδομένα μεταφέρονται στην πλακέτα για τον προγραμματισμό της. Μόλις ολοκληρωθεί η διαδικασία αυτή το κύκλωμα έχει κατεβεί με επιτυχία και η πλακέτα είναι έτοιμη για χρήση.
ΠΡΟΣΟΧΗ! Πριν από τη χρήση ανεξαρτήτως παραδείγματος και κυκλωμάτων προτείνεται να γίνει η επαναφορά στην αρχική του κατάσταση όπως αυτή έχει δηλωθεί κατά το σχεδιασμό για να μην υπάρχουν αποκλίσεις στη συμπεριφορά του.
Οι παρακάτω εικόνες είναι ενδεικτικές του πως αναμένεται να λειτουργήσει το κύκλωμα του χρωματισμού της οθόνης που σχεδιάστηκε σε αυτή την ενότητα. Στην πρώτη εικόνα φαίνεται κεντραρισμένος ο άσπρος καμβάς διάστασης 160x120 pixels, περιτριγυρισμένος από ένα μαύρο πλαίσιο.
Στην παρακάτω εικόνα φαίνεται από κοντά ο καμβάς μετά την προσθήκη χρώματος σε κάποια από τα pixel: