3 from nmigen
.hdl
.rec
import Direction
4 from nmigen
.utils
import log2_int
6 from ..memory
import MemoryMap
7 from ..scheduler
import *
10 __all__
= ["CycleType", "BurstTypeExt", "Interface", "Decoder",
11 "Arbiter", "InterconnectShared"]
14 class CycleType(Enum
):
15 """Wishbone Registered Feedback cycle type."""
22 class BurstTypeExt(Enum
):
23 """Wishbone Registered Feedback burst type extension."""
30 class Interface(Record
):
31 """Wishbone interface.
33 See the `Wishbone specification
34 <https://opencores.org/howto/wishbone>`_ for description of the
35 Wishbone signals. The ``RST_I`` and ``CLK_I`` signals are provided
36 as a part of the clock domain that drives the interface.
38 Note that the data width of the underlying memory map of the interface
39 is equal to port granularity, not port size. If port granularity is
40 less than port size, then the address width of the underlying memory
41 map is extended to reflect that.
46 Width of the address signal.
48 Width of the data signals ("port size" in Wishbone terminology).
50 granularity : int or None
51 Granularity of select signals ("port granularity" in Wishbone
52 terminology). One of 8, 16, 32, 64. Optional and defaults to
53 None, meaning it is equal to the address width.
55 Selects the optional signals that will be a part of this interface.
57 Resource and window alignment. Optional. See :class:`MemoryMap`.
59 Name of the underlying record.
63 The correspondence between the nMigen-SoC signals and the Wishbone
64 signals changes depending on whether the interface acts as an
65 initiator or a target.
67 adr : Signal(addr_width)
68 Corresponds to Wishbone signal ``ADR_O`` (initiator) or ``ADR_I``
70 dat_w : Signal(data_width)
71 Corresponds to Wishbone signal ``DAT_O`` (initiator) or ``DAT_I``
73 dat_r : Signal(data_width)
74 Corresponds to Wishbone signal ``DAT_I`` (initiator) or ``DAT_O``
76 sel : Signal(data_width // granularity)
77 Corresponds to Wishbone signal ``SEL_O`` (initiator) or ``SEL_I``
80 Corresponds to Wishbone signal ``CYC_O`` (initiator) or ``CYC_I``
83 Corresponds to Wishbone signal ``STB_O`` (initiator) or ``STB_I``
86 Corresponds to Wishbone signal ``WE_O`` (initiator) or ``WE_I``
89 Corresponds to Wishbone signal ``ACK_I`` (initiator) or ``ACK_O``
92 Optional. Corresponds to Wishbone signal ``ERR_I`` (initiator)
93 or ``ERR_O`` (target).
95 Optional. Corresponds to Wishbone signal ``RTY_I`` (initiator)
96 or ``RTY_O`` (target).
98 Optional. Corresponds to Wishbone signal ``STALL_I`` (initiator)
99 or ``STALL_O`` (target).
101 Optional. Corresponds to Wishbone signal ``LOCK_O`` (initiator)
102 or ``LOCK_I`` (target).
104 Optional. Corresponds to Wishbone signal ``CTI_O`` (initiator)
105 or ``CTI_I`` (target).
107 Optional. Corresponds to Wishbone signal ``BTE_O`` (initiator)
108 or ``BTE_I`` (target).
110 def __init__(self
, *, addr_width
, data_width
, granularity
=None, features
=frozenset(),
111 alignment
=0, name
=None):
112 if not isinstance(addr_width
, int) or addr_width
< 0:
113 raise ValueError("Address width must be a non-negative integer, not {!r}"
115 if data_width
not in (8, 16, 32, 64):
116 raise ValueError("Data width must be one of 8, 16, 32, 64, not {!r}"
118 if granularity
is None:
119 granularity
= data_width
120 elif granularity
not in (8, 16, 32, 64):
121 raise ValueError("Granularity must be one of 8, 16, 32, 64, not {!r}"
122 .format(granularity
))
123 if granularity
> data_width
:
124 raise ValueError("Granularity {} may not be greater than data width {}"
125 .format(granularity
, data_width
))
126 self
.addr_width
= addr_width
127 self
.data_width
= data_width
128 self
.granularity
= granularity
129 granularity_bits
= log2_int(data_width
// granularity
)
130 self
._alignment
= alignment
131 self
.memory_map
= MemoryMap(addr_width
=max(1, addr_width
+ granularity_bits
),
132 data_width
=data_width
>> granularity_bits
,
135 self
._features
= set(features
)
136 unknown
= self
._features
- {"rty", "err", "stall", "lock", "cti", "bte"}
138 raise ValueError("Optional signal(s) {} are not supported"
139 .format(", ".join(map(repr, unknown
))))
141 ("adr", addr_width
, Direction
.FANOUT
),
142 ("dat_w", data_width
, Direction
.FANOUT
),
143 ("dat_r", data_width
, Direction
.FANIN
),
144 ("sel", data_width
// granularity
, Direction
.FANOUT
),
145 ("cyc", 1, Direction
.FANOUT
),
146 ("stb", 1, Direction
.FANOUT
),
147 ("we", 1, Direction
.FANOUT
),
148 ("ack", 1, Direction
.FANIN
),
150 if "err" in features
:
151 layout
+= [("err", 1, Direction
.FANIN
)]
152 if "rty" in features
:
153 layout
+= [("rty", 1, Direction
.FANIN
)]
154 if "stall" in features
:
155 layout
+= [("stall", 1, Direction
.FANIN
)]
156 if "lock" in features
:
157 layout
+= [("lock", 1, Direction
.FANOUT
)]
158 if "cti" in features
:
159 layout
+= [("cti", CycleType
, Direction
.FANOUT
)]
160 if "bte" in features
:
161 layout
+= [("bte", BurstTypeExt
, Direction
.FANOUT
)]
162 super().__init
__(layout
, name
=name
, src_loc_at
=1)
165 def from_pure_record(cls
, record
):
166 """Instantiate a :class:`wishbone.Interface` from a simple :class:`Record`
168 if not isinstance(record
, Record
):
169 raise TypeError("{!r} is not a Record"
171 addr_width
= len(record
.adr
)
172 if len(record
.dat_w
) != len(record
.dat_r
):
173 raise AttributeError("Record {!r} has {}-bit long \"dat_w\" "
174 "but {}-bit long \"dat_r\""
175 .format(record
, len(record
.dat_w
), len(record
.dat_r
)))
176 data_width
= len(record
.dat_w
)
177 if data_width
%len(record
.sel
) != 0:
178 raise AttributeError("Record {!r} has invalid granularity value because "
179 "its data width is {}-bit long but "
180 "its \"sel\" is {}-bit long"
181 .format(record
, data_width
, len(record
.sel
)))
182 granularity
= data_width
// len(record
.sel
)
184 for signal_name
in ["rty", "err", "stall", "lock", "cti", "bte"]:
185 if hasattr(record
, signal_name
):
186 features
.append(signal_name
)
187 return cls(addr_width
=addr_width
,
188 data_width
=data_width
,
189 granularity
=granularity
,
192 name
=record
.name
+"_intf")
195 class Decoder(Elaboratable
):
196 """Wishbone bus decoder.
198 An address decoder for subordinate Wishbone buses.
203 Address width. See :class:`Interface`.
205 Data width. See :class:`Interface`.
206 granularity : int or None
207 Granularity. Optional. See :class:`Interface`
209 Optional signal set. See :class:`Interface`.
211 Window alignment. Optional. See :class:`Interface`.
215 bus : :class:`Interface`
216 Bus providing access to subordinate buses.
218 def __init__(self
, *, addr_width
, data_width
, granularity
=None, features
=frozenset(),
220 self
.bus
= Interface(addr_width
=addr_width
, data_width
=data_width
,
221 granularity
=granularity
, features
=features
,
223 self
._map
= self
.bus
.memory_map
226 def align_to(self
, alignment
):
227 """Align the implicit address of the next window.
229 See :meth:`MemoryMap.align_to` for details.
231 return self
._map
.align_to(alignment
)
233 def add(self
, sub_bus
, *, addr
=None, sparse
=False):
234 """Add a window to a subordinate bus.
236 The decoder can perform either sparse or dense address
237 translation. If dense address translation is used (the default),
238 the subordinate bus must have the same data width as the decoder;
239 the window will be contiguous. If sparse address translation is
240 used, the subordinate bus may have data width less than the data
241 width of the decoder; the window may be discontiguous. In either
242 case, the granularity of the subordinate bus must be equal to
243 or less than the granularity of the decoder.
245 See :meth:`MemoryMap.add_resource` for details.
247 if not isinstance(sub_bus
, Interface
):
248 raise TypeError("Subordinate bus must be an instance of wishbone.Interface, not {!r}"
250 if sub_bus
.granularity
> self
.bus
.granularity
:
251 raise ValueError("Subordinate bus has granularity {}, which is greater than the "
252 "decoder granularity {}"
253 .format(sub_bus
.granularity
, self
.bus
.granularity
))
255 if sub_bus
.data_width
!= self
.bus
.data_width
:
256 raise ValueError("Subordinate bus has data width {}, which is not the same as "
257 "decoder data width {} (required for dense address translation)"
258 .format(sub_bus
.data_width
, self
.bus
.data_width
))
260 if sub_bus
.granularity
!= sub_bus
.data_width
:
261 raise ValueError("Subordinate bus has data width {}, which is not the same as "
262 "subordinate bus granularity {} (required for sparse address "
264 .format(sub_bus
.data_width
, sub_bus
.granularity
))
265 for opt_output
in {"err", "rty", "stall"}:
266 if hasattr(sub_bus
, opt_output
) and not hasattr(self
.bus
, opt_output
):
267 raise ValueError("Subordinate bus has optional output {!r}, but the decoder "
268 "does not have a corresponding input"
271 self
._subs
[sub_bus
.memory_map
] = sub_bus
272 return self
._map
.add_window(sub_bus
.memory_map
, addr
=addr
, sparse
=sparse
)
274 def elaborate(self
, platform
):
282 with m
.Switch(self
.bus
.adr
):
283 for sub_map
, (sub_pat
, sub_ratio
) in self
._map
.window_patterns():
284 sub_bus
= self
._subs
[sub_map
]
287 sub_bus
.adr
.eq(self
.bus
.adr
<< log2_int(sub_ratio
)),
288 sub_bus
.dat_w
.eq(self
.bus
.dat_w
),
289 sub_bus
.sel
.eq(Cat(Repl(sel
, sub_ratio
) for sel
in self
.bus
.sel
)),
290 sub_bus
.we
.eq(self
.bus
.we
),
291 sub_bus
.stb
.eq(self
.bus
.stb
),
293 if hasattr(sub_bus
, "lock"):
294 m
.d
.comb
+= sub_bus
.lock
.eq(getattr(self
.bus
, "lock", 0))
295 if hasattr(sub_bus
, "cti"):
296 m
.d
.comb
+= sub_bus
.cti
.eq(getattr(self
.bus
, "cti", CycleType
.CLASSIC
))
297 if hasattr(sub_bus
, "bte"):
298 m
.d
.comb
+= sub_bus
.bte
.eq(getattr(self
.bus
, "bte", BurstTypeExt
.LINEAR
))
300 with m
.Case(sub_pat
[:-log2_int(self
.bus
.data_width
// self
.bus
.granularity
)]):
302 sub_bus
.cyc
.eq(self
.bus
.cyc
),
303 self
.bus
.dat_r
.eq(sub_bus
.dat_r
),
306 ack_fanin |
= sub_bus
.ack
307 if hasattr(sub_bus
, "err"):
308 err_fanin |
= sub_bus
.err
309 if hasattr(sub_bus
, "rty"):
310 rty_fanin |
= sub_bus
.rty
311 if hasattr(sub_bus
, "stall"):
312 stall_fanin |
= sub_bus
.stall
314 m
.d
.comb
+= self
.bus
.ack
.eq(ack_fanin
)
315 if hasattr(self
.bus
, "err"):
316 m
.d
.comb
+= self
.bus
.err
.eq(err_fanin
)
317 if hasattr(self
.bus
, "rty"):
318 m
.d
.comb
+= self
.bus
.rty
.eq(rty_fanin
)
319 if hasattr(self
.bus
, "stall"):
320 m
.d
.comb
+= self
.bus
.stall
.eq(stall_fanin
)
325 class Arbiter(Elaboratable
):
326 """Wishbone bus arbiter.
328 An arbiter for initiators (masters) to access a shared Wishbone bus.
333 Address width of the shared bus. See :class:`Interface`.
335 Data width of the shared bus. See :class:`Interface`.
336 granularity : int or None
337 Granularity of the shared bus. Optional. See :class:`Interface`.
339 Optional signal set for the shared bus. See :class:`Interface`.
341 Method for bus arbitration. Optional and defaults to "rr"
342 (Round Robin, see :class:`scheduler.RoundRobin`).
346 bus : :class:`Interface`
347 Shared bus to which the selected initiator gains access.
349 def __init__(self
, *, addr_width
, data_width
, granularity
=None, features
=frozenset(),
351 self
.bus
= Interface(addr_width
=addr_width
, data_width
=data_width
,
352 granularity
=granularity
, features
=features
)
354 if scheduler
not in ["rr"]:
355 raise ValueError("Scheduling mode must be \"rr\", not {!r}"
357 self
._scheduler
= scheduler
359 def add(self
, itor_bus
):
360 """Add an initiator bus to the arbiter.
362 The initiator bus must have the same address width and data
363 width as the arbiter. The granularity of the initiator bus must
364 be greater than or equal to the granularity of the arbiter.
366 if not isinstance(itor_bus
, Interface
):
367 raise TypeError("Initiator bus must be an instance of wishbone.Interface, not {!r}"
369 if itor_bus
.addr_width
!= self
.bus
.addr_width
:
370 raise ValueError("Initiator bus has address width {}, which is not the same as "
371 "arbiter address width {}"
372 .format(itor_bus
.addr_width
, self
.bus
.addr_width
))
373 if itor_bus
.granularity
< self
.bus
.granularity
:
374 raise ValueError("Initiator bus has granularity {}, which is lesser than the "
375 "arbiter granularity {}"
376 .format(itor_bus
.granularity
, self
.bus
.granularity
))
377 if itor_bus
.data_width
!= self
.bus
.data_width
:
378 raise ValueError("Initiator bus has data width {}, which is not the same as "
379 "arbiter data width {}"
380 .format(itor_bus
.data_width
, self
.bus
.data_width
))
381 for opt_output
in {"lock", "cti", "bte"}:
382 if hasattr(itor_bus
, opt_output
) and not hasattr(self
.bus
, opt_output
):
383 raise ValueError("Initiator bus has optional output {!r}, but the arbiter "
384 "does not have a corresponding input"
387 self
._itors
.append(itor_bus
)
389 def elaborate(self
, platform
):
392 if self
._scheduler
== "rr":
393 m
.submodules
.scheduler
= scheduler
= RoundRobin(len(self
._itors
))
394 grant
= Signal(range(len(self
._itors
)))
396 # CYC should not be indefinitely asserted. (See RECOMMENDATION 3.05, Wishbone B4)
397 bus_busy
= self
.bus
.cyc
398 if hasattr(self
.bus
, "lock"):
399 # If LOCK is not asserted, we also wait for STB to be deasserted before granting bus
400 # ownership to the next initiator. If we didn't, the next bus owner could receive
401 # an ACK (or ERR, RTY) from the previous transaction when targeting the same
403 bus_busy
&= self
.bus
.lock | self
.bus
.stb
406 scheduler
.stb
.eq(~bus_busy
),
407 grant
.eq(scheduler
.grant
),
408 scheduler
.request
.eq(Cat(itor_bus
.cyc
for itor_bus
in self
._itors
))
411 with m
.Switch(grant
):
412 for i
, itor_bus
in enumerate(self
._itors
):
413 m
.d
.comb
+= itor_bus
.dat_r
.eq(self
.bus
.dat_r
)
414 if hasattr(itor_bus
, "stall"):
415 itor_bus_stall
= Signal(reset
=1)
416 m
.d
.comb
+= itor_bus
.stall
.eq(itor_bus_stall
)
419 ratio
= itor_bus
.granularity
// self
.bus
.granularity
421 self
.bus
.adr
.eq(itor_bus
.adr
),
422 self
.bus
.dat_w
.eq(itor_bus
.dat_w
),
423 self
.bus
.sel
.eq(Cat(Repl(sel
, ratio
) for sel
in itor_bus
.sel
)),
424 self
.bus
.we
.eq(itor_bus
.we
),
425 self
.bus
.stb
.eq(itor_bus
.stb
),
427 m
.d
.comb
+= self
.bus
.cyc
.eq(itor_bus
.cyc
)
428 if hasattr(self
.bus
, "lock"):
429 m
.d
.comb
+= self
.bus
.lock
.eq(getattr(itor_bus
, "lock", 1))
430 if hasattr(self
.bus
, "cti"):
431 m
.d
.comb
+= self
.bus
.cti
.eq(getattr(itor_bus
, "cti", CycleType
.CLASSIC
))
432 if hasattr(self
.bus
, "bte"):
433 m
.d
.comb
+= self
.bus
.bte
.eq(getattr(itor_bus
, "bte", BurstTypeExt
.LINEAR
))
435 m
.d
.comb
+= itor_bus
.ack
.eq(self
.bus
.ack
)
436 if hasattr(itor_bus
, "err"):
437 m
.d
.comb
+= itor_bus
.err
.eq(getattr(self
.bus
, "err", 0))
438 if hasattr(itor_bus
, "rty"):
439 m
.d
.comb
+= itor_bus
.rty
.eq(getattr(self
.bus
, "rty", 0))
440 if hasattr(itor_bus
, "stall"):
441 m
.d
.comb
+= itor_bus_stall
.eq(getattr(self
.bus
, "stall", ~self
.bus
.ack
))
446 class InterconnectShared(Elaboratable
):
447 """Wishbone bus interconnect module.
449 This is initialised using the following components:
450 (1) A shared Wishbone bus connecting multiple initiators (MASTERs) with
451 multiple targeted SLAVEs;
452 (2) A list of initiator Wishbone busses; and
453 (3) A list of SLAVE Wishbone busses targeted by the MASTERs.
455 This instantiates the following components:
456 (1) An arbiter (:class:`Arbiter`) controlling access of
457 multiple MASTERs to the shared bus; and
458 (2) A decoder (:class:`Decoder`) specifying which targeted SLAVE is to be accessed
459 using address translation on the shared bus.
461 See Section 8.2.3 of Wishbone B4 for implemenation specifications.
466 Address width of the shared bus. See :class:`Interface`.
468 Data width of the shared bus. See :class:`Interface`.
469 itors : list of (:class:`Interface` OR :class:`Record`)
470 List of MASTERs on the arbiter to request access to the shared bus.
471 If the item is a :class:`Record`, its fields must be named using the
472 convention of :class:`Interface`.
473 targets : list of (:class:`Interface` OR tuple of (:class:`Interface`, int))
474 List of SLAVEs on the decoder whose accesses are to be targeted
475 by the shared bus. If the item is a tuple of (intf, addr), the
476 :class:`Interface`-type intf is added at the (address width +
477 granularity bits)-wide address of the int-type addr.
478 granularity : int or None
479 Granularity of the shared bus. Optional. See :class:`Interface`.
481 Optional signal set for the shared bus. See :class:`Interface`.
483 Method for bus arbitration for the arbiter. Optional and defaults
484 to "rr" (Round Robin, see :class:`scheduler.RoundRobin`).
486 Window alignment for the decoder. Optional. See :class:`Interface`.
490 arbiter : :class:`Arbiter`
491 The arbiter that connects the list of MASTERs to a shared bus.
492 decoder : :class:`Decoder`
493 The decoder that connects the shared bus to the list of SLAVEs.
495 def __init__(self
, *, addr_width
, data_width
, itors
, targets
, **kwargs
):
496 self
.addr_width
= addr_width
497 self
.data_width
= data_width
502 self
._itors
_convert
_stmts
= []
503 for itor_bus
in itors
:
504 if isinstance(itor_bus
, Interface
):
505 self
._itors
.append(itor_bus
)
506 elif isinstance(itor_bus
, Record
):
507 master_interface
= Interface
.from_pure_record(itor_bus
)
508 self
._itors
_convert
_stmts
.append(
509 itor_bus
.connect(master_interface
)
511 self
._itors
.append(master_interface
)
513 raise TypeError("Master {!r} must be a Wishbone interface"
516 for target_bus
in targets
:
517 self
._targets
.append(target_bus
)
519 arbiter_kwargs
= dict()
520 for name
in ["granularity", "features", "scheduler"]:
522 arbiter_kwargs
[name
] = kwargs
[name
]
523 self
.arbiter
= Arbiter(
524 addr_width
=self
.addr_width
, data_width
=self
.data_width
, **arbiter_kwargs
526 self
.arbiter
.bus
.name
= "arbiter_shared"
527 for itor_bus
in self
._itors
:
528 self
.arbiter
.add(itor_bus
)
530 decoder_kwargs
= dict()
531 for name
in ["granularity", "features", "alignment"]:
533 decoder_kwargs
[name
] = kwargs
[name
]
534 self
.decoder
= Decoder(
535 addr_width
=self
.addr_width
, data_width
=self
.data_width
, **decoder_kwargs
537 self
.decoder
.bus
.name
= "decoder_shared"
538 for item
in self
._targets
:
539 if isinstance(item
, Interface
):
540 self
.decoder
.add(item
)
541 elif isinstance(item
, tuple) and len(item
) == 2:
542 self
.decoder
.add(item
[0], addr
=item
[1])
544 raise TypeError("Target must be a Wishbone interface, "
545 "or a (Wishbone interface, start address) tuple, not {!r}"
548 def elaborate(self
, platform
):
551 m
.submodules
.arbiter
= self
.arbiter
552 m
.submodules
.decoder
= self
.decoder
555 self
._itors
_convert
_stmts
+
556 self
.arbiter
.bus
.connect(self
.decoder
.bus
)