ad6d2d3332c55c193c4a9aec390545c9b99e646f
[nmigen-soc.git] / nmigen_soc / wishbone / bus.py
1 from enum import Enum
2 from nmigen import *
3 from nmigen.hdl.rec import Direction
4 from nmigen.utils import log2_int
5
6 from ..memory import MemoryMap
7 from ..scheduler import *
8
9
10 __all__ = ["CycleType", "BurstTypeExt", "Interface", "Decoder",
11 "Arbiter", "InterconnectShared"]
12
13
14 class CycleType(Enum):
15 """Wishbone Registered Feedback cycle type."""
16 CLASSIC = 0b000
17 CONST_BURST = 0b001
18 INCR_BURST = 0b010
19 END_OF_BURST = 0b111
20
21
22 class BurstTypeExt(Enum):
23 """Wishbone Registered Feedback burst type extension."""
24 LINEAR = 0b00
25 WRAP_4 = 0b01
26 WRAP_8 = 0b10
27 WRAP_16 = 0b11
28
29
30 class Interface(Record):
31 """Wishbone interface.
32
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.
37
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.
42
43 Parameters
44 ----------
45 addr_width : int
46 Width of the address signal.
47 data_width : int
48 Width of the data signals ("port size" in Wishbone terminology).
49 One of 8, 16, 32, 64.
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.
54 features : iter(str)
55 Selects the optional signals that will be a part of this interface.
56 alignment : int
57 Resource and window alignment. Optional. See :class:`MemoryMap`.
58 name : str
59 Name of the underlying record.
60
61 Attributes
62 ----------
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.
66
67 adr : Signal(addr_width)
68 Corresponds to Wishbone signal ``ADR_O`` (initiator) or ``ADR_I``
69 (target).
70 dat_w : Signal(data_width)
71 Corresponds to Wishbone signal ``DAT_O`` (initiator) or ``DAT_I``
72 (target).
73 dat_r : Signal(data_width)
74 Corresponds to Wishbone signal ``DAT_I`` (initiator) or ``DAT_O``
75 (target).
76 sel : Signal(data_width // granularity)
77 Corresponds to Wishbone signal ``SEL_O`` (initiator) or ``SEL_I``
78 (target).
79 cyc : Signal()
80 Corresponds to Wishbone signal ``CYC_O`` (initiator) or ``CYC_I``
81 (target).
82 stb : Signal()
83 Corresponds to Wishbone signal ``STB_O`` (initiator) or ``STB_I``
84 (target).
85 we : Signal()
86 Corresponds to Wishbone signal ``WE_O`` (initiator) or ``WE_I``
87 (target).
88 ack : Signal()
89 Corresponds to Wishbone signal ``ACK_I`` (initiator) or ``ACK_O``
90 (target).
91 err : Signal()
92 Optional. Corresponds to Wishbone signal ``ERR_I`` (initiator)
93 or ``ERR_O`` (target).
94 rty : Signal()
95 Optional. Corresponds to Wishbone signal ``RTY_I`` (initiator)
96 or ``RTY_O`` (target).
97 stall : Signal()
98 Optional. Corresponds to Wishbone signal ``STALL_I`` (initiator)
99 or ``STALL_O`` (target).
100 lock : Signal()
101 Optional. Corresponds to Wishbone signal ``LOCK_O`` (initiator)
102 or ``LOCK_I`` (target).
103 cti : Signal()
104 Optional. Corresponds to Wishbone signal ``CTI_O`` (initiator)
105 or ``CTI_I`` (target).
106 bte : Signal()
107 Optional. Corresponds to Wishbone signal ``BTE_O`` (initiator)
108 or ``BTE_I`` (target).
109 """
110
111 def __init__(self, *, addr_width, data_width, granularity=None,
112 features=None,
113 alignment=0, name=None):
114 if features is None:
115 features = frozenset()
116 if not isinstance(addr_width, int) or addr_width < 0:
117 raise ValueError("Address width must be a non-negative integer, "
118 "not {!r}" .format(addr_width))
119 if data_width not in (8, 16, 32, 64):
120 raise ValueError("Data width must be one of 8, 16, 32, 64, not {!r}"
121 .format(data_width))
122 if granularity is None:
123 granularity = data_width
124 elif granularity not in (8, 16, 32, 64):
125 raise ValueError("Granularity must be one of 8, 16, 32, 64, "
126 "not {!r}" .format(granularity))
127 if granularity > data_width:
128 raise ValueError("Granularity {} may not be greater than data "
129 "width {}" .format(granularity, data_width))
130 self.addr_width = addr_width
131 self.data_width = data_width
132 self.granularity = granularity
133 granularity_bits = log2_int(data_width // granularity)
134 self._alignment = alignment
135 self.memory_map = MemoryMap(addr_width=max(1, addr_width +
136 granularity_bits),
137 data_width=data_width >> granularity_bits,
138 alignment=alignment)
139
140 self._features = set(features)
141 unknown = self._features - \
142 {"rty", "err", "stall", "lock", "cti", "bte"}
143 if unknown:
144 raise ValueError("Optional signal(s) {} are not supported"
145 .format(", ".join(map(repr, unknown))))
146 layout = [
147 ("adr", addr_width, Direction.FANOUT),
148 ("dat_w", data_width, Direction.FANOUT),
149 ("dat_r", data_width, Direction.FANIN),
150 ("sel", data_width // granularity, Direction.FANOUT),
151 ("cyc", 1, Direction.FANOUT),
152 ("stb", 1, Direction.FANOUT),
153 ("we", 1, Direction.FANOUT),
154 ("ack", 1, Direction.FANIN),
155 ]
156 if "err" in features:
157 layout += [("err", 1, Direction.FANIN)]
158 if "rty" in features:
159 layout += [("rty", 1, Direction.FANIN)]
160 if "stall" in features:
161 layout += [("stall", 1, Direction.FANIN)]
162 if "lock" in features:
163 layout += [("lock", 1, Direction.FANOUT)]
164 if "cti" in features:
165 layout += [("cti", CycleType, Direction.FANOUT)]
166 if "bte" in features:
167 layout += [("bte", BurstTypeExt, Direction.FANOUT)]
168 super().__init__(layout, name=name, src_loc_at=1)
169
170 @classmethod
171 def from_pure_record(cls, record):
172 """Instantiate a :class:`wishbone.Interface`
173 from a simple :class:`Record`
174 """
175 if not isinstance(record, Record):
176 raise TypeError("{!r} is not a Record"
177 .format(record))
178 addr_width = len(record.adr)
179 if len(record.dat_w) != len(record.dat_r):
180 raise AttributeError("Record {!r} has {}-bit long \"dat_w\" "
181 "but {}-bit long \"dat_r\""
182 .format(record, len(record.dat_w),
183 len(record.dat_r)))
184 data_width = len(record.dat_w)
185 if data_width % len(record.sel) != 0:
186 raise AttributeError("Record {!r} has invalid granularity "
187 "value because "
188 "its data width is {}-bit long but "
189 "its \"sel\" is {}-bit long"
190 .format(record, data_width, len(record.sel)))
191 granularity = data_width // len(record.sel)
192 features = []
193 for signal_name in ["rty", "err", "stall", "lock", "cti", "bte"]:
194 if hasattr(record, signal_name):
195 features.append(signal_name)
196 return cls(addr_width=addr_width,
197 data_width=data_width,
198 granularity=granularity,
199 features=features,
200 alignment=0,
201 name=record.name + "_intf")
202
203
204 class Decoder(Elaboratable):
205 """Wishbone bus decoder.
206
207 An address decoder for subordinate Wishbone buses.
208
209 Parameters
210 ----------
211 addr_width : int
212 Address width. See :class:`Interface`.
213 data_width : int
214 Data width. See :class:`Interface`.
215 granularity : int or None
216 Granularity. Optional. See :class:`Interface`
217 features : iter(str)
218 Optional signal set. See :class:`Interface`.
219 alignment : int
220 Window alignment. Optional. See :class:`Interface`.
221
222 Attributes
223 ----------
224 bus : :class:`Interface`
225 Bus providing access to subordinate buses.
226 """
227
228 def __init__(self, *, addr_width, data_width, granularity=None, features=frozenset(),
229 alignment=0):
230 self.bus = Interface(addr_width=addr_width, data_width=data_width,
231 granularity=granularity, features=features,
232 alignment=alignment)
233 self._map = self.bus.memory_map
234 self._subs = dict()
235
236 def align_to(self, alignment):
237 """Align the implicit address of the next window.
238
239 See :meth:`MemoryMap.align_to` for details.
240 """
241 return self._map.align_to(alignment)
242
243 def add(self, sub_bus, *, addr=None, sparse=False):
244 """Add a window to a subordinate bus.
245
246 The decoder can perform either sparse or dense address
247 translation. If dense address translation is used (the default),
248 the subordinate bus must have the same data width as the decoder;
249 the window will be contiguous. If sparse address translation is
250 used, the subordinate bus may have data width less than the data
251 width of the decoder; the window may be discontiguous. In either
252 case, the granularity of the subordinate bus must be equal to
253 or less than the granularity of the decoder.
254
255 See :meth:`MemoryMap.add_resource` for details.
256 """
257 if not isinstance(sub_bus, Interface):
258 raise TypeError("Subordinate bus must be an instance of "
259 "wishbone.Interface, not {!r}" .format(sub_bus))
260 if sub_bus.granularity > self.bus.granularity:
261 raise ValueError("Subordinate bus has granularity {}, "
262 "which is greater than the "
263 "decoder granularity {}"
264 .format(sub_bus.granularity, self.bus.granularity))
265 if not sparse:
266 if sub_bus.data_width != self.bus.data_width:
267 raise ValueError("Subordinate bus has data width {}, "
268 "which is not the same as "
269 "decoder data width {} "
270 "(required for dense address translation)"
271 .format(sub_bus.data_width,
272 self.bus.data_width))
273 else:
274 if sub_bus.granularity != sub_bus.data_width:
275 raise ValueError("Subordinate bus has data width {}, "
276 "which is not the same as "
277 "subordinate bus granularity {} "
278 "(required for sparse address "
279 "translation)"
280 .format(sub_bus.data_width,
281 sub_bus.granularity))
282 for opt_output in {"err", "rty", "stall"}:
283 if hasattr(sub_bus, opt_output) and not hasattr(
284 self.bus, opt_output):
285 raise ValueError("Subordinate bus has optional output "
286 "{!r}, but the decoder "
287 "does not have a corresponding input"
288 .format(opt_output))
289
290 self._subs[sub_bus.memory_map] = sub_bus
291 return self._map.add_window(
292 sub_bus.memory_map, addr=addr, sparse=sparse)
293
294 def elaborate(self, platform):
295 m = Module()
296
297 ack_fanin = 0
298 err_fanin = 0
299 rty_fanin = 0
300 stall_fanin = 0
301
302 with m.Switch(self.bus.adr):
303 for sub_map, (sub_pat, sub_ratio) in self._map.window_patterns():
304 sub_bus = self._subs[sub_map]
305
306 m.d.comb += [
307 sub_bus.adr.eq(self.bus.adr << log2_int(sub_ratio)),
308 sub_bus.dat_w.eq(self.bus.dat_w),
309 sub_bus.sel.eq(Cat(Repl(sel, sub_ratio)
310 for sel in self.bus.sel)),
311 sub_bus.we.eq(self.bus.we),
312 sub_bus.stb.eq(self.bus.stb),
313 ]
314 if hasattr(sub_bus, "lock"):
315 m.d.comb += sub_bus.lock.eq(getattr(self.bus, "lock", 0))
316 if hasattr(sub_bus, "cti"):
317 m.d.comb += sub_bus.cti.eq(getattr(self.bus,
318 "cti",
319 CycleType.CLASSIC))
320 if hasattr(sub_bus, "bte"):
321 m.d.comb += sub_bus.bte.eq(getattr(self.bus,
322 "bte",
323 BurstTypeExt.LINEAR))
324
325 with m.Case(sub_pat[:-log2_int(self.bus.data_width //
326 self.bus.granularity)]):
327 m.d.comb += [
328 sub_bus.cyc.eq(self.bus.cyc),
329 self.bus.dat_r.eq(sub_bus.dat_r),
330 ]
331
332 ack_fanin |= sub_bus.ack
333 if hasattr(sub_bus, "err"):
334 err_fanin |= sub_bus.err
335 if hasattr(sub_bus, "rty"):
336 rty_fanin |= sub_bus.rty
337 if hasattr(sub_bus, "stall"):
338 stall_fanin |= sub_bus.stall
339
340 m.d.comb += self.bus.ack.eq(ack_fanin)
341 if hasattr(self.bus, "err"):
342 m.d.comb += self.bus.err.eq(err_fanin)
343 if hasattr(self.bus, "rty"):
344 m.d.comb += self.bus.rty.eq(rty_fanin)
345 if hasattr(self.bus, "stall"):
346 m.d.comb += self.bus.stall.eq(stall_fanin)
347
348 return m
349
350
351 class Arbiter(Elaboratable):
352 """Wishbone bus arbiter.
353
354 An arbiter for initiators (masters) to access a shared Wishbone bus.
355
356 Parameters
357 ----------
358 addr_width : int
359 Address width of the shared bus. See :class:`Interface`.
360 data_width : int
361 Data width of the shared bus. See :class:`Interface`.
362 granularity : int or None
363 Granularity of the shared bus. Optional. See :class:`Interface`.
364 features : iter(str)
365 Optional signal set for the shared bus. See :class:`Interface`.
366 scheduler : str
367 Method for bus arbitration. Optional and defaults to "rr"
368 (Round Robin, see :class:`scheduler.RoundRobin`).
369
370 Attributes
371 ----------
372 bus : :class:`Interface`
373 Shared bus to which the selected initiator gains access.
374 """
375
376 def __init__(self, *, addr_width, data_width, granularity=None, features=frozenset(),
377 scheduler="rr"):
378 self.bus = Interface(addr_width=addr_width, data_width=data_width,
379 granularity=granularity, features=features)
380 self._itors = []
381 if scheduler not in ["rr"]:
382 raise ValueError("Scheduling mode must be \"rr\", not {!r}"
383 .format(scheduler))
384 self._scheduler = scheduler
385
386 def add(self, itor_bus):
387 """Add an initiator bus to the arbiter.
388
389 The initiator bus must have the same address width and data
390 width as the arbiter. The granularity of the initiator bus must
391 be greater than or equal to the granularity of the arbiter.
392 """
393 if not isinstance(itor_bus, Interface):
394 raise TypeError("Initiator bus must be an instance of "
395 "wishbone.Interface, not {!r}".format(itor_bus))
396 if itor_bus.addr_width != self.bus.addr_width:
397 raise ValueError("Initiator bus has address width {}, "
398 "which is not the same as "
399 "arbiter address width {}"
400 .format(itor_bus.addr_width, self.bus.addr_width))
401 if itor_bus.granularity < self.bus.granularity:
402 raise ValueError("Initiator bus has granularity {}, "
403 "which is lesser than the "
404 "arbiter granularity {}"
405 .format(itor_bus.granularity, self.bus.granularity))
406 if itor_bus.data_width != self.bus.data_width:
407 raise ValueError("Initiator bus has data width {}, "
408 "which is not the same as "
409 "arbiter data width {}"
410 .format(itor_bus.data_width, self.bus.data_width))
411 for opt_output in {"lock", "cti", "bte"}:
412 if hasattr(itor_bus, opt_output) and not hasattr(
413 self.bus, opt_output):
414 raise ValueError("Initiator bus has optional output {!r}, "
415 "but the arbiter "
416 "does not have a corresponding input"
417 .format(opt_output))
418
419 self._itors.append(itor_bus)
420
421 def elaborate(self, platform):
422 m = Module()
423
424 if self._scheduler == "rr":
425 m.submodules.scheduler = scheduler = RoundRobin(len(self._itors))
426 grant = Signal(range(len(self._itors)))
427
428 # CYC should not be indefinitely asserted. (See RECOMMENDATION 3.05,
429 # Wishbone B4)
430 bus_busy = self.bus.cyc
431 if hasattr(self.bus, "lock"):
432 # If LOCK is not asserted, we also wait for STB to be
433 # deasserted before granting bus ownership to the next
434 # initiator. If we didn't, the next bus owner could receive
435 # an ACK (or ERR, RTY) from the previous transaction when
436 # targeting the same peripheral.
437 bus_busy &= self.bus.lock | self.bus.stb
438
439 m.d.comb += [
440 scheduler.stb.eq(~bus_busy),
441 grant.eq(scheduler.grant),
442 scheduler.request.eq(Cat(itor_bus.cyc for itor_bus in self._itors))
443 ]
444
445 with m.Switch(grant):
446 for i, itor_bus in enumerate(self._itors):
447 m.d.comb += itor_bus.dat_r.eq(self.bus.dat_r)
448 if hasattr(itor_bus, "stall"):
449 itor_bus_stall = Signal(reset=1)
450 m.d.comb += itor_bus.stall.eq(itor_bus_stall)
451
452 with m.Case(i):
453 ratio = itor_bus.granularity // self.bus.granularity
454 m.d.comb += [
455 self.bus.adr.eq(itor_bus.adr),
456 self.bus.dat_w.eq(itor_bus.dat_w),
457 self.bus.sel.eq(Cat(Repl(sel, ratio)
458 for sel in itor_bus.sel)),
459 self.bus.we.eq(itor_bus.we),
460 self.bus.stb.eq(itor_bus.stb),
461 ]
462 m.d.comb += self.bus.cyc.eq(itor_bus.cyc)
463 if hasattr(self.bus, "lock"):
464 m.d.comb += self.bus.lock.eq(
465 getattr(itor_bus, "lock", 1))
466 if hasattr(self.bus, "cti"):
467 m.d.comb += self.bus.cti.eq(
468 getattr(itor_bus, "cti", CycleType.CLASSIC))
469 if hasattr(self.bus, "bte"):
470 m.d.comb += self.bus.bte.eq(
471 getattr(itor_bus, "bte", BurstTypeExt.LINEAR))
472
473 m.d.comb += itor_bus.ack.eq(self.bus.ack)
474 if hasattr(itor_bus, "err"):
475 m.d.comb += itor_bus.err.eq(
476 getattr(self.bus, "err", 0))
477 if hasattr(itor_bus, "rty"):
478 m.d.comb += itor_bus.rty.eq(
479 getattr(self.bus, "rty", 0))
480 if hasattr(itor_bus, "stall"):
481 m.d.comb += itor_bus_stall.eq(
482 getattr(self.bus, "stall", ~self.bus.ack))
483
484 return m
485
486
487 class InterconnectShared(Elaboratable):
488 """Wishbone bus interconnect module.
489
490 This is initialised using the following components:
491 (1) A shared Wishbone bus connecting multiple initiators (MASTERs) with
492 multiple targeted SLAVEs;
493 (2) A list of initiator Wishbone busses; and
494 (3) A list of SLAVE Wishbone busses targeted by the MASTERs.
495
496 This instantiates the following components:
497 (1) An arbiter (:class:`Arbiter`) controlling access of
498 multiple MASTERs to the shared bus; and
499 (2) A decoder (:class:`Decoder`) specifying which targeted SLAVE is
500 to be accessed using address translation on the shared bus.
501
502 See Section 8.2.3 of Wishbone B4 for implemenation specifications.
503
504 Parameters
505 ----------
506 addr_width : int
507 Address width of the shared bus. See :class:`Interface`.
508 data_width : int
509 Data width of the shared bus. See :class:`Interface`.
510 itors : list of (:class:`Interface` OR :class:`Record`)
511 List of MASTERs on the arbiter to request access to the shared bus.
512 If the item is a :class:`Record`, its fields must be named using the
513 convention of :class:`Interface`.
514 targets : list of (:class:`Interface` OR tuple of (:class:`Interface`, int))
515 List of SLAVEs on the decoder whose accesses are to be targeted
516 by the shared bus. If the item is a tuple of (intf, addr), the
517 :class:`Interface`-type intf is added at the (address width +
518 granularity bits)-wide address of the int-type addr.
519 granularity : int or None
520 Granularity of the shared bus. Optional. See :class:`Interface`.
521 features : iter(str)
522 Optional signal set for the shared bus. See :class:`Interface`.
523 scheduler : str
524 Method for bus arbitration for the arbiter. Optional and defaults
525 to "rr" (Round Robin, see :class:`scheduler.RoundRobin`).
526 alignment : int
527 Window alignment for the decoder. Optional. See :class:`Interface`.
528
529 Attributes
530 ----------
531 arbiter : :class:`Arbiter`
532 The arbiter that connects the list of MASTERs to a shared bus.
533 decoder : :class:`Decoder`
534 The decoder that connects the shared bus to the list of SLAVEs.
535 """
536
537 def __init__(self, *, addr_width, data_width, itors, targets, **kwargs):
538 self.addr_width = addr_width
539 self.data_width = data_width
540
541 self._itors = []
542 self._targets = []
543
544 self._itors_convert_stmts = []
545 for itor_bus in itors:
546 if isinstance(itor_bus, Interface):
547 self._itors.append(itor_bus)
548 elif isinstance(itor_bus, Record):
549 master_interface = Interface.from_pure_record(itor_bus)
550 self._itors_convert_stmts.append(
551 itor_bus.connect(master_interface)
552 )
553 self._itors.append(master_interface)
554 else:
555 raise TypeError("Master {!r} must be a Wishbone interface"
556 .format(itor_bus))
557
558 for target_bus in targets:
559 self._targets.append(target_bus)
560
561 arbiter_kwargs = dict()
562 for name in ["granularity", "features", "scheduler"]:
563 if name in kwargs:
564 arbiter_kwargs[name] = kwargs[name]
565 self.arbiter = Arbiter(
566 addr_width=self.addr_width, data_width=self.data_width,
567 **arbiter_kwargs
568 )
569 self.arbiter.bus.name = "arbiter_shared"
570 for itor_bus in self._itors:
571 self.arbiter.add(itor_bus)
572
573 decoder_kwargs = dict()
574 for name in ["granularity", "features", "alignment"]:
575 if name in kwargs:
576 decoder_kwargs[name] = kwargs[name]
577 self.decoder = Decoder(addr_width=self.addr_width,
578 data_width=self.data_width,
579 **decoder_kwargs)
580 self.decoder.bus.name = "decoder_shared"
581 for item in self._targets:
582 if isinstance(item, Interface):
583 self.decoder.add(item)
584 elif isinstance(item, tuple) and len(item) == 2:
585 self.decoder.add(item[0], addr=item[1])
586 else:
587 raise TypeError("Target must be a Wishbone interface, "
588 "or a (Wishbone interface, "
589 "start address) tuple, not {!r}"
590 .format(item))
591
592 def elaborate(self, platform):
593 m = Module()
594
595 m.submodules.arbiter = self.arbiter
596 m.submodules.decoder = self.decoder
597
598 m.d.comb += (
599 self._itors_convert_stmts +
600 self.arbiter.bus.connect(self.decoder.bus)
601 )
602
603 return m