Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
8347485
[Metamodel] Refactor Literal and one common PrimitiveKind type
jokap11 Apr 24, 2026
80e6787
[MetaModel] Move Primitive+Literal Types+Literals into own file
jokap11 Apr 27, 2026
5bc24ac
[Backend] Use type_info
jokap11 Apr 27, 2026
0fb43f7
[Metamodel] Remove IntLiteral occurances
jokap11 Apr 28, 2026
58ba68c
[MetaModel] Switch Typing System
jokap11 Apr 29, 2026
f606f80
[MetaModel] Move etiss attribute into ETISS backend
jokap11 Apr 29, 2026
2ed0c73
[MetaModel] Attributes into type_info
jokap11 Apr 30, 2026
3e4c72b
[MetaModel] StaticType into TypeInfo
jokap11 Apr 30, 2026
8c3f788
[MetaModel] Move attributes into own attribute func
jokap11 Apr 30, 2026
c4e5099
[Metamodel] Use properties for expressive assertions
jokap11 Apr 30, 2026
4df2be8
[Metmodel] Warning for deprecated IntegerType Constr
jokap11 Apr 30, 2026
30bce23
[Metamodel] Typing remove redundant TypeKind enums
jokap11 Apr 30, 2026
e8ac700
[MetaModel] Avoid Deprecated IntegerType constructor
jokap11 Apr 30, 2026
9a47542
[MetaModel] Lit = 0 needs to be signed (Warning in GCC)
jokap11 Apr 30, 2026
bd999e8
Merge branch 'develop' into meta_type_refactor
jokap11 Apr 30, 2026
1627b73
[MetaModel] Abstract scalar class in more abstract Symbol
jokap11 Apr 30, 2026
7bec7bb
[MetaModel] WIP abstract Symbol class for ARCH
jokap11 May 1, 2026
d285249
[MetaModel] Bugfix use attributes[static] for Symbols
jokap11 May 4, 2026
e73670e
[MetaModel] Get Rid of IntegerType by PointerType
jokap11 May 4, 2026
dd0366e
[MetaModel] WIP split arch.Memory in RegisterBanks/Alias/MEM
jokap11 May 11, 2026
a8334e7
[MetaModel] WIP Architecture Classes bugfix for infer+simplify
jokap11 May 14, 2026
2121a50
[Transform] Type Inferene for Funcs as well
jokap11 May 14, 2026
dac6ea0
[Backend] MetaModel Viewer update for Alias/Memory
jokap11 May 14, 2026
68a21ee
[Metamodel] Remove addition Type info for Type Conv
jokap11 May 14, 2026
8e192b0
[Frontend] CoreDSL 2 add properties for regs/mem
jokap11 May 26, 2026
0cd5136
[FrontEnd] CoreDSL bugfix of Alias init + Dict instead of Set
jokap11 May 26, 2026
5c3bd66
[Backend] Make Viewer Ref type better readable
jokap11 May 26, 2026
56de585
[Backend] ETISS refactor with new Register/Memory/Alias classes
jokap11 May 26, 2026
f7deed3
[MetaModel] Remove init from Alias declaration
jokap11 May 26, 2026
56bc5b4
[MetaModel] Constrain Types of Literals
jokap11 May 26, 2026
27ba05a
[Backend] Etiss eval constants at a certain time
jokap11 May 26, 2026
9cfa338
[MetaModel] Switch Constant with more precise Parameter term
jokap11 May 29, 2026
ca607fd
[MetaModel] Rename ArrayType Subtype from kind to type
jokap11 May 29, 2026
7acca32
[Backend] ETISS change element_kind with type
jokap11 May 31, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 1 addition & 5 deletions m2isar/backends/coverage/id_transform.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,7 @@ def _(self, expr: behav.ConcatOperation, context: "IdMatcherContext"):
self.generate(expr.right, context)

@generate.register
def _(self, expr: behav.NumberLiteral, context: "IdMatcherContext"):
self._store_id(expr, context)

@generate.register
def _(self, expr: behav.IntLiteral, context: "IdMatcherContext"):
def _(self, expr: behav.Literal, context: "IdMatcherContext"):
self._store_id(expr, context)

@generate.register
Expand Down
121 changes: 85 additions & 36 deletions m2isar/backends/etiss/architecture_writer.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,54 +10,81 @@

import logging
import pathlib
from itertools import chain
from typing import Union

from mako.template import Template


from .instruction_utils import actual_size
from ... import M2TypeError
from ...metamodel import arch, behav
from ...metamodel import arch, behav, type_info, attribute_info
from . import BlockEndType
from .instruction_generator import (generate_fields,
generate_instruction_callback)
from .templates import template_dir

