autopep8 cleanup
[nmigen-soc.git] / nmigen_soc / csr / bus.py
index f65f7e8a82705a6b08dbbbce23d7e8184243cd22..7bf5694c2de045d7f8ec9fb4299368cb703d82c9 100644 (file)
@@ -1,19 +1,23 @@
 import enum
 from nmigen import *
+from nmigen.utils import log2_int
 
+from ..memory import MemoryMap
 
-__all__ = ["Element", "Interface", "Decoder"]
+
+__all__ = ["Element", "Interface", "Decoder", "Multiplexer"]
 
 
 class Element(Record):
     class Access(enum.Enum):
         """Register access mode.
 
-        Coarse access mode for the entire register. Individual fields can have more restrictive
-        access mode, e.g. R/O fields can be a part of an R/W register.
+        Coarse access mode for the entire register. Individual fields
+        can have more restrictive access mode, e.g. R/O fields can be
+        a part of an R/W register.
         """
-        R  = "r"
-        W  = "w"
+        R = "r"
+        W = "w"
         RW = "rw"
 
         def readable(self):
@@ -24,9 +28,10 @@ class Element(Record):
 
     """Peripheral-side CSR interface.
 
-    A low-level interface to a single atomically readable and writable register in a peripheral.
-    This interface supports any register width and semantics, provided that both reads and writes
-    always succeed and complete in one cycle.
+    A low-level interface to a single atomically readable and writable
+    register in a peripheral.  This interface supports any register
+    width and semantics, provided that both reads and writes always
+    succeed and complete in one cycle.
 
     Parameters
     ----------
@@ -40,88 +45,107 @@ class Element(Record):
     Attributes
     ----------
     r_data : Signal(width)
-        Read data. Must be always valid, and is sampled when ``r_stb`` is asserted.
+        Read data. Must be always valid, and is sampled when ``r_stb``
+        is asserted.
     r_stb : Signal()
-        Read strobe. Registers with read side effects should perform the read side effect when this
-        strobe is asserted.
+        Read strobe. Registers with read side effects should perform
+        the read side effect when this strobe is asserted.
     w_data : Signal(width)
         Write data. Valid only when ``w_stb`` is asserted.
     w_stb : Signal()
-        Write strobe. Registers should update their value or perform the write side effect when
-        this strobe is asserted.
+        Write strobe. Registers should update their value or perform
+        the write side effect when this strobe is asserted.
     """
+
     def __init__(self, width, access, *, name=None, src_loc_at=0):
         if not isinstance(width, int) or width < 0:
             raise ValueError("Width must be a non-negative integer, not {!r}"
                              .format(width))
