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