11import enum
22from nmigen import *
3+ from nmigen .utils import log2_int
34
45from ..memory import MemoryMap
56
67
7- __all__ = ["Element" , "Interface" , "Decoder" ]
8+ __all__ = ["Element" , "Interface" , "Decoder" , "Multiplexer" ]
89
910
1011class Element (Record ):
@@ -103,15 +104,20 @@ class Interface(Record):
103104 Address width. At most ``(2 ** addr_width) * data_width`` register bits will be available.
104105 data_width : int
105106 Data width. Registers are accessed in ``data_width`` sized chunks.
107+ alignment : int
108+ Alignment. See :class:`MemoryMap`.
106109 name : str
107110 Name of the underlying record.
108111
109112 Attributes
110113 ----------
114+ memory_map : MemoryMap
115+ Map of the bus.
111116 addr : Signal(addr_width)
112117 Address for reads and writes.
113118 r_data : Signal(data_width)
114- Read data. Valid on the next cycle after ``r_stb`` is asserted.
119+ Read data. Valid on the next cycle after ``r_stb`` is asserted. Otherwise, zero. (Keeping
120+ read data of an unused interface at zero simplifies multiplexers.)
115121 r_stb : Signal()
116122 Read strobe. If ``addr`` points to the first chunk of a register, captures register value
117123 and causes read side effects to be performed (if any). If ``addr`` points to any chunk
@@ -126,7 +132,7 @@ class Interface(Record):
126132 nothing.
127133 """
128134
129- def __init__ (self , * , addr_width , data_width , name = None ):
135+ def __init__ (self , * , addr_width , data_width , alignment = 0 , name = None ):
130136 if not isinstance (addr_width , int ) or addr_width <= 0 :
131137 raise ValueError ("Address width must be a positive integer, not {!r}"
132138 .format (addr_width ))
@@ -135,6 +141,8 @@ def __init__(self, *, addr_width, data_width, name=None):
135141 .format (data_width ))
136142 self .addr_width = addr_width
137143 self .data_width = data_width
144+ self .memory_map = MemoryMap (addr_width = addr_width , data_width = data_width ,
145+ alignment = alignment )
138146
139147 super ().__init__ ([
140148 ("addr" , addr_width ),
@@ -185,17 +193,16 @@ class Decoder(Elaboratable):
185193 data_width : int
186194 Data width. See :class:`Interface`.
187195 alignment : int
188- Register alignment. The address assigned to each register will be a multiple of
189- ``2 ** alignment``.
196+ Register alignment. See :class:`Interface`.
190197
191198 Attributes
192199 ----------
193200 bus : :class:`Interface`
194201 CSR bus providing access to registers.
195202 """
196203 def __init__ (self , * , addr_width , data_width , alignment = 0 ):
197- self .bus = Interface (addr_width = addr_width , data_width = data_width )
198- self ._map = MemoryMap ( addr_width = addr_width , data_width = data_width , alignment = alignment )
204+ self .bus = Interface (addr_width = addr_width , data_width = data_width , alignment = alignment )
205+ self ._map = self . bus . memory_map
199206
200207 def align_to (self , alignment ):
201208 """Align the implicit address of the next register.
@@ -266,3 +273,91 @@ def elaborate(self, platform):
266273 m .d .comb += self .bus .r_data .eq (r_data_fanin )
267274
268275 return m
276+
277+
278+ class Multiplexer (Elaboratable ):
279+ """CSR bus multiplexer.
280+
281+ An address-based multiplexer for subordinate CSR buses.
282+
283+ Usage
284+ -----
285+
286+ Although there is no functional difference between adding a set of registers directly to
287+ a :class:`Decoder` and adding a set of reigsters to multiple :class:`Decoder`s that are
288+ aggregated with a :class:`Multiplexer`, hierarchical CSR buses are useful for organizing
289+ a hierarchical design. If many peripherals are directly served by a single :class:`Decoder`,
290+ a very large amount of ports will connect the peripheral registers with the decoder, and
291+ the cost of decoding logic would not be attributed to specific peripherals. With a multiplexer,
292+ only five signals per peripheral will be used, and the logic could be kept together with
293+ the peripheral.
294+
295+ Parameters
296+ ----------
297+ addr_width : int
298+ Address width. See :class:`Interface`.
299+ data_width : int
300+ Data width. See :class:`Interface`.
301+ alignment : int
302+ Window alignment. See :class:`Interface`.
303+
304+ Attributes
305+ ----------
306+ bus : :class:`Interface`
307+ CSR bus providing access to subordinate buses.
308+ """
309+ def __init__ (self , * , addr_width , data_width , alignment = 0 ):
310+ self .bus = Interface (addr_width = addr_width , data_width = data_width , alignment = alignment )
311+ self ._map = self .bus .memory_map
312+ self ._subs = dict ()
313+
314+ def align_to (self , alignment ):
315+ """Align the implicit address of the next window.
316+
317+ See :meth:`MemoryMap.align_to` for details.
318+ """
319+ return self ._map .align_to (alignment )
320+
321+ def add (self , sub_bus , * , addr = None ):
322+ """Add a window to a subordinate bus.
323+
324+ See :meth:`MemoryMap.add_resource` for details.
325+ """
326+ if not isinstance (sub_bus , Interface ):
327+ raise TypeError ("Subordinate bus must be an instance of csr.Interface, not {!r}"
328+ .format (sub_bus ))
329+ if sub_bus .data_width != self .bus .data_width :
330+ raise ValueError ("Subordinate bus has data width {}, which is not the same as "
331+ "multiplexer data width {}"
332+ .format (sub_bus .data_width , self .bus .data_width ))
333+
334+ start , end , ratio = window_range = self ._map .add_window (sub_bus .memory_map , addr = addr )
335+ assert ratio == 1
336+ pattern = "{:0{}b}{}" .format (start >> sub_bus .addr_width ,
337+ self .bus .addr_width - sub_bus .addr_width ,
338+ "-" * sub_bus .addr_width )
339+ self ._subs [pattern ] = sub_bus
340+ return window_range
341+
342+ def elaborate (self , platform ):
343+ m = Module ()
344+
345+ # See Decoder.elaborate above.
346+ r_data_fanin = 0
347+
348+ with m .Switch (self .bus .addr ):
349+ for sub_pat , sub_bus in self ._subs .items ():
350+ m .d .comb += sub_bus .addr .eq (self .bus .addr [:sub_bus .addr_width ])
351+
352+ # The CSR bus interface is defined to output zero when idle, allowing us to avoid
353+ # adding a multiplexer here.
354+ r_data_fanin |= sub_bus .r_data
355+ m .d .comb += sub_bus .w_data .eq (self .bus .w_data )
356+
357+ with m .Case (sub_pat ):
358+ m .d .comb += sub_bus .r_stb .eq (self .bus .r_stb )
359+ m .d .comb += sub_bus .w_stb .eq (self .bus .w_stb )
360+
361+ m .d .comb += self .bus .r_data .eq (r_data_fanin )
362+
363+ return m
0 commit comments