ΗΥ-120: Ψηφιακή Σχεδίαση
Φθινόπωρο 2002 |
Τμ. Επ. Υπολογιστών Πανεπιστήμιο Κρήτης |
Εκτός από τον δεκαεξάμπιτο αθροιστή, η ALU θα έχει και 16 πύλες ΚΑΙ και 16 πύλες Ή γιά να μπορεί να κάνει τις αντίστοιχες λογικές πράξεις πάνω στις λέξεις εισόδου· πρόκειται γιά λογικές πράξεις bit-προς-bit (bitwise operations), δηλαδή το bit i της εξόδου θα είναι το λογικό ΚΑΙ ή το λογικό Ή του bit i της εισόδου A και του bit i της εισόδου B. Κάθε τετοια δεκαεξάδα πυλών φαίνεται σχεδιασμένη σαν μία πύλη κάτω από τον αθροιστή. Ο πολυπλέκτης δεξιά επιλέγει αν επιθυμούμε η έξοδος της ALU να είναι το αποτέλεσμα της αριθμητικής πράξης που κάνει ο αθροιστής, ή το αποτέλεσμα της λογικής πράξης που κάνουν οι πύλες· ο πολυπλέκτης κάτω επιλέγει αν το αποτέλεσμα της λογικής πράξης θα είναι το λογικό ΚΑΙ ή το λογικό Ή. Τέλος, ο αριστερός επάνω πολυπλέκτης επιλέγει αν η πράξη θα γίνει με την πρώτη είσοδο της ALU, A, ή με τον αριθμό μηδέν· εάν επιλέξουμε τον αριθμό 0, ο αθροιστής θα δώσει έξοδο 0+B = B ή 0+(B')+1 = -B, οι πύλες ΚΑΙ θα δώσουν έξοδο 0, και οι πύλες Ή θα δώσουν έξοδο B ή B' (ΌΧΙ B). Έτσι, συνολικά, διαπιστώνουμε ότι η ALU μπορεί να κάνει τις παρακάτω πράξεις, ανάλογα με την εκάστοτε τιμή των τεσσάρων σημάτων ελέγχου της, mode. Η ένδειξη "x" στο mode σημαίνει ότι η ALU κάνει την ίδια πράξη όποια τιμή και να έχει το αντίστοιχο bit του mode (συνθήκη αδιαφορίας). Οι πράξεις που μας ενδιαφέρουν να χρησιμοποιήσουμε στον υπολογιστή μας σημειώνονται με παχειά γράμματα.
mode: Πράξη: (όνομα) 0x01 ALUout = 0 0011 ALUout = B 00x0 ALUout = B (passB) 0111 ALUout = NOT B (not) 01x0 ALUout = -B 1001 ALUout = A AND B (and) 10x0 ALUout = A+B (add) 1011 ALUout = A OR B (or) 11x0 ALUout = A-B (sub) 1101 ALUout = A AND (NOT B) 1111 ALUout = A OR (NOT B)
Στο σχήμα φαίνεται η απαιτούμενη συνδεσμολογιά γιά να γίνονται οι πράξεις όπως τις περιγράψαμε. Ο συσσωρευτής είναι ένας ακμοπυροδότητος καταχωρητής, που σημειώνεται σαν "ACC". Ένα εξωτερικό κύκλωμα, που θα δούμε σε λίγο, τροφοδοτεί τη διεύθυνση Addr στη μνήμη δεδομένων, καθώς και τα σήματα ελέγχου ανάγνωσης (read enable, dm_re) και εγγραφής (write enable, dm_we), προκαλώντας την ανάγνωση μιάς λέξης (δηλ. ενός αριθμού) από τη θέση μνήμης Addr. Η ALU παίρνει το περιεχόμενο του συσσωρευτή ACC στη μιά της είσοδο, και τον αριθμό που διαβάσαμε από τη μνήμη στην άλλη· το εξωτερικό κύκλωμα ελέγχου προσδιορίζει, μέσω του alu_md, το είδος της πράξης που πρέπει να γίνει, και το αποτέλεσμα της πράξης δίδεται σαν είσοδος στο συσσωρευτή. Όταν έλθει η ενεργή ακμή του ρολογιού, το αποτέλεσμα αυτής της πράξης αντικατθιστά το παλαιό περιεχόμενο του συσσωρευτή. Κατά καιρούς, πρέπει το αποτέλεσμα των πράξεων να αποθηκεύεται (γράφεται) σε μιά επιθυμητή θέση (λέξη) μνήμης, προκειμένου μετά να ξεκινήσει κάποια νέα σειρά πράξεων στο συσσωρευτή. Γιά να γίνεται η αποθήκευση αυτή προβλέφτηκε ένας τρικατάστατος οδηγητής, δεξιά κάτω, ο οποίος τοποθετεί το περιεχόμενο του ACC πάνω στη λεωφόρο (bus), απ' όπου και το παραλαμβάνει η μνήμη γιά να γίνει η εγγραφή. Τα σήματα ελέγχου που πρέπει να ενεργοποιηθούν είναι τα acc2bus (ACC προς bus - ACC to bus, όπου το "2" είναι ομόηχο με το "to") και dm_we.
Ο κάθε υπολογιστής "καταλαβαίνει", δηλαδή μπορεί να αναγνωρίσει και να εκτελέσει, ορισμένες μόνο, συγκεκριμένες εντολές ή τύπους εντολών· αυτές τις ονομάζουμε σύνολο ή ρεπερτόριο εντολών (instruction set) του υπολογιστή. Οι εντολές του δικού μας υπολογιστή θα αποτελούνται από δύο κομάτια καθεμία: έναν "κώδικα πράξης" (operation code, ή opcode εν συντομία), και μιά διεύθυνση Addr. Κάθε εντολή μας θα είναι 16 bits, από τα οποία τα 4 MS bits θα είναι ο opcode και τα 12 LS bits θα είναι η διεύθυνση. Γιά να μπορεί ο υπολογιστής μας να εκτελεί τις αριθμητικές και λογικές πράξεις που περιγράψαμε παραπάνω, το ρεπερτόριό του πρέπει να περιλαμβάνει τις εξής εντολές:
load Addr ACC <- DM[Addr] not Addr ACC <- NOT DM[Addr] add Addr ACC <- ACC + DM[Addr] and Addr ACC <- ACC AND DM[Addr] sub Addr ACC <- ACC - DM[Addr] or Addr ACC <- ACC OR DM[Addr] store Addr DM[Addr] <- ACCΟι δύο λέξεις αριστερά, με τα πλάγια γράμματα, δείχνουν τη συμβολική γραφή της εντολής: η πρώτη λέξη είναι το σύμβολο του opcode, ενώ το Addr αντικαθίσταται κάθε φορά από τη συγκεκριμένη διεύθυνση που επιθυμούμε να χρησιμοποιήσουμε --έναν αριθμό από 0 μέχρι 4095, αφού οι διευθύνσεις είναι δωδεκάμπιτες στον υπολογιστή μας. Ένα πρόγραμμα με τις εντολές του γραμμένες με αυτό το συμβολισμό λέμε ότι είναι γραμμένο σε "Γλώσσα Assembly". Στη μνήμη του υπολογιστή, φυσικά, το μόνο που υπάρχει είναι άσσοι και μηδενικά, άρα γιά να εκτελεστεί ένα πρόγραμμα γραμμένο σε Assembly πρέπει πρώτα να μετατραπεί στην δυαδική του αναπαράσταση, που λέγεται "Γλώσσα Μηχανής (machine language, ή object code, ή binary code). Η μετατροπή αυτή είναι πολύ απλή, και συνίσταται στην αντικατάσταση κάθε συμβολικού opcode με τον αντίστοιχο δυαδικό του κώδικα (βλ. παρακάτω), και στη μετατροπή κάθε διεύθυνσης από το δεκαδικό στο δυαδικό. Τη μετατροπή αυτή (και μερικές άλλες σχετικές βοηθητικές εργασίες) την κάνει συνήθως ένα μικρό πρόγραμμα υπολογιστή που ονομάζεται Assembler.
Δίπλα στο συμβολισμό Assembly της κάθε εντολής, στο παραπάνω πινακάκι, υπάρχει μία "εξίσωση μεταφοράς καταχωρητών" (register transfer equation), η οποία περιγράφει τις ενέργειες που πρέπει να γίνουν προκειμένου ο υπολογιστής να εκτελέσει την εντολή. Σε αυτές τις εξισώσεις, το αριστερό βέλος υποδεικνύει μεταφορά και εγγραφή πληροφορίας (εκχώρηση - assignment). Ο συμβολισμός "DM[Addr]" σημαίνει τη θέση (λέξη) της μνήμης δεδομένων στη διεύθυνση Addr. Όταν ο συσσωρευτής, ACC, εμφανίζεται και δεξιά και αριστερά από το βέλος, τότε δεξιά μεν σημαίνει το παλαιό περιεχόμενό του (πριν την ακμή ρολογιού), αριστερά δε σημαίνει τη νέα τιμή του (μετά την ακμή ρολογιού).
Έτσι, η εντολή load Addr (φόρτωσε) διαβάζει τον αριθμό που περιέχεται στη διεύθυνση Addr της μνήμης, δηλαδή διαβάζει το DM[Addr], και το αντιγράφει στο συσσωρευτή. Η εντολή add Addr προσθέτει το παλαιό περιεχόμενο του ACC με τον αριθμό που περιέχεται στη διεύθυνση Addr της μνήμης, και γράφει το αποτέλεσμα στον ACC. Αντίστοιχα, οι εντολές sub, not, and, or κάνουν άλλες παρόμοιες πράξεις. Τέλος, η εντολή store Addr (αποθήκευσε) αντιγράφει το περιεχόμενο του ACC στη θέση (λέξη) μνήμης με διεύθυνση Addr. Γιά παράδειγμα, λοιπόν, αν η μνήμη δεδομένων περιέχει τους αριθμούς που φαίνονται στο παραπάνω σχήμα, τότε το πρόγραμμα: "load 11; add 12; add 13; store 14;" θα προκαλέσει τις εξής ενέργειες. Πρώτα θα διαβαστεί ο αριθμός 110 από τη θέση 11 και θα γραφτεί στο συσσωρευτή· μετά, θα διαβαστεί ο αριθμός 120 και θα προστεθεί στον 110, και το αποτέλεσμα 230 θα γραφτεί στο συσσωρευτή· εν συνεχεία, θα διαβαστεί το 130 από τη θέση 13, θα προστεθεί στο 230, και το αποτέλεσμα 360 θα γραφτεί στο συσσωρευτή· και τέλος, το περιεχόμενο 360 του συσσωρευτή θα γραφτεί στη θέση μνήμης 14.
Γιά να μπορέσει το κύκλωμά μας να εκτελέσει μιάν εντολή, πρέπει να τη διαβάσουμε από τη μνήμη εντολών, και γιά το σκοπό αυτό χρειαζόμαστε τη διεύθυνση της μνήμης αυτής όπου βρίσκεται η επιθυμητή εντολή. Αυτός είναι ο ρόλος του καταχωρητή PC: να θυμάται τη διεύθυνση αυτή. Στο παράδειγμα του σχήματος, ο PC περιέχει τον αριθμό 3. Ο αριθμός αυτός δίδεται σαν διεύθυνση στη μνήμη εντολών. Συνεπώς, η μνήμη εντολών διαβάζει και μας δίνει το περιεχόμενο της θέσης της υπ' αριθμόν 3, το οποίο εδώ τυχαίνει να είναι η εντολή "load 11" --κωδικοποιημένη σε γλώσσα μηχανής φυσικά, δηλαδή "0000000000001011" όπως θα δούμε σε λίγο. Κάθε λέξη της μνήμης εντολών είναι 16 bits, και περιέχει μιάν εντολή. Τα 16 σύρματα ανάγνωσης που βγαίνουν από τη μνήμη αυτή, τα χωρίζουμε σε 4 αριστερά (MS) σύρματα που πηγαίνουν στο κύκλωμα ελέγχου, και 12 δεξιά (LS) σύρματα που πηγαίνουν σαν διεύθυνση στη μνήμη δεδομένων. Αφού όλες οι εντολές του υπολογιστή μας αποτελούνται από έναν opcode στα 4 MS bits και μία διεύθυνση στα 12 LS bits, με τη συνδεσμολογία αυτή πηγαίνει ο opcode στο κύκλωμα ελέγχου και η διεύθυνση στη μνήμη δεδομένων. Στο παράδειγμά μας, ο opcode είναι 0000 (που σημαίνει load), και η διεύθυνση είναι 000000001011 (δηλ. 11 δεκαδικό). Το κύκλωμα ελέγχου, βλέποντας την εντολή load, θα ζητήσει ανάγνωση από τη μνήμη δεδομένων (dm_re=1, dm_we=0, acc2bus=0) και θα θέσει την ALU σε λειτουργία passB (alu_md=0000 ή 0010). Η μνήμη δεδομένων, βλέποντας τη διεύθυνση 11 και ότι της ζητείται ανάγνωση, θα τοποθετήσει τον αριθμό 110 στη λεωφόρο· η ALU, εκτελώντας λειτουργία passB, θα περάσει τον αριθμό 110 στην έξοδό της· μόλις έλθει η ενεργή ακμή του ρολογιού, ο αριθμός αυτός θα γραφτεί στο συσσωρευτή ACC, ολοκληρώνοντας έτσι την εκτέλεση της εντολής load 11.
Μετά την εκτέλεση της εντολής load 11 από τη θέση 3 της μνήμης εντολών, πρέπει να εκτελεστεί η επόμενη εντολή. Κατά πάγια σύμβαση, εκτός ειδικών εξαιρέσεων που θα δούμε σε λίγο, η επόμενη εντολή βρίσκεται γραμμένη στην ακριβώς επόμενη θέση μνήμης --εδώ, στη διεύθυνση 4. Γιά να προκύψει η επόμενη αυτή διεύθυνση γιά τη μνήμη εντολών, χρησιμοποιούμε τον αυξητή (incrementor) που φαίνεται αριστερά στο σχήμα, δηλαδή έναν αθροιστή με δεύτερη είσοδο το +1. Έτσι, στην ίδια παραπάνω ενεργή ακμή του ρολογιού που γράφεται ο αριθμός 110 στο συσσωρευτή ACC, γράφεται και το αποτέλεσμα 4 της πρόσθεσης 3+1 στον καταχωρητή PC. Το αποτέλεσμα είναι ότι στον επόμενο κύκλο ρολογιού ο PC θα περιέχει 4· η μνήμη εντολών θα διαβάσει και θα μας δώσει το περιεχόμενο "0010000000001100" της θέσης 4, δηλαδή την εντολή "add 12"· το κύκλωμα ελέγχου, βλέποντας opcode=0010 (add), θα δώσει dm_re=1, dm_we=0, acc2bus=0, και alu_md=10x0 (δηλ. add)· η μνήμη δεδομένων, βλέποντας διεύθυνση 12 και dm_re=1, θα διαβάσει και θα δώσει στη λεωφόρο τον αριθμό 120· η ALU, βλέποντας ACC=110, στη λεωφόρο το 120, και alu_md=add, θα προσθέσει 110+120 και θα δώσει στην έξοδό της 230· και ο αθροιστής/αυξητής αριστερά, βλέποντας PC=4, θα δώσει στην έξοδό του 4+1=5. Μόλις έλθει η επόμενη ενεργή ακμή ρολογιού, το 230 θα μπεί στον ACC, και το 5 θα μπεί στον PC, ολοκληρώνοντας έτσι την εκτέλεση της εντολής add 12, και προετοιμάζοντάς μας γιά την επόμενη εντολή, add 13, από τη θέση 5.
Ο καταχωρητής PC ονομάζεται "Μετρητής Προγράμματος" (Program Counter - PC), επειδή είναι κατά βάση ένας μετρητής που αυξάνεται κατά 1 στο τέλος της εκτέλεσης κάθε εντολής γιά να μας δώσει τη διεύθυνση της επόμενης εντολής· ο πολυπλέκτης που υπάρχει στην είσοδό του προορίζεται γιά την αρχικοποίησή του, όταν δίδεται σήμα reset. Το κύκλωμα ελέγχου (control) είναι υπεύθυνο γιά τη δημιουργία όλων των σημάτων ελέγχου που λένε σε κάθε μονάδα τι να κάνει κάθε φορά. Όλες οι εντολές που είδαμε μέχρι στιγμής διαβάζονται και εκτελούνται σε έναν κύκλο ρολογιού η καθεμία, και γι' αυτό το κύκλωμα ελέγχου, μέχρι στιγμής, είναι ένα απλό συνδυαστικό κύκλωμα. Ο πίνακας αληθείας του προκύπτει αν σκεφτούμε τι εργασίες πρέπει να γίνουν γιά την εκτέλεση κάθε εντολής:
reset: opcode: md_re: md_we: alu_md: acc2bus: pc_md: 1 xxxx 0 0 0x01 (zero) 0 0 0 0000 (load) 1 0 00x0 (passB) 0 1 0 0001 (store) 0 1 00x0 (passB) 1 1 0 0010 (add) 1 0 10x0 (add) 0 1 0 0011 (sub) 1 0 11x0 (sub) 0 1 0 0100 (and) 1 0 1001 (and) 0 1 0 0101 (or) 1 0 1011 (or) 0 1 0 0111 (not) 1 0 0111 (not) 0 1Το σήμα reset επαναφέρει τον υπολογιστή στην αρχική κατάσταση εκκίνησης, ό,τι κι αν έκανε αυτός πριν (opcode=xxxx): αρχικοποιεί τον PC στο 0, ούτως ώστε να αρχίσει να εκτελεί εντολές από την αρχή της μνήμης εντολών. Οι εντολές load και add εκτελούνται όπως περιγράφηκε λεπτομερώς παραπάνω. Οι εντολές sub, and, or, και not εκτελούνται κατά εντελώς ανάλογο τρόπο --απλώς αλλάζει το mode της ALU. Η εντολή store διαφέρει λίγο: ανάβοντας το acc2bus=1 (με md_re=0, φυσικά), τοποθετεί την τιμή του συσσωρευτή στο bus· ενεργοποιώντας το md_we=1, η τιμή αυτή από το bus εγγράφεται στη μνήμη δεδομένων· επίσης, θέτοντας την ALU σε mode "passB", η τιμή από το bus επανεγγράφεται στον ACC, άρα διατηρείται εκεί αμετάβλητη. Υπάρχει μιά λεπτομέρεια που δεν είναι σωστή σε αυτό το συνδυαστικό τρόπο γέννησης του σήματος md_we: δεν υπάρχει εγγύηση ότι το σήμα αυτό θα ανάψει μετά τη σταθεροποίηση της διεύθυνσης της μνήμης δεδομένων, όπως πρέπει να γίνει· το πρόβλημα αυτό δεν μπορεί να διορθωθεί παρά μόνο αν αλλάξει το κύκλωμα ελέγχου και γίνει ακολουθιακό (FSM).
Δεξιά βλέπουμε ένα παράδειγμα προγράμματος που υπολογίζει το άθροισμα 10+9+8+...+3+2+1 και το γράφει στη θέση 14 της μνήμης δεδομένων. Ο υπολογισμός γίνεται χρησιμοποιώντας κυρίως τις θέσεις 12 και 13 της μνήμης δεδομένων (κάτω μέρος σχήματος), οι οποίες έχουν αρχική τιμή 10 (η μεταβλητή "n") και 0 (η μεταβλητή "s") αντίστοιχα. Οι τρείς πρώτες εντολές του προγράμματος (θέσεις 3, 4, και 5) διαβάζουν την τρέχουσα τιμή της μεταβλητής s, της προσθέτουν την τρέχουσα τιμή της μεταβλητής n, και γράφουν το αποτέλεσμα πίσω στην s. Την πρώτη φορά που εκτελούνται αυτές οι εντολές, αυξάνουν το s από 0 σε 10. Οι τρείς επόμενες εντολές (θέσεις 6, 7, και 8) ελάττώνουν τη μεταβλητή n κατά 1· την πρώτη φορά που εκτελούνται αλλάζουν το n από 10 σε 9. Στη συνέχεια εκτελείται η εντολή bne 3 από τη θέση 9· η εντολή αυτή σημαίνει "εάν ο συσσωρευτής δεν ισούται με μηδέν, διακλαδώσου (πήγαινε) στην εντολή 3" (branch if ACC not equal to zero - brach not equal - bne). Επειδή εκείνη την ώρα ο συσσωρευτής περιέχει το n=9, που είναι διάφορο του μηδενός, η συνθήκη της διακλάδωσης είναι αληθής και η διακλάδωση επιτυγχάνει (πραγματοποιείται). Έτσι, επόμενες εντολές εκτελούνται οι εντολές 3, 4, και 5, αυξάνοντας το s από 10 σε 19, και μετά οι 6, 7, και 8, μειώνοντας το n από 9 σε 8. Μετά, ξαναεκτελείται η bne 3· επειδή ο συσσωρευτής περιέχει το 8, η διακλάδωση επιτυγχάνει και πάλι. Έτσι, οι εντολές 3 έως και 9 θα ξαναεκτελεστούν κάμποσες φορές ακόμα, αυξάνοντας διαδοχικά το s κατά 8, 7, ..., 2, και 1, και μειώνοντας το n διαδοχικά σε 7, 6, ..., 1, και 0. Την τελευταία φορά, στο συσσωρευτή θα έχει μείνει n=0. Τότε, η διακλάδωση bne 3 θα αποτύχει, διότι ο συσσωρευτής δεν είναι πλέον διάφορος του μηδενός· έτσι, η επόμενη εντολή δεν θα διαβαστεί από τη θέση 3 όπως πρίν, αλλά από τη θέση 10, δηλαδή από την "από κάτω" θέση, όπως κάνουν και όλες οι άλλες εντολές που δεν είναι διακλαδώσεις. Τώρα, οι εντολές 10 και 11 θα αντιγράψουν το τελικό αποτέλεσμα 10+9+8+...+3+2+1 = 55 από τη θέση 13 (s) στη θέση 14 (S) και ο σκοπός του προγράμματος θα έχει εκπληρωθεί.
Γιά να μπορέσει ο υπολογιστής μας να εκτελεί εντολές διακλάδωσης (υπό συνθήκη) και άλματος (χωρίς συνθήκη), απαιτείται μιά προσθήκη στο κύκλωμα του PC, η οποία φαίνεται στο επόμενο σχήμα, και μιά αλλαγή στο κύκλωμα ελέγχου. Στον PC, μεγαλώσαμε τον πολυπλέκτη εισόδου του απο 2-σε-1 σε 4-σε-1, ούτως ώστε να προσφέρουμε περισσότερες επιλογές γιά τον τρόπο προσδιορισμού της επόμενης τιμής του PC, δηλαδή του ποιά θα είναι (από ποιά διεύθυνση) η επόμενη εντολή. Η νέα επιλογή που προσθέτουμε είναι το πεδίο Addr της παρούσας εντολής· έτσι, όταν εκτελούμε μιάν εντολή διακλάδωσης ή άλματος, όπως bne 3, το πεδίο διεύθυνσής της (εδώ το "3") μπορεί να αποτελέσει την επόμενη τιμή του PC όταν η εντολή είναι άλμα ή επιτυχημένη διακλάδωση. Η είσοδος "10" του πολυπλέκτη προορίζεται γιά τις προσθήκες της επομένης παραγράφου. Όσον αφορά το κύκλωμα ελέγχου, αυτό χρειάζεται περισσότερες εισόδους τώρα, γιά να ξέρει να ελέγξει σωστά τη λειτουργία του υπολογιστή: δεν αρκεί πλέον να ξέρει μόνο τον opcode της παρούσας εντολής (και το αν κάνουμε reset ή όχι), αλλά πρέπει να ξέρει και το αν ο συσσωρευτής είναι μηδέν ή όχι, δεδομένου ότι από αυτό εξαρτάται η έκβαση της εντολής bne. Αυτός είναι ο ρόλος της προσθήκης "sign, zero" (πρόσημο, μηδέν) που κοιτάζει το περιεχόμενο του συσσωρευτή, ACC:
Ο υπολογιστής μας θα έχει τέσσερεις εντολές διακλάδωσης υπό συνθήκη: (α) bne (branch not equal): διακλαδώσου εάν ACC διάφορος του μηδενός· (β) beq (branch equal): διακλαδώσου εάν ACC ίσος με το μηδέν· (γ) bge (branch greater or equal): διακλαδώσου εάν ACC μεγαλύτερος ή ίσος του μηδενός· και (δ) blt (branch less than): διακλαδώσου εάν ACC μικρότερος του μηδενός· επίσης θα έχει μιάν εντολή άλματος χωρίς συνθήκη, jump, η οποία αλλάζει πάντοτε την επόμενη εντολή. Γιά να εκτελεστούν οι εντολές διακλάδωσης πρέπει να ξέρουμε το πρόσημο του συσσωρευτή καθώς και αν αυτός είναι μηδέν ή όχι. Όπως είδαμε στο εργαστήριο 6, το πρόσημο ενός αριθμού κωδικοποιημένου κατά συμπλήρωμα ως προς 2 είναι το περισσότερο σημαντικό (MS) bit του (το αριστερότερο bit): όταν αυτό είναι 1, ο αριθμός είναι αρνητικός, ενώ όταν το MS bit είναι 0, ο αριθμός είναι θετικός ή μηδέν. Η ανίχνευση του εάν ο αριθμός είναι μηδέν ή διάφορος του μηδενός είναι ελαφρά πιό δαπανηρή: απαιτεί μιά πύλη NOR με τόσες εισόδους όσα τα bits του αριθμού. Αφού η έξοδος της πύλης NOR είναι 1 όταν και μόνον όταν όλες οι είσοδοί της είναι μηδέν, συνδέοντας κάθε bit του συσσωρευτή σε μία είσοδο της NOR έχουμε την έξοδό της να ανάβει όταν και μόνον όταν όλα τα bits του συσσωρευτή είναι μηδέν, δηλαδή όταν ο ACC περιέχει τον αριθμό μηδέν. Ονομάζουμε "accSign" το MS bit του ACC, ονομάζουμε "accZero" την έξοδο της πύλης NOR 16 εισόδων, και παρέχουμε στο κύκλωμα ελέγχου αυτά τα δύο σήματα σαν εισόδους του. Τώρα, ο πίνακας αληθείας του κυκλώματος ελέγχου πρέπει να μετατραπεί ως εξής, σε σχέση με αυτόν που είδαμε στη σελίδα 4:
reset: opcode: aluZero: aluSign: md_re,we: alu_md: acc2bus: pc_md: 1 xxxx x x 0 0 zero 0 00 0 0000 (load) x x 1 0 passB 0 01 0 0001 (store) x x 0 1 passB 1 01 0 ... (add,...) x x 1 0 add,... 0 01 0 1000 (bne) 0 x 0 0 passB 1 11 0 1000 (bne) 1 x 0 0 passB 1 01 0 1001 (beq) 0 x 0 0 passB 1 01 0 1001 (beq) 1 x 0 0 passB 1 11 0 1010 (bge) x 0 0 0 passB 1 11 0 1010 (bge) x 1 0 0 passB 1 01 0 1011 (blt) x 0 0 0 passB 1 01 0 1011 (blt) x 1 0 0 passB 1 11 0 1100 (jump) x x 0 0 passB 1 11Γιά τις εντολές που προϋπήρχαν, τα σήματα ελέγχου δεν εξαρτώνται από τις νέες εισόδους, aluZero και aluSign, όπως υποδεικνύουν τα "x" στις αντίστοιχες γραμμές και στήλες· το ίδιο ισχύει και γιά την εντολή jump. Γιά τις εντολές διακλάδωσης, όμως, το σήμα pc_md που ελέγχει το ποιά θα είναι η επόμενη εντολή καθορίζεται από τον τύπο της διακλάδωσης και από τις εισόδους aluZero και aluSign. Κατά την εκτέλεση των εντολών διακλάδωσης και άλματος, οδηγούμε το συσσωρευτή ACC στη λεωφόρο bus (acc2bus=1, md_re=0), και θέτουμε την ALU σε λειτουργία passB προκειμένου η τιμή που υπήρχε στο συσσωρευτή να ανακυκλώνεται εκεί, και επομένως να διατηρείται αμετάβλητη.
Γιά να μπορεί να εκτελεί εντολές όπως η παραπάνω addx, ο υπολογιστής μας χρειάζεται τις προσθήκες που φάινονται δίπλα: ένα πολυπλέκτη στην είσοδο διευθύνσεων της μνήμης δεδομένων, προκειμένου η διεύθυνση αυτή να μπορεί να προέρχεται είτε από την εντολή, είτε από ένα δεδομένο που διαβάσαμε από την ίδια τη μνήμη δεδομένων. Επειδή πρέπει να γίνουν δύο αναγνώσεις από τη μνήμη δεδομένων, η μία μετά την άλλη, χρειαζόμαστε και έναν καταχωρητή, tmp, που να κρατάει το αποτέλεσμα της πρώτης ανάγνωσης διαθέσιμο γιά τη δεύτερη. Το κύκλωμα ελέγχου, τώρα, δεν μπορεί πλέον να είναι συνδυαστικό: επειδή η εντολή addx χρειάζεται δύο κύκλους ρολογιού, το κύκλωμα ελέγχου πρέπει να περιέχει και 1 bit κατάστασης που να μας πληροφορεί αν τώρα βρισκόμαστε στον πρώτο ή στο δεύτερο κύκλο της εκτέλεσης· επομένως, το κύκλωμα ελέγχου τώρα θα είναι μιά μικρή FSM δύο καταστάσεων. Επίσης, χρειαζόμαστε μιά μέθοδο ώστε οι καταχωρητές ACC και PC να μπορούν να διατηρούν την τιμή τους αμετάβλητη τον πρώτο από τους δύο αυτούς κύκλους ρολογιού. Η λύση είναι η χρήση καταχωρητών με ένα μικρό εσωτερικό πολυπλέκτη ανακύκλωσης· αυτοί ονομάζονται καταχωρητές με είσοδο ελέγχου "επίτρεψης φόρτωσης" (load enable). Παρόμοια με την εντολή addx, θα χρειαστούμε σίγουρα και μιάν εντολή "storex", και βολική θα είναι και μία "loadx".
Μιά άλλη δυνατότητα που δεν είχε η προηγούμενη μορφή του υπολογιστή μας, και που προστέθηκε εδώ, είναι η δυνατότητα εντολής άλματος "jumpx Addr" με μεταβλητή διεύθυνση προορισμού: η εντολή αυτή διαβάζει έναν αριθμό από τη θέση Addr της μνήμης δεδομένων και τον γράφει στον PC. Με τον τρόπο αυτό, η επόμενη εντολή που θα εκτελεστεί θα είναι μιά εντολή που μπορεί να την επιλέξει και να τη μεταβάλει το ίδιο το πρόγραμμα την ώρα που τρέχει! Όπως θα δούμε στο μάθημα "Οργάνωση Υπολογιστών", με την εντολή αυτή μπορούμε να υλοποιούμε διαδικασίες (procedures) και άλλες προηγμένες δυνατότητες των οντοκεντρικών γλωσσών προγραμματισμού.
Όταν μεγαλώνουμε τα transistors μιάς πύλης, τότε μεγαλώνει κατ' αναλογία και το ρεύμα οδήγησης που δίνει η πύλη, κι επομένως μειώνεται ανάλογα και η καθυστέρησή της. Βέβαια, από ένα σημείο και πέρα δεν μπορούμε να μειώσουμε άλλο την καθυστέρηση, διότι τα μεγαλύτερα transistors αυξάνουν την παρασιτική χωρητικότητα της ίδιας της εξόδου της πύλης μας. Τέλος, οι πύλες με περισσότερες εισόδους έχουν μεγαλύτερη καθυστέρηση· σε πολλές περιπτώσεις, η καθυστέρηση είναι ανάλογη προς τον αριθμό των εισόδων, ενώ σε άλλες περιπτώσεις δεν είναι τόσο άσχημη αλλά και πάλι χειροτερεύει όταν αυξάνει το πλήθος των εισόδων.
Η μεγαλύτερη συνηθισμένη πηγή κατανάλωσης ισχύος (ξόδεμα μπαταρίας, ή θέρμανση του κυκλώματος) των chips CMOS είναι η ενέργεια φόρτισης και εκφόρτισης των παρασιτικών χωρητικοτήτων που αυτά έχουν μέσα τους. Κάθε φορά που η λογική τιμή ενός ηλεκτρικού κόμβου ανεβαίνει από το 0 στο 1 και μετά ξαναπέφτει στο 0, χάνεται (μετατρέπεται σε θερμότητα) μιά ποσότητα ενέργειας ανάλογη προς το μέγεθος της χωρητικότητας. Όσο περισσότεροι, και μεγαλύτερης χωρητικότητας, ηλεκτρικοι κόμβοι ανεβοκατεβαίνουν (αναβοσβήνουν) μέσα σε ένα chip, και όσο περισσότερες φορές ανά δευτερόλεπτο το κάνουν αυτό, τόσο μεγαλύτερη ισχύ καταναλώνει το chip αυτό.
Up to the Home Page of CS-120
|
© copyright
University of Crete, Greece.
Last updated: 14 Jan. 2003, by M. Katevenis. |