Skip to content

Commit

Permalink
Enable hardware metrics in Coreblocks (#586)
Browse files Browse the repository at this point in the history
  • Loading branch information
Jakub Urbańczyk authored Feb 12, 2024
1 parent 263720c commit 226cda0
Show file tree
Hide file tree
Showing 8 changed files with 94 additions and 9 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/benchmark.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ jobs:
- name: Synthesize
run: |
. venv/bin/activate
PYTHONHASHSEED=0 ./scripts/synthesize.py --verbose --config ${{ matrix.config }}
PYTHONHASHSEED=0 ./scripts/synthesize.py --verbose --strip-debug --config ${{ matrix.config }}
- name: Print synthesis information
run: cat ./build/top.tim
Expand Down
7 changes: 6 additions & 1 deletion coreblocks/core.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from amaranth import *

from transactron.utils.dependencies import DependencyManager
from transactron.utils.dependencies import DependencyManager, DependencyContext
from coreblocks.stages.func_blocks_unifier import FuncBlocksUnifier
from coreblocks.structs_common.instr_counter import CoreInstructionCounter
from coreblocks.structs_common.interrupt_controller import InterruptController
Expand Down Expand Up @@ -32,6 +32,7 @@
from coreblocks.frontend.fetch import Fetch, UnalignedFetch
from transactron.lib.transformers import MethodMap, MethodProduct
from transactron.lib import BasicFifo
from transactron.lib.metrics import HwMetricsEnabledKey

__all__ = ["Core"]

Expand All @@ -40,6 +41,10 @@ class Core(Elaboratable):
def __init__(self, *, gen_params: GenParams, wb_instr_bus: WishboneBus, wb_data_bus: WishboneBus):
self.gen_params = gen_params

dep_manager = DependencyContext.get()
if self.gen_params.debug_signals_enabled:
dep_manager.add_dependency(HwMetricsEnabledKey(), True)

self.wb_instr_bus = wb_instr_bus
self.wb_data_bus = wb_data_bus

Expand Down
4 changes: 4 additions & 0 deletions coreblocks/params/configurations.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ class CoreConfiguration:
Enables 16-bit Compressed Instructions extension.
embedded: bool
Enables Reduced Integer (E) extension.
debug_signals: bool
Enable debug signals (for example hardware metrics etc). If disabled, none of them will be synthesized.
phys_regs_bits: int
Size of the Physical Register File is 2**phys_regs_bits.
rob_entries_bits: int
Expand Down Expand Up @@ -76,6 +78,8 @@ class CoreConfiguration:
compressed: bool = False
embedded: bool = False

debug_signals: bool = True

phys_regs_bits: int = 6
rob_entries_bits: int = 7
start_pc: int = 0
Expand Down
2 changes: 2 additions & 0 deletions coreblocks/params/genparams.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ def __init__(self, cfg: CoreConfiguration):
block_size_bits=cfg.icache_block_size_bits,
)

self.debug_signals_enabled = cfg.debug_signals

# Verification temporally disabled
# if not optypes_required_by_extensions(self.isa.extensions) <= optypes_supported(func_units_config):
# raise Exception(f"Functional unit configuration fo not support all extension required by{isa_str}")
Expand Down
5 changes: 5 additions & 0 deletions coreblocks/stages/retirement.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from transactron.core import Method, Transaction, TModule, def_method
from transactron.lib.simultaneous import condition
from transactron.utils.dependencies import DependencyManager
from transactron.lib.metrics import *

from coreblocks.params.genparams import GenParams
from coreblocks.params.isa import ExceptionCause
Expand Down Expand Up @@ -46,6 +47,7 @@ def __init__(
self.trap_entry = trap_entry

self.instret_csr = DoubleCounterCSR(gen_params, CSRAddress.INSTRET, CSRAddress.INSTRETH)
self.perf_instr_ret = HwCounter("backend.retirement.retired_instr", "Number of retired instructions")

self.dependency_manager = gen_params.get(DependencyManager)
self.core_state = Method(o=self.gen_params.get(RetirementLayouts).core_state, nonexclusive=True)
Expand All @@ -54,6 +56,8 @@ def __init__(
def elaborate(self, platform):
m = TModule()

m.submodules += [self.perf_instr_ret]

m_csr = self.dependency_manager.get_dependency(GenericCSRRegistersKey()).m_mode
m.submodules.instret_csr = self.instret_csr

Expand Down Expand Up @@ -81,6 +85,7 @@ def retire_instr(rob_entry):
free_phys_reg(rat_out.old_rp_dst)

self.instret_csr.increment(m)
self.perf_instr_ret.incr(m)

def flush_instr(rob_entry):
# get original rp_dst mapped to instruction rl_dst in R-RAT
Expand Down
12 changes: 11 additions & 1 deletion scripts/gen_verilog.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,12 @@ def main():
+ f"Available configurations: {', '.join(list(str_to_coreconfig.keys()))}. Default: %(default)s",
)

parser.add_argument(
"--strip-debug",
action="store_true",
help="Remove debugging signals. Default: %(default)s",
)

parser.add_argument(
"-o", "--output", action="store", default="core.v", help="Output file path. Default: %(default)s"
)
Expand All @@ -83,7 +89,11 @@ def main():
if args.config not in str_to_coreconfig:
raise KeyError(f"Unknown config '{args.config}'")

gen_verilog(str_to_coreconfig[args.config], args.output)
config = str_to_coreconfig[args.config]
if args.strip_debug:
config = config.replace(debug_signals=False)

gen_verilog(config, args.output)


if __name__ == "__main__":
Expand Down
12 changes: 11 additions & 1 deletion scripts/synthesize.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,12 @@ def main():
help="Select core unit." + f"Available units: {', '.join(core_units.keys())}. Default: %(default)s",
)