-        if not isinstance(access, Element.Access) and access not in ("r", "w", "rw"):
-            raise ValueError("Access mode must be one of \"r\", \"w\", or \"rw\", not {!r}"
+        if not isinstance(access, Element.Access) and access not in (
+                "r", "w", "rw"):
+            raise ValueError("Access mode must be one of \"r\", "
+                             "\"w\", or \"rw\", not {!r}"
                              .format(access))
-        self.width  = width
+        self.width = width
         self.access = Element.Access(access)
 
         layout = []
         if self.access.readable():
             layout += [
                 ("r_data", width),
-                ("r_stb",  1),
+                ("r_stb", 1),
             ]
         if self.access.writable():
             layout += [
                 ("w_data", width),
-                ("w_stb",  1),
+                ("w_stb", 1),
             ]
         super().__init__(layout, name=name, src_loc_at=1)
 
+    # FIXME: get rid of this
+    __hash__ = object.__hash__
+
 
 class Interface(Record):
     """CPU-side CSR interface.
 
-    A low-level interface to a set of atomically readable and writable peripheral CSR registers.
+    A low-level interface to a set of atomically readable and writable
+    peripheral CSR registers.
 
     Operation
     ---------
 
-    CSR registers mapped to the CSR bus are split into chunks according to the bus data width.
-    Each chunk is assigned a consecutive address on the bus. This allows accessing CSRs of any
-    size using any datapath width.
+    CSR registers mapped to the CSR bus are split into chunks according to
+    the bus data width.  Each chunk is assigned a consecutive address on
+    the bus. This allows accessing CSRs of any size using any datapath
+    width.
 
-    When the first chunk of a register is read, the value of a register is captured, and reads
-    from subsequent chunks of the same register return the captured values. When any chunk except
-    the last chunk of a register is written, the written value is captured; a write to the last
-    chunk writes the captured value to the register. This allows atomically accessing CSRs larger
-    than datapath width.
+    When the first chunk of a register is read, the value of a register
+    is captured, and reads from subsequent chunks of the same register
+    return the captured values. When any chunk except the last chunk
+    of a register is written, the written value is captured; a write
+    to the last chunk writes the captured value to the register. This
+    allows atomically accessing CSRs larger than datapath width.
 
     Parameters
     ----------
     addr_width : int
-        Address width. At most ``(2 ** addr_width) * data_width`` register bits will be available.
+        Address width. At most ``(2 ** addr_width) * data_width``
+        register bits will be available.
     data_width : int
         Data width. Registers are accessed in ``data_width`` sized chunks.
+    alignment : int
+        Register and window alignment. See :class:`MemoryMap`.
     name : str
         Name of the underlying record.
 
     Attributes
     ----------
+    memory_map : MemoryMap
+        Map of the bus.
     addr : Signal(addr_width)
         Address for reads and writes.
     r_data : Signal(data_width)
-        Read data. Valid on the next cycle after ``r_stb`` is asserted.
+        Read data. Valid on the next cycle after ``r_stb`` is
+        asserted. Otherwise, zero. (Keeping read data of an unused
+        interface at zero simplifies multiplexers.)
     r_stb : Signal()
-        Read strobe. If ``addr`` points to the first chunk of a register, captures register value
-        and causes read side effects to be performed (if any). If ``addr`` points to any chunk
-        of a register, latches the captured value to ``r_data``. Otherwise, latches zero
-        to ``r_data``.
+        Read strobe. If ``addr`` points to the first chunk of a
+        register, captures register value and causes read side effects
+        to be performed (if any). If ``addr`` points to any chunk of a
+        register, latches the captured value to ``r_data``. Otherwise,
+        latches zero to ``r_data``.
     w_data : Signal(data_width)
         Write data. Must be valid when ``w_stb`` is asserted.
     w_stb : Signal()
-        Write strobe. If ``addr`` points to the last chunk of a register, writes captured value
-        to the register and causes write side effects to be performed (if any). If ``addr`` points
-        to any chunk of a register, latches ``w_data`` to the captured value. Otherwise, does
-        nothing.
+        Write strobe. If ``addr`` points to the last chunk of a register,
+        writes captured value to the register and causes write side
+        effects to be performed (if any). If ``addr`` points to
+        any chunk of a register, latches ``w_data`` to the captured
+        value. Otherwise, does nothing.
     """
 
-    def __init__(self, *, addr_width, data_width, name=None):
+    def __init__(self, *, addr_width, data_width, alignment=0, name=None):
         if not isinstance(addr_width, int) or addr_width <= 0:
             raise ValueError("Address width must be a positive integer, not {!r}"
                              .format(addr_width))
@@ -130,48 +154,59 @@ class Interface(Record):
                              .format(data_width))
         self.addr_width = addr_width
         self.data_width = data_width
+        self.memory_map = MemoryMap(addr_width=addr_width,
+                                    data_width=data_width,
+                                    alignment=alignment)
 
         super().__init__([
-            ("addr",    addr_width),
-            ("r_data",  data_width),
-            ("r_stb",   1),
-            ("w_data",  data_width),
-            ("w_stb",   1),
+            ("addr", addr_width),
+            ("r_data", data_width),
+            ("r_stb", 1),
+            ("w_data", data_width),
+            ("w_stb", 1),
         ], name=name, src_loc_at=1)
 
 
-class Decoder(Elaboratable):
-    """CSR bus decoder.
+class Multiplexer(Elaboratable):
+    """CSR register multiplexer.
 
     An address-based multiplexer for CSR registers implementing atomic updates.
 
     Latency
     -------
 
