tinyriscv/tools/regtool/reggen/window.py

170 lines
6.4 KiB
Python

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