[topgen] Updates for declaring memory regions within IPs
Signed-off-by: Michael Schaffner <msf@opentitan.org>
diff --git a/hw/top_earlgrey/data/autogen/top_earlgrey.gen.hjson b/hw/top_earlgrey/data/autogen/top_earlgrey.gen.hjson
index 324fcfa..0780f93 100644
--- a/hw/top_earlgrey/data/autogen/top_earlgrey.gen.hjson
+++ b/hw/top_earlgrey/data/autogen/top_earlgrey.gen.hjson
@@ -1317,6 +1317,7 @@
}
domain: "0"
param_decl: {}
+ memory: {}
param_list:
[
{
@@ -1604,6 +1605,7 @@
}
domain: "0"
param_decl: {}
+ memory: {}
param_list:
[
{
@@ -2040,12 +2042,6 @@
rst_edn_ni: rstmgr_aon_resets.rst_sys_n[rstmgr_pkg::Domain0Sel]
}
attr: templated
- localparam:
- {
- EscCntDw: 32
- AccuCntDw: 16
- LfsrSeed: 0x7FFFFFFF
- }
clock_connections:
{
clk_i: clkmgr_aon_clocks.clk_io_div4_timers
@@ -2053,6 +2049,7 @@
}
domain: "0"
param_decl: {}
+ memory: {}
param_list:
[
{
@@ -2956,6 +2953,7 @@
clk_aon_i: clkmgr_aon_clocks.clk_aon_powerup
}
param_decl: {}
+ memory: {}
param_list:
[
{
@@ -3438,6 +3436,7 @@
clk_otp_i: clkmgr_aon_clocks.clk_io_div4_infra
}
param_decl: {}
+ memory: {}
param_list:
[
{
@@ -3627,6 +3626,7 @@
}
domain: "0"
param_decl: {}
+ memory: {}
param_list:
[
{
@@ -3889,6 +3889,7 @@
}
domain: "0"
param_decl: {}
+ memory: {}
param_list:
[
{
@@ -4073,6 +4074,7 @@
}
domain: "0"
param_decl: {}
+ memory: {}
param_list:
[
{
@@ -4325,6 +4327,7 @@
clk_edn_i: clkmgr_aon_clocks.clk_main_kmac
}
domain: "0"
+ memory: {}
param_list:
[
{
@@ -4449,6 +4452,7 @@
}
domain: "0"
param_decl: {}
+ memory: {}
param_list:
[
{
@@ -4771,6 +4775,7 @@
}
domain: "0"
param_decl: {}
+ memory: {}
param_list:
[
{
@@ -4909,6 +4914,7 @@
}
domain: "0"
param_decl: {}
+ memory: {}
param_list:
[
{
@@ -5200,6 +5206,7 @@
}
domain: "0"
param_decl: {}
+ memory: {}
param_list:
[
{
@@ -5386,6 +5393,7 @@
}
domain: "0"
param_decl: {}
+ memory: {}
param_list:
[
{
@@ -5555,6 +5563,15 @@
rom: 0x00008000
regs: 0x411e0000
}
+ memory:
+ {
+ rom:
+ {
+ label: rom
+ swaccess: rx
+ size: 0x4000
+ }
+ }
clock_connections:
{
clk_i: clkmgr_aon_clocks.clk_main_infra
diff --git a/hw/top_earlgrey/data/top_earlgrey.hjson b/hw/top_earlgrey/data/top_earlgrey.hjson
index 439778b..63c5668 100644
--- a/hw/top_earlgrey/data/top_earlgrey.hjson
+++ b/hw/top_earlgrey/data/top_earlgrey.hjson
@@ -413,11 +413,6 @@
reset_connections: {rst_ni: "sys_io_div4", rst_edn_ni: "sys"},
base_addr: "0x40150000",
attr: "templated",
- localparam: {
- EscCntDw: 32,
- AccuCntDw: 16,
- LfsrSeed: "0x7FFFFFFF"
- }
},
// dummy module to capture the alert handler escalation signals
// and test them by converting them into IRQs
@@ -631,6 +626,13 @@
clock_group: "infra",
reset_connections: {rst_ni: "sys"},
base_addrs: {rom: "0x00008000", regs: "0x411e0000"}
+ memory: {
+ rom: {
+ label: "rom",
+ swaccess: "rx",
+ size: "0x4000"
+ }
+ }
},
{ name: "rv_core_ibex_peri",
type: "rv_core_ibex_peri",
diff --git a/hw/top_earlgrey/rtl/autogen/top_earlgrey_pkg.sv b/hw/top_earlgrey/rtl/autogen/top_earlgrey_pkg.sv
index f6cf6cb..34769ff 100644
--- a/hw/top_earlgrey/rtl/autogen/top_earlgrey_pkg.sv
+++ b/hw/top_earlgrey/rtl/autogen/top_earlgrey_pkg.sv
@@ -500,6 +500,16 @@
*/
parameter int unsigned TOP_EARLGREY_EFLASH_SIZE_BYTES = 32'h100000;
+ /**
+ * Memory base address for rom in top earlgrey.
+ */
+ parameter int unsigned TOP_EARLGREY_ROM_BASE_ADDR = 32'h8000;
+
+ /**
+ * Memory size for rom in top earlgrey.
+ */
+ parameter int unsigned TOP_EARLGREY_ROM_SIZE_BYTES = 32'h4000;
+
// Enumeration of IO power domains.
// Only used in ASIC target.
diff --git a/hw/top_earlgrey/sw/autogen/top_earlgrey.h b/hw/top_earlgrey/sw/autogen/top_earlgrey.h
index 9683d5d..f98efc3 100644
--- a/hw/top_earlgrey/sw/autogen/top_earlgrey.h
+++ b/hw/top_earlgrey/sw/autogen/top_earlgrey.h
@@ -884,6 +884,16 @@
*/
#define TOP_EARLGREY_EFLASH_SIZE_BYTES 0x100000u
+/**
+ * Memory base address for rom in top earlgrey.
+ */
+#define TOP_EARLGREY_ROM_BASE_ADDR 0x8000u
+
+/**
+ * Memory size for rom in top earlgrey.
+ */
+#define TOP_EARLGREY_ROM_SIZE_BYTES 0x4000u
+
/**
* PLIC Interrupt Source Peripheral.
diff --git a/hw/top_earlgrey/sw/autogen/top_earlgrey_memory.h b/hw/top_earlgrey/sw/autogen/top_earlgrey_memory.h
index 63db6fc..124d5e5 100644
--- a/hw/top_earlgrey/sw/autogen/top_earlgrey_memory.h
+++ b/hw/top_earlgrey/sw/autogen/top_earlgrey_memory.h
@@ -20,16 +20,18 @@
// Include guard for assembler
#ifdef __ASSEMBLER__
+
/**
- * Memory base address for rom in top earlgrey.
+ * Memory base for rom_ctrl_rom in top earlgrey.
*/
#define TOP_EARLGREY_ROM_BASE_ADDR 0x00008000
/**
- * Memory size for rom in top earlgrey.
+ * Memory size for rom_ctrl_rom in top earlgrey.
*/
#define TOP_EARLGREY_ROM_SIZE_BYTES 0x4000
+
/**
* Memory base address for ram_main in top earlgrey.
*/
diff --git a/util/reggen/gen_cheader.py b/util/reggen/gen_cheader.py
index f68bd39..ba84258 100644
--- a/util/reggen/gen_cheader.py
+++ b/util/reggen/gen_cheader.py
@@ -25,14 +25,16 @@
def genout(outfile: TextIO, msg: str) -> None:
outfile.write(msg)
+
def to_snake_case(s: str) -> str:
val = []
for i, ch in enumerate(s):
- if i > 0 and ch.isupper():
- val.append('_')
- val.append(ch)
+ if i > 0 and ch.isupper():
+ val.append('_')
+ val.append(ch)
return ''.join(val)
+
def as_define(s: str) -> str:
s = s.upper()
r = ''
diff --git a/util/reggen/params.py b/util/reggen/params.py
index 69ee38e..541625b 100644
--- a/util/reggen/params.py
+++ b/util/reggen/params.py
@@ -115,6 +115,14 @@
return rd
+class MemSizeParameter(BaseParam):
+ def __init__(self,
+ name: str,
+ desc: Optional[str],
+ param_type: str):
+ super().__init__(name, desc, param_type)
+
+
def _parse_parameter(where: str, raw: object) -> BaseParam:
rd = check_keys(raw, where,
list(REQUIRED_FIELDS.keys()),
@@ -198,6 +206,36 @@
"with RndCnst."
.format(where=where, name=name, fld=fld))
+ if name.lower().startswith('memsize'):
+ r_type = rd.get('type')
+ if r_type is None:
+ raise ValueError('At {}, parameter {} has no type field (which is '
+ 'required for memory size parameters).'
+ .format(where, name))
+ param_type = check_str(r_type, 'type field of ' + where)
+
+ if rd.get('type') != "int":
+ raise ValueError('At {}, memory size parameter {} must be of type integer.'
+ .format(where, name))
+
+ local = check_bool(rd.get('local', 'false'), 'local field of ' + where)
+ if local:
+ raise ValueError('At {}, the parameter {} specifies local = true, '
+ 'meaning that it is a localparam. This is '
+ 'incompatible with being a memory size parameter.'
+ .format(where, name))
+
+ expose = check_bool(rd.get('expose', 'false'),
+ 'expose field of ' + where)
+ if expose:
+ raise ValueError('At {}, the parameter {} specifies expose = '
+ 'true, meaning that the parameter is exposed to '
+ 'the top-level. This is incompatible with '
+ 'being a memory size parameter.'
+ .format(where, name))
+
+ return MemSizeParameter(name, desc, param_type)
+
r_type = rd.get('type')
if r_type is None:
param_type = 'int'
diff --git a/util/topgen.py b/util/topgen.py
index 2d3dde0..d473632 100755
--- a/util/topgen.py
+++ b/util/topgen.py
@@ -122,16 +122,6 @@
topname = top["name"]
- # check if there are any params to be passed through reggen and placed into
- # the generated package
- ip_list_in_top = [x["name"].lower() for x in top["module"]]
- ah_idx = ip_list_in_top.index("alert_handler")
- if 'localparam' in top['module'][ah_idx]:
- if 'EscCntDw' in top['module'][ah_idx]['localparam']:
- esc_cnt_dw = int(top['module'][ah_idx]['localparam']['EscCntDw'])
- if 'AccuCntDw' in top['module'][ah_idx]['localparam']:
- accu_cnt_dw = int(top['module'][ah_idx]['localparam']['AccuCntDw'])
-
if esc_cnt_dw < 1:
log.error("EscCntDw must be larger than 0")
if accu_cnt_dw < 1:
@@ -151,7 +141,7 @@
for k in range(alert['width']):
async_on = str(alert['async']) + async_on
# convert to hexstring to shorten line length
- async_on = ("%d'h" % n_alerts) + hex(int(async_on,2))[2:]
+ async_on = ("%d'h" % n_alerts) + hex(int(async_on, 2))[2:]
log.info("alert handler parameterization:")
log.info("NAlerts = %d" % n_alerts)
diff --git a/util/topgen/c.py b/util/topgen/c.py
index 58760a3..b10d741 100644
--- a/util/topgen/c.py
+++ b/util/topgen/c.py
@@ -147,11 +147,25 @@
return ret
def memories(self):
- return [(m["name"],
- MemoryRegion(self._top_name + Name.from_snake_case(m["name"]),
- int(m["base_addr"], 0),
- int(m["size"], 0)))
- for m in self.top["memory"]]
+ ret = []
+ for m in self.top["memory"]:
+ ret.append((m["name"],
+ MemoryRegion(self._top_name +
+ Name.from_snake_case(m["name"]),
+ int(m["base_addr"], 0),
+ int(m["size"], 0))))
+
+ for inst in self.top['module']:
+ if "memory" in inst:
+ for if_name, val in inst["memory"].items():
+ base, size = get_base_and_size(self._name_to_block,
+ inst, if_name)
+
+ name = self._top_name + Name.from_snake_case(val["label"])
+ region = MemoryRegion(name, base, size)
+ ret.append((val["label"], region))
+
+ return ret
def _init_plic_targets(self):
enum = CEnum(self._top_name + Name(["plic", "target"]))
diff --git a/util/topgen/lib.py b/util/topgen/lib.py
index a1354fd..1f1e848 100644
--- a/util/topgen/lib.py
+++ b/util/topgen/lib.py
@@ -401,16 +401,31 @@
# that corresponds to ifname
rb = block.reg_blocks.get(ifname)
if rb is None:
- log.error('Cannot connect to non-existent {} device interface '
- 'on {!r} (an instance of the {!r} block)'
- .format('default' if ifname is None else repr(ifname),
- inst['name'], block.name))
- bytes_used = 0
+ raise RuntimeError(
+ 'Cannot connect to non-existent {} device interface '
+ 'on {!r} (an instance of the {!r} block).'
+ .format('default' if ifname is None else repr(ifname),
+ inst['name'], block.name))
else:
bytes_used = 1 << rb.get_addr_width()
base_addr = inst['base_addrs'][ifname]
+ # If an instance has a nonempty "memory" field, take the memory
+ # size configuration from there.
+ if "memory" in inst:
+ if ifname in inst["memory"]:
+ memory_size = int(inst["memory"][ifname]["size"], 0)
+ if bytes_used > memory_size:
+ raise RuntimeError(
+ 'Memory region on {} device interface '
+ 'on {!r} (an instance of the {!r} block) '
+ 'is smaller than the corresponding register block.'
+ .format('default' if ifname is None else repr(ifname),
+ inst['name'], block.name))
+
+ bytes_used = memory_size
+
# Round up to min_device_spacing if necessary
size_byte = max(bytes_used, min_device_spacing)
diff --git a/util/topgen/merge.py b/util/topgen/merge.py
index d29f9b1..41ce4bd 100644
--- a/util/topgen/merge.py
+++ b/util/topgen/merge.py
@@ -11,7 +11,7 @@
from topgen import c, lib
from reggen.ip_block import IpBlock
-from reggen.params import LocalParam, Parameter, RandParameter
+from reggen.params import LocalParam, Parameter, RandParameter, MemSizeParameter
def _get_random_data_hex_literal(width):
@@ -74,6 +74,7 @@
instance["param_decl"] = {}
mod_name = instance["name"]
+ cc_mod_name = c.Name.from_snake_case(mod_name).as_camel_case()
# Check to see if all declared parameters exist
param_decl_accounting = [decl for decl in instance["param_decl"].keys()]
@@ -89,6 +90,10 @@
param_expose = param.expose if isinstance(param, Parameter) else False
+ # assign an empty entry if this is not present
+ if "memory" not in instance:
+ instance["memory"] = {}
+
# Check for security-relevant parameters that are not exposed,
# adding a top-level name.
if param.name.lower().startswith("sec") and not param_expose:
@@ -97,8 +102,7 @@
mod_name, param.name))
# Move special prefixes to the beginnining of the parameter name.
- param_prefixes = ["Sec", "RndCnst"]
- cc_mod_name = c.Name.from_snake_case(mod_name).as_camel_case()
+ param_prefixes = ["Sec", "RndCnst", "MemSize"]
name_top = cc_mod_name + param.name
for prefix in param_prefixes:
if param.name.lower().startswith(prefix.lower()):
@@ -122,6 +126,18 @@
new_param['default'] = new_default
new_param['randwidth'] = randwidth
+
+ elif isinstance(param, MemSizeParameter):
+ key = param.name[7:].lower()
+ # Set the parameter to the specified memory size.
+ if key in instance["memory"]:
+ new_default = int(instance["memory"][key]["size"], 0)
+ new_param['default'] = new_default
+ else:
+ log.error("Missing memory configuration for "
+ "memory {} in instance {}"
+ .format(key, instance["name"]))
+
# if this exposed parameter is listed in the `param_decl` dict,
# override its default value.
elif param.name in instance["param_decl"].keys():
diff --git a/util/topgen/templates/toplevel_memory.h.tpl b/util/topgen/templates/toplevel_memory.h.tpl
index bfb0274..021beb5 100644
--- a/util/topgen/templates/toplevel_memory.h.tpl
+++ b/util/topgen/templates/toplevel_memory.h.tpl
@@ -20,15 +20,23 @@
// Include guard for assembler
#ifdef __ASSEMBLER__
+
+% for m in top["module"]:
+ % if "memory" in m:
+ % for key, val in m["memory"].items():
/**
- * Memory base address for rom in top earlgrey.
+ * Memory base for ${m["name"]}_${val["label"]} in top ${top["name"]}.
*/
-#define TOP_EARLGREY_ROM_BASE_ADDR 0x00008000
+#define TOP_${top["name"].upper()}_${val["label"].upper()}_BASE_ADDR ${m["base_addrs"][key]}
/**
- * Memory size for rom in top earlgrey.
+ * Memory size for ${m["name"]}_${val["label"]} in top ${top["name"]}.
*/
-#define TOP_EARLGREY_ROM_SIZE_BYTES 0x4000
+#define TOP_${top["name"].upper()}_${val["label"].upper()}_SIZE_BYTES ${val["size"]}
+
+ % endfor
+ % endif
+% endfor
% for m in top["memory"]:
/**
diff --git a/util/topgen/templates/toplevel_memory.ld.tpl b/util/topgen/templates/toplevel_memory.ld.tpl
index 3bb6271..0a21022 100644
--- a/util/topgen/templates/toplevel_memory.ld.tpl
+++ b/util/topgen/templates/toplevel_memory.ld.tpl
@@ -25,7 +25,13 @@
* translation base
*/
MEMORY {
- rom(rx) : ORIGIN = 0x00008000, LENGTH = 0x4000
+% for m in top["module"]:
+ % if "memory" in m:
+ % for key, val in m["memory"].items():
+ ${val["label"]}(${val["swaccess"]}) : ORIGIN = ${m["base_addrs"][key]}, LENGTH = ${val["size"]}
+ % endfor
+ % endif
+% endfor
% for m in top["memory"]:
${m["name"]}(${memory_to_flags(m)}) : ORIGIN = ${m["base_addr"]}, LENGTH = ${m["size"]}
% endfor
diff --git a/util/topgen/validate.py b/util/topgen/validate.py
index dfbaa9c..eb4e62c 100644
--- a/util/topgen/validate.py
+++ b/util/topgen/validate.py
@@ -199,7 +199,7 @@
eflash_required = {
'banks': ['d', 'number of flash banks'],
- 'base_addr': ['s', 'strarting hex address of memory'],
+ 'base_addr': ['s', 'hex start address of memory'],
'clock_connections': ['g', 'generated, elaborated version of clock_srcs'],
'clock_group': ['s', 'associated clock attribute group'],
'clock_srcs': ['g', 'clock connections'],
@@ -216,6 +216,45 @@
eflash_added = {}
+module_required = {
+ 'name': ['s', 'name of the instance'],
+ 'type': ['s', 'comportable IP type'],
+ 'clock_srcs': ['g', 'dict with clock sources'],
+ 'clock_group': ['s', 'clock group'],
+ 'reset_connections': ['g', 'dict with reset sources'],
+}
+
+module_optional = {
+ 'domain': ['s', 'power domain, defaults to Domain0'],
+ 'clock_reset_export': ['l', 'optional list with prefixes for exported '
+ 'clocks and resets at the chip level'],
+ 'attr': ['s', 'optional attribute indicating whether the IP is '
+ '"templated" or "reggen_only"'],
+ 'base_addr': ['s', 'hex start address of the peripheral '
+ '(if the IP has only a single TL-UL interface)'],
+ 'base_addrs': ['d', 'hex start addresses of the peripheral '
+ ' (if the IP has multiple TL-UL interfaces)'],
+ 'memory': ['g', 'optional dict with memory region attributes'],
+ 'param_decl': ['g', 'optional dict that allows to override instantiation parameters']
+}
+
+module_added = {
+ 'clock_connections': ['g', 'generated clock connections']
+}
+
+memory_required = {
+ 'label': ['s', 'region label for the linker script'],
+ 'swaccess': ['s', 'access attributes for the linker script'],
+ 'size': ['d', 'memory region size in bytes for the linker script, '
+ 'xbar and RTL parameterisations'],
+}
+
+memory_optional = {
+}
+
+memory_added = {
+}
+
# Supported PAD types.
# Needs to coincide with enum definition in prim_pad_wrapper_pkg.sv
@@ -835,6 +874,40 @@
return error
+def check_modules(top, prefix):
+ error = 0
+ for m in top['module']:
+ modname = m.get("name", "unnamed module")
+ error += check_keys(m, module_required, module_optional, module_added,
+ prefix + " " + modname)
+
+ # these fields are mutually exclusive
+ if 'base_addr' in m and 'base_addrs' in m:
+ log.error("{} {} a module cannot define both the 'base_addr' "
+ "and 'base_addrs' keys at the same time"
+ .format(prefix, modname))
+ error += 1
+
+ if 'base_addrs' in m and 'memory' in m:
+ for intf, value in m['memory'].items():
+ error += check_keys(value, memory_required,
+ memory_optional, memory_added,
+ prefix + " " + modname + " " + intf)
+ # make sure the memory regions correspond to the TL-UL interfaces
+ if intf not in m['base_addrs']:
+ log.error("{} {} memory region {} does not "
+ "correspond to any of the defined "
+ "TL-UL interfaces".format(prefix, modname, intf))
+ error += 1
+ # make sure the linker region access attribute is valid
+ attr = value.get('swaccess', 'unknown attribute')
+ if attr not in ['r', 'rw', 'rx', 'rwx']:
+ log.error('{} {} swaccess attribute {} of memory region {} '
+ 'is not valid'.format(prefix, modname, attr, intf))
+ error += 1
+ return error
+
+
def validate_top(top, ipobjs, xbarobjs):
# return as it is for now
error = check_keys(top, top_required, top_optional, top_added, "top")
@@ -845,7 +918,10 @@
component = top['name']
- # MODULE check
+ # Check module instantiations
+ error += check_modules(top, component)
+
+ # MODULE check
err, ip_idxs = check_target(top, ipobjs, Target(TargetType.MODULE))
error += err