|  | # 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 | 
|  |  | 
|  | from .access import SWAccess | 
|  | from .lib import check_keys, check_str, check_bool, check_int | 
|  | from .params import ReggenParams | 
|  |  | 
|  |  | 
|  | REQUIRED_FIELDS = { | 
|  | 'name': ['s', "name of the window"], | 
|  | 'desc': ['t', "description of the window"], | 
|  | 'items': ['d', "size in fieldaccess width words of the window"], | 
|  | 'swaccess': ['s', "software access permitted"], | 
|  | } | 
|  |  | 
|  | # TODO potential for additional optional to give more type info? | 
|  | # eg sram-hw-port: "none", "sync", "async" | 
|  | OPTIONAL_FIELDS = { | 
|  | 'data-intg-passthru': [ | 
|  | 's', "True if the window has data integrity pass through. " | 
|  | "Defaults to false if not present." | 
|  | ], | 
|  | 'byte-write': [ | 
|  | 's', "True if byte writes are supported. " | 
|  | "Defaults to false if not present." | 
|  | ], | 
|  | 'validbits': [ | 
|  | 'd', "Number of valid data bits within " | 
|  | "regwidth sized word. " | 
|  | "Defaults to regwidth. If " | 
|  | "smaller than the regwidth then in each " | 
|  | "word of the window bits " | 
|  | "[regwidth-1:validbits] are unused and " | 
|  | "bits [validbits-1:0] are valid." | 
|  | ], | 
|  | 'unusual': [ | 
|  | 's', "True if window has unusual parameters " | 
|  | "(set to prevent Unusual: errors)." | 
|  | "Defaults to false if not present." | 
|  | ] | 
|  | } | 
|  |  | 
|  |  | 
|  | class Window: | 
|  | '''A class representing a memory window''' | 
|  | def __init__(self, | 
|  | name: str, | 
|  | desc: str, | 
|  | unusual: bool, | 
|  | byte_write: bool, | 
|  | data_intg_passthru: bool, | 
|  | validbits: int, | 
|  | items: int, | 
|  | size_in_bytes: int, | 
|  | offset: int, | 
|  | swaccess: SWAccess): | 
|  | assert 0 < validbits | 
|  | assert 0 < items <= size_in_bytes | 
|  |  | 
|  | self.name = name | 
|  | self.desc = desc | 
|  | self.unusual = unusual | 
|  | self.byte_write = byte_write | 
|  | self.data_intg_passthru = data_intg_passthru | 
|  | self.validbits = validbits | 
|  | self.items = items | 
|  | self.size_in_bytes = size_in_bytes | 
|  | self.offset = offset | 
|  | self.swaccess = swaccess | 
|  |  | 
|  | # Check that offset has been adjusted so that the first item in the | 
|  | # window has all zeros in the low bits. | 
|  | po2_size = 1 << (self.size_in_bytes - 1).bit_length() | 
|  | assert not (offset & (po2_size - 1)) | 
|  |  | 
|  | @staticmethod | 
|  | def from_raw(offset: int, | 
|  | reg_width: int, | 
|  | params: ReggenParams, | 
|  | raw: object) -> 'Window': | 
|  | rd = check_keys(raw, 'window', | 
|  | list(REQUIRED_FIELDS.keys()), | 
|  | list(OPTIONAL_FIELDS.keys())) | 
|  |  | 
|  | wind_desc = 'window at offset {:#x}'.format(offset) | 
|  | name = check_str(rd['name'], wind_desc) | 
|  | wind_desc = '{!r} {}'.format(name, wind_desc) | 
|  |  | 
|  | desc = check_str(rd['desc'], 'desc field for ' + wind_desc) | 
|  |  | 
|  | unusual = check_bool(rd.get('unusual', False), | 
|  | 'unusual field for ' + wind_desc) | 
|  | byte_write = check_bool(rd.get('byte-write', False), | 
|  | 'byte-write field for ' + wind_desc) | 
|  | data_intg_passthru = check_bool(rd.get('data-intg-passthru', False), | 
|  | 'data-intg-passthru field for ' + wind_desc) | 
|  |  | 
|  | validbits = check_int(rd.get('validbits', reg_width), | 
|  | 'validbits field for ' + wind_desc) | 
|  | if validbits <= 0: | 
|  | raise ValueError('validbits field for {} is not positive.' | 
|  | .format(wind_desc)) | 
|  | if validbits > reg_width: | 
|  | raise ValueError('validbits field for {} is {}, ' | 
|  | 'which is greater than {}, the register width.' | 
|  | .format(wind_desc, validbits, reg_width)) | 
|  |  | 
|  | r_items = check_str(rd['items'], 'items field for ' + wind_desc) | 
|  | items = params.expand(r_items, 'items field for ' + wind_desc) | 
|  | if items <= 0: | 
|  | raise ValueError("Items field for {} is {}, " | 
|  | "which isn't positive." | 
|  | .format(wind_desc, items)) | 
|  |  | 
|  | assert reg_width % 8 == 0 | 
|  | size_in_bytes = items * (reg_width // 8) | 
|  |  | 
|  | # Round size_in_bytes up to the next power of 2. The calculation is | 
|  | # like clog2 calculations in SystemVerilog, where we start with the | 
|  | # last index, rather than the number of elements. | 
|  | assert size_in_bytes > 0 | 
|  | po2_size = 1 << (size_in_bytes - 1).bit_length() | 
|  |  | 
|  | # A size that isn't a power of 2 is not allowed unless the unusual flag | 
|  | # is set. | 
|  | if po2_size != size_in_bytes and not unusual: | 
|  | raise ValueError('Items field for {} is {}, which gives a size of ' | 
|  | '{} bytes. This is not a power of 2 (next power ' | 
|  | 'of 2 is {}). If you want to do this even so, ' | 
|  | 'set the "unusual" flag.' | 
|  | .format(wind_desc, items, | 
|  | size_in_bytes, po2_size)) | 
|  |  | 
|  | # Adjust offset if necessary to make sure the base address of the first | 
|  | # item in the window has all zeros in the low bits. | 
|  | addr_mask = po2_size - 1 | 
|  | if offset & addr_mask: | 
|  | offset = (offset | addr_mask) + 1 | 
|  | offset = offset | 
|  |  | 
|  | swaccess = SWAccess(wind_desc, rd['swaccess']) | 
|  | if not (swaccess.value[4] or unusual): | 
|  | raise ValueError('swaccess field for {} is {}, which is an ' | 
|  | 'unusual access type for a window. If you want ' | 
|  | 'to do this, set the "unusual" flag.' | 
|  | .format(wind_desc, swaccess.key)) | 
|  |  | 
|  | return Window(name, desc, unusual, byte_write, data_intg_passthru, | 
|  | validbits, items, size_in_bytes, offset, swaccess) | 
|  |  | 
|  | def next_offset(self, addrsep: int) -> int: | 
|  | return self.offset + self.size_in_bytes | 
|  |  | 
|  | def _asdict(self) -> Dict[str, object]: | 
|  | rd = { | 
|  | 'desc': self.desc, | 
|  | 'items': self.items, | 
|  | 'swaccess': self.swaccess.key, | 
|  | 'byte-write': self.byte_write, | 
|  | 'validbits': self.validbits, | 
|  | 'unusual': self.unusual | 
|  | } | 
|  | if self.name is not None: | 
|  | rd['name'] = self.name | 
|  |  | 
|  | return {'window': rd} |