diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index c515414c2..cbba538a7 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -14,7 +14,7 @@ jobs: name: Synthesis benchmarks runs-on: ubuntu-latest timeout-minutes: 40 - container: ghcr.io/kuznia-rdzeni/amaranth-synth:ecp5-3.11 + container: ghcr.io/kuznia-rdzeni/amaranth-synth:ecp5-2023.11.19_v steps: - uses: actions/checkout@v3 @@ -63,7 +63,7 @@ jobs: build-perf-benchmarks: name: Build performance benchmarks runs-on: ubuntu-latest - container: ghcr.io/kuznia-rdzeni/riscv-toolchain:2023.10.08_v + container: ghcr.io/kuznia-rdzeni/riscv-toolchain:2023.11.19_v steps: - name: Checkout uses: actions/checkout@v3 @@ -83,7 +83,7 @@ jobs: name: Run performance benchmarks runs-on: ubuntu-latest timeout-minutes: 60 - container: ghcr.io/kuznia-rdzeni/verilator:v5.008-3.11 + container: ghcr.io/kuznia-rdzeni/verilator:v5.008-2023.11.19_v needs: build-perf-benchmarks steps: - name: Checkout diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 41e20abc5..1bcdac433 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -15,7 +15,7 @@ jobs: build-regression-tests: name: Build regression tests runs-on: ubuntu-latest - container: ghcr.io/kuznia-rdzeni/riscv-toolchain:2023.10.08_v + container: ghcr.io/kuznia-rdzeni/riscv-toolchain:2023.11.19_v outputs: cache_hit: ${{ steps.cache-regression.outputs.cache-hit }} steps: @@ -54,7 +54,7 @@ jobs: name: Run regression tests runs-on: ubuntu-latest timeout-minutes: 10 - container: ghcr.io/kuznia-rdzeni/verilator:v5.008-3.11 + container: ghcr.io/kuznia-rdzeni/verilator:v5.008-2023.11.19_v needs: build-regression-tests steps: - name: Checkout @@ -97,12 +97,8 @@ jobs: . venv/bin/activate scripts/run_tests.py -a regression - - name: Test Report - uses: EnricoMi/publish-unit-test-result-action@v2 - with: - files: test/regression/cocotb/results.xml - check_name: cocotb test results - comment_mode: off + - name: Check for test failure + run: ./scripts/check_test_results.py unit-test: name: Run unit tests @@ -131,7 +127,7 @@ jobs: run: ./scripts/run_tests.py --verbose - name: Check traces - run: ./scripts/run_tests.py -t -c 1 TestCore + run: ./scripts/run_tests.py -t -c 1 TestCore lint: name: Check code formatting and typing diff --git a/docker/AmaranthSynthECP5.Dockerfile b/docker/AmaranthSynthECP5.Dockerfile index 3b9326ccf..3ae726972 100644 --- a/docker/AmaranthSynthECP5.Dockerfile +++ b/docker/AmaranthSynthECP5.Dockerfile @@ -3,7 +3,7 @@ FROM ubuntu:23.04 RUN apt-get update && \ DEBIAN_FRONTEND=noninteractive \ apt-get install -y --no-install-recommends \ - python3.11 python3-pip git yosys lsb-release \ + python3.11 python3-pip python3.11-venv git yosys lsb-release \ build-essential cmake python3-dev libboost-all-dev libeigen3-dev && \ rm -rf /var/lib/apt/lists/* diff --git a/docker/Verilator.Dockerfile b/docker/Verilator.Dockerfile index 64c60c5e4..785e76b26 100644 --- a/docker/Verilator.Dockerfile +++ b/docker/Verilator.Dockerfile @@ -3,12 +3,12 @@ FROM ubuntu:23.04 RUN apt-get update && \ DEBIAN_FRONTEND=noninteractive \ apt-get install -y --no-install-recommends \ - python3.11 libpython3.11 python3-pip git lsb-release \ + python3.11 libpython3.11 python3-pip python3.11-venv git lsb-release \ perl perl-doc help2man make autoconf g++ flex bison ccache numactl \ libgoogle-perftools-dev libfl-dev zlib1g-dev && \ rm -rf /var/lib/apt/lists/* -RUN git clone --recursive \ +RUN git clone --recursive --shallow-since=2023.03.01 \ https://github.com/verilator/verilator.git \ verilator && \ cd verilator && \ diff --git a/docker/riscv-toolchain.Dockerfile b/docker/riscv-toolchain.Dockerfile index d35c604b9..957141eb0 100644 --- a/docker/riscv-toolchain.Dockerfile +++ b/docker/riscv-toolchain.Dockerfile @@ -3,15 +3,39 @@ FROM ubuntu:23.04 RUN apt-get update && \ DEBIAN_FRONTEND=noninteractive \ apt-get install -y --no-install-recommends \ - autoconf automake autotools-dev curl python3 bc lsb-release \ + autoconf automake autotools-dev curl python3.11 python3.11-venv python3-pip bc lsb-release \ libmpc-dev libmpfr-dev libgmp-dev gawk build-essential \ - bison flex texinfo gperf libtool patchutils zlib1g-dev \ - libexpat-dev ninja-build git ca-certificates python-is-python3 && \ + bison flex texinfo gperf libtool patchutils zlib1g-dev device-tree-compiler \ + libexpat-dev ninja-build git ca-certificates python-is-python3 \ + libssl-dev libbz2-dev libreadline-dev libsqlite3-dev libncursesw5-dev xz-utils tk-dev libxml2-dev libxmlsec1-dev libffi-dev liblzma-dev && \ rm -rf /var/lib/apt/lists/* -RUN git clone https://github.com/riscv/riscv-gnu-toolchain && \ +RUN git clone --shallow-since=2023.05.01 https://github.com/riscv/riscv-gnu-toolchain && \ cd riscv-gnu-toolchain && \ git checkout 2023.05.14 && \ ./configure --with-multilib-generator="rv32i-ilp32--a*zifence*zicsr;rv32im-ilp32--a*zifence*zicsr;rv32ic-ilp32--a*zifence*zicsr;rv32imc-ilp32--a*zifence*zicsr;rv32imfc-ilp32f--a*zifence;rv32i_zmmul-ilp32--a*zifence*zicsr;rv32ic_zmmul-ilp32--a*zifence*zicsr" && \ make -j$(nproc) && \ cd / && rm -rf riscv-gnu-toolchain + +RUN git clone --shallow-since=2023.10.01 https://github.com/riscv-software-src/riscv-isa-sim.git spike && \ + cd spike && \ + git checkout eeef09ebb894c3bb7e42b7b47aae98792b8eef79 && \ + mkdir build/ install/ && \ + cd build/ && \ + ../configure --prefix=/spike/install/ && \ + make -j$(nproc) && \ + make install && \ + cd .. && \ + rm -rf build/ + +RUN git clone --depth=1 https://github.com/pyenv/pyenv.git .pyenv && \ + export PATH=/.pyenv/bin:$PATH && \ + export PYENV_ROOT=/root/.pyenv && \ + eval "$(pyenv init --path)" && \ + pyenv install 3.6.15 && \ + pyenv global 3.6.15 && \ + python -m venv venv3.6 && \ + . venv3.6/bin/activate && \ + python -m pip install --upgrade pip && \ + python -m pip install riscof && \ + pyenv global system diff --git a/docs/Current_graph.md b/docs/Current_graph.md deleted file mode 100644 index 1d64977b4..000000000 --- a/docs/Current_graph.md +++ /dev/null @@ -1,5 +0,0 @@ -# Full transaction-method graph - -```{eval-rst} -.. include:: auto_graph.rst -``` diff --git a/docs/Assumptions.md b/docs/assumptions.md similarity index 100% rename from docs/Assumptions.md rename to docs/assumptions.md diff --git a/docs/current-graph.md b/docs/current-graph.md new file mode 100644 index 000000000..c176682f2 --- /dev/null +++ b/docs/current-graph.md @@ -0,0 +1,12 @@ +# Full transaction-method graph + +
+
+ + ```{eval-rst} + .. include:: auto_graph.rst + + ``` + +
+
diff --git a/docs/Development_environment.md b/docs/development-environment.md similarity index 100% rename from docs/Development_environment.md rename to docs/development-environment.md diff --git a/docs/Home.md b/docs/home.md similarity index 100% rename from docs/Home.md rename to docs/home.md diff --git a/docs/index.md b/docs/index.md index 85d7b3e2c..0e16a25ec 100644 --- a/docs/index.md +++ b/docs/index.md @@ -5,17 +5,17 @@ maxdepth: 3 --- -Home.md -Assumptions.md -Development_environment.md -Transactions.md -scheduler/Overview.md -shared_structs/Implementation/RS_impl.md -shared_structs/RS.md -Current_graph.md -Problem-checklist.md -synthesis/Synthesis.md +home.md +assumptions.md +development-environment.md +transactions.md +scheduler/overview.md +shared-structs/implementation/rs-impl.md +shared-structs/rs.md +current-graph.md +problem-checklist.md +synthesis/synthesis.md components/icache.md -miscellany/exceptionsSummary.md +miscellany/exceptions-summary.md api.md ``` diff --git a/docs/miscellany/exceptionsSummary.md b/docs/miscellany/exceptions-summary.md similarity index 100% rename from docs/miscellany/exceptionsSummary.md rename to docs/miscellany/exceptions-summary.md diff --git a/docs/Problem-checklist.md b/docs/problem-checklist.md similarity index 100% rename from docs/Problem-checklist.md rename to docs/problem-checklist.md diff --git a/docs/scheduler/Overview.md b/docs/scheduler/overview.md similarity index 100% rename from docs/scheduler/Overview.md rename to docs/scheduler/overview.md diff --git a/docs/shared_structs/Implementation/RS_impl.md b/docs/shared-structs/implementation/rs-impl.md similarity index 100% rename from docs/shared_structs/Implementation/RS_impl.md rename to docs/shared-structs/implementation/rs-impl.md diff --git a/docs/shared_structs/RS.md b/docs/shared-structs/rs.md similarity index 100% rename from docs/shared_structs/RS.md rename to docs/shared-structs/rs.md diff --git a/docs/synthesis/Synthesis.md b/docs/synthesis/synthesis.md similarity index 100% rename from docs/synthesis/Synthesis.md rename to docs/synthesis/synthesis.md diff --git a/docs/Transactions.md b/docs/transactions.md similarity index 100% rename from docs/Transactions.md rename to docs/transactions.md diff --git a/scripts/check_test_results.py b/scripts/check_test_results.py new file mode 100755 index 000000000..c10af9bc2 --- /dev/null +++ b/scripts/check_test_results.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python3 + +import sys +import os +import pathlib +import xml.etree.ElementTree as eT + +FAILURE_TAG = "failure" +TOP_DIR = pathlib.Path(__file__).parent.parent +TEST_RESULTS_FILE = TOP_DIR.joinpath("test/regression/cocotb/results.xml") + +if not os.path.exists(TEST_RESULTS_FILE): + print("File not found: ", TEST_RESULTS_FILE) + sys.exit(1) + +tree = eT.parse(TEST_RESULTS_FILE) + +if len(list(tree.iter(FAILURE_TAG))) > 0: + print("Some regression tests failed") + sys.exit(1) + +print("All regression tests pass") diff --git a/test/structs_common/test_rat.py b/test/structs_common/test_rat.py new file mode 100644 index 000000000..2119a9ced --- /dev/null +++ b/test/structs_common/test_rat.py @@ -0,0 +1,85 @@ +from ..common import TestCaseWithSimulator, SimpleTestCircuit + +from coreblocks.structs_common.rat import FRAT, RRAT +from coreblocks.params import GenParams +from coreblocks.params.configurations import test_core_config + +from collections import deque +from random import Random + + +class TestFrontendRegisterAliasTable(TestCaseWithSimulator): + def gen_input(self): + for _ in range(self.test_steps): + rl = self.rand.randrange(self.gen_params.isa.reg_cnt) + rp = self.rand.randrange(1, 2**self.gen_params.phys_regs_bits) if rl != 0 else 0 + rl_s1 = self.rand.randrange(self.gen_params.isa.reg_cnt) + rl_s2 = self.rand.randrange(self.gen_params.isa.reg_cnt) + + self.to_execute_list.append({"rl": rl, "rp": rp, "rl_s1": rl_s1, "rl_s2": rl_s2}) + + def do_rename(self): + for _ in range(self.test_steps): + to_execute = self.to_execute_list.pop() + res = yield from self.m.rename.call( + rl_dst=to_execute["rl"], rp_dst=to_execute["rp"], rl_s1=to_execute["rl_s1"], rl_s2=to_execute["rl_s2"] + ) + self.assertEqual(res["rp_s1"], self.expected_entries[to_execute["rl_s1"]]) + self.assertEqual(res["rp_s2"], self.expected_entries[to_execute["rl_s2"]]) + + self.expected_entries[to_execute["rl"]] = to_execute["rp"] + + def test_single(self): + self.rand = Random(0) + self.test_steps = 2000 + self.gen_params = GenParams(test_core_config.replace(phys_regs_bits=5, rob_entries_bits=6)) + m = SimpleTestCircuit(FRAT(gen_params=self.gen_params)) + self.m = m + + self.log_regs = self.gen_params.isa.reg_cnt + self.phys_regs = 2**self.gen_params.phys_regs_bits + + self.to_execute_list = deque() + self.expected_entries = [0 for _ in range(self.log_regs)] + + self.gen_input() + with self.run_simulation(m) as sim: + sim.add_sync_process(self.do_rename) + + +class TestRetirementRegisterAliasTable(TestCaseWithSimulator): + def gen_input(self): + for _ in range(self.test_steps): + rl = self.rand.randrange(self.gen_params.isa.reg_cnt) + rp = self.rand.randrange(1, 2**self.gen_params.phys_regs_bits) if rl != 0 else 0 + side_fx = self.rand.randrange(0, 2) + + self.to_execute_list.append({"rl": rl, "rp": rp, "side_fx": side_fx}) + + def do_commit(self): + for _ in range(self.test_steps): + to_execute = self.to_execute_list.pop() + res = yield from self.m.commit.call( + rl_dst=to_execute["rl"], rp_dst=to_execute["rp"], side_fx=to_execute["side_fx"] + ) + self.assertEqual(res["old_rp_dst"], self.expected_entries[to_execute["rl"]]) + + if to_execute["side_fx"]: + self.expected_entries[to_execute["rl"]] = to_execute["rp"] + + def test_single(self): + self.rand = Random(0) + self.test_steps = 2000 + self.gen_params = GenParams(test_core_config.replace(phys_regs_bits=5, rob_entries_bits=6)) + m = SimpleTestCircuit(RRAT(gen_params=self.gen_params)) + self.m = m + + self.log_regs = self.gen_params.isa.reg_cnt + self.phys_regs = 2**self.gen_params.phys_regs_bits + + self.to_execute_list = deque() + self.expected_entries = [0 for _ in range(self.log_regs)] + + self.gen_input() + with self.run_simulation(m) as sim: + sim.add_sync_process(self.do_commit) diff --git a/test/transactions/test_transaction_lib.py b/test/transactions/test_transaction_lib.py index d43540860..6daa6517c 100644 --- a/test/transactions/test_transaction_lib.py +++ b/test/transactions/test_transaction_lib.py @@ -351,7 +351,7 @@ def test_many_out(self): sim.add_sync_process(self.generate_producer(i)) -class MethodTransformerTestCircuit(Elaboratable): +class MethodMapTestCircuit(Elaboratable): def __init__(self, iosize: int, use_methods: bool, use_dicts: bool): self.iosize = iosize self.use_methods = use_methods @@ -399,25 +399,21 @@ def _(arg: Record): def _(arg: Record): return otransform(m, arg) - trans = MethodTransformer( - self.target.adapter.iface, i_transform=(layout, imeth), o_transform=(layout, ometh) - ) + trans = MethodMap(self.target.adapter.iface, i_transform=(layout, imeth), o_transform=(layout, ometh)) else: - trans = MethodTransformer( + trans = MethodMap( self.target.adapter.iface, i_transform=(layout, itransform), o_transform=(layout, otransform), ) - m.submodules.trans = trans - - m.submodules.source = self.source = TestbenchIO(AdapterTrans(trans.method)) + m.submodules.source = self.source = TestbenchIO(AdapterTrans(trans.use(m))) return m class TestMethodTransformer(TestCaseWithSimulator): - m: MethodTransformerTestCircuit + m: MethodMapTestCircuit def source(self): for i in range(2**self.m.iosize): @@ -430,19 +426,19 @@ def target(self, data): return {"data": (data << 1) | (data >> (self.m.iosize - 1))} def test_method_transformer(self): - self.m = MethodTransformerTestCircuit(4, False, False) + self.m = MethodMapTestCircuit(4, False, False) with self.run_simulation(self.m) as sim: sim.add_sync_process(self.source) sim.add_sync_process(self.target) def test_method_transformer_dicts(self): - self.m = MethodTransformerTestCircuit(4, False, True) + self.m = MethodMapTestCircuit(4, False, True) with self.run_simulation(self.m) as sim: sim.add_sync_process(self.source) sim.add_sync_process(self.target) def test_method_transformer_with_methods(self): - self.m = MethodTransformerTestCircuit(4, True, True) + self.m = MethodMapTestCircuit(4, True, True) with self.run_simulation(self.m) as sim: sim.add_sync_process(self.source) sim.add_sync_process(self.target) @@ -517,9 +513,9 @@ def elaborate(self, platform): if self.add_combiner: combiner = (layout, lambda _, vs: {"data": sum(vs)}) - m.submodules.product = product = MethodProduct(methods, combiner) + product = MethodProduct(methods, combiner) - m.submodules.method = self.method = TestbenchIO(AdapterTrans(product.method)) + m.submodules.method = self.method = TestbenchIO(AdapterTrans(product.use(m))) return m @@ -704,9 +700,9 @@ def elaborate(self, platform): if self.add_combiner: combiner = (layout, lambda _, vs: {"data": sum(Mux(s, r, 0) for (s, r) in vs)}) - m.submodules.product = product = MethodTryProduct(methods, combiner) + product = MethodTryProduct(methods, combiner) - m.submodules.method = self.method = TestbenchIO(AdapterTrans(product.method)) + m.submodules.method = self.method = TestbenchIO(AdapterTrans(product.use(m))) return m diff --git a/transactron/lib/transformers.py b/transactron/lib/transformers.py index b3d1e2470..18c3ac73a 100644 --- a/transactron/lib/transformers.py +++ b/transactron/lib/transformers.py @@ -1,28 +1,56 @@ +from abc import ABC from amaranth import * from ..core import * from ..core import RecordDict from typing import Optional from collections.abc import Callable -from transactron.utils import ValueLike, assign, AssignType +from transactron.utils import ValueLike, assign, AssignType, ModuleLike from .connectors import Forwarder, ManyToOneConnectTrans, ConnectTrans __all__ = [ - "MethodTransformer", + "Transformer", + "MethodMap", "MethodFilter", "MethodProduct", "MethodTryProduct", "Collector", "CatTrans", - "ConnectAndTransformTrans", + "ConnectAndMapTrans", ] -class MethodTransformer(Elaboratable): - """Method transformer. +class Transformer(ABC): + """Method transformer abstract class. + + Method transformers construct a new method which utilizes other methods. + + Attributes + ---------- + method: Method + The method. + """ + + method: Method + + def use(self, m: ModuleLike): + """ + Returns the method and adds the transformer to a module. + + Parameters + ---------- + m: Module or TModule + The module to which this transformer is added as a submodule. + """ + m.submodules += self + return self.method + + +class MethodMap(Transformer, Elaboratable): + """Bidirectional map for methods. Takes a target method and creates a transformed method which calls the - original target method, transforming the input and output values. - The transformation functions take two parameters, a `Module` and the + original target method, mapping the input and output values with + functions. The mapping functions take two parameters, a `Module` and the `Record` being transformed. Alternatively, a `Method` can be passed. @@ -45,13 +73,13 @@ def __init__( target: Method The target method. i_transform: (record layout, function or Method), optional - Input transformation. If specified, it should be a pair of a + Input mapping function. If specified, it should be a pair of a function and a input layout for the transformed method. - If not present, input is not transformed. + If not present, input is passed unmodified. o_transform: (record layout, function or Method), optional - Output transformation. If specified, it should be a pair of a + Output mapping function. If specified, it should be a pair of a function and a output layout for the transformed method. - If not present, output is not transformed. + If not present, output is passed unmodified. """ if i_transform is None: i_transform = (target.data_in.layout, lambda _, x: x) @@ -73,7 +101,7 @@ def _(arg): return m -class MethodFilter(Elaboratable): +class MethodFilter(Transformer, Elaboratable): """Method filter. Takes a target method and creates a method which calls the target method @@ -129,7 +157,7 @@ def _(arg): return m -class MethodProduct(Elaboratable): +class MethodProduct(Transformer, Elaboratable): def __init__( self, targets: list[Method], @@ -177,7 +205,7 @@ def _(arg): return m -class MethodTryProduct(Elaboratable): +class MethodTryProduct(Transformer, Elaboratable): def __init__( self, targets: list[Method], @@ -229,7 +257,7 @@ def _(arg): return m -class Collector(Elaboratable): +class Collector(Transformer, Elaboratable): """Single result collector. Creates method that collects results of many methods with identical @@ -308,14 +336,13 @@ def elaborate(self, platform): return m -class ConnectAndTransformTrans(Elaboratable): - """Connecting transaction with transformations. +class ConnectAndMapTrans(Elaboratable): + """Connecting transaction with mapping functions. Behaves like `ConnectTrans`, but modifies the transferred data using - functions or `Method`s. Equivalent to a combination of - `ConnectTrans` and `MethodTransformer`. The transformation - functions take two parameters, a `Module` and the `Record` being - transformed. + functions or `Method`s. Equivalent to a combination of `ConnectTrans` + and `MethodMap`. The mapping functions take two parameters, a `Module` + and the `Record` being transformed. """ def __init__( @@ -346,7 +373,7 @@ def __init__( def elaborate(self, platform): m = TModule() - m.submodules.transformer = transformer = MethodTransformer( + m.submodules.transformer = transformer = MethodMap( self.method2, i_transform=(self.method1.data_out.layout, self.i_fun), o_transform=(self.method1.data_in.layout, self.o_fun),