From c5c49851c7ed67ed72a2b23db2b424739cefb666 Mon Sep 17 00:00:00 2001 From: Piotro Date: Fri, 15 Sep 2023 08:56:40 +0200 Subject: [PATCH 01/11] Support ELF running with signature section dump --- scripts/run_signature.py | 72 ++++++++++++++++++++++++++++++++++++ test/regression/memory.py | 47 ++++++++++++----------- test/regression/signature.py | 63 +++++++++++++++++++++++++++++++ 3 files changed, 158 insertions(+), 24 deletions(-) create mode 100755 scripts/run_signature.py create mode 100644 test/regression/signature.py diff --git a/scripts/run_signature.py b/scripts/run_signature.py new file mode 100755 index 000000000..9f77f0e78 --- /dev/null +++ b/scripts/run_signature.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python3 + +import asyncio +import argparse +import sys +import os +from typing import Literal + +if __name__ == "__main__": + parent = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + sys.path.insert(0, parent) + +import test.regression.signature # noqa: E402 +from test.regression.pysim import PySimulation # noqa: E402 + + +# def run_with_cocotb(benchmarks: list[str], traces: bool) -> bool: +# arglist = ["make", "-C", "test/regression/cocotb", "-f", "benchmark.Makefile", "--no-print-directory"] +# +# test_cases = ",".join(benchmarks) +# arglist += [f"TESTCASE={test_cases}"] +# +# if traces: +# 3 arglist += ["TRACES=1"] +# +# res = subprocess.run(arglist) +# +# return res.returncode == 0 + + +def run_with_pysim(test_name: str, traces: bool, verbose: bool, output: str) -> bool: + traces_file = None + if traces: + traces_file = os.path.basename(test_name) + try: + asyncio.run( + test.regression.signature.run_test(PySimulation(verbose, traces_file=traces_file), test_name, output) + ) + except RuntimeError as e: + print("RuntimeError:", e) + return False + return True + + +def run_test(test: str, backend: Literal["pysim", "cocotb"], traces: bool, verbose: bool, output: str) -> bool: + # if backend == "cocotb": + # return run_benchmarks_with_cocotb(test, traces) + # elif backend == "pysim": + return run_with_pysim(test, traces, verbose, output) + # return False + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("-t", "--trace", action="store_true", help="Dump waveforms") + parser.add_argument("-v", "--verbose", action="store_true", help="Verbose output") + parser.add_argument("-b", "--backend", default="pysim", choices=["cocotb", "pysim"], help="Simulation backend") + parser.add_argument("-o", "--output", default=None, help="Selects output file to write test signature to") + parser.add_argument("path") + + args = parser.parse_args() + + output = args.output if args.output else args.path + ".signature" + + success = run_test(args.path, args.backend, args.trace, args.verbose, output) + if not success: + print("Program execution failed") + sys.exit(1) + + +if __name__ == "__main__": + main() diff --git a/test/regression/memory.py b/test/regression/memory.py index 1f0fa407f..068a6edd5 100644 --- a/test/regression/memory.py +++ b/test/regression/memory.py @@ -4,7 +4,7 @@ from typing import Optional, TypeVar from dataclasses import dataclass, replace from elftools.elf.constants import P_FLAGS -from elftools.elf.elffile import ELFFile +from elftools.elf.elffile import ELFFile, Segment all = [ "ReplyStatus", @@ -137,6 +137,27 @@ def write(self, req: WriteRequest) -> WriteReply: return WriteReply(status=ReplyStatus.ERROR) +def load_segment(segment: Segment) -> RandomAccessMemory: + paddr = segment.header["p_paddr"] + memsz = segment.header["p_memsz"] + flags_raw = segment.header["p_flags"] + + seg_start = paddr + seg_end = paddr + memsz + + data = segment.data() + + flags = SegmentFlags(0) + if flags_raw & P_FLAGS.PF_R == flags_raw & P_FLAGS.PF_R: + flags |= SegmentFlags.READ + if flags_raw & P_FLAGS.PF_W == flags_raw & P_FLAGS.PF_W: + flags |= SegmentFlags.WRITE + if flags_raw & P_FLAGS.PF_X == flags_raw & P_FLAGS.PF_X: + flags |= SegmentFlags.EXECUTABLE + + return RandomAccessMemory(range(seg_start, seg_end), flags, data) + + def load_segments_from_elf(file_path: str) -> list[RandomAccessMemory]: segments: list[RandomAccessMemory] = [] @@ -145,28 +166,6 @@ def load_segments_from_elf(file_path: str) -> list[RandomAccessMemory]: for segment in elffile.iter_segments(): if segment.header["p_type"] != "PT_LOAD": continue - - paddr = segment.header["p_paddr"] - alignment = segment.header["p_align"] - memsz = segment.header["p_memsz"] - flags_raw = segment.header["p_flags"] - - def align_down(n: int) -> int: - return (n // alignment) * alignment - - seg_start = align_down(paddr) - seg_end = align_down(paddr + memsz + alignment - 1) - - data = b"\x00" * (paddr - seg_start) + segment.data() + b"\x00" * (seg_end - (paddr + len(segment.data()))) - - flags = SegmentFlags(0) - if flags_raw & P_FLAGS.PF_R == flags_raw & P_FLAGS.PF_R: - flags |= SegmentFlags.READ - if flags_raw & P_FLAGS.PF_W == flags_raw & P_FLAGS.PF_W: - flags |= SegmentFlags.WRITE - if flags_raw & P_FLAGS.PF_X == flags_raw & P_FLAGS.PF_X: - flags |= SegmentFlags.EXECUTABLE - - segments.append(RandomAccessMemory(range(seg_start, seg_end), flags, data)) + segments.append(load_segment(segment)) return segments diff --git a/test/regression/signature.py b/test/regression/signature.py new file mode 100644 index 000000000..9a4e6815d --- /dev/null +++ b/test/regression/signature.py @@ -0,0 +1,63 @@ +from .memory import * +from .common import SimulationBackend + + +class ToHostMMIO(MemorySegment): + def __init__(self, addr: range, on_finish: Callable[[], None]): + super().__init__(addr, SegmentFlags.READ | SegmentFlags.WRITE) + self.on_finish = on_finish + + def read(self, _) -> ReadReply: + return ReadReply() + + def write(self, _) -> WriteReply: + self.on_finish() + return WriteReply() + + +def map_mem_segments( + elf_path: str, stop_callback: Callable[[], None] +) -> tuple[list[MemorySegment], RandomAccessMemory]: + mem_segments = [] + signature_ram = RandomAccessMemory(range(0, 0), SegmentFlags.WRITE, bytearray()) + + with open(elf_path, "rb") as f: + elffile = ELFFile(f) + + signature_section = elffile.get_section(elffile.get_section_index(".signature")) + tohost_section = elffile.get_section(elffile.get_section_index(".hostmmio")) + + for segment in elffile.iter_segments(): + # .signature and .tohost sections have direct segment mapping + addr_range = range(segment.header["p_vaddr"], segment.header["p_vaddr"] + segment.header["p_memsz"]) + if segment.section_in_segment(signature_section): + signature_ram = load_segment(segment) + elif segment.section_in_segment(tohost_section): + mem_segments.append(ToHostMMIO(addr_range, stop_callback)) + elif segment.header["p_type"] == "PT_LOAD": + mem_segments.append(load_segment(segment)) + + return (mem_segments, signature_ram) + + +async def run_test(sim_backend: SimulationBackend, test_path: str, signature_path: str): + (mem_segments, signature_ram) = map_mem_segments(test_path, sim_backend.stop) + + mem_segments.append(signature_ram) + mem_model = CoreMemoryModel(mem_segments) + + success = await sim_backend.run(mem_model, timeout_cycles=50000) + + if not success: + raise RuntimeError("Simulation timed out") + + print("Program execution finished!") + + # generate signature file in riscv-torture format (used also by riscof) + # 32-bit little endian memory dump from data between .begin_signature and .end_signature + # symbols, mapped to .signature section in our linker script + with open(signature_path, "w") as sig_file: + data = signature_ram.data.zfill(((len(signature_ram.data) + 3) // 4) * 4) + for idx in range(0, len(data), 4): + num = int.from_bytes(data[idx : idx + 4], "little") + sig_file.write(hex(num)[2:].zfill(8) + "\n") From 8041a96f4ed6dedb0bb69f06fbf8178bbf455c26 Mon Sep 17 00:00:00 2001 From: Piotro Date: Thu, 5 Oct 2023 10:39:46 +0200 Subject: [PATCH 02/11] Support running with cocotb --- scripts/run_signature.py | 35 ++++++++++--------- test/regression/cocotb/signature.Makefile | 28 +++++++++++++++ .../regression/cocotb/signature_entrypoint.py | 25 +++++++++++++ test/regression/signature.py | 4 +-- 4 files changed, 73 insertions(+), 19 deletions(-) create mode 100644 test/regression/cocotb/signature.Makefile create mode 100644 test/regression/cocotb/signature_entrypoint.py diff --git a/scripts/run_signature.py b/scripts/run_signature.py index 9f77f0e78..067b992c6 100755 --- a/scripts/run_signature.py +++ b/scripts/run_signature.py @@ -4,6 +4,7 @@ import argparse import sys import os +import subprocess from typing import Literal if __name__ == "__main__": @@ -14,18 +15,18 @@ from test.regression.pysim import PySimulation # noqa: E402 -# def run_with_cocotb(benchmarks: list[str], traces: bool) -> bool: -# arglist = ["make", "-C", "test/regression/cocotb", "-f", "benchmark.Makefile", "--no-print-directory"] -# -# test_cases = ",".join(benchmarks) -# arglist += [f"TESTCASE={test_cases}"] -# -# if traces: -# 3 arglist += ["TRACES=1"] -# -# res = subprocess.run(arglist) -# -# return res.returncode == 0 +def run_with_cocotb(test_name: str, traces: bool, output: str) -> bool: + arglist = ["make", "-C", "test/regression/cocotb", "-f", "signature.Makefile", "--no-print-directory"] + + arglist += [f"TESTNAME={test_name}"] + arglist += [f"OUTPUT={output}"] + + if traces: + arglist += ["TRACES=1"] + + res = subprocess.run(arglist) + + return res.returncode == 0 def run_with_pysim(test_name: str, traces: bool, verbose: bool, output: str) -> bool: @@ -43,11 +44,11 @@ def run_with_pysim(test_name: str, traces: bool, verbose: bool, output: str) -> def run_test(test: str, backend: Literal["pysim", "cocotb"], traces: bool, verbose: bool, output: str) -> bool: - # if backend == "cocotb": - # return run_benchmarks_with_cocotb(test, traces) - # elif backend == "pysim": - return run_with_pysim(test, traces, verbose, output) - # return False + if backend == "cocotb": + return run_with_cocotb(test, traces, output) + elif backend == "pysim": + return run_with_pysim(test, traces, verbose, output) + return False def main(): diff --git a/test/regression/cocotb/signature.Makefile b/test/regression/cocotb/signature.Makefile new file mode 100644 index 000000000..e7da43e25 --- /dev/null +++ b/test/regression/cocotb/signature.Makefile @@ -0,0 +1,28 @@ +# Makefile + +# defaults +SIM ?= verilator +TOPLEVEL_LANG ?= verilog + +VERILOG_SOURCES += $(PWD)/../../../core.v +# use VHDL_SOURCES for VHDL files + +# TOPLEVEL is the name of the toplevel module in your Verilog or VHDL file +TOPLEVEL = top + +# MODULE is the basename of the Python test file +MODULE = signature_entrypoint + +SIM_BUILD = build/signature + +# Yosys/Amaranth borkedness workaround +ifeq ($(SIM),verilator) + EXTRA_ARGS += -Wno-CASEINCOMPLETE -Wno-CASEOVERLAP -Wno-WIDTHEXPAND -Wno-WIDTHTRUNC +endif + +ifeq ($(TRACES),1) + EXTRA_ARGS += --trace-fst --trace-structs +endif + +# include cocotb's make rules to take care of the simulator setup +include $(shell cocotb-config --makefiles)/Makefile.sim diff --git a/test/regression/cocotb/signature_entrypoint.py b/test/regression/cocotb/signature_entrypoint.py new file mode 100644 index 000000000..1508502fe --- /dev/null +++ b/test/regression/cocotb/signature_entrypoint.py @@ -0,0 +1,25 @@ +import os +import sys +import cocotb +from pathlib import Path + +top_dir = Path(__file__).parent.parent.parent.parent +sys.path.insert(0, str(top_dir)) + +from test.regression.cocotb import CocotbSimulation # noqa: E402 +from test.regression.signature import run_test # noqa: E402 + + +@cocotb.test() +async def do_test(dut): + cocotb.logging.getLogger().setLevel(cocotb.logging.INFO) + + test_name = os.environ["TESTNAME"] + if test_name is None: + raise RuntimeError("No ELF file provided") + + output = os.environ["OUTPUT"] + if output is None: + output = test_name + ".signature" + + await run_test(CocotbSimulation(dut), test_name, output) diff --git a/test/regression/signature.py b/test/regression/signature.py index 9a4e6815d..398427b3c 100644 --- a/test/regression/signature.py +++ b/test/regression/signature.py @@ -49,9 +49,9 @@ async def run_test(sim_backend: SimulationBackend, test_path: str, signature_pat success = await sim_backend.run(mem_model, timeout_cycles=50000) if not success: - raise RuntimeError("Simulation timed out") + raise RuntimeError(f"{test_path}: Simulation timed out") - print("Program execution finished!") + print(f"{test_path}: Program execution finished! Signature: {signature_path}") # generate signature file in riscv-torture format (used also by riscof) # 32-bit little endian memory dump from data between .begin_signature and .end_signature From 6c63e7ad7fd23a50c2c94cd2388e39b72023c9c6 Mon Sep 17 00:00:00 2001 From: Piotro Date: Thu, 5 Oct 2023 14:56:46 +0200 Subject: [PATCH 03/11] fixes --- scripts/run_signature.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/scripts/run_signature.py b/scripts/run_signature.py index 067b992c6..772cbb36e 100755 --- a/scripts/run_signature.py +++ b/scripts/run_signature.py @@ -16,7 +16,7 @@ def run_with_cocotb(test_name: str, traces: bool, output: str) -> bool: - arglist = ["make", "-C", "test/regression/cocotb", "-f", "signature.Makefile", "--no-print-directory"] + arglist = ["make", "-C", parent + "/test/regression/cocotb", "-f", "signature.Makefile", "--no-print-directory"] arglist += [f"TESTNAME={test_name}"] arglist += [f"OUTPUT={output}"] @@ -65,7 +65,12 @@ def main(): success = run_test(args.path, args.backend, args.trace, args.verbose, output) if not success: - print("Program execution failed") + print(f"{args.path}: Program execution failed") + + if output is not None: # create empty file on failure for checker scripts + with open(output, "w"): + pass + sys.exit(1) From da912cab1ab2b01e992b953d7f5d87828395db11 Mon Sep 17 00:00:00 2001 From: Piotro Date: Thu, 5 Oct 2023 16:00:49 +0200 Subject: [PATCH 04/11] Fix error reporting --- scripts/run_signature.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/scripts/run_signature.py b/scripts/run_signature.py index 772cbb36e..8b7060ff5 100755 --- a/scripts/run_signature.py +++ b/scripts/run_signature.py @@ -18,15 +18,18 @@ def run_with_cocotb(test_name: str, traces: bool, output: str) -> bool: arglist = ["make", "-C", parent + "/test/regression/cocotb", "-f", "signature.Makefile", "--no-print-directory"] + if os.path.isfile(output): + os.remove(output) + arglist += [f"TESTNAME={test_name}"] arglist += [f"OUTPUT={output}"] if traces: arglist += ["TRACES=1"] - res = subprocess.run(arglist) + subprocess.run(arglist) - return res.returncode == 0 + return os.path.isfile(output) # completed successfully if signature file was created def run_with_pysim(test_name: str, traces: bool, verbose: bool, output: str) -> bool: From c4acb21a264e884d182127c2f06fa7b2be22e67c Mon Sep 17 00:00:00 2001 From: Piotro Date: Thu, 5 Oct 2023 20:52:23 +0200 Subject: [PATCH 05/11] Fix segment loading --- test/regression/memory.py | 11 ++++++++--- test/regression/signature.py | 2 +- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/test/regression/memory.py b/test/regression/memory.py index 068a6edd5..d403c01c3 100644 --- a/test/regression/memory.py +++ b/test/regression/memory.py @@ -148,13 +148,18 @@ def load_segment(segment: Segment) -> RandomAccessMemory: data = segment.data() flags = SegmentFlags(0) - if flags_raw & P_FLAGS.PF_R == flags_raw & P_FLAGS.PF_R: + if flags_raw & P_FLAGS.PF_R: flags |= SegmentFlags.READ - if flags_raw & P_FLAGS.PF_W == flags_raw & P_FLAGS.PF_W: + if flags_raw & P_FLAGS.PF_W: flags |= SegmentFlags.WRITE - if flags_raw & P_FLAGS.PF_X == flags_raw & P_FLAGS.PF_X: + if flags_raw & P_FLAGS.PF_X: flags |= SegmentFlags.EXECUTABLE + # append safe memory region for instruction fetch prediction + seg_end += 0x10 + data += b"\0" * 0x10 + print(f"seg {hex(paddr)} {flags_raw} {flags}") + return RandomAccessMemory(range(seg_start, seg_end), flags, data) diff --git a/test/regression/signature.py b/test/regression/signature.py index 398427b3c..96b661199 100644 --- a/test/regression/signature.py +++ b/test/regression/signature.py @@ -46,7 +46,7 @@ async def run_test(sim_backend: SimulationBackend, test_path: str, signature_pat mem_segments.append(signature_ram) mem_model = CoreMemoryModel(mem_segments) - success = await sim_backend.run(mem_model, timeout_cycles=50000) + success = await sim_backend.run(mem_model, timeout_cycles=100000) if not success: raise RuntimeError(f"{test_path}: Simulation timed out") From dd1b762d51298d44bdc560bbc864f18cb8d3b0a2 Mon Sep 17 00:00:00 2001 From: Piotro Date: Thu, 5 Oct 2023 20:55:26 +0200 Subject: [PATCH 06/11] Add RVTEST enviorment configuration --- test/external/rvtest_env/link.ld | 40 +++++++++++++++++ test/external/rvtest_env/model_test.h | 64 +++++++++++++++++++++++++++ 2 files changed, 104 insertions(+) create mode 100644 test/external/rvtest_env/link.ld create mode 100644 test/external/rvtest_env/model_test.h diff --git a/test/external/rvtest_env/link.ld b/test/external/rvtest_env/link.ld new file mode 100644 index 000000000..4509f68a5 --- /dev/null +++ b/test/external/rvtest_env/link.ld @@ -0,0 +1,40 @@ +OUTPUT_ARCH( "riscv" ) +ENTRY(rvtest_entry_point) + +MEMORY +{ + text (rxai!w) : ORIGIN = 0x00000000, LENGTH = 2M + data (wxa!ri) : ORIGIN = 0x10000000, LENGTH = 1M + mmio (wa!rxi) : ORIGIN = 0x80000000, LENGTH = 1K + signature (wa!rxi) : ORIGIN = 0x81000000, LENGTH = 16K + +} + +PHDRS +{ + text PT_LOAD; + data_init PT_LOAD; + data PT_NULL; + mmio PT_LOAD; + signature PT_LOAD; +} + +SECTIONS +{ + .text.init : { *(.text.init) } >text AT>text :text + . = ALIGN(0x1000); + .text : { *(.text) } >text AT>text :text + + . = ALIGN(0x1000); + .data : { *(.data) } >data AT>data :data_init + .data.string : { *(.data.string)} >data AT>data :data_init + .bss : { *(.bss) } >data AT>data :data + + . = ALIGN(0x1000); + .hostmmio : { *(.hostmmio) } >mmio AT>mmio :mmio + + . = ALIGN(0x1000); + .signature : { *(.signature) } >signature AT>signature :signature + + _end = .; +} diff --git a/test/external/rvtest_env/model_test.h b/test/external/rvtest_env/model_test.h new file mode 100644 index 000000000..2b9b38a05 --- /dev/null +++ b/test/external/rvtest_env/model_test.h @@ -0,0 +1,64 @@ +#ifndef _COMPLIANCE_MODEL_H +#define _COMPLIANCE_MODEL_H +#define RVMODEL_DATA_SECTION \ + .pushsection .hostmmio,"aw",@progbits; \ + .align 8; .global tohost; tohost: .dword 0; \ + .align 8; .global fromhost; fromhost: .dword 0; \ + .popsection; \ + .align 8; .global begin_regstate; begin_regstate: \ + .word 128; \ + .align 8; .global end_regstate; end_regstate: \ + .word 4; + +//RV_COMPLIANCE_HALT +#define RVMODEL_HALT \ + li x1, 1; \ + write_tohost: \ + sw x1, tohost, t5; \ + j write_tohost; + +#define RVMODEL_BOOT + +//RV_COMPLIANCE_DATA_BEGIN +#define RVMODEL_DATA_BEGIN \ + RVMODEL_DATA_SECTION \ + .pushsection .signature,"aw",@progbits; \ + .align 2; \ + .global begin_signature; begin_signature: + +//RV_COMPLIANCE_DATA_END +#define RVMODEL_DATA_END \ + .global end_signature; end_signature: \ + .popsection; + +//RVTEST_IO_INIT +#define RVMODEL_IO_INIT +//RVTEST_IO_WRITE_STR +#define RVMODEL_IO_WRITE_STR(_R, _STR) +//RVTEST_IO_CHECK +#define RVMODEL_IO_CHECK() +//RVTEST_IO_ASSERT_GPR_EQ +#define RVMODEL_IO_ASSERT_GPR_EQ(_S, _R, _I) +//RVTEST_IO_ASSERT_SFPR_EQ +#define RVMODEL_IO_ASSERT_SFPR_EQ(_F, _R, _I) +//RVTEST_IO_ASSERT_DFPR_EQ +#define RVMODEL_IO_ASSERT_DFPR_EQ(_D, _R, _I) + +// empty macros to supress warnings +#define RVMODEL_SET_MSW_INT +#define RVMODEL_CLEAR_MSW_INT +#define RVMODEL_CLEAR_MTIMER_INT +#define RVMODEL_CLEAR_MEXT_INT +#define RVMODEL_CLR_MSW_INT +#define RVMODEL_CLR_MTIMER_INT +#define RVMODEL_CLR_MEXT_INT +#define RVMODEL_SET_SSW_INT +#define RVMODEL_CLR_SSW_INT +#define RVMODEL_CLR_STIMER_INT +#define RVMODEL_CLR_SEXT_INT +#define RVMODEL_SET_VSW_INT +#define RVMODEL_CLR_VSW_INT +#define RVMODEL_CLR_VTIMER_INT +#define RVMODEL_CLR_VEXT_INT + +#endif // _COMPLIANCE_MODEL_H From fdac4fd3e6282a8fd5d4b9a47f3910771532e374 Mon Sep 17 00:00:00 2001 From: Piotro Date: Thu, 5 Oct 2023 21:26:58 +0200 Subject: [PATCH 07/11] Remove debug line --- test/regression/memory.py | 1 - 1 file changed, 1 deletion(-) diff --git a/test/regression/memory.py b/test/regression/memory.py index d403c01c3..c15b636b4 100644 --- a/test/regression/memory.py +++ b/test/regression/memory.py @@ -158,7 +158,6 @@ def load_segment(segment: Segment) -> RandomAccessMemory: # append safe memory region for instruction fetch prediction seg_end += 0x10 data += b"\0" * 0x10 - print(f"seg {hex(paddr)} {flags_raw} {flags}") return RandomAccessMemory(range(seg_start, seg_end), flags, data) From 14aa970695b489c89268a7e5269bfd9e6aacf0a7 Mon Sep 17 00:00:00 2001 From: Piotro Date: Sat, 28 Oct 2023 16:22:05 +0200 Subject: [PATCH 08/11] fix typing --- scripts/run_signature.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/scripts/run_signature.py b/scripts/run_signature.py index 8b7060ff5..6acb4e311 100755 --- a/scripts/run_signature.py +++ b/scripts/run_signature.py @@ -16,7 +16,14 @@ def run_with_cocotb(test_name: str, traces: bool, output: str) -> bool: - arglist = ["make", "-C", parent + "/test/regression/cocotb", "-f", "signature.Makefile", "--no-print-directory"] + arglist = [ + "make", + "-C", + parent + "/" if parent else "" + "test/regression/cocotb", + "-f", + "signature.Makefile", + "--no-print-directory", + ] if os.path.isfile(output): os.remove(output) From 2dd30d5c4c2a282b47f4616817a6c440449dc3f3 Mon Sep 17 00:00:00 2001 From: Piotro Date: Sat, 28 Oct 2023 18:09:16 +0200 Subject: [PATCH 09/11] Align instruction sections to ICache lane size --- test/regression/memory.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/test/regression/memory.py b/test/regression/memory.py index 534cd0929..b184ac88e 100644 --- a/test/regression/memory.py +++ b/test/regression/memory.py @@ -5,6 +5,7 @@ from dataclasses import dataclass, replace from elftools.elf.constants import P_FLAGS from elftools.elf.elffile import ELFFile, Segment +from coreblocks.params.configurations import CoreConfiguration all = [ "ReplyStatus", @@ -155,9 +156,20 @@ def load_segment(segment: Segment, *, disable_write_protection: bool = False) -> if flags_raw & P_FLAGS.PF_X: flags |= SegmentFlags.EXECUTABLE - # append safe memory region for instruction fetch prediction - seg_end += 0x10 - data += b"\0" * 0x10 + if flags_raw & P_FLAGS.PF_X: + # align only instruction section to full icache lines + alignment = 2 ** CoreConfiguration().icache_block_size_bits + + def align_down(n: int) -> int: + return (n // alignment) * alignment + + align_front = seg_start - align_down(seg_start) + align_back = align_down(seg_end + alignment - 1) - seg_end + + data = b"\x00" * align_front + data + b"\x00" * align_back + + seg_start -= align_front + seg_end += align_back return RandomAccessMemory(range(seg_start, seg_end), flags, data) From 23cf60c3b97f83f690446ced5d48778bc0d2f605 Mon Sep 17 00:00:00 2001 From: Piotro Date: Tue, 31 Oct 2023 16:03:30 +0100 Subject: [PATCH 10/11] Sync coreblocks env directory --- test/external/{rvtest_env => riscof/coreblocks/env}/link.ld | 0 test/external/{rvtest_env => riscof/coreblocks/env}/model_test.h | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename test/external/{rvtest_env => riscof/coreblocks/env}/link.ld (100%) rename test/external/{rvtest_env => riscof/coreblocks/env}/model_test.h (100%) diff --git a/test/external/rvtest_env/link.ld b/test/external/riscof/coreblocks/env/link.ld similarity index 100% rename from test/external/rvtest_env/link.ld rename to test/external/riscof/coreblocks/env/link.ld diff --git a/test/external/rvtest_env/model_test.h b/test/external/riscof/coreblocks/env/model_test.h similarity index 100% rename from test/external/rvtest_env/model_test.h rename to test/external/riscof/coreblocks/env/model_test.h From be074c47f39b3afd2bc87536c480e7c0d5c3e8c8 Mon Sep 17 00:00:00 2001 From: Piotro Date: Tue, 31 Oct 2023 16:15:53 +0100 Subject: [PATCH 11/11] Move align to utils, change align assignments --- coreblocks/utils/utils.py | 21 +++++++++++++++++++++ test/regression/memory.py | 16 +++++++--------- test/utils/test_utils.py | 25 ++++++++++++++++++++++++- 3 files changed, 52 insertions(+), 10 deletions(-) diff --git a/coreblocks/utils/utils.py b/coreblocks/utils/utils.py index 31fc830ab..2f7b78cb6 100644 --- a/coreblocks/utils/utils.py +++ b/coreblocks/utils/utils.py @@ -15,6 +15,7 @@ "OneHotSwitch", "flatten_signals", "align_to_power_of_two", + "align_down_to_power_of_two", "bits_from_int", "ModuleConnector", "silence_mustuse", @@ -377,6 +378,26 @@ def align_to_power_of_two(num: int, power: int) -> int: return (num & ~mask) + 2**power +def align_down_to_power_of_two(num: int, power: int) -> int: + """Rounds down a number to the given power of two. + + Parameters + ---------- + num : int + The number to align. + power : int + The power of two to align to. + + Returns + ------- + int + The aligned number. + """ + mask = 2**power - 1 + + return num & ~mask + + def bits_from_int(num: int, lower: int, length: int): """Returns [`lower`:`lower`+`length`) bits from integer `num`.""" return (num >> lower) & ((1 << (length)) - 1) diff --git a/test/regression/memory.py b/test/regression/memory.py index b184ac88e..40599d044 100644 --- a/test/regression/memory.py +++ b/test/regression/memory.py @@ -6,6 +6,7 @@ from elftools.elf.constants import P_FLAGS from elftools.elf.elffile import ELFFile, Segment from coreblocks.params.configurations import CoreConfiguration +from coreblocks.utils.utils import align_to_power_of_two, align_down_to_power_of_two all = [ "ReplyStatus", @@ -158,18 +159,15 @@ def load_segment(segment: Segment, *, disable_write_protection: bool = False) -> if flags_raw & P_FLAGS.PF_X: # align only instruction section to full icache lines - alignment = 2 ** CoreConfiguration().icache_block_size_bits + align_bits = CoreConfiguration().icache_block_size_bits - def align_down(n: int) -> int: - return (n // alignment) * alignment + align_data_front = seg_start - align_down_to_power_of_two(seg_start, align_bits) + align_data_back = align_to_power_of_two(seg_end, align_bits) - seg_end - align_front = seg_start - align_down(seg_start) - align_back = align_down(seg_end + alignment - 1) - seg_end + data = b"\x00" * align_data_front + data + b"\x00" * align_data_back - data = b"\x00" * align_front + data + b"\x00" * align_back - - seg_start -= align_front - seg_end += align_back + seg_start = align_down_to_power_of_two(seg_start, align_bits) + seg_end = align_to_power_of_two(seg_end, align_bits) return RandomAccessMemory(range(seg_start, seg_end), flags, data) diff --git a/test/utils/test_utils.py b/test/utils/test_utils.py index fa2f63bc1..6a948f490 100644 --- a/test/utils/test_utils.py +++ b/test/utils/test_utils.py @@ -3,7 +3,13 @@ from amaranth import * from test.common import * -from coreblocks.utils import align_to_power_of_two, popcount, count_leading_zeros, count_trailing_zeros +from coreblocks.utils import ( + align_to_power_of_two, + align_down_to_power_of_two, + popcount, + count_leading_zeros, + count_trailing_zeros, +) from parameterized import parameterized_class @@ -29,6 +35,23 @@ def test_align_to_power_of_two(self): out = align_to_power_of_two(num, power) self.assertEqual(expected, out) + def test_align_down_to_power_of_two(self): + test_cases = [ + (3, 1, 2), + (3, 0, 3), + (3, 3, 0), + (8, 3, 8), + (8, 2, 8), + (33, 5, 32), + (29, 5, 0), + (29, 1, 28), + (29, 3, 24), + ] + + for num, power, expected in test_cases: + out = align_down_to_power_of_two(num, power) + self.assertEqual(expected, out) + class PopcountTestCircuit(Elaboratable): def __init__(self, size: int):