|  | # Copyright lowRISC contributors. | 
|  | # Licensed under the Apache License, Version 2.0, see LICENSE for details. | 
|  | # SPDX-License-Identifier: Apache-2.0 | 
|  |  | 
|  | from typing import Dict, List, Optional | 
|  |  | 
|  | from .access import SWAccess, HWAccess | 
|  | from .bits import Bits | 
|  | from .enum_entry import EnumEntry | 
|  | from .lib import (check_keys, check_str, check_name, | 
|  | check_list, check_str_list, check_xint) | 
|  | from .params import ReggenParams | 
|  |  | 
|  | REQUIRED_FIELDS = { | 
|  | 'bits': ['b', "bit or bit range (msb:lsb)"] | 
|  | } | 
|  |  | 
|  | OPTIONAL_FIELDS = { | 
|  | 'name': ['s', "name of the field"], | 
|  | 'desc': ['t', "description of field (required if the field has a name)"], | 
|  | 'swaccess': [ | 
|  | 's', "software access permission, copied from " | 
|  | "register if not provided in field. " | 
|  | "(Tool adds if not provided.)" | 
|  | ], | 
|  | 'hwaccess': [ | 
|  | 's', "hardware access permission, copied from " | 
|  | "register if not prvided in field. " | 
|  | "(Tool adds if not provided.)" | 
|  | ], | 
|  | 'resval': [ | 
|  | 'x', "reset value, comes from register resval " | 
|  | "if not provided in field. Zero if neither " | 
|  | "are provided and the field is readable, " | 
|  | "x if neither are provided and the field " | 
|  | "is wo. Must match if both are provided." | 
|  | ], | 
|  | 'enum': ['l', "list of permitted enumeration groups"], | 
|  | 'tags': [ | 
|  | 's', | 
|  | "tags for the field, followed by the format 'tag_name:item1:item2...'" | 
|  | ] | 
|  | } | 
|  |  | 
|  |  | 
|  | class Field: | 
|  | def __init__(self, | 
|  | name: str, | 
|  | desc: Optional[str], | 
|  | tags: List[str], | 
|  | swaccess: SWAccess, | 
|  | hwaccess: HWAccess, | 
|  | bits: Bits, | 
|  | resval: Optional[int], | 
|  | enum: Optional[List[EnumEntry]]): | 
|  | self.name = name | 
|  | self.desc = desc | 
|  | self.tags = tags | 
|  | self.swaccess = swaccess | 
|  | self.hwaccess = hwaccess | 
|  | self.bits = bits | 
|  | self.resval = resval | 
|  | self.enum = enum | 
|  |  | 
|  | @staticmethod | 
|  | def from_raw(reg_name: str, | 
|  | field_idx: int, | 
|  | num_fields: int, | 
|  | default_swaccess: SWAccess, | 
|  | default_hwaccess: HWAccess, | 
|  | reg_resval: Optional[int], | 
|  | reg_width: int, | 
|  | params: ReggenParams, | 
|  | hwext: bool, | 
|  | shadowed: bool, | 
|  | raw: object) -> 'Field': | 
|  | where = 'field {} of {} register'.format(field_idx, reg_name) | 
|  | rd = check_keys(raw, where, | 
|  | list(REQUIRED_FIELDS.keys()), | 
|  | list(OPTIONAL_FIELDS.keys())) | 
|  |  | 
|  | raw_name = rd.get('name') | 
|  | if raw_name is None: | 
|  | name = ('field{}'.format(field_idx + 1) | 
|  | if num_fields > 1 else reg_name) | 
|  | else: | 
|  | name = check_name(raw_name, 'name of {}'.format(where)) | 
|  |  | 
|  | raw_desc = rd.get('desc') | 
|  | if raw_desc is None and raw_name is not None: | 
|  | raise ValueError('Missing desc field for {}' | 
|  | .format(where)) | 
|  | if raw_desc is None: | 
|  | desc = None | 
|  | else: | 
|  | desc = check_str(raw_desc, 'desc field for {}'.format(where)) | 
|  |  | 
|  | tags = check_str_list(rd.get('tags', []), | 
|  | 'tags for {}'.format(where)) | 
|  |  | 
|  | raw_swaccess = rd.get('swaccess') | 
|  | if raw_swaccess is not None: | 
|  | swaccess = SWAccess(where, raw_swaccess) | 
|  | else: | 
|  | swaccess = default_swaccess | 
|  |  | 
|  | raw_hwaccess = rd.get('hwaccess') | 
|  | if raw_hwaccess is not None: | 
|  | hwaccess = HWAccess(where, raw_hwaccess) | 
|  | else: | 
|  | hwaccess = default_hwaccess | 
|  |  | 
|  | # Currently internal shadow registers do not support hw write type | 
|  | if not hwext and shadowed and hwaccess.allows_write(): | 
|  | raise ValueError('Internal Shadow registers do not currently support ' | 
|  | 'hardware write') | 
|  |  | 
|  | bits = Bits.from_raw(where, reg_width, params, rd['bits']) | 
|  |  | 
|  | raw_resval = rd.get('resval') | 
|  | if raw_resval is None: | 
|  | # The field doesn't define a reset value. Use bits from reg_resval | 
|  | # if it's defined, otherwise None (which means "x"). | 
|  | if reg_resval is None: | 
|  | resval = None | 
|  | else: | 
|  | resval = bits.extract_field(reg_resval) | 
|  | else: | 
|  | # The field does define a reset value. It should be an integer or | 
|  | # 'x'. In the latter case, we set resval to None (as above). | 
|  | resval = check_xint(raw_resval, 'resval field for {}'.format(where)) | 
|  | if resval is None: | 
|  | # We don't allow a field to be explicitly 'x' on reset but for | 
|  | # the containing register to have a reset value. | 
|  | if reg_resval is not None: | 
|  | raise ValueError('resval field for {} is "x", but the ' | 
|  | 'register defines a resval as well.' | 
|  | .format(where)) | 
|  | else: | 
|  | # Check that the reset value is representable with bits | 
|  | if not (0 <= resval <= bits.max_value()): | 
|  | raise ValueError("resval field for {} is {}, which " | 
|  | "isn't representable as an unsigned " | 
|  | "{}-bit integer." | 
|  | .format(where, resval, bits.width())) | 
|  |  | 
|  | # If the register had a resval, check this value matches it. | 
|  | if reg_resval is not None: | 
|  | resval_from_reg = bits.extract_field(reg_resval) | 
|  | if resval != resval_from_reg: | 
|  | raise ValueError('resval field for {} is {}, but the ' | 
|  | 'register defines a resval as well, ' | 
|  | 'where bits {}:{} would give {}.' | 
|  | .format(where, resval, | 
|  | bits.msb, bits.lsb, | 
|  | resval_from_reg)) | 
|  |  | 
|  | raw_enum = rd.get('enum') | 
|  | if raw_enum is None: | 
|  | enum = None | 
|  | else: | 
|  | enum = [] | 
|  | raw_entries = check_list(raw_enum, | 
|  | 'enum field for {}'.format(where)) | 
|  | enum_val_to_name = {}  # type: Dict[int, str] | 
|  | for idx, raw_entry in enumerate(raw_entries): | 
|  | entry = EnumEntry('entry {} in enum list for {}' | 
|  | .format(idx + 1, where), | 
|  | bits.max_value(), | 
|  | raw_entry) | 
|  | if entry.value in enum_val_to_name: | 
|  | raise ValueError('In {}, duplicate enum entries for ' | 
|  | 'value {} ({} and {}).' | 
|  | .format(where, | 
|  | entry.value, | 
|  | enum_val_to_name[entry.value], | 
|  | entry.name)) | 
|  | enum.append(entry) | 
|  | enum_val_to_name[entry.value] = entry.name | 
|  |  | 
|  | return Field(name, desc, tags, swaccess, hwaccess, bits, resval, enum) | 
|  |  | 
|  | def has_incomplete_enum(self) -> bool: | 
|  | return (self.enum is not None and | 
|  | len(self.enum) != 1 + self.bits.max_value()) | 
|  |  | 
|  | def get_n_bits(self, hwext: bool, hwqe: bool, hwre: bool, bittype: List[str]) -> int: | 
|  | '''Get the size of this field in bits | 
|  |  | 
|  | bittype should be a list of the types of signals to count. The elements | 
|  | should come from the following list: | 
|  |  | 
|  | - 'q': A signal for the value of the field. Only needed if HW can read | 
|  | its contents. | 
|  |  | 
|  | - 'd': A signal for the next value of the field. Only needed if HW can | 
|  | write its contents. | 
|  |  | 
|  | - 'de': A write enable signal for hardware accesses. Only needed if HW | 
|  | can write the field's contents and the register data is stored in the | 
|  | register block (true if the hwext flag is false). | 
|  |  | 
|  | ''' | 
|  | n_bits = 0 | 
|  | if "q" in bittype and self.hwaccess.allows_read(): | 
|  | n_bits += self.bits.width() | 
|  | if "d" in bittype and self.hwaccess.allows_write(): | 
|  | n_bits += self.bits.width() | 
|  | if "qe" in bittype and self.hwaccess.allows_read(): | 
|  | n_bits += int(hwqe) | 
|  | if "re" in bittype and self.hwaccess.allows_read(): | 
|  | n_bits += int(hwre) | 
|  | if "de" in bittype and self.hwaccess.allows_write(): | 
|  | n_bits += int(not hwext) | 
|  | return n_bits | 
|  |  | 
|  | def make_multi(self, | 
|  | reg_width: int, | 
|  | min_reg_idx: int, | 
|  | max_reg_idx: int, | 
|  | cname: str, | 
|  | creg_idx: int, | 
|  | stripped: bool) -> List['Field']: | 
|  | assert 0 <= min_reg_idx <= max_reg_idx | 
|  |  | 
|  | # Check that we won't overflow reg_width. We assume that the LSB should | 
|  | # be preserved: if msb=5, lsb=2 then the replicated copies will be | 
|  | # [5:2], [11:8] etc. | 
|  | num_copies = 1 + max_reg_idx - min_reg_idx | 
|  | field_width = self.bits.msb + 1 | 
|  |  | 
|  | if field_width * num_copies > reg_width: | 
|  | raise ValueError('Cannot replicate field {} {} times: the ' | 
|  | 'resulting width would be {}, but the register ' | 
|  | 'width is just {}.' | 
|  | .format(self.name, num_copies, | 
|  | field_width * num_copies, reg_width)) | 
|  |  | 
|  | desc = ('For {}{}'.format(cname, creg_idx) | 
|  | if stripped else self.desc) | 
|  | enum = None if stripped else self.enum | 
|  |  | 
|  | ret = [] | 
|  | for reg_idx in range(min_reg_idx, max_reg_idx + 1): | 
|  | name = '{}_{}'.format(self.name, reg_idx) | 
|  |  | 
|  | bit_offset = field_width * (reg_idx - min_reg_idx) | 
|  | bits = (self.bits | 
|  | if bit_offset == 0 | 
|  | else self.bits.make_translated(bit_offset)) | 
|  |  | 
|  | ret.append(Field(name, desc, | 
|  | self.tags, self.swaccess, self.hwaccess, | 
|  | bits, self.resval, enum)) | 
|  |  | 
|  | return ret | 
|  |  | 
|  | def make_suffixed(self, suffix: str, | 
|  | cname: str, | 
|  | creg_idx: int, | 
|  | stripped: bool) -> 'Field': | 
|  | desc = ('For {}{}'.format(cname, creg_idx) | 
|  | if stripped else self.desc) | 
|  | enum = None if stripped else self.enum | 
|  |  | 
|  | return Field(self.name + suffix, | 
|  | desc, self.tags, self.swaccess, self.hwaccess, | 
|  | self.bits, self.resval, enum) | 
|  |  | 
|  | def _asdict(self) -> Dict[str, object]: | 
|  | rd = { | 
|  | 'bits': self.bits.as_str(), | 
|  | 'name': self.name, | 
|  | 'swaccess': self.swaccess.key, | 
|  | 'hwaccess': self.hwaccess.key, | 
|  | 'resval': 'x' if self.resval is None else str(self.resval), | 
|  | 'tags': self.tags | 
|  | }  # type: Dict[str, object] | 
|  |  | 
|  | if self.desc is not None: | 
|  | rd['desc'] = self.desc | 
|  | if self.enum is not None: | 
|  | rd['enum'] = self.enum | 
|  | return rd | 
|  |  | 
|  | def sw_readable(self) -> bool: | 
|  | return self.swaccess.key not in ['wo', 'r0w1c'] | 
|  |  | 
|  | def sw_writable(self) -> bool: | 
|  | return self.swaccess.key != 'ro' |