├── .gitignore ├── CMakeLists.txt ├── README.md ├── inc ├── Gselect.hpp ├── Tomasulo.hpp └── types.hpp ├── src ├── Gselect.cpp ├── Tomasulo.cpp └── procsim.cpp └── traces ├── gcc.100k.trace ├── gcc_branch.100k.trace ├── gobmk.100k.trace ├── gobmk_branch.100k.trace ├── hmmer.100k.trace ├── hmmer_branch.100k.trace ├── mcf.100k.trace └── mcf_branch.100k.trace /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | procsim 34 | 35 | # VS Code 36 | .vscode 37 | 38 | # Mac archiving files 39 | .DS_Store 40 | 41 | # CMake 42 | CMakeCache.txt 43 | CMakeFiles 44 | CMakeScripts 45 | Testing 46 | Makefile 47 | cmake_install.cmake 48 | install_manifest.txt 49 | compile_commands.json 50 | CTestTestfile.cmake 51 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0) 2 | 3 | # Target C++14 4 | set (CMAKE_CXX_STANDARD 14) 5 | 6 | # Make sure warning flags are always set 7 | set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wall") 8 | set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -Wall") 9 | 10 | # Bring in the headers 11 | include_directories(inc) 12 | 13 | # Project Name 14 | project(TomasuloPipeline) 15 | 16 | # Add all the files from the directory 17 | file(GLOB SRCS src/*.cxx src/*.cpp src/*.cc) 18 | 19 | add_executable(procsim ${SRCS}) 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tomasulo Simulation 2 | 3 | ## Introduction 4 | This project is an educational simulation of the [Tomasulo algorithm](https://en.wikipedia.org/wiki/Tomasulo_algorithm), a hardware algorithm for out-of-order scheduling and execution of computer instructions, written in C++. 5 | 6 | ## Implementation 7 | 8 | Below are some of the key concepts of the simulation's algorithm: 9 | 10 | * **Reorder Buffer:** simulates branching resolution using a reorder buffer 11 | * **Register Renaming:** a technique to minimize data hazards 12 | * **Result Buses:** unlike the original Tomasulo algorithm, which contains a single *Common Data Bus (CDB)*, there can be multiple result buses to update the state of the pipeline. 13 | * **Reservation Station Scheduling:** units of scheduling managing an instruction fired to a *Function Unit* 14 | * **Specialized Function Units:** specialized hardware units specific to different types of instructions 15 | * **Gselect Branch Prediction:** a global branch prediction scheme that keeps a pattern history table and concatenates the global branch history -------------------------------------------------------------------------------- /inc/Gselect.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | using namespace std; 7 | 8 | namespace pipeline { 9 | class Gselect { 10 | public: 11 | Gselect(unsigned int ghr_bits, uintmax_t ghr_init_val, unsigned int pred_bits, uintmax_t pred_init_val, size_t num_preds); 12 | bool predict(uint_least64_t hash); 13 | void update(uint_least64_t hash, bool taken); 14 | 15 | private: 16 | unsigned int ghr_bits_; 17 | uintmax_t ghr_val_; 18 | unsigned int pred_bits_; 19 | vector> pred_vals_; 20 | }; 21 | } -------------------------------------------------------------------------------- /inc/Tomasulo.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "types.hpp" 9 | #include "Gselect.hpp" 10 | 11 | using namespace std; 12 | 13 | namespace pipeline { 14 | class Tomasulo { 15 | public: 16 | Tomasulo(ProcessorSettings settings); 17 | ProcessorSettings getSettings() const; 18 | Statistics getStatistics() const; 19 | void reset(); 20 | void run(deque>& instruction_q); 21 | 22 | private: 23 | typedef intmax_t tag_t; 24 | 25 | typedef struct ReservationStation { 26 | bool fired; 27 | 28 | tag_t target_tag; 29 | vector source_tags; 30 | vector sources_ready; 31 | 32 | shared_ptr instr; 33 | } ReservationStation; 34 | 35 | typedef struct FunctionUnit { 36 | uint_fast32_t latency; 37 | clock_cycle_t enter_cycle; 38 | 39 | shared_ptr station; 40 | 41 | bool operator <(const FunctionUnit& rhs) const { 42 | return tie(enter_cycle, station->target_tag) < tie(rhs.enter_cycle, rhs.station->target_tag); 43 | } 44 | 45 | bool operator >(const FunctionUnit& rhs) const { 46 | return tie(enter_cycle, station->target_tag) > tie(rhs.enter_cycle, rhs.station->target_tag); 47 | } 48 | 49 | bool operator ==(const FunctionUnit& rhs) const { 50 | return enter_cycle == rhs.enter_cycle && station == rhs.station; 51 | } 52 | } FunctionUnit; 53 | 54 | typedef struct ResultBus { 55 | regno_t reg; 56 | tag_t tag; 57 | 58 | shared_ptr station; 59 | 60 | bool operator <(const ResultBus& rhs) const { 61 | return tag < rhs.tag; 62 | } 63 | 64 | bool operator >(const ResultBus& rhs) const { 65 | return tag > rhs.tag; 66 | } 67 | 68 | bool operator ==(const ResultBus& rhs) const { 69 | return tag == rhs.tag && reg == rhs.reg; 70 | } 71 | } ResultBus; 72 | 73 | typedef struct Register { 74 | tag_t tag; 75 | } Register; 76 | 77 | ProcessorSettings settings_; 78 | Statistics stats_; 79 | Gselect branch_pred_; 80 | 81 | vector register_file; 82 | deque> fetch_q_; 83 | deque> dispatch_q_; 84 | vector> schedule_q_; 85 | vector> function_units_; 86 | vector busy_result_buses_; 87 | vector retire_buffer_; 88 | 89 | size_t schedule_q_limit_; 90 | clock_cycle_t current_clock_; 91 | tag_t current_tag_; 92 | shared_ptr bad_branch_instr_; 93 | 94 | uint_least64_t hashAddress(uint_least64_t addr); 95 | void fetchInstructions(deque>& instruction_q, deque>& result_q); 96 | bool isPipelineEmpty() const; 97 | void dispatchInstructions(); 98 | void scheduleInstructions(); 99 | void fireInstructions(); 100 | void retireInstructions(); 101 | void updateState(); 102 | void sweepRetirementBuffer(); 103 | tag_t getNewTag(); 104 | }; 105 | } -------------------------------------------------------------------------------- /inc/types.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | using namespace std; 7 | 8 | typedef int_least32_t regno_t; 9 | typedef uintmax_t clock_cycle_t; 10 | 11 | typedef struct ProcessorSettings { 12 | uintmax_t result_bus_count; 13 | uintmax_t fetch_rate; 14 | 15 | vector function_units_count; 16 | vector function_units_latency; 17 | 18 | uintmax_t register_count; 19 | 20 | unsigned int ghr_bits; 21 | uintmax_t ghr_init_val; 22 | unsigned int predictor_bits; 23 | uintmax_t predictor_init_val; 24 | size_t predictor_table_size; 25 | } ProcessorSettings; 26 | 27 | typedef struct InstructionLife { 28 | clock_cycle_t fetch_cycle; 29 | clock_cycle_t dispatch_cycle; 30 | clock_cycle_t schedule_cycle; 31 | clock_cycle_t execute_cycle; 32 | clock_cycle_t state_update_cycle; 33 | } InstructionLife; 34 | 35 | typedef struct Instruction { 36 | uintmax_t number; 37 | 38 | uint_least64_t address; 39 | int_least16_t func_type; 40 | 41 | regno_t dst_reg; 42 | vector src_regs; 43 | 44 | uint_least64_t branch_address; 45 | bool branch_taken; 46 | bool is_branch; 47 | 48 | shared_ptr life; 49 | } Instruction; 50 | 51 | typedef struct Statistics { 52 | uintmax_t instructions; 53 | clock_cycle_t clock_cycles; 54 | 55 | uintmax_t instr_dispatched; 56 | uintmax_t instr_scheduled; 57 | uintmax_t instr_fired; 58 | uintmax_t instr_executed; 59 | uintmax_t instr_retired; 60 | 61 | uintmax_t peak_dispatch_size; 62 | uintmax_t dispatch_size_sum; 63 | 64 | uintmax_t branches; 65 | uintmax_t correct_branches; 66 | 67 | } Statistics; -------------------------------------------------------------------------------- /src/Gselect.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | namespace pipeline { 5 | 6 | Gselect::Gselect(unsigned int ghr_bits, uintmax_t ghr_init_val, unsigned int pred_bits, uintmax_t pred_init_val, size_t pred_table_size) { 7 | ghr_bits_ = ghr_bits; 8 | ghr_val_ = ghr_init_val & ((1 << ghr_bits_) - 1); 9 | pred_bits_ = pred_bits; 10 | 11 | // Initialize all of the prediction values 12 | pred_vals_.resize(pred_table_size); 13 | for (size_t i = 0; i < pred_table_size; ++i) { 14 | size_t num_preds = 1u << ghr_bits_; 15 | 16 | pred_vals_[i].resize(num_preds); 17 | 18 | for (size_t j = 0; j < num_preds; ++j) 19 | pred_vals_[i][j] = pred_init_val & ((1 << pred_bits) - 1); 20 | } 21 | } 22 | 23 | bool Gselect::predict(uint_least64_t hash) { 24 | return pred_vals_[hash % pred_vals_.size()][ghr_val_] >= (UINTMAX_C(1) << (pred_bits_ - 1)); 25 | } 26 | 27 | void Gselect::update(uint_least64_t hash, bool taken) { 28 | size_t index = hash % pred_vals_.size(); 29 | if (taken && pred_vals_[index][ghr_val_] < ((UINTMAX_C(1) << pred_bits_) - 1)) 30 | ++pred_vals_[index][ghr_val_]; 31 | else if (!taken && pred_vals_[index][ghr_val_] > 0) 32 | --pred_vals_[index][ghr_val_]; 33 | 34 | ghr_val_ = ((ghr_val_ << 1) | taken) & ((1 << ghr_bits_) - 1); 35 | } 36 | } -------------------------------------------------------------------------------- /src/Tomasulo.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "Tomasulo.hpp" 6 | 7 | 8 | namespace pipeline { 9 | Tomasulo::Tomasulo(ProcessorSettings settings) : 10 | branch_pred_(settings.ghr_bits, settings.ghr_init_val, settings.predictor_bits, settings.predictor_init_val, settings.predictor_table_size) 11 | { 12 | settings_ = settings; 13 | 14 | // Calculate the size of schedule queue 15 | schedule_q_limit_ = 0; 16 | for (auto count : settings_.function_units_count) 17 | schedule_q_limit_ += count; 18 | schedule_q_limit_ *= 2; 19 | 20 | register_file.resize(settings_.register_count); 21 | function_units_.resize(settings_.function_units_count.size()); 22 | 23 | reset(); 24 | } 25 | 26 | ProcessorSettings Tomasulo::getSettings() const { 27 | return settings_; 28 | } 29 | 30 | Statistics Tomasulo::getStatistics() const { 31 | return stats_; 32 | } 33 | 34 | void Tomasulo::reset() { 35 | stats_ = {}; 36 | current_clock_ = 0; 37 | current_tag_ = 1; 38 | 39 | for (auto& reg : register_file) 40 | reg.tag = -1; 41 | 42 | 43 | for (auto& units : function_units_) 44 | units.clear(); 45 | } 46 | 47 | 48 | uint_least64_t Tomasulo::hashAddress(uint_least64_t addr) { 49 | return addr >> 2; 50 | } 51 | 52 | 53 | void Tomasulo::run(deque>& instruction_q) { 54 | deque> result_q; 55 | 56 | while (!instruction_q.empty() || !isPipelineEmpty()) { 57 | ++current_clock_; 58 | 59 | // ~~ 1ST HALF ~~ 60 | updateState(); 61 | 62 | busy_result_buses_.swap(retire_buffer_); 63 | 64 | retireInstructions(); 65 | 66 | fireInstructions(); 67 | 68 | scheduleInstructions(); 69 | 70 | dispatchInstructions(); 71 | 72 | fetchInstructions(instruction_q, result_q); 73 | 74 | 75 | sweepRetirementBuffer(); 76 | 77 | // STATS: update total clock cycles 78 | ++stats_.clock_cycles; 79 | } 80 | 81 | // Fix off-by-one for last instruction in the pipeline 82 | --current_clock_; 83 | --stats_.clock_cycles; 84 | 85 | // Swap the results into the parameter passed in 86 | instruction_q.swap(result_q); 87 | 88 | // Clean up some memory usage 89 | schedule_q_.shrink_to_fit(); 90 | busy_result_buses_.shrink_to_fit(); 91 | result_q.shrink_to_fit(); 92 | } 93 | 94 | bool Tomasulo::isPipelineEmpty() const { 95 | return fetch_q_.empty() && dispatch_q_.empty() && schedule_q_.empty() && busy_result_buses_.empty(); 96 | } 97 | 98 | void Tomasulo::fetchInstructions(deque>& instruction_q, deque>& result_q) { 99 | for (size_t i = 0; i < settings_.fetch_rate && !instruction_q.empty(); ++i) { 100 | // Grab instruction from the front of the queue 101 | auto instr = instruction_q.front(); 102 | instruction_q.pop_front(); 103 | 104 | // Make a new life for the instruction 105 | instr->life = make_shared(); 106 | 107 | // Record when the instruction gets to fetch 108 | instr->life->fetch_cycle = current_clock_; 109 | 110 | fetch_q_.push_back(instr); 111 | result_q.push_back(instr); 112 | 113 | // STATS: instruction total 114 | ++stats_.instructions; 115 | } 116 | } 117 | 118 | void Tomasulo::dispatchInstructions() { 119 | for (size_t i = 0; i < settings_.fetch_rate && bad_branch_instr_ == nullptr && !fetch_q_.empty(); ++i) { 120 | shared_ptr instr = fetch_q_.front(); 121 | fetch_q_.pop_front(); 122 | 123 | instr->life->dispatch_cycle = current_clock_; 124 | 125 | // Stall dispatch stage 126 | if (instr->is_branch) { 127 | // STATS: update branch prediction 128 | ++stats_.branches; 129 | 130 | if (instr->branch_taken != branch_pred_.predict(hashAddress(instr->address))) { 131 | bad_branch_instr_ = instr; 132 | } 133 | else { 134 | // STATS: update number of correct branches 135 | ++stats_.correct_branches; 136 | } 137 | } 138 | 139 | 140 | dispatch_q_.push_back(instr); 141 | 142 | // STATS: dispatch total 143 | ++stats_.instr_dispatched; 144 | } 145 | 146 | // STATS: dispatch size 147 | uintmax_t dispatch_size = (uintmax_t)dispatch_q_.size(); 148 | stats_.peak_dispatch_size = max(stats_.peak_dispatch_size, dispatch_size); 149 | stats_.dispatch_size_sum += dispatch_size; 150 | } 151 | 152 | void Tomasulo::scheduleInstructions() { 153 | while (schedule_q_.size() < schedule_q_limit_ && !dispatch_q_.empty()) { 154 | shared_ptr instr = dispatch_q_.front(); 155 | dispatch_q_.pop_front(); 156 | 157 | // Mark down the schedule time 158 | instr->life->schedule_cycle = current_clock_; 159 | 160 | auto station = make_shared(); 161 | station->instr = instr; 162 | station->fired = false; 163 | 164 | for (auto const& src_reg : instr->src_regs) { 165 | if (src_reg < 0 || register_file[src_reg].tag < 0) { 166 | // Register file is updated and can be read directly 167 | station->source_tags.push_back(-1); 168 | station->sources_ready.push_back(true); 169 | } 170 | else { 171 | // Need to await instruction to publish a result 172 | station->source_tags.push_back(register_file[src_reg].tag); 173 | station->sources_ready.push_back(false); 174 | } 175 | } 176 | 177 | // Set destination tag 178 | tag_t unique_tag = getNewTag(); 179 | station->target_tag = unique_tag; 180 | if (instr->dst_reg >= 0) { 181 | register_file[instr->dst_reg].tag = unique_tag; 182 | } 183 | 184 | // Add to the scheduling queue 185 | schedule_q_.push_back(station); 186 | 187 | 188 | // STATS: scheduled total 189 | ++stats_.instr_scheduled; 190 | } 191 | } 192 | 193 | void Tomasulo::fireInstructions() { 194 | for (auto station : schedule_q_) { 195 | shared_ptr instr = station->instr; 196 | 197 | // Don't fire if it's already fired 198 | if (station->fired) 199 | continue; 200 | 201 | if (function_units_[instr->func_type].size() >= settings_.function_units_count[instr->func_type]) 202 | continue; 203 | 204 | bool all_ready = true; 205 | for (auto const& ready : station->sources_ready) 206 | all_ready &= ready; 207 | 208 | // Don't fire if all the tags aren't ready 209 | if (!all_ready) 210 | continue; 211 | 212 | // Fire away 213 | FunctionUnit unit; 214 | unit.latency = settings_.function_units_latency[instr->func_type]; 215 | unit.enter_cycle = current_clock_; 216 | unit.station = station; 217 | 218 | function_units_[instr->func_type].push_back(unit); 219 | 220 | station->fired = true; 221 | 222 | // Record when the instruction gets to execute 223 | instr->life->execute_cycle = current_clock_; 224 | 225 | 226 | // STATS: instructions fired 227 | ++stats_.instr_fired; 228 | } 229 | } 230 | 231 | void Tomasulo::retireInstructions() { 232 | // Make a min heap to pick the lowest retirable instruction (greater) 233 | priority_queue, greater> retirable; 234 | 235 | for (auto& unit_type : function_units_) { 236 | for (auto unit : unit_type) { 237 | if ((current_clock_ - unit.enter_cycle) >= unit.latency) { 238 | retirable.push(unit); 239 | } 240 | } 241 | } 242 | 243 | assert(busy_result_buses_.empty()); 244 | 245 | // Fill the result buses with the as many retirable instructions as possible 246 | for (size_t i = 0; i < settings_.result_bus_count && !retirable.empty(); i++) { 247 | auto unit = retirable.top(); 248 | retirable.pop(); 249 | 250 | auto station = unit.station; 251 | auto instr = unit.station->instr; 252 | 253 | // Put instruction on a result bus 254 | ResultBus bus; 255 | bus.reg = instr->dst_reg; 256 | bus.tag = station->target_tag; 257 | bus.station = station; 258 | busy_result_buses_.push_back(bus); 259 | 260 | // Record when the instruction gets to state update 261 | instr->life->state_update_cycle = current_clock_; 262 | 263 | 264 | // Delete instruction from active function units 265 | auto& func_bucket = function_units_[instr->func_type]; 266 | auto func_i = find(func_bucket.begin(), func_bucket.end(), unit); 267 | assert(func_i != func_bucket.end()); 268 | func_bucket.erase(func_i); 269 | 270 | // STATS: executed total 271 | ++stats_.instr_executed; 272 | } 273 | 274 | for (auto const& bus : busy_result_buses_) { 275 | // Update branch predictor bits 276 | if (bus.station->instr->is_branch) { 277 | branch_pred_.update(hashAddress(bus.station->instr->address), bus.station->instr->branch_taken); 278 | 279 | // Unmark branch tag 280 | if (bus.station->instr == bad_branch_instr_) 281 | bad_branch_instr_ = nullptr; 282 | } 283 | } 284 | } 285 | 286 | void Tomasulo::updateState() { 287 | for (auto const& bus : busy_result_buses_) { 288 | // Write to the register file 289 | if (bus.reg >= 0 && bus.tag == register_file[bus.reg].tag) { 290 | // Simulate write to register file 291 | register_file[bus.reg].tag = -1; 292 | } 293 | 294 | // Mark any matching tags as ready 295 | for (auto station : schedule_q_) { 296 | for (size_t i = 0; i < station->source_tags.size(); i++) { 297 | if (station->source_tags[i] == bus.tag) 298 | station->sources_ready[i] = true; 299 | } 300 | } 301 | } 302 | } 303 | 304 | void Tomasulo::sweepRetirementBuffer() { 305 | for (auto const& bus : retire_buffer_) { 306 | auto station_i = find(schedule_q_.begin(), schedule_q_.end(), bus.station); 307 | assert(station_i != schedule_q_.end()); 308 | schedule_q_.erase(station_i); 309 | 310 | // STATS: executed total 311 | ++stats_.instr_retired; 312 | } 313 | 314 | retire_buffer_.clear(); 315 | } 316 | 317 | intmax_t Tomasulo::getNewTag() { 318 | return current_tag_ == INTMAX_MAX ? 1 : current_tag_++; 319 | } 320 | } -------------------------------------------------------------------------------- /src/procsim.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "types.hpp" 12 | #include "Tomasulo.hpp" 13 | 14 | using namespace std; 15 | 16 | 17 | void exitUsage() 18 | { 19 | cout << "Usage:" << endl; 20 | cout << "\tprocsim -r result_bus_count -f fetch_rate -j k0_func_units -k k1_func_units -l k2_func_units" << endl; 21 | exit(EXIT_SUCCESS); 22 | } 23 | 24 | ProcessorSettings parseArguments(int argc, char* argv[]) { 25 | ProcessorSettings opts = {}; 26 | 27 | // Set default values from PDF 28 | opts.register_count = UINTMAX_C(128); 29 | 30 | opts.function_units_count.resize(3, 0); 31 | opts.function_units_latency.resize(3, 1); 32 | 33 | opts.ghr_bits = 3u; 34 | opts.ghr_init_val = UINTMAX_C(0b000); 35 | opts.predictor_bits = 2u; 36 | opts.predictor_init_val = UINTMAX_C(0b01); 37 | opts.predictor_table_size = 128; 38 | 39 | int c; 40 | while ((c = getopt(argc, argv,"r:f:j:k:l:h")) != -1) { 41 | switch (c) { 42 | case 'r': 43 | opts.result_bus_count = stoull(optarg); 44 | break; 45 | case 'f': 46 | opts.fetch_rate = stoull(optarg); 47 | break; 48 | case 'j': 49 | opts.function_units_count[0] = stoull(optarg); 50 | break; 51 | case 'k': 52 | opts.function_units_count[1] = stoull(optarg); 53 | break; 54 | case 'l': 55 | opts.function_units_count[2] = stoull(optarg); 56 | break; 57 | default: 58 | exitUsage(); 59 | } 60 | } 61 | 62 | if (opts.result_bus_count == 0 || opts.fetch_rate == 0 || opts.function_units_count.size() != opts.function_units_latency.size()) 63 | exitUsage(); 64 | 65 | for (auto count : opts.function_units_count) { 66 | if (count == 0) 67 | exitUsage(); 68 | } 69 | 70 | 71 | return opts; 72 | } 73 | 74 | void printSettings(const ProcessorSettings& opts) { 75 | cout << "Processor Settings" << endl; 76 | cout << "R: " << opts.result_bus_count << endl; 77 | cout << "k0: " << static_cast(opts.function_units_count[0]) << endl; 78 | cout << "k1: " << static_cast(opts.function_units_count[1]) << endl; 79 | cout << "k2: " << static_cast(opts.function_units_count[2]) << endl; 80 | cout << "F: " << opts.fetch_rate << endl; 81 | cout << endl; 82 | } 83 | 84 | shared_ptr parseInstruction(const string& line) { 85 | shared_ptr instr = make_shared(); 86 | 87 | int_least32_t dst_reg = -1, src1_reg = -1, src2_reg = -1; 88 | int branch_taken; 89 | 90 | int read = sscanf(line.c_str(), "%" SCNxLEAST64 " %" SCNdLEAST16 " %" SCNdLEAST32 " %" SCNdLEAST32 " %" SCNdLEAST32 " %" SCNxLEAST64 " %d", &instr->address, &instr->func_type, &dst_reg, &src1_reg, &src2_reg, &instr->branch_address, &branch_taken); 91 | 92 | if (instr->func_type == -1) 93 | instr->func_type = 1; 94 | 95 | instr->branch_taken = (bool)branch_taken; 96 | 97 | if (read == 7) 98 | instr->is_branch = true; 99 | else if (read != 5) { 100 | cout << "Error: unreadable format '" << line << "'" << endl; 101 | exit(EXIT_FAILURE); 102 | } 103 | 104 | instr->dst_reg = dst_reg; 105 | instr->src_regs.push_back(src1_reg); 106 | instr->src_regs.push_back(src2_reg); 107 | 108 | return instr; 109 | } 110 | 111 | void printInstructionLives(deque>& instrs) { 112 | cout << "INST\tFETCH\tDISP\tSCHED\tEXEC\tSTATE" << endl; 113 | for (size_t i = 0; i < instrs.size(); i++) { 114 | auto life = instrs[i]->life; 115 | 116 | cout << (i + 1) << "\t"; 117 | cout << life->fetch_cycle << "\t"; 118 | cout << life->dispatch_cycle << "\t"; 119 | cout << life->schedule_cycle << "\t"; 120 | cout << life->execute_cycle << "\t"; 121 | cout << life->state_update_cycle << "\t"; 122 | cout << endl; 123 | } 124 | } 125 | 126 | void printStatistics(Statistics const& stats) { 127 | // Additional calculations 128 | double avg_dispatch_size = (double)stats.dispatch_size_sum / (double)stats.clock_cycles; 129 | double avg_fired_instr = (double)stats.instr_fired / (double)stats.clock_cycles; 130 | double avg_retired_instr = (double)stats.instr_retired / (double)stats.clock_cycles; 131 | 132 | double pred_accuracy = (double)stats.correct_branches / (double)stats.branches; 133 | 134 | cout << endl; 135 | cout << "Processor stats:" << endl; 136 | cout << "Total branch instructions: " << stats.branches << endl; 137 | cout << "Total correct predicted branch instructions: " << stats.correct_branches << endl; 138 | cout << "prediction accuracy: " << pred_accuracy << endl; 139 | cout << "Avg Dispatch queue size: " << avg_dispatch_size << endl; 140 | cout << "Maximum Dispatch queue size: " << stats.peak_dispatch_size << endl; 141 | cout << "Avg inst Issue per cycle: " << avg_fired_instr << endl; 142 | cout << "Avg inst retired per cycle: " << avg_retired_instr << endl; 143 | cout << "Total run time (cycles): " << stats.clock_cycles << endl; 144 | } 145 | 146 | int main(int argc, char** argv) { 147 | // Parse arguments and print out the settings 148 | ProcessorSettings settings = parseArguments(argc, argv); 149 | printSettings(settings); 150 | 151 | // Read the entire stdin into memory 152 | deque> instrs; 153 | string line; 154 | for (uintmax_t i = 1; getline(cin, line); ++i) { 155 | auto instr = parseInstruction(line); 156 | instr->number = i; 157 | instrs.push_back(instr); 158 | } 159 | 160 | // Create the pipeline 161 | pipeline::Tomasulo ooe_pipeline(settings); 162 | 163 | ooe_pipeline.run(instrs); 164 | 165 | // Print out the per-instruction details (per-stage) 166 | printInstructionLives(instrs); 167 | 168 | 169 | // Print out statistics 170 | printStatistics(ooe_pipeline.getStatistics()); 171 | } --------------------------------------------------------------------------------