Skip to content

Commit

Permalink
csr.bus: instantiate a Multiplexer from a MemoryMap.
Browse files Browse the repository at this point in the history
Before this commit, CSR elements were added with `Multiplexer.add()`.

Now, a memory map of elements is populated beforehand, then given as
argument to `Multiplexer.__init__()`.

This decouples the definition of a group of neighboring registers from
their implementation by a CSR Multiplexer.
  • Loading branch information
jfng committed Sep 1, 2023
1 parent 26cff66 commit 9ca5fe5
Show file tree
Hide file tree
Showing 4 changed files with 160 additions and 144 deletions.
96 changes: 44 additions & 52 deletions amaranth_soc/csr/bus.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,18 @@ def __init__(self, width, access):
self.w_data = Signal(unsigned(width))
self.w_stb = Signal()

def add_to(self, memory_map, *, name, addr=None, alignment=None, extend=False):
"""Add register to memory map.
See :meth:`MemoryMap.add_resource` for details.
"""
if not isinstance(memory_map, MemoryMap):
raise TypeError("Memory map must be an instance of MemoryMap, not {!r}"
.format(memory_map))
size = (self.width + memory_map.data_width - 1) // memory_map.data_width
return memory_map.add_resource(self, size=size, addr=addr, alignment=alignment,
extend=extend, name=name)

def __repr__(self):
return "Element({}, {})".format(self.width, self.access)

