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 <https://opencores.org/howto/wishbone>`_ for description
34 of the Wishbone signals. The ``RST_I`` and ``CLK_I`` signals are provided as a part of
35 the clock domain that drives the interface.
37 Note that the data width of the underlying memory map of the interface is equal to port
38 granularity, not port size. If port granularity is less than port size, then the address width
39 of the underlying memory map is extended to reflect that.
44 Width of the address signal.
46 Width of the data signals ("port size" in Wishbone terminology).
48 granularity : int or None
49 Granularity of select signals ("port granularity" in Wishbone terminology).
50 One of 8, 16, 32, 64. Optional and defaults to None, meaning it is equal
53 Selects the optional signals that will be a part of this interface.
55 Resource and window alignment. Optional. See :class:`MemoryMap`.
57 Name of the underlying record.
61 The correspondence between the nMigen-SoC signals and the Wishbone signals changes depending
62 on whether the interface acts as an initiator or a target.
64 adr : Signal(addr_width)
65 Corresponds to Wishbone signal ``ADR_O`` (initiator) or ``ADR_I`` (target).
66 dat_w : Signal(data_width)
67 Corresponds to Wishbone signal ``DAT_O`` (initiator) or ``DAT_I`` (target).
68 dat_r : Signal(data_width)
69 Corresponds to Wishbone signal ``DAT_I`` (initiator) or ``DAT_O`` (target).
70 sel : Signal(data_width // granularity)
71 Corresponds to Wishbone signal ``SEL_O`` (initiator) or ``SEL_I`` (target).
73 Corresponds to Wishbone signal ``CYC_O`` (initiator) or ``CYC_I`` (target).
75 Corresponds to Wishbone signal ``STB_O`` (initiator) or ``STB_I`` (target).
77 Corresponds to Wishbone signal ``WE_O`` (initiator) or ``WE_I`` (target).
79 Corresponds to Wishbone signal ``ACK_I`` (initiator) or ``ACK_O`` (target).
81 Optional. Corresponds to Wishbone signal ``ERR_I`` (initiator) or ``ERR_O`` (target).
83 Optional. Corresponds to Wishbone signal ``RTY_I`` (initiator) or ``RTY_O`` (target).
85 Optional. Corresponds to Wishbone signal ``STALL_I`` (initiator) or ``STALL_O`` (target).
87 Optional. Corresponds to Wishbone signal ``LOCK_O`` (initiator) or ``LOCK_I`` (target).
89 Optional. Corresponds to Wishbone signal ``CTI_O`` (initiator) or ``CTI_I`` (target).
91 Optional. Corresponds to Wishbone signal ``BTE_O`` (initiator) or ``BTE_I`` (target).
93 def __init__(self
, *, addr_width
, data_width
, granularity
=None, features
=frozenset(),
94 alignment
=0, name
=None):
95 if not isinstance(addr_width
, int) or addr_width
< 0:
96 raise ValueError("Address width must be a non-negative integer, not {!r}"
98 if data_width
not in (8, 16, 32, 64):
99 raise ValueError("Data width must be one of 8, 16, 32, 64, not {!r}"
101 if granularity
is None:
102 granularity
= data_width
103 elif granularity
not in (8, 16, 32, 64):
104 raise ValueError("Granularity must be one of 8, 16, 32, 64, not {!r}"
105 .format(granularity
))
106 if granularity
> data_width
:
107 raise ValueError("Granularity {} may not be greater than data width {}"
108 .format(granularity
, data_width
))
109 self
.addr_width
= addr_width
110 self
.data_width
= data_width
111 self
.granularity
= granularity
112 granularity_bits
= log2_int(data_width
// granularity
)
113 self
._alignment
= alignment
114 self
.memory_map
= MemoryMap(addr_width
=max(1, addr_width
+ granularity_bits
),
115 data_width
=data_width
>> granularity_bits
,
118 self
._features
= set(features
)
119 unknown
= self
._features
- {"rty", "err", "stall", "lock", "cti", "bte"}
121 raise ValueError("Optional signal(s) {} are not supported"
122 .format(", ".join(map(repr, unknown
))))
124 ("adr", addr_width
, Direction
.FANOUT
),
125 ("dat_w", data_width
, Direction
.FANOUT
),
126 ("dat_r", data_width
, Direction
.FANIN
),
127 ("sel", data_width
// granularity
, Direction
.FANOUT
),
128 ("cyc", 1, Direction
.FANOUT
),
129 ("stb", 1, Direction
.FANOUT
),
130 ("we", 1, Direction
.FANOUT
),
131 ("ack", 1, Direction
.FANIN
),
133 if "err" in features
:
134 layout
+= [("err", 1, Direction
.FANIN
)]
135 if "rty" in features
:
136 layout
+= [("rty", 1, Direction
.FANIN
)]
137 if "stall" in features
:
138 layout
+= [("stall", 1, Direction
.FANIN
)]
139 if "lock" in features
:
140 layout
+= [("lock", 1, Direction
.FANOUT
)]
141 if "cti" in features
:
142 layout
+= [("cti", CycleType
, Direction
.FANOUT
)]
143 if "bte" in features
:
144 layout
+= [("bte", BurstTypeExt
, Direction
.FANOUT
)]
145 super().__init
__(layout
, name
=name
, src_loc_at
=1)
148 def from_pure_record(cls
, record
):
149 """Instantiate a :class:`wishbone.Interface` from a simple :class:`Record`
151 if not isinstance(record
, Record
):
152 raise TypeError("{!r} is not a Record"
154 addr_width
= len(record
.adr
)
155 if len(record
.dat_w
) != len(record
.dat_r
):
156 raise AttributeError("Record {!r} has {}-bit long \"dat_w\" "
157 "but {}-bit long \"dat_r\""
158 .format(record
, len(record
.dat_w
), len(record
.dat_r
)))
159 data_width
= len(record
.dat_w
)
160 if data_width
%len(record
.sel
) != 0:
161 raise AttributeError("Record {!r} has invalid granularity value because "
162 "its data width is {}-bit long but "
163 "its \"sel\" is {}-bit long"
164 .format(record
, data_width
, len(record
.sel
)))
165 granularity
= data_width
// len(record
.sel
)
167 for signal_name
in ["rty", "err", "stall", "lock", "cti", "bte"]:
168 if hasattr(record
, signal_name
):
169 features
.append(signal_name
)
170 return cls(addr_width
=addr_width
,
171 data_width
=data_width
,
172 granularity
=granularity
,
175 name
=record
.name
+"_intf")
178 class Decoder(Elaboratable
):
179 """Wishbone bus decoder.
181 An address decoder for subordinate Wishbone buses.
186 Address width. See :class:`Interface`.
188 Data width. See :class:`Interface`.
189 granularity : int or None
190 Granularity. Optional. See :class:`Interface`
192 Optional signal set. See :class:`Interface`.
194 Window alignment. Optional. See :class:`Interface`.
198 bus : :class:`Interface`
199 Bus providing access to subordinate buses.
201 def __init__(self
, *, addr_width
, data_width
, granularity
=None, features
=frozenset(),
203 self
.bus
= Interface(addr_width
=addr_width
, data_width
=data_width
,
204 granularity
=granularity
, features
=features
,
206 self
._map
= self
.bus
.memory_map
209 def align_to(self
, alignment
):
210 """Align the implicit address of the next window.
212 See :meth:`MemoryMap.align_to` for details.
214 return self
._map
.align_to(alignment
)
216 def add(self
, sub_bus
, *, addr
=None, sparse
=False):
217 """Add a window to a subordinate bus.
219 The decoder can perform either sparse or dense address translation. If dense address
220 translation is used (the default), the subordinate bus must have the same data width as
221 the decoder; the window will be contiguous. If sparse address translation is used,
222 the subordinate bus may have data width less than the data width of the decoder;
223 the window may be discontiguous. In either case, the granularity of the subordinate bus
224 must be equal to or less than the granularity of the decoder.
226 See :meth:`MemoryMap.add_resource` for details.
228 if not isinstance(sub_bus
, Interface
):
229 raise TypeError("Subordinate bus must be an instance of wishbone.Interface, not {!r}"
231 if sub_bus
.granularity
> self
.bus
.granularity
:
232 raise ValueError("Subordinate bus has granularity {}, which is greater than the "
233 "decoder granularity {}"
234 .format(sub_bus
.granularity
, self
.bus
.granularity
))
236 if sub_bus
.data_width
!= self
.bus
.data_width
:
237 raise ValueError("Subordinate bus has data width {}, which is not the same as "
238 "decoder data width {} (required for dense address translation)"
239 .format(sub_bus
.data_width
, self
.bus
.data_width
))
241 if sub_bus
.granularity
!= sub_bus
.data_width
:
242 raise ValueError("Subordinate bus has data width {}, which is not the same as "
243 "subordinate bus granularity {} (required for sparse address "
245 .format(sub_bus
.data_width
, sub_bus
.granularity
))
246 for opt_output
in {"err", "rty", "stall"}:
247 if hasattr(sub_bus
, opt_output
) and not hasattr(self
.bus
, opt_output
):
248 raise ValueError("Subordinate bus has optional output {!r}, but the decoder "
249 "does not have a corresponding input"
252 self
._subs
[sub_bus
.memory_map
] = sub_bus
253 return self
._map
.add_window(sub_bus
.memory_map
, addr
=addr
, sparse
=sparse
)
255 def elaborate(self
, platform
):
263 with m
.Switch(self
.bus
.adr
):
264 for sub_map
, (sub_pat
, sub_ratio
) in self
._map
.window_patterns():
265 sub_bus
= self
._subs
[sub_map
]
268 sub_bus
.adr
.eq(self
.bus
.adr
<< log2_int(sub_ratio
)),
269 sub_bus
.dat_w
.eq(self
.bus
.dat_w
),
270 sub_bus
.sel
.eq(Cat(Repl(sel
, sub_ratio
) for sel
in self
.bus
.sel
)),
271 sub_bus
.we
.eq(self
.bus
.we
),
272 sub_bus
.stb
.eq(self
.bus
.stb
),
274 if hasattr(sub_bus
, "lock"):
275 m
.d
.comb
+= sub_bus
.lock
.eq(getattr(self
.bus
, "lock", 0))
276 if hasattr(sub_bus
, "cti"):
277 m
.d
.comb
+= sub_bus
.cti
.eq(getattr(self
.bus
, "cti", CycleType
.CLASSIC
))
278 if hasattr(sub_bus
, "bte"):
279 m
.d
.comb
+= sub_bus
.bte
.eq(getattr(self
.bus
, "bte", BurstTypeExt
.LINEAR
))
281 with m
.Case(sub_pat
[:-log2_int(self
.bus
.data_width
// self
.bus
.granularity
)]):
283 sub_bus
.cyc
.eq(self
.bus
.cyc
),
284 self
.bus
.dat_r
.eq(sub_bus
.dat_r
),
287 ack_fanin |
= sub_bus
.ack
288 if hasattr(sub_bus
, "err"):
289 err_fanin |
= sub_bus
.err
290 if hasattr(sub_bus
, "rty"):
291 rty_fanin |
= sub_bus
.rty
292 if hasattr(sub_bus
, "stall"):
293 stall_fanin |
= sub_bus
.stall
295 m
.d
.comb
+= self
.bus
.ack
.eq(ack_fanin
)
296 if hasattr(self
.bus
, "err"):
297 m
.d
.comb
+= self
.bus
.err
.eq(err_fanin
)
298 if hasattr(self
.bus
, "rty"):
299 m
.d
.comb
+= self
.bus
.rty
.eq(rty_fanin
)
300 if hasattr(self
.bus
, "stall"):
301 m
.d
.comb
+= self
.bus
.stall
.eq(stall_fanin
)
306 class Arbiter(Elaboratable
):
307 """Wishbone bus arbiter.
309 An arbiter for initiators (masters) to access a shared Wishbone bus.
314 Address width of the shared bus. See :class:`Interface`.
316 Data width of the shared bus. See :class:`Interface`.
317 granularity : int or None
318 Granularity of the shared bus. Optional. See :class:`Interface`.
320 Optional signal set for the shared bus. See :class:`Interface`.
322 Method for bus arbitration. Optional and defaults to "rr" (Round Robin, see
323 :class:`scheduler.RoundRobin`).
327 bus : :class:`Interface`
328 Shared bus to which the selected initiator gains access.
330 def __init__(self
, *, addr_width
, data_width
, granularity
=None, features
=frozenset(),
332 self
.bus
= Interface(addr_width
=addr_width
, data_width
=data_width
,
333 granularity
=granularity
, features
=features
)
335 if scheduler
not in ["rr"]:
336 raise ValueError("Scheduling mode must be \"rr\", not {!r}"
338 self
._scheduler
= scheduler
340 def add(self
, itor_bus
):
341 """Add an initiator bus to the arbiter.
343 The initiator bus must have the same address width and data width as the arbiter. The
344 granularity of the initiator bus must be greater than or equal to the granularity of
347 if not isinstance(itor_bus
, Interface
):
348 raise TypeError("Initiator bus must be an instance of wishbone.Interface, not {!r}"
350 if itor_bus
.addr_width
!= self
.bus
.addr_width
:
351 raise ValueError("Initiator bus has address width {}, which is not the same as "
352 "arbiter address width {}"
353 .format(itor_bus
.addr_width
, self
.bus
.addr_width
))
354 if itor_bus
.granularity
< self
.bus
.granularity
:
355 raise ValueError("Initiator bus has granularity {}, which is lesser than the "
356 "arbiter granularity {}"
357 .format(itor_bus
.granularity
, self
.bus
.granularity
))
358 if itor_bus
.data_width
!= self
.bus
.data_width
:
359 raise ValueError("Initiator bus has data width {}, which is not the same as "
360 "arbiter data width {}"
361 .format(itor_bus
.data_width
, self
.bus
.data_width
))
362 for opt_output
in {"lock", "cti", "bte"}:
363 if hasattr(itor_bus
, opt_output
) and not hasattr(self
.bus
, opt_output
):
364 raise ValueError("Initiator bus has optional output {!r}, but the arbiter "
365 "does not have a corresponding input"
368 self
._itors
.append(itor_bus
)
370 def elaborate(self
, platform
):
373 if self
._scheduler
== "rr":
374 m
.submodules
.scheduler
= scheduler
= RoundRobin(len(self
._itors
))
375 grant
= Signal(range(len(self
._itors
)))
377 # CYC should not be indefinitely asserted. (See RECOMMENDATION 3.05, Wishbone B4)
378 bus_busy
= self
.bus
.cyc
379 if hasattr(self
.bus
, "lock"):
380 # If LOCK is not asserted, we also wait for STB to be deasserted before granting bus
381 # ownership to the next initiator. If we didn't, the next bus owner could receive
382 # an ACK (or ERR, RTY) from the previous transaction when targeting the same
384 bus_busy
&= self
.bus
.lock | self
.bus
.stb
387 scheduler
.stb
.eq(~bus_busy
),
388 grant
.eq(scheduler
.grant
),
389 scheduler
.request
.eq(Cat(itor_bus
.cyc
for itor_bus
in self
._itors
))
392 with m
.Switch(grant
):
393 for i
, itor_bus
in enumerate(self
._itors
):
394 m
.d
.comb
+= itor_bus
.dat_r
.eq(self
.bus
.dat_r
)
395 if hasattr(itor_bus
, "stall"):
396 itor_bus_stall
= Signal(reset
=1)
397 m
.d
.comb
+= itor_bus
.stall
.eq(itor_bus_stall
)
400 ratio
= itor_bus
.granularity
// self
.bus
.granularity
402 self
.bus
.adr
.eq(itor_bus
.adr
),
403 self
.bus
.dat_w
.eq(itor_bus
.dat_w
),
404 self
.bus
.sel
.eq(Cat(Repl(sel
, ratio
) for sel
in itor_bus
.sel
)),
405 self
.bus
.we
.eq(itor_bus
.we
),
406 self
.bus
.stb
.eq(itor_bus
.stb
),
408 m
.d
.comb
+= self
.bus
.cyc
.eq(itor_bus
.cyc
)
409 if hasattr(self
.bus
, "lock"):
410 m
.d
.comb
+= self
.bus
.lock
.eq(getattr(itor_bus
, "lock", 1))
411 if hasattr(self
.bus
, "cti"):
412 m
.d
.comb
+= self
.bus
.cti
.eq(getattr(itor_bus
, "cti", CycleType
.CLASSIC
))
413 if hasattr(self
.bus
, "bte"):
414 m
.d
.comb
+= self
.bus
.bte
.eq(getattr(itor_bus
, "bte", BurstTypeExt
.LINEAR
))
416 m
.d
.comb
+= itor_bus
.ack
.eq(self
.bus
.ack
)
417 if hasattr(itor_bus
, "err"):
418 m
.d
.comb
+= itor_bus
.err
.eq(getattr(self
.bus
, "err", 0))
419 if hasattr(itor_bus
, "rty"):
420 m
.d
.comb
+= itor_bus
.rty
.eq(getattr(self
.bus
, "rty", 0))
421 if hasattr(itor_bus
, "stall"):
422 m
.d
.comb
+= itor_bus_stall
.eq(getattr(self
.bus
, "stall", ~self
.bus
.ack
))
427 class InterconnectShared(Elaboratable
):
428 """Wishbone bus interconnect module.
430 This is initialised using the following components:
431 (1) A shared Wishbone bus connecting multiple initiators (MASTERs) with
432 multiple targeted SLAVEs;
433 (2) A list of initiator Wishbone busses; and
434 (3) A list of SLAVE Wishbone busses targeted by the MASTERs.
436 This instantiates the following components:
437 (1) An arbiter (:class:`Arbiter`) controlling access of
438 multiple MASTERs to the shared bus; and
439 (2) A decoder (:class:`Decoder`) specifying which targeted SLAVE is to be accessed
440 using address translation on the shared bus.
442 See Section 8.2.3 of Wishbone B4 for implemenation specifications.
447 Address width of the shared bus. See :class:`Interface`.
449 Data width of the shared bus. See :class:`Interface`.
450 itors : list of (:class:`Interface` OR :class:`Record`)
451 List of MASTERs on the arbiter to request access to the shared bus.
452 If the item is a :class:`Record`, its fields must be named using the
453 convention of :class:`Interface`.
454 targets : list of (:class:`Interface` OR tuple of (:class:`Interface`, int))
455 List of SLAVEs on the decoder whose accesses are to be targeted by the shared bus.
456 If the item is a tuple of (intf, addr), the :class:`Interface`-type intf is added
457 at the (address width + granularity bits)-wide address of the int-type addr.
458 granularity : int or None
459 Granularity of the shared bus. Optional. See :class:`Interface`.
461 Optional signal set for the shared bus. See :class:`Interface`.
463 Method for bus arbitration for the arbiter. Optional and defaults to "rr" (Round Robin, see
464 :class:`scheduler.RoundRobin`).
466 Window alignment for the decoder. Optional. See :class:`Interface`.
470 arbiter : :class:`Arbiter`
471 The arbiter that connects the list of MASTERs to a shared bus.
472 decoder : :class:`Decoder`
473 The decoder that connects the shared bus to the list of SLAVEs.
475 def __init__(self
, *, addr_width
, data_width
, itors
, targets
, **kwargs
):
476 self
.addr_width
= addr_width
477 self
.data_width
= data_width
482 self
._itors
_convert
_stmts
= []
483 for itor_bus
in itors
:
484 if isinstance(itor_bus
, Interface
):
485 self
._itors
.append(itor_bus
)
486 elif isinstance(itor_bus
, Record
):
487 master_interface
= Interface
.from_pure_record(itor_bus
)
488 self
._itors
_convert
_stmts
.append(
489 itor_bus
.connect(master_interface
)
491 self
._itors
.append(master_interface
)
493 raise TypeError("Master {!r} must be a Wishbone interface"
496 for target_bus
in targets
:
497 self
._targets
.append(target_bus
)
499 arbiter_kwargs
= dict()
500 for name
in ["granularity", "features", "scheduler"]:
502 arbiter_kwargs
[name
] = kwargs
[name
]
503 self
.arbiter
= Arbiter(
504 addr_width
=self
.addr_width
, data_width
=self
.data_width
, **arbiter_kwargs
506 self
.arbiter
.bus
.name
= "arbiter_shared"
507 for itor_bus
in self
._itors
:
508 self
.arbiter
.add(itor_bus
)
510 decoder_kwargs
= dict()
511 for name
in ["granularity", "features", "alignment"]:
513 decoder_kwargs
[name
] = kwargs
[name
]
514 self
.decoder
= Decoder(
515 addr_width
=self
.addr_width
, data_width
=self
.data_width
, **decoder_kwargs
517 self
.decoder
.bus
.name
= "decoder_shared"
518 for item
in self
._targets
:
519 if isinstance(item
, Interface
):
520 self
.decoder
.add(item
)
521 elif isinstance(item
, tuple) and len(item
) == 2:
522 self
.decoder
.add(item
[0], addr
=item
[1])
524 raise TypeError("Target must be a Wishbone interface, "
525 "or a (Wishbone interface, start address) tuple, not {!r}"
528 def elaborate(self
, platform
):
531 m
.submodules
.arbiter
= self
.arbiter
532 m
.submodules
.decoder
= self
.decoder
535 self
._itors
_convert
_stmts
+
536 self
.arbiter
.bus
.connect(self
.decoder
.bus
)