| # 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 reggen.access import SWAccess |
| from reggen.lib import check_keys, check_str, check_bool, check_int |
| from reggen.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} |