rob.py
1 from functools import reduce 2 import operator 3 from amaranth import * 4 from transactron import Method, Methods, Transaction, def_method, TModule, def_methods 5 from transactron.lib.fifo import WideFifo 6 from transactron.lib import logging 7 from transactron.lib.metrics import * 8 from coreblocks.interface.layouts import ROBLayouts 9 from coreblocks.params import GenParams 10 11 __all__ = ["ReorderBuffer"] 12 13 log = logging.HardwareLogger("core_structs.rob") 14 15 16 class ReorderBuffer(Elaboratable): 17 """Reorder buffer. 18 19 The reorder buffer (ROB) is a data structure responsible for preserving 20 the original instruction order for the purpose of correctly freeing 21 allocated registers and exception handling. The ROB is essentially 22 a FIFO queue: the instructions are added to it in order by the scheduler 23 (a part of the frontend) and removed in order by the retirement module 24 (a part of the backend). Additionally, the ROB remembers which 25 instructions finished execution: this information is updated by the 26 announcement module. 27 28 Attributes 29 ---------- 30 put : Method 31 Inserts instructions into the ROB. Used by the scheduler. 32 mark_done : Methods 33 Marks instruction as completed. Used by the announcement module. 34 retire : Method 35 Removes instructions from the ROB. Used by the retirement module. 36 peek : Method 37 Returns the front of the ROB without removing instructions. 38 get_indices : Method 39 Returns the `rob_id` of the oldest instruction in the ROB and the 40 first `rob_id` after the newest instruction in the ROB. 41 """ 42 43 def __init__(self, gen_params: GenParams, mark_done_ports: int) -> None: 44 """ 45 Parameters 46 ---------- 47 gen_params : GenParams 48 Core generator parameters for the selected core configuration. 49 mark_done_ports : int 50 The number of `mark_done` methods (announcement ports). 51 """ 52 self.params = gen_params 53 layouts = gen_params.get(ROBLayouts) 54 self.put = Method(i=layouts.put_layout, o=layouts.put_out_layout) 55 self.mark_done = Methods(mark_done_ports, i=layouts.mark_done_layout) 56 self.peek = Method(o=layouts.peek_layout) 57 self.retire = Method(i=layouts.retire_layout) 58 self.done = Array(Signal() for _ in range(2**self.params.rob_entries_bits)) 59 self.exception = Array(Signal() for _ in range(2**self.params.rob_entries_bits)) 60 self.data = WideFifo( 61 shape=layouts.data_layout, 62 depth=2**self.params.rob_entries_bits, 63 read_width=self.params.retirement_superscalarity, 64 write_width=self.params.frontend_superscalarity, 65 ) 66 self.get_indices = Method(o=layouts.get_indices) 67 68 self.perf_rob_wait_time = WideFIFOLatencyMeasurer( 69 "backend.rob.wait_time", 70 description="Distribution of time instructions spend in ROB", 71 slots_number=(2**gen_params.rob_entries_bits + 1), 72 max_latency=1000, 73 max_start_count=gen_params.frontend_superscalarity, 74 max_stop_count=gen_params.retirement_superscalarity, 75 ) 76 self.perf_rob_size = HwExpHistogram( 77 "backend.rob.size", 78 description="Number of instructions in ROB", 79 bucket_count=gen_params.rob_entries_bits + 1, 80 sample_width=gen_params.rob_entries_bits, 81 ) 82 self.perf_rob_put_count = TaggedCounter( 83 "backend.rob.put_count", 84 description="Number of instructions inserted into ROB in one cycle", 85 tags=range(gen_params.frontend_superscalarity + 1), 86 ) 87 self.perf_rob_retire_count = TaggedCounter( 88 "backend.rob.retire_count", 89 description="Number of instructions removed from ROB in one cycle", 90 tags=range(gen_params.retirement_superscalarity + 1), 91 ) 92 93 def elaborate(self, platform): 94 m = TModule() 95 96 m.submodules += [ 97 self.perf_rob_wait_time, 98 self.perf_rob_size, 99 self.perf_rob_put_count, 100 self.perf_rob_retire_count, 101 ] 102 103 start_idx = Value.cast(self.data.read_idx) 104 end_idx = Value.cast(self.data.write_idx) 105 106 start_idx_plus = [Signal.like(start_idx) for _ in range(self.params.retirement_superscalarity)] 107 for i in range(len(start_idx_plus)): 108 m.d.comb += start_idx_plus[i].eq(start_idx + i) 109 110 m.submodules.data = self.data 111 112 with Transaction().body(m): 113 peek_ret = self.data.peek(m) 114 115 @def_method(m, self.peek, nonexclusive=True) 116 def _(): 117 entries = [] 118 for i in range(self.params.retirement_superscalarity): 119 entries.append( 120 { 121 "rob_data": peek_ret.data[i], 122 "rob_id": start_idx_plus[i], 123 "exception": self.exception[start_idx_plus[i]], 124 } 125 ) 126 return {"count": peek_ret.count, "entries": entries} 127 128 @def_method(m, self.retire, ready=self.done[start_idx]) 129 def _(count: int): 130 retire_ok = reduce( 131 operator.and_, 132 [self.done[start_idx_plus[i]] | (i >= count) for i in range(self.params.retirement_superscalarity)], 133 ) 134 log.assertion(m, (count <= peek_ret.count) & retire_ok, "retire called with invalid count {}", count) 135 self.perf_rob_wait_time.stop(m, count=count) 136 self.perf_rob_retire_count.incr(m, tag=count) 137 self.data.read(m, count=count) 138 for i in range(self.params.retirement_superscalarity): 139 with m.If(i < count): 140 m.d.sync += self.done[start_idx_plus[i]].eq(0) 141 142 @def_method(m, self.put) 143 def _(count: int, entries): 144 self.perf_rob_wait_time.start(m, count=count) 145 self.perf_rob_put_count.incr(m, tag=count) 146 self.data.write(m, count=count, data=entries) 147 entries = [] 148 for i in range(self.params.frontend_superscalarity): 149 rob_id = (end_idx + i)[: len(end_idx)] 150 entries.append({"rob_id": rob_id}) 151 return {"entries": entries} 152 153 # TODO: There is a potential race condition when ROB is flushed. 154 # If functional units aren't flushed, finished obsolete instructions 155 # could mark fields in ROB as done when they shouldn't. 156 @def_methods(m, self.mark_done) 157 def _(k: int, rob_id: Value, exception): 158 log.assertion(m, ~self.done[rob_id], "mark_done called on already done ROB entry {}", rob_id) 159 m.d.sync += self.done[rob_id].eq(1) 160 m.d.sync += self.exception[rob_id].eq(exception) 161 162 @def_method(m, self.get_indices, nonexclusive=True) 163 def _(): 164 return {"start": start_idx, "end": end_idx} 165 166 if self.perf_rob_size.metrics_enabled(): 167 rob_size = Signal(self.params.rob_entries_bits) 168 m.d.comb += rob_size.eq((end_idx - start_idx)[0 : self.params.rob_entries_bits]) 169 with Transaction(name="perf").body(m): 170 self.perf_rob_size.add(m, rob_size) 171 172 return m