[topgen] Allow multiple device interfaces to connect to the crossbar
This looks like quite a big change, but lots of the changes are
auto-generated.
The main change to the data model is that an IpBlock can now contain
multiple RegBlock objects (one for each device interface). While we're
at it, we also remove the "Block" base class that nothing ever used:
we're Python, not Java: time to embrace sum types :-D
The other noticeable change is in how the xbar parsing logic works. If
you want multiple device interfaces for a block, you should create a
node for each in e.g. xbar_main.hjson. These should be named
"<inst_name>.<if_name>" for a named interface. Obviously, these nodes
also need adding to the connections list at the bottom.
There's a problem of aliasing, where the first register in each
interface will have address zero in <block>_ral_pkg.sv. For now, we're
"solving" this by adding the index of the device interface to the
address, shifted up by 28 bits. I'm not sure how best to do this at
the chip level, but it can probably be addressed in a follow-up.
The structure of most output files are unchanged. The only difference
is stuff that needs creating per device interface (such as the reg_top
modules and the FPV CSR files).
Signed-off-by: Rupert Swarbrick <rswarbrick@lowrisc.org>
diff --git a/util/topgen/c.py b/util/topgen/c.py
index 6c272b3..cca58c8 100644
--- a/util/topgen/c.py
+++ b/util/topgen/c.py
@@ -5,10 +5,14 @@
`top_{name}.h`.
"""
from collections import OrderedDict
-from math import ceil
+from typing import Dict, List, Optional, Tuple
from mako.template import Template
+from .lib import get_base_and_size
+
+from reggen.ip_block import IpBlock
+
class Name(object):
"""We often need to format names in specific ways; this class does so."""
@@ -52,11 +56,12 @@
class MemoryRegion(object):
- def __init__(self, name, base_addr, size_bytes):
+ def __init__(self, name: Name, base_addr: int, size_bytes: int):
+ assert isinstance(base_addr, int)
self.name = name
self.base_addr = base_addr
self.size_bytes = size_bytes
- self.size_words = ceil(int(str(size_bytes), 0) / 4)
+ self.size_words = (size_bytes + 3) // 4
def base_addr_name(self):
return self.name + Name(["base", "addr"])
@@ -137,10 +142,11 @@
return Template(template).render(mapping=self)
-class TopGenC(object):
- def __init__(self, top_info):
+class TopGenC:
+ def __init__(self, top_info, name_to_block: Dict[str, IpBlock]):
self.top = top_info
self._top_name = Name(["top"]) + Name.from_snake_case(top_info["name"])
+ self._name_to_block = name_to_block
# The .c file needs the .h file's relative path, store it here
self.header_path = None
@@ -154,16 +160,38 @@
self._init_pwrmgr_reset_requests()
self._init_clkmgr_clocks()
- def modules(self):
- return [(m["name"],
- MemoryRegion(self._top_name + Name.from_snake_case(m["name"]),
- m["base_addr"], m["size"]))
- for m in self.top["module"]]
+ def devices(self) -> List[Tuple[Tuple[str, Optional[str]], MemoryRegion]]:
+ '''Return a list of MemoryRegion objects for devices on the bus
+
+ The list returned is pairs (full_if, region) where full_if is itself a
+ pair (inst_name, if_name). inst_name is the name of some IP block
+ instantiation. if_name is the name of the interface (may be None).
+ region is a MemoryRegion object representing the device.
+
+ '''
+ ret = [] # type: List[Tuple[Tuple[str, Optional[str]], MemoryRegion]]
+ for inst in self.top['module']:
+ block = self._name_to_block[inst['type']]
+ for if_name, rb in block.reg_blocks.items():
+ full_if = (inst['name'], if_name)
+ full_if_name = Name.from_snake_case(full_if[0])
+ if if_name is not None:
+ full_if_name += Name.from_snake_case(if_name)
+
+ name = self._top_name + full_if_name
+ base, size = get_base_and_size(self._name_to_block,
+ inst, if_name)
+
+ region = MemoryRegion(name, base, size)
+ ret.append((full_if, region))
+
+ return ret
def memories(self):
return [(m["name"],
MemoryRegion(self._top_name + Name.from_snake_case(m["name"]),
- m["base_addr"], m["size"]))
+ int(m["base_addr"], 0),
+ int(m["size"], 0)))
for m in self.top["memory"]]
def _init_plic_targets(self):