-    Writes are registered, and are performed 1 cycle after ``w_stb`` is asserted.
+    Writes are registered, and are performed 1 cycle after ``w_stb``
+    is asserted.
 
     Alignment
     ---------
 
-    Because the CSR bus conserves logic and routing resources, it is common to e.g. access
-    a CSR bus with an *n*-bit data path from a CPU with a *k*-bit datapath (*k>n*) in cases
-    where CSR access latency is less important than resource usage. In this case, two strategies
+    Because the CSR bus conserves logic and routing resources, it is
+    common to e.g. access a CSR bus with an *n*-bit data path from a CPU
+    with a *k*-bit datapath (*k>n*) in cases where CSR access latency
+    is less important than resource usage. In this case, two strategies
     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, 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 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.
+        * 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, 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 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
     ----------
@@ -180,70 +215,40 @@ class Decoder(Elaboratable):
     data_width : int
         Data width. See :class:`Interface`.
     alignment : int
-        Register alignment. The address assigned to each register will be a multiple of
-        ``2 ** alignment``.
+        Register alignment. See :class:`Interface`.
 
     Attributes
     ----------
     bus : :class:`Interface`
         CSR bus providing access to registers.
     """
+
     def __init__(self, *, addr_width, data_width, alignment=0):
-        self.bus = Interface(addr_width=addr_width, data_width=data_width)
+        self.bus = Interface(addr_width=addr_width,
+                            data_width=data_width,
+                            alignment=alignment,
+                             name="csr")
+        self._map = self.bus.memory_map
 
-        if not isinstance(alignment, int) or alignment < 0:
-            raise ValueError("Alignment must be a non-negative integer, not {!r}"
-                             .format(alignment))
-        self.alignment = alignment
+    def align_to(self, alignment):
+        """Align the implicit address of the next register.
 
-        self._next_addr = 0
-        self._elements  = dict()
+        See :meth:`MemoryMap.align_to` for details.
+        """
+        return self._map.align_to(alignment)
 
-    def add(self, element):
+    def add(self, element, *, addr=None, alignment=None):
         """Add a register.
 
-        Arguments
-        ---------
-        element : :class:`Element`
-            Interface of the register.
-
-        Return value
-        ------------
-        An ``(addr, size)`` tuple, where ``addr`` is the address assigned to the first chunk of
-        the register, and ``size`` is the amount of chunks it takes, which may be greater than
-        ``element.size // self.data_width`` due to alignment.
+        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))
+            raise TypeError("Element must be an instance of csr.Element, "
+                            "not {!r}" .format(element))
 
-        addr = self.align_to(self.alignment)
-        self._next_addr += (element.width + self.bus.data_width - 1) // self.bus.data_width
-        size = self.align_to(self.alignment) - addr
-        self._elements[addr] = element, size
-        return addr, size
-
-    def align_to(self, alignment):
-        """Align the next register explicitly.
-
-        Arguments
-        ---------
-        alignment : int
-            Register alignment. The address assigned to the next register will be a multiple of
-            ``2 ** alignment`` or ``2 ** self.alignment``, whichever is greater.
-
-        Return value
-        ------------
-        Address of the next register.
-        """
-        if not isinstance(alignment, int) or alignment < 0:
-            raise ValueError("Alignment must be a non-negative integer, not {!r}"
-                             .format(alignment))
-
-        align_chunks = 1 << alignment
-        if self._next_addr % align_chunks != 0:
-            self._next_addr += align_chunks - (self._next_addr % align_chunks)
-        return self._next_addr
+        size = (element.width + self.bus.data_width - 1) // self.bus.data_width
+        return self._map.add_resource(
+            element, size=size, addr=addr, alignment=alignment)
 
     def elaborate(self, platform):
         m = Module()
@@ -255,41 +260,139 @@ class Decoder(Elaboratable):
         # 2-AND or 2-OR gates.
         r_data_fanin = 0
 
-        for elem_addr, (elem, elem_size) in self._elements.items():
+        for elem, (elem_start, elem_end) in self._map.resources():
             shadow = Signal(elem.width, name="{}__shadow".format(elem.name))
