Source code for microprobe.target.isa.instruction_format
# Copyright 2011-2021 IBM Corporation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
""":mod:`microprobe.target.isa.instruction_format` module
"""
# Futures
from __future__ import absolute_import, division, annotations
# Built-in modules
import abc
import os
from typing import TYPE_CHECKING, List, Tuple
# Third party modules
# Own modules
from microprobe.exceptions import MicroprobeArchitectureDefinitionError, \
MicroprobeLookupError
from microprobe.utils.logger import get_logger
from microprobe.utils.typeguard_decorator import typeguard_testsuite
from microprobe.utils.yaml import read_yaml
# Type hinting
if TYPE_CHECKING:
from microprobe.target.isa.instruction_field import InstructionField
# Constants
SCHEMA = os.path.join(os.path.dirname(os.path.abspath(__file__)), "schemas",
"instruction_format.yaml")
LOG = get_logger(__name__)
__all__ = [
"import_definition", "InstructionFormat", "GenericInstructionFormat"
]
# Functions
[docs]
@typeguard_testsuite
def import_definition(cls, filenames, ifields):
"""
:param filenames:
:param ifields:
"""
LOG.debug("Start")
iformats = {}
iformats_duplicated = {}
for filename in filenames:
iformat_data = read_yaml(filename, SCHEMA)
if iformat_data is None:
continue
for elem in iformat_data:
name = elem["Name"]
descr = elem.get("Description", "No description")
assembly = elem["Assembly"]
# TODO: Document the convention
nonzero_fields = [
field for field in elem["Fields"] if not field.startswith("0_")
]
key = tuple([tuple(elem["Fields"]), assembly])
if key in iformats_duplicated:
LOG.warning(
"Similar definition of instruction format: '%s' "
"and '%s'. Check if definition needed.", name,
iformats_duplicated[key])
else:
iformats_duplicated[key] = name
if len(nonzero_fields) != len(set(nonzero_fields)):
raise MicroprobeArchitectureDefinitionError(
f"Definition of instruction format '{name}' found in "
f"'{filename}' contains duplicated fields.")
try:
fields = [ifields[ifieldname] for ifieldname in elem["Fields"]]
except KeyError as key:
raise MicroprobeArchitectureDefinitionError(
f"Unknown field {key} definition in instruction format "
f"'{name}' found in '{filename}'.")
iformat = cls(name, descr, fields, assembly)
if name in iformats:
raise MicroprobeArchitectureDefinitionError(
f"Duplicated definition of instruction format '{name}' "
f"found in '{filename}'")
LOG.debug(iformat)
iformats[name] = iformat
LOG.debug("End")
return iformats
# Classes
[docs]
@typeguard_testsuite
class InstructionFormat(abc.ABC):
"""Abstract class to represent an instruction format"""
[docs]
@abc.abstractmethod
def __init__(self, fname: str, descr: str):
"""
:param fname:
:param descr:
"""
self._name = fname
self._descr = descr
@property
def name(self):
return self._name
@property
def description(self):
return self._descr
@property
@abc.abstractmethod
def fields(self) -> List[InstructionField]:
raise NotImplementedError
@property
@abc.abstractmethod
def assembly_format(self) -> str:
raise NotImplementedError
[docs]
@abc.abstractmethod
def get_operands(self):
"""Returns a :class:`~.list` of :func:`tuple` of three elements.
The first is a :class:`~.Operand` object, the second is a
:class:`~.bool` indicating if the operand is an input operand and the
third is a :class:`~.bool` indicating if the operand is an output
operand.
"""
raise NotImplementedError
[docs]
@abc.abstractmethod
def get_fields(self) -> List[InstructionField]:
"""Returns a :class:`~.list` of
the :class:`~.InstructionField`
"""
raise NotImplementedError
[docs]
@abc.abstractmethod
def get_field(self, fname: str) -> InstructionField:
"""Returns a the :class:`~.InstructionField` with name
*fname*.
:param fname:
"""
raise NotImplementedError
[docs]
@abc.abstractmethod
def get_field_props(self, fname: str):
"""Returns extra properties of field with name *fname*.
:param fname: Field name.
:type fname: :class:`~.str`
"""
raise NotImplementedError
[docs]
@abc.abstractmethod
def get_findex(self, fname: str):
"""Returns the index of the field *fname* within the instruction format
:param fname: Field name.
:type fname: :class:`~.str`
"""
raise NotImplementedError
[docs]
@abc.abstractmethod
def flip_fields(self, fname1: str, fname2: str):
"""Interchanges the position of the fields with name *fname1* and
*fname2*.
:param fname1: Field 1 name.
:type fname1: :class:`~.str`
:param fname2: Field 2 name.
:type fname2: :class:`~.str`.
"""
raise NotImplementedError
[docs]
@abc.abstractmethod
def set_fields(self, fields: List[InstructionField], reset: bool = True):
"""Sets the fields of the instruction format. If *reset* is *True* the
properties and the flip records of the fields are removed.
:param fields: List of fields.
:type fields: :class:`~.list` of
:class:`~.InstructionField`
:param reset: Flag indicating if a full reset is needed
(Default value = True)
:type reset: :class:`~.bool`
"""
raise NotImplementedError
def __str__(self):
values = "%-10s : %-30s ([" % (self.name, self.description)
fields = []
for field in self.fields:
fmt = "{0:^%d}" % (field.size + (field.size - 4) // 4)
fields.append(fmt.format(field.name))
values = "%s%s])" % (values, "|".join(fields))
return values
[docs]
def full_report(self, tabs=0):
rstr = "\t" * tabs + str(self)
rstr += "\n"
rstr += "\t" * tabs + "%-10s : %-30s\n" % ("Assembly",
self.assembly_format)
for field in self.fields:
rstr += "\t" * tabs + \
"Fieldname : %s (bitsize: %d)\n" % (field.name, field.size)
return rstr
[docs]
@typeguard_testsuite
class GenericInstructionFormat(InstructionFormat):
"""Instruction format generic class."""
[docs]
def __init__(self, fname: str, descr: str, fields: List[InstructionField],
assembly: str):
"""
:param fname:
:param descr:
:param fields:
:param assembly:
"""
super(GenericInstructionFormat, self).__init__(fname, descr)
self._fname = fname
self._fields: List[InstructionField] = []
self._props: List[None] = [
] # TODO: Check if this list of None is needed
self._flips: List[Tuple[int, int]] = []
self._length = 0
self._assembly_format = assembly
for field in fields:
self._add_field(field)
self._compute_length()
@property
def fields(self):
return self._fields
@property
def length(self):
return self._length
@property
def assembly_format(self):
return self._assembly_format
[docs]
def get_operands(self):
"""Returns a :class:`~.list` of :func:`tuple` of three elements.
The first is a :class:`~.Operand` object, the second is a
:class:`~.bool` indicating if the operand is an input operand and the
third is a :class:`~.bool` indicating if the operand is an output
operand.
"""
return [(field.get_foperand(), field.is_input(), field.is_output())
for field in self.get_fields() if field.get_fshow()]
[docs]
def get_fields(self):
"""Returns a :class:`~.list` of the
:class:`~.InstructionField`
"""
return self._fields
[docs]
def get_field(self, fname: str):
"""Returns a the :class:`~.InstructionField` with name
*fname*.
:param fname:
"""
field = [
field for field in self.get_fields() if field.get_fname() == fname
]
if len(field) == 0:
raise MicroprobeLookupError(
f"Unable to find a field with name '{fname}'")
assert len(
field
) == 1, "Field names should be key identifiers." + \
f" Field '{fname}' is duplicated"
return field[0]
[docs]
def get_field_props(self, fname: str):
"""Returns extra properties of field with name *fname*.
:param fname: Field name.
:type fname: :class:`~.str`
"""
idx = self.get_findex(fname)
return self._props[idx]
[docs]
def get_findex(self, fname: str):
"""Returns the index of the field *fname* within the instruction
format.
:param fname: Field name.
:type fname: :class:`~.str`
"""
return self.get_fields().index(self.get_field(fname))
[docs]
def flip_fields(self, fname1: str, fname2: str):
"""Interchanges the position of the fields with name *fname1* and
*fname2*.
:param fname1: Field 1 name.
:type fname1: :class:`~.str`
:param fname2: Field 2 name.
:type fname2: :class:`~.str`.
"""
idx1 = self.get_findex(fname1)
idx2 = self.get_findex(fname2)
fields = self.get_fields()
tmp_field = fields[idx1]
fields[idx1] = fields[idx2]
fields[idx2] = tmp_field
self.set_fields(fields, reset=False)
tmp_prop = self._props[idx1]
self._props[idx1] = self._props[idx2]
self._props[idx2] = tmp_prop
self._flips.append((idx1, idx2))
[docs]
def set_fields(self, fields: List[InstructionField], reset: bool = True):
"""Sets the fields of the instruction format. If *reset* is *True* the
properties and the flip records of the fields are removed.
:param fields: List of fields.
:type fields: :class:`~.list` of
:class:`~.InstructionField`
:param reset: Flag indicating if a full reset is needed
(Default value = True)
:type reset: :class:`~.bool`
"""
self._fields = fields
if reset:
self._props = [None] * len(self.get_fields())
self._flips = []
self._compute_length()
def _add_field(self, field: InstructionField):
"""Adds an field to the instruction format.
:param field: Instruction field
:type field: :class:`~.InstructionField`
"""
self._fields.append(field)
self._props.append(None)
def _compute_length(self):
length = sum([field.size for field in self._fields])
if length % 8 != 0:
LOG.error("%s", self)
LOG.error("\tTotal length: %d", length)
for field in self._fields:
LOG.error("\t\t - %s: %d", field.name, field.size)
raise MicroprobeArchitectureDefinitionError(
"Instruction format"
" '%s' length is not multiple of a byte" % self.name)
self._length = sum([field.size for field in self._fields]) // 8