151 lines
5.6 KiB
Python
151 lines
5.6 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, List
|
|
|
|
from reggen import register
|
|
from .field import Field
|
|
from .lib import check_keys, check_str, check_name, check_bool
|
|
from .params import ReggenParams
|
|
from .reg_base import RegBase
|
|
from .register import Register
|
|
|
|
REQUIRED_FIELDS = {
|
|
'name': ['s', "base name of the registers"],
|
|
'desc': ['t', "description of the registers"],
|
|
'count': [
|
|
's', "number of instances to generate."
|
|
" This field can be integer or string matching"
|
|
" from param_list."
|
|
],
|
|
'cname': [
|
|
's', "base name for each instance, mostly"
|
|
" useful for referring to instance in messages."
|
|
],
|
|
'fields': [
|
|
'l', "list of register field description"
|
|
" groups. Describes bit positions used for"
|
|
" base instance."
|
|
]
|
|
}
|
|
OPTIONAL_FIELDS = register.OPTIONAL_FIELDS.copy()
|
|
OPTIONAL_FIELDS.update({
|
|
'regwen_multi': [
|
|
'pb', "If true, regwen term increments"
|
|
" along with current multireg count."
|
|
],
|
|
'compact': [
|
|
'pb', "If true, allow multireg compacting."
|
|
"If false, do not compact."
|
|
]
|
|
})
|
|
|
|
|
|
class MultiRegister(RegBase):
|
|
def __init__(self,
|
|
offset: int,
|
|
addrsep: int,
|
|
reg_width: int,
|
|
params: ReggenParams,
|
|
raw: object):
|
|
super().__init__(offset)
|
|
|
|
rd = check_keys(raw, 'multireg',
|
|
list(REQUIRED_FIELDS.keys()),
|
|
list(OPTIONAL_FIELDS.keys()))
|
|
|
|
# Now that we've checked the schema of rd, we make a "reg" version of
|
|
# it that removes any fields that are allowed by MultiRegister but
|
|
# aren't allowed by Register. We'll pass that to the register factory
|
|
# method.
|
|
reg_allowed_keys = (set(register.REQUIRED_FIELDS.keys()) |
|
|
set(register.OPTIONAL_FIELDS.keys()))
|
|
reg_rd = {key: value
|
|
for key, value in rd.items()
|
|
if key in reg_allowed_keys}
|
|
self.reg = Register.from_raw(reg_width, offset, params, reg_rd)
|
|
|
|
self.cname = check_name(rd['cname'],
|
|
'cname field of multireg {}'
|
|
.format(self.reg.name))
|
|
self.name = self.reg.name
|
|
|
|
self.regwen_multi = check_bool(rd.get('regwen_multi', False),
|
|
'regwen_multi field of multireg {}'
|
|
.format(self.reg.name))
|
|
|
|
default_compact = True if len(self.reg.fields) == 1 else False
|
|
self.compact = check_bool(rd.get('compact', default_compact),
|
|
'compact field of multireg {}'
|
|
.format(self.reg.name))
|
|
if self.compact and len(self.reg.fields) > 1:
|
|
raise ValueError('Multireg {} sets the compact flag '
|
|
'but has multiple fields.'
|
|
.format(self.reg.name))
|
|
|
|
count_str = check_str(rd['count'],
|
|
'count field of multireg {}'
|
|
.format(self.reg.name))
|
|
self.count = params.expand(count_str,
|
|
'count field of multireg ' + self.reg.name)
|
|
if self.count <= 0:
|
|
raise ValueError("Multireg {} has a count of {}, "
|
|
"which isn't positive."
|
|
.format(self.reg.name, self.count))
|
|
|
|
# Generate the registers that this multireg expands into. Here, a
|
|
# "creg" is a "compacted register", which might contain multiple actual
|
|
# registers.
|
|
if self.compact:
|
|
assert len(self.reg.fields) == 1
|
|
width_per_reg = self.reg.fields[0].bits.msb + 1
|
|
assert width_per_reg <= reg_width
|
|
regs_per_creg = reg_width // width_per_reg
|
|
else:
|
|
regs_per_creg = 1
|
|
|
|
self.regs = []
|
|
creg_count = (self.count + regs_per_creg - 1) // regs_per_creg
|
|
for creg_idx in range(creg_count):
|
|
min_reg_idx = regs_per_creg * creg_idx
|
|
max_reg_idx = min(min_reg_idx + regs_per_creg, self.count) - 1
|
|
creg_offset = offset + creg_idx * addrsep
|
|
|
|
reg = self.reg.make_multi(reg_width,
|
|
creg_offset, creg_idx, creg_count,
|
|
self.regwen_multi, self.compact,
|
|
min_reg_idx, max_reg_idx, self.cname)
|
|
self.regs.append(reg)
|
|
|
|
# dv_compact is true if the multireg can be equally divided, and we can
|
|
# pack them as an array
|
|
if self.count < regs_per_creg or (self.count % regs_per_creg) == 0:
|
|
self.dv_compact = True
|
|
else:
|
|
self.dv_compact = False
|
|
|
|
def next_offset(self, addrsep: int) -> int:
|
|
return self.offset + len(self.regs) * addrsep
|
|
|
|
def get_n_bits(self, bittype: List[str] = ["q"]) -> int:
|
|
return sum(reg.get_n_bits(bittype) for reg in self.regs)
|
|
|
|
def get_field_list(self) -> List[Field]:
|
|
ret = []
|
|
for reg in self.regs:
|
|
ret += reg.get_field_list()
|
|
return ret
|
|
|
|
def is_homogeneous(self) -> bool:
|
|
return self.reg.is_homogeneous()
|
|
|
|
def _asdict(self) -> Dict[str, object]:
|
|
rd = self.reg._asdict()
|
|
rd['count'] = str(self.count)
|
|
rd['cname'] = self.cname
|
|
rd['regwen_multi'] = str(self.regwen_multi)
|
|
rd['compact'] = str(self.compact)
|
|
|
|
return {'multireg': rd}
|