ΗΥ-120: Ψηφιακή Σχεδίαση
Φθινόπωρο 2006 |
Τμ. Επ. Υπολογιστών © Πανεπιστήμιο Κρήτης |
Το φυλλάδιο αυτό αντιστοιχεί στην Έκδοση 2 (έτος 2005) της πλακέτας
Ορισμένοι ασκούμενοι ενδέχεται να χρησιμοποιήσουν μιά νεότερη, έκδοση 3, της πλακέτας, η οποία θα περιγραφεί χωριστά
[Up - Table of Contents] [Prev - 10. FSM] |
[printer version - PDF] [12. Branches, Pointers - Next] |
Στο τελευταίο αυτό μέρος του μαθήματος θα δούμε πώς μπορούμε να φτιάξουμε έναν απλό υπολογιστή χρησιμοποιώντας μόνο βασικούς δομικούς λίθους αυτού του μαθήματος: καταχωρητές, μνήμες, αθροιστές, πολυπλέκτες, και ένα απλό συνδυαστικό κύκλωμα ή μιά απλή FSM γιά να ελέγχει τα παραπάνω. Στο εργαστήριο, θα βρείτε έτοιμο το τμήμα δεδομένων (datapath) αυτού του υπολογιστή, όπου υπάρχουν ενδείκτες με τις τιμές όλων των διευθύνσεων, δεδομένων, και σημάτων, και θα φτιάξετε εσείς το κύκλωμα ελέγχου· την επόμενη βδομάδα, θα συνεχίσετε με πιό πολύπλοκες εντολές και κυκλώματα ελέγχου. Παρ' ότι ο υπολογιστής αυτός είναι πολύ απλός (απλοϊκός) --και εξαιρετικά αργός!-- είναι εντούτοις ένας κανονικός υπολογιστής, ικανός να εκτελεί (σχεδόν) το κάθε πρόγραμμα· τα μόνα που του λείπουν είναι σχετικά δευτερεύουσες λεπτομέρειες, και όχι κάτι εννοιολογικά κεντρικό. Ο υπολογιστής μας θα είναι οκτάμπιτος (8-bit), δηλαδή θα δουλεύει με λέξεις των 8 bits· μοναδική εξαίρεση θα είναι οι εντολές, που έχουν 12 bits καθεμία (οπότε και η μνήμη που τις περιέχει θα είναι δωδεκάμπιτη στο πλάτος).
Εκτός από τον οκτάμπιτο αθροιστή, η ALU έχει και 8 πύλες ΚΑΙ (AND) καθώς και 8 πύλες ΟΥΤΕ (NOR) γιά να μπορεί να κάνει τις αντίστοιχες λογικές πράξεις πάνω στις λέξεις εισόδου· πρόκειται γιά λογικές πράξεις bit-προς-bit (bitwise operations), δηλαδή το bit i της εξόδου θα είναι το λογικό ΚΑΙ ή το λογικό ΟΥΤΕ του bit i της εισόδου A και του bit i της εισόδου B. Κάθε τέτοια οκτάδα πυλών φαίνεται σχεδιασμένη σαν μία πύλη, κάτω αριστερά. Ο πολυπλέκτης αριστερά κάτω επιλέγει αν το αποτέλεσμα λογικής πράξης που θέλουμε να κρατήσουμε είναι το λογικό ΚΑΙ ή το λογικό ΟΥΤΕ. Η επιλογή αυτή γίνεται με το ίδιο σήμα ελέγχου που επιλέγει και εάν θα κάνουμε πρόσθεση ή αφαίρεση· αυτό σημαίνει ότι δεν μπορούμε να επιλέξουμε ταυτόχρονα συνδυασμούς όπως π.χ. πρόσθεση και ΟΥΤΕ --όμως, προφανώς, κάτι τέτοιο δεν μας ενδιαφέρει να το κάνουμε, αφού η ALU μίας μόνο πράξης το αποτέλεσμα μας ενδιαφέρει να βγάζει στην έξοδό της, και ποτέ δύο διαφορετικά αποτελέσματα ταυτόχρονα. Έτσι, ο πρώτος από τους πολυπλέκτες δεξιά επιλέγει αν επιθυμούμε να κρατήσουμε το αποτέλεσμα της αριθμητικής πράξης που κάνει ο αθροιστής, ή το αποτέλεσμα της λογικής πράξης που κάνουν οι πύλες. Και τελικά, ο τελευταίος δεξιά πολυπλέκτης επιλέγει αν θα δοθεί στην έξοδο (α) το αποτέλεσμα της αριθμητικής ή της λογικής πράξης, ή (β) η δεύτερη είσοδος της ALU, B, αυτή καθεαυτή χωρίς άλλην επεξεργασία. Την τελευταία αυτή δυνατότητα, που την λέμε "πέρασε το B" (passB), θα δούμε ότι θα την χρειαστούμε σε εντολές όπως η "load", παρακάτω. Έτσι, συνολικά, η ALU μπορεί να κάνει τις παρακάτω πράξεις, ανάλογα με την εκάστοτε τιμή των τριών σημάτων ελέγχου της, mode. Η ένδειξη "x" στο mode σημαίνει ότι η ALU κάνει την ίδια πράξη όποια τιμή και να έχει το αντίστοιχο bit του mode (συνθήκη αδιαφορίας, που συγγενεύει με όσα είδαμε στην §4.4).
mode: Πράξη: Όνομα: .000 ALUout = A+B (add) .001 ALUout = A-B (sub) .010 ALUout = A AND B (and) .011 ALUout = NOT (A OR B) (nor) .1xx ALUout = B (passB)
Συχνά, ένας υπολογιστής θέλουμε να έχει τουλάχιστο τρείς λογικές πράξεις: AND, OR, NOT. Ο δικός μας θα έχει μόνο δύο επειδή, όπως θα δούμε πιό κάτω, δέχεται μόνο 16 συνολικά εντολές, και δεν υπήρχε μεταξύ τους χώρος και γιά την OR και γιά την NOT. Όταν χρειαζόμαστε ένα απλό NOT, μπορούμε να κάνουμε NOR με το μηδέν, και όποτε χρειαζόμαστε ένα OR μπορούμε να κάνουμε πρώτα ένα NOR και μετά ένα NOT (δηλ. NOR με μηδέν). Αυτό, ταυτόχρονα, απλοποιεί και το κύκλωμα της ALU και την κωδικοποίηση των mode bits της.
Σημείωση: οι πλακέτες έκδοσης 2 είναι ίδιες με τις πλακέτες της παλαιότερης έκδοσης 1, αλλά χρησιμοποιούν διαφορετική διαμόρφωση της κεντρικής "FPGA". Έτσι, οι πλακέτες έκδοσης 2 έχουν "κληρονομήσει" μεν τέσσερεις (4) ακροδέκτες mode bits της ALU, αλλά από αυτούς μόνον οι τρείς (3) δεξιοί (LS) "παίζουν" και αντιστοιχούν στα 3 mode bits του παραπάνω πίνακα. Αφήστε τον τέταρτο, αριστερό ακροδέκτη (σημειωμένο με μία τελεία στον παραπάνω πίνακα) ασύνδετο.
Στο σχήμα φαίνεται η απαιτούμενη συνδεσμολογιά γιά να γίνονται οι πράξεις όπως τις περιγράψαμε. Ο συσσωρευτής είναι ένας ακμοπυροδότητος καταχωρητής (§8.5), που σημειώνεται σαν "ACC". Ένα εξωτερικό κύκλωμα, που θα δούμε σε λίγο, τροφοδοτεί τη διεύθυνση ADDR στη μνήμη δεδομένων, καθώς και τα σήματα ελέγχου ανάγνωσης (dm_rd - data memory read enable) και εγγραφής (dm_wr - data memory write enable), προκαλώντας την ανάγνωση μιάς λέξης (δηλ. ενός αριθμού) από τη θέση μνήμης ADDR. Η ALU παίρνει το περιεχόμενο του συσσωρευτή ACC στη μιά της είσοδο, και τον αριθμό που διαβάσαμε από τη μνήμη στην άλλη· το εξωτερικό κύκλωμα ελέγχου προσδιορίζει, μέσω του alu_md (ALU mode), το είδος της πράξης που πρέπει να γίνει, και το αποτέλεσμα της πράξης δίδεται σαν είσοδος στο συσσωρευτή. Όταν έλθει η ενεργή ακμή του ρολογιού, το αποτέλεσμα αυτής της πράξης αντικατθιστά το παλαιό περιεχόμενο του συσσωρευτή. Κατά καιρούς, πρέπει το αποτέλεσμα των πράξεων να αποθηκεύεται (γράφεται) σε μιά επιθυμητή θέση (λέξη) μνήμης, προκειμένου μετά να ξεκινήσει κάποια νέα σειρά πράξεων στο συσσωρευτή. Γιά να γίνεται η αποθήκευση αυτή προβλέφτηκε ένας τρικατάστατος οδηγητής, δεξιά κάτω, ο οποίος τοποθετεί το περιεχόμενο του ACC πάνω στη λεωφόρο (bus), απ' όπου και το παραλαμβάνει η μνήμη γιά να γίνει η εγγραφή. Τα σήματα ελέγχου που πρέπει να ενεργοποιηθούν είναι τα acc2bus (ACC προς bus - ACC to bus, όπου το "2" είναι ομόηχο με το "to") και dm_wr.
Ο κάθε υπολογιστής "καταλαβαίνει", δηλαδή μπορεί να αναγνωρίσει και να εκτελέσει, ορισμένες μόνο, συγκεκριμένες εντολές ή τύπους εντολών· αυτές τις ονομάζουμε σύνολο ή ρεπερτόριο εντολών (instruction set) του υπολογιστή. Οι εντολές του δικού μας υπολογιστή αποτελούνται από δύο κομάτια καθεμία: έναν "κώδικα πράξης" (operation code, ή opcode εν συντομία), και μιά διεύθυνση ADDR. Κάθε εντολή αποτελείται από 12 bits, από τα οποία τα 4 MS bits είναι ο opcode και τα 8 LS bits είναι η διεύθυνση. Σ' αυτό το εργαστήριο, ο υπολογιστής μας θα έχει το ρεπερτόριο των 8 εντολών που φαίνεται στον παρακάτω πίνακα· μερικές εντολές θα εξηγηθούν σε επόμενες παραγράφους· στο επόμενο εργαστήριο, θα προσθέσουμε και τις υπόλοιπες 8 εντολές.
Ρεπερτόριο Εντολών (Instruction Set): Γλώσσα Μηχανής: Γλ. Assembly: Σημαίνει: 0000aaaaaaaa add ADDR ACC <- ACC + DM[ADDR] 0001aaaaaaaa sub ADDR ACC <- ACC - DM[ADDR] 0010aaaaaaaa and ADDR ACC <- ACC AND DM[ADDR] 0011aaaaaaaa nor ADDR ACC <- NOT ( ACC OR DM[ADDR] ) 0100aaaaaaaa input ADDR DM[ADDR] <- εξωτερική είσοδος από πληκτρολόγιο (§11.8) 0101aaaaaaaa load ADDR ACC <- DM[ADDR] 0110aaaaaaaa store ADDR DM[ADDR] <- ACC 0111aaaaaaaa jump ADDR PC <- ADDR (επόμενη εντολή σε ADDR - §11.9)
Στο μεσαίο μέρος του πίνακα φαίνεται η συμβολική γραφή της εντολής: η πρώτη λέξη είναι το σύμβολο του opcode, ενώ το ADDR αντικαθίσταται κάθε φορά από τη συγκεκριμένη διεύθυνση που επιθυμούμε να χρησιμοποιήσουμε --έναν αριθμό από 0 μέχρι 255, αφού οι διευθύνσεις είναι οκτάμπιτες στον υπολογιστή μας. Ένα πρόγραμμα με τις εντολές του γραμμένες με αυτό το συμβολισμό λέμε ότι είναι γραμμένο σε "Γλώσσα Assembly". Στη μνήμη του υπολογιστή, φυσικά, το μόνο που υπάρχει είναι άσσοι και μηδενικά, άρα γιά να εκτελεστεί ένα πρόγραμμα Assembly πρέπει πρώτα να μετατραπεί στην δυαδική του αναπαράσταση, δηλαδή στη Γλώσσα Μηχανής. Η μετατροπή αυτή είναι πολύ απλή: κάθε συμβολικός opcode αντικαθίσταται με τον αντίστοιχο δυαδικό του κώδικα, και κάθε διεύθυνση μετατρέπεται στο δυαδικό. Τη μετατροπή αυτή (και μερικές άλλες σχετικές βοηθητικές εργασίες) την κάνει συνήθως ένα μικρό πρόγραμμα υπολογιστή που ονομάζεται Assembler.
Στις δεξιές στήλες του πίνακα φαίνεται μία "εξίσωση μεταφοράς καταχωρητών" (register transfer equation), η οποία περιγράφει τις ενέργειες που πρέπει να γίνουν προκειμένου ο υπολογιστής να εκτελέσει την εντολή. Σε αυτές τις εξισώσεις, το αριστερό βέλος υποδεικνύει μεταφορά και εγγραφή πληροφορίας (εκχώρηση - assignment). Ο συμβολισμός "DM[ADDR]" σημαίνει τη θέση (λέξη) της μνήμης δεδομένων (DM) στη διεύθυνση ADDR. Όταν ο συσσωρευτής, ACC, εμφανίζεται και δεξιά και αριστερά από το βέλος, τότε δεξιά μεν σημαίνει το παλαιό περιεχόμενό του (πριν την ακμή ρολογιού), αριστερά δε σημαίνει τη νέα τιμή του (μετά την ακμή ρολογιού) --όπως και στις εκχωρήσεις (assignments) των γλωσσών προγραμματισμού.
Έτσι, η εντολή load ADDR (φόρτωσε) διαβάζει τον αριθμό που περιέχεται στη διεύθυνση ADDR της μνήμης, δηλαδή διαβάζει το DM[ADDR], και το αντιγράφει στο συσσωρευτή. Η εντολή add ADDR προσθέτει το παλαιό περιεχόμενο του ACC με τον αριθμό που περιέχεται στη διεύθυνση ADDR της μνήμης, και γράφει το αποτέλεσμα στον ACC. Αντίστοιχα, οι εντολές sub, and, nor κάνουν τις ανάλογες πράξεις. Τέλος, η εντολή store ADDR (αποθήκευσε) αντιγράφει το περιεχόμενο του ACC στη θέση (λέξη) μνήμης με διεύθυνση ADDR. Στην παράγραφο 11.8 θα μιλήσουμε γιά την εντολή input ADDR, η οποία γράφει ομοίως στη θέση (λέξη) μνήμης με διεύθυνση ADDR, αλλά γράφει δεδομένα από μιάν εξωτερική είσοδο (πληκτρολόγιο) και όχι από τον συσσωρευτή. Στην παράγραφο 11.9 θα μιλήσουμε γιά την εντολή jump ADDR, η οποία κάνει ώστε η επόμενη εντολή που θα εκτελεστεί να μην είναι η "από κάτω" γραμμένη στη μνήμη εντολών, αλλά μιά άλλη (στη διεύθυνση ADDR).
Γιά παράδειγμα, λοιπόν, αν η μνήμη δεδομένων περιέχει τους αριθμούς που φαίνονται στο παραπάνω σχήμα (διευθύνσεις και δεδομένα φαίνονται στο δεκαεξαδικό), τότε το πρόγραμμα: "load 08; add 09; add 0A; store 0B;" θα προκαλέσει τις εξής ενέργειες. Πρώτα θα διαβαστεί ο αριθμός 12 (δεκαεξαδικό) από τη θέση 08 και θα γραφτεί στο συσσωρευτή· μετά, θα διαβαστεί ο αριθμός 1816 και θα προστεθεί στον 1216 παράγοντας το αποτέλεσμα 2A (δεκαεξαδικό) το οποίο και θα γραφτεί στο συσσωρευτή· εν συνεχεία, θα διαβαστεί το 1A από τη θέση 0A, θα προστεθεί στο 2A, και το αποτέλεσμα 44 (δεκαεξαδικό) θα μείνει στο συσσωρευτή· και τέλος, το περιεχόμενο 4416 του συσσωρευτή θα γραφτεί στη θέση μνήμης 0B.
Στο παράδειγμα του σχήματος, ο PC περιέχει τον αριθμό 10 (δεκαεξαδικό), ο οποίος δίδεται σα διεύθυνση στη μνήμη εντολών· η μνήμη εντολών διαβάζει και μας δίνει το περιεχόμενο της θέσης 10, το οποίο εδώ τυχαίνει να είναι η εντολή load 08 --κωδικοποιημένη σε γλώσσα μηχανής φυσικά, δηλαδή "010100001000" σύμφωνα με το παραπάνω ρεπερτόριο εντολών. Κάθε λέξη της μνήμης εντολών είναι 12 bits, και περιέχει μιάν εντολή. Τα 12 σύρματα ανάγνωσης που βγαίνουν από τη μνήμη αυτή, τα χωρίζουμε σε 4 αριστερά (MS) σύρματα που πηγαίνουν στο κύκλωμα ελέγχου, και 8 δεξιά (LS) σύρματα που πηγαίνουν σα διεύθυνση στη μνήμη δεδομένων. Αφού όλες οι εντολές του υπολογιστή μας αποτελούνται από έναν opcode στα 4 MS bits και μία διεύθυνση στα 8 LS bits, με τη συνδεσμολογία αυτή πηγαίνει ο opcode στο κύκλωμα ελέγχου και η διεύθυνση στη μνήμη δεδομένων. Στο παράδειγμά μας, ο opcode είναι 0101 (που σημαίνει load), και η διεύθυνση είναι 00001000 (δηλ. 08 δεκαεξαδικό). Το κύκλωμα ελέγχου, βλέποντας την εντολή load, θα ζητήσει ανάγνωση από τη μνήμη δεδομένων (dm_rd=1, dm_wr=0, acc2bus=0) και θα θέσει την ALU σε λειτουργία passB (alu_md=1xx). Η μνήμη δεδομένων, βλέποντας τη διεύθυνση 08 και ότι της ζητείται ανάγνωση, θα τοποθετήσει τον αριθμό 12 στη λεωφόρο· η ALU, εκτελώντας λειτουργία passB, θα περάσει τον αριθμό 12 στην έξοδό της· στην ενεργή ακμή του ρολογιού, ο αριθμός αυτός θα γραφτεί στο συσσωρευτή ACC, ολοκληρώνοντας έτσι την εκτέλεση της εντολής load 08.
Μετά την εκτέλεση της εντολής load 08
από τη θέση 10 της μνήμης εντολών,
πρέπει να εκτελεστεί η επόμενη εντολή.
Κατά πάγια σύμβαση, εκτός ειδικών εξαιρέσεων που θα δούμε πιό κάτω
(§11.9),
η επόμενη εντολή βρίσκεται γραμμένη
στην ακριβώς επόμενη θέση μνήμης --εδώ, στη διεύθυνση 11.
Γιά να προκύψει η επόμενη αυτή διεύθυνση γιά τη μνήμη εντολών,
χρησιμοποιούμε τον αυξητή (incrementor) που φαίνεται αριστερά στο σχήμα,
δηλαδή έναν αθροιστή με δεύτερη είσοδο το +1.
Έτσι, στην ίδια παραπάνω ενεργή ακμή του ρολογιού
που γράφεται ο αριθμός 12 στο συσσωρευτή ACC,
γράφεται και το αποτέλεσμα της πρόσθεσης 10+1=11 στον καταχωρητή PC.
Το αποτέλεσμα είναι ότι στον επόμενο κύκλο ρολογιού
ο PC θα περιέχει 11·
η μνήμη εντολών θα διαβάσει και θα μας δώσει
το περιεχόμενο "000000001001" της θέσης 11,
δηλαδή την εντολή add 09·
το κύκλωμα ελέγχου, βλέποντας opcode=0000 (add),
θα δώσει dm_rd=1, dm_wr=0, acc2bus=0,
και alu_md=000 (δηλ. add)·
η μνήμη δεδομένων, βλέποντας διεύθυνση 09 και dm_rd=1,
θα διαβάσει και θα δώσει στη λεωφόρο τον αριθμό 18·
η ALU, βλέποντας ACC=12, στη λεωφόρο το 18, και alu_md=add,
θα προσθέσει 12+18 και θα δώσει στην έξοδό της 2A·
και ο αθροιστής/αυξητής αριστερά, βλέποντας PC=11,
θα δώσει στην έξοδό του 11+1=12.
Μόλις έλθει η επόμενη ενεργή ακμή ρολογιού,
το 2A θα μπεί στον ACC, και το 12 θα μπεί στον PC,
ολοκληρώνοντας έτσι την εκτέλεση της εντολής add 09,
και προετοιμάζοντάς μας γιά την επόμενη εντολή, add 0A,
από τη θέση 12.
Ο καταχωρητής PC ονομάζεται "Μετρητής Προγράμματος"
ακριβώς επειδή είναι κατά βάση ένας μετρητής
που αυξάνεται κατά 1 στο τέλος της εκτέλεσης κάθε εντολής
γιά να μας δώσει τη διεύθυνση της επόμενης εντολής·
ο πολυπλέκτης που υπάρχει στην είσοδό του
προορίζεται γιά την αρχικοποίησή του, όταν δίδεται σήμα reset.
Η πλακέτα του datapath έχει περισσότερα στοιχεία από αυτά που είδαμε στην §11.4, προκειμένου να προσφέρει και τις λειτουργίες που θα περιγράψουμε παρακάτω σε αυτό και στο επόμενο εργαστήριο. Το πλήρες κύκλωμα που υλοποιεί η πλακέτα φαίνεται στο παρακάτω σχηματικό διάγραμμα. Υπάρχουν εννέα διψήφιοι ενδείκτες 7 τμημάτων (7-segment displays) οι οποίοι σας δείχνουν ανά πάσα στιγμή την παρούσα τιμή σε εννέα ενδιαφέροντα (οκτάμπιτα) σημεία του κυκλώματος· ο κάτω-κάτω (μέση) ενδείκτης μετράει τους κύκλους ρολογιού. Οι ενδείκτες αυτοί λειτουργούν στο δεκαεξαδικό --τα ψηφία πάνω από το 9 μοιάζουν με A, b, c, d, E, και F· προσοχή: το 6 διαφέρει από το b κατά το ότι το μεν 6 έχει παύλα επάνω, ενώ το b δεν έχει.
Εκτός από τους διψήφιους ενδείκτες 7 τμημάτων γιά τους οκτάμπιτους δρόμους, υπάρχουν και μικρές LED που δείχνουν τις τιμές μεμονωμένων bits. Οι κόκκινες LED δείχνουν ποιός από τους τρικατάστατους οδηγητές που οδηγούν το BUS είναι ενεργοποιημένος. Όταν δεν είναι ενεργοποιημένος κανένας οδηγητής του BUS, ή όταν είναι ενεργοποιημένοι δύο ή περισσότεροι, οπότε η τιμή του BUS είναι απροσδιόριστη, η πλακέτα κατασκευάζει εσωτερικά και δείχει στους ενδείκτες ψευδοτυχαίες τιμές, οι οποίες εναλλάσονται με ρυθμό περίπου 2 Hz (η πλακέτα υλοποιεί εσωτερικά το BUS μέσω ενός πολυπλέκτη, οπότε δεν καίγεται όταν ανάβουν δύο ή περισσότεροι οδηγητές --αυτό βέβαια δεν είναι δικαιολογία γιά να οδηγήτε απρόσεκτα το BUS...). Οι πράσινες LED δείχνουν τις τιμές των εξόδων της πλακέτας: opcode και τα 2 bits σύγκρισης του συσσωρευτή με το μηδέν. Οι κίτρινες LED δείχνουν τις τιμές των εισόδων της πλακέτας, δηλαδή των σημάτων ελέγχου. Στους δύο πολυπλέκτες, ανάβει πάντα μία από τις δύο LED, υποδεικνύοντας την επιλεγμένη είσοδο· τιμή μηδέν (0) του σήματος ελέγχου επιλέγει την επάνω είσοδο, και τιμή ένα (1) επιλέγει την κάτω. Στους τρείς καταχωρητές, η LED δείχνει την τιμή του σήματος επίτρεψης φόρτωσης (1 = αναμένη = φόρτωση στην επόμενη ακμή ρολογιού).
Οι είσοδοι των σημάτων ελέγχου της πλακέτας, στην επάνω πλευρά, έχουν ασθενείς αντιστάσεις καθέλκυσης πάνω στην πλακέτα, επομένως αν τις αφήσετε ανοικτοκυκλωμένες (ασύνδετες) παίρνουν την default τιμή μηδέν (0). Αυτό είναι χρήσιμο γιά τους πολυπλέκτες (εκτελούν την συνηθισμένη λειτουργία τους που είδαμε στην §11.4), και γιά τους τρικατάστατους οδηγητές (όλοι σβηστοί --αρκεί να ανάψετε έναν)· γιά τους καταχωρητές, η default τιμή 0 δεν είναι ιδιαίτερα χρήσιμη, δεδομένου ότι στο σημερινό εργαστήριο θέλουμε πάντα να φορτώνουμε τον PC, και συχνά τον ACC. Η Μνήμη Εντολών βρίσκεται πάντα σε κατάσταση ανάγνωσης, δηλαδή λειτουργεί πάντα σαν συνδυαστικό κύκλωμα --δεν μπορείτε να εγγράψετε σε αυτήν (Read-Only Memory - ROM). Στο μέσον της κάτω πλευράς υπάρχει ένας διακόπτης που αποτελεί την είσοδο ρολογιού: όποτε πατιέται ck=1 (και ανάβει η κόκκινη LED από πάνω του), και όποτε είναι ελεύθερος ck=0· ο διακόπτης αυτός είναι debounced (§8.8).
Σε σχέση με όσα είδαμε στην §11.4, οι επιπλέον δρόμοι που υπάρχουν στην πλακέτα είναι οι εξής, από αριστερά προς τα δεξιά: (α) Ο τρικατάστατος οδηγητής που ελέγχεται από το zero2bus μπορεί να δώσει αρχικές τιμές σε διάφορες μονάδες πάνω στο BUS. (β) Ο πολυπλέκτης στην είσοδο του PC, ελεγχόμενος από το pc_md, επιτρέπει η επόμενη εντολή να μην είναι πάντα η "από κάτω" (η "συν ένα") της τωρινής. (γ) Ο τρικατάστατος οδηγητής στα 8 LS bits της εντολής, που ελέγχεται από το im_rd, προορίζεται γιά τα άλματα (§11.9) και άλλες λειτουργίες του επόμενου εργαστηρίου. (δ) Ομοίως, ο καταχωρητής TMP και ο πολυπλέκτης στην έξοδό του είναι γιά το επόμενο εργαστήριο. (ε) Το στοιχείο "sign, zero" στην έξοδο του συσσωρευτή, που γεννά τις εξόδους accSign και accZero, συγκρίνει την τιμή του συσσωρευτή με το μηδέν (ίση, άνιση, αρνητική, θετική ή μηδέν), και θα το χρειαστούμε στις διακλαδώσεις υπό συνθήκη, στο επόμενο εργαστηριο. (στ) Τέλος, οι δύο τρικατάστατοι οδηγητές δεξιά, ελεγχόμενοι από τα io_out και io_in, προορίζονται γιά επικοινωνία με τον έξω κόσμο (I/O BUS) --βλ. §11.8.
ADDR I_MEM: alu_md: Αποτέλεσμα μετά την ακμή ρολογιού: 00: 0100.10 passB ACC := DM[10] = FD 01: 1111.10 passB ACC := DM[10] = FD 02: 0010.07 and ACC := ACC and DM[07] = FD AND 0F = 0D 03: 0011.00 nor ACC := ACC nor DM[00] = 0D nor 00 = not 0D = F2 04: 0011.04 nor ACC := ACC nor DM[04] = F2 nor 08 = not FA = 05 05: 0000.03 add ACC := ACC + DM[03] = 05 + 04 = 09 06: 0000.01 add ACC := ACC + DM[01] = 09 + 01 = 0A 07: 0000.0F add ACC := ACC + DM[0F] = 0A + FF = 09 08: 0000.04 add ACC := ACC + DM[04] = 09 + 08 = 11 09: 0001.02 sub ACC := ACC - DM[02] = 11 - 02 = 0F 0A: 0001.05 sub ACC := ACC - DM[05] = 0F - 10 = FF 0B: 0001.01 sub ACC := ACC - DM[01] = FF - 01 = FE 0C: 0010.0A and ... < συμπληρώστε τον πίνακα εσείς > 0D: 0011.06 ... < συμπληρώστε τον πίνακα πριν το εργαστήριο > 0E: 0000.0B ... < συμπληρώστε πριν το εργαστήριο > 0F: 0000.06 ... < συμπληρώστε πριν το εργαστήριο > Αρχικά Περιεχόμενα Μνήμης Δεδομένων (διευθύνσεις & δεδομένα στο δεκαεξαδικό): ADDR D_MEM ADDR D_MEM [ Μετέπειτα Χρήση: ] 00: 00 08: 12 01: 01 09: 18 02: 02 0A: 1A 03: 04 0B: F0 [ αργότερα: tmp ] 04: 08 0C: F8 [ αργότερα: sum ] 05: 10 0D: FC (= -4 αν ερμηνευτεί σαν προσημασμένος) 06: 80 0E: FE (= -2 αν ερμηνευτεί σαν προσημασμένος) 07: 0F 0F: FF (= -1 αν ερμηνευτεί σαν προσημασμένος) 10: FD [ αργότερα: δεδομένα εισόδου από πληκτρολόγιο ]
Παίξτε κάμποσο με τους διάφορους διακόπτες, μέχρι να καταλάβετε πώς ελέγχετε εσείς ο ίδιος το τι λειτουργία λέτε στο datapath να κάνει! Φυσικά, δεν πρέπει να είναι αναμένοι ταυτόχρονα δύο ή περισσότεροι οδηγητές (κόκκινα LED) του BUS (αλλοιώς θα δείτε το BUS να παίρνει ψευδοτυχαίες τιμές): σβήστε τον M πριν αρχίσετε να πατάτε εναλλάξ τους A, C, και im_rd. Γιά να καταλάβετε πλήρως τι κάνετε, θα πρέπει να έχετε διαβάσει και τις επόμενες δύο παραγράφους.
Μέσα πλεόν στον υπολογιστή μας, τι θα κάνουμε την εξωτερική πληροφορία που έφτασε στο BUS; Η απάντηση είναι απλή: πρόκειται γιά κατάσταση αντίστοιχη αυτής όταν ο συσσωρευτής, ACC, βάζει την δική του πληροφορία στο BUS --την πληροφορία αυτή την αποθηκεύουμε στη θέση μνήμης με διεύθυνση ADDR, όπου ADDR είναι τα 8 LS bits της εντολής. Έτσι λοιπόν, η εντολή "input ADDR" θα κάνει: I/O_BUS --> BUS --> DM[ADDR], δηλαδή, πέρα από το άναμα του io_in, πρέπει να ανάψουμε και το dm_wr γιά να γίνει εγγραφή στη μνήμη δεδομένων.
Η εντολή "jump ADDR" έχει μιάν απλή αποστολή: η επόμενη εντολή που θα εκτελεστεί μετά από αυτήν δεν θα είναι η "από κάτω" εντολή της jump (η εντολή που είναι γραμμένη στην επόμενη διεύθυνση μνήμης), αλλά μιά άλλη εντολή, σε μιάν αυθαίρετη διεύθυνση ADDR της μνήμης εντολών. Δεδομένου ότι ο υπολογιστής πάντα διαβάζει και εκτελεί την εντολή που βρίσκεται στη διεύθυνση της μνήμης εντολών την οποία διεύθυνση περιέχει ο PC, προκύπτει ότι ο ρόλος της "jump ADDR" είναι να μην φορτώσει στον PC την παλαιά του τιμή συν 1, αλλά αντ' αυτής να φορτώσει εκεί την διεύθυνση ADDR. Γιά να επιτευχθεί αυτό, το datapath μας έχει τον οδηγητή που ελέγχεται από το σήμα im_rd, ο οποίος μπορεί να βάλει την ADDR πάνω στο BUS, και τον πολυπλέκτη στην είσοδο του PC, ο οποίος μπορεί να κάνει τον PC να φορτωθεί από το BUS και όχι από τον αθροιστή που δίνει την παλαιά του τιμή συν 1.
opcode: Λειτουργία: alu_md acc2bus dm_wr pc_md dm_rd acc_ld io_in im_rd 0000 (add) ACC:=ACC+DM[A] 1 000 1 0 0 0 0 0 0001 (sub) ACC:=ACC-DM[A] 1 001 1 0 0 0 0 0 0010 (and) ACC:=ACCandDM[A] 1 010 1 0 0 0 0 0 0011 (nor) ACC:=ACCnorDM[A] 1 011 1 0 0 0 0 0 0100 (input) DM[A]:=IO_BUS 0 xxx 0 0 1 1 0 0 0101 (load) ACC:=DM[A] 1 1xx 1 0 0 0 0 0 0110 (store) DM[A]:=ACC 0 xxx 0 1 0 1 0 0 0111 (jump) PC := A 0 xxx 0 0 0 0 1 1 Πάντα γιά όλες αυτές τις εντολές: pc_ld = 1 ; zero2bus = 0 ; addr_md = 0 ; tmp_ld = 0 ; io_out = 0 ;
Στο εργαστήριο, κατασκευάστε το κύκλωμα ελέγχου στο breadboard, συνδέστε το στην πλακέτα του υπολογιστή, και ελέγξτε το εκτελώντας το πρόγραμμα που υπάρχει στη μνήμη εντολών και φαίνεται στον παρακάτω πίνακα. Όταν ο PC είναι μηδέν (0), μην ξεχνάτε να δίνετε από το πληκτρολόγιο έναν "ενδιαφέροντα" οκτάμπιτο αριθμό.
ADDR I_MEM: Assembly: Αποτέλεσμα μετά την ακμή ρολογιού: 00: 0100.10 input 10 DM[10] = input := δεδομένα εισόδου από πληκτρολόγιο 01: 1111.10 jump 10 PC := 10 .... 10: 0101.08 load 08 ACC := DM[08] = 12 11: 0000.09 add 09 ACC := ACC + DM[09] = 12 + 18 = 2A 12: 0000.0A add 0A ACC := ACC + DM[0A] = 2A + 1A = 44 13: 0110.0B store 0B DM[0B] = tmp := ACC = 44 14: 0001.01 sub 01 ACC := ACC - DM[01] = 44 - 01 = 43 15: 0010.07 and 07 ACC := ACC and DM[07] = 43 and 0F = 03 16: 0011.00 nor 00 ACC := ACC nor DM[00] = 03 nor 00 = not 03 = FC 17: 0011.01 nor 01 ACC := ACC nor DM[01] = FC nor 01 = not FD = 02 18: 0000.10 add 10 ACC := ACC + DM[10] = 02 + input 19: 0000.0C add 0C ACC := ACC + DM[0C] = 02 + input + sum 1A: 0110.0C store 0C DM[0C] = sum := ACC = sum + input + 2 1B: 0111.00 jump 00 PC := 00 [άπειρος βρόχος άθροισης] Αρχικά Περιεχόμενα Μνήμης Δεδομένων (διευθ. & δεδομένα στο δεκαεξαδικό): ADDR D_MEM ADDR D_MEM [ Χρήση: ] 00: 00 08: 12 01: 01 09: 18 02: 02 0A: 1A 03: 04 0B: F0 [ tmp ] 04: 08 0C: F8 [ sum ] 05: 10 0D: FC (= -4 αν ερμηνευτεί σαν προσημασμένος) 06: 80 0E: FE (= -2 αν ερμηνευτεί σαν προσημασμένος) 07: 0F 0F: FF (= -1 αν ερμηνευτεί σαν προσημασμένος) 10: FD [ input από πληκτρολόγιο ]
[Up - Table of Contents] [Prev - 10. FSM] |
[printer version - PDF] [12. Branches, Pointers - Next] |
Up to the Home Page of CS-120
|
© copyright
University of Crete, Greece.
last updated: 22 Jan. 2007, by M. Katevenis. |