Source code for m2isar.frontends.coredsl2_set.parser

# 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

import argparse
import itertools
import logging
import pathlib
import pickle
import sys

from ... import M2Error, M2SyntaxError
from ...metamodel import M2_METAMODEL_VERSION, M2Model, arch, behav, type_info, attribute_info
from ...metamodel.code_info import CodeInfoBase
from ..coredsl2.parser import try_eval_bool
from ..coredsl2.importer import recursive_import
from ..coredsl2.utils import make_parser
from ..coredsl2.behavior_model_builder import BehaviorModelBuilder
from ..coredsl2.architecture_model_builder import ArchitectureModelBuilder
from .load_order import LoadOrder

from ...backends.etiss.writer import BooleanOptionalAction  # TODO: refactor
from ...transforms.infer_types.transform import infer_types
from ...transforms.validate_behav.validate import validate_behav
from ...warnings import add_warnings_flags, KNOWN_WARNINGS


[docs] def parse_define(value): if "=" in value: name, raw_value = value.split("=", 1) else: name, raw_value = value, "1" if not name: raise argparse.ArgumentTypeError("define name must not be empty") try: parsed_value = int(raw_value, 0) except ValueError: raise argparse.ArgumentTypeError( f"invalid value for -D{name}: {raw_value!r}; expected an integer" ) return name, parsed_value
[docs] def main(): parser = argparse.ArgumentParser() parser.add_argument("top_level", help="The CoreDSL file.") parser.add_argument("--log", default="info", choices=["critical", "error", "warning", "info", "debug"]) parser.add_argument("-I", dest="includes", action="append", default=[], help="Extra include directories") parser.add_argument('--infer-types', action=BooleanOptionalAction, default=True, help="Run type inference after parsing.") parser.add_argument('--validate', action=BooleanOptionalAction, default=False, help="Run validator after parsing.") parser.add_argument('--allow-undefined-const', action=BooleanOptionalAction, default=False, help="Allow undefined constants.") parser.add_argument('--allow-empty', action=BooleanOptionalAction, default=False, help="Allow empty model (no sets).") parser.add_argument( "-D", dest="defines", action="append", type=parse_define, default=[], metavar="NAME[=VALUE]", help="Define a CoreDSL constant (default VALUE: 1), e.g. -DXLEN=32", ) add_warnings_flags(parser, KNOWN_WARNINGS, KNOWN_WARNINGS) # only if --validate parser.add_argument("--output", "-o", type=str, default=None) args = parser.parse_args() defines = dict(args.defines) allow_undefined_const = args.allow_undefined_const logging.basicConfig(level=getattr(logging, args.log.upper())) logger = logging.getLogger("parser") top_level = pathlib.Path(args.top_level) abs_top_level = top_level.resolve() extra_includes = args.includes extra_includes = list(map(lambda x: x.resolve(), map(pathlib.Path, extra_includes))) search_paths = [abs_top_level.parent] + extra_includes parser = make_parser(abs_top_level) try: logger.info("parsing top level") tree = parser.description_content() recursive_import(tree, search_paths) except M2SyntaxError as e: logger.critical("Error during parsing: %s", e) sys.exit(1) logger.info("reading instruction load order") lo = LoadOrder() try: sets = lo.visit(tree) except M2Error as e: logger.critical("Error during load order building: %s", e) sys.exit(1) if args.output is None: model_path = abs_top_level.parent.joinpath('gen_model') else: model_path = pathlib.Path(args.output) model_path.mkdir(exist_ok=True) temp_save = {} for set_name, set_def in sets.items(): logger.info(f"building architecture model for set %s", set_name) try: arch_builder = ArchitectureModelBuilder(merge=True) s = arch_builder.visit(set_def) if not isinstance(s, list): s = [s] # print("s", s) except M2Error as e: logger.critical("Error building architecture model of set %s: %s", set_name, e) sys.exit(-1) # for orig, overwritten in arch_builder._overwritten_instrs: # logger.warning( # "instr %s from extension %s was overwritten by %s from %s", # orig.name, # orig.ext_name, # overwritten.name, # overwritten.ext_name, # ) temp_save[set_name] = (s, arch_builder) sets[set_name] = s[-1] skip_sets = set() for set_name, set_def in sets.items(): logger.info("building behavior model for set %s", set_name) # print("set", set_name, set_def, dir(set_def)) warned_fns = set() logger.debug("checking core parameters") unassigned_const = False for const in set_def.parameters.values(): # print("const", const) if const.name in defines: logger.debug( "setting constant %s in set %s to %s from command line", const.name, set_name, defines[const.name], ) const.value = defines[const.name] if const.value is None: unassigned_const = True if allow_undefined_const: logger.warning("ignoring constant %s in set %s which has no value assigned...", const.name, set_name) continue logger.critical("constant %s in set %s has no value assigned!", const.name, set_name) if unassigned_const: if allow_undefined_const: skip_sets.add(set_name) continue else: sys.exit(-1) logger.debug("evaluating set parameters") for const_def in set_def.parameters.values(): const_def._value = const_def.value for mem_def in itertools.chain(set_def.memories.values(), set_def.memory_aliases.values()): if isinstance(mem_def.ty, type_info.ArrayType): size = arch.get_const_or_val(mem_def.ty.element_type.size) elif isinstance(mem_def.ty, type_info.PrimitiveType): size = arch.get_const_or_val(mem_def.ty.size) else: assert(isinstance(mem_def.ty, type_info.PointerType)) if isinstance(mem_def.ty.ty, type_info.ArrayType): size = arch.get_const_or_val(mem_def.ty.ty.element_type.size) else: size = arch.get_const_or_val(mem_def.ty.ty.size) mem_def.ty.size = size for attr_name, attr_ops in mem_def.attributes.items(): ops = [] for attr_op in attr_ops: try: behav_builder = BehaviorModelBuilder( set_def.parameters, set_def.memories, set_def.memory_aliases, set_def.register_banks, set_def.register_aliases, {}, set_def.functions, warned_fns, ) op = behav_builder.visit(attr_op) ops.append(op) except M2Error as e: logger.critical( 'error processing attribute "%s" of memory "%s": %s', attr_name, mem_def.name, e ) sys.exit(1) mem_def.attributes[attr_name] = ops for reg_def in itertools.chain(set_def.register_banks.values(), set_def.register_aliases.values()): if isinstance(reg_def.ty, type_info.ArrayType): reg_def.ty.element_type.size = arch.get_const_or_val(reg_def.ty.element_type.size) reg_def.ty.length = arch.get_const_or_val(reg_def.ty.length) elif isinstance(reg_def.ty, type_info.PrimitiveType): reg_def.ty.size = arch.get_const_or_val(reg_def.ty.size) else: assert(isinstance(reg_def.ty, type_info.PointerType)) # TODO: PointerType handlind looks like cancer!!!! pass # if isinstance(reg_def.ty.ty, type_info.ArrayType): # reg_def.ty.size = arch.get_const_or_val(reg_def.ty.ty.element_type.size) # else: # reg_def.ty.size = arch.get_const_or_val(reg_def.ty.ty.size) for attr_name, attr_ops in reg_def.attributes.items(): ops = [] for attr_op in attr_ops: try: behav_builder = BehaviorModelBuilder(set_def.parameters, {}, {}, set_def.register_banks, set_def.register_aliases, {}, set_def.functions, warned_fns) op = behav_builder.visit(attr_op) ops.append(op) except M2Error as e: logger.critical("error processing attribute \"%s\" of memory \"%s\": %s", attr_name, reg_def.name, e) sys.exit(1) reg_def.attributes[attr_name] = ops for fn_def in set_def.functions.values(): if isinstance(fn_def.operation, behav.Operation) and not fn_def.extern: raise M2SyntaxError(f"non-extern function {fn_def.name} has no body") fn_def.ty.size = arch.get_const_or_val(fn_def.ty.size) logger.debug("generating function behavior") for fn_name, fn_def in set_def.functions.items(): logger.debug("generating function %s", fn_name) logger.debug("generating attributes") for attr_name, attr_ops in fn_def.attributes.items(): ops = [] for attr_op in attr_ops: try: behav_builder = BehaviorModelBuilder( set_def.parameters, set_def.memories, set_def.memory_aliases, set_def.register_banks, set_def.register_aliases, fn_def.args, set_def.functions, warned_fns, ) op = behav_builder.visit(attr_op) ops.append(op) except M2Error as e: logger.critical( 'error processing attribute "%s" of function "%s": %s', attr_name, fn_def.name, e ) sys.exit(1) fn_def.attributes[attr_name] = ops behav_builder = BehaviorModelBuilder( set_def.parameters, set_def.memories, set_def.memory_aliases, set_def.register_banks, set_def.register_aliases, fn_def.args, set_def.functions, warned_fns ) if not isinstance(fn_def.operation, behav.Operation): try: op = behav_builder.visit(fn_def.operation) except M2Error as e: logger.critical("Error building behavior for function %s: %s", fn_name, e) sys.exit() fn_def.vars = behav_builder._vars if isinstance(op, list): fn_def.operation = behav.Operation(op) else: fn_def.operation = behav.Operation([op]) logger.debug("generating always blocks") for block_def in set_def.always_blocks.values(): logger.debug("generating always block %s", block_def.name) logger.debug("generating attributes") for attr_name, attr_ops in block_def.attributes.items(): ops = [] for attr_op in attr_ops: try: behav_builder = BehaviorModelBuilder( set_def.parameters, set_def.memories, set_def.memory_aliases, set_def.register_banks, set_def.register_aliases, {}, set_def.functions, warned_fns, ) op = behav_builder.visit(attr_op) ops.append(op) except M2Error as e: logger.critical( 'error processing attribute "%s" of instruction "%s": %s', attr_name, block_def.name, e ) sys.exit(1) block_def.attributes[attr_name] = ops behav_builder = BehaviorModelBuilder( set_def.parameters, set_def.memories, set_def.memory_aliases, set_def.register_banks, set_def.register_aliases, {}, set_def.functions, warned_fns ) try: op = behav_builder.visit(block_def.operation) except M2Error as e: logger.critical("error building behavior for always block %s: %s", block_def.name, e) sys.exit(1) if isinstance(op, list): op = behav.Operation(op) elif not isinstance(op, behav.Operation): op = behav.Operation([op]) block_def.operation = op block_def.vars = behav_builder._vars logger.debug("generating instruction behavior") instructions_by_enc = {} assert isinstance(set_def.instructions, list) overwritten_instrs: "list[tuple[arch.Instruction, arch.Instruction]]" = [] # for instr_def in set_def.instructions.values(): for instr_def in set_def.instructions: logger.debug("generating instruction %s", instr_def.name) logger.debug("generating attributes") for attr_name, attr_ops in instr_def.attributes.items(): ops = [] for attr_op in attr_ops: try: behav_builder = BehaviorModelBuilder( set_def.parameters, set_def.memories, set_def.memory_aliases, set_def.register_banks, set_def.register_aliases, instr_def.fields, set_def.functions, warned_fns, ) op = behav_builder.visit(attr_op) ops.append(op) except M2Error as e: logger.critical( 'error processing attribute "%s" of instruction "%s": %s', attr_name, instr_def.name, e ) sys.exit(1) instr_def.attributes[attr_name] = ops if attribute_info.InstrAttribute.ENABLE in instr_def.attributes: enable_attr = instr_def.attributes[attribute_info.InstrAttribute.ENABLE] assert isinstance(enable_attr, list) assert len(enable_attr) == 1 enable_attr = enable_attr[0] enable = try_eval_bool(enable_attr, set_def.parameters, set_def.memories, set_def.memory_aliases, instr_def.fields, set_def.functions, warned_fns) if enable is not None: assert isinstance(enable, bool) instr_def.attributes.pop(attribute_info.InstrAttribute.ENABLE) if not enable: continue print("set_def.parameters", set_def.parameters) print("set_def.memories", set_def.memories) print("set_def.memory_aliases", set_def.memory_aliases) print("set_def.register_banks", set_def.register_banks) print("set_def.register_aliases", set_def.register_aliases) behav_builder = BehaviorModelBuilder( set_def.parameters, set_def.memories, set_def.memory_aliases, set_def.register_banks, set_def.register_aliases, instr_def.fields, set_def.functions, warned_fns, ) try: op = behav_builder.visit(instr_def.operation) except M2Error as e: logger.critical( "error building behavior for instruction %s::%s: %s", instr_def.ext_name, instr_def.name, e ) raise e sys.exit(1) instr_def.vars = behav_builder._vars if isinstance(op, list): op = behav.Operation(op) else: op = behav.Operation([op]) # pc_inc = behav.Assignment( # behav.NamedReference(set_def.pc_memory), # behav.BinaryOperation( # behav.NamedReference(set_def.pc_memory), # behav.Operator("+"), # behav.IntLiteral(int(instr_def.size/8)) # ) # ) # op.statements.insert(0, pc_inc) instr_def.operation = op instr_id = (instr_def.code, instr_def.mask) # check for duplicate instructions if instr_id in instructions_by_enc: overwritten_instrs.append((instructions_by_enc[instr_id], instr_def)) instructions_by_enc[instr_id] = instr_def set_def.instructions = instructions_by_enc assert isinstance(set_def.instructions, dict) for orig, overwritten in overwritten_instrs: logger.warning("instr %s from extension %s was overwritten by %s from %s", orig.name, orig.ext_name, overwritten.name, overwritten.ext_name) sets = {set_name: set_def for set_name, set_def in sets.items() if set_name not in skip_sets} model_obj = M2Model(M2_METAMODEL_VERSION, {}, sets, CodeInfoBase.database) if len(sets) == 0: if args.allow_empty: logger.warning("no sets were generated, resulting model will be empty") else: logger.critical("no sets were generated, resulting model will be empty") sys.exit(-1) warnings_info = args.warnings if args.infer_types or args.validate: logger.info("Running type inference") model_obj = infer_types(model_obj, warnings_info=warnings_info) if args.validate: logger.info("Running validator") validate_behav(model_obj, warnings_info) logger.info("dumping model") with open(model_path / (abs_top_level.stem + ".m2isarmodel"), "wb") as f: pickle.dump(model_obj, f)
if __name__ == "__main__": main()