From 6cf6e75576145607e07c017cc6ed53460964e24f Mon Sep 17 00:00:00 2001 From: Philipp van Kempen Date: Thu, 23 Mar 2023 06:58:45 +0100 Subject: [PATCH 01/18] support is_float_reg attr --- m2isar/backends/etiss/architecture_writer.py | 5 +- .../templates/etiss_arch_specific_h.mako | 47 +++++++++++++++++++ m2isar/backends/viewer/viewer.py | 1 + .../coredsl2/architecture_model_builder.py | 2 + m2isar/metamodel/arch.py | 4 ++ 5 files changed, 58 insertions(+), 1 deletion(-) diff --git a/m2isar/backends/etiss/architecture_writer.py b/m2isar/backends/etiss/architecture_writer.py index 6bb671d3..2899c162 100644 --- a/m2isar/backends/etiss/architecture_writer.py +++ b/m2isar/backends/etiss/architecture_writer.py @@ -122,11 +122,13 @@ def write_arch_cpp(core: arch.CoreDef, start_time: str, output_path: pathlib.Pat # generate main register file names for ETISS's 'char* reg_name[]' reg_names = [f"{core.main_reg_file.name}{n}" for n in range(core.main_reg_file.data_range.length)] + # TODO(annnnna42): add float reg names (F0-F31) here # if main register file entries have aliases optionally use these for 'char* reg_name[]' if aliased_regnames: for child in core.main_reg_file.children: reg_names[child.range.lower] = child.name + # TODO(annnnna42): add float reg aliases here txt = arch_header_template.render( start_time=start_time, @@ -165,7 +167,8 @@ def write_arch_specific_header(core: arch.CoreDef, start_time: str, output_path: txt = arch_header_template.render( start_time=start_time, core_name=core.name, - main_reg=core.main_reg_file + main_reg=core.main_reg_file, + float_reg=core.float_reg_file ) with open(output_path / f"{core.name}ArchSpecificImp.h", "w", encoding="utf-8") as f: diff --git a/m2isar/backends/etiss/templates/etiss_arch_specific_h.mako b/m2isar/backends/etiss/templates/etiss_arch_specific_h.mako index d9c9881b..c3967667 100644 --- a/m2isar/backends/etiss/templates/etiss_arch_specific_h.mako +++ b/m2isar/backends/etiss/templates/etiss_arch_specific_h.mako @@ -71,6 +71,53 @@ protected: } }; +% if float_reg: +class FloatRegField_${core_name} : public etiss::VirtualStruct::Field{ +private: + const unsigned gprid_; +public: + FloatRegField_${core_name}(etiss::VirtualStruct & parent,unsigned gprid) + : Field(parent, + std::string("${float_reg.name}")+etiss::toString(gprid), + std::string("${float_reg.name}")+etiss::toString(gprid), + R|W, + ${int(float_reg.size / 8)} + ), + gprid_(gprid) + {} + + FloatRegField_${core_name}(etiss::VirtualStruct & parent, std::string name, unsigned gprid) + : Field(parent, + name, + name, + R|W, + ${int(float_reg.size / 8)} + ), + gprid_(gprid) + {} + + virtual ~FloatRegField_${core_name}(){} + +protected: + virtual uint64_t _read() const { + % if len(main_reg.children) > 0: + return (uint64_t) *((${core_name}*)parent_.structure_)->${main_reg.name}[gprid_]; + % else: + return (uint64_t) ((${core_name}*)parent_.structure_)->${main_reg.name}[gprid_]; + % endif + } + + virtual void _write(uint64_t val) { + etiss::log(etiss::VERBOSE, "write to ETISS cpu state", name_, val); + % if len(main_reg.children) > 0: + *((${core_name}*)parent_.structure_)->${main_reg.name}[gprid_] = (etiss_uint${main_reg.size}) val; + % else: + ((${core_name}*)parent_.structure_)->${main_reg.name}[gprid_] = (etiss_uint${main_reg.size}) val; + % endif + } +}; +% endif + class pcField_${core_name} : public etiss::VirtualStruct::Field{ public: pcField_${core_name}(etiss::VirtualStruct & parent) diff --git a/m2isar/backends/viewer/viewer.py b/m2isar/backends/viewer/viewer.py index 4178d3c8..80782384 100644 --- a/m2isar/backends/viewer/viewer.py +++ b/m2isar/backends/viewer/viewer.py @@ -122,6 +122,7 @@ def main(): # add auxillary attributes tree.insert(core_id, tk.END, text="Main Memory Object", values=(core_def.main_memory,)) tree.insert(core_id, tk.END, text="Main Register File Object", values=(core_def.main_reg_file,)) + # TODO: float_reg_file? tree.insert(core_id, tk.END, text="PC Memory Object", values=(core_def.pc_memory,)) # add functions to tree diff --git a/m2isar/frontends/coredsl2/architecture_model_builder.py b/m2isar/frontends/coredsl2/architecture_model_builder.py index 22bcfd6e..3988be8e 100644 --- a/m2isar/frontends/coredsl2/architecture_model_builder.py +++ b/m2isar/frontends/coredsl2/architecture_model_builder.py @@ -383,6 +383,8 @@ def visitDeclaration(self, ctx: CoreDSL2Parser.DeclarationContext): if arch.MemoryAttribute.IS_MAIN_REG in attributes: self._main_reg_file = m + if arch.MemoryAttribute.IS_FLOAT_REG in attributes: + self._float_reg_file = m self._memories[name] = m ret_decls.append(m) diff --git a/m2isar/metamodel/arch.py b/m2isar/metamodel/arch.py index adcd7e9f..7d536b7d 100644 --- a/m2isar/metamodel/arch.py +++ b/m2isar/metamodel/arch.py @@ -198,6 +198,7 @@ class MemoryAttribute(Enum): IS_PC = auto() IS_MAIN_MEM = auto() IS_MAIN_REG = auto() + IS_FLOAT_REG = auto() DELETE = auto() ETISS_CAN_FAIL = auto() ETISS_IS_GLOBAL_IRQ_EN = auto() @@ -572,6 +573,7 @@ def __init__(self, name, contributing_types: "list[str]", template: str, constan self.instructions = instructions self.instr_classes = instr_classes self.main_reg_file = None + self.float_reg_file = None self.main_memory = None self.pc_memory = None self.global_irq_en_memory = None @@ -595,6 +597,8 @@ def __init__(self, name, contributing_types: "list[str]", template: str, constan for mem in itertools.chain(self.memories.values(), self.memory_aliases.values()): if MemoryAttribute.IS_MAIN_REG in mem.attributes: self.main_reg_file = mem + if MemoryAttribute.IS_FLOAT_REG in mem.attributes: + self.float_reg_file = mem elif MemoryAttribute.IS_PC in mem.attributes: self.pc_memory = mem elif MemoryAttribute.IS_MAIN_MEM in mem.attributes: From f60a42049ccd6a93852621d3567a787a42c9262b Mon Sep 17 00:00:00 2001 From: Philipp van Kempen Date: Wed, 19 Apr 2023 14:37:32 +0200 Subject: [PATCH 02/18] fix typos in FloatRegfields --- .../backends/etiss/templates/etiss_arch_specific_h.mako | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/m2isar/backends/etiss/templates/etiss_arch_specific_h.mako b/m2isar/backends/etiss/templates/etiss_arch_specific_h.mako index c3967667..046d54ca 100644 --- a/m2isar/backends/etiss/templates/etiss_arch_specific_h.mako +++ b/m2isar/backends/etiss/templates/etiss_arch_specific_h.mako @@ -101,18 +101,18 @@ public: protected: virtual uint64_t _read() const { % if len(main_reg.children) > 0: - return (uint64_t) *((${core_name}*)parent_.structure_)->${main_reg.name}[gprid_]; + return (uint64_t) *((${core_name}*)parent_.structure_)->${float_reg.name}[gprid_]; % else: - return (uint64_t) ((${core_name}*)parent_.structure_)->${main_reg.name}[gprid_]; + return (uint64_t) ((${core_name}*)parent_.structure_)->${float_reg.name}[gprid_]; % endif } virtual void _write(uint64_t val) { etiss::log(etiss::VERBOSE, "write to ETISS cpu state", name_, val); % if len(main_reg.children) > 0: - *((${core_name}*)parent_.structure_)->${main_reg.name}[gprid_] = (etiss_uint${main_reg.size}) val; + *((${core_name}*)parent_.structure_)->${float_reg.name}[gprid_] = (etiss_uint${float_reg.size}) val; % else: - ((${core_name}*)parent_.structure_)->${main_reg.name}[gprid_] = (etiss_uint${main_reg.size}) val; + ((${core_name}*)parent_.structure_)->${float_reg.name}[gprid_] = (etiss_uint${float_reg.size}) val; % endif } }; From 814466aa824d8be3abaf7ccb566f277acee9ad96 Mon Sep 17 00:00:00 2001 From: Philipp van Kempen Date: Tue, 16 Sep 2025 19:19:38 +0200 Subject: [PATCH 03/18] float_reg_file fixes --- m2isar/backends/etiss/architecture_writer.py | 8 ++++++-- .../etiss/templates/etiss_arch_gdbcore.mako | 12 ++++++++++++ .../etiss/templates/etiss_arch_specific_cpp.mako | 16 ++++++++++++++++ .../coredsl2/architecture_model_builder.py | 2 ++ 4 files changed, 36 insertions(+), 2 deletions(-) diff --git a/m2isar/backends/etiss/architecture_writer.py b/m2isar/backends/etiss/architecture_writer.py index 2899c162..a17bcc0b 100644 --- a/m2isar/backends/etiss/architecture_writer.py +++ b/m2isar/backends/etiss/architecture_writer.py @@ -122,6 +122,8 @@ def write_arch_cpp(core: arch.CoreDef, start_time: str, output_path: pathlib.Pat # generate main register file names for ETISS's 'char* reg_name[]' reg_names = [f"{core.main_reg_file.name}{n}" for n in range(core.main_reg_file.data_range.length)] + if core.float_reg_file is not None: + reg_names += [f"{core.float_reg_file.name}{n}" for n in range(core.float_reg_file.data_range.length)] # TODO(annnnna42): add float reg names (F0-F31) here # if main register file entries have aliases optionally use these for 'char* reg_name[]' @@ -168,7 +170,7 @@ def write_arch_specific_header(core: arch.CoreDef, start_time: str, output_path: start_time=start_time, core_name=core.name, main_reg=core.main_reg_file, - float_reg=core.float_reg_file + float_reg=core.float_reg_file, ) with open(output_path / f"{core.name}ArchSpecificImp.h", "w", encoding="utf-8") as f: @@ -219,6 +221,7 @@ def write_arch_specific_cpp(core: arch.CoreDef, start_time: str, output_path: pa start_time=start_time, core_name=core.name, main_reg=core.main_reg_file, + float_reg=core.float_reg_file, irq_en_reg=core.irq_en_memory, irq_pending_reg=core.irq_pending_memory, global_irq_en_reg=core.global_irq_en_memory, @@ -238,7 +241,8 @@ def write_arch_gdbcore(core: arch.CoreDef, start_time: str, output_path: pathlib txt = arch_header_template.render( start_time=start_time, core_name=core.name, - main_reg=core.main_reg_file + main_reg=core.main_reg_file, + float_reg=core.float_reg_file ) with open(output_path / f"{core.name}GDBCore.h", "w", encoding="utf-8") as f: diff --git a/m2isar/backends/etiss/templates/etiss_arch_gdbcore.mako b/m2isar/backends/etiss/templates/etiss_arch_gdbcore.mako index dd905d16..31dcb1c4 100644 --- a/m2isar/backends/etiss/templates/etiss_arch_gdbcore.mako +++ b/m2isar/backends/etiss/templates/etiss_arch_gdbcore.mako @@ -46,6 +46,18 @@ public: ss << "${main_reg.name}" << index; return ss.str(); } + % if float_reg is not None: + if ((${main_reg.range.length} < index) && (index < ${main_reg.range.length + float_reg.range.length + 1})){ + std::stringstream ss; + ss << "${float_reg.name}" << (index - ${main_reg.range.length + 1}); + return ss.str(); + } + if ((${main_reg.range.length + float_reg.range.length} < index) && (index < ${main_reg.range.length + float_reg.range.length + 5})){ // FCSR + std::stringstream ss; + ss << "CSR" << (index - ${main_reg.range.length + float_reg.range.length + 1}); + return ss.str(); + } + % endif switch (index){ case ${main_reg.range.length}: return "instructionPointer"; diff --git a/m2isar/backends/etiss/templates/etiss_arch_specific_cpp.mako b/m2isar/backends/etiss/templates/etiss_arch_specific_cpp.mako index 996ad759..62fb48fe 100644 --- a/m2isar/backends/etiss/templates/etiss_arch_specific_cpp.mako +++ b/m2isar/backends/etiss/templates/etiss_arch_specific_cpp.mako @@ -149,6 +149,22 @@ std::shared_ptr ${core_name}Arch::getVirtualStruct(ETISS_C } ret->addField(new pcField_${core_name}(*ret)); + + % if float_reg is not None: + // FPRs + for (uint32_t i = 0; i < ${float_reg.range.length}; ++i){ + ret->addField(new FloatRegField_${core_name}(*ret,i)); + } + % endif + % if csr_reg is not None: + // CSRs + % if float_reg is not None: + // FCSR + ret->addField(new CSRField_${core_name}(*ret,1)); + ret->addField(new CSRField_${core_name}(*ret,2)); + ret->addField(new CSRField_${core_name}(*ret,3)); + % endif + % endif return ret; } diff --git a/m2isar/frontends/coredsl2/architecture_model_builder.py b/m2isar/frontends/coredsl2/architecture_model_builder.py index 3988be8e..6f914788 100644 --- a/m2isar/frontends/coredsl2/architecture_model_builder.py +++ b/m2isar/frontends/coredsl2/architecture_model_builder.py @@ -34,6 +34,7 @@ class ArchitectureModelBuilder(CoreDSL2Visitor): _overwritten_instrs: "list[tuple[arch.Instruction, arch.Instruction]]" _instr_classes: "set[int]" _main_reg_file: Union[arch.Memory, None] + _float_reg_file: Union[arch.Memory, None] def __init__(self): super().__init__() @@ -49,6 +50,7 @@ def __init__(self): self._overwritten_instrs = [] self._instr_classes = set() self._main_reg_file = None + self._float_reg_file = None def visitBit_field(self, ctx: CoreDSL2Parser.Bit_fieldContext): """Generate a bit field (instruction parameter in encoding).""" From 261032aafbc06dc965f27a083336e263d388d58f Mon Sep 17 00:00:00 2001 From: Philipp van Kempen Date: Tue, 16 Sep 2025 19:20:18 +0200 Subject: [PATCH 04/18] introduce CSRField and IS_CSR_REG --- m2isar/backends/etiss/architecture_writer.py | 1 + .../templates/etiss_arch_specific_h.mako | 43 +++++++++++++++++++ .../coredsl2/architecture_model_builder.py | 4 ++ m2isar/metamodel/arch.py | 4 ++ 4 files changed, 52 insertions(+) diff --git a/m2isar/backends/etiss/architecture_writer.py b/m2isar/backends/etiss/architecture_writer.py index a17bcc0b..a2be8bd3 100644 --- a/m2isar/backends/etiss/architecture_writer.py +++ b/m2isar/backends/etiss/architecture_writer.py @@ -171,6 +171,7 @@ def write_arch_specific_header(core: arch.CoreDef, start_time: str, output_path: core_name=core.name, main_reg=core.main_reg_file, float_reg=core.float_reg_file, + csr_reg=core.csr_reg_file ) with open(output_path / f"{core.name}ArchSpecificImp.h", "w", encoding="utf-8") as f: diff --git a/m2isar/backends/etiss/templates/etiss_arch_specific_h.mako b/m2isar/backends/etiss/templates/etiss_arch_specific_h.mako index 046d54ca..b0505fcc 100644 --- a/m2isar/backends/etiss/templates/etiss_arch_specific_h.mako +++ b/m2isar/backends/etiss/templates/etiss_arch_specific_h.mako @@ -19,6 +19,8 @@ #ifndef ETISS_${core_name}Arch_${core_name}ARCHSPECIFICIMP_H_ #define ETISS_${core_name}Arch_${core_name}ARCHSPECIFICIMP_H_ +#include "${core_name}Funcs.h" + /** @brief VirtualStruct for ${core_name} architecture to faciliate register acess @@ -118,6 +120,47 @@ protected: }; % endif + +% if csr_reg: +class CSRField_${core_name} : public etiss::VirtualStruct::Field{ +private: + const unsigned gprid_; +public: + CSRField_${core_name}(etiss::VirtualStruct & parent,unsigned gprid) + : Field(parent, + std::string("${csr_reg.name}")+etiss::toString(gprid), + std::string("${csr_reg.name}")+etiss::toString(gprid), + R|W, + ${int(float_reg.size / 8)} + ), + gprid_(gprid) + {} + + CSRField_${core_name}(etiss::VirtualStruct & parent, std::string name, unsigned gprid) + : Field(parent, + name, + name, + R|W, + ${int(float_reg.size / 8)} + ), + gprid_(gprid) + {} + + virtual ~CSRField_${core_name}(){} + +protected: + virtual uint64_t _read() const { + return (uint64_t) ${core_name}_csr_read((ETISS_CPU*)parent_.structure_, nullptr, nullptr, (etiss_uint${float_reg.size}) gprid_); + } + + virtual void _write(uint64_t val) { + etiss::log(etiss::VERBOSE, "write to ETISS cpu state", name_, val); + ${core_name}_csr_write((ETISS_CPU*)parent_.structure_, nullptr, nullptr, gprid_, (etiss_uint${float_reg.size}) val); + } +}; +% endif + + class pcField_${core_name} : public etiss::VirtualStruct::Field{ public: pcField_${core_name}(etiss::VirtualStruct & parent) diff --git a/m2isar/frontends/coredsl2/architecture_model_builder.py b/m2isar/frontends/coredsl2/architecture_model_builder.py index 6f914788..78d1997c 100644 --- a/m2isar/frontends/coredsl2/architecture_model_builder.py +++ b/m2isar/frontends/coredsl2/architecture_model_builder.py @@ -35,6 +35,7 @@ class ArchitectureModelBuilder(CoreDSL2Visitor): _instr_classes: "set[int]" _main_reg_file: Union[arch.Memory, None] _float_reg_file: Union[arch.Memory, None] + _csr_reg_file: Union[arch.Memory, None] def __init__(self): super().__init__() @@ -51,6 +52,7 @@ def __init__(self): self._instr_classes = set() self._main_reg_file = None self._float_reg_file = None + self._csr_reg_file = None def visitBit_field(self, ctx: CoreDSL2Parser.Bit_fieldContext): """Generate a bit field (instruction parameter in encoding).""" @@ -387,6 +389,8 @@ def visitDeclaration(self, ctx: CoreDSL2Parser.DeclarationContext): self._main_reg_file = m if arch.MemoryAttribute.IS_FLOAT_REG in attributes: self._float_reg_file = m + if arch.MemoryAttribute.IS_CSR_REG in attributes or name.upper() == "CSR": + self._csr_reg_file = m self._memories[name] = m ret_decls.append(m) diff --git a/m2isar/metamodel/arch.py b/m2isar/metamodel/arch.py index 7d536b7d..1582cd59 100644 --- a/m2isar/metamodel/arch.py +++ b/m2isar/metamodel/arch.py @@ -199,6 +199,7 @@ class MemoryAttribute(Enum): IS_MAIN_MEM = auto() IS_MAIN_REG = auto() IS_FLOAT_REG = auto() + IS_CSR_REG = auto() DELETE = auto() ETISS_CAN_FAIL = auto() ETISS_IS_GLOBAL_IRQ_EN = auto() @@ -574,6 +575,7 @@ def __init__(self, name, contributing_types: "list[str]", template: str, constan self.instr_classes = instr_classes self.main_reg_file = None self.float_reg_file = None + self.csr_reg_file = None self.main_memory = None self.pc_memory = None self.global_irq_en_memory = None @@ -599,6 +601,8 @@ def __init__(self, name, contributing_types: "list[str]", template: str, constan self.main_reg_file = mem if MemoryAttribute.IS_FLOAT_REG in mem.attributes: self.float_reg_file = mem + if MemoryAttribute.IS_CSR_REG in mem.attributes or mem.name.upper() == "CSR": + self.csr_reg_file = mem elif MemoryAttribute.IS_PC in mem.attributes: self.pc_memory = mem elif MemoryAttribute.IS_MAIN_MEM in mem.attributes: From ab479db3808af823a5c8410a16360baa38eb015e Mon Sep 17 00:00:00 2001 From: Philipp van Kempen Date: Tue, 16 Sep 2025 19:20:54 +0200 Subject: [PATCH 05/18] m2isar/backends/etiss/templates/etiss_arch_cmake.mako: add code to copy xml files to etiss build & install dir --- .../etiss/templates/etiss_arch_cmake.mako | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/m2isar/backends/etiss/templates/etiss_arch_cmake.mako b/m2isar/backends/etiss/templates/etiss_arch_cmake.mako index 338bb75f..f4acc475 100644 --- a/m2isar/backends/etiss/templates/etiss_arch_cmake.mako +++ b/m2isar/backends/etiss/templates/etiss_arch_cmake.mako @@ -33,4 +33,17 @@ add_custom_command( ) INSTALL(FILES "$${}{CMAKE_CURRENT_LIST_DIR}/$${}{PROJECT_NAME}Funcs.h" DESTINATION "include/jit/Arch/$${}{PROJECT_NAME}") -ETISSPluginArch($${}{PROJECT_NAME}) \ No newline at end of file +# handle gdbserver xml files +if(EXISTS "$${}{CMAKE_CURRENT_SOURCE_DIR}/xml") + add_custom_target(copy_$${}{PROJECT_NAME}_xml ALL + COMMAND $${}{CMAKE_COMMAND} -E make_directory + "$${}{CMAKE_CURRENT_LIST_DIR}/xml" + COMMAND $${}{CMAKE_COMMAND} -E copy_directory + "$${}{CMAKE_CURRENT_LIST_DIR}/xml" + "$${}{ETISS_BINARY_DIR}/xml/$${}{PROJECT_NAME}" + ) + add_dependencies($${}{PROJECT_NAME} copy_$${}{PROJECT_NAME}_xml) + install(DIRECTORY $${}{CMAKE_CURRENT_SOURCE_DIR}/xml/ DESTINATION xml/$${}{PROJECT_NAME}) +endif() + +ETISSPluginArch($${}{PROJECT_NAME}) From 236aebe4285a8a4c9921b52c59fb18c67e000483 Mon Sep 17 00:00:00 2001 From: Philipp van Kempen Date: Wed, 17 Sep 2025 12:42:53 +0200 Subject: [PATCH 06/18] m2isar/backends/etiss/writer.py: parse gdb xml descriptions (wip) --- m2isar/backends/etiss/writer.py | 99 +++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) diff --git a/m2isar/backends/etiss/writer.py b/m2isar/backends/etiss/writer.py index bd1b41ff..91121577 100755 --- a/m2isar/backends/etiss/writer.py +++ b/m2isar/backends/etiss/writer.py @@ -8,6 +8,7 @@ """Main entrypoint for the etiss_writer program.""" +import re import argparse import logging import pathlib @@ -85,6 +86,7 @@ def setup(): help="Force end translation blocks on no instructions, uncoditional jumps or all jumps.") parser.add_argument("--coverage", action=BooleanOptionalAction, default=False, help="Generate coverage tracking code into model.") parser.add_argument("--log", default="info", choices=["critical", "error", "warning", "info", "debug"]) + parser.add_argument("--gdb-xml-descr", nargs="+", default=[]) args = parser.parse_args() # configure logging @@ -125,11 +127,55 @@ def setup(): return (model_obj.cores, logger, output_base_path, spec_name, start_time, args) + +def process_xml_descr(path): + import xml.etree.ElementTree as ET + from xml.etree import ElementTree, ElementInclude + # print("process_xml_descr", path) + tree = ElementTree.parse(path) + # print("tree", tree) + root = tree.getroot() + # print("root", root) + parent = pathlib.Path(path).parent + def custom_loader(href, parse, encoding=None): + if not pathlib.Path(href).is_file(): + href = parent / href + if parse == "xml": + with open(href, 'rb') as file: + data = ElementTree.parse(file).getroot() + else: + if not encoding: + encoding = 'UTF-8' + with open(href, 'r', encoding=encoding) as file: + data = file.read() + return data + ElementInclude.include(root, loader=custom_loader) + mapping = {} + num = 0 + for node in tree.iter(): + if node.tag == "reg": + # print("node", node.tag, node.attrib) + num = int(node.attrib.get("regnum", num + 1)) + name = node.attrib["name"] + sz = int(node.attrib["bitsize"]) + mapping[num] = (name, sz) + # print("mapping", mapping) + # input(">>>") + return mapping + def main(): """etiss_writer main entrypoint function.""" # setup etiss writer cores, logger, output_base_path, spec_name, start_time, args = setup() + descr_mapping = {} + for descr in args.gdb_xml_descr: + assert ":" in descr + core, xml_path = descr.split(":", 1) + print("core", core) + assert core in cores + mapping = process_xml_descr(xml_path) + descr_mapping[core] = mapping # preprocess all models for core_name, core in cores.items(): @@ -153,6 +199,59 @@ def main(): # generate each core in the model for core_name, core in cores.items(): logger.info("processing model %s", core_name) + # print("core.memories", core.memories) + # print("core.memory_aliases", core.memory_aliases) + mapping = descr_mapping.get(core_name) + # print("mapping", mapping) + # assert mapping is not None + if mapping is not None: + default_aliases = {"zero": "x0", "ra": "x1", "sp": "x2", "gp": "x3", "tp": "x4", "t0": "x5", "t1": "x6", "t2": "x7", "s0": "x8", "fp": "x8", "s1": "x9", "a0": "x10", "a1": "x11", "a2": "x12", "a3": "x13", "a4": "x14", "a5": "x15", "a6": "x16", "a7": "x17", "s2": "x18", "s3": "x19", "s4": "x20", "s5": "x21", "s6": "x22", "s7": "x23", "s8": "x24", "s9": "x25", "s10": "x26", "s11": "x27", "t3": "x28", "t4": "x29", "t5": "x30", "t6": "x31"} + def resolve_reg(name, mems, aliases): + split_name = lambda s: (m.group(1), int(m.group(2))) if (m:=re.fullmatch(r'([a-zA-Z]+)(\d+)', s)) else None + # print("resolve_reg", name) + idx = None + ret = mems.get(name, mems.get(name.lower(), mems.get(name.upper()))) + if ret is None: + ret = aliases.get(name, aliases.get(name.lower(), aliases.get(name.upper()))) + if ret is None: + splitted = split_name(name) + if splitted is not None: + # print("splitted", splitted) + name, idx = splitted + # print("name", name) + # print("idx", idx) + ret = mems.get(name, mems.get(name.lower(), mems.get(name.upper()))) + # print("ret", ret) + if ret is None: + ret = aliases.get(name, aliases.get(name.lower(), aliases.get(name.upper()))) + # input("?") + if ret is None: + new_name = default_aliases.get(name, default_aliases.get(name.lower(), default_aliases.get(name.upper()))) + if new_name is not None: + return resolve_reg(new_name, mems, aliases) + return ret, idx + for regnum, data in mapping.items(): + # print("regnum,data", regnum, data) + name, sz = data + resolved = resolve_reg(name, core.memories, core.memory_aliases) + # print("resolved", resolved) + # print("resolved[0]", dir(resolved[0])) + # print("resolved[0].children", resolved[0].children) + # print("resolved[0].parent", resolved[0].parent) + # print("resolved[0].range", resolved[0].range) + # print("resolved[0].size", resolved[0].size) + assert resolved is not None + if resolved is not None: + mem, idx = resolved + assert mem is not None + if mem.parent is not None: # alias + rng = mem.range + assert rng.length == 1 + assert idx is None + idx = rng.lower + mem = mem.parent + assert mem.size == sz + print("regnum,mem,idx", regnum, mem, idx) # create output files path output_path = output_base_path / spec_name / core_name From d5f5ffe219b13333ca0ee6ed5b9631b10df6522b Mon Sep 17 00:00:00 2001 From: Philipp van Kempen Date: Wed, 17 Sep 2025 18:46:45 +0200 Subject: [PATCH 07/18] add automated generation of virtualstruct fields based on aliases and gdb mapping based on XML descr --- m2isar/backends/etiss/architecture_writer.py | 10 +- .../etiss/templates/etiss_arch_gdbcore.mako | 8 + .../templates/etiss_arch_specific_cpp.mako | 33 ++- m2isar/backends/etiss/virtualstruct_utils.py | 217 ++++++++++++++++++ m2isar/backends/etiss/writer.py | 104 +-------- 5 files changed, 251 insertions(+), 121 deletions(-) create mode 100644 m2isar/backends/etiss/virtualstruct_utils.py diff --git a/m2isar/backends/etiss/architecture_writer.py b/m2isar/backends/etiss/architecture_writer.py index a2be8bd3..7966c650 100644 --- a/m2isar/backends/etiss/architecture_writer.py +++ b/m2isar/backends/etiss/architecture_writer.py @@ -177,7 +177,7 @@ def write_arch_specific_header(core: arch.CoreDef, start_time: str, output_path: with open(output_path / f"{core.name}ArchSpecificImp.h", "w", encoding="utf-8") as f: f.write(txt) -def write_arch_specific_cpp(core: arch.CoreDef, start_time: str, output_path: pathlib.Path): +def write_arch_specific_cpp(core: arch.CoreDef, start_time: str, output_path: pathlib.Path, virtualstruct_regs: dict): arch_header_template = Template(filename=str(template_dir/'etiss_arch_specific_cpp.mako')) error_fn = None @@ -228,13 +228,14 @@ def write_arch_specific_cpp(core: arch.CoreDef, start_time: str, output_path: pa global_irq_en_reg=core.global_irq_en_memory, global_irq_en_mask=global_irq_en_mask, error_callbacks=error_callbacks, - error_fn=error_fn + error_fn=error_fn, + virtualstruct_regs=virtualstruct_regs, ) with open(output_path / f"{core.name}ArchSpecificImp.cpp", "w", encoding="utf-8") as f: f.write(txt) -def write_arch_gdbcore(core: arch.CoreDef, start_time: str, output_path: pathlib.Path): +def write_arch_gdbcore(core: arch.CoreDef, start_time: str, output_path: pathlib.Path, gdb_mapping: dict): arch_header_template = Template(filename=str(template_dir/'etiss_arch_gdbcore.mako')) logger.info("writing gdbcore") @@ -243,7 +244,8 @@ def write_arch_gdbcore(core: arch.CoreDef, start_time: str, output_path: pathlib start_time=start_time, core_name=core.name, main_reg=core.main_reg_file, - float_reg=core.float_reg_file + float_reg=core.float_reg_file, + mapping=gdb_mapping, ) with open(output_path / f"{core.name}GDBCore.h", "w", encoding="utf-8") as f: diff --git a/m2isar/backends/etiss/templates/etiss_arch_gdbcore.mako b/m2isar/backends/etiss/templates/etiss_arch_gdbcore.mako index 31dcb1c4..37bfbc47 100644 --- a/m2isar/backends/etiss/templates/etiss_arch_gdbcore.mako +++ b/m2isar/backends/etiss/templates/etiss_arch_gdbcore.mako @@ -41,6 +41,7 @@ class ${core_name}GDBCore : public etiss::plugin::gdb::GDBCore { public: std::string mapRegister(unsigned index){ + % if mapping is None: if (index < ${main_reg.range.length}){ std::stringstream ss; ss << "${main_reg.name}" << index; @@ -58,9 +59,16 @@ public: return ss.str(); } % endif + % endif switch (index){ + % if mapping is not None: + % for regnum, name in mapping.items(): + case ${regnum}: return "${name}"; + % endfor + % else: case ${main_reg.range.length}: return "instructionPointer"; + % endif /************************************************************************** * Further register should be added here to send data over gdbserver * ***************************************************************************/ diff --git a/m2isar/backends/etiss/templates/etiss_arch_specific_cpp.mako b/m2isar/backends/etiss/templates/etiss_arch_specific_cpp.mako index 62fb48fe..be9e2c50 100644 --- a/m2isar/backends/etiss/templates/etiss_arch_specific_cpp.mako +++ b/m2isar/backends/etiss/templates/etiss_arch_specific_cpp.mako @@ -144,27 +144,22 @@ std::shared_ptr ${core_name}Arch::getVirtualStruct(ETISS_C } ); - for (uint32_t i = 0; i < ${main_reg.range.length}; ++i){ - ret->addField(new RegField_${core_name}(*ret,i)); - } - - ret->addField(new pcField_${core_name}(*ret)); - - % if float_reg is not None: - // FPRs - for (uint32_t i = 0; i < ${float_reg.range.length}; ++i){ - ret->addField(new FloatRegField_${core_name}(*ret,i)); + % if virtualstruct_regs is not None: + % for virtualstruct_class, idxs in virtualstruct_regs.items(): + % for idx in idxs: + % if isinstance(idx, range): + for (uint32_t i = ${idx.start}; i < ${idx.stop}; i += ${idx.step}){ + ret->addField(new ${virtualstruct_class}_${core_name}(*ret, i)); } + % elif idx is None: + ret->addField(new ${virtualstruct_class}_${core_name}(*ret)); + % else: + ret->addField(new ${virtualstruct_class}_${core_name}(*ret, ${idx})); + % endif + % endfor + % endfor % endif - % if csr_reg is not None: - // CSRs - % if float_reg is not None: - // FCSR - ret->addField(new CSRField_${core_name}(*ret,1)); - ret->addField(new CSRField_${core_name}(*ret,2)); - ret->addField(new CSRField_${core_name}(*ret,3)); - % endif - % endif + return ret; } diff --git a/m2isar/backends/etiss/virtualstruct_utils.py b/m2isar/backends/etiss/virtualstruct_utils.py new file mode 100644 index 00000000..0a058f44 --- /dev/null +++ b/m2isar/backends/etiss/virtualstruct_utils.py @@ -0,0 +1,217 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# This file is part of the M2-ISA-R project: https://github.com/tum-ei-eda/M2-ISA-R +# +# Copyright (C) 2025 +# Chair of Electrical Design Automation +# Technical University of Munich + +"""Helper functions for dealing with ETISS VirtualStruct and GDBCore.""" + +import re +import pathlib +from typing import List, Union +from collections import defaultdict +import xml.etree.ElementTree as ET +from xml.etree import ElementTree, ElementInclude +from ...metamodel.arch import MemoryAttribute + +DEFAULT_ALIASES = {"zero": "x0", "ra": "x1", "sp": "x2", "gp": "x3", "tp": "x4", "t0": "x5", "t1": "x6", "t2": "x7", "s0": "x8", "fp": "x8", "s1": "x9", "a0": "x10", "a1": "x11", "a2": "x12", "a3": "x13", "a4": "x14", "a5": "x15", "a6": "x16", "a7": "x17", "s2": "x18", "s3": "x19", "s4": "x20", "s5": "x21", "s6": "x22", "s7": "x23", "s8": "x24", "s9": "x25", "s10": "x26", "s11": "x27", "t3": "x28", "t4": "x29", "t5": "x30", "t6": "x31"} + +def list_to_ranges(nums: List[int]) -> List[Union[int, range]]: + """ + Convert a list of integers into a list of contiguous ranges and single values. + - Contiguous sequences (len >= 2) -> range(start, stop) + - Single isolated numbers -> int + """ + if not nums: + return [] + + nums = list(set(nums)) # deduplicate + if len(nums) == 1 and nums[0] is None: + return nums + + # Expand everything into a flat sorted list of ints + expanded = [] + for item in nums: + if isinstance(item, range): + expanded.extend(item) + else: + expanded.append(item) + + nums = sorted(set(expanded)) # sort & deduplicate + result = [] + start = prev = nums[0] + + for n in nums[1:]: + if n == prev + 1: + prev = n + continue + # close current segment + if prev > start: + result.append(range(start, prev + 1)) + else: + result.append(start) + start = prev = n + + # handle last segment + if prev > start: + result.append(range(start, prev + 1)) + else: + result.append(start) + + return result + + +def process_xml_descr(path): + tree = ElementTree.parse(path) + root = tree.getroot() + parent = pathlib.Path(path).parent + def custom_loader(href, parse, encoding=None): + if not pathlib.Path(href).is_file(): + href = parent / href + if parse == "xml": + with open(href, 'rb') as file: + data = ElementTree.parse(file).getroot() + else: + if not encoding: + encoding = 'UTF-8' + with open(href, 'r', encoding=encoding) as file: + data = file.read() + return data + ElementInclude.include(root, loader=custom_loader) + mapping = {} + num = -1 + for node in tree.iter(): + if node.tag == "reg": + num = int(node.attrib.get("regnum", num + 1)) + name = node.attrib["name"] + sz = int(node.attrib["bitsize"]) + mapping[num] = (name, sz) + return mapping + + +def resolve_reg(name, mems, aliases): + split_name = lambda s: (m.group(1), int(m.group(2))) if (m:=re.fullmatch(r'([a-zA-Z]+)(\d+)', s)) else None + idx = None + ret = mems.get(name, mems.get(name.lower(), mems.get(name.upper()))) + if ret is None: + ret = aliases.get(name, aliases.get(name.lower(), aliases.get(name.upper()))) + if ret is None: + splitted = split_name(name) + if splitted is not None: + name, idx = splitted + ret = mems.get(name, mems.get(name.lower(), mems.get(name.upper()))) + if ret is None: + ret = aliases.get(name, aliases.get(name.lower(), aliases.get(name.upper()))) + if ret is None: + new_name = DEFAULT_ALIASES.get(name, DEFAULT_ALIASES.get(name.lower(), DEFAULT_ALIASES.get(name.upper()))) + if new_name is not None: + return resolve_reg(new_name, mems, aliases) + return ret, idx + + +def process_gdb_xml_descr_args(args: List[str], cores: list): + descr_mapping = {} + args = [part.strip() for arg in args for part in arg.split(",") if part.strip()] + + for descr in args: + assert ":" in descr, f"Invalid mapping: {descr}" + core, xml_path = descr.split(":", 1) + assert core in cores, f"Unknown core: {core}" + mapping = process_xml_descr(xml_path) + descr_mapping[core] = mapping + return descr_mapping + +def get_gdb_mapping(mapping: dict, memories: dict, memory_aliases: dict): + HARCODED_NAMES = {"PC": "instructionPointer"} + gdb_mapping = None + if mapping is not None: + gdb_mapping = {} + for regnum, data in mapping.items(): + name, sz = data + resolved = resolve_reg(name, memories, memory_aliases) + assert resolved is not None, f"Register lookup failed: {name}" + if resolved is not None: + mem, idx = resolved + assert mem is not None, f"Register lookup failed: {name}" + if mem.parent is not None: # alias + rng = mem.range + assert rng.length == 1, "Aliased ranges are not allowed" + assert idx is None + idx = rng.lower + mem = mem.parent + # assert mem.size == sz, f"Expected size missmatch: {mem.size} vs. {sz}" + # TODO: handle fcsr size + assert mem.size >= sz, f"Expected size missmatch: {mem.size} vs. {sz}" + name = mem.name + name = HARCODED_NAMES.get(name, name) + if idx is not None: + name += str(idx) + gdb_mapping[regnum] = name + return gdb_mapping + + +def get_virtualstruct_regs(mapping: dict, memories: dict, memory_aliases: dict): + gdb_mapping = None + main_reg = None + float_reg = None + csr_reg = None + pc_reg = None + for mem in memories.values(): + if mem.is_pc: + pc_reg = mem + elif MemoryAttribute.IS_MAIN_REG in mem.attributes or mem.name == "X": + main_reg = mem + elif MemoryAttribute.IS_FLOAT_REG in mem.attributes or mem.name == "F": + float_reg = mem + elif MemoryAttribute.IS_CSR_REG in mem.attributes or mem.name == "CSR": + csr_reg = mem + aliased_csrs = set() + if csr_reg is not None: + for mem in memory_aliases.values(): + if mem.parent == csr_reg: + if mem.range.length == 1: + idx = mem.range.lower + aliased_csrs.add(idx) + assert main_reg is not None, "Unable to identify main_reg" + assert pc_reg is not None, "Unable to identify pc_reg" + VIRTUALSTRUCT_CLASSES = { + main_reg.name: "RegField", + **({float_reg.name: "FloatRegField"} if float_reg is not None else {}), + **({csr_reg.name: "CSRField"} if csr_reg is not None else {}), + **({pc_reg.name: "pcField"} if pc_reg is not None else {}), + } + aliased_csrs_only = True + DEFAULT_VIRTUALSTRUCT_REGS = { + "RegField": [range(0, main_reg.range.length)], + **({"FloatRegField": [range(0, float_reg.range.length)]} if float_reg is not None else {}), + **({"CSRField": list(sorted(aliased_csrs)) if aliased_csrs_only else [range(0, csr_reg.range.length)]} if csr_reg is not None else {}), + "pcField": [None] + } + virtualstruct_regs = defaultdict(list) + virtualstruct_regs.update(DEFAULT_VIRTUALSTRUCT_REGS) + if mapping is not None: + for regnum, data in mapping.items(): + name, sz = data + resolved = resolve_reg(name, memories, memory_aliases) + assert resolved is not None, f"Register lookup failed: {name}" + if resolved is not None: + mem, idx = resolved + assert mem is not None, f"Register lookup failed: {name}" + if mem.parent is not None: # alias + rng = mem.range + assert rng.length == 1, "Aliased ranges are not allowed" + assert idx is None + idx = rng.lower + mem = mem.parent + # assert mem.size == sz, f"Expected size missmatch: {mem.size} vs. {sz}" + # TODO: handle fcsr size + assert mem.size >= sz, f"Expected size missmatch: {mem.size} vs. {sz}" + name = mem.name + virtualstruct_class = VIRTUALSTRUCT_CLASSES.get(name) + assert virtualstruct_class is not None, f"Unable to find VirtualStruct class for reg: {name}" + virtualstruct_regs[virtualstruct_class].append(idx) + if virtualstruct_regs is not None: + virtualstruct_regs = {key: list_to_ranges(val) for key, val in virtualstruct_regs.items()} + return virtualstruct_regs diff --git a/m2isar/backends/etiss/writer.py b/m2isar/backends/etiss/writer.py index 91121577..b5818416 100755 --- a/m2isar/backends/etiss/writer.py +++ b/m2isar/backends/etiss/writer.py @@ -8,7 +8,6 @@ """Main entrypoint for the etiss_writer program.""" -import re import argparse import logging import pathlib @@ -27,6 +26,7 @@ write_arch_specific_header, write_arch_struct) from .instruction_writer import write_functions, write_instructions +from .virtualstruct_utils import process_gdb_xml_descr_args, get_virtualstruct_regs, get_gdb_mapping class BooleanOptionalAction(argparse.Action): @@ -128,54 +128,12 @@ def setup(): return (model_obj.cores, logger, output_base_path, spec_name, start_time, args) -def process_xml_descr(path): - import xml.etree.ElementTree as ET - from xml.etree import ElementTree, ElementInclude - # print("process_xml_descr", path) - tree = ElementTree.parse(path) - # print("tree", tree) - root = tree.getroot() - # print("root", root) - parent = pathlib.Path(path).parent - def custom_loader(href, parse, encoding=None): - if not pathlib.Path(href).is_file(): - href = parent / href - if parse == "xml": - with open(href, 'rb') as file: - data = ElementTree.parse(file).getroot() - else: - if not encoding: - encoding = 'UTF-8' - with open(href, 'r', encoding=encoding) as file: - data = file.read() - return data - ElementInclude.include(root, loader=custom_loader) - mapping = {} - num = 0 - for node in tree.iter(): - if node.tag == "reg": - # print("node", node.tag, node.attrib) - num = int(node.attrib.get("regnum", num + 1)) - name = node.attrib["name"] - sz = int(node.attrib["bitsize"]) - mapping[num] = (name, sz) - # print("mapping", mapping) - # input(">>>") - return mapping - def main(): """etiss_writer main entrypoint function.""" # setup etiss writer cores, logger, output_base_path, spec_name, start_time, args = setup() - descr_mapping = {} - for descr in args.gdb_xml_descr: - assert ":" in descr - core, xml_path = descr.split(":", 1) - print("core", core) - assert core in cores - mapping = process_xml_descr(xml_path) - descr_mapping[core] = mapping + descr_mapping = process_gdb_xml_descr_args(args.gdb_xml_descr, cores) # preprocess all models for core_name, core in cores.items(): @@ -199,59 +157,9 @@ def main(): # generate each core in the model for core_name, core in cores.items(): logger.info("processing model %s", core_name) - # print("core.memories", core.memories) - # print("core.memory_aliases", core.memory_aliases) mapping = descr_mapping.get(core_name) - # print("mapping", mapping) - # assert mapping is not None - if mapping is not None: - default_aliases = {"zero": "x0", "ra": "x1", "sp": "x2", "gp": "x3", "tp": "x4", "t0": "x5", "t1": "x6", "t2": "x7", "s0": "x8", "fp": "x8", "s1": "x9", "a0": "x10", "a1": "x11", "a2": "x12", "a3": "x13", "a4": "x14", "a5": "x15", "a6": "x16", "a7": "x17", "s2": "x18", "s3": "x19", "s4": "x20", "s5": "x21", "s6": "x22", "s7": "x23", "s8": "x24", "s9": "x25", "s10": "x26", "s11": "x27", "t3": "x28", "t4": "x29", "t5": "x30", "t6": "x31"} - def resolve_reg(name, mems, aliases): - split_name = lambda s: (m.group(1), int(m.group(2))) if (m:=re.fullmatch(r'([a-zA-Z]+)(\d+)', s)) else None - # print("resolve_reg", name) - idx = None - ret = mems.get(name, mems.get(name.lower(), mems.get(name.upper()))) - if ret is None: - ret = aliases.get(name, aliases.get(name.lower(), aliases.get(name.upper()))) - if ret is None: - splitted = split_name(name) - if splitted is not None: - # print("splitted", splitted) - name, idx = splitted - # print("name", name) - # print("idx", idx) - ret = mems.get(name, mems.get(name.lower(), mems.get(name.upper()))) - # print("ret", ret) - if ret is None: - ret = aliases.get(name, aliases.get(name.lower(), aliases.get(name.upper()))) - # input("?") - if ret is None: - new_name = default_aliases.get(name, default_aliases.get(name.lower(), default_aliases.get(name.upper()))) - if new_name is not None: - return resolve_reg(new_name, mems, aliases) - return ret, idx - for regnum, data in mapping.items(): - # print("regnum,data", regnum, data) - name, sz = data - resolved = resolve_reg(name, core.memories, core.memory_aliases) - # print("resolved", resolved) - # print("resolved[0]", dir(resolved[0])) - # print("resolved[0].children", resolved[0].children) - # print("resolved[0].parent", resolved[0].parent) - # print("resolved[0].range", resolved[0].range) - # print("resolved[0].size", resolved[0].size) - assert resolved is not None - if resolved is not None: - mem, idx = resolved - assert mem is not None - if mem.parent is not None: # alias - rng = mem.range - assert rng.length == 1 - assert idx is None - idx = rng.lower - mem = mem.parent - assert mem.size == sz - print("regnum,mem,idx", regnum, mem, idx) + virtualstruct_regs = get_virtualstruct_regs(mapping, core.memories, core.memory_aliases) + gdb_mapping = get_gdb_mapping(mapping, core.memories, core.memory_aliases) # create output files path output_path = output_base_path / spec_name / core_name @@ -266,10 +174,10 @@ def resolve_reg(name, mems, aliases): write_arch_header(core, start_time, output_path) write_arch_cpp(core, start_time, output_path, False) write_arch_specific_header(core, start_time, output_path) - write_arch_specific_cpp(core, start_time, output_path) + write_arch_specific_cpp(core, start_time, output_path, virtualstruct_regs) write_arch_lib(core, start_time, output_path) write_arch_cmake(core, start_time, output_path, args.separate) - write_arch_gdbcore(core, start_time, output_path) + write_arch_gdbcore(core, start_time, output_path, gdb_mapping) write_functions(core, start_time, output_path, args.static_scalars, args.coverage) write_instructions(core, start_time, output_path, args.separate, args.static_scalars, BlockEndType[args.block_end_on.upper()], args.coverage) From 0c03de018d7b156f6b4b1b4aef725bbb3e8ca0e5 Mon Sep 17 00:00:00 2001 From: Philipp van Kempen Date: Mon, 30 Mar 2026 13:38:32 +0200 Subject: [PATCH 08/18] lint templates --- .../etiss/templates/etiss_arch_gdbcore.mako | 70 +++++----- .../templates/etiss_arch_specific_h.mako | 128 +++++++++--------- 2 files changed, 99 insertions(+), 99 deletions(-) diff --git a/m2isar/backends/etiss/templates/etiss_arch_gdbcore.mako b/m2isar/backends/etiss/templates/etiss_arch_gdbcore.mako index e4fb1cbe..1b5b3ce2 100644 --- a/m2isar/backends/etiss/templates/etiss_arch_gdbcore.mako +++ b/m2isar/backends/etiss/templates/etiss_arch_gdbcore.mako @@ -41,45 +41,45 @@ class ${core_name}GDBCore : public etiss::plugin::gdb::GDBCore { public: - std::string mapRegister(unsigned index) + std::string mapRegister(unsigned index) { - % if mapping is None: - if (index < ${main_reg.range.length}) + % if mapping is None: + if (index < ${main_reg.range.length}) { - std::stringstream ss; - ss << "${main_reg.name}" << index; - return ss.str(); - } - % if float_reg is not None: - if ((${main_reg.range.length} < index) && (index < ${main_reg.range.length + float_reg.range.length + 1})) + std::stringstream ss; + ss << "${main_reg.name}" << index; + return ss.str(); + } + % if float_reg is not None: + if ((${main_reg.range.length} < index) && (index < ${main_reg.range.length + float_reg.range.length + 1})) { - std::stringstream ss; - ss << "${float_reg.name}" << (index - ${main_reg.range.length + 1}); - return ss.str(); - } - if ((${main_reg.range.length + float_reg.range.length} < index) && (index < ${main_reg.range.length + float_reg.range.length + 5})) + std::stringstream ss; + ss << "${float_reg.name}" << (index - ${main_reg.range.length + 1}); + return ss.str(); + } + if ((${main_reg.range.length + float_reg.range.length} < index) && (index < ${main_reg.range.length + float_reg.range.length + 5})) { // FCSR - std::stringstream ss; - ss << "CSR" << (index - ${main_reg.range.length + float_reg.range.length + 1}); - return ss.str(); - } - % endif - % endif - switch (index){ - % if mapping is not None: - % for regnum, name in mapping.items(): - case ${regnum}: return "${name}"; - % endfor - % else: - case ${main_reg.range.length}: - return "instructionPointer"; - % endif - /************************************************************************** - * Further register should be added here to send data over gdbserver * - ***************************************************************************/ - } - return ""; - } + std::stringstream ss; + ss << "CSR" << (index - ${main_reg.range.length + float_reg.range.length + 1}); + return ss.str(); + } + % endif + % endif + switch (index){ + % if mapping is not None: + % for regnum, name in mapping.items(): + case ${regnum}: return "${name}"; + % endfor + % else: + case ${main_reg.range.length}: + return "instructionPointer"; + % endif + /************************************************************************** + * Further register should be added here to send data over gdbserver * + ***************************************************************************/ + } + return ""; + } unsigned mapRegister(std::string name) { return INVALIDMAPPING; } diff --git a/m2isar/backends/etiss/templates/etiss_arch_specific_h.mako b/m2isar/backends/etiss/templates/etiss_arch_specific_h.mako index 800774ad..fe7e6307 100644 --- a/m2isar/backends/etiss/templates/etiss_arch_specific_h.mako +++ b/m2isar/backends/etiss/templates/etiss_arch_specific_h.mako @@ -97,53 +97,53 @@ class RegField_${core_name} : public etiss::VirtualStruct::Field class FloatRegField_${core_name} : public etiss::VirtualStruct::Field { private: - const unsigned gprid_; + const unsigned gprid_; public: - FloatRegField_${core_name}(etiss::VirtualStruct & parent,unsigned gprid) + FloatRegField_${core_name}(etiss::VirtualStruct & parent,unsigned gprid) // clang-format off - : Field(parent, - std::string("${float_reg.name}")+etiss::toString(gprid), - std::string("${float_reg.name}")+etiss::toString(gprid), - R|W, - ${int(float_reg.size / 8)} - ), - gprid_(gprid) + : Field(parent, + std::string("${float_reg.name}")+etiss::toString(gprid), + std::string("${float_reg.name}")+etiss::toString(gprid), + R|W, + ${int(float_reg.size / 8)} + ), + gprid_(gprid) // clang-format on - { + { } - FloatRegField_${core_name}(etiss::VirtualStruct & parent, std::string name, unsigned gprid) + FloatRegField_${core_name}(etiss::VirtualStruct & parent, std::string name, unsigned gprid) // clang-format off - : Field(parent, - name, - name, - R|W, - ${int(float_reg.size / 8)} - ), - gprid_(gprid) + : Field(parent, + name, + name, + R|W, + ${int(float_reg.size / 8)} + ), + gprid_(gprid) // clang-format on - { + { } - virtual ~FloatRegField_${core_name}(){} + virtual ~FloatRegField_${core_name}(){} protected: - virtual uint64_t _read() const { - % if len(main_reg.children) > 0: - return (uint64_t) *((${core_name}*)parent_.structure_)->${float_reg.name}[gprid_]; - % else: - return (uint64_t) ((${core_name}*)parent_.structure_)->${float_reg.name}[gprid_]; - % endif - } - - virtual void _write(uint64_t val) { - etiss::log(etiss::VERBOSE, "write to ETISS cpu state", name_, val); - % if len(main_reg.children) > 0: - *((${core_name}*)parent_.structure_)->${float_reg.name}[gprid_] = (etiss_uint${float_reg.size}) val; - % else: - ((${core_name}*)parent_.structure_)->${float_reg.name}[gprid_] = (etiss_uint${float_reg.size}) val; - % endif - } + virtual uint64_t _read() const { + % if len(main_reg.children) > 0: + return (uint64_t) *((${core_name}*)parent_.structure_)->${float_reg.name}[gprid_]; + % else: + return (uint64_t) ((${core_name}*)parent_.structure_)->${float_reg.name}[gprid_]; + % endif + } + + virtual void _write(uint64_t val) { + etiss::log(etiss::VERBOSE, "write to ETISS cpu state", name_, val); + % if len(main_reg.children) > 0: + *((${core_name}*)parent_.structure_)->${float_reg.name}[gprid_] = (etiss_uint${float_reg.size}) val; + % else: + ((${core_name}*)parent_.structure_)->${float_reg.name}[gprid_] = (etiss_uint${float_reg.size}) val; + % endif + } }; % endif @@ -152,42 +152,42 @@ class FloatRegField_${core_name} : public etiss::VirtualStruct::Field class CSRField_${core_name} : public etiss::VirtualStruct::Field { private: - const unsigned gprid_; + const unsigned gprid_; public: - CSRField_${core_name}(etiss::VirtualStruct & parent,unsigned gprid) + CSRField_${core_name}(etiss::VirtualStruct & parent,unsigned gprid) // clang-format off - : Field(parent, - std::string("${csr_reg.name}")+etiss::toString(gprid), - std::string("${csr_reg.name}")+etiss::toString(gprid), - R|W, - ${int(float_reg.size / 8)} - ), - gprid_(gprid) + : Field(parent, + std::string("${csr_reg.name}")+etiss::toString(gprid), + std::string("${csr_reg.name}")+etiss::toString(gprid), + R|W, + ${int(float_reg.size / 8)} + ), + gprid_(gprid) // clang-format on - { + { } - CSRField_${core_name}(etiss::VirtualStruct & parent, std::string name, unsigned gprid) - : Field(parent, - name, - name, - R|W, - ${int(float_reg.size / 8)} - ), - gprid_(gprid) - {} + CSRField_${core_name}(etiss::VirtualStruct & parent, std::string name, unsigned gprid) + : Field(parent, + name, + name, + R|W, + ${int(float_reg.size / 8)} + ), + gprid_(gprid) + {} - virtual ~CSRField_${core_name}(){} + virtual ~CSRField_${core_name}(){} protected: - virtual uint64_t _read() const { - return (uint64_t) ${core_name}_csr_read((ETISS_CPU*)parent_.structure_, nullptr, nullptr, (etiss_uint${float_reg.size}) gprid_); - } - - virtual void _write(uint64_t val) { - etiss::log(etiss::VERBOSE, "write to ETISS cpu state", name_, val); - ${core_name}_csr_write((ETISS_CPU*)parent_.structure_, nullptr, nullptr, gprid_, (etiss_uint${float_reg.size}) val); - } + virtual uint64_t _read() const { + return (uint64_t) ${core_name}_csr_read((ETISS_CPU*)parent_.structure_, nullptr, nullptr, (etiss_uint${float_reg.size}) gprid_); + } + + virtual void _write(uint64_t val) { + etiss::log(etiss::VERBOSE, "write to ETISS cpu state", name_, val); + ${core_name}_csr_write((ETISS_CPU*)parent_.structure_, nullptr, nullptr, gprid_, (etiss_uint${float_reg.size}) val); + } }; % endif From c762b4f876e527cc9b7456d6165b1ccc68e10e65 Mon Sep 17 00:00:00 2001 From: Philipp van Kempen Date: Mon, 30 Mar 2026 14:47:19 +0200 Subject: [PATCH 09/18] m2isar/backends/etiss/templates/etiss_arch_specific_h.mako: add missing include --- m2isar/backends/etiss/templates/etiss_arch_specific_h.mako | 1 + 1 file changed, 1 insertion(+) diff --git a/m2isar/backends/etiss/templates/etiss_arch_specific_h.mako b/m2isar/backends/etiss/templates/etiss_arch_specific_h.mako index fe7e6307..b9414f27 100644 --- a/m2isar/backends/etiss/templates/etiss_arch_specific_h.mako +++ b/m2isar/backends/etiss/templates/etiss_arch_specific_h.mako @@ -24,6 +24,7 @@ #include "etiss/VirtualStruct.h" #include "etiss/jit/CPU.h" #include "${core_name}.h" +#include "${core_name}Funcs.h" /** @brief VirtualStruct for ${core_name} architecture to faciliate register acess From 481fecfa83532a33b99e013d36d31dc1997d894a Mon Sep 17 00:00:00 2001 From: Philipp van Kempen Date: Mon, 30 Mar 2026 14:47:33 +0200 Subject: [PATCH 10/18] m2isar/backends/etiss/virtualstruct_utils.py: improve assert msg --- m2isar/backends/etiss/virtualstruct_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/m2isar/backends/etiss/virtualstruct_utils.py b/m2isar/backends/etiss/virtualstruct_utils.py index 0a058f44..43285bc9 100644 --- a/m2isar/backends/etiss/virtualstruct_utils.py +++ b/m2isar/backends/etiss/virtualstruct_utils.py @@ -207,7 +207,7 @@ def get_virtualstruct_regs(mapping: dict, memories: dict, memory_aliases: dict): mem = mem.parent # assert mem.size == sz, f"Expected size missmatch: {mem.size} vs. {sz}" # TODO: handle fcsr size - assert mem.size >= sz, f"Expected size missmatch: {mem.size} vs. {sz}" + assert mem.size >= sz, f"Expected size missmatch: {mem.size} vs. {sz} [{name}]" name = mem.name virtualstruct_class = VIRTUALSTRUCT_CLASSES.get(name) assert virtualstruct_class is not None, f"Unable to find VirtualStruct class for reg: {name}" From 5cec8e3fcd66c2205fe775a92652e3bac3daea6f Mon Sep 17 00:00:00 2001 From: Philipp van Kempen Date: Tue, 7 Apr 2026 10:47:17 +0200 Subject: [PATCH 11/18] add and handle IS_VECTOR_REG attribute --- m2isar/backends/etiss/architecture_writer.py | 1 + m2isar/backends/etiss/virtualstruct_utils.py | 8 +++++++- m2isar/frontends/coredsl2/architecture_model_builder.py | 4 ++++ m2isar/metamodel/arch.py | 4 ++++ 4 files changed, 16 insertions(+), 1 deletion(-) diff --git a/m2isar/backends/etiss/architecture_writer.py b/m2isar/backends/etiss/architecture_writer.py index 7966c650..27edc170 100644 --- a/m2isar/backends/etiss/architecture_writer.py +++ b/m2isar/backends/etiss/architecture_writer.py @@ -171,6 +171,7 @@ def write_arch_specific_header(core: arch.CoreDef, start_time: str, output_path: core_name=core.name, main_reg=core.main_reg_file, float_reg=core.float_reg_file, + vector_reg=core.vector_reg_file, csr_reg=core.csr_reg_file ) diff --git a/m2isar/backends/etiss/virtualstruct_utils.py b/m2isar/backends/etiss/virtualstruct_utils.py index 43285bc9..419e75a5 100644 --- a/m2isar/backends/etiss/virtualstruct_utils.py +++ b/m2isar/backends/etiss/virtualstruct_utils.py @@ -156,6 +156,7 @@ def get_virtualstruct_regs(mapping: dict, memories: dict, memory_aliases: dict): gdb_mapping = None main_reg = None float_reg = None + vector_reg = None csr_reg = None pc_reg = None for mem in memories.values(): @@ -165,6 +166,8 @@ def get_virtualstruct_regs(mapping: dict, memories: dict, memory_aliases: dict): main_reg = mem elif MemoryAttribute.IS_FLOAT_REG in mem.attributes or mem.name == "F": float_reg = mem + elif MemoryAttribute.IS_VECTOR_REG in mem.attributes or mem.name == "F": + vector_reg = mem elif MemoryAttribute.IS_CSR_REG in mem.attributes or mem.name == "CSR": csr_reg = mem aliased_csrs = set() @@ -179,6 +182,7 @@ def get_virtualstruct_regs(mapping: dict, memories: dict, memory_aliases: dict): VIRTUALSTRUCT_CLASSES = { main_reg.name: "RegField", **({float_reg.name: "FloatRegField"} if float_reg is not None else {}), + **({vector_reg.name: "VectorRegField"} if vector_reg is not None else {}), **({csr_reg.name: "CSRField"} if csr_reg is not None else {}), **({pc_reg.name: "pcField"} if pc_reg is not None else {}), } @@ -186,6 +190,8 @@ def get_virtualstruct_regs(mapping: dict, memories: dict, memory_aliases: dict): DEFAULT_VIRTUALSTRUCT_REGS = { "RegField": [range(0, main_reg.range.length)], **({"FloatRegField": [range(0, float_reg.range.length)]} if float_reg is not None else {}), + # **({"VectorRegField": [range(0, vector_reg.range.length)]} if vector_reg is not None else {}), + **({"VectorRegField": [range(0, 32)]} if vector_reg is not None else {}), **({"CSRField": list(sorted(aliased_csrs)) if aliased_csrs_only else [range(0, csr_reg.range.length)]} if csr_reg is not None else {}), "pcField": [None] } @@ -207,7 +213,7 @@ def get_virtualstruct_regs(mapping: dict, memories: dict, memory_aliases: dict): mem = mem.parent # assert mem.size == sz, f"Expected size missmatch: {mem.size} vs. {sz}" # TODO: handle fcsr size - assert mem.size >= sz, f"Expected size missmatch: {mem.size} vs. {sz} [{name}]" + assert mem.size >= sz or name in [f"v{i}" for i in range(32)], f"Expected size missmatch: {mem.size} vs. {sz} [{name}]" name = mem.name virtualstruct_class = VIRTUALSTRUCT_CLASSES.get(name) assert virtualstruct_class is not None, f"Unable to find VirtualStruct class for reg: {name}" diff --git a/m2isar/frontends/coredsl2/architecture_model_builder.py b/m2isar/frontends/coredsl2/architecture_model_builder.py index 78d1997c..f829035b 100644 --- a/m2isar/frontends/coredsl2/architecture_model_builder.py +++ b/m2isar/frontends/coredsl2/architecture_model_builder.py @@ -35,6 +35,7 @@ class ArchitectureModelBuilder(CoreDSL2Visitor): _instr_classes: "set[int]" _main_reg_file: Union[arch.Memory, None] _float_reg_file: Union[arch.Memory, None] + _vector_reg_file: Union[arch.Memory, None] _csr_reg_file: Union[arch.Memory, None] def __init__(self): @@ -52,6 +53,7 @@ def __init__(self): self._instr_classes = set() self._main_reg_file = None self._float_reg_file = None + self._vector_reg_file = None self._csr_reg_file = None def visitBit_field(self, ctx: CoreDSL2Parser.Bit_fieldContext): @@ -389,6 +391,8 @@ def visitDeclaration(self, ctx: CoreDSL2Parser.DeclarationContext): self._main_reg_file = m if arch.MemoryAttribute.IS_FLOAT_REG in attributes: self._float_reg_file = m + if arch.MemoryAttribute.IS_VECTOR_REG in attributes: + self._vector_reg_file = m if arch.MemoryAttribute.IS_CSR_REG in attributes or name.upper() == "CSR": self._csr_reg_file = m diff --git a/m2isar/metamodel/arch.py b/m2isar/metamodel/arch.py index 1582cd59..45f0b1dd 100644 --- a/m2isar/metamodel/arch.py +++ b/m2isar/metamodel/arch.py @@ -199,6 +199,7 @@ class MemoryAttribute(Enum): IS_MAIN_MEM = auto() IS_MAIN_REG = auto() IS_FLOAT_REG = auto() + IS_VECTOR_REG = auto() IS_CSR_REG = auto() DELETE = auto() ETISS_CAN_FAIL = auto() @@ -575,6 +576,7 @@ def __init__(self, name, contributing_types: "list[str]", template: str, constan self.instr_classes = instr_classes self.main_reg_file = None self.float_reg_file = None + self.vector_reg_file = None self.csr_reg_file = None self.main_memory = None self.pc_memory = None @@ -601,6 +603,8 @@ def __init__(self, name, contributing_types: "list[str]", template: str, constan self.main_reg_file = mem if MemoryAttribute.IS_FLOAT_REG in mem.attributes: self.float_reg_file = mem + if MemoryAttribute.IS_VECTOR_REG in mem.attributes: + self.vector_reg_file = mem if MemoryAttribute.IS_CSR_REG in mem.attributes or mem.name.upper() == "CSR": self.csr_reg_file = mem elif MemoryAttribute.IS_PC in mem.attributes: From c6e85058dd84b8a73891ff027191384e5031fd58 Mon Sep 17 00:00:00 2001 From: Philipp van Kempen Date: Tue, 7 Apr 2026 10:48:26 +0200 Subject: [PATCH 12/18] annotate gdb mapping with reg/alias names --- .../backends/etiss/templates/etiss_arch_gdbcore.mako | 4 ++-- m2isar/backends/etiss/virtualstruct_utils.py | 11 +++++------ 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/m2isar/backends/etiss/templates/etiss_arch_gdbcore.mako b/m2isar/backends/etiss/templates/etiss_arch_gdbcore.mako index 1b5b3ce2..d9f6c9d9 100644 --- a/m2isar/backends/etiss/templates/etiss_arch_gdbcore.mako +++ b/m2isar/backends/etiss/templates/etiss_arch_gdbcore.mako @@ -67,8 +67,8 @@ class ${core_name}GDBCore : public etiss::plugin::gdb::GDBCore % endif switch (index){ % if mapping is not None: - % for regnum, name in mapping.items(): - case ${regnum}: return "${name}"; + % for regnum, (name, name2) in mapping.items(): + case ${regnum}: return "${name2}"; // ${name} % endfor % else: case ${main_reg.range.length}: diff --git a/m2isar/backends/etiss/virtualstruct_utils.py b/m2isar/backends/etiss/virtualstruct_utils.py index 419e75a5..2c5cdc83 100644 --- a/m2isar/backends/etiss/virtualstruct_utils.py +++ b/m2isar/backends/etiss/virtualstruct_utils.py @@ -143,17 +143,16 @@ def get_gdb_mapping(mapping: dict, memories: dict, memory_aliases: dict): mem = mem.parent # assert mem.size == sz, f"Expected size missmatch: {mem.size} vs. {sz}" # TODO: handle fcsr size - assert mem.size >= sz, f"Expected size missmatch: {mem.size} vs. {sz}" - name = mem.name - name = HARCODED_NAMES.get(name, name) + assert mem.size >= sz or name in [f"v{i}" for i in range(32)], f"Expected size missmatch: {mem.size} vs. {sz} [{name}]" + name2 = mem.name + name2 = HARCODED_NAMES.get(name2, name2) if idx is not None: - name += str(idx) - gdb_mapping[regnum] = name + name2 += str(idx) + gdb_mapping[regnum] = (name, name2) return gdb_mapping def get_virtualstruct_regs(mapping: dict, memories: dict, memory_aliases: dict): - gdb_mapping = None main_reg = None float_reg = None vector_reg = None From 2888a84c98159519eede24b26952a797ab532184 Mon Sep 17 00:00:00 2001 From: Philipp van Kempen Date: Tue, 7 Apr 2026 10:48:49 +0200 Subject: [PATCH 13/18] improve csr detection --- m2isar/backends/etiss/virtualstruct_utils.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/m2isar/backends/etiss/virtualstruct_utils.py b/m2isar/backends/etiss/virtualstruct_utils.py index 2c5cdc83..82a4492d 100644 --- a/m2isar/backends/etiss/virtualstruct_utils.py +++ b/m2isar/backends/etiss/virtualstruct_utils.py @@ -84,7 +84,9 @@ def custom_loader(href, parse, encoding=None): num = -1 for node in tree.iter(): if node.tag == "reg": - num = int(node.attrib.get("regnum", num + 1)) + num = node.attrib.get("regnum", num + 1) + if isinstance(num, str): + num = int(num, 0) name = node.attrib["name"] sz = int(node.attrib["bitsize"]) mapping[num] = (name, sz) @@ -92,18 +94,19 @@ def custom_loader(href, parse, encoding=None): def resolve_reg(name, mems, aliases): + # print("resolve_reg", name, mems, aliases) split_name = lambda s: (m.group(1), int(m.group(2))) if (m:=re.fullmatch(r'([a-zA-Z]+)(\d+)', s)) else None idx = None ret = mems.get(name, mems.get(name.lower(), mems.get(name.upper()))) if ret is None: - ret = aliases.get(name, aliases.get(name.lower(), aliases.get(name.upper()))) + ret = aliases.get(name, aliases.get(name.lower(), aliases.get(name.upper(), aliases.get(f"{name.upper()}_CSR")))) if ret is None: splitted = split_name(name) if splitted is not None: name, idx = splitted ret = mems.get(name, mems.get(name.lower(), mems.get(name.upper()))) if ret is None: - ret = aliases.get(name, aliases.get(name.lower(), aliases.get(name.upper()))) + ret = aliases.get(name, aliases.get(name.lower(), aliases.get(name.upper(), aliases.get(f"{name.upper()}_CSR")))) if ret is None: new_name = DEFAULT_ALIASES.get(name, DEFAULT_ALIASES.get(name.lower(), DEFAULT_ALIASES.get(name.upper()))) if new_name is not None: From 7fed19129f8154138afeced7b5b4b83d6c5aaae6 Mon Sep 17 00:00:00 2001 From: Philipp van Kempen Date: Tue, 7 Apr 2026 10:49:09 +0200 Subject: [PATCH 14/18] update virtualstruct _read and _write for new api --- .../templates/etiss_arch_specific_h.mako | 68 ++++++++++++++++--- 1 file changed, 60 insertions(+), 8 deletions(-) diff --git a/m2isar/backends/etiss/templates/etiss_arch_specific_h.mako b/m2isar/backends/etiss/templates/etiss_arch_specific_h.mako index b9414f27..44793b81 100644 --- a/m2isar/backends/etiss/templates/etiss_arch_specific_h.mako +++ b/m2isar/backends/etiss/templates/etiss_arch_specific_h.mako @@ -70,8 +70,9 @@ class RegField_${core_name} : public etiss::VirtualStruct::Field virtual ~RegField_${core_name}() {} protected: - virtual uint64_t _read() const + virtual uint64_t _read(size_t offset) const { + assert((offset == 0 || (offset < (bitwidth_ / sizeof(uint64_t)))) && "Virtualstruct field offset out of range"); // clang-format off % if len(main_reg.children) > 0: return (uint64_t) *((${core_name}*)parent_.structure_)->${main_reg.name}[gprid_]; @@ -81,8 +82,9 @@ class RegField_${core_name} : public etiss::VirtualStruct::Field // clang-format on } - virtual void _write(uint64_t val) + virtual void _write(uint64_t val, size_t offset) { + assert((offset == 0 || (offset < (bitwidth_ / sizeof(uint64_t)))) && "Virtualstruct field offset out of range"); // clang-format off etiss::log(etiss::VERBOSE, "write to ETISS cpu state", name_, val); % if len(main_reg.children) > 0: @@ -129,7 +131,8 @@ class FloatRegField_${core_name} : public etiss::VirtualStruct::Field virtual ~FloatRegField_${core_name}(){} protected: - virtual uint64_t _read() const { + virtual uint64_t _read(size_t offset) const { + assert((offset == 0 || (offset < (bitwidth_ / sizeof(uint64_t)))) && "Virtualstruct field offset out of range"); % if len(main_reg.children) > 0: return (uint64_t) *((${core_name}*)parent_.structure_)->${float_reg.name}[gprid_]; % else: @@ -137,7 +140,8 @@ class FloatRegField_${core_name} : public etiss::VirtualStruct::Field % endif } - virtual void _write(uint64_t val) { + virtual void _write(uint64_t val, size_t offset) { + assert((offset == 0 || (offset < (bitwidth_ / sizeof(uint64_t)))) && "Virtualstruct field offset out of range"); etiss::log(etiss::VERBOSE, "write to ETISS cpu state", name_, val); % if len(main_reg.children) > 0: *((${core_name}*)parent_.structure_)->${float_reg.name}[gprid_] = (etiss_uint${float_reg.size}) val; @@ -148,6 +152,50 @@ class FloatRegField_${core_name} : public etiss::VirtualStruct::Field }; % endif +% if vector_reg: +class VectorRegField_${core_name} : public etiss::VirtualStruct::Field{ +private: + const unsigned gprid_; +public: + VectorRegField_${core_name}(etiss::VirtualStruct & parent,unsigned gprid) + : Field(parent, + std::string("${vector_reg.name}")+etiss::toString(gprid), + std::string("${vector_reg.name}")+etiss::toString(gprid), + R|W, + ${int((vector_reg.range.length // 32) // (vector_reg.size // 8))} + ), + gprid_(gprid) + {} + + VectorRegField_${core_name}(etiss::VirtualStruct & parent, std::string name, unsigned gprid) + : Field(parent, + name, + name, + R|W, + ${int((vector_reg.range.length // 32) // (vector_reg.size // 8))} + ), + gprid_(gprid) + {} + + virtual ~VectorRegField_${core_name}(){} + +protected: + virtual uint64_t _read(size_t offset) const { + assert((offset == 0 || (offset < (bitwidth_ / sizeof(uint64_t)))) && "Virtualstruct field offset out of range"); + // printf("v_read with gprid_ %d\n", gprid_); + // TODO: check for out of bounds? (offset < width_/8) + return (uint64_t) *((uint64_t*)&((${core_name}*)parent_.structure_)->${vector_reg.name}[gprid_ * width_ + sizeof(uint64_t) * offset]); + } + + virtual void _write(uint64_t val, size_t offset) { + assert((offset == 0 || (offset < (bitwidth_ / sizeof(uint64_t)))) && "Virtualstruct field offset out of range"); + etiss::log(etiss::VERBOSE, "write to ETISS cpu state", name_, val); + // printf("v_write (%lu) with gprid_ %d\n", val, gprid_); + // TODO: check for out of bounds? (offset < width_/8) + *((uint64_t*)&((${core_name}*)parent_.structure_)->${vector_reg.name}[gprid_ * width_ + sizeof(uint64_t)]) = (etiss_uint64) val; // TODO: write V[gprid_] instead + } +}; +% endif % if csr_reg: class CSRField_${core_name} : public etiss::VirtualStruct::Field @@ -181,11 +229,13 @@ class CSRField_${core_name} : public etiss::VirtualStruct::Field virtual ~CSRField_${core_name}(){} protected: - virtual uint64_t _read() const { + virtual uint64_t _read(size_t offset) const { + assert((offset == 0 || (offset < (bitwidth_ / sizeof(uint64_t)))) && "Virtualstruct field offset out of range"); return (uint64_t) ${core_name}_csr_read((ETISS_CPU*)parent_.structure_, nullptr, nullptr, (etiss_uint${float_reg.size}) gprid_); } - virtual void _write(uint64_t val) { + virtual void _write(uint64_t val, size_t offset) { + assert((offset == 0 || (offset < (bitwidth_ / sizeof(uint64_t)))) && "Virtualstruct field offset out of range"); etiss::log(etiss::VERBOSE, "write to ETISS cpu state", name_, val); ${core_name}_csr_write((ETISS_CPU*)parent_.structure_, nullptr, nullptr, gprid_, (etiss_uint${float_reg.size}) val); } @@ -212,15 +262,17 @@ class pcField_${core_name} : public etiss::VirtualStruct::Field virtual ~pcField_${core_name}() {} protected: - virtual uint64_t _read() const + virtual uint64_t _read(size_t offset) const { + assert((offset == 0 || (offset < (bitwidth_ / sizeof(uint64_t)))) && "Virtualstruct field offset out of range"); // clang-format off return (uint64_t) ((ETISS_CPU *)parent_.structure_)->instructionPointer; // clang-format on } - virtual void _write(uint64_t val) + virtual void _write(uint64_t val, size_t offset) { + assert((offset == 0 || (offset < (bitwidth_ / sizeof(uint64_t)))) && "Virtualstruct field offset out of range"); // clang-format off etiss::log(etiss::VERBOSE, "write to ETISS cpu state", name_, val); ((ETISS_CPU *)parent_.structure_)->instructionPointer = (etiss_uint${main_reg.size}) val; From a338b36c3bb2717f506492a9069dcc9d65d49b6b Mon Sep 17 00:00:00 2001 From: Philipp van Kempen Date: Tue, 7 Apr 2026 11:08:51 +0200 Subject: [PATCH 15/18] fix etiss_arch_specific_h.mako template (float_reg -> csr_reg) --- .../backends/etiss/templates/etiss_arch_specific_h.mako | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/m2isar/backends/etiss/templates/etiss_arch_specific_h.mako b/m2isar/backends/etiss/templates/etiss_arch_specific_h.mako index 44793b81..f08284fb 100644 --- a/m2isar/backends/etiss/templates/etiss_arch_specific_h.mako +++ b/m2isar/backends/etiss/templates/etiss_arch_specific_h.mako @@ -209,7 +209,7 @@ class CSRField_${core_name} : public etiss::VirtualStruct::Field std::string("${csr_reg.name}")+etiss::toString(gprid), std::string("${csr_reg.name}")+etiss::toString(gprid), R|W, - ${int(float_reg.size / 8)} + ${int(csr_reg.size / 8)} ), gprid_(gprid) // clang-format on @@ -221,7 +221,7 @@ class CSRField_${core_name} : public etiss::VirtualStruct::Field name, name, R|W, - ${int(float_reg.size / 8)} + ${int(csr_reg.size / 8)} ), gprid_(gprid) {} @@ -231,13 +231,13 @@ class CSRField_${core_name} : public etiss::VirtualStruct::Field protected: virtual uint64_t _read(size_t offset) const { assert((offset == 0 || (offset < (bitwidth_ / sizeof(uint64_t)))) && "Virtualstruct field offset out of range"); - return (uint64_t) ${core_name}_csr_read((ETISS_CPU*)parent_.structure_, nullptr, nullptr, (etiss_uint${float_reg.size}) gprid_); + return (uint64_t) ${core_name}_csr_read((ETISS_CPU*)parent_.structure_, nullptr, nullptr, (etiss_uint${csr_reg.size}) gprid_); } virtual void _write(uint64_t val, size_t offset) { assert((offset == 0 || (offset < (bitwidth_ / sizeof(uint64_t)))) && "Virtualstruct field offset out of range"); etiss::log(etiss::VERBOSE, "write to ETISS cpu state", name_, val); - ${core_name}_csr_write((ETISS_CPU*)parent_.structure_, nullptr, nullptr, gprid_, (etiss_uint${float_reg.size}) val); + ${core_name}_csr_write((ETISS_CPU*)parent_.structure_, nullptr, nullptr, gprid_, (etiss_uint${csr_reg.size}) val); } }; % endif From 50f7d997a3884d4d4b5a332187522fb64538b6c9 Mon Sep 17 00:00:00 2001 From: Philipp van Kempen Date: Fri, 24 Apr 2026 17:08:28 +0200 Subject: [PATCH 16/18] Support ETISS Writer --fill-mode arg to auto-fill otherwise hardcoded archspeclib contents (#85) * m2isar/backends/etiss/architecture_writer.py: fix template var names * etiss_writer: expose --fill-mode arg (avoid manual patching of arch specific templates) * fix etiss_arch_specific_h.mako template (float_reg -> csr_reg) * fill mode fixes * fill mode fixes * fill mode fixes --- m2isar/backends/etiss/architecture_writer.py | 60 ++++++++++--- .../templates/etiss_arch_specific_cpp.mako | 15 ++++ .../etiss/templates/etiss_jit_extensions.mako | 15 ++++ .../etiss/templates/etiss_length_updater.mako | 88 +++++++++++++++++++ m2isar/backends/etiss/writer.py | 4 +- 5 files changed, 168 insertions(+), 14 deletions(-) create mode 100644 m2isar/backends/etiss/templates/etiss_jit_extensions.mako create mode 100644 m2isar/backends/etiss/templates/etiss_length_updater.mako diff --git a/m2isar/backends/etiss/architecture_writer.py b/m2isar/backends/etiss/architecture_writer.py index 27edc170..1ecbdb05 100644 --- a/m2isar/backends/etiss/architecture_writer.py +++ b/m2isar/backends/etiss/architecture_writer.py @@ -105,7 +105,7 @@ def build_reg_hierarchy(reg: arch.Memory, ptr_regs: "list[arch.Memory]", actual_ def write_arch_cpp(core: arch.CoreDef, start_time: str, output_path: pathlib.Path, aliased_regnames: bool=True): """Generate {CoreName}Arch.cpp file. Contains mainly register initialization code.""" - arch_header_template = Template(filename=str(template_dir/'etiss_arch_cpp.mako')) + arch_cpp_template = Template(filename=str(template_dir/'etiss_arch_cpp.mako')) ptr_regs = [] actual_regs = [] @@ -132,7 +132,7 @@ def write_arch_cpp(core: arch.CoreDef, start_time: str, output_path: pathlib.Pat reg_names[child.range.lower] = child.name # TODO(annnnna42): add float reg aliases here - txt = arch_header_template.render( + txt = arch_cpp_template.render( start_time=start_time, core_name=core.name, instr_classes=sorted(core.instr_classes), @@ -149,11 +149,11 @@ def write_arch_cpp(core: arch.CoreDef, start_time: str, output_path: pathlib.Pat f.write(txt) def write_arch_lib(core: arch.CoreDef, start_time: str, output_path: pathlib.Path): - arch_header_template = Template(filename=str(template_dir/'etiss_arch_lib.mako')) + arch_lib_template = Template(filename=str(template_dir/'etiss_arch_lib.mako')) logger.info("writing architecture lib") - txt = arch_header_template.render( + txt = arch_lib_template.render( start_time=start_time, core_name=core.name ) @@ -162,11 +162,11 @@ def write_arch_lib(core: arch.CoreDef, start_time: str, output_path: pathlib.Pat f.write(txt) def write_arch_specific_header(core: arch.CoreDef, start_time: str, output_path: pathlib.Path): - arch_header_template = Template(filename=str(template_dir/'etiss_arch_specific_h.mako')) + arch_specific_header_template = Template(filename=str(template_dir/'etiss_arch_specific_h.mako')) logger.info("writing architecture specific header") - txt = arch_header_template.render( + txt = arch_specific_header_template.render( start_time=start_time, core_name=core.name, main_reg=core.main_reg_file, @@ -178,8 +178,39 @@ def write_arch_specific_header(core: arch.CoreDef, start_time: str, output_path: with open(output_path / f"{core.name}ArchSpecificImp.h", "w", encoding="utf-8") as f: f.write(txt) -def write_arch_specific_cpp(core: arch.CoreDef, start_time: str, output_path: pathlib.Path, virtualstruct_regs: dict): - arch_header_template = Template(filename=str(template_dir/'etiss_arch_specific_cpp.mako')) +def write_arch_specific_cpp(core: arch.CoreDef, start_time: str, output_path: pathlib.Path, virtualstruct_regs: dict, fill_mode: str): + fill_jit_extensions=None + fill_length_updater=None + fill_endianess_compensation=None + assert isinstance(fill_mode, str) + fill_mode = fill_mode.lower() + if fill_mode == "auto": + extra_headers = set() + extra_libs = set() + extra_header_paths = set() + extra_lib_paths = set() + has_softfloat = core.float_reg_file is not None + has_softvector = core.vector_reg_file is not None + extra_header_paths.add("etiss/jit") + extra_lib_paths.add("etiss/jit") + if has_softfloat: + extra_headers.add("etiss/jit/libsoftfloat.h") + extra_libs.add("softfloat") + if has_softvector: + extra_headers.add("etiss/jit/libsoftvector.h") + extra_headers.add("etiss/jit/softvector.h") + extra_libs.add("softvector") + extra_libs.add("etiss_softvector") + fill_jit_extensions = Template(filename=str(template_dir/'etiss_jit_extensions.mako')).render( + extra_headers=";".join(sorted(list(extra_headers))), + extra_libs=";".join(sorted(list(extra_libs))), + extra_header_paths=";".join(sorted(list(extra_header_paths))), + extra_lib_paths=";".join(sorted(list(extra_lib_paths))), + ) + fill_length_updater = Template(filename=str(template_dir/'etiss_length_updater.mako')).render(core_name=core.name) + else: + assert fill_mode == "empty", f"Unsupported fill_mode: {fill_mode}" + arch_source_template = Template(filename=str(template_dir/'etiss_arch_specific_cpp.mako')) error_fn = None @@ -219,7 +250,7 @@ def write_arch_specific_cpp(core: arch.CoreDef, start_time: str, output_path: pa raise M2TypeError(f"IRQ enable mask of {core.global_irq_en_memory.name} is not compile static") global_irq_en_mask = attr.value - txt = arch_header_template.render( + txt = arch_source_template.render( start_time=start_time, core_name=core.name, main_reg=core.main_reg_file, @@ -231,17 +262,20 @@ def write_arch_specific_cpp(core: arch.CoreDef, start_time: str, output_path: pa error_callbacks=error_callbacks, error_fn=error_fn, virtualstruct_regs=virtualstruct_regs, + fill_jit_extensions=fill_jit_extensions, + fill_length_updater=fill_length_updater, + fill_endianess_compensation=fill_endianess_compensation, ) with open(output_path / f"{core.name}ArchSpecificImp.cpp", "w", encoding="utf-8") as f: f.write(txt) def write_arch_gdbcore(core: arch.CoreDef, start_time: str, output_path: pathlib.Path, gdb_mapping: dict): - arch_header_template = Template(filename=str(template_dir/'etiss_arch_gdbcore.mako')) + arch_gdbcore_template = Template(filename=str(template_dir/'etiss_arch_gdbcore.mako')) logger.info("writing gdbcore") - txt = arch_header_template.render( + txt = arch_gdbcore_template.render( start_time=start_time, core_name=core.name, main_reg=core.main_reg_file, @@ -253,7 +287,7 @@ def write_arch_gdbcore(core: arch.CoreDef, start_time: str, output_path: pathlib f.write(txt) def write_arch_cmake(core: arch.CoreDef, start_time: str, output_path: pathlib.Path, separate: bool): - arch_header_template = Template(filename=str(template_dir/'etiss_arch_cmake.mako')) + arch_cmake_template = Template(filename=str(template_dir/'etiss_arch_cmake.mako')) logger.info("writing CMakeLists") @@ -264,7 +298,7 @@ def write_arch_cmake(core: arch.CoreDef, start_time: str, output_path: pathlib.P if separate: arch_files += [f'{core.name}_{ext_name}Instr.cpp' for ext_name in core.contributing_types if len(core.instructions_by_ext[ext_name]) > 0] - txt = arch_header_template.render( + txt = arch_cmake_template.render( start_time=start_time, core_name=core.name, arch_files=arch_files diff --git a/m2isar/backends/etiss/templates/etiss_arch_specific_cpp.mako b/m2isar/backends/etiss/templates/etiss_arch_specific_cpp.mako index 72ee369a..2cc70848 100644 --- a/m2isar/backends/etiss/templates/etiss_arch_specific_cpp.mako +++ b/m2isar/backends/etiss/templates/etiss_arch_specific_cpp.mako @@ -74,6 +74,13 @@ etiss::int32 ${core_name}Arch::handleException(etiss::int32 cause, ETISS_CPU *cp */ void ${core_name}Arch::initInstrSet(etiss::instr::ModedInstructionSet &mis) const { +% if fill_jit_extensions is not None: +${fill_jit_extensions} +% else: + /************************************************************************** + * JIT extensions should be defined here * + **************************************************************************/ +% endif if (false) { // Pre-compilation of instruction set to view instruction tree. Enable by setting 'true' above. @@ -104,9 +111,13 @@ ${callback}, %endfor +% if fill_length_updater is not None: +${fill_length_updater} +% else: /************************************************************************** * vis->length_updater_ should be replaced here * **************************************************************************/ +% endif } /** @@ -134,9 +145,13 @@ ${callback}, */ void ${core_name}Arch::compensateEndianess(ETISS_CPU *cpu, etiss::instr::BitArray &ba) const { +% if fill_endianess_compensation is not None: +${fill_endianess_compensation} +% else: /************************************************************************** * Endianess compensation * **************************************************************************/ +% endif } std::shared_ptr ${core_name}Arch::getVirtualStruct(ETISS_CPU *cpu) diff --git a/m2isar/backends/etiss/templates/etiss_jit_extensions.mako b/m2isar/backends/etiss/templates/etiss_jit_extensions.mako new file mode 100644 index 00000000..81c6ca09 --- /dev/null +++ b/m2isar/backends/etiss/templates/etiss_jit_extensions.mako @@ -0,0 +1,15 @@ + { + /* Set default JIT Extensions. Read Parameters set from ETISS configuration and append with architecturally needed */ + std::string cfgPar = ""; + cfgPar = etiss::cfg().get("jit.external_headers", ";"); + etiss::cfg().set("jit.external_headers", cfgPar + "${extra_headers}"); + + cfgPar = etiss::cfg().get("jit.external_libs", ";"); + etiss::cfg().set("jit.external_libs", cfgPar + "${extra_libs}"); + + cfgPar = etiss::cfg().get("jit.external_header_paths", ";"); + etiss::cfg().set("jit.external_header_paths", cfgPar + "${extra_header_paths}"); + + cfgPar = etiss::cfg().get("jit.external_lib_paths", ";"); + etiss::cfg().set("jit.external_lib_paths", cfgPar + "${extra_lib_paths}"); + } diff --git a/m2isar/backends/etiss/templates/etiss_length_updater.mako b/m2isar/backends/etiss/templates/etiss_length_updater.mako new file mode 100644 index 00000000..8c9123af --- /dev/null +++ b/m2isar/backends/etiss/templates/etiss_length_updater.mako @@ -0,0 +1,88 @@ + vis->length_updater_ = [](VariableInstructionSet &, InstructionContext &ic, BitArray &ba) { + std::function update${core_name}InstrLength = + [](InstructionContext &ic, etiss_uint32 opRd) { + ic.instr_width_fully_evaluated_ = true; + ic.is_not_default_width_ = true; + if (opRd == 0x3f) + ic.instr_width_ = 64; + else if ((opRd & 0x3f) == 0x1f) + ic.instr_width_ = 48; + else if (((opRd & 0x1f) >= 0x3) && ((opRd & 0x1f) < 0x1f)) + ic.instr_width_ = 32; + else if(opRd == 0x7f) /* P-Extension instructions */ + ic.instr_width_ = 32; + else if ((opRd & 0x3) != 0x3) + ic.instr_width_ = 16; + else + // This might happen when code is followed by data. + ic.is_not_default_width_ = false; + }; + + BitArrayRange op(6, 0); + etiss_uint32 opRd = op.read(ba); + + /*BitArrayRange fullOp(ba.byteCount()*8-1,0); + etiss_uint32 fullOpRd = fullOp.read(ba); + + std::stringstream ss; + ss << "Byte count: " << ba.byteCount()<< std::endl; + ss << "opcode: 0x" <= 0x3) || ((opRd & 0x1f) < 0x1f)) || (opRd == 0)) + { + ic.is_not_default_width_ = false; + break; + } + else if(opRd == 0x7f) /* P-Extension instructions */ + { + update${core_name}InstrLength(ic, opRd); + break; + } + else + { + update${core_name}InstrLength(ic, opRd); + break; + } + case 6: + if (((opRd & 0x3f) == 0x1f) || (opRd == 0)) + { + ic.is_not_default_width_ = false; + break; + } + else + { + update${core_name}InstrLength(ic, opRd); + break; + } + case 8: + if ((opRd == 0x3f) || (opRd == 0)) + { + ic.is_not_default_width_ = false; + break; + } + else + { + update${core_name}InstrLength(ic, opRd); + break; + } + default: + // This might happen when code is followed by data. + ic.is_not_default_width_ = false; + } + }; diff --git a/m2isar/backends/etiss/writer.py b/m2isar/backends/etiss/writer.py index b5818416..53735d19 100755 --- a/m2isar/backends/etiss/writer.py +++ b/m2isar/backends/etiss/writer.py @@ -87,6 +87,8 @@ def setup(): parser.add_argument("--coverage", action=BooleanOptionalAction, default=False, help="Generate coverage tracking code into model.") parser.add_argument("--log", default="info", choices=["critical", "error", "warning", "info", "debug"]) parser.add_argument("--gdb-xml-descr", nargs="+", default=[]) + parser.add_argument("--fill-mode", choices=["auto", "empty"], default="empty") + # TODO: add modes for rvv,... args = parser.parse_args() # configure logging @@ -174,7 +176,7 @@ def main(): write_arch_header(core, start_time, output_path) write_arch_cpp(core, start_time, output_path, False) write_arch_specific_header(core, start_time, output_path) - write_arch_specific_cpp(core, start_time, output_path, virtualstruct_regs) + write_arch_specific_cpp(core, start_time, output_path, virtualstruct_regs, args.fill_mode) write_arch_lib(core, start_time, output_path) write_arch_cmake(core, start_time, output_path, args.separate) write_arch_gdbcore(core, start_time, output_path, gdb_mapping) From 529f0c46e595a9e54e78e80f5a7ff7c561e82558 Mon Sep 17 00:00:00 2001 From: Philipp van Kempen Date: Mon, 27 Apr 2026 15:04:57 +0200 Subject: [PATCH 17/18] m2isar/backends/etiss/templates: lint --- .../etiss/templates/etiss_arch_gdbcore.mako | 12 +- .../templates/etiss_arch_specific_cpp.mako | 39 ++-- .../templates/etiss_arch_specific_h.mako | 215 ++++++++++-------- 3 files changed, 148 insertions(+), 118 deletions(-) diff --git a/m2isar/backends/etiss/templates/etiss_arch_gdbcore.mako b/m2isar/backends/etiss/templates/etiss_arch_gdbcore.mako index d9f6c9d9..69582d47 100644 --- a/m2isar/backends/etiss/templates/etiss_arch_gdbcore.mako +++ b/m2isar/backends/etiss/templates/etiss_arch_gdbcore.mako @@ -65,18 +65,20 @@ class ${core_name}GDBCore : public etiss::plugin::gdb::GDBCore } % endif % endif - switch (index){ + switch (index) + { % if mapping is not None: % for regnum, (name, name2) in mapping.items(): - case ${regnum}: return "${name2}"; // ${name} + case ${regnum}: + return "${name2}"; // ${name} % endfor % else: case ${main_reg.range.length}: return "instructionPointer"; % endif - /************************************************************************** - * Further register should be added here to send data over gdbserver * - ***************************************************************************/ + /************************************************************************** + * Further register should be added here to send data over gdbserver * + **************************************************************************/ } return ""; } diff --git a/m2isar/backends/etiss/templates/etiss_arch_specific_cpp.mako b/m2isar/backends/etiss/templates/etiss_arch_specific_cpp.mako index 2cc70848..c0949a1d 100644 --- a/m2isar/backends/etiss/templates/etiss_arch_specific_cpp.mako +++ b/m2isar/backends/etiss/templates/etiss_arch_specific_cpp.mako @@ -156,25 +156,26 @@ ${fill_endianess_compensation} std::shared_ptr ${core_name}Arch::getVirtualStruct(ETISS_CPU *cpu) { - auto ret = etiss::VirtualStruct::allocate(cpu, [](etiss::VirtualStruct::Field*f) { delete f; }); - - % if virtualstruct_regs is not None: - % for virtualstruct_class, idxs in virtualstruct_regs.items(): - % for idx in idxs: - % if isinstance(idx, range): - for (uint32_t i = ${idx.start}; i < ${idx.stop}; i += ${idx.step}){ - ret->addField(new ${virtualstruct_class}_${core_name}(*ret, i)); - } - % elif idx is None: - ret->addField(new ${virtualstruct_class}_${core_name}(*ret)); - % else: - ret->addField(new ${virtualstruct_class}_${core_name}(*ret, ${idx})); - % endif - % endfor - % endfor - % endif - - return ret; + auto ret = etiss::VirtualStruct::allocate(cpu, [](etiss::VirtualStruct::Field *f) { delete f; }); + + % if virtualstruct_regs is not None: + % for virtualstruct_class, idxs in virtualstruct_regs.items(): + % for idx in idxs: + % if isinstance(idx, range): + for (uint32_t i = ${idx.start}; i < ${idx.stop}; i += ${idx.step}) + { + ret->addField(new ${virtualstruct_class}_${core_name}(*ret, i)); + } + % elif idx is None: + ret->addField(new ${virtualstruct_class}_${core_name}(*ret)); + % else: + ret->addField(new ${virtualstruct_class}_${core_name}(*ret, ${idx})); + % endif + % endfor + % endfor + % endif + + return ret; } /** diff --git a/m2isar/backends/etiss/templates/etiss_arch_specific_h.mako b/m2isar/backends/etiss/templates/etiss_arch_specific_h.mako index f08284fb..5de27206 100644 --- a/m2isar/backends/etiss/templates/etiss_arch_specific_h.mako +++ b/m2isar/backends/etiss/templates/etiss_arch_specific_h.mako @@ -72,8 +72,8 @@ class RegField_${core_name} : public etiss::VirtualStruct::Field protected: virtual uint64_t _read(size_t offset) const { - assert((offset == 0 || (offset < (bitwidth_ / sizeof(uint64_t)))) && "Virtualstruct field offset out of range"); // clang-format off + assert((offset == 0 || (offset < (bitwidth_ / sizeof(uint64_t)))) && "Virtualstruct field offset out of range"); % if len(main_reg.children) > 0: return (uint64_t) *((${core_name}*)parent_.structure_)->${main_reg.name}[gprid_]; % else: @@ -84,8 +84,8 @@ class RegField_${core_name} : public etiss::VirtualStruct::Field virtual void _write(uint64_t val, size_t offset) { - assert((offset == 0 || (offset < (bitwidth_ / sizeof(uint64_t)))) && "Virtualstruct field offset out of range"); // clang-format off + assert((offset == 0 || (offset < (bitwidth_ / sizeof(uint64_t)))) && "Virtualstruct field offset out of range"); etiss::log(etiss::VERBOSE, "write to ETISS cpu state", name_, val); % if len(main_reg.children) > 0: *((${core_name}*)parent_.structure_)->${main_reg.name}[gprid_] = (etiss_uint${main_reg.size}) val; @@ -101,8 +101,9 @@ class FloatRegField_${core_name} : public etiss::VirtualStruct::Field { private: const unsigned gprid_; + public: - FloatRegField_${core_name}(etiss::VirtualStruct & parent,unsigned gprid) + FloatRegField_${core_name}(etiss::VirtualStruct &parent, unsigned gprid) // clang-format off : Field(parent, std::string("${float_reg.name}")+etiss::toString(gprid), @@ -111,11 +112,11 @@ class FloatRegField_${core_name} : public etiss::VirtualStruct::Field ${int(float_reg.size / 8)} ), gprid_(gprid) - // clang-format on - { - } + // clang-format on + { + } - FloatRegField_${core_name}(etiss::VirtualStruct & parent, std::string name, unsigned gprid) + FloatRegField_${core_name}(etiss::VirtualStruct &parent, std::string name, unsigned gprid) // clang-format off : Field(parent, name, @@ -124,76 +125,92 @@ class FloatRegField_${core_name} : public etiss::VirtualStruct::Field ${int(float_reg.size / 8)} ), gprid_(gprid) - // clang-format on - { - } + // clang-format on + { + } - virtual ~FloatRegField_${core_name}(){} + virtual ~FloatRegField_${core_name}() {} protected: - virtual uint64_t _read(size_t offset) const { - assert((offset == 0 || (offset < (bitwidth_ / sizeof(uint64_t)))) && "Virtualstruct field offset out of range"); - % if len(main_reg.children) > 0: - return (uint64_t) *((${core_name}*)parent_.structure_)->${float_reg.name}[gprid_]; - % else: - return (uint64_t) ((${core_name}*)parent_.structure_)->${float_reg.name}[gprid_]; - % endif + virtual uint64_t _read(size_t offset) const + { + // clang-format off + assert((offset == 0 || (offset < (bitwidth_ / sizeof(uint64_t)))) && "Virtualstruct field offset out of range"); + % if len(main_reg.children) > 0: + return (uint64_t) *((${core_name}*)parent_.structure_)->${float_reg.name}[gprid_]; + % else: + return (uint64_t) ((${core_name}*)parent_.structure_)->${float_reg.name}[gprid_]; + % endif + // clang-format on } - virtual void _write(uint64_t val, size_t offset) { - assert((offset == 0 || (offset < (bitwidth_ / sizeof(uint64_t)))) && "Virtualstruct field offset out of range"); - etiss::log(etiss::VERBOSE, "write to ETISS cpu state", name_, val); - % if len(main_reg.children) > 0: - *((${core_name}*)parent_.structure_)->${float_reg.name}[gprid_] = (etiss_uint${float_reg.size}) val; - % else: - ((${core_name}*)parent_.structure_)->${float_reg.name}[gprid_] = (etiss_uint${float_reg.size}) val; - % endif + virtual void _write(uint64_t val, size_t offset) + { + // clang-format off + assert((offset == 0 || (offset < (bitwidth_ / sizeof(uint64_t)))) && "Virtualstruct field offset out of range"); + etiss::log(etiss::VERBOSE, "write to ETISS cpu state", name_, val); + % if len(main_reg.children) > 0: + *((${core_name}*)parent_.structure_)->${float_reg.name}[gprid_] = (etiss_uint${float_reg.size}) val; + % else: + ((${core_name}*)parent_.structure_)->${float_reg.name}[gprid_] = (etiss_uint${float_reg.size}) val; + % endif + // clang-format on } - }; +}; % endif % if vector_reg: -class VectorRegField_${core_name} : public etiss::VirtualStruct::Field{ -private: - const unsigned gprid_; -public: - VectorRegField_${core_name}(etiss::VirtualStruct & parent,unsigned gprid) - : Field(parent, - std::string("${vector_reg.name}")+etiss::toString(gprid), - std::string("${vector_reg.name}")+etiss::toString(gprid), - R|W, - ${int((vector_reg.range.length // 32) // (vector_reg.size // 8))} - ), - gprid_(gprid) - {} - - VectorRegField_${core_name}(etiss::VirtualStruct & parent, std::string name, unsigned gprid) - : Field(parent, - name, - name, - R|W, - ${int((vector_reg.range.length // 32) // (vector_reg.size // 8))} - ), - gprid_(gprid) - {} - - virtual ~VectorRegField_${core_name}(){} - -protected: - virtual uint64_t _read(size_t offset) const { - assert((offset == 0 || (offset < (bitwidth_ / sizeof(uint64_t)))) && "Virtualstruct field offset out of range"); - // printf("v_read with gprid_ %d\n", gprid_); - // TODO: check for out of bounds? (offset < width_/8) - return (uint64_t) *((uint64_t*)&((${core_name}*)parent_.structure_)->${vector_reg.name}[gprid_ * width_ + sizeof(uint64_t) * offset]); - } - - virtual void _write(uint64_t val, size_t offset) { - assert((offset == 0 || (offset < (bitwidth_ / sizeof(uint64_t)))) && "Virtualstruct field offset out of range"); - etiss::log(etiss::VERBOSE, "write to ETISS cpu state", name_, val); - // printf("v_write (%lu) with gprid_ %d\n", val, gprid_); - // TODO: check for out of bounds? (offset < width_/8) - *((uint64_t*)&((${core_name}*)parent_.structure_)->${vector_reg.name}[gprid_ * width_ + sizeof(uint64_t)]) = (etiss_uint64) val; // TODO: write V[gprid_] instead - } +class VectorRegField_${core_name} : public etiss::VirtualStruct::Field +{ + private: + const unsigned gprid_; + + public: + VectorRegField_${core_name}(etiss::VirtualStruct &parent,unsigned gprid) + // clang-format off + : Field(parent, + std::string("${vector_reg.name}")+etiss::toString(gprid), + std::string("${vector_reg.name}")+etiss::toString(gprid), + R|W, + ${int((vector_reg.range.length // 32) // (vector_reg.size // 8))} + ), + gprid_(gprid) + // clang-format on + { + } + + VectorRegField_${core_name}(etiss::VirtualStruct &parent, std::string name, unsigned gprid) + // clang-format off + : Field(parent, + name, + name, + R|W, + ${int((vector_reg.range.length // 32) // (vector_reg.size // 8))} + ), + gprid_(gprid) + // clang-format on + { + } + + virtual ~VectorRegField_${core_name}() {} + + protected: + virtual uint64_t _read(size_t offset) const + { + // clang-format off + assert((offset == 0 || (offset < (bitwidth_ / sizeof(uint64_t)))) && "Virtualstruct field offset out of range"); + return (uint64_t) *((uint64_t*)&((${core_name}*)parent_.structure_)->${vector_reg.name}[gprid_ * width_ + sizeof(uint64_t) * offset]); + // clang-format on + } + + virtual void _write(uint64_t val, size_t offset) + { + // clang-format off + assert((offset == 0 || (offset < (bitwidth_ / sizeof(uint64_t)))) && "Virtualstruct field offset out of range"); + etiss::log(etiss::VERBOSE, "write to ETISS cpu state", name_, val); + *((uint64_t*)&((${core_name}*)parent_.structure_)->${vector_reg.name}[gprid_ * width_ + sizeof(uint64_t)]) = (etiss_uint64) val; // TODO: write V[gprid_] instead + // clang-format on + } }; % endif @@ -202,8 +219,9 @@ class CSRField_${core_name} : public etiss::VirtualStruct::Field { private: const unsigned gprid_; + public: - CSRField_${core_name}(etiss::VirtualStruct & parent,unsigned gprid) + CSRField_${core_name}(etiss::VirtualStruct &parent, unsigned gprid) // clang-format off : Field(parent, std::string("${csr_reg.name}")+etiss::toString(gprid), @@ -216,29 +234,38 @@ class CSRField_${core_name} : public etiss::VirtualStruct::Field { } - CSRField_${core_name}(etiss::VirtualStruct & parent, std::string name, unsigned gprid) - : Field(parent, - name, - name, - R|W, - ${int(csr_reg.size / 8)} - ), - gprid_(gprid) - {} - - virtual ~CSRField_${core_name}(){} - -protected: - virtual uint64_t _read(size_t offset) const { - assert((offset == 0 || (offset < (bitwidth_ / sizeof(uint64_t)))) && "Virtualstruct field offset out of range"); - return (uint64_t) ${core_name}_csr_read((ETISS_CPU*)parent_.structure_, nullptr, nullptr, (etiss_uint${csr_reg.size}) gprid_); - } - - virtual void _write(uint64_t val, size_t offset) { - assert((offset == 0 || (offset < (bitwidth_ / sizeof(uint64_t)))) && "Virtualstruct field offset out of range"); - etiss::log(etiss::VERBOSE, "write to ETISS cpu state", name_, val); - ${core_name}_csr_write((ETISS_CPU*)parent_.structure_, nullptr, nullptr, gprid_, (etiss_uint${csr_reg.size}) val); - } + CSRField_${core_name}(etiss::VirtualStruct &parent, std::string name, unsigned gprid) + // clang-format off + : Field(parent, + name, + name, + R|W, + ${int(csr_reg.size / 8)} + ), + gprid_(gprid) + // clang-format on + { + } + + virtual ~CSRField_${core_name}() {} + + protected: + virtual uint64_t _read(size_t offset) const + { + // clang-format off + assert((offset == 0 || (offset < (bitwidth_ / sizeof(uint64_t)))) && "Virtualstruct field offset out of range"); + return (uint64_t) ${core_name}_csr_read((ETISS_CPU*)parent_.structure_, nullptr, nullptr, (etiss_uint${csr_reg.size}) gprid_); + // clang-format on + } + + virtual void _write(uint64_t val, size_t offset) + { + // clang-format off + assert((offset == 0 || (offset < (bitwidth_ / sizeof(uint64_t)))) && "Virtualstruct field offset out of range"); + etiss::log(etiss::VERBOSE, "write to ETISS cpu state", name_, val); + ${core_name}_csr_write((ETISS_CPU*)parent_.structure_, nullptr, nullptr, gprid_, (etiss_uint${csr_reg.size}) val); + // clang-format on + } }; % endif @@ -264,16 +291,16 @@ class pcField_${core_name} : public etiss::VirtualStruct::Field protected: virtual uint64_t _read(size_t offset) const { - assert((offset == 0 || (offset < (bitwidth_ / sizeof(uint64_t)))) && "Virtualstruct field offset out of range"); // clang-format off + assert((offset == 0 || (offset < (bitwidth_ / sizeof(uint64_t)))) && "Virtualstruct field offset out of range"); return (uint64_t) ((ETISS_CPU *)parent_.structure_)->instructionPointer; // clang-format on } virtual void _write(uint64_t val, size_t offset) { - assert((offset == 0 || (offset < (bitwidth_ / sizeof(uint64_t)))) && "Virtualstruct field offset out of range"); // clang-format off + assert((offset == 0 || (offset < (bitwidth_ / sizeof(uint64_t)))) && "Virtualstruct field offset out of range"); etiss::log(etiss::VERBOSE, "write to ETISS cpu state", name_, val); ((ETISS_CPU *)parent_.structure_)->instructionPointer = (etiss_uint${main_reg.size}) val; // clang-format on From 7ee847121ae3320105275e87ccdcc5fa5dac5739 Mon Sep 17 00:00:00 2001 From: Philipp van Kempen Date: Wed, 6 May 2026 15:23:18 +0200 Subject: [PATCH 18/18] m2isar/backends/etiss/templates: implement jitFiles and helpers to support out-of-tree archlibs --- .../etiss/templates/etiss_arch_h.mako | 14 +++++ .../templates/etiss_arch_specific_cpp.mako | 53 +++++++++++++++++++ .../etiss/templates/etiss_jit_extensions.mako | 3 +- 3 files changed, 69 insertions(+), 1 deletion(-) diff --git a/m2isar/backends/etiss/templates/etiss_arch_h.mako b/m2isar/backends/etiss/templates/etiss_arch_h.mako index 8d422b8c..0bd40cfb 100644 --- a/m2isar/backends/etiss/templates/etiss_arch_h.mako +++ b/m2isar/backends/etiss/templates/etiss_arch_h.mako @@ -75,6 +75,20 @@ class ${core_name}Arch : public etiss::CPUArch */ virtual etiss::int32 handleException(etiss::int32 code, ETISS_CPU *cpu); + /** + @brief This function will return the base installation directory of the ArchLib + + @see ${core_name}ArchSpecificImp.h + */ + virtual std::string installDir() const; + + /** + @brief This function will return the include prefix relative to the base installation directory of the ArchLib + + @see ${core_name}ArchSpecificImp.h + */ + virtual std::string jitFiles() const; + /** @brief This function is called during CPUArch initialization diff --git a/m2isar/backends/etiss/templates/etiss_arch_specific_cpp.mako b/m2isar/backends/etiss/templates/etiss_arch_specific_cpp.mako index c0949a1d..f2a5e02b 100644 --- a/m2isar/backends/etiss/templates/etiss_arch_specific_cpp.mako +++ b/m2isar/backends/etiss/templates/etiss_arch_specific_cpp.mako @@ -21,6 +21,7 @@ #include "${core_name}Arch.h" #include "${core_name}ArchSpecificImp.h" #include "${core_name}Funcs.h" +#include "etiss/Memory.h" /** @brief This function will be called automatically in order to handling exceptions such as interrupt, system call, @@ -48,6 +49,58 @@ etiss::int32 ${core_name}Arch::handleException(etiss::int32 cause, ETISS_CPU *cp return 0; } +/** + @brief See etiss/src/Misc.cpp +*/ +static etiss::ModuleHandle GetCurrentModule() +{ + static etiss::ModuleHandle hModule = 0; + + if (!hModule) + { + hModule = etiss::GetModuleByAddress((uintptr_t)GetCurrentModule); + } + + return hModule; +} + +/** + @brief See etiss/src/Misc.cpp +*/ +static std::string GetCurrentModulePath() +{ + static std::string modulePath; + + if (modulePath == "") + { + modulePath = etiss::GetModulePath(GetCurrentModule()); + } + + return modulePath; +} + +/** + @brief Resolves the ETISS install dir for both in-tree as well as out-of-tree builds +*/ +std::string ${core_name}Arch::installDir() const +{ + auto archLib = GetCurrentModulePath(); + auto libPathLoc = archLib.find_last_of("/\\"); + auto libPath = archLib.substr(0, libPathLoc); + auto pluginsPathLoc = libPath.find_last_of("/\\"); + auto pluginsPath = libPath.substr(0, pluginsPathLoc); + auto archPathLoc = pluginsPath.find_last_of("/\\"); + return libPath.substr(0, archPathLoc); +} + +/** + @brief This function is called during InstrSet initialization returns path to jit includes +*/ +std::string ${core_name}Arch::jitFiles() const +{ + return installDir() + "/include/jit"; +} + /** @brief This function is called during CPUArch initialization diff --git a/m2isar/backends/etiss/templates/etiss_jit_extensions.mako b/m2isar/backends/etiss/templates/etiss_jit_extensions.mako index 81c6ca09..577b2f3a 100644 --- a/m2isar/backends/etiss/templates/etiss_jit_extensions.mako +++ b/m2isar/backends/etiss/templates/etiss_jit_extensions.mako @@ -1,4 +1,5 @@ { + std::string requiredJitFilesPath = jitFiles(); /* Set default JIT Extensions. Read Parameters set from ETISS configuration and append with architecturally needed */ std::string cfgPar = ""; cfgPar = etiss::cfg().get("jit.external_headers", ";"); @@ -8,7 +9,7 @@ etiss::cfg().set("jit.external_libs", cfgPar + "${extra_libs}"); cfgPar = etiss::cfg().get("jit.external_header_paths", ";"); - etiss::cfg().set("jit.external_header_paths", cfgPar + "${extra_header_paths}"); + etiss::cfg().set("jit.external_header_paths", cfgPar + "${extra_header_paths}" + requiredJitFilesPath); cfgPar = etiss::cfg().get("jit.external_lib_paths", ";"); etiss::cfg().set("jit.external_lib_paths", cfgPar + "${extra_lib_paths}");