Expand Down Expand Up @@ -356,31 +368,25 @@ def chunks(self):
are possible for connecting the CSR bus to the CPU:
* The CPU could access the CSR bus directly (with no intervening logic other than simple
translation of control signals). In this case, the register alignment should be set
to 1 (i.e. `alignment` should be set to 0), and each *w*-bit register would occupy
*ceil(w/n)* addresses from the CPU perspective, requiring the same amount of memory
instructions to access.
to 1 (i.e. `memory_map.alignment` should be set to 0), and each *w*-bit register would
occupy *ceil(w/n)* addresses from the CPU perspective, requiring the same amount of
memory instructions to access.
* The CPU could also access the CSR bus through a width down-converter, which would issue
*k/n* CSR accesses for each CPU access. In this case, the register alignment should be
set to *k/n*, and each *w*-bit register would occupy *ceil(w/k)* addresses from the CPU
perspective, requiring the same amount of memory instructions to access.
If the register alignment (i.e. `2 ** alignment`) is greater than 1, it affects which CSR bus
write is considered a write to the last register chunk. For example, if a 24-bit register is
used with a 8-bit CSR bus and a CPU with a 32-bit datapath, a write to this register requires
4 CSR bus writes to complete and the 4th write is the one that actually writes the value to
the register. This allows determining write latency solely from the amount of addresses the
register occupies in the CPU address space, and the width of the CSR bus.
If the register alignment (i.e. `2 ** memory_map.alignment`) is greater than 1, it affects
which CSR bus write is considered a write to the last register chunk. For example, if a 24-bit
register is used with a 8-bit CSR bus and a CPU with a 32-bit datapath, a write to this
register requires 4 CSR bus writes to complete and the 4th write is the one that actually
writes the value to the register. This allows determining write latency solely from the amount
of addresses the register occupies in the CPU address space, and the width of the CSR bus.
Parameters
----------
addr_width : int
Address width. See :class:`Interface`.
data_width : int
Data width. See :class:`Interface`.
alignment : log2 of int
Register alignment. See :class:`..memory.MemoryMap`.
name : str
Window name. Optional.
memory_map : :class:`..memory.MemoryMap`
Memory map of the CSR bus.
shadow_overlaps : int
Maximum number of CSR registers that can share a chunk of a shadow register.
Optional. If ``None``, any number of CSR registers can share a shadow chunk.
Expand All @@ -391,47 +397,33 @@ def chunks(self):
bus : :class:`Interface`
CSR bus providing access to registers.
"""
def __init__(self, *, addr_width, data_width, alignment=0, name=None, shadow_overlaps=None):
self._map = MemoryMap(addr_width=addr_width, data_width=data_width, alignment=alignment,
name=name)
self._bus = None
self._r_shadow = Multiplexer._Shadow(data_width, shadow_overlaps, name="r_shadow")
self._w_shadow = Multiplexer._Shadow(data_width, shadow_overlaps, name="w_shadow")
def __init__(self, memory_map, *, shadow_overlaps=None):
if not isinstance(memory_map, MemoryMap):
raise TypeError("Memory map must be an instance of MemoryMap, not {!r}"
.format(memory_map))
for elem, elem_name, (elem_start, elem_end) in memory_map.resources():
if not isinstance(elem, Element):
raise TypeError("Memory map resource must be an instance of csr.Element, not {!r}"
.format(elem))
if list(memory_map.windows()):
raise ValueError("Memory map cannot have windows")

memory_map.freeze()
self._bus = Interface(addr_width=memory_map.addr_width, data_width=memory_map.data_width,
name="csr")
self._bus.memory_map = memory_map

self._r_shadow = Multiplexer._Shadow(memory_map.data_width, shadow_overlaps, name="r_shadow")
self._w_shadow = Multiplexer._Shadow(memory_map.data_width, shadow_overlaps, name="w_shadow")

@property
def bus(self):
if self._bus is None:
self._map.freeze()
self._bus = Interface(addr_width=self._map.addr_width,
data_width=self._map.data_width,
name="csr")
self._bus.memory_map = self._map
return self._bus

def align_to(self, alignment):
"""Align the implicit address of the next register.
See :meth:`MemoryMap.align_to` for details.
"""
return self._map.align_to(alignment)

def add(self, element, *, name, addr=None, alignment=None, extend=False):
"""Add a register.
See :meth:`MemoryMap.add_resource` for details.
"""
if not isinstance(element, Element):
raise TypeError("Element must be an instance of csr.Element, not {!r}"
.format(element))

size = (element.width + self._map.data_width - 1) // self._map.data_width
return self._map.add_resource(element, size=size, addr=addr, alignment=alignment,
extend=extend, name=name)

def elaborate(self, platform):
m = Module()

for elem, _, (elem_start, elem_end) in self._map.resources():
for elem, _, (elem_start, elem_end) in self.bus.memory_map.resources():
elem_range = range(elem_start, elem_end)
if elem.access.readable():
self._r_shadow.add(elem_range)
Expand All @@ -458,7 +450,7 @@ def elaborate(self, platform):
with m.Switch(self.bus.addr):
for elem_range in r_chunk.elements():
chunk_addr = self._r_shadow.encode_offset(chunk_offset, elem_range)
elem = self._map.decode_address(elem_range.start)
elem = self.bus.memory_map.decode_address(elem_range.start)
elem_offset = chunk_addr - elem_range.start
elem_slice = elem.r_data.word_select(elem_offset, self.bus.data_width)

Expand All @@ -483,7 +475,7 @@ def elaborate(self, platform):
with m.Switch(self.bus.addr):
for elem_range in w_chunk.elements():
chunk_addr = self._w_shadow.encode_offset(chunk_offset, elem_range)
elem = self._map.decode_address(elem_range.start)
elem = self.bus.memory_map.decode_address(elem_range.start)
elem_offset = chunk_addr - elem_range.start
elem_slice = elem.w_data.word_select(elem_offset, self.bus.data_width)

Expand Down
9 changes: 6 additions & 3 deletions amaranth_soc/csr/event.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from . import Element, Multiplexer
from .. import event
from ..memory import MemoryMap


__all__ = ["EventMonitor"]
Expand Down Expand Up @@ -41,7 +42,8 @@ def __init__(self, *, data_width, alignment=0, trigger="level"):
self._monitor = None
self._enable = None
self._pending = None
self._mux = Multiplexer(addr_width=1, data_width=data_width, alignment=alignment)
self._mux_map = MemoryMap(addr_width=1, data_width=data_width, alignment=alignment)
self._mux = None
self._frozen = False

def freeze(self):
Expand All @@ -54,8 +56,9 @@ def freeze(self):
self._monitor = event.Monitor(self._map, trigger=self._trigger)
self._enable = Element(self._map.size, "rw")
self._pending = Element(self._map.size, "rw")
self._mux.add(self._enable, extend=True)
self._mux.add(self._pending, extend=True)
self._enable .add_to(self._mux_map, name="enable", extend=True)
self._pending.add_to(self._mux_map, name="pending", extend=True)
self._mux = Multiplexer(self._mux_map)
self._frozen = True

@property
Expand Down
Loading

0 comments on commit 9ca5fe5

Please sign in to comment.