Προαιρετική Άσκηση 13.3: Απλή Ομοχειρία (Προ-Ανάγνωση επόμενης Εντολής)
Όσοι από σας ενδιαφέρεστε ιδιαίτερα γιά το hardware και έχετε χρόνο,
αλλάξτε το datapath και την FSM και το κύκλωμα ελέγχου
προκειμένου να εισάγετε στον επεξεργαστή σας
μιάν απλή μορφή ομοχειρίας (pipelining) δύο (μόνο) επιπέδων:
κατά τη διάρκεια εκτέλεσης κάθε εντολής
θα γίνεται και η ανάγνωση της επόμενης εντολής.
Αυτή η απλή μορφή pipelining ονομάζεται συνήθως "fetch-execute overlap".
Στην παλαιά (non-pipelined) μορφή του επεξεργαστή,
κάθε εντολή "παραδίδει" στην επόμενή της
--εκτός από τα περιεχόμενα των καταχωρητών γενικού σκοπού $0-$31
και τα περιεχόμενα της μνήμης--
την τιμή (διεύθυνση) που περιέχεται στον PC,
και η οποία καθορίζει από πού θα διαβαστεί η επόμενη εντολή.
Στη νέα (pipelined) μορφή του επεξεργαστή,
κάθε εντολή θα "παραδίδει" στην επόμενή της
--εκτός από τους $0-$31 και τη μνήμη--
τόσο τη διεύθυνση της επόμενης εντολής συν 4,
σαν περιεχόμενο του PC,
όσο και την ίδια την επόμενη εντολή,
σαν περιεχόμενο του IR.
Έτσι,
η εκτέλεση της κάθε εντολής θα ξεκινά με
έτοιμο, φορτωμένο τον καταχωρητή IR,
και επομένως θα γλιτώνουμε τον ένα κύκλο ανάγνωσης της εντολής,
δηλαδή δεν θα υπάρχει η κατάσταση i_fetch.
Αυτό θα επιτευχθεί ώς εξής:
-
decode_rr:
Όλες οι εντολές ξεκινούν με την κατάσταση αυτή.
Στη διάρκειά της, εκτός από όσα ήδη γίνονταν,
θα γίνονται και τα εξής:
- Διαβάζουμε από M[pc] και φυλάμε αυτό που διαβάσαμε
στον (νέο) καταχωρητή irNxt.
Αφού ο καταχωρητής pc περιέχει
τη διεύθυνση της παρούσας εντολής συν 4,
αυτό που διαβάσαμε από τη μνήμη είναι η "από κάτω" εντολή.
Σε περίπτωση που η παρούσα εντολή αποδειχθεί ότι δεν είναι
εντολή επιτυχημένης μεταφοράς ελέγχου,
αυτό που διαβάσαμε θα είναι η επόμενη εντολή (next IR).
- Υπολογίζουμε την τιμή pc+4,
και την φυλάμε σ' έναν (νέο) καταχωρητή pcNxt.
Δεδομένου ότι η κεντρική ALU είναι απασχολημένη
με τον υπολογισμό του pc+Immx4,
χρειαζόμαστε έναν δεύτερο αθροιστή (ALU).
Σε περίπτωση που η παρούσα εντολή αποδειχθεί ότι δεν είναι
εντολή επιτυχημένης μεταφοράς ελέγχου,
αυτό που υπολογίσαμε θα είναι ο PC (συν 4) της επόμενης εντολής.
-
jump:
Στην κατάσταση αυτή
διαβάζουμε από M[jmpAddr] και φυλάμε αυτό που διαβάσαμε στον ir
(είναι η επόμενη εντολή).
Επίσης, υπολογίζουμε την τιμή jmpAddr+4
και την φυλάμε στον pc
(είναι η διεύθυνση της επόμενης εντολής συν 4).
-
branch:
Είτε προσθέτετε μιά νέα κατάσταση μετά από την κατάσταση αυτή,
και εκεί πηγαίνετε όταν η διακλάδωση επιτύχει
και κάνετε τα ανάλογα όπως και παραπάνω στην jump,
είτε μέσα στην κατάσταση branch
διαβάζετε από M[ALUout] (εντολή προορισμού)
και υπολογίζετε το ALUout+4,
και στο τέλος του κύκλου
φορτώνετε τον ir από M[ALUout] (επιτυχημένη διακλάδωση)
ή από irNxt (αποτυχημένη διακλάδωση),
και τον pc ανάλογα από ALUout+4 ή pcNxt.
-
Τελευταίος κύκλος των υπολοίπων εντολών:
επιπλέον του ό,τι κάνατε σε κάθε τέτοιον τελευταίο κύκλο,
φορτώνετε τον ir από τον irNxt και τον pc από τον pcNxt,
ώστε να τα βρεί έτοιμα η κατάσταση decode_rr της επόμενης εντολής.