From c61a1eebdb05677245c40b6bd3273475d02ebd88 Mon Sep 17 00:00:00 2001 From: Arkadiusz Kozdra Date: Tue, 29 Oct 2024 08:35:32 +0100 Subject: [PATCH] Make RS feed FUs with garbage if flushing See #598 --- coreblocks/func_blocks/fu/common/rs.py | 17 ++- test/func_blocks/fu/common/test_rs.py | 166 +++++++++++++++++++++++-- 2 files changed, 169 insertions(+), 14 deletions(-) diff --git a/coreblocks/func_blocks/fu/common/rs.py b/coreblocks/func_blocks/fu/common/rs.py index 3c39045ad..9639c78e9 100644 --- a/coreblocks/func_blocks/fu/common/rs.py +++ b/coreblocks/func_blocks/fu/common/rs.py @@ -6,8 +6,9 @@ from coreblocks.params import GenParams from coreblocks.arch import OpType from coreblocks.interface.layouts import RSLayouts +from coreblocks.interface.keys import CoreStateKey from transactron.lib.metrics import HwExpHistogram, TaggedLatencyMeasurer -from transactron.utils import RecordDict +from transactron.utils import DependencyContext, RecordDict from transactron.utils import assign from transactron.utils.assign import AssignType from transactron.utils.amaranth_ext.coding import PriorityEncoder @@ -44,6 +45,8 @@ def __init__( self.ready_for = [list(op_list) for op_list in ready_for] self.get_ready_list = [Method(o=self.layouts.get_ready_list_out, nonexclusive=True) for _ in self.ready_for] + self.dependency_manager = DependencyContext.get() + self.data = Array(Signal(self.internal_layout) for _ in range(self.rs_entries)) self.data_ready = Signal(self.rs_entries) @@ -67,10 +70,14 @@ def elaborate(self, platform) -> TModule: def _elaborate(self, m: TModule, selected_id: Value, select_possible: Value, take_vector: Value): m.submodules += [self.perf_rs_wait_time, self.perf_num_full] - for i, record in enumerate(self.data): - m.d.comb += self.data_ready[i].eq( - ~record.rs_data.rp_s1.bool() & ~record.rs_data.rp_s2.bool() & record.rec_full.bool() - ) + with Transaction(name="readiness").body(m): + core_state = self.dependency_manager.get_dependency(CoreStateKey()) + state = core_state(m) + for i, record in enumerate(self.data): + m.d.comb += self.data_ready[i].eq( + (~record.rs_data.rp_s1.bool() & ~record.rs_data.rp_s2.bool() & record.rec_full.bool()) + | state.flushing + ) ready_lists: list[Value] = [] for op_list in self.ready_for: diff --git a/test/func_blocks/fu/common/test_rs.py b/test/func_blocks/fu/common/test_rs.py index 7d311dede..9b5ed0b22 100644 --- a/test/func_blocks/fu/common/test_rs.py +++ b/test/func_blocks/fu/common/test_rs.py @@ -1,17 +1,50 @@ import random from collections import deque +from collections.abc import Iterable from parameterized import parameterized_class -from transactron.testing import TestCaseWithSimulator, SimpleTestCircuit, TestbenchContext +from transactron.lib import Adapter +from transactron.testing import ( + SimpleTestCircuit, + TestCaseWithSimulator, + TestbenchContext, + TestbenchIO, + def_method_mock, +) +from transactron.utils.dependencies import DependencyContext from coreblocks.func_blocks.fu.common.rs import RS, RSBase from coreblocks.func_blocks.fu.common.fifo_rs import FifoRS +from coreblocks.interface.keys import CoreStateKey +from coreblocks.interface.layouts import RetirementLayouts from coreblocks.params import * from coreblocks.params.configurations import test_core_config from coreblocks.arch import OpType from transactron.testing.functions import data_const_to_dict +class RSTestCircuit(SimpleTestCircuit): + def __init__( + self, + gen_params: GenParams, + dut: type[RSBase], + rs_entries: int, + rs_number: int, + ready_for: Iterable[Iterable[OpType]] | None, + ): + super().__init__(dut(gen_params, rs_entries, rs_number, ready_for)) + self.gen_params = gen_params + + def elaborate(self, platform): + m = super().elaborate(platform) + m.submodules.core_state = self.core_state = TestbenchIO( + Adapter(o=self.gen_params.get(RetirementLayouts).core_state) + ) + DependencyContext.get().add_dependency(CoreStateKey(), self.core_state.adapter.iface) + + return m + + def create_check_list(rs_entries_bits: int, insert_list: list[dict]) -> list[dict]: check_list = [{"rs_data": None, "rec_reserved": 0, "rec_full": 0} for _ in range(2**rs_entries_bits)] @@ -66,13 +99,17 @@ def test_rs(self): random.seed(42) self.gen_params = GenParams(test_core_config) self.rs_entries_bits = self.gen_params.max_rs_entries_bits - self.m = SimpleTestCircuit(self.rs_elaboratable(self.gen_params, 2**self.rs_entries_bits, 0, None)) + self.m = RSTestCircuit(self.gen_params, self.rs_elaboratable, 2**self.rs_entries_bits, 0, None) self.data_list = create_data_list(self.gen_params, 10 * 2**self.rs_entries_bits) self.select_queue: deque[int] = deque() self.regs_to_update: set[int] = set() self.rs_entries: dict[int, int] = {} self.finished = False + @def_method_mock(lambda: self.m.core_state) + def core_state_mock(): + return {"flushing": 0} + with self.run_simulation(self.m) as sim: sim.add_testbench(self.select_process) sim.add_testbench(self.insert_process) @@ -142,7 +179,7 @@ class TestRSMethodInsert(TestCaseWithSimulator): def test_insert(self): self.gen_params = GenParams(test_core_config) self.rs_entries_bits = self.gen_params.max_rs_entries_bits - self.m = SimpleTestCircuit(RS(self.gen_params, 2**self.rs_entries_bits, 0, None)) + self.m = RSTestCircuit(self.gen_params, RS, 2**self.rs_entries_bits, 0, None) self.insert_list = [ { "rs_entry_id": id, @@ -185,7 +222,7 @@ class TestRSMethodSelect(TestCaseWithSimulator): def test_select(self): self.gen_params = GenParams(test_core_config) self.rs_entries_bits = self.gen_params.max_rs_entries_bits - self.m = SimpleTestCircuit(RS(self.gen_params, 2**self.rs_entries_bits, 0, None)) + self.m = RSTestCircuit(self.gen_params, RS, 2**self.rs_entries_bits, 0, None) self.insert_list = [ { "rs_entry_id": id, @@ -243,7 +280,7 @@ class TestRSMethodUpdate(TestCaseWithSimulator): def test_update(self): self.gen_params = GenParams(test_core_config) self.rs_entries_bits = self.gen_params.max_rs_entries_bits - self.m = SimpleTestCircuit(RS(self.gen_params, 2**self.rs_entries_bits, 0, None)) + self.m = RSTestCircuit(self.gen_params, RS, 2**self.rs_entries_bits, 0, None) self.insert_list = [ { "rs_entry_id": id, @@ -267,6 +304,10 @@ def test_update(self): ] self.check_list = create_check_list(self.rs_entries_bits, self.insert_list) + @def_method_mock(lambda: self.m.core_state) + def core_state_mock(): + return {"flushing": 0} + with self.run_simulation(self.m) as sim: sim.add_testbench(self.simulation_process) @@ -329,7 +370,7 @@ class TestRSMethodTake(TestCaseWithSimulator): def test_take(self): self.gen_params = GenParams(test_core_config) self.rs_entries_bits = self.gen_params.max_rs_entries_bits - self.m = SimpleTestCircuit(RS(self.gen_params, 2**self.rs_entries_bits, 0, None)) + self.m = RSTestCircuit(self.gen_params, RS, 2**self.rs_entries_bits, 0, None) self.insert_list = [ { "rs_entry_id": id, @@ -353,6 +394,10 @@ def test_take(self): ] self.check_list = create_check_list(self.rs_entries_bits, self.insert_list) + @def_method_mock(lambda: self.m.core_state) + def core_state_mock(): + return {"flushing": 0} + with self.run_simulation(self.m) as sim: sim.add_testbench(self.simulation_process) @@ -417,11 +462,102 @@ async def simulation_process(self, sim: TestbenchContext): assert sim.get(self.m._dut.get_ready_list[0].ready) == 0 +class TestRSMethodTakeFlushing(TestCaseWithSimulator): + def test_take_flushing(self): + self.gen_params = GenParams(test_core_config) + self.rs_entries_bits = self.gen_params.max_rs_entries_bits + self.m = RSTestCircuit(self.gen_params, RS, 2**self.rs_entries_bits, 0, None) + self.insert_list = [ + { + "rs_entry_id": id, + "rs_data": { + "rp_s1": id * 2, + "rp_s2": id * 2, + "rp_dst": id * 2, + "rob_id": id, + "exec_fn": { + "op_type": 1, + "funct3": 2, + "funct7": 4, + }, + "s1_val": id, + "s2_val": id, + "imm": id, + "pc": id, + }, + } + for id in range(2) + ] + self.check_list = create_check_list(self.rs_entries_bits, self.insert_list) + + @def_method_mock(lambda: self.m.core_state) + def core_state_mock(): + return {"flushing": 1} + + with self.run_simulation(self.m) as sim: + sim.add_testbench(self.simulation_process) + + async def simulation_process(self, sim: TestbenchContext): + # After each insert, entry should be marked as full + for record in self.insert_list: + await self.m.insert.call(sim, record) + + # Check data integrity + for expected, record in zip(self.check_list, self.m._dut.data): + assert expected == data_const_to_dict(sim.get(record)) + + # Take first instruction + assert sim.get(self.m._dut.get_ready_list[0].ready) == 1 + data = data_const_to_dict(await self.m.take.call(sim, rs_entry_id=0)) + for key in data: + assert data[key] == self.check_list[0]["rs_data"][key] + + # Take second instuction + assert sim.get(self.m._dut.get_ready_list[0].ready) == 1 + data = data_const_to_dict(await self.m.take.call(sim, rs_entry_id=1)) + for key in data: + assert data[key] == self.check_list[1]["rs_data"][key] + assert sim.get(self.m._dut.get_ready_list[0].ready) == 0 + + # Insert two new instructions and take them + reg_id = 4 + entry_data = { + "rp_s1": reg_id, + "rp_s2": reg_id, + "rp_dst": 1, + "rob_id": 12, + "exec_fn": { + "op_type": 1, + "funct3": 2, + "funct7": 4, + }, + "s1_val": 0, + "s2_val": 0, + "imm": 1, + "pc": 40, + } + + for index in range(2): + await self.m.insert.call(sim, rs_entry_id=index, rs_data=entry_data) + assert sim.get(self.m._dut.get_ready_list[0].ready) == 1 + assert sim.get(self.m._dut.data_ready[index]) == 1 + + data = data_const_to_dict(await self.m.take.call(sim, rs_entry_id=0)) + for key in data: + assert data[key] == entry_data[key] + assert sim.get(self.m._dut.get_ready_list[0].ready) == 1 + + data = data_const_to_dict(await self.m.take.call(sim, rs_entry_id=1)) + for key in data: + assert data[key] == entry_data[key] + assert sim.get(self.m._dut.get_ready_list[0].ready) == 0 + + class TestRSMethodGetReadyList(TestCaseWithSimulator): def test_get_ready_list(self): self.gen_params = GenParams(test_core_config) self.rs_entries_bits = self.gen_params.max_rs_entries_bits - self.m = SimpleTestCircuit(RS(self.gen_params, 2**self.rs_entries_bits, 0, None)) + self.m = RSTestCircuit(self.gen_params, RS, 2**self.rs_entries_bits, 0, None) self.insert_list = [ { "rs_entry_id": id, @@ -445,6 +581,10 @@ def test_get_ready_list(self): ] self.check_list = create_check_list(self.rs_entries_bits, self.insert_list) + @def_method_mock(lambda: self.m.core_state) + def core_state_mock(): + return {"flushing": 0} + with self.run_simulation(self.m) as sim: sim.add_testbench(self.simulation_process) @@ -473,8 +613,12 @@ def test_two_get_ready_lists(self): self.gen_params = GenParams(test_core_config) self.rs_entries = self.gen_params.max_rs_entries self.rs_entries_bits = self.gen_params.max_rs_entries_bits - self.m = SimpleTestCircuit( - RS(self.gen_params, 2**self.rs_entries_bits, 0, [[OpType(1), OpType(2)], [OpType(3), OpType(4)]]), + self.m = RSTestCircuit( + self.gen_params, + RS, + 2**self.rs_entries_bits, + 0, + [[OpType(1), OpType(2)], [OpType(3), OpType(4)]], ) self.insert_list = [ { @@ -498,6 +642,10 @@ def test_two_get_ready_lists(self): ] self.check_list = create_check_list(self.rs_entries_bits, self.insert_list) + @def_method_mock(lambda: self.m.core_state) + def core_state_mock(): + return {"flushing": 0} + with self.run_simulation(self.m) as sim: sim.add_testbench(self.simulation_process)