Source code for m2isar.backends.isa_manual.writer

# 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) 2022
# Chair of Electrical Design Automation
# Technical University of Munich

"""Viewer tool to visualize an M2-ISA-R model hierarchy."""

import re
import argparse
import logging
import pathlib
import pickle
from collections import defaultdict
from mako.template import Template

from m2isar.metamodel import type_info

from .utils import generate_encoding
from ..coredsl2.writer import CDSLWriterVisitor, CoreDSL2Writer

from ...metamodel import M2_METAMODEL_VERSION, M2Model, arch, attribute_info
from ...metamodel.utils.expr_preprocessor import (process_attributes,
                                                  process_functions,
                                                  process_instructions)

[docs] HEADER_CONTENT = """ = M2-ISA-R Metamodel :doctype: article :encoding: utf-8 :lang: en :toc: left :toclevels: 3 :numbered: :stem: latexmath :le: ≤ :ge: ≥ :ne: ≠ :approx: ≈ :inf: ∞ :sectnums!: """
[docs] MAKO_TEMPLATE_INSTR = """ ==== ${name} ===== Assembly ```asm ${mnemonic} ${assembly} ``` ===== Behavior ```c ${behavior} ``` ===== Encoding ```wavedrom ${encoding} ``` ===== Exceptions % if throws: Throws: ${throws.name} % else: N/A % endif ===== Attributes % if attributes: % for key, value in attributes.items(): % if value: * ${key.name} (${value}) % else: * ${key.name} % endif % endfor % else: N/A % endif """
[docs] logger = logging.getLogger("isa_manual")
[docs] def sort_instruction(entry: "tuple[tuple[int, int], arch.Instruction]"): """Instruction sort key function. Sorts most restrictive encoding first.""" (code, mask), _ = entry return bin(mask).count("1"), code
#return code, bin(mask).count("1") # TODO: sort by name
[docs] def main(): """Main app entrypoint.""" # read command line args parser = argparse.ArgumentParser() parser.add_argument('top_level', help="A .m2isarmodel file containing the models to generate.") parser.add_argument('-s', '--separate', action='store_true', help="Generate separate .adoc files for each core.") parser.add_argument("--log", default="info", choices=["critical", "error", "warning", "info", "debug"]) parser.add_argument('--output', '-o', default="out.adoc", help="TODO") args = parser.parse_args() # initialize logging logging.basicConfig(level=getattr(logging, args.log.upper())) # resolve model paths top_level = pathlib.Path(args.top_level) abs_top_level = top_level.resolve() model_fname = abs_top_level output_file = pathlib.Path(args.output) if args.separate: raise NotImplementedError if abs_top_level.suffix == ".core_desc": logger.warning(".core_desc file passed as input. This is deprecated behavior, please change your scripts!") model_path = top_level.parent.joinpath('gen_model') if not model_path.exists(): raise FileNotFoundError('Models not generated!') model_fname = model_path / (abs_top_level.stem + '.m2isarmodel') logger.info("loading models") # load models with open(model_fname, 'rb') as f: model_obj: "M2Model" = pickle.load(f) if model_obj.model_version != M2_METAMODEL_VERSION: logger.warning("Loaded model version mismatch") models = model_obj.cores out_text = "" out_text += HEADER_CONTENT # preprocess model for core_name, core in models.items(): logger.info("preprocessing model %s", core_name) process_functions(core) process_instructions(core) process_attributes(core) # add each core to the treeview for core_name, core_def in sorted(models.items()): logger.info("processing core %s", core_name) out_text += f"== {core_name}\n" # group instructions by size instrs_by_size = defaultdict(dict) for k, v in core_def.instructions.items(): instrs_by_size[v.size][k] = v # generate instruction size groups for size, instrs in sorted(instrs_by_size.items()): # generate instructions out_text += f"=== {size}-bit Instructions\n" for (code, mask), instr_def in instrs.items(): # opcode_str = "{code:0{width}x}:{mask:0{width}x}".format(code=code, mask=mask, width=int(instr_def.size/4)) logger.info("processing instruction %s", instr_def.name) # generate encoding enc_str = [] for enc in instr_def.encoding: if isinstance(enc, arch.BitVal): enc_str.append(f"{enc.value:0{enc.length}b}") elif isinstance(enc, arch.BitField): enc_str.append(f"{enc.name}[{enc.range.upper}:{enc.range.lower}]") visitor = CDSLWriterVisitor() writer = CoreDSL2Writer(visitor, reduced=False, drop_first_op=True) writer.write_behavior2(instr_def.operation, drop_first=True) behavior_text = writer.text asm_str = None if instr_def.assembly: asm_str = instr_def.assembly.replace("\"", "") asm_str = re.sub(r"{([a-zA-Z0-9]+)}", r"\g<1>", re.sub(r"{([a-zA-Z0-9]+):[#0-9a-zA-Z\.]+}", r"{\g<1>}", re.sub(r"name\(([a-zA-Z0-9]+)\)", r"\g<1>", asm_str))) content_template = Template(MAKO_TEMPLATE_INSTR) encoding_text = generate_encoding(instr_def.encoding) content_text = content_template.render(name=instr_def.name, mnemonic=instr_def.mnemonic, assembly=asm_str if asm_str else "N/A", encoding=encoding_text, attributes=instr_def.attributes, throws=attribute_info.FunctionThrows(instr_def.throws), behavior=behavior_text) out_text += content_text with open(output_file, "w") as f: f.write(out_text)
if __name__ == "__main__": main()