-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathProcessor.cpp
More file actions
1480 lines (1237 loc) · 61.4 KB
/
Processor.cpp
File metadata and controls
1480 lines (1237 loc) · 61.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// CMSC 411 Project - Summer 2025
// File: Processor.cpp
// Desc: This class represents a processor
// Date: June 23, 2025
// Authors: Padina Nasiri Toussi and Qanita Baqibillah
#include <iostream>
#include <string>
#include <fstream>
#include <vector>
#include <map>
#include <vector>
#include <array>
#include <algorithm>
#include "Processor.h"
using namespace std;
Processor::Processor(){
// Constructor
m_instructions;
m_filename = "";
m_clock = 0;
for (int i = 0; i < 19; i++) {
m_memory[i] = DEFAULT_MEMORY[i];
}
}
Processor::Processor(string filename, string outputFile){
// Overloaded Constructor
m_instructions;
m_filename = filename;
m_outputFile = outputFile;
m_clock = 0;
for (int i = 0; i < 19; i++) {
m_memory[i] = DEFAULT_MEMORY[i];
}
}
Processor::~Processor(){
// Destructor
// todo: make sure to deallocate anything dynamically allocated such as
// // m_instructions
}
// PUBLIC HELPERS FOR TESTING
int Processor::getMemoryValue(int index) {
if (index >= 0 && index < 19) {
return m_memory[index];
}
return -1;
}
// note: commented these for now, as they are out of date and they cause errors (we are no longer using m_registers)
// void Processor::setRegisterValue(string reg, int value) {
// int reg_index = atoi(reg.substr(1).c_str());
// if (reg_index >= 0 && reg_index < 32) {
// m_registers[reg_index] = value;
// }
// }
// int Processor::getRegisterValue(string reg) {
// int reg_index = atoi(reg.substr(1).c_str());
// if (reg_index >= 0 && reg_index < 32) {
// return m_registers[reg_index];
// }
// return -1;
// }
// loads instructions from the given file name into m_instructions
// Preconditions: filename contains the load instructions
// Note: currently, only loads plaintext instructions
void Processor::loadInstructions(string filename){
// loop 1: find out the count of instructions (this is so we don't have to use vectors instead of arrays)
ifstream infile(filename);
string result;
string line;
int len = 0;
int count = 0; // used in loop 2
if (infile.is_open()) {
while(getline(infile, result)){
len++;
}
infile.close();
} else {
cout << "Error opening instructions file";
}
// Update the length of m_instructions
m_instructions_len = len;
const int const_len = len; // need a constant
string instructions[const_len]; // will be filled with empty strings
for (int i = 0; i < const_len; i++) {
instructions[i] = ""; // Explicitly setting each index to "" so compiler doesn't complain
}
m_instructions = instructions;
// loop 2: For each instruction in the file, create an object and load it into the m_instructions array
infile.open(filename);
if (infile.is_open()) {
while(getline(infile, line)){
// set the var result equal to the line but with the beginning and end whitespaces removed
// // create the corresponding Instruction object
// Instruction* newInstruction = new Instruction();
// newInstruction->setPlaintext(result); // plaintext
// add instruction into m_instructions
// m_instructions[count] = newInstruction;
result = trimExtraWhiteSpace(line);
m_instructions[count] = result;
cout << "result: " << result << endl;
count++;
}
infile.close();
cout << count << " instructions loaded." << endl;
} else {
cout << "Error opening instructions file";
}
}
// note: commented these for now, as they are out of date and they cause errors (we are no longer using m_registers)
void Processor::load(string reg_address, string mem_address){
// To convert mem_address to an actual index
int mem_index = memoryAddressToIndex(mem_address);
// Invalid mem index
if(mem_index < 0 || mem_index >= 19){
return;
}
// Extracting the register index
int reg_index = registerAddressToIndex(reg_address);
// Make sure the reg_index is valid between 0-32
if (reg_index < 0 || reg_index >= 32) {
return;
}
if(reg_address[0] == '$'){
m_registersInt[reg_index] = m_memory[mem_index];
}
else if(reg_address[0] == 'F'){
m_registersF[reg_index] = m_memory[mem_index];
}
else{
// Invalid prefix for register
return;
}
}
void Processor::store(string reg_address, string mem_address){
// Extracting the register index
int reg_index = registerAddressToIndex(reg_address);
// Invalid index
if(reg_index < 0 || reg_index >= 32){
return;
}
// Getting the value stored in the the register
int value = 0;
if(reg_address[0] == '$'){
value = m_registersInt[reg_index];
}
else if( reg_address[0] == 'F'){
value = m_registersF[reg_index];
}
else{
//Invalid
return;
}
// To convert it to an actual index
int mem_index = memoryAddressToIndex(mem_address);
// Make sure the index returned was valid and not -1 in case of an error
if (mem_index < 0 || mem_index >= 19) {
cout << "Invalid memory index returned by memoryAddressToIndex(): " << mem_index << endl;
return;
}
// To store the value into memory
m_memory[mem_index] = value;
}
int Processor::memoryAddressToIndex(string memAddress){
if(memAddress.empty()) return -1;
int openParen = -1;
int closeParen = -1;
// If the address is just a num precded by $
if (memAddress.length() > 1 && memAddress[0] == '$') {
int reg_index = atoi(memAddress.substr(1).c_str());
return reg_index % 19;
}
// Checking for parantheses
for (int i = 0; i < memAddress.length(); i++) {
if (memAddress[i] == '(') { // Looking for the opening parantheses
openParen = i;
} else if (memAddress[i] == ')') { // Looking for the closing parantheses
closeParen = i;
}
}
// Checking if there are opening and closing parantheses that are not empty
if (openParen != -1 && closeParen != -1 && closeParen > openParen + 1) {
// Taking out contents from the parantheses
string offsetStr = memAddress.substr(0, openParen);
string reg = memAddress.substr(openParen + 1, closeParen - openParen - 1);
int offset = 0;
try {
offset = stoi(offsetStr);
} catch (...) {
// Some invalid offset
return -1;
}
if (!reg.empty()) {
char prefix = reg[0];
int index = atoi(reg.substr(1).c_str());
if (prefix == '$' && index >= 0 && index < 32) {
return (offset + m_registersInt[index]) % 19;
} else if (prefix == 'F' && index >= 0 && index < 32) {
return (offset + m_registersF[index]) % 19;
} else if (isdigit(prefix)) {
// Something like "10(5)" will treat 5 as literal number
int regVal = atoi(reg.c_str());
return (offset + regVal) % 19;
}
}
}
// If it's just a num without preceding $ or ()
bool is_num = true;
for (char c: memAddress){
if(!isdigit(c)){
is_num = false;
break;
}
}
// If it's just a num without preceding $ or ()
if(is_num){
int index = atoi(memAddress.c_str());
return index % 19;
}
// Something went wrong
return -1;
}
// convert valid register address (from instructions) to a memory index between 0-31 inclusive
// Valid Example: register_address = "F24" ---> 24
// Invalid Example: register_address = "5" ---> -1
int Processor::registerAddressToIndex(string register_address){
// CHeck to make sure not empty
if(register_address.empty()) return -1;
// if the first letter isn't an F, return -1
char s1 = register_address[0]; // first character
string s2 = register_address.substr(1); // everything else
if (s1 == 'F' || s1 == '$'){
// assuming that if there is an F, the rest is a valid number --> the register index
return atoi(s2.c_str());
}
return -1;
}
// given a string with '(' and ')', returns the substring inside those brackets
// if unsuccessful, return ""
// precondition: text ends in the character ')'
// note: this is a helper function for these operands: offset(addr)
// Example: "100($4)" --> "$4"
string Processor::getSubstringInsideBrackets(string text){
// Check the precondition: must end with ')' and not be empty
if (text.empty() or text.back() != ')') {
return "";
}
// Find the position of the last closing ')'
int posClose = text.size() - 1; // index of the last char
// Find the first '('
int posOpen = text.find('(');
if (posOpen == string::npos) {
return ""; // no '(' found or nothing inside
}
// Get the substring between '(' and ')'
return text.substr(posOpen + 1, posClose - posOpen - 1); // I hope this is right
}
// given a string with '(', returns the substring before reaching those
// if unsuccessful, return ""
// precondition: text ends in the character '('
// note: this is a helper function for these operands: offset(addr)
// Example: "100($4)" --> "100"
string Processor::getSubstringBeforeBrackets(string text){
if (text.empty()) {
return "";
}
// Find the first '('
int posOpen = text.find('(');
if (posOpen == string::npos) {
return ""; // no '(' found or nothing inside
}
// Get the substring before '('
return text.substr(0, posOpen);
}
// given an int value and an addrLocation (), returns a string representation for the address
// Precondition: addrLocation is in ["MEMORY", "R_INT", "R_F"]
// Examples:
// (5, "MEMORY") --> "5"
// (5, "R_INT") --> "$5"
// (5, "R_F") --> "F5"
// unexpected: --> ""
string Processor::getIndexToAddressWithoutOffset(int value, string addrLocation){
if (addrLocation == "MEMORY"){
return to_string(value);
}
if (addrLocation == "R_INT"){
return ("$" + to_string(value));
}
if (addrLocation == "R_F"){
return ("F" + to_string(value));
}
return ""; // unexpected
}
// PIPELINING IMPLEMENTATION FUNCTIONS:
// KEY FUNCTION: startProcessor()
// This function would be called to start and run the simulated pipeline
// Precondition: loadInstructions() has been called to load the instructions
// Postcondition: when done, would also save the pipeline results into a new file
void Processor::startProcessor(){
/* How the processor works:
- Precondition: load instructions (should be done before calling startProcessor)
- start pipeline loop (main portion) - 1 iteration corresponds to 1 clock cycle:
- update forwarding related vectors for the new cycle
- look at every (alive) instruction already in the pipeline and have them progress by 1 step
- if any is stalling, everything after would also stall, and we'd skip the IF part
- otherwise, instruction fetch from the next line (if not empty line), and increment the m_instruction_pointer
- incremeny clock cycle by 1
- when the loop is done, and no active instruction is left in the pipeline, convert the pipeline into 2D array
- export the array into a text file
*/
// The Pipeline Loop
bool pipeline_active = true;
// reset some variables just in case
m_clock = 0;
m_instruction_pointer = 0;
m_first_alive_instruction_ptr = 0;
while(pipeline_active){
// Each Iteration is a cycle
bool stall_all_the_way_down = false; // when this is true, every instruction below stalls
// Clear m_availableNextCycle vectors (used to avoid RAW hazards) for the new cycle
m_availableNextCycleRead.clear();
m_availableNextCycleWrite.clear();
registerRAW_WriteTracker.clear();
registerRAW_ReadTracker.clear();
// NOTE: due to forwarding potentially not being fully implemented, I'm adding these so there is a chance the processor can run
// in reality, the 2 lines below shouldn't be here
m_heldUpRead.clear();
m_heldUpWrite.clear();
// TODO: deal with instructions already in m_pipeline
if (not m_pipeline.empty()){
// ADD CODE HERE LATER
bool deactivateRestOfBranch = false; // useful boolean for branch selection
// for each instruction in the pipeline...
for (int i = 0; i < m_pipeline.size(); i++){
Instruction currInst = m_pipeline[i]; // (hopefully)this should be a shallow copy
// check if stalling
// check if not breaking any rules/hazards for forwarding
// potentially update stall_all_the_way_down
// apply instruction if
// update the two forwarding queues
// end instruction if we are done
// update pointer if we had a branch prediction or branch decision
// TODO check for inactivity (add logic for that and check it before the stall stage)
// for the actual branch selection prediction/determination, that would happen later on
// todo Section Description: add a section + boolean that if a previous branch selection had been wrong, deactivate everything until loop's end
if (deactivateRestOfBranch and currInst.getIsAlive()){
currInst.deActivate(m_clock);
// also update all the vectors so it doesn't cause issues (update based on the previous stage and prevRelevant stage)
// not sure if it would help with keeping the vectors straight, if we move this section to slightly later??? thought prob not
// vectors // TODO
}
if (currInst.getIsAlive()){ // skip over anything that is inactive or dead
// STALL - we are stalling
if (stall_all_the_way_down){
// set instruction to stall - note: we do not need to modify the forwarding queues in this version
currInst.pushToStageLog(STALL_NAME);
// SOME EXTRA STEPS DEPENDING ON THE PREVIOUS RELEVANT (non-stall) STAGE:
string prevRelevantStage = currInst.getLatestNonStallStageLog();
// IF - none
if (prevRelevantStage == "IF"){
// nothing here
}
// ID - update the operator values again
else if (prevRelevantStage == "ID"){
instructionDecode(currInst);
// Section Description: update the forwarding vectors based on the instruction type and operators
// Note: this section is based on the one used during the ID stage, but slightly different
// update forwarding vectors (note: since we are stalling, don't modify m_heldUpRead and m_heldUpWrite, ONLY availableNextCycle)
// Basically, we are just refilling the forwarding vectors that get automatically cleared at every cycle
string type = currInst.getType();
string category = currInst.getCategory();
string dest = currInst.getDest();
string s1 = currInst.getS1();
string s2 = currInst.getS2();
// CONTROL
if (type == "J"){
// skip for now - dest is a label, s1 and s2 are nonexistant
// no need for any forwarding
} else if (category == INSTRUCTION_CATEGORIES[2]) {
// rest of "CONTROL" category - ie: BEQ or BNE
// dest and s1 ARE BOTH READ FROM (not written to), s2 is a label
m_availableNextCycleRead.push_back(s1);
m_availableNextCycleRead.push_back(dest);
}
// ALU
else if (type == "ADDI"){
// add immediate - dest and s1 are normal registers, s2 is a immediate
m_availableNextCycleRead.push_back(s1);
} else if (category == INSTRUCTION_CATEGORIES[0]){
// rest of the ALU categories - dest, s1, s2 are normal registers
// update forwarding for all three
m_availableNextCycleRead.push_back(s1);
m_availableNextCycleRead.push_back(s2);
}
// MEMORY
// L.D Fa, Offset(addr)
// S.D Fa, Offset(addr)
// LI $d, IMM64 -Integer Immediate Load
// LW $d, Offset(addr)
// SW $s, Offset(addr)
// A memory instruction has dest and s1 only
// for LI, only dest would go to heldUp
// for loads, dest and (if applicable) s1 would go to heldUp
// for stores, dest would go to heldUp, s1 would go to availableNextCycle
else {
// MEMORY
// we don't need to do anything, unless it's a store instruction
if(type == "S.D" or type == "SW"){ // every other load
m_availableNextCycleRead.push_back(s1);
}
}
}
// Other stages: EX, MEM, WB - also none
else {
// nothing here // todo: potentially add more stuff to this later if needed
}
}
// VALID STAGE - take a valid stage between ID-WB (if allowed through forwarding)
else {
string prevStage = currInst.getLatestStageLog();
string expectedStage = currInst.getNextExpectedStageLog(prevStage);
// ID - load operands, update forwarding(most cases), update mem-specific forwarding
if (expectedStage == DEFAULT_PIPELINE_STAGES[1]){
// call ID stage (load all operands)
instructionDecode(currInst);
currInst.pushToStageLog("ID");
// IGNORE:
// todo: check whether everything is available or if we should stall
// update forwarding (register mem-specific forwarding) (only if MEMORY instruction, and reading from register memory which was written to on this cycle)
// Note: only do this section if we can successfully continue
// update forwarding (heldUp R + W)
// update forwarding (availableNextCycle R)
// update forwarding (register mem-specific forwarding) - (Note: This will now be done at the beginning of EX and end of MEM)
// update the forwarding vectors based on the instruction type and operators
string type = currInst.getType();
string category = currInst.getCategory();
string dest = currInst.getDest();
string s1 = currInst.getS1();
string s2 = currInst.getS2();
// CONTROL
if (type == "J"){
// skip for now - dest is a label, s1 and s2 are nonexistant
// no need for any forwarding
} else if (category == INSTRUCTION_CATEGORIES[2]) {
// rest of "CONTROL" category - ie: BEQ or BNE
// dest and s1 ARE BOTH READ FROM (not written to), s2 is a label
// update forwarding for dest and s1 only
// m_heldUpRead.push_back(s1); // NOPE
m_availableNextCycleRead.push_back(s1);
m_availableNextCycleRead.push_back(dest);
}
// ALU
else if (type == "ADDI"){
// add immediate - dest and s1 are normal registers, s2 is a immediate
// update forwarding for dest and s1 only
m_availableNextCycleRead.push_back(s1);
m_heldUpWrite.push_back(dest);
} else if (category == INSTRUCTION_CATEGORIES[0]){
// rest of the ALU categories - dest, s1, s2 are normal registers
// update forwarding for all three
m_availableNextCycleRead.push_back(s1);
m_availableNextCycleRead.push_back(s2);
m_heldUpWrite.push_back(dest);
}
// MEMORY
// L.D Fa, Offset(addr)
// S.D Fa, Offset(addr)
// LI $d, IMM64 -Integer Immediate Load
// LW $d, Offset(addr)
// SW $s, Offset(addr)
// A memory instruction has dest and s1 only
// for LI, only dest would go to heldUp
// for loads, dest and (if applicable) s1 would go to heldUp
// for stores, dest would go to heldUp, s1 would go to availableNextCycle
else {
// MEMORY
if (type == "LI"){ // Load Immediate
m_heldUpWrite.push_back(dest);
} else if(type == "L.D" or type == "LW"){ // every other load
m_heldUpRead.push_back(s1);
m_heldUpWrite.push_back(dest);
} else { // stores
m_availableNextCycleRead.push_back(s1);
m_heldUpWrite.push_back(dest);
}
}
} else if (expectedStage == DEFAULT_PIPELINE_STAGES[2] or (expectedStage[0]+"1") == DOUBLE_ADD_EXECUTE_PREFIX
or (expectedStage[0]+"1") == DOUBLE_MULT_EXECUTE_PREFIX or (expectedStage[0]+"1") == DOUBLE_DIV_EXECUTE_PREFIX){
// It's the FIRST execute stage! A lot to break down
string type = currInst.getType();
string category = currInst.getCategory();
string unit = currInst.getUnit();
string dest = currInst.getDest();
string s1 = currInst.getS1();
string s2 = currInst.getS2();
// STOP: check if we have to end up stalling all the way down
// if so, skip the rest of this section's code
// note: todo use isExecuteAllowed function
bool executeIsAllowed = isExecuteAllowed(currInst);
// No: We are NOT completing the first EX stage on this cycle
if (!executeIsAllowed){
currInst.pushToStageLog(STALL_NAME); // Push "STALL" to the instruction's stage log
stall_all_the_way_down = true; // stall ripples down
}
// Yes: We are completing the first EX stage on this cycle
else {
// Push the correct EX stage name to the instruction's stage log
currInst.pushToStageLogDefault();
// Cases to deal with
// CONTROL EXECUTE - branch determination + ending the instruction + potentially deactivating later remaining instructions in the loop this round
// MEMORY EXECUTE - the only execute stage - mostly nothing, although there might be some forwarding?
// ALU Integer EXECUTE - the only execute stage
// ALU FP EXECUTE - the first execute stage
// CONTROL EXECUTE
if (category == "CONTROL"){
// First: update forwarding (for the registers we will read from) - they may now be accessed after this cycle
updateForwardingForEX1(currInst);
// make branch determination
int determination = getBranchActual(currInst);
int prediction = currInst.getBranchingInt("m_predicted_taken");
// If prediction was wrong
if (determination != prediction){
m_bp.updatePrediction(); // let the predictor know that it was wrong
// update m_instruction_pointer to what it should have been
if (determination == 1){
// determination is taken
m_instruction_pointer = currInst.getBranchingInt("m_ptr_to_taken");
} else if (determination == 0) {
// determination is not taken
m_instruction_pointer = currInst.getBranchingInt("m_ptr_to_not_taken");
}
}
// end the instruction
currInst.endInstruction(m_clock);
// If prediction was wrong: deactivate any instruction in the pipeline right now after currInst
if (determination != prediction){
deactivateRestOfBranch = true;
}
}
// MEMORY EXECUTE - single stage
else if (category == "MEMORY"){
updateForwardingForEX1(currInst);
// NOTE: MEMORY SPECIFIC (register to register) FORWARDING WOULD GO HERE (todo)
// if it is a load instruction:
// //if dest not in registerRAW_WriteTracker or registerRAW_ReadTracker: add it to registerRAW_WriteTracker
}
// ALU FP EXECUTE
else if (expectedStage == DOUBLE_MULT_EXECUTE_PREFIX){
updateForwardingForEX1(currInst);
}
// ALU INT EXECUTE
else {
// This is the first and final EX stage
// calculate result
int result = 0;
if (type == "ADD" or type == "ADDI"){
// add or add immediate
result = currInst.getS1Val() + currInst.getS2Val();
} else if (type == "SUB"){
result = currInst.getS1Val() - currInst.getS2Val();
}
// enable forwarding next cycle
updateForwardingForEX1(currInst);
}
}
} else if ((expectedStage[0]+"2") == DOUBLE_ADD_EXECUTE_PREFIX or (expectedStage[0]+"10") == DOUBLE_MULT_EXECUTE_PREFIX
or (expectedStage[0]+"40") == DOUBLE_DIV_EXECUTE_PREFIX) {
// THE FINAL EX STAGE STAGE FOR FP ALU INSTRUCTIONS
// add to stage log
// Push the correct EX stage name to the instruction's stage log
currInst.pushToStageLogDefault();
// update forwarding
updateForwardingForEXf(currInst);
// save the result
int result = 0;
string type = currInst.getType();
if (type == "ADD.D"){
result = currInst.getS1Val() + currInst.getS2Val();
} else if (type == "SUB.D"){
result = currInst.getS1Val() - currInst.getS2Val();
} else if (type == "MUL.D"){
result = currInst.getS1Val() * currInst.getS2Val();
} else if (type == "DIV.D"){
result = currInst.getS1Val() / currInst.getS2Val();
}
currInst.setResult(result);
} else if (expectedStage[0] == DOUBLE_ADD_EXECUTE_PREFIX[0] or expectedStage[0] == DOUBLE_MULT_EXECUTE_PREFIX[0] or expectedStage[0] == DOUBLE_DIV_EXECUTE_PREFIX[0]){
// DO NOTHING IN A MIDWAY EXECUTE STAGE FOR FP ALU
// only add the stage to stage log
currInst.pushToStageLogDefault();
} else if (expectedStage == DEFAULT_PIPELINE_STAGES[3]){
// The memory ("MEM") stage!
// we are guaranteed to have either a MEMORY or a ALU instruction
string category = currInst.getCategory();
string type = currInst.getType();
// note: isMemAllowed() is basically checking there isn't any forwarding conflicts or hazards for load/store operations
if (category == "ALU" or isMemAllowed(currInst)){
// we can go ahead with the MEM stage
// log the stage
currInst.pushToStageLogDefault();
if (category == "MEMORY"){
// if it's a memory instruction, perform load or store
string dest = currInst.getDest(); // register
string s1 = currInst.getS1(); // memory address or immediate
int destVal = currInst.getDestVal();
int s1Val = currInst.getS1Val();
if (type == "L.D" or type == "LW" or type == "LI"){
// load
load(dest, s1);
} else {
// store
store(dest, s1);
}
// then, update the forwarding checks
updateForwardingForMEM(currInst);
}
} else {
// STALL
currInst.pushToStageLog(STALL_NAME);
stall_all_the_way_down = true; // stall ripples down
}
} else if (expectedStage == DEFAULT_PIPELINE_STAGES[4]){
// Finally, the Writeback Stage
// check if it is possible, if not stall
if (not isWBAllowed(currInst)){
// STALL
currInst.pushToStageLog(STALL_NAME);
stall_all_the_way_down = true; // stall ripples down
} else {
// log
currInst.pushToStageLogDefault();
// update forwarding if needed
updateForwardingForWB(currInst);
// save results for ALU instructions (into dest register)
string type = currInst.getType();
string category = currInst.getCategory();
string unit = currInst.getUnit();
string addr = currInst.getDest();
int result = currInst.getResult();
if (category == "ALU" and unit == "INT"){
// Int register
m_registersInt[registerAddressToIndex(addr)] = result;
} else if (category == "ALU"){
// FP register
m_registersF[registerAddressToIndex(addr)] = result;
}
// end the instruction
currInst.endInstruction(m_clock);
}
}
}
}
}
}
bool deactivateRestOfBranch = false; // reset this boolean for branch selection so it is false again
// fetch the next instruction if we are not stalling AND there are more instructions left to fetch
if (!stall_all_the_way_down and (m_instruction_pointer >= m_instructions_len)){
Instruction newInst = instructionFetch();
// update the stage log in newInst
newInst.pushToStageLog("IF"); // pushes "IF" to the back of the stage log
// add newInst to the end of the pipeline
m_pipeline.push_back(newInst);
// NEW SECTION: BRACNH PREDICTION
if (newInst.getType() == "CONTROL"){
// make prediction using m_bp
int prediction = getBranchPrediction(newInst);
// set the instruction_pointer to the address we predicted it would take
if (prediction){
m_instruction_pointer = newInst.getBranchingInt("m_ptr_to_taken");
} else {
m_instruction_pointer += 1;
}
} else {
// update the pointer as normal
m_instruction_pointer += 1;
}
}
// update some pipeline-related variable to update the cycle
m_clock += 1; // clock cycle
// EXIT CONDITIONS: check if we are done -
// ie: if every instruction in pipeline is dead, and there is no instructions left to fetch
if (m_instruction_pointer >= m_instructions_len){
if (m_pipeline.empty()){
pipeline_active = false;
} else {
// check every pipeline instruction to see that it's not alive
// using a lambda expression, none_of(), and getIsAlive()
if (none_of(m_pipeline.begin(), m_pipeline.end(),
[](Instruction &x) { return x.getIsAlive(); })){
pipeline_active = false;
}
}
}
}
// We have exited the while loop
// Step: convert the pipeline into 2D array
// Step: export the array into a text file
// We are done!
}
// fetches the next instruction from m_instructions (corresponds to the IF stage)
// creates and returns instruction object
// new: also sets m_ptr_to_taken and m_ptr_to_not_taken in "CONTROL" instructions
Instruction Processor::instructionFetch(){
// Loads instruction plaintext (if any exist at address)
// Creates instruction class and loads relevant attributes (ignore any labels at the beginning)
// NOTE: things this function no longer does:
// Updates instruction pointer (this is currently just pointer +1)
// // part 0 - would be done before the function was called
// if (m_instruction_pointer >= m_instructions_len){
// // pointer out of range - we've reached the end of the program
// return;
// }
// part 1
string plaintext = m_instructions[m_instruction_pointer];
// part 1.5 remove label from beginning if any
char labelSignifier = ':';
int posLabelSignifier = plaintext.find(labelSignifier); // position of the ':' character
if (posLabelSignifier != string::npos) {
// There IS a label --> remove it
// string labelName = plaintext.substr(0, posLabelSignifier); // doesn't include ':' // code is here for reference
string theRest = plaintext.substr(posLabelSignifier + 1);
plaintext = trimExtraWhiteSpace(theRest); // update the plaintext
}
// part 2 - creating an instruction object
Instruction myInst(plaintext, m_clock);
// detrmine type, dest, source1, source2
// Some Valid Examples:
// "ADD.D F2,F0,F1"
// "S.D F2,0($2)
// "LI $7,0"
// "J Loop1"
// "BNE $1,$2,Loop1"
// find first word until space --> type
int space_pos = plaintext.find(' '); // position of the first space
string text1 = plaintext.substr(0, space_pos); // first chunk (before the space) --> the instruction type!
string text2 = plaintext.substr(space_pos + 1); // The rest of the string (after the space)
myInst.setType(text1);
// NEW SECTION: WE'LL DO THE CODE SEPARATELY FOR A J INSTRUCTION (only has dest)
if (text1 == "J"){
// s1 and s2 are ""
text2 = trimExtraWhiteSpace(text2);
myInst.setDest(text2);
myInst.setS1("");
myInst.setS2("");
return myInst; // early return
}
// split string by commas: dest, source1, source2(optional)
string text_a, text_b, text_c;
int first_comma = plaintext.find(',');
int second_comma = plaintext.find(',', first_comma + 1);
if (second_comma == -1){ // note: npos should be converted into -1 since we saved second_comma as int
// there is only 1 comma
text_a = plaintext.substr(0, first_comma);
text_b = plaintext.substr(first_comma + 1, second_comma - first_comma - 1);
text_c = plaintext.substr(second_comma + 1);
} else {
// there are 2 commas
text_a = plaintext.substr(0, first_comma);
text_b = plaintext.substr(first_comma + 1);
text_c = "";
}
text_a = trimExtraWhiteSpace(text_a);
text_b = trimExtraWhiteSpace(text_b);
text_c = trimExtraWhiteSpace(text_c); // check this doesn't cause an error
myInst.setDest(text_a);
myInst.setS1(text_b);
myInst.setS2(text_c);
// NOTE: parts 3 and 4 are no longer done inside the function
// // part 3 - update instruction pointer (TODO: add branch prediction)
// if (1){
// // default case
// m_instruction_pointer += 1;
// } else {
// // Use Branch Prediction To Figure Where to Point Next?
// // todo: add a member attr to keep track of prev pointers if prediction is wrong
// }
// // part 4
// m_pipeline.push_back(myInst); // Note: this should now be done in startProcessor
// New Section: If it is a control instruction, also add m_ptr_to_taken and m_ptr_to_not_taken
if(myInst.getCategory() == "CONTROL"){
int posTaken;
int posNotTaken;
// posTaken is the position of the label at dest
posTaken = findLabelIndex(myInst.getDest());
// posNotTaken is the currentptr + 1 (ie: the next line as the branch instruction)
posNotTaken = m_instruction_pointer + 1;
myInst.setBranchingInt("m_ptr_to_taken", posTaken);
myInst.setBranchingInt("m_ptr_to_not_taken", posNotTaken);
}
return myInst;
}
// NEW (not yet implemented in Processor.cpp)
// decode a given instruction object and load the values of all delivarables (corresponds to the ID stage)
// What it doesn't do now: also check if we should stall (ie repeat this stage)
// What it doesn't do: push "ID to stage log"
// would interpret its dependencies, and other relevant details not already known
// if we have a memory function, replace offset(addr) with string(int(offset + addr))
// // Example: m_s1 = "10($5)" ----> m_s1 = "$15"
// // Example: m_s1 = "32($1)" ----> m_s1 = "$1"
void Processor::instructionDecode(Instruction &inst){
// call getOperandVals to find operand values
getOperandVals(inst);
// in case of mem functions where an operand name ends in brackets
// for S1
if (inst.getS1().back() == ')'){
int value = inst.getS1Val();
int intLoc = inst.getS1().find('$'); // location of '$' sign
int fLoc = inst.getS1().find('F'); // location of 'F'
// if both are -1, we can assume it's a memory address
if (intLoc != -1){
// int register $
int num = value % 32;
inst.setS1(getIndexToAddressWithoutOffset(num, "R_INT"));
} else if (fLoc != -1){
// float register F
int num = value % 32;
inst.setS1(getIndexToAddressWithoutOffset(num, "R_F"));
} else {
// mem address
int num = value % 19;
inst.setS1(getIndexToAddressWithoutOffset(num, "MEMORY"));
}
}
// for S2
if (inst.getS2().back() == ')'){
int value = inst.getS2Val();
int intLoc = inst.getS2().find('$'); // location of '$' sign
int fLoc = inst.getS2().find('F'); // location of 'F'
// if both are -1, we can assume it's a memory address
if (intLoc != -1){
// int register $
int num = value % 32;
inst.setS2(getIndexToAddressWithoutOffset(num, "R_INT"));
} else if (fLoc != -1){
// float register F
int num = value % 32;