+            if elem.access.readable():
+                shadow_en = Signal(
+                    elem_end - elem_start,
+                    name="{}__shadow_en".format(
+                        elem.name))
+                m.d.sync += shadow_en.eq(0)
             if elem.access.writable():
                 m.d.comb += elem.w_data.eq(shadow)
+                m.d.sync += elem.w_stb.eq(0)
 
             # Enumerate every address used by the register explicitly, rather than using
             # arithmetic comparisons, since some toolchains (e.g. Yosys) are too eager to infer
             # carry chains for comparisons, even with a constant. (Register sizes don't have
             # to be powers of 2.)
             with m.Switch(self.bus.addr):
-                for chunk_offset in range(elem_size):
-                    chunk_slice = slice(chunk_offset * self.bus.data_width,
-                                        (chunk_offset + 1) * self.bus.data_width)
-                    with m.Case(elem_addr + chunk_offset):
+                for chunk_offset, chunk_addr in enumerate(
+                        range(elem_start, elem_end)):
+                    shadow_slice = shadow.word_select(
+                        chunk_offset, self.bus.data_width)
+
+                    with m.Case(chunk_addr):
                         if elem.access.readable():
-                            chunk_r_stb = Signal(self.bus.data_width,
-                                name="{}__r_stb_{}".format(elem.name, chunk_offset))
-                            r_data_fanin |= Mux(chunk_r_stb, shadow[chunk_slice], 0)
-                            if chunk_offset == 0:
+                            r_data_fanin |= Mux(
+                                shadow_en[chunk_offset], shadow_slice, 0)
+                            if chunk_addr == elem_start:
                                 m.d.comb += elem.r_stb.eq(self.bus.r_stb)
                                 with m.If(self.bus.r_stb):
                                     m.d.sync += shadow.eq(elem.r_data)
                             # Delay by 1 cycle, allowing reads to be pipelined.
-                            m.d.sync += chunk_r_stb.eq(self.bus.r_stb)
+                            m.d.sync += shadow_en.eq(self.bus.r_stb <<
+                                                     chunk_offset)
 
                         if elem.access.writable():
-                            if chunk_offset == elem_size - 1:
+                            if chunk_addr == elem_end - 1:
                                 # Delay by 1 cycle, avoiding combinatorial paths through
                                 # the CSR bus and into CSR registers.
                                 m.d.sync += elem.w_stb.eq(self.bus.w_stb)
                             with m.If(self.bus.w_stb):
-                                m.d.sync += shadow[chunk_slice].eq(self.bus.w_data)
+                                m.d.sync += shadow_slice.eq(self.bus.w_data)
+
+        m.d.comb += self.bus.r_data.eq(r_data_fanin)
+
+        return m
+
+
+class Decoder(Elaboratable):
+    """CSR bus decoder.
+
+    An address decoder for subordinate CSR buses.
+
+    Usage
+    -----
+
+    Although there is no functional difference between adding a set of
+    registers directly to a :class:`Multiplexer` and adding a set of
+    registers to multiple :class:`Multiplexer`s that are aggregated with
+    a :class:`Decoder`, hierarchical CSR buses are useful for organizing
+    a hierarchical design. If many peripherals are directly served by
+    a single :class:`Multiplexer`, 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 decoder, 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,
+                             name="csr")
+        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 "
+                             "decoder data width {}"
+                             .format(sub_bus.data_width, self.bus.data_width))
+        self._subs[sub_bus.memory_map] = sub_bus
+        return self._map.add_window(sub_bus.memory_map, addr=addr)
+
+    def elaborate(self, platform):
+        m = Module()
+
+        # See Multiplexer.elaborate above.
+        r_data_fanin = 0
+
+        with m.Switch(self.bus.addr):
+            for sub_map, (sub_pat, sub_ratio) in self._map.window_patterns():
+                assert sub_ratio == 1
+
+                sub_bus = self._subs[sub_map]
+                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.Default():
-                    m.d.sync += shadow.eq(0)
+                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)