[reggen] Define a class wrapping the top-level IP block
The bulk of this patch is in ip_block.py, which defines the IpBlock class.
This object replaces the top-level dictionary that we were parsing.
Client code then replaces something like this:
obj = hjson.load(hjson_file.open('r'),
use_decimal=True,
object_pairs_hook=OrderedDict)
if validate.validate(obj, params=[]) != 0:
log.info("Parsing %s configuration failed." % hjson_file)
sys.exit(1)
with
obj = IpBlock.from_path(str(hjson_file), [])
where obj is now an IpBlock object instead of a dict.
Other than some pesky rewrites in the various gen_FOO scripts and
template files, the other big change on the reggen side was to replace
the hierarchical "Block" class that was defined in data.py. Now, we
have a Top class (created by topgen code) and a Top can contain
multiple blocks. We've also now got some validation logic to make sure
that the sub-blocks and memories don't overlap: I'm not sure that was
there before.
As well as changing how we load files (as described above), topgen
also needed a bit of work. We now have to convert various objects to
dicts in the merge stage. (Before, we cloned the dictionaries and
added some keys; now we construct the new dictionary explicitly).
The idea is that in time we'll start to generate objects instead of
dicts in topgen as well. As a bonus, we should be able to get rid of
some of the spurious "dump & load" logic found there.
Signed-off-by: Rupert Swarbrick <rswarbrick@lowrisc.org>
diff --git a/util/reggen/alert.py b/util/reggen/alert.py
index 8f8a3cd..a23ff49 100644
--- a/util/reggen/alert.py
+++ b/util/reggen/alert.py
@@ -10,9 +10,10 @@
class Alert(Signal):
- def __init__(self, name: str, desc: str, bit: int):
+ def __init__(self, name: str, desc: str, bit: int, fatal: bool):
super().__init__(name, desc, Bits(bit, bit))
self.bit = bit
+ self.fatal = fatal
@staticmethod
def from_raw(what: str,
@@ -22,7 +23,20 @@
name = check_name(rd['name'], 'name field of ' + what)
desc = check_str(rd['desc'], 'desc field of ' + what)
- return Alert(name, desc, lsb)
+
+ # Make sense of the alert name, which should be prefixed with recov_ or
+ # fatal_.
+ pfx = name.split('_')[0]
+ if pfx == 'recov':
+ fatal = False
+ elif pfx == 'fatal':
+ fatal = True
+ else:
+ raise ValueError('Invalid name field of {}: alert names must be '
+ 'prefixed with "recov_" or "fatal_". Saw {!r}.'
+ .format(what, name))
+
+ return Alert(name, desc, lsb, fatal)
@staticmethod
def from_raw_list(what: str, raw: object) -> List['Alert']:
diff --git a/util/reggen/block.py b/util/reggen/block.py
new file mode 100644
index 0000000..7e2afeb
--- /dev/null
+++ b/util/reggen/block.py
@@ -0,0 +1,25 @@
+# Copyright lowRISC contributors.
+# Licensed under the Apache License, Version 2.0, see LICENSE for details.
+# SPDX-License-Identifier: Apache-2.0
+
+'''A subclass that can represent either a single block or a whole chip'''
+
+from typing import Dict
+
+from .reg_block import RegBlock
+
+
+class Block:
+ def __init__(self, name: str, regwidth: int, regs: RegBlock):
+ assert regwidth > 0
+
+ self.name = name
+ self.regwidth = regwidth
+ self.regs = regs
+
+ def _asdict(self) -> Dict[str, object]:
+ return {
+ 'name': self.name,
+ 'regwidth': self.regwidth,
+ 'regs': self.regs.as_dicts(),
+ }
diff --git a/util/reggen/data.py b/util/reggen/data.py
deleted file mode 100644
index 634827d..0000000
--- a/util/reggen/data.py
+++ /dev/null
@@ -1,29 +0,0 @@
-# Copyright lowRISC contributors.
-# Licensed under the Apache License, Version 2.0, see LICENSE for details.
-# SPDX-License-Identifier: Apache-2.0
-
-from collections import OrderedDict
-import re
-
-
-# helper function 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):
- match = re.search(r'_[0-9]+$', name)
- assert match
- assert match.start() > 0
- return name[0:match.start()]
-
-
-class Block():
- def __init__(self):
- self.width = 32
- self.addr_width = 12
- # Key is instance name
- self.base_addr = OrderedDict()
- self.name = ""
- self.hier_path = ""
- self.reg_block = None
- self.blocks = []
- self.params = []
- self.tags = []
diff --git a/util/reggen/fpv_csr.sv.tpl b/util/reggen/fpv_csr.sv.tpl
index 313440a..20bf4f8 100644
--- a/util/reggen/fpv_csr.sv.tpl
+++ b/util/reggen/fpv_csr.sv.tpl
@@ -10,19 +10,18 @@
from reggen.register import Register
from topgen import lib
+
+ lblock = block.name.lower()
%>\
<%def name="construct_classes(block)">\
-% for b in block.blocks:
-${construct_classes(b)}
-% endfor
`include "prim_assert.sv"
`ifdef UVM
import uvm_pkg::*;
`endif
-// Block: ${block.name}
-module ${block.name}_csr_assert_fpv import tlul_pkg::*; import ${block.name}_reg_pkg::*;
+// Block: ${lblock}
+module ${lblock}_csr_assert_fpv import tlul_pkg::*; import ${lblock}_reg_pkg::*;
import top_pkg::*;(
input clk_i,
input rst_ni,
@@ -32,8 +31,8 @@
input tl_d2h_t d2h,
// reg and hw ports
- input ${block.name}_reg2hw_t reg2hw,
- input ${block.name}_hw2reg_t hw2reg
+ input ${lblock}_reg2hw_t reg2hw,
+ input ${lblock}_hw2reg_t hw2reg
);
`ifndef VERILATOR
@@ -57,9 +56,9 @@
assign a_mask_bit[31:24] = h2d.a_mask[3] ? '1 : '0;
<%
- addr_msb = block.addr_width - 1
- reg_width = block.addr_width
- block_size = ((block.reg_block.flat_regs[-1].offset) >> 2) + 1
+ addr_width = block.regs.get_addr_width()
+ addr_msb = addr_width - 1
+ block_size = ((block.regs.flat_regs[-1].offset) >> 2) + 1
%>\
// store internal expected values for HW ReadOnly registers
logic [TL_DW-1:0] exp_vals[${block_size}];
@@ -70,7 +69,7 @@
assign normalized_addr = {h2d.a_address[${addr_msb}:2], 2'b0};
<% hro_regs_list = list(); %>\
-% for r in block.reg_block.flat_regs:
+% for r in block.regs.flat_regs:
% if not r.hwaccess.allows_write():
<% hro_regs_list.append(r) %>\
% endif
@@ -125,7 +124,7 @@
% if reg_mask != 0:
<% reg_mask_hex = format(reg_mask, 'x') %>\
`ASSERT(${r_name}_rd_A, d2h.d_valid && pend_trans[d2h.d_source].rd_pending &&
- pend_trans[d2h.d_source].addr == (${reg_width}'h${reg_addr_hex} >> 2) |->
+ pend_trans[d2h.d_source].addr == (${addr_width}'h${reg_addr_hex} >> 2) |->
d2h.d_error ||
(d2h.d_data & 'h${reg_mask_hex}) == (exp_vals[${reg_addr >> 2}] & 'h${reg_mask_hex}))
diff --git a/util/reggen/gen_cfg_html.py b/util/reggen/gen_cfg_html.py
index a69dfc6..bd47e66 100644
--- a/util/reggen/gen_cfg_html.py
+++ b/util/reggen/gen_cfg_html.py
@@ -13,16 +13,17 @@
def name_width(x):
- if 'width' not in x or x['width'] == '1':
- return x['name']
- return x['name'] + '[' + str(int(x['width'], 0) - 1) + ':0]'
+ if x.bits.width() == 1:
+ return x.name
+
+ return '{}[{}:0]'.format(x.name, x.bits.msb)
# Must have called cfg_validate, so should have no errors
def gen_cfg_html(cfgs, outfile):
- rnames = cfgs['genrnames']
+ rnames = list(cfgs.regs.name_to_offset.keys())
genout(outfile, "<p>Referring to the \n")
genout(
@@ -32,31 +33,31 @@
genout(outfile,
"Comportable guideline for peripheral device functionality</a>,\n")
genout(outfile,
- "the module <b><code>" + cfgs['name'] + "</code></b> has \n")
+ "the module <b><code>" + cfgs.name + "</code></b> has \n")
genout(outfile, "the following hardware interfaces defined.</p>\n")
# clocks
genout(
- outfile, "<p><i>Primary Clock:</i> <b><code>" + cfgs['clock_primary'] +
+ outfile, "<p><i>Primary Clock:</i> <b><code>" + cfgs.clock_signals[0] +
"</code></b></p>\n")
- if 'other_clock_list' in cfgs:
+ if len(cfgs.clock_signals) > 1:
genout(outfile, "<p><i>Other Clocks:</i></p>\n")
else:
genout(outfile, "<p><i>Other Clocks: none</i></p>\n")
# bus interfaces
genout(
outfile, "<p><i>Bus Device Interface:</i> <b><code>" +
- cfgs['bus_device'] + "</code></b></p>\n")
- if 'bus_host' in cfgs:
+ (cfgs.bus_device or '') + "</code></b></p>\n")
+ if cfgs.bus_host is not None:
genout(
outfile, "<p><i>Bus Host Interface:</i> <b><code>" +
- cfgs['bus_host'] + "</code></b></p>\n")
+ cfgs.bus_host + "</code></b></p>\n")
else:
genout(outfile, "<p><i>Bus Host Interface: none</i></p>\n")
# IO
- ios = ([('input', x) for x in cfgs.get('available_input_list', [])] +
- [('output', x) for x in cfgs.get('available_output_list', [])] +
- [('inout', x) for x in cfgs.get('available_inout_list', [])])
+ ios = ([('input', x) for x in cfgs.xputs[1]] +
+ [('output', x) for x in cfgs.xputs[2]] +
+ [('inout', x) for x in cfgs.xputs[0]])
if ios:
genout(outfile, "<p><i>Peripheral Pins for Chip IO:</i></p>\n")
genout(
@@ -68,37 +69,35 @@
'<tr><td>{}</td><td>{}</td>{}</tr>'
.format(name_width(x),
direction,
- render_td(x['desc'], rnames, None)))
+ render_td(x.desc, rnames, None)))
genout(outfile, "</table>\n")
else:
genout(outfile, "<p><i>Peripheral Pins for Chip IO: none</i></p>\n")
- interrupts = cfgs.get('interrupt_list', [])
- if not interrupts:
+ if not cfgs.interrupts:
genout(outfile, "<p><i>Interrupts: none</i></p>\n")
else:
genout(outfile, "<p><i>Interrupts:</i></p>\n")
genout(
outfile, "<table class=\"cfgtable\"><tr><th>Interrupt Name</th>" +
"<th>Description</th></tr>\n")
- for x in interrupts:
+ for x in cfgs.interrupts:
genout(outfile,
'<tr><td>{}</td>{}</tr>'
.format(name_width(x),
- render_td(x['desc'], rnames, None)))
+ render_td(x.desc, rnames, None)))
genout(outfile, "</table>\n")
- alerts = cfgs.get('alert_list', [])
- if not alerts:
+ if not cfgs.alerts:
genout(outfile, "<p><i>Security Alerts: none</i></p>\n")
else:
genout(outfile, "<p><i>Security Alerts:</i></p>\n")
genout(
outfile, "<table class=\"cfgtable\"><tr><th>Alert Name</th>" +
"<th>Description</th></tr>\n")
- for x in alerts:
+ for x in cfgs.alerts:
genout(outfile,
'<tr><td>{}</td>{}</tr>'
- .format(x['name'],
- render_td(x['desc'], rnames, None)))
+ .format(x.name,
+ render_td(x.desc, rnames, None)))
genout(outfile, "</table>\n")
diff --git a/util/reggen/gen_cheader.py b/util/reggen/gen_cheader.py
index 1180c79..cb37a3c 100644
--- a/util/reggen/gen_cheader.py
+++ b/util/reggen/gen_cheader.py
@@ -12,6 +12,7 @@
import warnings
+from .ip_block import IpBlock
from .register import Register
from .multi_register import MultiRegister
from .window import Window
@@ -190,10 +191,7 @@
def gen_cdefines_module_params(outstr, module_data, module_name,
register_width, existing_defines):
- module_params = set()
-
- if 'param_list' in module_data:
- module_params = module_data['param_list']
+ module_params = module_data.params
for param in module_params.get_localparams():
gen_cdefines_module_param(outstr, param, module_name, existing_defines)
@@ -272,77 +270,55 @@
.format(dname=defname), existing_defines))
-def gen_cdefines_interrupts(outstr, regs, component, regwidth,
+def gen_cdefines_interrupts(outstr, block, component, regwidth,
existing_defines):
- # no_auto_intr_regs controls whether interrupt registers are automatically
- # generated from the interrupt_list. This key could be 'true' or 'false',
- # but might also be True or False (the python booleans).
- no_auto_i = False
- if 'no_auto_intr_regs' in regs:
- no_auto_intr_regs_val = regs['no_auto_intr_regs']
- if isinstance(no_auto_intr_regs_val, bool):
- no_auto_i = no_auto_intr_regs_val
- elif no_auto_intr_regs_val.lower() in ["true", "false"]:
- no_auto_i = no_auto_intr_regs_val == "true"
- else:
- pass
-
# If no_auto_intr_regs is true, then we do not generate common defines,
# because the bit offsets for a particular interrupt may differ between
# the interrupt enable/state/test registers.
- if no_auto_i:
+ if block.no_auto_intr:
return
- interrupts = regs.get('interrupt_list', [])
genout(outstr, format_comment(first_line("Common Interrupt Offsets")))
- for intr in interrupts:
+ for intr in block.interrupts:
gen_cdefines_interrupt_field(outstr, intr, component, regwidth,
existing_defines)
genout(outstr, '\n')
-# Must have called validate, so should have no errors
-def gen_cdefines(regs, outfile, src_lic, src_copy):
- component = regs['name']
- registers = regs['registers']
- rnames = regs['genrnames']
+def gen_cdefines(block: IpBlock, outfile, src_lic, src_copy):
+ rnames = list(block.regs.name_to_offset.keys())
outstr = io.StringIO()
# This tracks the defines that have been generated so far, so we
# can error if we attempt to duplicate a definition
existing_defines = set()
- if 'regwidth' in regs:
- regwidth = int(regs['regwidth'], 0)
- else:
- regwidth = 32
-
- gen_cdefines_module_params(outstr, regs, component, regwidth,
+ gen_cdefines_module_params(outstr, block, block.name, block.regwidth,
existing_defines)
- gen_cdefines_interrupts(outstr, regs, component, regwidth,
+ gen_cdefines_interrupts(outstr, block, block.name, block.regwidth,
existing_defines)
- for x in registers.entries:
+ for x in block.regs.entries:
if isinstance(x, Register):
- gen_cdefine_register(outstr, x, component, regwidth, rnames,
+ gen_cdefine_register(outstr, x, block.name, block.regwidth, rnames,
existing_defines)
continue
if isinstance(x, MultiRegister):
- gen_cdefine_multireg(outstr, x, component, regwidth, rnames,
+ gen_cdefine_multireg(outstr, x, block.name, block.regwidth, rnames,
existing_defines)
continue
if isinstance(x, Window):
- gen_cdefine_window(outstr, x, component, regwidth,
+ gen_cdefine_window(outstr, x, block.name, block.regwidth,
rnames, existing_defines)
continue
generated = outstr.getvalue()
outstr.close()
- genout(outfile, '// Generated register defines for ' + component + '\n\n')
+ genout(outfile, '// Generated register defines for ' + block.name + '\n\n')
if src_copy != '':
genout(outfile, '// Copyright information found in source file:\n')
genout(outfile, '// ' + src_copy + '\n\n')
@@ -353,8 +329,8 @@
genout(outfile, '\n')
# Header Include Guard
- genout(outfile, '#ifndef _' + as_define(component) + '_REG_DEFS_\n')
- genout(outfile, '#define _' + as_define(component) + '_REG_DEFS_\n\n')
+ genout(outfile, '#ifndef _' + as_define(block.name) + '_REG_DEFS_\n')
+ genout(outfile, '#define _' + as_define(block.name) + '_REG_DEFS_\n\n')
# Header Extern Guard (so header can be used from C and C++)
genout(outfile, '#ifdef __cplusplus\n')
@@ -369,9 +345,9 @@
genout(outfile, '#endif\n')
# Header Include Guard
- genout(outfile, '#endif // _' + as_define(component) + '_REG_DEFS_\n')
+ genout(outfile, '#endif // _' + as_define(block.name) + '_REG_DEFS_\n')
- genout(outfile, '// End generated register defines for ' + component)
+ genout(outfile, '// End generated register defines for ' + block.name)
return 0
diff --git a/util/reggen/gen_dv.py b/util/reggen/gen_dv.py
index 6053d0b..c6e96e7 100644
--- a/util/reggen/gen_dv.py
+++ b/util/reggen/gen_dv.py
@@ -4,29 +4,30 @@
'''Generate DV code for an IP block'''
import logging as log
-import sys
from mako import exceptions
from mako.template import Template
from pkg_resources import resource_filename
from .access import HwAccess, SwRdAccess, SwWrAccess
-from .gen_rtl import json_to_reg
+from .block import Block
+from .top import Top
+from .ip_block import IpBlock
def bcname(b):
'''Get the name of the dv_base_reg_block subclass for this block'''
- return b.name + "_reg_block"
+ return b.name.lower() + "_reg_block"
def rcname(b, r):
'''Get the name of the dv_base_reg subclass for this register'''
- return b.name + "_reg_" + r.name.lower()
+ return b.name.lower() + "_reg_" + r.name.lower()
def mcname(b, m):
'''Get the name of the dv_base_mem subclass for this memory'''
- return b.name + "_mem_" + m.name.lower()
+ return b.name.lower() + "_mem_" + m.name.lower()
def miname(m):
@@ -34,19 +35,19 @@
return m.name.lower()
-def sv_base_addr(b, inst):
+def sv_base_addr(top: Top, inst_name: str):
'''Get the base address of a block in SV syntax'''
- return "{}'h{:x}".format(b.width, b.base_addr[inst])
+ return "{}'h{:x}".format(top.regwidth,
+ top.by_inst_name[inst_name][0])
-def gen_dv(obj, dv_base_prefix, outdir):
- '''Generate DV files using a raw dict object parsed from hjson'''
- gen_ral(json_to_reg(obj), dv_base_prefix, outdir)
+def gen_dv(block: IpBlock, dv_base_prefix, outdir):
+ '''Generate DV files for an IpBlock'''
+ gen_ral(block, dv_base_prefix, outdir)
-def gen_ral(block, dv_base_prefix, outdir):
- '''Generate DV RAL model from a gen_rtl.Block specification'''
-
+def gen_ral(block: Block, dv_base_prefix, outdir):
+ '''Generate DV RAL model for a Block'''
# Read template
tpl_filename = resource_filename('reggen', 'uvm_reg.sv.tpl')
uvm_reg_tpl = Template(filename=tpl_filename)
@@ -63,7 +64,7 @@
return 1
# Dump to output file
- dest_path = '{}/{}_ral_pkg.sv'.format(outdir, block.name)
+ dest_path = '{}/{}_ral_pkg.sv'.format(outdir, block.name.lower())
with open(dest_path, 'w') as fout:
fout.write(to_write)
diff --git a/util/reggen/gen_fpv.py b/util/reggen/gen_fpv.py
index d29c562..bfacce6 100644
--- a/util/reggen/gen_fpv.py
+++ b/util/reggen/gen_fpv.py
@@ -13,7 +13,7 @@
from pkg_resources import resource_filename
from .access import HwAccess, SwRdAccess, SwWrAccess
-from .gen_rtl import json_to_reg
+from .ip_block import IpBlock
# function get write property name
@@ -26,19 +26,13 @@
return r.name + "_rd_p"
-def gen_fpv(obj, outdir):
- # obj: OrderedDict
- block = json_to_reg(obj)
- gen_assertion(block, outdir)
-
-
-def gen_assertion(block, outdir):
+def gen_fpv(block: IpBlock, outdir):
# Read Register templates
fpv_csr_tpl = Template(
filename=resource_filename('reggen', 'fpv_csr.sv.tpl'))
# Generate pkg.sv with block name
- with open(outdir + "/" + block.name + "_csr_assert_fpv.sv", 'w') as fout:
+ with open(outdir + "/" + block.name.lower() + "_csr_assert_fpv.sv", 'w') as fout:
try:
fout.write(
fpv_csr_tpl.render(block=block,
diff --git a/util/reggen/gen_html.py b/util/reggen/gen_html.py
index 6e01600..5f7d35d 100644
--- a/util/reggen/gen_html.py
+++ b/util/reggen/gen_html.py
@@ -286,28 +286,21 @@
# Must have called validate, so should have no errors
-def gen_html(regs, outfile, toclist=None, toclevel=3):
- component = regs['name']
- registers = regs['registers']
- rnames = regs['genrnames']
+def gen_html(block, outfile, toclist=None, toclevel=3):
+ rnames = list(block.regs.name_to_offset.keys())
- if 'regwidth' in regs:
- regwidth = int(regs['regwidth'], 0)
- else:
- regwidth = 32
-
- for x in registers.entries:
+ for x in block.regs.entries:
if isinstance(x, Register):
- gen_html_register(outfile, x, component, regwidth, rnames, toclist,
- toclevel)
+ gen_html_register(outfile, x, block.name, block.regwidth, rnames,
+ toclist, toclevel)
continue
if isinstance(x, MultiRegister):
for reg in x.regs:
- gen_html_register(outfile, reg, component, regwidth, rnames,
- toclist, toclevel)
+ gen_html_register(outfile, reg, block.name, block.regwidth,
+ rnames, toclist, toclevel)
continue
if isinstance(x, Window):
- gen_html_window(outfile, x, component, regwidth, rnames,
+ gen_html_window(outfile, x, block.name, block.regwidth, rnames,
toclist, toclevel)
continue
diff --git a/util/reggen/gen_json.py b/util/reggen/gen_json.py
index 2e65f70..c593cc1 100644
--- a/util/reggen/gen_json.py
+++ b/util/reggen/gen_json.py
@@ -8,14 +8,6 @@
def gen_json(obj, outfile, format):
- # Temporary hack to deal with the fact that the 'registers' and
- # 'param_list' fields are lists rather than dictionaries. When we convert
- # the top-level object to a class (with its own _as_dict method), this
- # logic can go in there.
- obj = obj.copy()
- obj['registers'] = obj['registers'].as_dicts()
- obj['param_list'] = obj['param_list'].as_dicts()
-
if format == 'json':
hjson.dumpJSON(obj,
outfile,
diff --git a/util/reggen/gen_rtl.py b/util/reggen/gen_rtl.py
index 5bc04dd..a0df901 100644
--- a/util/reggen/gen_rtl.py
+++ b/util/reggen/gen_rtl.py
@@ -11,64 +11,14 @@
from pkg_resources import resource_filename
from .access import HwAccess, SwRdAccess, SwWrAccess
-from .data import Block
+from .ip_block import IpBlock
def escape_name(name):
return name.lower().replace(' ', '_')
-def check_field_bool(obj, field, default):
- if field in obj:
- return True if obj[field] == "true" else False
- else:
- return default
-
-
-def json_to_reg(obj):
- """Converts JSON OrderedDict into structure having useful information for
- Template to use.
-
- Main purpose of this function is:
- - Add Offset value based on auto calculation
- - Prepare Systemverilog data structure to generate _pkg file
- """
- block = Block()
-
- # Name
- block.name = escape_name(obj["name"])
- log.info("Processing module: %s", block.name)
-
- block.width = int(obj["regwidth"], 0)
-
- if block.width != 32 and block.width != 64:
- log.error(
- "Current reggen tool doesn't support field width that is not 32 nor 64"
- )
-
- log.info("Data Width is set to %d bits", block.width)
-
- block.params = obj["param_list"] if "param_list" in obj else []
-
- block.hier_path = obj["hier_path"] if "hier_path" in obj else ""
-
- block.reg_block = obj['registers']
-
- # Last offset and calculate space
- # Later on, it could use block.regs[-1].genoffset
- if "space" in obj:
- block.addr_width = int(obj["space"], 0).bit_length()
- else:
- block.addr_width = (obj["gensize"] - 1).bit_length()
-
- return block
-
-
-def gen_rtl(obj, outdir):
- # obj: OrderedDict
-
- block = json_to_reg(obj)
-
+def gen_rtl(block: IpBlock, outdir: str):
# Read Register templates
reg_top_tpl = Template(
filename=resource_filename('reggen', 'reg_top.sv.tpl'))
@@ -76,7 +26,7 @@
filename=resource_filename('reggen', 'reg_pkg.sv.tpl'))
# Generate pkg.sv with block name
- with open(outdir + "/" + block.name + "_reg_pkg.sv", 'w',
+ with open(outdir + "/" + block.name.lower() + "_reg_pkg.sv", 'w',
encoding='UTF-8') as fout:
try:
fout.write(
@@ -89,7 +39,7 @@
return 1
# Generate top.sv
- with open(outdir + "/" + block.name + "_reg_top.sv", 'w',
+ with open(outdir + "/" + block.name.lower() + "_reg_top.sv", 'w',
encoding='UTF-8') as fout:
try:
fout.write(
diff --git a/util/reggen/gen_selfdoc.py b/util/reggen/gen_selfdoc.py
index 8e46ac9..5f38404 100644
--- a/util/reggen/gen_selfdoc.py
+++ b/util/reggen/gen_selfdoc.py
@@ -7,7 +7,7 @@
"""
from .access import SWACCESS_PERMITTED, HWACCESS_PERMITTED
from reggen import (validate,
- enum_entry, field,
+ ip_block, enum_entry, field,
register, multi_register, window)
@@ -248,12 +248,10 @@
outfile, "\n\nThe top level of the JSON is a group containing "
"the following keys:\n")
doc_tbl_head(outfile, 1)
- for x in validate.top_required:
- doc_tbl_line(outfile, x, 'r', validate.top_required[x])
- for x in validate.top_optional:
- doc_tbl_line(outfile, x, 'o', validate.top_optional[x])
- for x in validate.top_added:
- doc_tbl_line(outfile, x, 'a', validate.top_added[x])
+ for k, v in ip_block.REQUIRED_FIELDS.items():
+ doc_tbl_line(outfile, k, 'r', v)
+ for k, v in ip_block.OPTIONAL_FIELDS.items():
+ doc_tbl_line(outfile, k, 'o', v)
genout(outfile, top_example)
genout(
diff --git a/util/reggen/ip_block.py b/util/reggen/ip_block.py
new file mode 100644
index 0000000..5d7fe3d
--- /dev/null
+++ b/util/reggen/ip_block.py
@@ -0,0 +1,319 @@
+# Copyright lowRISC contributors.
+# Licensed under the Apache License, Version 2.0, see LICENSE for details.
+# SPDX-License-Identifier: Apache-2.0
+
+'''Code representing an IP block for reggen'''
+
+from typing import Dict, List, Optional, Sequence, Tuple
+
+import hjson # type: ignore
+
+from .alert import Alert
+from .block import Block
+from .inter_signal import InterSignal
+from .lib import (check_keys, check_name, check_int, check_bool,
+ check_list, check_optional_str, check_name_list)
+from .params import Params, LocalParam
+from .reg_block import RegBlock
+from .signal import Signal
+
+
+REQUIRED_FIELDS = {
+ 'name': ['s', "name of the component"],
+ 'clock_primary': ['s', "name of the primary clock"],
+ 'bus_device': ['s', "name of the bus interface for the device"],
+ 'registers': [
+ 'l',
+ "list of register definition groups and "
+ "offset control groups"
+ ]
+}
+
+OPTIONAL_FIELDS = {
+ 'alert_list': ['lnw', "list of peripheral alerts"],
+ 'available_inout_list': ['lnw', "list of available peripheral inouts"],
+ 'available_input_list': ['lnw', "list of available peripheral inputs"],
+ 'available_output_list': ['lnw', "list of available peripheral outputs"],
+ 'bus_host': ['s', "name of the bus interface as host"],
+ 'hier_path': [
+ None,
+ 'additional hierarchy path before the reg block instance'
+ ],
+ 'interrupt_list': ['lnw', "list of peripheral interrupts"],
+ 'inter_signal_list': ['l', "list of inter-module signals"],
+ 'no_auto_alert_regs': [
+ 's', "Set to true to suppress automatic "
+ "generation of alert test registers. "
+ "Defaults to true if no alert_list is present. "
+ "Otherwise this defaults to false. "
+ ],
+ 'no_auto_intr_regs': [
+ 's', "Set to true to suppress automatic "
+ "generation of interrupt registers. "
+ "Defaults to true if no interrupt_list is present. "
+ "Otherwise this defaults to false. "
+ ],
+ 'other_clock_list': ['l', "list of other chip clocks needed"],
+ 'other_reset_list': ['l', "list of other resets"],
+ 'param_list': ['lp', "list of parameters of the IP"],
+ 'regwidth': ['d', "width of registers in bits (default 32)"],
+ 'reset_primary': ['s', "primary reset used by the module"],
+ 'reset_request_list': ['l', 'list of signals requesting reset'],
+ 'scan': ['pb', 'Indicates the module have `scanmode_i`'],
+ 'scan_reset': ['pb', 'Indicates the module have `test_rst_ni`'],
+ 'SPDX-License-Identifier': [
+ 's', "License ientifier (if using pure json) "
+ "Only use this if unable to put this "
+ "information in a comment at the top of the "
+ "file."
+ ],
+ 'wakeup_list': ['lnw', "list of peripheral wakeups"]
+}
+
+
+class IpBlock(Block):
+ def __init__(self,
+ name: str,
+ regwidth: int,
+ params: Params,
+ regs: RegBlock,
+ interrupts: Sequence[Signal],
+ no_auto_intr: bool,
+ alerts: List[Alert],
+ no_auto_alert: bool,
+ scan: bool,
+ inter_signals: List[InterSignal],
+ bus_device: Optional[str],
+ bus_host: Optional[str],
+ hier_path: Optional[str],
+ clock_signals: List[str],
+ reset_signals: List[str],
+ xputs: Tuple[Sequence[Signal],
+ Sequence[Signal],
+ Sequence[Signal]],
+ wakeups: Sequence[Signal],
+ reset_requests: Sequence[Signal],
+ scan_reset: bool):
+ assert clock_signals
+ assert reset_signals
+
+ super().__init__(name, regwidth, regs)
+
+ self.params = params
+ self.interrupts = interrupts
+ self.no_auto_intr = no_auto_intr
+ self.alerts = alerts
+ self.no_auto_alert = no_auto_alert
+ self.scan = scan
+ self.inter_signals = inter_signals
+ self.bus_device = bus_device
+ self.bus_host = bus_host
+ self.hier_path = hier_path
+ self.clock_signals = clock_signals
+ self.reset_signals = reset_signals
+ self.xputs = xputs
+ self.wakeups = wakeups
+ self.reset_requests = reset_requests
+ self.scan_reset = scan_reset
+
+ @staticmethod
+ def from_raw(param_defaults: List[Tuple[str, str]],
+ raw: object,
+ where: str) -> 'IpBlock':
+
+ rd = check_keys(raw, 'block at ' + where,
+ list(REQUIRED_FIELDS.keys()),
+ list(OPTIONAL_FIELDS.keys()))
+
+ name = check_name(rd['name'], 'name of block at ' + where)
+
+ what = '{} block at {}'.format(name, where)
+
+ r_regwidth = rd.get('regwidth')
+ if r_regwidth is None:
+ regwidth = 32
+ else:
+ regwidth = check_int(r_regwidth, 'regwidth field of ' + what)
+ if regwidth <= 0:
+ raise ValueError('Invalid regwidth field for {}: '
+ '{} is not positive.'
+ .format(what, regwidth))
+
+ params = Params.from_raw('parameter list for ' + what,
+ rd.get('param_list', []))
+ try:
+ params.apply_defaults(param_defaults)
+ except (ValueError, KeyError) as err:
+ raise ValueError('Failed to apply defaults to params: {}'
+ .format(err)) from None
+
+ regs = RegBlock(regwidth, params)
+
+ interrupts = Signal.from_raw_list('interrupt_list for block {}'
+ .format(name),
+ rd.get('interrupt_list', []))
+ alerts = Alert.from_raw_list('alert_list for block {}'
+ .format(name),
+ rd.get('alert_list', []))
+
+ no_auto_intr = check_bool(rd.get('no_auto_intr_regs', not interrupts),
+ 'no_auto_intr_regs field of ' + what)
+
+ no_auto_alert = check_bool(rd.get('no_auto_alert_regs', not alerts),
+ 'no_auto_alert_regs field of ' + what)
+
+ if interrupts and not no_auto_intr:
+ if interrupts[-1].bits.msb >= regwidth:
+ raise ValueError("Interrupt list for {} is too wide: "
+ "msb is {}, which doesn't fit with a "
+ "regwidth of {}."
+ .format(what,
+ interrupts[-1].bits.msb, regwidth))
+ regs.make_intr_regs(interrupts)
+
+ if alerts:
+ if not no_auto_alert:
+ if len(alerts) > regwidth:
+ raise ValueError("Interrupt list for {} is too wide: "
+ "{} alerts don't fit with a regwidth of {}."
+ .format(what, len(alerts), regwidth))
+ regs.make_alert_regs(alerts)
+
+ # Generate a NumAlerts parameter
+ existing_param = params.get('NumAlerts')
+ if existing_param is not None:
+ if ((not isinstance(existing_param, LocalParam) or
+ existing_param.param_type != 'int' or
+ existing_param.value != str(len(alerts)))):
+ raise ValueError('Conflicting definition of NumAlerts '
+ 'parameter.')
+ else:
+ params.add(LocalParam(name='NumAlerts',
+ desc='Number of alerts',
+ param_type='int',
+ value=str(len(alerts))))
+
+ scan = check_bool(rd.get('scan', False), 'scan field of ' + what)
+
+ regs.add_raw_registers(rd['registers'])
+ regs.validate()
+
+ r_inter_signals = check_list(rd.get('inter_signal_list', []),
+ 'inter_signal_list field')
+ inter_signals = [
+ InterSignal.from_raw('entry {} of the inter_signal_list field'
+ .format(idx + 1),
+ entry)
+ for idx, entry in enumerate(r_inter_signals)
+ ]
+
+ bus_device = check_optional_str(rd.get('bus_device', None),
+ 'bus_device field of ' + what)
+ bus_host = check_optional_str(rd.get('bus_host', None),
+ 'bus_host field of ' + what)
+
+ if bus_device == "tlul":
+ # Add to inter_module_signal
+ port_name = "tl" if bus_host in ["none", "", None] else "tl_d"
+ inter_signals.append(InterSignal(port_name, None, 'tl', 'tlul_pkg',
+ 'req_rsp', 'rsp', 1, None))
+
+ if bus_host == "tlul":
+ inter_signals.append(InterSignal('tl_h', None, 'tl', 'tlul_pkg',
+ 'req_rsp', 'rsp', 1, None))
+
+ hier_path = check_optional_str(rd.get('hier_path', None),
+ 'hier_path field of ' + what)
+
+ clock_primary = check_name(rd['clock_primary'],
+ 'clock_primary field of ' + what)
+ other_clock_list = check_name_list(rd.get('other_clock_list', []),
+ 'other_clock_list field of ' + what)
+ clock_signals = [clock_primary] + other_clock_list
+
+ reset_primary = check_name(rd.get('reset_primary', 'rst_ni'),
+ 'reset_primary field of ' + what)
+ other_reset_list = check_name_list(rd.get('other_reset_list', []),
+ 'other_reset_list field of ' + what)
+ reset_signals = [reset_primary] + other_reset_list
+
+ xputs = (
+ Signal.from_raw_list('available_inout_list for block ' + name,
+ rd.get('available_inout_list', [])),
+ Signal.from_raw_list('available_input_list for block ' + name,
+ rd.get('available_input_list', [])),
+ Signal.from_raw_list('available_output_list for block ' + name,
+ rd.get('available_output_list', []))
+ )
+ wakeups = Signal.from_raw_list('wakeup_list for block ' + name,
+ rd.get('wakeup_list', []))
+ rst_reqs = Signal.from_raw_list('reset_request_list for block ' + name,
+ rd.get('reset_request_list', []))
+
+ scan_reset = check_bool(rd.get('scan_reset', False),
+ 'scan_reset field of ' + what)
+
+ return IpBlock(name, regwidth, params, regs,
+ interrupts, no_auto_intr, alerts, no_auto_alert,
+ scan, inter_signals, bus_device, bus_host,
+ hier_path, clock_signals, reset_signals,
+ xputs, wakeups, rst_reqs, scan_reset)
+
+ @staticmethod
+ def from_text(txt: str,
+ param_defaults: List[Tuple[str, str]],
+ where: str) -> 'IpBlock':
+ '''Load an IpBlock from an hjson description in txt'''
+ return IpBlock.from_raw(param_defaults,
+ hjson.loads(txt, use_decimal=True),
+ where)
+
+ @staticmethod
+ def from_path(path: str,
+ param_defaults: List[Tuple[str, str]]) -> 'IpBlock':
+ '''Load an IpBlock from an hjson description in a file at path'''
+ with open(path, 'r') as handle:
+ return IpBlock.from_text(handle.read(), param_defaults,
+ 'file at {!r}'.format(path))
+
+ def _asdict(self) -> Dict[str, object]:
+ ret = super()._asdict()
+ ret['param_list'] = self.params.as_dicts()
+ ret['interrupt_list'] = self.interrupts
+ ret['no_auto_intr_regs'] = self.no_auto_intr
+ ret['alert_list'] = self.alerts
+ ret['no_auto_alert_regs'] = self.no_auto_alert
+ ret['scan'] = self.scan
+ ret['inter_signal_list'] = self.inter_signals
+
+ if self.bus_device is not None:
+ ret['bus_device'] = self.bus_device
+ if self.bus_host is not None:
+ ret['bus_host'] = self.bus_host
+ if self.hier_path is not None:
+ ret['hier_path'] = self.hier_path
+
+ ret['clock_primary'] = self.clock_signals[0]
+ if len(self.clock_signals) > 1:
+ ret['other_clock_list'] = self.clock_signals[1:]
+
+ ret['reset_primary'] = self.reset_signals[0]
+ if len(self.reset_signals) > 1:
+ ret['other_reset_list'] = self.reset_signals[1:]
+
+ inouts, inputs, outputs = self.xputs
+ if inouts:
+ ret['available_inout_list'] = inouts
+ if inputs:
+ ret['available_input_list'] = inputs
+ if outputs:
+ ret['available_output_list'] = outputs
+
+ if self.wakeups:
+ ret['wakeup_list'] = self.wakeups
+ if self.reset_requests:
+ ret['reset_request_list'] = self.reset_requests
+
+ ret['scan_reset'] = self.scan_reset
+
+ return ret
diff --git a/util/reggen/lib.py b/util/reggen/lib.py
index ddfd573..d72ef3d 100644
--- a/util/reggen/lib.py
+++ b/util/reggen/lib.py
@@ -189,6 +189,19 @@
return cast(List[str], lst)
+def check_name_list(obj: object, what: str) -> List[str]:
+ '''Check that the given object is a list of valid names
+
+ If not, raise a ValueError; the what argument names the object.
+
+ '''
+ lst = check_list(obj, what)
+ for idx, elt in enumerate(lst):
+ check_name(elt, 'Element {} of {}'.format(idx + 1, what))
+
+ return cast(List[str], lst)
+
+
def check_int(obj: object, what: str) -> int:
'''Check that obj is an integer or a string that parses to an integer.
@@ -237,3 +250,13 @@
def check_optional_str(obj: object, what: str) -> Optional[str]:
'''Check that obj is a string or None'''
return None if obj is None else check_str(obj, what)
+
+
+def get_basename(name: str) -> str:
+ '''Strip trailing _number (used as multireg suffix) from name'''
+ # TODO: This is a workaround, should solve this as part of parsing a
+ # multi-reg.
+ match = re.search(r'_[0-9]+$', name)
+ assert match
+ assert match.start() > 0
+ return name[0:match.start()]
diff --git a/util/reggen/params.py b/util/reggen/params.py
index 14f52a0..7dbde24 100644
--- a/util/reggen/params.py
+++ b/util/reggen/params.py
@@ -3,7 +3,7 @@
# SPDX-License-Identifier: Apache-2.0
import re
-from typing import Dict, List, Optional
+from typing import Dict, List, Optional, Tuple
from .lib import check_keys, check_str, check_int, check_bool, check_list
@@ -252,24 +252,15 @@
def get(self, name: str) -> Optional[BaseParam]:
return self.by_name.get(name)
- def _apply_default(self, name: str, value: str) -> None:
- param = self.by_name.get(name)
- if param is None:
- raise KeyError('Cannot find parameter '
- '{} to set default value.'
- .format(name))
+ def apply_defaults(self, defaults: List[Tuple[str, str]]) -> None:
+ for idx, (key, value) in enumerate(defaults):
+ param = self.by_name.get(key)
+ if param is None:
+ raise KeyError('Cannot find parameter '
+ '{} to set default value.'
+ .format(key))
- param.apply_default(value)
-
- def apply_defaults(self, defaults: List[str]) -> None:
- for idx, entry in enumerate(defaults):
- tokens = entry.split('=')
- if len(tokens) != 2:
- raise ValueError('Entry {} in list of parameter defaults to '
- 'apply is {!r}, which is not of the form '
- 'param=value.'
- .format(idx, entry))
- self._apply_default(tokens[0], tokens[1])
+ param.apply_default(value)
def _expand_one(self, value: str, when: str) -> int:
# Check whether value is already an integer: if so, return that.
diff --git a/util/reggen/reg_block.py b/util/reggen/reg_block.py
index 54cf784..a3c3486 100644
--- a/util/reggen/reg_block.py
+++ b/util/reggen/reg_block.py
@@ -5,8 +5,12 @@
'''Code representing the registers, windows etc. for a block'''
import re
-from typing import Dict, List, Union
+from typing import Callable, Dict, List, Optional, Sequence, Union
+from .alert import Alert
+from .access import SWAccess, HWAccess
+from .field import Field
+from .signal import Signal
from .lib import check_int, check_list, check_str_dict
from .multi_register import MultiRegister
from .params import Params
@@ -15,9 +19,9 @@
class RegBlock:
- def __init__(self, addrsep: int, reg_width: int, params: Params):
+ def __init__(self, reg_width: int, params: Params):
- self._addrsep = addrsep
+ self._addrsep = (reg_width + 7) // 8
self._reg_width = reg_width
self._params = params
@@ -259,3 +263,112 @@
offset = entry.next_offset(self._addrsep)
return entries
+
+ _FieldFormatter = Callable[[bool, str], str]
+
+ def _add_intr_alert_reg(self,
+ signals: Sequence[Signal],
+ reg_name: str,
+ reg_desc: str,
+ field_desc_fmt: Optional[Union[str, _FieldFormatter]],
+ swaccess: str,
+ hwaccess: str,
+ is_testreg: bool,
+ reg_tags: List[str]) -> None:
+ swaccess_obj = SWAccess('RegBlock._make_intr_alert_reg()', swaccess)
+ hwaccess_obj = HWAccess('RegBlock._make_intr_alert_reg()', hwaccess)
+
+ fields = []
+ for signal in signals:
+ if field_desc_fmt is None:
+ field_desc = signal.desc
+ elif isinstance(field_desc_fmt, str):
+ field_desc = field_desc_fmt
+ else:
+ width = signal.bits.width()
+ field_desc = field_desc_fmt(width > 1, signal.name)
+
+ fields.append(Field(signal.name,
+ field_desc or signal.desc,
+ tags=[],
+ swaccess=swaccess_obj,
+ hwaccess=hwaccess_obj,
+ hwqe=is_testreg,
+ hwre=False,
+ bits=signal.bits,
+ resval=0,
+ enum=None))
+
+ reg = Register(self.offset,
+ reg_name,
+ reg_desc,
+ swaccess_obj,
+ hwaccess_obj,
+ hwext=is_testreg,
+ hwqe=is_testreg,
+ hwre=False,
+ regwen=None,
+ tags=reg_tags,
+ resval=None,
+ shadowed=False,
+ fields=fields,
+ update_err_alert=None,
+ storage_err_alert=None)
+ self.add_register(reg)
+
+ def make_intr_regs(self, interrupts: Sequence[Signal]) -> None:
+ assert interrupts
+ assert interrupts[-1].bits.msb < self._reg_width
+
+ self._add_intr_alert_reg(interrupts,
+ 'INTR_STATE',
+ 'Interrupt State Register',
+ None,
+ 'rw1c',
+ 'hrw',
+ False,
+ # intr_state csr is affected by writes to
+ # other csrs - skip write-check
+ ["excl:CsrNonInitTests:CsrExclWriteCheck"])
+ self._add_intr_alert_reg(interrupts,
+ 'INTR_ENABLE',
+ 'Interrupt Enable Register',
+ lambda w, n: ('Enable interrupt when '
+ '{}!!INTR_STATE.{} is set.'
+ .format('corresponding bit in '
+ if w else '',
+ n)),
+ 'rw',
+ 'hro',
+ False,
+ [])
+ self._add_intr_alert_reg(interrupts,
+ 'INTR_TEST',
+ 'Interrupt Test Register',
+ lambda w, n: ('Write 1 to force '
+ '{}!!INTR_STATE.{} to 1.'
+ .format('corresponding bit in '
+ if w else '',
+ n)),
+ 'wo',
+ 'hro',
+ True,
+ # intr_test csr is WO so reads back 0s
+ ["excl:CsrNonInitTests:CsrExclWrite"])
+
+ def make_alert_regs(self, alerts: List[Alert]) -> None:
+ assert alerts
+ assert len(alerts) < self._reg_width
+ self._add_intr_alert_reg(alerts,
+ 'ALERT_TEST',
+ 'Alert Test Register',
+ ('Write 1 to trigger '
+ 'one alert event of this kind.'),
+ 'wo',
+ 'hro',
+ True,
+ [])
+
+ def get_addr_width(self) -> int:
+ '''Calculate the number of bits to address every byte of the block'''
+ return (self.offset - 1).bit_length()
diff --git a/util/reggen/reg_pkg.sv.tpl b/util/reggen/reg_pkg.sv.tpl
index f97b476..780d9e3 100644
--- a/util/reggen/reg_pkg.sv.tpl
+++ b/util/reggen/reg_pkg.sv.tpl
@@ -9,12 +9,16 @@
from reggen.register import Register
from reggen.multi_register import MultiRegister
- flat_regs = block.reg_block.flat_regs
+ flat_regs = block.regs.flat_regs
num_regs = len(flat_regs)
max_regs_char = len("{}".format(num_regs - 1))
localparams = block.params.get_localparams()
+ addr_width = block.regs.get_addr_width()
+
+ lblock = block.name.lower()
+ ublock = lblock.upper()
%>\
-package ${block.name}_reg_pkg;
+package ${lblock}_reg_pkg;
% if localparams:
// Param list
@@ -24,12 +28,12 @@
% endfor
// Address width within the block
- parameter int BlockAw = ${block.addr_width};
+ parameter int BlockAw = ${addr_width};
////////////////////////////
// Typedefs for registers //
////////////////////////////
-% for r in block.reg_block.all_regs:
+% for r in block.regs.all_regs:
% if r.get_n_bits(["q"]):
<%
if isinstance(r, Register):
@@ -41,7 +45,7 @@
type_suff = 'mreg_t'
reg2hw_name = ('{}_reg2hw_{}_{}'
- .format(block.name, r0.name.lower(), type_suff))
+ .format(lblock, r0.name.lower(), type_suff))
%>\
typedef struct packed {
% if r.is_homogeneous():
@@ -97,7 +101,7 @@
%endif
% endfor
-% for r in block.reg_block.all_regs:
+% for r in block.regs.all_regs:
% if r.get_n_bits(["d"]):
<%
if isinstance(r, Register):
@@ -109,7 +113,7 @@
type_suff = 'mreg_t'
hw2reg_name = ('{}_hw2reg_{}_{}'
- .format(block.name, r0.name.lower(), type_suff))
+ .format(lblock, r0.name.lower(), type_suff))
%>\
typedef struct packed {
% if r.is_homogeneous():
@@ -155,12 +159,12 @@
// Register to internal design logic //
///////////////////////////////////////
<%
-nbits = block.reg_block.get_n_bits(["q", "qe", "re"])
+nbits = block.regs.get_n_bits(["q", "qe", "re"])
packbit = 0
%>\
% if nbits > 0:
typedef struct packed {
-% for r in block.reg_block.all_regs:
+% for r in block.regs.all_regs:
% if r.get_n_bits(["q"]):
<%
if isinstance(r, MultiRegister):
@@ -173,7 +177,7 @@
type_suff = 'reg_t'
struct_type = ('{}_reg2hw_{}_{}'
- .format(block.name, r0.name.lower(), type_suff))
+ .format(lblock, r0.name.lower(), type_suff))
struct_width = r0.get_n_bits(['q', 'qe', 're']) * repl_count
msb = nbits - packbit - 1
@@ -183,19 +187,19 @@
${struct_type} ${r0.name.lower()}; // [${msb}:${lsb}]
% endif
% endfor
- } ${block.name}_reg2hw_t;
+ } ${lblock}_reg2hw_t;
% endif
///////////////////////////////////////
// Internal design logic to register //
///////////////////////////////////////
<%
-nbits = block.reg_block.get_n_bits(["d", "de"])
+nbits = block.regs.get_n_bits(["d", "de"])
packbit = 0
%>\
% if nbits > 0:
typedef struct packed {
-% for r in block.reg_block.all_regs:
+% for r in block.regs.all_regs:
% if r.get_n_bits(["d"]):
<%
if isinstance(r, MultiRegister):
@@ -208,7 +212,7 @@
type_suff = 'reg_t'
struct_type = ('{}_hw2reg_{}_{}'
- .format(block.name, r0.name.lower(), type_suff))
+ .format(lblock, r0.name.lower(), type_suff))
struct_width = r0.get_n_bits(['d', 'de']) * repl_count
msb = nbits - packbit - 1
@@ -218,12 +222,11 @@
${struct_type} ${r0.name.lower()}; // [${msb}:${lsb}]
% endif
% endfor
- } ${block.name}_hw2reg_t;
+ } ${lblock}_hw2reg_t;
% endif
// Register Address
<%
-ublock = block.name.upper()
def reg_pfx(reg):
return '{}_{}'.format(ublock, reg.name.upper())
@@ -236,7 +239,7 @@
%>\
% for r in flat_regs:
- parameter logic [BlockAw-1:0] ${reg_pfx(r)}_OFFSET = ${block.addr_width}'h ${"%x" % r.offset};
+ parameter logic [BlockAw-1:0] ${reg_pfx(r)}_OFFSET = ${addr_width}'h ${"%x" % r.offset};
% endfor
<%
@@ -262,13 +265,13 @@
% endfor
% endif
-% if len(block.reg_block.windows) > 0:
+% if len(block.regs.windows) > 0:
// Window parameter
-% for i,w in enumerate(block.reg_block.windows):
+% for i,w in enumerate(block.regs.windows):
<%
win_pfx = '{}_{}'.format(ublock, w.name.upper())
- base_txt_val = "{}'h {:x}".format(block.addr_width, w.offset)
- size_txt_val = "{}'h {:x}".format(block.addr_width, w.size_in_bytes)
+ base_txt_val = "{}'h {:x}".format(addr_width, w.offset)
+ size_txt_val = "{}'h {:x}".format(addr_width, w.size_in_bytes)
%>\
parameter logic [BlockAw-1:0] ${win_pfx}_OFFSET = ${base_txt_val};
parameter logic [BlockAw-1:0] ${win_pfx}_SIZE = ${size_txt_val};
@@ -280,7 +283,7 @@
% for r in flat_regs:
${ublock}_${r.name.upper()}${"" if loop.last else ","}
% endfor
- } ${block.name}_id_e;
+ } ${lblock}_id_e;
// Register width information to check illegal writes
parameter logic [3:0] ${ublock}_PERMIT [${len(flat_regs)}] = '{
diff --git a/util/reggen/reg_top.sv.tpl b/util/reggen/reg_top.sv.tpl
index 474f37c..1923d19 100644
--- a/util/reggen/reg_top.sv.tpl
+++ b/util/reggen/reg_top.sv.tpl
@@ -4,19 +4,23 @@
//
// Register Top module auto-generated by `reggen`
<%
- from reggen.data import get_basename
+ from reggen.lib import get_basename
from reggen.register import Register
from reggen.multi_register import MultiRegister
- num_wins = len(block.reg_block.windows)
+ num_wins = len(block.regs.windows)
num_wins_width = ((num_wins+1).bit_length()) - 1
num_dsp = num_wins + 1
- regs_flat = block.reg_block.flat_regs
+ regs_flat = block.regs.flat_regs
max_regs_char = len("{}".format(len(regs_flat) - 1))
+ addr_width = block.regs.get_addr_width()
+
+ lblock = block.name.lower()
+ ublock = lblock.upper()
%>
`include "prim_assert.sv"
-module ${block.name}_reg_top (
+module ${lblock}_reg_top (
input clk_i,
input rst_ni,
@@ -31,11 +35,11 @@
% endif
// To HW
-% if block.reg_block.get_n_bits(["q","qe","re"]):
- output ${block.name}_reg_pkg::${block.name}_reg2hw_t reg2hw, // Write
+% if block.regs.get_n_bits(["q","qe","re"]):
+ output ${lblock}_reg_pkg::${lblock}_reg2hw_t reg2hw, // Write
% endif
-% if block.reg_block.get_n_bits(["d","de"]):
- input ${block.name}_reg_pkg::${block.name}_hw2reg_t hw2reg, // Read
+% if block.regs.get_n_bits(["d","de"]):
+ input ${lblock}_reg_pkg::${lblock}_hw2reg_t hw2reg, // Read
% endif
// Integrity check errors
@@ -45,10 +49,10 @@
input devmode_i // If 1, explicit error return for unmapped register access
);
- import ${block.name}_reg_pkg::* ;
+ import ${lblock}_reg_pkg::* ;
- localparam int AW = ${block.addr_width};
- localparam int DW = ${block.width};
+ localparam int AW = ${addr_width};
+ localparam int DW = ${block.regwidth};
localparam int DBW = DW/8; // Byte Width
// register signals
@@ -105,7 +109,7 @@
assign tl_reg_h2d = tl_socket_h2d[${num_wins}];
assign tl_socket_d2h[${num_wins}] = tl_reg_d2h;
- % for i,t in enumerate(block.reg_block.windows):
+ % for i,t in enumerate(block.regs.windows):
assign tl_win_o[${i}] = tl_socket_h2d[${i}];
assign tl_socket_d2h[${i}] = tl_win_i[${i}];
% endfor
@@ -136,12 +140,12 @@
reg_steer = ${num_dsp-1}; // Default set to register
// TODO: Can below codes be unique case () inside ?
- % for i,w in enumerate(block.reg_block.windows):
+ % for i,w in enumerate(block.regs.windows):
<%
base_addr = w.offset
limit_addr = w.offset + w.size_in_bytes
%>\
- % if limit_addr == 2**block.addr_width:
+ % if limit_addr == 2**addr_width:
if (tl_i.a_address[AW-1:0] >= ${base_addr}) begin
// Exceed or meet the address range. Removed the comparison of limit addr 'h ${'{:x}'.format(limit_addr)}
% else:
@@ -192,7 +196,7 @@
% endfor
// Register instances
- % for r in block.reg_block.all_regs:
+ % for r in block.regs.all_regs:
######################## multiregister ###########################
% if isinstance(r, MultiRegister):
<%
@@ -251,7 +255,7 @@
% endfor
% endif
- ## for: block.reg_block.all_regs
+ ## for: block.regs.all_regs
% endfor
@@ -259,7 +263,7 @@
always_comb begin
addr_hit = '0;
% for i,r in enumerate(regs_flat):
- addr_hit[${"{}".format(i).rjust(max_regs_char)}] = (reg_addr == ${block.name.upper()}_${r.name.upper()}_OFFSET);
+ addr_hit[${"{}".format(i).rjust(max_regs_char)}] = (reg_addr == ${ublock}_${r.name.upper()}_OFFSET);
% endfor
end
@@ -270,7 +274,7 @@
wr_err = 1'b0;
% for i,r in enumerate(regs_flat):
<% index_str = "{}".format(i).rjust(max_regs_char) %>\
- if (addr_hit[${index_str}] && reg_we && (${block.name.upper()}_PERMIT[${index_str}] != (${block.name.upper()}_PERMIT[${index_str}] & reg_be))) wr_err = 1'b1 ;
+ if (addr_hit[${index_str}] && reg_we && (${ublock}_PERMIT[${index_str}] != (${ublock}_PERMIT[${index_str}] & reg_be))) wr_err = 1'b1 ;
% endfor
end
% for i, r in enumerate(regs_flat):
diff --git a/util/reggen/top.py b/util/reggen/top.py
new file mode 100644
index 0000000..f69d169
--- /dev/null
+++ b/util/reggen/top.py
@@ -0,0 +1,80 @@
+# Copyright lowRISC contributors.
+# Licensed under the Apache License, Version 2.0, see LICENSE for details.
+# SPDX-License-Identifier: Apache-2.0
+
+'''Code representing the entire chip for reggen'''
+
+from typing import Dict, List, Tuple, Union
+
+from .block import Block
+from .ip_block import IpBlock
+from .params import Params
+from .reg_block import RegBlock
+from .window import Window
+
+_Triple = Tuple[int, str, IpBlock]
+
+
+class Top(Block):
+ def __init__(self,
+ regwidth: int,
+ blocks: Dict[int, Tuple[str, IpBlock]],
+ windows: List[Window]):
+ super().__init__('chip',
+ regwidth,
+ RegBlock(regwidth, Params()))
+
+ self.regwidth = regwidth
+
+ addrsep = (regwidth + 7) // 8
+
+ # A list of blocks. Each appears just once, even if it has multiple instances
+ self.blocks = [] # type: List[IpBlock]
+
+ # A dictionary mapping instance name to a pair (base_addr, block)
+ self.by_inst_name = {} # type: Dict[str, Tuple[int, IpBlock]]
+
+ # A list of tuples: (addr, instance_name, block) in increasing order of
+ # address.
+ self.instances = [] # type: List[_Triple]
+
+ # A dictionary mapping block name to instances of that block. Items are
+ # pairs (addr, instance_name, block).
+ self.block_name_to_instances = {} # type: Dict[str, List[_Triple]]
+
+ # Generate one big map from base address to object (with each value
+ # either a pair (instance_name, block) or a window).
+ addr_to_obj = {} # type: Dict[int, Union[Tuple[str, IpBlock], Window]]
+ for addr, pr in blocks.items():
+ addr_to_obj[addr] = pr
+ for window in windows:
+ addr_to_obj[window.offset] = window
+
+ # Now walk this big map again, constructing the two views on the blocks
+ # and adding the windows as we go but checking for overlaps the whole
+ # time.
+ offset = 0
+ for base_addr in sorted(addr_to_obj.keys()):
+ obj = addr_to_obj[base_addr]
+
+ # Make sure that this block doesn't overlap with the previous one
+ assert offset <= base_addr
+
+ if isinstance(obj, Window):
+ offset = obj.next_offset(addrsep)
+ self.regs.add_window(obj)
+ else:
+ name, block = blocks[base_addr]
+
+ if block.name not in self.block_name_to_instances:
+ self.blocks.append(block)
+
+ assert name not in self.by_inst_name
+ self.by_inst_name[name] = (base_addr, block)
+
+ triple = (base_addr, name, block)
+ self.instances.append(triple)
+ insts = self.block_name_to_instances.setdefault(block.name, [])
+ insts.append(triple)
+
+ offset = base_addr + block.regs.offset
diff --git a/util/reggen/uvm_reg.sv.tpl b/util/reggen/uvm_reg.sv.tpl
index a4d932b..58e40f2 100644
--- a/util/reggen/uvm_reg.sv.tpl
+++ b/util/reggen/uvm_reg.sv.tpl
@@ -4,30 +4,40 @@
// UVM Registers auto-generated by `reggen` containing data structure
// Do Not Edit directly
-<% from reggen import (gen_dv)
+<%
+from reggen import (gen_dv)
+from reggen.ip_block import IpBlock
+from reggen.top import Top
+
%>\
<%def name="construct_classes(block)">\
-% for b in block.blocks:
-${construct_classes(b)}
-% endfor
<%
-regs_flat = block.reg_block.flat_regs
-hier_path = ""
-if (block.hier_path):
- hier_path = block.hier_path + "."
-%>\
+ if isinstance(block, IpBlock) and block.hier_path is not None:
+ hier_path = block.hier_path + "."
+ else:
+ hier_path = ""
-// Block: ${block.name}
-package ${block.name}_ral_pkg;
+ regs_flat = block.regs.flat_regs
+%>\
+% if isinstance(block, Top):
+ % for sub_block in block.blocks:
+${construct_classes(sub_block)}
+ % endfor
+% endif
+
+// Block: ${block.name.lower()}
+package ${block.name.lower()}_ral_pkg;
// dep packages
import uvm_pkg::*;
import dv_base_reg_pkg::*;
% if dv_base_prefix != "dv_base":
import ${dv_base_prefix}_reg_pkg::*;
% endif
-% for b in block.blocks:
- import ${b.name}_ral_pkg::*;
-% endfor
+% if isinstance(block, Top):
+ % for b in block.blocks:
+ import ${b.name.lower()}_ral_pkg::*;
+ % endfor
+% endif
// macro includes
`include "uvm_macros.svh"
@@ -36,14 +46,14 @@
% for r in regs_flat:
typedef class ${gen_dv.rcname(block, r)};
% endfor
-% for w in block.reg_block.windows:
+% for w in block.regs.windows:
typedef class ${gen_dv.mcname(block, w)};
% endfor
typedef class ${gen_dv.bcname(block)};
% for r in regs_flat:
<%
- reg_width = block.width
+ reg_width = block.regwidth
reg_name = r.name.lower()
is_ext = 0
reg_shadowed = r.shadowed
@@ -152,7 +162,7 @@
endclass : ${gen_dv.rcname(block, r)}
% endfor
-% for w in block.reg_block.windows:
+% for w in block.regs.windows:
<%
mem_name = w.name.lower()
mem_right = w.swaccess.dv_rights()
@@ -180,24 +190,24 @@
% endfor
// Class: ${gen_dv.bcname(block)}
class ${gen_dv.bcname(block)} extends ${dv_base_prefix}_reg_block;
-% if block.blocks:
+% if isinstance(block, Top):
// sub blocks
-% endif
-% for b in block.blocks:
- % for inst in b.base_addr.keys():
+ % for b in block.blocks:
+ % for base_addr, inst, _ in block.block_name_to_instances[b.name]:
rand ${gen_dv.bcname(b)} ${inst};
+ % endfor
% endfor
-% endfor
+%endif
% if regs_flat:
// registers
% endif
% for r in regs_flat:
rand ${gen_dv.rcname(block, r)} ${r.name.lower()};
% endfor
-% if block.reg_block.windows:
+% if block.regs.windows:
// memories
% endif
-% for w in block.reg_block.windows:
+% for w in block.regs.windows:
rand ${gen_dv.mcname(block, w)} ${gen_dv.miname(w)};
% endfor
@@ -213,28 +223,28 @@
// create default map
this.default_map = create_map(.name("default_map"),
.base_addr(base_addr),
- .n_bytes(${block.width//8}),
+ .n_bytes(${block.regwidth//8}),
.endian(UVM_LITTLE_ENDIAN));
if (csr_excl == null) begin
csr_excl = csr_excl_item::type_id::create("csr_excl");
this.csr_excl = csr_excl;
end
-% if block.blocks:
+% if isinstance(block, Top):
// create sub blocks and add their maps
-% endif
-% for b in block.blocks:
- % for inst, base_addr in b.base_addr.items():
+ % for b in block.blocks:
+ % for base_addr, inst, _ in block.block_name_to_instances[b.name]:
${inst} = ${gen_dv.bcname(b)}::type_id::create("${inst}");
${inst}.configure(.parent(this));
- ${inst}.build(.base_addr(base_addr + ${gen_dv.sv_base_addr(b, inst)}), .csr_excl(csr_excl));
+ ${inst}.build(.base_addr(base_addr + ${gen_dv.sv_base_addr(block, inst)}), .csr_excl(csr_excl));
${inst}.set_hdl_path_root("tb.dut.top_earlgrey.u_${inst}", "BkdrRegPathRtl");
${inst}.set_hdl_path_root("tb.dut.top_earlgrey.u_${inst}", "BkdrRegPathRtlCommitted");
${inst}.set_hdl_path_root("tb.dut.top_earlgrey.u_${inst}", "BkdrRegPathRtlShadow");
default_map.add_submap(.child_map(${inst}.default_map),
- .offset(base_addr + ${gen_dv.sv_base_addr(b, inst)}));
+ .offset(base_addr + ${gen_dv.sv_base_addr(block, inst)}));
+ % endfor
% endfor
-% endfor
+% endif
% if regs_flat:
set_hdl_path_root("tb.dut", "BkdrRegPathRtl");
set_hdl_path_root("tb.dut", "BkdrRegPathRtlCommitted");
@@ -245,7 +255,7 @@
<%
reg_name = r.name.lower()
reg_right = r.dv_rights()
- reg_width = block.width
+ reg_width = block.regwidth
reg_offset = str(reg_width) + "'h" + "%x" % r.offset
reg_tags = r.tags
reg_shadowed = r.shadowed
@@ -291,15 +301,15 @@
% endif
% endfor
-% if block.reg_block.windows:
+% if block.regs.windows:
// create memories
% endif
-% for w in block.reg_block.windows:
+% for w in block.regs.windows:
<%
mem_name = w.name.lower()
mem_right = w.swaccess.dv_rights()
- mem_offset = str(block.width) + "'h" + "%x" % w.offset
+ mem_offset = str(block.regwidth) + "'h" + "%x" % w.offset
mem_n_bits = w.validbits
mem_size = w.items
%>\
diff --git a/util/reggen/validate.py b/util/reggen/validate.py
index 33f05da..e1cea7f 100644
--- a/util/reggen/validate.py
+++ b/util/reggen/validate.py
@@ -6,33 +6,6 @@
"""
import logging as log
-from typing import List
-
-from .access import SWAccess, HWAccess
-from .alert import Alert
-from .field import Field
-from .inter_signal import InterSignal
-from .lib import check_list
-from .params import LocalParam, Params
-from .reg_block import RegBlock
-from .register import Register
-from .signal import Signal
-
-
-# Routine that can be used for Hjson object_pairs_hook
-# The baseline is dict(pairs) i.e. construct a dictonary from pairs
-# The usual is OrderedDict(pairs) which is redundant in latest python
-# Both of these silently allow repeated keys, which this version detects
-def checking_dict(pairs):
- d = {}
- for x in pairs:
- if x[0] in d:
- repkey = 'Repeated' + x[0]
- log.warn("Repeated key " + x[0] + " added as " + repkey)
- d[repkey] = x[1]
- else:
- d[x[0]] = x[1]
- return d
# validating version of int(x, 0)
@@ -156,64 +129,6 @@
'pe': ["python enum", "Native Python type enum (generated)"]
}
-# Toplevel keys
-top_required = {
- 'name': ['s', "name of the component"],
- 'clock_primary': ['s', "name of the primary clock"],
- 'bus_device': ['s', "name of the bus interface for the device"],
- 'registers':
- ['l', "list of register definition groups and "
- "offset control groups"]
-}
-top_optional = {
- 'alert_list': ['ln', "list of peripheral alerts"],
- 'available_inout_list': ['lnw', "list of available peripheral inouts"],
- 'available_input_list': ['lnw', "list of available peripheral inputs"],
- 'available_output_list': ['lnw', "list of available peripheral outputs"],
- 'bus_host': ['s', "name of the bus interface as host"],
- 'hier_path': [
- None,
- 'additional hierarchy path before the reg block instance'
- ],
- 'interrupt_list': ['lnw', "list of peripheral interrupts"],
- 'inter_signal_list': ['l', "list of inter-module signals"],
- 'no_auto_alert_regs': [
- 's', "Set to true to suppress automatic "
- "generation of alert test registers. "
- "Defaults to true if no alert_list is present. "
- "Otherwise this defaults to false. "
- ],
- 'no_auto_intr_regs': [
- 's', "Set to true to suppress automatic "
- "generation of interrupt registers. "
- "Defaults to true if no interrupt_list is present. "
- "Otherwise this defaults to false. "
- ],
- 'other_clock_list': ['l', "list of other chip clocks needed"],
- 'other_reset_list': ['l', "list of other resets"],
- 'param_list': ['lp', "list of parameters of the IP"],
- 'regwidth': ['d', "width of registers in bits (default 32)"],
- 'reset_primary': ['s', "primary reset used by the module"],
- 'reset_request_list': ['l', 'list of signals requesting reset'],
- 'scan': ['pb', 'Indicates the module have `scanmode_i`'],
- 'scan_reset': ['pb', 'Indicates the module have `test_rst_ni`'],
- 'SPDX-License-Identifier': [
- 's', "License ientifier (if using pure json) "
- "Only use this if unable to put this "
- "information in a comment at the top of the "
- "file."
- ],
- 'wakeup_list': ['lnw', "list of peripheral wakeups"]
-}
-top_added = {
- 'genrnames': ['pl', "list of register names"],
- 'genautoregs': ['pb', "Registers were generated from config info"],
- 'gensize': [
- 'pi', "address space size needed for registers. "
- "Generated by tool as next power of 2."
- ]
-}
-
# ln type has list of groups with only name and description
# (was called "subunit" in cfg_validate)
ln_required = {
@@ -238,287 +153,3 @@
}
key_use = {'r': "required", 'o': "optional", 'a': "added by tool"}
-
-
-def make_intr_alert_reg(reg_block, signals, name, swaccess, hwaccess, desc):
- # these names will be converted into test registers
- testreg_names = ['INTR_TEST', 'ALERT_TEST']
-
- swaccess_obj = SWAccess('make_intr_alert_reg()', swaccess)
- hwaccess_obj = HWAccess('make_intr_alert_reg()', hwaccess)
-
- fields = []
- for signal in signals:
- width = signal.bits.width()
-
- if name == 'INTR_ENABLE':
- field_desc = ('Enable interrupt when {}!!INTR_STATE.{} is set.'
- .format('corresponding bit in ' if width > 1 else '',
- signal.name))
- elif name == 'INTR_TEST':
- field_desc = ('Write 1 to force {}!!INTR_STATE.{} to 1.'
- .format('corresponding bit in ' if width > 1 else '',
- signal.name))
- elif name == 'ALERT_TEST':
- field_desc = 'Write 1 to trigger one alert event of this kind.'
- else:
- field_desc = signal.desc
-
- fields.append(Field(signal.name,
- field_desc,
- tags=[],
- swaccess=swaccess_obj,
- hwaccess=hwaccess_obj,
- hwqe=name in testreg_names,
- hwre=False,
- bits=signal.bits,
- resval=0,
- enum=None))
-
- hwext = 'true' if name in testreg_names else 'false'
- if name == 'INTR_TEST':
- # intr_test csr is WO which - it reads back 0s
- reg_tags = ["excl:CsrNonInitTests:CsrExclWrite"]
- elif name == 'INTR_STATE':
- # intr_state csr is affected by writes to other csrs - skip write-check
- reg_tags = ["excl:CsrNonInitTests:CsrExclWriteCheck"]
- else:
- reg_tags = []
-
- bool_hwext = hwext.lower() == 'true'
-
- reg = Register(reg_block.offset,
- name,
- desc,
- swaccess_obj,
- hwaccess_obj,
- hwext=bool_hwext,
- hwqe=bool_hwext,
- hwre=False,
- regwen=None,
- tags=reg_tags,
- resval=None,
- shadowed=False,
- fields=fields,
- update_err_alert=None,
- storage_err_alert=None)
- reg_block.add_register(reg)
- return reg
-
-
-def make_intr_regs(reg_block, interrupt_list, fullwidth):
- assert interrupt_list
-
- iregs = []
- msb = interrupt_list[-1].bits.msb
- if msb >= fullwidth:
- log.error('More than {} interrupts in list'.format(fullwidth))
- return iregs, 1
-
- try:
- new_reg = make_intr_alert_reg(reg_block, interrupt_list, 'INTR_STATE', 'rw1c',
- 'hrw', 'Interrupt State Register')
- iregs.append(new_reg)
- new_reg = make_intr_alert_reg(reg_block, interrupt_list, 'INTR_ENABLE', 'rw',
- 'hro', 'Interrupt Enable Register')
- iregs.append(new_reg)
- new_reg = make_intr_alert_reg(reg_block, interrupt_list, 'INTR_TEST',
- 'wo', 'hro', 'Interrupt Test Register')
- iregs.append(new_reg)
- except ValueError as err:
- log.error(str(err))
- return iregs, 1
-
- return iregs, 0
-
-
-def make_alert_regs(reg_block, alert_list, fullwidth):
- assert alert_list
-
- alert_regs = []
- if len(alert_list) > fullwidth:
- log.error('More than {} alerts in list'.format(fullwidth))
- return alert_regs, 1
-
- try:
- new_reg = make_intr_alert_reg(reg_block, alert_list, 'ALERT_TEST',
- 'wo', 'hro', 'Alert Test Register')
- alert_regs.append(new_reg)
- except ValueError as err:
- log.error(str(err))
- return alert_regs, 1
-
- return alert_regs, 0
-
-
-def validate(regs, params: List[str]):
- if 'name' not in regs:
- log.error("Component has no name. Aborting.")
- return 1
-
- component = regs['name']
-
- error = check_keys(regs, top_required, top_optional, top_added, component)
- if (error > 0):
- log.error("Component has top level errors. Aborting.")
- return error
- regs['genrnames'] = []
- error = 0
-
- if 'regwidth' in regs:
- fullwidth, ierr = check_int(regs['regwidth'], "regwidth")
- if ierr:
- fullwidth = 32
- error += 1
- else:
- fullwidth = 32
- log.warning('regwidth not specified, assuming 32.')
- regs['regwidth'] = str(fullwidth)
-
- if ((fullwidth % 8) != 0):
- addrsep = (fullwidth // 8) + 1
- log.warning("regwidth is not a multiple of 8 bits!")
- else:
- addrsep = fullwidth // 8
-
- param_list = Params.from_raw('block parameter list',
- regs.get('param_list', []))
- regs['param_list'] = param_list
-
- reg_block = RegBlock(addrsep, fullwidth, param_list)
-
- autoregs = []
-
- # auto header generation would go here and update autoregs
-
- interrupt_list = Signal.from_raw_list('interrupt_list for block {}'
- .format(component),
- regs.get('interrupt_list', []))
- alert_list = Alert.from_raw_list('alert_list for block {}'
- .format(component),
- regs.get('alert_list', []))
-
- regs['interrupt_list'] = interrupt_list
- regs['alert_list'] = alert_list
-
- if 'no_auto_intr_regs' in regs:
- no_auto_intr, err = check_bool(regs['no_auto_intr_regs'],
- 'no_auto_intr_regs')
- if err:
- error += 1
- else:
- no_auto_intr = not interrupt_list
-
- if 'no_auto_alert_regs' in regs:
- no_auto_alerts, err = check_bool(regs['no_auto_alert_regs'],
- 'no_auto_alert_regs')
- if err:
- error += 1
- else:
- no_auto_alerts = not alert_list
-
- if interrupt_list and 'genautoregs' not in regs and not no_auto_intr:
- iregs, err = make_intr_regs(reg_block, interrupt_list, fullwidth)
- error += err
- autoregs.extend(iregs)
-
- # Generate a NumAlerts parameter for provided alert_list.
- if alert_list:
- # Generate alert test registers.
- if 'genautoregs' not in regs and not no_auto_alerts:
- aregs, err = make_alert_regs(reg_block, alert_list, fullwidth)
- error += err
- autoregs.extend(aregs)
-
- num_alerts = len(alert_list)
- for alert in alert_list:
- # check alert naming scheme
- if alert.name == "":
- log.error("{}: Alert name cannot be empty".format(alert.name))
- error += 1
- prefix = alert.name.split('_')
- if prefix[0] not in ['recov', 'fatal']:
- log.error(
- "{}: Alerts must be prefixed with either 'recov_' or "
- "'fatal_'.".format(alert.name))
- error += 1
-
- if num_alerts:
- existing_param = param_list.get('NumAlerts')
- if existing_param is not None:
- if ((not isinstance(existing_param, LocalParam) or
- existing_param.param_type != 'int' or
- existing_param.value != str(num_alerts))):
- log.error('Conflicting definition of NumAlerts parameter.')
- error += 1
- else:
- param_list.add(LocalParam(name='NumAlerts',
- desc='Number of alerts',
- param_type='int',
- value=str(num_alerts)))
-
- try:
- param_list.apply_defaults(params)
- except (ValueError, KeyError) as err:
- log.error(str(err))
- return error + 1
-
- if "scan" in regs:
- scan, err = check_bool(regs["scan"], component + " scan")
- else:
- regs["scan"] = "false"
-
- reg_block.add_raw_registers(regs['registers'])
-
- regs['gensize'] = 1 << (reg_block.offset - 1).bit_length()
-
- try:
- reg_block.validate()
- except ValueError as err:
- log.error(str(err))
- error += 1
-
- if autoregs:
- regs['genautoregs'] = True
-
- regs['registers'] = reg_block
- regs['genrnames'] = list(reg_block.name_to_offset.keys())
-
- log.debug("Validated, size = " + hex(regs['gensize']) + " errors=" +
- str(error) + " names are " + str(regs['genrnames']))
- if (error > 0):
- log.error("Register description had " + str(error) + " error" +
- "s" if error > 1 else "")
-
- try:
- r_inter_signal_list = check_list(regs.get('inter_signal_list', []),
- 'inter_signal_list field')
- inter_signal_list = [
- InterSignal.from_raw('entry {} of the inter_signal_list field'
- .format(idx + 1),
- entry)
- for idx, entry in enumerate(r_inter_signal_list)
- ]
- except ValueError as err:
- log.error(str(err))
- error += 1
-
- regs.setdefault('bus_device', '')
- regs.setdefault('bus_host', '')
-
- if regs["bus_device"] == "tlul":
- # Add to inter_module_signal
- port_name = "tl" if regs["bus_host"] in ["none", ""] else "tl_d"
-
- inter_signal_list.append(InterSignal(port_name, None, 'tl', 'tlul_pkg',
- 'req_rsp', 'rsp', 1, None))
-
- if regs['bus_host'] == "tlul":
- port_name = "tl" if regs["bus_host"] in ["none", ""] else "tl_h"
-
- inter_signal_list.append(InterSignal(port_name, None, 'tl', 'tlul_pkg',
- 'req_rsp', 'req', 1, None))
-
- regs['inter_signal_list'] = inter_signal_list
-
- return error