|  | # Copyright lowRISC contributors. | 
|  | # Licensed under the Apache License, Version 2.0, see LICENSE for details. | 
|  | # SPDX-License-Identifier: Apache-2.0 | 
|  |  | 
|  | import re | 
|  | from typing import Dict, Tuple, Union | 
|  |  | 
|  | from .bool_literal import BoolLiteral | 
|  | from .encoding_scheme import EncSchemeField, EncSchemes | 
|  | from .yaml_parse_helpers import check_keys, check_str | 
|  |  | 
|  |  | 
|  | class EncodingField: | 
|  | '''A single element of an encoding's mapping''' | 
|  | def __init__(self, | 
|  | value: Union[BoolLiteral, str], | 
|  | scheme_field: EncSchemeField) -> None: | 
|  | self.value = value | 
|  | self.scheme_field = scheme_field | 
|  |  | 
|  | @staticmethod | 
|  | def from_yaml(as_str: str, | 
|  | scheme_field: EncSchemeField, | 
|  | what: str) -> 'EncodingField': | 
|  | # The value should either be a boolean literal ("000xx11" or similar) | 
|  | # or should be a name, which is taken as the name of an operand. | 
|  | if not as_str: | 
|  | raise ValueError('Empty string as {}.'.format(what)) | 
|  |  | 
|  | # Set self.value to be either the bool literal or the name of the | 
|  | # operand. | 
|  | value = ''  # type: Union[BoolLiteral, str] | 
|  | if re.match(r'b[01x_]+$', as_str): | 
|  | value = BoolLiteral.from_string(as_str, what) | 
|  |  | 
|  | # Check that the literal operand value matches the width of the | 
|  | # schema field. | 
|  | if scheme_field.bits.width != value.width: | 
|  | raise ValueError('{} is mapped to a literal value with width ' | 
|  | '{}, but the encoding schema field has ' | 
|  | 'width {}.' | 
|  | .format(what, value.width, | 
|  | scheme_field.bits.width)) | 
|  | else: | 
|  | value = as_str | 
|  |  | 
|  | # Track the scheme field as well (so we don't have to keep track of a | 
|  | # scheme once we've made an encoding object) | 
|  | return EncodingField(value, scheme_field) | 
|  |  | 
|  |  | 
|  | class Encoding: | 
|  | '''The encoding for an instruction''' | 
|  | def __init__(self, | 
|  | yml: object, | 
|  | schemes: EncSchemes, | 
|  | mnemonic: str): | 
|  | what = 'encoding for instruction {!r}'.format(mnemonic) | 
|  | yd = check_keys(yml, what, ['scheme', 'mapping'], []) | 
|  |  | 
|  | scheme_what = 'encoding scheme for instruction {!r}'.format(mnemonic) | 
|  | scheme_name = check_str(yd['scheme'], scheme_what) | 
|  | scheme_fields = schemes.resolve(scheme_name, mnemonic) | 
|  |  | 
|  | what = 'encoding mapping for instruction {!r}'.format(mnemonic) | 
|  |  | 
|  | # Check we've got exactly the right fields for the scheme | 
|  | ydm = check_keys(yd['mapping'], what, list(scheme_fields.op_fields), []) | 
|  |  | 
|  | # Build a map from operand name to the name of a field that uses it. | 
|  | self.op_to_field_name = {}  # type: Dict[str, str] | 
|  |  | 
|  | self.fields = {} | 
|  | for field_name, scheme_field in scheme_fields.fields.items(): | 
|  | if scheme_field.value is not None: | 
|  | field = EncodingField(scheme_field.value, scheme_field) | 
|  | else: | 
|  | field_what = ('value for {} field in ' | 
|  | 'encoding for instruction {!r}' | 
|  | .format(field_name, mnemonic)) | 
|  | ef_val = check_str(ydm[field_name], field_what) | 
|  | field = EncodingField.from_yaml(ef_val, | 
|  | scheme_fields.fields[field_name], | 
|  | field_what) | 
|  |  | 
|  | # If the field's value has type str, the field uses an operand | 
|  | # rather than a literal. Check for linearity and store the | 
|  | # mapping. | 
|  | if isinstance(field.value, str): | 
|  | other_field_name = self.op_to_field_name.get(field.value) | 
|  | if other_field_name is not None: | 
|  | raise ValueError('Non-linear use of operand with name ' | 
|  | '{!r} in encoding for instruction ' | 
|  | '{!r}: used in fields {!r} and {!r}.' | 
|  | .format(field.value, mnemonic, | 
|  | other_field_name, | 
|  | field_name)) | 
|  | self.op_to_field_name[field.value] = field_name | 
|  |  | 
|  | self.fields[field_name] = field | 
|  |  | 
|  | def get_masks(self) -> Tuple[int, int]: | 
|  | '''Return zeros/ones masks for encoding | 
|  |  | 
|  | Returns a pair (m0, m1) where m0 is the "zeros mask": a mask where a | 
|  | bit is set if there is an bit pattern matching this encoding with that | 
|  | bit zero. m1 is the ones mask: equivalent, but for that bit one. | 
|  |  | 
|  | ''' | 
|  | m0 = 0 | 
|  | m1 = 0 | 
|  | for field_name, field in self.fields.items(): | 
|  | if isinstance(field.value, str): | 
|  | m0 |= field.scheme_field.bits.mask | 
|  | m1 |= field.scheme_field.bits.mask | 
|  | else: | 
|  | # Match up the bits in the value with the ranges in the scheme. | 
|  | assert field.value.width > 0 | 
|  | assert field.value.width == field.scheme_field.bits.width | 
|  | bits_seen = 0 | 
|  | for msb, lsb in field.scheme_field.bits.ranges: | 
|  | val_msb = field.scheme_field.bits.width - 1 - bits_seen | 
|  | val_lsb = val_msb - msb + lsb | 
|  | bits_seen += msb - lsb + 1 | 
|  |  | 
|  | for idx in range(0, msb - lsb + 1): | 
|  | desc = field.value.char_for_bit(val_lsb + idx) | 
|  | if desc in ['0', 'x']: | 
|  | m0 |= 1 << (idx + lsb) | 
|  | if desc in ['1', 'x']: | 
|  | m1 |= 1 << (idx + lsb) | 
|  |  | 
|  | all_bits = (1 << 32) - 1 | 
|  | assert (m0 | m1) == all_bits | 
|  | return (m0, m1) | 
|  |  | 
|  | def get_ones_mask(self) -> int: | 
|  | '''Return the mask of fixed bits that are set | 
|  |  | 
|  | For literal values of x (unused bits in the encoding), we'll prefer | 
|  | '0'. | 
|  |  | 
|  | ''' | 
|  | m0, m1 = self.get_masks() | 
|  | return m1 & ~m0 | 
|  |  | 
|  | def assemble(self, op_to_idx: Dict[str, int]) -> int: | 
|  | '''Assemble an instruction | 
|  |  | 
|  | op_to_idx should map each operand in the encoding to some integer | 
|  | index, which should be small enough to fit in the width of the | 
|  | operand's type and should be representable after any shift. Will raise | 
|  | a ValueError if not. | 
|  |  | 
|  | ''' | 
|  | val = self.get_ones_mask() | 
|  | for field_name, field in self.fields.items(): | 
|  | if not isinstance(field.value, str): | 
|  | # We've done this field already (in get_ones_mask) | 
|  | continue | 
|  |  | 
|  | # Try to get the operand value for the field. If this is an | 
|  | # optional operand, we might not have one, and just encode zero. | 
|  | field_val = op_to_idx.get(field.value, 0) | 
|  |  | 
|  | # The encoding process should already have converted anything | 
|  | # negative to a 2's complement representation. | 
|  | assert field_val >= 0 | 
|  |  | 
|  | # Is the number too big? At the moment, we are assuming immediates | 
|  | # are unsigned (because the OTBN big number instructions all have | 
|  | # unsigned immediates). | 
|  | if field_val >> field.scheme_field.bits.width: | 
|  | raise ValueError("operand field {} has a width of {}, " | 
|  | "so can't represent the value {:#x}." | 
|  | .format(field.value, | 
|  | field.scheme_field.bits.width, | 
|  | field_val)) | 
|  |  | 
|  | val |= field.scheme_field.bits.encode(field_val) | 
|  |  | 
|  | return val | 
|  |  | 
|  | def extract_operands(self, word: int) -> Dict[str, int]: | 
|  | '''Extract the encoded operand values from an encoded instruction''' | 
|  | ret = {} | 
|  | for field in self.fields.values(): | 
|  | # The operand fields (rather than fixed ones) have the operand name as | 
|  | # their value. | 
|  | if not isinstance(field.value, str): | 
|  | continue | 
|  |  | 
|  | ret[field.value] = field.scheme_field.bits.decode(word) | 
|  |  | 
|  | return ret |