logger = logging.getLogger("arch_writer")

def write_child_reg_def(reg: arch.Memory, regs: "list[str]"):
"""Recursively generate register declarations"""
def write_child_reg_def(reg: Union[arch.Memory, arch.RegisterBank, arch.Register], regs: "list[str]"):
"""Recursively generate child declarations for Memories and Register(Banks)"""

logger.debug("processing register %s", reg)
if arch.MemoryAttribute.IS_PC in reg.attributes or arch.MemoryAttribute.IS_MAIN_MEM in reg.attributes:
if attribute_info.RegisterAttribute.IS_PC in reg.attributes or attribute_info.MemoryAttribute.IS_MAIN_MEM in reg.attributes:
logger.debug("this register is either the PC or main memory, skipping")
return

array_txt = f"[{reg.data_range.length}]" if reg.data_range.length > 1 else ""
assert(isinstance(reg.ty, (type_info.ArrayType, type_info.PrimitiveType, type_info.PointerType)))
if isinstance(reg.ty, type_info.ArrayType):
array_txt = f"[{arch.get_const_or_val(reg.ty.length)}]"
elif isinstance(reg.ty, type_info.PointerType):
array_txt = f"[{arch.get_const_or_val(reg.data_range.length)}]" if arch.get_const_or_val(reg.data_range.length) > 1 else ""
else:
array_txt = ""

if hasattr(reg, "children"):

if len(reg.children) > 0:
logger.debug("processing children")
for child in reg.children:
write_child_reg_def(child, regs)

# registers with children (aliases) are defined as two arrays:
# 1) array of pointers, used for actual access
# 2) array of actual data type, for every index which is not aliased
regs.append(f"etiss_uint{reg.actual_size} *{reg.name}{array_txt}")
regs.append(f"etiss_uint{reg.actual_size} ins_{reg.name}{array_txt}")
if isinstance(reg.ty, type_info.ArrayType):
size = actual_size(reg.ty.element_type.size)
elif isinstance(reg.ty, type_info.PrimitiveType):
size = actual_size(reg.ty.size)
else:
raise "Register Types needs to be of Array or PrimitiveType"

if (len(reg.children) > 0):
regs.append(f"etiss_uint{size} *{reg.name}{array_txt}")
regs.append(f"etiss_uint{size} ins_{reg.name}{array_txt}")
else:
regs.append(f"etiss_uint{actual_size(reg.ty.size)} {reg.name}{array_txt}")

else:
regs.append(f"etiss_uint{reg.actual_size} {reg.name}{array_txt}")
regs.append(f"etiss_uint{actual_size(reg.ty.size)} {reg.name}{array_txt}")

def write_arch_struct(core: arch.CoreDef, start_time: str, output_path: pathlib.Path):
arch_struct_template = Template(filename=str(template_dir/'etiss_arch_struct.mako'))
regs = []
mems = []

logger.info("writing architecture struct")

for _, mem_desc in core.memories.items():
write_child_reg_def(mem_desc, regs)
assert core.memories is not None and core.register_banks is not None
for _, reg_desc in chain(core.register_banks.items()):
write_child_reg_def(reg_desc, regs)
for _, mem_desc in chain(core.memories.items()):
write_child_reg_def(mem_desc, mems)

txt = arch_struct_template.render(
start_time=start_time,
core_name=core.name,
regs=regs
regs=regs,
mems=mems
)

