[reggen] Delete duplicate Register/MultiRegister types in data.py We've recently moved to representing registers, fields etc. in classes, rather than dicts. This duplicated some work done in gen_rtl.py and data.py and this commit removes the duplicated Register and MultiRegister types. There's a certain amount of tidying up in reg_pkg.sv.tpl, which I think makes the implementation a bit easier to read (as much as is possible for template code like this). Signed-off-by: Rupert Swarbrick <rswarbrick@lowrisc.org>
diff --git a/util/reggen/data.py b/util/reggen/data.py index 477b007..3c03b7c 100644 --- a/util/reggen/data.py +++ b/util/reggen/data.py
@@ -3,136 +3,19 @@ # SPDX-License-Identifier: Apache-2.0 from collections import OrderedDict +import re -from .field import Field +from .multi_register import MultiRegister +from .register import Register # helper funtion that strips trailing _number (used as multireg suffix) from name # TODO: this is a workaround, should solve this in validate.py def get_basename(name): - for (k, c) in enumerate(name[::-1]): - if not str.isdigit(c): - if c == "_": - return name[0:len(name) - (k + 1)] - else: - break - return "" - - -class Reg(): - def __init__(self, name=""): - self.name = name - self.offset = 0 - self.hwqe = False - self.hwre = False - self.hwext = False # External register - self.resval = 0 - self.dvrights = "RO" # Used by UVM REG only - self.regwen = "" - self.fields = [] - self.width = 0 - self.ishomog = 0 - self.tags = [] - self.shadowed = False - self.update_err_alert = "" # Used by shadow reg DV - self.storage_err_alert = "" # Used by shadow reg DV - - def is_multi_reg(self): - """Returns true if this is a multireg""" - return False - - def get_n_bits(self, bittype=["q"]): - """Returns number of bits in this register (including all multiregs and - fields). By default this function counts read data bits (bittype "q"), - but other bits such as "d", qe", "re", "de" can be counted as well by - specifying them in the bittype list argument. - """ - n_bits = 0 - for f in self.fields: - if isinstance(f, Reg): - n_bits += f.get_n_bits(bittype) - else: - n_bits += f.get_n_bits(self.hwext, bittype) - return n_bits - - def get_fields_flat(self): - """Returns a flat list of all the fields in this register""" - ret = [] - for f in self.fields: - if isinstance(f, Reg): - ret += f.get_fields_flat() - else: - ret.append(f) - - return ret - - def get_field_flat(self, linear_idx): - """Returns a specific field at a linear index position in - the flattened field list""" - fields_flat = self.get_fields_flat() - return fields_flat[linear_idx] - - def get_n_fields_flat(self): - """Returns the number of fields contained in the flat field list""" - return len(self.get_fields_flat()) - - def get_regs_flat(self): - """Returns a flat list containing all - registers and subregisters""" - if isinstance(self.fields[0], Field): - return [self] - else: - regs = [] - for r in self.fields: - regs += r.get_regs_flat() - return regs - - def get_reg_flat(self, linear_index): - """Returns a specific register at a linear index position in - the flattened regiser list""" - regs_flat = self.get_regs_flat() - return regs_flat[linear_index] - - def get_n_regs_flat(self): - """Returns the number of registers contained in - the flat register list""" - return len(self.get_regs_flat()) - - def get_nested_dims(self): - """Recursively get dimensions of nested registers (outputs a list)""" - # return length of flattened field array if this is a regular register, - # or if this is the last multiregister level in a nested multiregister - if not isinstance(self, MultiReg): - dims = [len(self.get_fields_flat())] - if isinstance(self, MultiReg) and\ - not isinstance(self.fields[0], MultiReg): - if self.ishomog: - dims = [len(self.get_fields_flat())] - else: - dims = [len(self.fields)] - else: - # nested multiregister case - dims = [len(self.fields)] + self.fields[0].get_nested_dims() - return dims - - def get_nested_params(self): - """Recursively get parameters of nested registers (outputs a list)""" - params = [] - if isinstance(self, MultiReg): - params += [self.param] - if isinstance(self.fields[0], MultiReg): - params += self.fields[0].get_nested_params() - return params - - -class MultiReg(Reg): - def __init__(self, name): - Reg.__init__(self, name) - self.param = "" - - def is_multi_reg(self): - """Returns true if this is a multireg""" - return True + match = re.search(r'_[0-9]+$', name) + assert match + assert match.start() > 0 + return name[0:match.start()] class Window(): @@ -163,7 +46,12 @@ """ regs = [] for r in self.regs: - regs += r.get_regs_flat() + if isinstance(r, Register): + regs.append(r) + else: + assert isinstance(r, MultiRegister) + regs += r.regs + return regs def get_n_bits(self, bittype=["q"]): @@ -179,11 +67,3 @@ def get_n_regs_flat(self): return len(self.get_regs_flat()) - - def contains_multiregs(self): - """Returns true if there are multiregs in this block - """ - for r in self.regs: - if isinstance(r, MultiReg): - return True - return False
diff --git a/util/reggen/field.py b/util/reggen/field.py index 291e997..1a8791a 100644 --- a/util/reggen/field.py +++ b/util/reggen/field.py
@@ -184,7 +184,29 @@ return (self.enum is not None and len(self.enum) != 1 + self.bits.max_value()) - def get_n_bits(self, hwext: bool, bittype: List[str] = ["q"]) -> int: + def get_n_bits(self, hwext: bool, bittype: List[str]) -> int: + '''Get the size of this field in bits + + bittype should be a list of the types of signals to count. The elements + should come from the following list: + + - 'q': A signal for the value of the field. Only needed if HW can read + its contents. + + - 'd': A signal for the next value of the field. Only needed if HW can + write its contents. + + - 'qe': A write enable signal for bus accesses. Only needed if HW can + read the field's contents and the field has the hwqe flag. + + - 're': A read enable signal for bus accesses. Only needed if HW can + read the field's contents and the field has the hwre flag. + + - 'de': A write enable signal for hardware accesses. Only needed if HW + can write the field's contents and the register data is stored in the + register block (true if the hwext flag is false). + + ''' n_bits = 0 if "q" in bittype and self.hwaccess.allows_read(): n_bits += self.bits.width()
diff --git a/util/reggen/fpv_csr.sv.tpl b/util/reggen/fpv_csr.sv.tpl index 66a54a5..22f8c53 100644 --- a/util/reggen/fpv_csr.sv.tpl +++ b/util/reggen/fpv_csr.sv.tpl
@@ -9,6 +9,8 @@ <% from reggen import (gen_fpv) from reggen.data import get_basename + from reggen.register import Register + from reggen.multi_register import MultiRegister from topgen import lib %>\ @@ -161,50 +163,52 @@ has_q = r.get_n_bits(["q"]) > 0 has_d = r.get_n_bits(["d"]) > 0 has_de = r.get_n_bits(["de"]) > 0 + + if isinstance(r, Register): + r0 = r + flat_regs = [r] + else: + assert isinstance(r, MultiRegister) + r0 = r.reg + flat_regs = r.regs %>\ - % if not r.shadowed: - % if r.is_multi_reg(): + % if not r0.shadowed: + % if isinstance(r, MultiRegister): <% - mreg_name = r.name + mreg_name = r0.name.lower() mreg_width_list = list() mreg_fpv_name_list = list() mreg_dut_path_list = list() mreg_has_q_list = list() mreg_has_d_list = list() mreg_has_de_list = list() - mreg_num_regs = r.get_n_fields_flat() + fields = r.get_field_list() + mreg_num_regs = len(fields) mreg_msb = -1 mreg_lsb = 0 i = 0 + + if not r.is_homogeneous(): + for field in r0.fields: + mreg_fpv_name_list.append(mreg_name + "_" + field.name.lower()) + mreg_dut_path_list.append(mreg_name + "[s]." + field.name.lower()) + mreg_width_list.append(field.bits.width()) + mreg_has_q_list.append(field.get_n_bits(r0.hwext, ["q"]) > 0) + mreg_has_d_list.append(field.get_n_bits(r0.hwext, ["d"]) > 0) + mreg_has_de_list.append(field.get_n_bits(r0.hwext, ["de"]) > 0) + mreg_num_base_fields = len(mreg_fpv_name_list) + mreg_num_regs = mreg_num_regs / mreg_num_base_fields + else: + f = fields[0] + mreg_num_base_fields = 1 + mreg_fpv_name_list.append(mreg_name) + mreg_dut_path_list.append(mreg_name + "[s]") + mreg_width_list.append(f.bits.width()) + mreg_has_q_list.append(has_q) + mreg_has_d_list.append(has_d) + mreg_has_de_list.append(has_de) %>\ - % if not r.ishomog: - % for field in r.get_reg_flat(0).fields: -<% - mreg_fpv_name_list.append(mreg_name + "_" + get_basename(field.name.lower())) - mreg_dut_path_list.append(mreg_name + "[s]." + get_basename(field.name.lower())) - mreg_width_list.append(field.bits.width()) - mreg_has_q_list.append(field.get_n_bits(r.hwext, ["q"]) > 0) - mreg_has_d_list.append(field.get_n_bits(r.hwext, ["d"]) > 0) - mreg_has_de_list.append(field.get_n_bits(r.hwext, ["de"]) > 0) -%>\ - % endfor -<% - mreg_num_base_fields = len(mreg_fpv_name_list) - mreg_num_regs = mreg_num_regs / mreg_num_base_fields -%>\ - % else: -<% - f = r.get_field_flat(0) - mreg_num_base_fields = 1 - mreg_fpv_name_list.append(mreg_name) - mreg_dut_path_list.append(mreg_name + "[s]") - mreg_width_list.append(f.bits.width()) - mreg_has_q_list.append(has_q) - mreg_has_d_list.append(has_d) - mreg_has_de_list.append(has_de) -%>\ - % endif // define local fpv variable for multi-reg % if r.get_n_bits(["q", "d"]): @@ -219,21 +223,17 @@ % endif % endif - % for reg_flat in r.get_regs_flat(): + % for reg_flat in flat_regs: <% - reg_name = reg_flat.name - reg_offset = str(reg_width) + "'h" + "%x" % reg_flat.offset - reg_msb = reg_flat.width - 1 - regwen = reg_flat.regwen + reg_name = reg_flat.name.lower() + reg_offset = "{}'h{:x}".format(reg_width, reg_flat.offset) + reg_msb = reg_flat.get_width() - 1 + reg_wen = ('`REGWEN_PATH.{}_qs'.format(reg_flat.regwen.lower()) + if reg_flat.regwen is not None else '1') reg_wr_mask = 0 %>\ - // assertions for register: ${reg_name} - % if regwen: -<% reg_wen = "`REGWEN_PATH." + regwen + "_qs" %>\ - % else: -<% reg_wen = "1" %>\ - % endif - % for f in reg_flat.get_fields_flat(): + // assertions for register: ${reg_name.lower()} + % for f in reg_flat.fields: <% field_name = f.name.lower() assert_path = reg_name + "." + field_name @@ -245,12 +245,12 @@ reg_wr_mask_h = format(reg_wr_mask, 'x') lsb = f.bits.lsb sw_rdaccess = f.swaccess.swrd().name - had_q = f.get_n_bits(r.hwext, ["q"]) > 0 - has_d = f.get_n_bits(r.hwext, ["d"]) > 0 - has_de = f.get_n_bits(r.hwext, ["de"]) > 0 + had_q = f.get_n_bits(r0.hwext, ["q"]) > 0 + has_d = f.get_n_bits(r0.hwext, ["d"]) > 0 + has_de = f.get_n_bits(r0.hwext, ["de"]) > 0 %>\ - % if not r.ishomog: - % if r.is_multi_reg(): + % if not r.is_homogeneous(): + % if isinstance(r, MultiRegister): // this is a non-homog multi-reg <% mreg_lsb = i * mreg_width_list[loop.index] @@ -262,15 +262,14 @@ % endif % endif % endfor - % if r.is_multi_reg(): <% - mreg_lsb = i * (mreg_msb - mreg_lsb + 1) - mreg_msb = mreg_lsb + reg_msb - i += 1 + if isinstance(r, MultiRegister): + mreg_lsb = i * (mreg_msb - mreg_lsb + 1) + mreg_msb = mreg_lsb + reg_msb + i += 1 %>\ - % endif - % if r.ishomog: - % if r.is_multi_reg(): + % if r.is_homogeneous(): + % if isinstance(r, MultiRegister): ${gen_multi_reg_asserts_by_category(reg_name, mreg_name, mreg_msb, mreg_lsb, reg_flat.hwext, reg_wr_mask_h)}\ % else: ${gen_asserts_by_category(reg_name, reg_name, reg_flat.hwext, reg_wr_mask_h)}\ @@ -330,7 +329,7 @@ % else: <% wr_property = "P" %>\ % endif - % if not r.ishomog: + % if not r.is_homogeneous(): <% shift_index = lsb %>\ % else: <% shift_index = 0 %>\ @@ -350,7 +349,7 @@ % else: <% rd_property = "rd_P" %>\ % endif - % if not r.ishomog: + % if not r.is_homogeneous(): <% shift_index = lsb %>\ % else: <% shift_index = 0 %>\
diff --git a/util/reggen/gen_dv.py b/util/reggen/gen_dv.py index c44559f..6053d0b 100644 --- a/util/reggen/gen_dv.py +++ b/util/reggen/gen_dv.py
@@ -21,7 +21,7 @@ def rcname(b, r): '''Get the name of the dv_base_reg subclass for this register''' - return b.name + "_reg_" + r.name + return b.name + "_reg_" + r.name.lower() def mcname(b, m):
diff --git a/util/reggen/gen_rtl.py b/util/reggen/gen_rtl.py index ea546fc..4196529 100644 --- a/util/reggen/gen_rtl.py +++ b/util/reggen/gen_rtl.py
@@ -11,7 +11,7 @@ from pkg_resources import resource_filename from .access import HwAccess, SwRdAccess, SwWrAccess -from .data import Block, MultiReg, Reg, Window +from .data import Block, Window from .register import Register from .multi_register import MultiRegister @@ -27,72 +27,6 @@ return default -def parse_reg(obj): - """Convert Register into a Reg object.""" - assert isinstance(obj, Register) - reg = Reg(escape_name(obj.name)) - reg.offset = obj.offset - reg.fields = [] - reg.hwext = obj.hwext - reg.hwqe = obj.hwqe - reg.hwre = obj.hwre - reg.resval = obj.resval - reg.dvrights = obj.dv_rights() - reg.regwen = (obj.regwen or '').lower() - reg.ishomog = len(obj.fields) == 1 - reg.tags = obj.tags - reg.shadowed = obj.shadowed - # For DV only: TODO: any good way we can move it to gen_dv? - reg.update_err_alert = obj.update_err_alert or '' - reg.storage_err_alert = obj.storage_err_alert or '' - - reg.fields = obj.fields - for field in reg.fields: - reg.width = max(reg.width, field.bits.msb + 1) - - # TODO: Field bitfield overlapping check - log.info("R[0x%04x]: %s ", reg.offset, reg.name) - for f in reg.fields: - log.info(" F[%2d:%2d]: %s", f.bits.msb, f.bits.lsb, f.name) - - return reg - - -def parse_multireg(obj): - '''Convert MultiRegister into a MultiReg object''' - assert isinstance(obj, MultiRegister) - regs = [parse_reg(r) for r in obj.regs] - reg0 = regs[0].get_reg_flat(0) - # get register properties of the first register in the multireg and - # copy them to the parent - # since all regs in a multireg have the same props - reg = MultiReg(escape_name(obj.reg.name)) - reg.offset = reg0.offset - reg.hwext = reg0.hwext - reg.hwqe = reg0.hwqe - reg.hwre = reg0.hwre - - # since this is a multireg, the list of fields can - # contain regs or multiregs - reg.fields = regs - - # Fields that don't really make sense for a multireg - reg.resval = None - reg.dvrights = None - reg.regwen = None - - # a homogenous multireg contains only one single field that is replicated - reg.ishomog = len(obj.reg.fields) == 1 - - reg.tags = reg0.tags - reg.shadowed = reg0.shadowed - - # TODO: need to reference proper param here such that it can be used - # in the package template for the array declaration - # reg.param = ... - return reg - - def parse_win(obj, width): # Convert register window fields into Window class # base_addr : genoffset @@ -137,11 +71,8 @@ block.hier_path = obj["hier_path"] if "hier_path" in obj else "" for r in obj["registers"]: - if isinstance(r, Register): - block.regs.append(parse_reg(r)) - continue - if isinstance(r, MultiRegister): - block.regs.append(parse_multireg(r)) + if isinstance(r, Register) or isinstance(r, MultiRegister): + block.regs.append(r) continue assert isinstance(r, dict) if 'window' in r:
diff --git a/util/reggen/multi_register.py b/util/reggen/multi_register.py index 71ddc62..12c1b72 100644 --- a/util/reggen/multi_register.py +++ b/util/reggen/multi_register.py
@@ -5,9 +5,11 @@ from typing import Dict, List from reggen import register -from .register import Register +from .field import Field from .lib import (check_keys, check_str, check_name, check_bool, expand_parameter) +from .reg_block import RegBlock +from .register import Register REQUIRED_FIELDS = { 'name': ['s', "base name of the registers"], @@ -40,13 +42,14 @@ }) -class MultiRegister: +class MultiRegister(RegBlock): def __init__(self, offset: int, addrsep: int, reg_width: int, params: List[Dict[str, object]], raw: object): + super().__init__(offset) rd = check_keys(raw, 'multireg', list(REQUIRED_FIELDS.keys()), @@ -115,6 +118,18 @@ min_reg_idx, max_reg_idx, self.cname) self.regs.append(reg) + 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)
diff --git a/util/reggen/reg_block.py b/util/reggen/reg_block.py new file mode 100644 index 0000000..ad8fc31 --- /dev/null +++ b/util/reggen/reg_block.py
@@ -0,0 +1,45 @@ +# Copyright lowRISC contributors. +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 + +from typing import List + +from .field import Field + + +class RegBlock: + '''An abstract class inherited by Register and MultiRegister + + This represents a block of one or more registers with a base address. + + ''' + def __init__(self, offset: int): + self.offset = offset + + def get_n_bits(self, bittype: List[str]) -> int: + '''Get the size of this register / these registers in bits + + See Field.get_n_bits() for the precise meaning of bittype. + + ''' + raise NotImplementedError() + + def get_field_list(self) -> List[Field]: + '''Get an ordered list of the fields in the register(s) + + Registers are ordered from low to high address. Within a register, + fields are ordered as Register.fields: from LSB to MSB. + + ''' + raise NotImplementedError() + + def is_homogeneous(self) -> bool: + '''True if every field in the block is identical + + For a single register, this is true if it only has one field. For a + multireg, it is true if the generating register has just one field. + Note that if the compact flag is set, the generated registers might + have multiple (replicated) fields. + + ''' + raise NotImplementedError()
diff --git a/util/reggen/reg_pkg.sv.tpl b/util/reggen/reg_pkg.sv.tpl index dce5c4f..2a7860a 100644 --- a/util/reggen/reg_pkg.sv.tpl +++ b/util/reggen/reg_pkg.sv.tpl
@@ -6,7 +6,8 @@ <% from topgen import lib # TODO: Split lib to common lib module - from reggen.data import get_basename + from reggen.register import Register + from reggen.multi_register import MultiRegister num_regs = block.get_n_regs_flat() max_regs_char = len("{}".format(num_regs-1)) @@ -27,71 +28,123 @@ // Typedefs for registers // //////////////////////////// % for r in block.regs: - ## in this case we have a homogeneous multireg, with only one replicated field - % if r.get_n_bits(["q"]) and r.ishomog: + % if r.get_n_bits(["q"]): +<% + if isinstance(r, Register): + r0 = r + type_suff = 'reg_t' + else: + assert isinstance(r, MultiRegister) + r0 = r.reg + type_suff = 'mreg_t' + + reg2hw_name = ('{}_reg2hw_{}_{}' + .format(block.name, r0.name.lower(), type_suff)) +%>\ typedef struct packed { - logic ${lib.bitarray(r.get_field_flat(0).get_n_bits(r.hwext, ["q"]),2)} q; - % if r.get_field_flat(0).hwqe: + % if r.is_homogeneous(): + ## If we have a homogeneous register or multireg, there is just one field + ## (possibly replicated many times). The typedef is for one copy of that + ## field. +<% + field = r.get_field_list()[0] + field_q_width = field.get_n_bits(r0.hwext, ['q']) + field_q_bits = lib.bitarray(field_q_width, 2) +%>\ + logic ${field_q_bits} q; + % if field.hwqe: logic qe; - % endif - % if r.get_field_flat(0).hwre or (r.shadowed and r.hwext): + % endif + % if field.hwre or (r0.shadowed and r0.hwext): logic re; - % endif - % if r.shadowed and not r.hwext: + % endif + % if r0.shadowed and not r0.hwext: logic err_update; logic err_storage; - % endif - } ${block.name + "_reg2hw_" + r.name + ("_mreg_t" if r.is_multi_reg() else "_reg_t")}; + % endif + % else: + ## We are inhomogeneous, which means there is more than one different + ## field. Generate a reg2hw typedef that packs together all the fields of + ## the register. + % for f in r0.fields: + % if f.get_n_bits(r0.hwext, ["q"]) >= 1: +<% + field_q_width = f.get_n_bits(r0.hwext, ['q']) + field_q_bits = lib.bitarray(field_q_width, 2) - ## in this case we have an inhomogeneous multireg, with several different fields per register - % elif r.get_n_bits(["q"]) and not r.ishomog: - typedef struct packed { - % for f in r.get_reg_flat(0).fields: - % if f.get_n_bits(r.hwext, ["q"]) >= 1: + struct_name = f.name.lower() +%>\ struct packed { - logic ${lib.bitarray(f.get_n_bits(r.hwext, ["q"]),2)} q; - % if f.hwqe: + logic ${field_q_bits} q; + % if f.hwqe: logic qe; - % endif - % if f.hwre or (r.shadowed and r.hwext): + % endif + % if f.hwre or (r0.shadowed and r0.hwext): logic re; - % endif - % if r.shadowed and not r.hwext: + % endif + % if r0.shadowed and not r0.hwext: logic err_update; logic err_storage; - % endif - } ${get_basename(f.name.lower()) if r.is_multi_reg() else f.name.lower()}; - %endif - %endfor - } ${block.name + "_reg2hw_" + r.name + ("_mreg_t" if r.is_multi_reg() else "_reg_t")}; + % endif + } ${struct_name}; + %endif + %endfor + %endif + } ${reg2hw_name}; %endif % endfor % for r in block.regs: - ## in this case we have a homogeneous multireg, with only one replicated field - % if r.get_n_bits(["d"]) and r.ishomog: - typedef struct packed { - logic ${lib.bitarray(r.get_field_flat(0).get_n_bits(r.hwext, ["d"]),2)} d; - % if not r.hwext: - logic de; - % endif - } ${block.name + "_hw2reg_" + r.name + ("_mreg_t" if r.is_multi_reg() else "_reg_t")}; + % if r.get_n_bits(["d"]): +<% + if isinstance(r, Register): + r0 = r + type_suff = 'reg_t' + else: + assert isinstance(r, MultiRegister) + r0 = r.reg + type_suff = 'mreg_t' - ## in this case we have an inhomogeneous multireg, with several different fields per register - % elif r.get_n_bits(["d"]) and not r.ishomog: + hw2reg_name = ('{}_hw2reg_{}_{}' + .format(block.name, r0.name.lower(), type_suff)) +%>\ typedef struct packed { - % for f in r.get_reg_flat(0).fields: - % if f.get_n_bits(r.hwext, ["d"]) >= 1: - struct packed { - logic ${lib.bitarray(f.get_n_bits(r.hwext, ["d"]),2)} d; - % if not r.hwext: - logic de; + % if r.is_homogeneous(): + ## If we have a homogeneous register or multireg, there is just one field + ## (possibly replicated many times). The typedef is for one copy of that + ## field. +<% + field = r.get_field_list()[0] + field_d_width = field.get_n_bits(r0.hwext, ['d']) + field_d_bits = lib.bitarray(field_d_width, 2) +%>\ + logic ${field_d_bits} d; + % if not r0.hwext: + logic de; % endif - } ${get_basename(f.name.lower()) if r.is_multi_reg() else f.name.lower()}; - %endif - %endfor - } ${block.name + "_hw2reg_" + r.name + ("_mreg_t" if r.is_multi_reg() else "_reg_t")}; + % else: + ## We are inhomogeneous, which means there is more than one different + ## field. Generate a hw2reg typedef that packs together all the fields of + ## the register. + % for f in r0.fields: + % if f.get_n_bits(r0.hwext, ["d"]) >= 1: +<% + field_d_width = f.get_n_bits(r0.hwext, ['d']) + field_d_bits = lib.bitarray(field_d_width, 2) + + struct_name = f.name.lower() +%>\ + struct packed { + logic ${field_d_bits} d; + % if not r0.hwext: + logic de; + % endif + } ${struct_name}; + %endif + %endfor + %endif + } ${hw2reg_name}; % endif % endfor @@ -100,26 +153,32 @@ // Register to internal design logic // /////////////////////////////////////// <% -nbits = block.get_n_bits(["q","qe","re"]) +nbits = block.get_n_bits(["q", "qe", "re"]) packbit = 0 %>\ % if nbits > 0: typedef struct packed { % for r in block.regs: - ######################## multiregister ########################### - % if r.is_multi_reg() and r.get_n_bits(["q"]): + % if r.get_n_bits(["q"]): <% - array_dims = "" - for d in r.get_nested_dims(): - array_dims += "[%d:0]" % (d-1) + if isinstance(r, MultiRegister): + r0 = r.reg + repl_count = r.count + type_suff = 'mreg_t [{}:0]'.format(repl_count - 1) + else: + r0 = r + repl_count = 1 + type_suff = 'reg_t' + + struct_type = ('{}_reg2hw_{}_{}' + .format(block.name, r0.name.lower(), type_suff)) + + struct_width = r0.get_n_bits(['q', 'qe', 're']) * repl_count + msb = nbits - packbit - 1 + lsb = msb - struct_width + 1 + packbit += struct_width %>\ - ${block.name + "_reg2hw_" + r.name + "_mreg_t"} ${array_dims} ${r.name}; // [${nbits - packbit - 1}:${nbits - (packbit + r.get_n_bits(["q", "qe", "re"]))}]<% packbit += r.get_n_bits(["q", "qe", "re"]) %>\ - - ######################## register ########################### - % elif r.get_n_bits(["q"]): - ## Only one field, should use register name as it is - ${block.name + "_reg2hw_" + r.name + "_reg_t"} ${r.name}; // [${nbits - packbit - 1}:${nbits - (packbit + r.get_n_bits(["q", "qe", "re"]))}]<% packbit += r.get_n_bits(["q", "qe", "re"]) %>\ - + ${struct_type} ${r0.name.lower()}; // [${msb}:${lsb}] % endif % endfor } ${block.name}_reg2hw_t; @@ -129,64 +188,75 @@ // Internal design logic to register // /////////////////////////////////////// <% -nbits = block.get_n_bits(["d","de"]) +nbits = block.get_n_bits(["d", "de"]) packbit = 0 %>\ % if nbits > 0: typedef struct packed { % for r in block.regs: -<% reg_d_bits = r.get_n_bits(["d"]) %>\ - % if reg_d_bits: + % if r.get_n_bits(["d"]): <% - if r.is_multi_reg(): - array_dims = "".join("[%d:0]" % (d-1) for d in r.get_nested_dims()) - reg_type = "{}_hw2reg_{}_mreg_t {}".format(block.name, r.name, array_dims) + if isinstance(r, MultiRegister): + r0 = r.reg + repl_count = r.count + type_suff = 'mreg_t [{}:0]'.format(repl_count - 1) else: - reg_type = "{}_hw2reg_{}_reg_t".format(block.name, r.name) + r0 = r + repl_count = 1 + type_suff = 'reg_t' - reg_width = r.get_n_bits(["d", "de"]) + struct_type = ('{}_hw2reg_{}_{}' + .format(block.name, r0.name.lower(), type_suff)) + + struct_width = r0.get_n_bits(['d', 'de']) * repl_count msb = nbits - packbit - 1 - lsb = nbits - (packbit + reg_width) - packbit += reg_width + lsb = msb - struct_width + 1 + packbit += struct_width %>\ - ${reg_type} ${r.name}; // [${msb}:${lsb}] + ${struct_type} ${r0.name.lower()}; // [${msb}:${lsb}] % endif % endfor } ${block.name}_hw2reg_t; % endif // Register Address +<% +ublock = block.name.upper() +%>\ % for r in block.get_regs_flat(): - parameter logic [BlockAw-1:0] ${block.name.upper()}_${r.name.upper()}_OFFSET = ${block.addr_width}'h ${"%x" % r.offset}; + parameter logic [BlockAw-1:0] ${ublock}_${r.name.upper()}_OFFSET = ${block.addr_width}'h ${"%x" % r.offset}; % endfor % if len(block.wins) > 0: // Window parameter % endif % for i,w in enumerate(block.wins): - parameter logic [BlockAw-1:0] ${block.name.upper()}_${w.name.upper()}_OFFSET = ${block.addr_width}'h ${"%x" % w.base_addr}; - parameter logic [BlockAw-1:0] ${block.name.upper()}_${w.name.upper()}_SIZE = ${block.addr_width}'h ${"%x" % (w.limit_addr - w.base_addr)}; + parameter logic [BlockAw-1:0] ${ublock}_${w.name.upper()}_OFFSET = ${block.addr_width}'h ${"%x" % w.base_addr}; + parameter logic [BlockAw-1:0] ${ublock}_${w.name.upper()}_SIZE = ${block.addr_width}'h ${"%x" % (w.limit_addr - w.base_addr)}; % endfor // Register Index typedef enum int { % for r in block.get_regs_flat(): - ${block.name.upper()}_${r.name.upper()}${"" if loop.last else ","} + ${ublock}_${r.name.upper()}${"" if loop.last else ","} % endfor } ${block.name}_id_e; // Register width information to check illegal writes - parameter logic [3:0] ${block.name.upper()}_PERMIT [${block.get_n_regs_flat()}] = '{ + parameter logic [3:0] ${ublock}_PERMIT [${block.get_n_regs_flat()}] = '{ % for i,r in enumerate(block.get_regs_flat()): -<% index_str = "{}".format(i).rjust(max_regs_char) %>\ - % if r.width > 24: - 4'b 1111${" " if i == num_regs-1 else ","} // index[${index_str}] ${block.name.upper()}_${r.name.upper()} - % elif r.width > 16: - 4'b 0111${" " if i == num_regs-1 else ","} // index[${index_str}] ${block.name.upper()}_${r.name.upper()} - % elif r.width > 8: - 4'b 0011${" " if i == num_regs-1 else ","} // index[${index_str}] ${block.name.upper()}_${r.name.upper()} +<% + index_str = "{}".format(i).rjust(max_regs_char) + width = r.get_width() +%>\ + % if width > 24: + 4'b 1111${" " if i == num_regs-1 else ","} // index[${index_str}] ${ublock}_${r.name.upper()} + % elif width > 16: + 4'b 0111${" " if i == num_regs-1 else ","} // index[${index_str}] ${ublock}_${r.name.upper()} + % elif width > 8: + 4'b 0011${" " if i == num_regs-1 else ","} // index[${index_str}] ${ublock}_${r.name.upper()} % else: - 4'b 0001${" " if i == num_regs-1 else ","} // index[${index_str}] ${block.name.upper()}_${r.name.upper()} + 4'b 0001${" " if i == num_regs-1 else ","} // index[${index_str}] ${ublock}_${r.name.upper()} % endif % endfor };
diff --git a/util/reggen/reg_top.sv.tpl b/util/reggen/reg_top.sv.tpl index 75bafaf..2fbdf9b 100644 --- a/util/reggen/reg_top.sv.tpl +++ b/util/reggen/reg_top.sv.tpl
@@ -5,6 +5,8 @@ // Register Top module auto-generated by `reggen` <% from reggen.data import get_basename + from reggen.register import Register + from reggen.multi_register import MultiRegister num_wins = len(block.wins) num_wins_width = ((num_wins+1).bit_length()) - 1 @@ -146,10 +148,10 @@ // or <reg>_{wd|we|qs} if field == 1 or 0 % for r in regs_flat: % if len(r.fields) == 1: -${sig_gen(r.fields[0], r.name, r.hwext, r.shadowed)}\ +${sig_gen(r.fields[0], r.name.lower(), r.hwext, r.shadowed)}\ % else: % for f in r.fields: -${sig_gen(f, r.name + "_" + f.name.lower(), r.hwext, r.shadowed)}\ +${sig_gen(f, r.name.lower() + "_" + f.name.lower(), r.hwext, r.shadowed)}\ % endfor % endif % endfor @@ -157,37 +159,36 @@ // Register instances % for r in block.regs: ######################## multiregister ########################### - % if r.is_multi_reg(): + % if isinstance(r, MultiRegister): <% - mreg_flat = r.get_regs_flat() k = 0 %> - % for sr in mreg_flat: - // Subregister ${k} of Multireg ${r.name} - // R[${sr.name}]: V(${str(sr.hwext)}) + % for sr in r.regs: + // Subregister ${k} of Multireg ${r.reg.name.lower()} + // R[${sr.name.lower()}]: V(${str(sr.hwext)}) % if len(sr.fields) == 1: <% f = sr.fields[0] - finst_name = sr.name - fsig_name = r.name + "[%d]" % k + finst_name = sr.name.lower() + fsig_name = r.reg.name.lower() + "[%d]" % k k = k + 1 %> ${finst_gen(f, finst_name, fsig_name, sr.hwext, sr.regwen, sr.shadowed)} % else: % for f in sr.fields: <% - finst_name = sr.name + "_" + f.name.lower() - if r.ishomog: - fsig_name = r.name + "[%d]" % k + finst_name = sr.name.lower() + "_" + f.name.lower() + if r.is_homogeneous(): + fsig_name = r.reg.name.lower() + "[%d]" % k k = k + 1 else: - fsig_name = r.name + "[%d]" % k + "." + get_basename(f.name.lower()) + fsig_name = r.reg.name.lower() + "[%d]" % k + "." + get_basename(f.name.lower()) %> // F[${f.name.lower()}]: ${f.bits.msb}:${f.bits.lsb} ${finst_gen(f, finst_name, fsig_name, sr.hwext, sr.regwen, sr.shadowed)} % endfor <% - if not r.ishomog: + if not r.is_homogeneous(): k += 1 %> % endif @@ -195,20 +196,20 @@ % endfor ######################## register with single field ########################### % elif len(r.fields) == 1: - // R[${r.name}]: V(${str(r.hwext)}) + // R[${r.name.lower()}]: V(${str(r.hwext)}) <% f = r.fields[0] - finst_name = r.name - fsig_name = r.name + finst_name = r.name.lower() + fsig_name = r.name.lower() %> ${finst_gen(f, finst_name, fsig_name, r.hwext, r.regwen, r.shadowed)} ######################## register with multiple fields ########################### % else: - // R[${r.name}]: V(${str(r.hwext)}) + // R[${r.name.lower()}]: V(${str(r.hwext)}) % for f in r.fields: <% - finst_name = r.name + "_" + f.name.lower() - fsig_name = r.name + "." + f.name.lower() + finst_name = r.name.lower() + "_" + f.name.lower() + fsig_name = r.name.lower() + "." + f.name.lower() %> // F[${f.name.lower()}]: ${f.bits.msb}:${f.bits.lsb} ${finst_gen(f, finst_name, fsig_name, r.hwext, r.regwen, r.shadowed)} @@ -239,10 +240,10 @@ end % for i, r in enumerate(regs_flat): % if len(r.fields) == 1: -${we_gen(r.fields[0], r.name, r.hwext, r.shadowed, i)}\ +${we_gen(r.fields[0], r.name.lower(), r.hwext, r.shadowed, i)}\ % else: % for f in r.fields: -${we_gen(f, r.name + "_" + f.name.lower(), r.hwext, r.shadowed, i)}\ +${we_gen(f, r.name.lower() + "_" + f.name.lower(), r.hwext, r.shadowed, i)}\ % endfor % endif % endfor @@ -254,13 +255,13 @@ % for i, r in enumerate(regs_flat): % if len(r.fields) == 1: addr_hit[${i}]: begin -${rdata_gen(r.fields[0], r.name)}\ +${rdata_gen(r.fields[0], r.name.lower())}\ end % else: addr_hit[${i}]: begin % for f in r.fields: -${rdata_gen(f, r.name + "_" + f.name.lower())}\ +${rdata_gen(f, r.name.lower() + "_" + f.name.lower())}\ % endfor end @@ -322,7 +323,7 @@ % if field.swaccess.allows_write(): % if regwen: // qualified with register enable - .we (${finst_name}_we & ${regwen}_qs), + .we (${finst_name}_we & ${regwen.lower()}_qs), % else: .we (${finst_name}_we), % endif @@ -384,7 +385,7 @@ % if field.swaccess.allows_write(): ## non-RO types % if regwen: // from register interface (qualified with register enable) - .we (${finst_name}_we & ${regwen}_qs), + .we (${finst_name}_we & ${regwen.lower()}_qs), % else: // from register interface .we (${finst_name}_we),
diff --git a/util/reggen/register.py b/util/reggen/register.py index c9d4025..d56c352 100644 --- a/util/reggen/register.py +++ b/util/reggen/register.py
@@ -8,6 +8,7 @@ from .field import Field from .lib import (check_keys, check_str, check_name, check_bool, check_list, check_str_list, check_int) +from .reg_block import RegBlock REQUIRED_FIELDS = { 'name': ['s', "name of the register"], @@ -72,7 +73,7 @@ } -class Register: +class Register(RegBlock): '''Code representing a register for reggen''' def __init__(self, offset: int, @@ -90,7 +91,7 @@ fields: List[Field], update_err_alert: Optional[str], storage_err_alert: Optional[str]): - self.offset = offset + super().__init__(offset) self.name = name self.desc = desc @@ -270,6 +271,27 @@ def dv_rights(self) -> str: return self.swaccess.dv_rights() + def get_n_bits(self, bittype: List[str]) -> int: + return sum(field.get_n_bits(self.hwext, bittype) + for field in self.fields) + + def get_field_list(self) -> List[Field]: + return self.fields + + def is_homogeneous(self) -> bool: + return len(self.fields) == 1 + + def get_width(self) -> int: + '''Get the width of the fields in the register in bits + + This counts dead space between and below fields, so it's calculated as + one more than the highest msb. + + ''' + # self.fields is ordered by (increasing) LSB, so we can find the MSB of + # the register by taking the MSB of the last field. + return 1 + self.fields[-1].bits.msb + def make_multi(self, reg_width: int, offset: int,
diff --git a/util/reggen/uvm_reg.sv.tpl b/util/reggen/uvm_reg.sv.tpl index d8672f5..d26746d 100644 --- a/util/reggen/uvm_reg.sv.tpl +++ b/util/reggen/uvm_reg.sv.tpl
@@ -44,7 +44,7 @@ % for r in regs_flat: <% reg_width = block.width - reg_name = r.name + reg_name = r.name.lower() is_ext = 0 reg_shadowed = r.shadowed %>\ @@ -192,7 +192,7 @@ // registers % endif % for r in regs_flat: - rand ${gen_dv.rcname(block, r)} ${r.name}; + rand ${gen_dv.rcname(block, r)} ${r.name.lower()}; % endfor % if block.wins: // memories @@ -243,8 +243,8 @@ % endif % for r in regs_flat: <% - reg_name = r.name - reg_right = r.dvrights + reg_name = r.name.lower() + reg_right = r.dv_rights() reg_width = block.width reg_offset = str(reg_width) + "'h" + "%x" % r.offset reg_tags = r.tags @@ -275,7 +275,7 @@ // assign locked reg to its regwen reg % for r in regs_flat: % if r.regwen: - ${r.regwen}.add_locked_reg(${r.name}); + ${r.regwen.lower()}.add_locked_reg(${r.name.lower()}); % endif % endfor