+
+
+class Multiplexer(Elaboratable):
+ """CSR bus multiplexer.
+
+ An address-based multiplexer for subordinate CSR buses.
+
+ Usage
+ -----
+
+ Although there is no functional difference between adding a set of registers directly to
+ a :class:`Decoder` and adding a set of reigsters to multiple :class:`Decoder`s that are
+ aggregated with a :class:`Multiplexer`, hierarchical CSR buses are useful for organizing
+ a hierarchical design. If many peripherals are directly served by a single :class:`Decoder`,
+ a very large amount of ports will connect the peripheral registers with the decoder, and
+ the cost of decoding logic would not be attributed to specific peripherals. With a multiplexer,
+ only five signals per peripheral will be used, and the logic could be kept together with
+ the peripheral.
+
+ Parameters
+ ----------
+ addr_width : int
+ Address width. See :class:`Interface`.
+ data_width : int
+ Data width. See :class:`Interface`.
+ alignment : int
+ Window alignment. See :class:`Interface`.
+
+ Attributes
+ ----------
+ bus : :class:`Interface`
+ CSR bus providing access to subordinate buses.
+ """
+ def __init__(self, *, addr_width, data_width, alignment=0):
+ self.bus = Interface(addr_width=addr_width, data_width=data_width, alignment=alignment)
+ self._map = self.bus.memory_map
+ self._subs = dict()
+
+ def align_to(self, alignment):
+ """Align the implicit address of the next window.
+
+ See :meth:`MemoryMap.align_to` for details.
+ """
+ return self._map.align_to(alignment)
+
+ def add(self, sub_bus, *, addr=None):
+ """Add a window to a subordinate bus.
+
+ See :meth:`MemoryMap.add_resource` for details.
+ """
+ if not isinstance(sub_bus, Interface):
+ raise TypeError("Subordinate bus must be an instance of csr.Interface, not {!r}"
+ .format(sub_bus))
+ if sub_bus.data_width != self.bus.data_width:
+ raise ValueError("Subordinate bus has data width {}, which is not the same as "
+ "multiplexer data width {}"
+ .format(sub_bus.data_width, self.bus.data_width))
+
+ start, end, ratio = window_range = self._map.add_window(sub_bus.memory_map, addr=addr)
+ assert ratio == 1
+ pattern = "{:0{}b}{}".format(start >> sub_bus.addr_width,
+ self.bus.addr_width - sub_bus.addr_width,
+ "-" * sub_bus.addr_width)
+ self._subs[pattern] = sub_bus
+ return window_range
+
+ def elaborate(self, platform):
+ m = Module()
+
+ # See Decoder.elaborate above.
+ r_data_fanin = 0
+
+ with m.Switch(self.bus.addr):
+ for sub_pat, sub_bus in self._subs.items():
+ m.d.comb += sub_bus.addr.eq(self.bus.addr[:sub_bus.addr_width])
+
+ # The CSR bus interface is defined to output zero when idle, allowing us to avoid
+ # adding a multiplexer here.
+ r_data_fanin |= sub_bus.r_data
+ m.d.comb += sub_bus.w_data.eq(self.bus.w_data)
+
+ with m.Case(sub_pat):
+ m.d.comb += sub_bus.r_stb.eq(self.bus.r_stb)
+ m.d.comb += sub_bus.w_stb.eq(self.bus.w_stb)
+
+ m.d.comb += self.bus.r_data.eq(r_data_fanin)
+
+ return m