parser.add_argument(
"--strip-debug",
action="store_true",
help="Remove debugging signals. Default: %(default)s",
)

parser.add_argument(
"-v",
"--verbose",
Expand All @@ -189,7 +195,11 @@ def main():
if args.unit not in core_units:
raise KeyError(f"Unknown core unit '{args.unit}'")

synthesize(str_to_coreconfig[args.config], args.platform, core_units[args.unit])
config = str_to_coreconfig[args.config]
if args.strip_debug:
config = config.replace(debug_signals=False)

synthesize(config, args.platform, core_units[args.unit])


if __name__ == "__main__":
Expand Down
59 changes: 54 additions & 5 deletions test/regression/pysim.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import re

from amaranth.sim import Passive, Settle
from amaranth.utils import log2_int
from amaranth import *

from .memory import *
from .common import SimulationBackend, SimulationExecutionResult

from transactron.testing import PysimSimulator
from transactron.testing import PysimSimulator, TestGen
from transactron.utils.dependencies import DependencyContext, DependencyManager
from transactron.lib.metrics import HardwareMetricsManager
from ..peripherals.test_wishbone import WishboneInterfaceWrapper

from coreblocks.core import Core
Expand All @@ -22,6 +26,8 @@ def __init__(self, verbose: bool, traces_file: Optional[str] = None):
self.verbose = verbose
self.traces_file = traces_file

self.metrics_manager = HardwareMetricsManager()

def _wishbone_slave(
self, mem_model: CoreMemoryModel, wb_ctrl: WishboneInterfaceWrapper, is_instr_bus: bool, delay: int = 0
):
Expand Down Expand Up @@ -82,14 +88,44 @@ def f():

return f

def _waiter(self):
def _waiter(self, on_finish: Callable[[], TestGen[None]]):
def f():
while self.running:
self.cycle_cnt += 1
yield

yield from on_finish()

return f

def pretty_dump_metrics(self, metric_values: dict[str, dict[str, int]], filter_regexp: str = ".*"):
print()
print("=== Core metrics dump ===")

put_space_before = True
for metric_name in sorted(metric_values.keys()):
if not re.search(filter_regexp, metric_name):
continue

metric = self.metrics_manager.get_metrics()[metric_name]

if metric.description != "":
if not put_space_before:
print()

print(f"# {metric.description}")

for reg in metric.regs.values():
reg_value = metric_values[metric_name][reg.name]

desc = f" # {reg.description} [reg width={reg.width}]"
print(f"{metric_name}/{reg.name} {reg_value}{desc}")

put_space_before = False
if metric.description != "":
print()
put_space_before = True

async def run(self, mem_model: CoreMemoryModel, timeout_cycles: int = 5000) -> SimulationExecutionResult:
with DependencyContext(DependencyManager()):
wb_instr_bus = WishboneBus(self.gp.wb_params)
Expand All @@ -105,13 +141,26 @@ async def run(self, mem_model: CoreMemoryModel, timeout_cycles: int = 5000) -> S
sim = PysimSimulator(core, max_cycles=timeout_cycles, traces_file=self.traces_file)
sim.add_sync_process(self._wishbone_slave(mem_model, wb_instr_ctrl, is_instr_bus=True))
sim.add_sync_process(self._wishbone_slave(mem_model, wb_data_ctrl, is_instr_bus=False))
sim.add_sync_process(self._waiter())

metric_values: dict[str, dict[str, int]] = {}

def on_sim_finish():
# Collect metric values before we finish the simulation
for metric_name, metric in self.metrics_manager.get_metrics().items():
metric = self.metrics_manager.get_metrics()[metric_name]
metric_values[metric_name] = {}
for reg_name in metric.regs:
metric_values[metric_name][reg_name] = yield self.metrics_manager.get_register_value(
metric_name, reg_name
)

sim.add_sync_process(self._waiter(on_finish=on_sim_finish))
success = sim.run()

if self.verbose:
print(f"Simulation finished in {self.cycle_cnt} cycles")
self.pretty_dump_metrics(metric_values)

return SimulationExecutionResult(success)
return SimulationExecutionResult(success, metric_values)

def stop(self):
self.running = False

0 comments on commit 226cda0

Please sign in to comment.