# 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}