with open(output_path / f"{core.name}.h", "w", encoding="utf-8") as f:
Expand All @@ -77,8 +104,9 @@ def write_arch_header(core: arch.CoreDef, start_time: str, output_path: pathlib.
with open(output_path / f"{core.name}Arch.h", "w", encoding="utf-8") as f:
f.write(txt)

def build_reg_hierarchy(reg: arch.Memory, ptr_regs: "list[arch.Memory]", actual_regs: "list[arch.Memory]",
alias_regs: "dict[arch.Memory, arch.Memory]", initval_regs: "list[arch.Memory]"):
def build_reg_hierarchy(reg: Union[arch.Memory, arch.Alias, arch.Register, arch.RegisterBank],
ptr_regs: "list[arch.Memory]", actual_regs: "list[arch.Memory]",
alias_regs: "dict[arch.Memory, arch.Memory]", initval_regs: "list[arch.Memory]"):
"""Populate the passed lists with memory objects of their category.

ptr_regs: Registers that need to be a pointer within ETISS
Expand All @@ -91,16 +119,21 @@ def build_reg_hierarchy(reg: arch.Memory, ptr_regs: "list[arch.Memory]", actual_
if reg._initval:
initval_regs.append(reg)

if len(reg.children) > 0:
for child in reg.children:
if child.is_main_mem:
logger.warning("main memory is a child memory of %s", reg)
continue
build_reg_hierarchy(child, ptr_regs, actual_regs, alias_regs, initval_regs)
alias_regs[child] = reg
ptr_regs.append(reg)
if isinstance(reg, (arch.Memory, arch.Register, arch.RegisterBank)):
if len(reg.children) > 0:
for child in reg.children:
if hasattr(child, "is_main_mem"):
if child.is_main_mem:
logger.warning("main memory is a child memory of %s", reg)
continue
build_reg_hierarchy(child, ptr_regs, actual_regs, alias_regs, initval_regs)
alias_regs[child] = reg
ptr_regs.append(reg)
else:
actual_regs.append(reg)
else:
actual_regs.append(reg)
assert isinstance(reg, arch.Alias)
return

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."""
Expand All @@ -115,15 +148,16 @@ def write_arch_cpp(core: arch.CoreDef, start_time: str, output_path: pathlib.Pat
logger.info("writing architecture class file")

# determine memory types
for _, mem_desc in core.memories.items():
if mem_desc.is_main_mem:
continue
for _, mem_desc in chain(core.memories.items(), core.register_banks.items(), core.memory_aliases.items(), core.register_aliases.items()):
if hasattr(mem_desc, "is_main_mem"):
if mem_desc.is_main_mem:
continue
build_reg_hierarchy(mem_desc, ptr_regs, actual_regs, alias_regs, initval_regs)

# 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)]
reg_names = [f"{core.main_reg_file.name}{n}" for n in range(core.main_reg_file.ty.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)]
reg_names += [f"{core.float_reg_file.name}{n}" for n in range(core.float_reg_file.ty.length)]
# TODO(annnnna42): add float reg names (F0-F31) here

# if main register file entries have aliases optionally use these for 'char* reg_name[]'
Expand All @@ -142,7 +176,9 @@ def write_arch_cpp(core: arch.CoreDef, start_time: str, output_path: pathlib.Pat
actual_regs=actual_regs,
alias_regs=alias_regs,
initval_regs=initval_regs,
procno_memory=core.procno_memory
procno_memory=core.procno_memory,
type_info=type_info,
arch=arch,
)

with open(output_path / f"{core.name}Arch.cpp", "w", encoding="utf-8") as f:
Expand All @@ -166,13 +202,26 @@ def write_arch_specific_header(core: arch.CoreDef, start_time: str, output_path:

logger.info("writing architecture specific header")

core.main_reg_file.ty.element_type.size = arch.get_const_or_val(core.main_reg_file.ty.element_type.size)
core.main_reg_file.ty.length = arch.get_const_or_val(core.main_reg_file.ty.length)
if core.float_reg_file is not None:
core.float_reg_file.ty.element_type.size = arch.get_const_or_val(core.float_reg_file.ty.element_type.size)
core.float_reg_file.ty.length = arch.get_const_or_val(core.float_reg_file.ty.length)
if core.vector_reg_file is not None:
core.vector_reg_file.ty.element_type.size = arch.get_const_or_val(core.vector_reg_file.ty.element_type.size)
core.vector_reg_file.ty.length = arch.get_const_or_val(core.vector_reg_file.ty.length)
if core.csr_reg_file is not None:
core.csr_reg_file.ty.element_type.size = arch.get_const_or_val(core.csr_reg_file.ty.element_type.size)
core.csr_reg_file.ty.length = arch.get_const_or_val(core.csr_reg_file.ty.length)

txt = arch_specific_header_template.render(
start_time=start_time,
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
csr_reg=core.csr_reg_file,
arch=arch
)

with open(output_path / f"{core.name}ArchSpecificImp.h", "w", encoding="utf-8") as f:
Expand Down Expand Up @@ -215,21 +264,21 @@ def write_arch_specific_cpp(core: arch.CoreDef, start_time: str, output_path: pa
error_fn = None

for fn in core.functions.values():
if arch.FunctionAttribute.ETISS_TRAP_ENTRY_FN in fn.attributes:
if attribute_info.FunctionAttribute.ETISS_TRAP_ENTRY_FN in fn.attributes:
error_fn = fn
break

for fn in core.functions.values():
if arch.FunctionAttribute.ETISS_TRAP_TRANSLATE_FN in fn.attributes:
if attribute_info.FunctionAttribute.ETISS_TRAP_TRANSLATE_FN in fn.attributes:
error_fn = fn
break

error_callbacks: "dict[int, str]" = {}

if error_fn is not None:
for bitsize in core.instr_classes:
error_bitfield = arch.BitField("error_code", arch.RangeSpec(31, 0), arch.DataType.U)
error_instr = arch.Instruction(f"trap_entry {bitsize}", {arch.InstrAttribute.NO_CONT: None}, [error_bitfield], "", "", None, None)
error_bitfield = arch.BitField("error_code", arch.RangeSpec(31, 0), type_info.TypeKind.UINT)
error_instr = arch.Instruction(f"trap_entry {bitsize}", {attribute_info.InstrAttribute.NO_CONT: None}, [error_bitfield], "", "", None, None)
error_bitfield_descr = error_instr.fields.get("error_code")
error_op = behav.Operation([
behav.ProcedureCall(error_fn, [behav.NamedReference(error_bitfield_descr)])
Expand All @@ -245,8 +294,8 @@ def write_arch_specific_cpp(core: arch.CoreDef, start_time: str, output_path: pa

global_irq_en_mask = None
if core.global_irq_en_memory is not None:
attr = core.global_irq_en_memory.attributes[arch.MemoryAttribute.ETISS_IS_GLOBAL_IRQ_EN][0]
if not isinstance(attr, behav.IntLiteral):
attr = core.global_irq_en_memory.attributes[attribute_info.MemoryAttribute.ETISS_IS_GLOBAL_IRQ_EN][0]
if not isinstance(attr, behav.Literal):
raise M2TypeError(f"IRQ enable mask of {core.global_irq_en_memory.name} is not compile static")
global_irq_en_mask = attr.value

Expand Down
48 changes: 25 additions & 23 deletions m2isar/backends/etiss/instruction_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,17 @@

from mako.template import Template

from ...metamodel import arch, behav
from ...metamodel import arch, behav, type_info, attribute_info
from . import BlockEndType, instruction_utils
from .instruction_transform import InstructionTransformVisitor
from .instruction_utils import actual_size
from .templates import template_dir

logger = logging.getLogger("instruction_generator")

def generate_arg_str(arg: arch.FnParam):
arg_name = f" {arg.name}" if arg.name is not None else ""
return f'{instruction_utils.data_type_map[arg.data_type]}{arg.actual_size}{arg_name}'
return f'{instruction_utils.data_type_map[arg.ty.kind]}{actual_size(arg.ty.size)}{arg_name}'

def generate_functions(core: arch.CoreDef, static_scalars: bool, decls_only: bool, generate_coverage: bool):
"""Return a generator object to generate function behavior code. Uses function
Expand All @@ -33,7 +34,7 @@ def generate_functions(core: arch.CoreDef, static_scalars: bool, decls_only: boo

fn_template = Template(filename=str(template_dir/'etiss_function.mako'))

core_default_width = core.constants['XLEN'].value
core_default_width = core.parameters['XLEN'].value
core_name = core.name

for fn_name, fn_def in core.functions.items():
Expand All @@ -42,13 +43,14 @@ def generate_functions(core: arch.CoreDef, static_scalars: bool, decls_only: boo
if fn_def.extern and not decls_only:
continue

return_type = instruction_utils.data_type_map[fn_def.data_type]
if fn_def.size:
return_type += f'{fn_def.actual_size}'
return_type = instruction_utils.data_type_map[fn_def.ty.kind]
if fn_def.ty.size:
return_type += f'{actual_size(fn_def.ty.size)}'

# set up a transformer context and generate code
context = instruction_utils.TransformerContext(core.constants, core.memories, core.memory_aliases, fn_def.args, fn_def.attributes,
core.functions, 0, core_default_width, core_name, static_scalars, core.intrinsics, generate_coverage, True)
context = instruction_utils.TransformerContext(core.parameters, core.memories, core.memory_aliases, core.register_banks,
core.register_aliases, fn_def.args, fn_def.attributes, core.functions,
0, core_default_width, core_name, static_scalars, core.intrinsics, generate_coverage, True)

logger.debug("generating code for %s", fn_name)

Expand All @@ -66,7 +68,7 @@ def generate_functions(core: arch.CoreDef, static_scalars: bool, decls_only: boo
args_list = [generate_arg_str(arg) for arg in fn_def.args.values()]

# if function needs access to ETISS architecture data, add these as arguments to the function
if arch.FunctionAttribute.ETISS_NEEDS_ARCH in fn_def.attributes or (not fn_def.extern and not fn_def.static):
if attribute_info.FunctionAttribute.ETISS_NEEDS_ARCH in fn_def.attributes or (not fn_def.extern and not fn_def.static):
args_list = ['ETISS_CPU * const cpu', 'ETISS_System * const system', 'void * const * const plugin_pointers'] + args_list

fn_args = ', '.join(args_list)
Expand Down Expand Up @@ -105,8 +107,8 @@ def generate_fields(core_default_width, instr_def: arch.Instruction):
if enc.name not in seen_fields:
# first encounter of this parameter, instantiate a new integer for it
seen_fields[enc.name] = 255
width = instr_def.fields[enc.name].actual_size
fields_code += f'{instruction_utils.data_type_map[enc.data_type]}{width} {enc.name} = 0;\n'
width = actual_size(instr_def.fields[enc.name].ty.size)
fields_code += f'{instruction_utils.data_type_map[enc.ty.kind]}{width} {enc.name} = 0;\n'

lower = enc.range.lower
length = enc.range.length
Expand All @@ -131,7 +133,7 @@ def generate_fields(core_default_width, instr_def: arch.Instruction):
asm_printer_code.append(f'{field_name}=" + std::to_string({field_name}) + "')

# generate sign extension if necessary
if field_descr.data_type == arch.DataType.S and field_descr.size < core_default_width:
if field_descr.ty.kind == type_info.TypeKind.INT and field_descr.size < core_default_width:
fields_code += '\n'
fields_code += f'struct {{etiss_int{core_default_width} x:{field_descr.size};}} {field_name}_ext;\n'
fields_code += f'{field_name} = {field_name}_ext.x = {field_name};'
Expand All @@ -146,20 +148,20 @@ def generate_instruction_callback(core: arch.CoreDef, instr_def: arch.Instructio
instr_name = instr_def.name
core_name = core.name
misc_code = []
core_default_width = core.constants['XLEN'].value
core_default_width = core.parameters['XLEN'].value
fields_code, _, _, enc_idx = fields

callback_template = Template(filename=str(template_dir/'etiss_instruction_callback.mako'))

context = instruction_utils.TransformerContext(core.constants, core.memories, core.memory_aliases, instr_def.fields, instr_def.attributes,
core.functions, enc_idx, core_default_width, core_name, static_scalars, core.intrinsics, generate_coverage, False)
context = instruction_utils.TransformerContext(core.parameters, core.memories, core.memory_aliases, core.register_banks, core.register_aliases, instr_def.fields, instr_def.attributes,
core.functions, enc_idx, core_default_width, core_name, static_scalars, core.intrinsics, generate_coverage, False)

# force a block end if necessary
if ((arch.InstrAttribute.NO_CONT in instr_def.attributes
and arch.InstrAttribute.COND not in instr_def.attributes
if ((attribute_info.InstrAttribute.NO_CONT in instr_def.attributes
and attribute_info.InstrAttribute.COND not in instr_def.attributes
and block_end_on == BlockEndType.UNCOND)
or (
arch.InstrAttribute.NO_CONT in instr_def.attributes
attribute_info.InstrAttribute.NO_CONT in instr_def.attributes
and block_end_on == BlockEndType.ALL)
):

Expand Down Expand Up @@ -196,7 +198,7 @@ def generate_instructions(core: arch.CoreDef, static_scalars: bool, block_end_on

error_fn = None
for fn in core.functions.values():
if arch.FunctionAttribute.ETISS_TRAP_TRANSLATE_FN in fn.attributes:
if attribute_info.FunctionAttribute.ETISS_TRAP_TRANSLATE_FN in fn.attributes:
error_fn = fn
break

Expand Down Expand Up @@ -224,20 +226,20 @@ def gen_rand_suffix(length: int = 8):
instr_def.attributes = []

# generate instruction parameter extraction code
fields = generate_fields(core.constants['XLEN'].value, instr_def)
fields = generate_fields(core.parameters['XLEN'].value, instr_def)
fields_code, asm_printer_code, seen_fields, enc_idx = fields

code_string = f'{code:#0{int(enc_idx/4)}x}'
mask_string = f'{mask:#0{int(enc_idx/4)}x}'

if arch.InstrAttribute.ENABLE in instr_def.attributes:
cond = instr_def.attributes[arch.InstrAttribute.ENABLE]
if attribute_info.InstrAttribute.ENABLE in instr_def.attributes:
cond = instr_def.attributes[attribute_info.InstrAttribute.ENABLE]
new_op = behav.Operation([
behav.Conditional(
[cond[0]],
[
instr_def.operation.statements,
behav.ProcedureCall(error_fn, [behav.IntLiteral(-11)])
behav.ProcedureCall(error_fn, [behav.Literal(-11, type_info.TypeKind.NONE)])
]
)
])
Expand Down
Loading