wishbone: fix docstring & unneeded parameter for InterconnectShared
[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 <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.
36
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.
40
41 Parameters
42 ----------
43 addr_width : int
44 Width of the address signal.
45 data_width : int
46 Width of the data signals ("port size" in Wishbone terminology).
47 One of 8, 16, 32, 64.
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
51 to the address width.
52 features : iter(str)
53 Selects the optional signals that will be a part of this interface.
54 alignment : int
55 Resource and window alignment. Optional. See :class:`MemoryMap`.
56 name : str
57 Name of the underlying record.
58
59 Attributes
60 ----------
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.
63
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).
72 cyc : Signal()
73 Corresponds to Wishbone signal ``CYC_O`` (initiator) or ``CYC_I`` (target).
74 stb : Signal()
75 Corresponds to Wishbone signal ``STB_O`` (initiator) or ``STB_I`` (target).
76 we : Signal()
77 Corresponds to Wishbone signal ``WE_O`` (initiator) or ``WE_I`` (target).
78 ack : Signal()
79 Corresponds to Wishbone signal ``ACK_I`` (initiator) or ``ACK_O`` (target).
80 err : Signal()
81 Optional. Corresponds to Wishbone signal ``ERR_I`` (initiator) or ``ERR_O`` (target).
82 rty : Signal()
83 Optional. Corresponds to Wishbone signal ``RTY_I`` (initiator) or ``RTY_O`` (target).
84 stall : Signal()
85 Optional. Corresponds to Wishbone signal ``STALL_I`` (initiator) or ``STALL_O`` (target).
86 lock : Signal()
87 Optional. Corresponds to Wishbone signal ``LOCK_O`` (initiator) or ``LOCK_I`` (target).
88 cti : Signal()
89 Optional. Corresponds to Wishbone signal ``CTI_O`` (initiator) or ``CTI_I`` (target).
90 bte : Signal()
91 Optional. Corresponds to Wishbone signal ``BTE_O`` (initiator) or ``BTE_I`` (target).
92 """
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}"
97 .format(addr_width))
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}"
100 .format(data_width))
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,
116 alignment=alignment)
117
118 self._features = set(features)
119 unknown = self._features - {"rty", "err", "stall", "lock", "cti", "bte"}
120 if unknown:
121 raise ValueError("Optional signal(s) {} are not supported"
122 .format(", ".join(map(repr, unknown))))
123 layout = [
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),
132 ]
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)
146
147 @classmethod
148 def from_pure_record(cls, record):
149 """Instantiate a :class:`wishbone.Interface` from a simple :class:`Record`
150 """
151 if not isinstance(record, Record):
152 raise TypeError("{!r} is not a Record"
153 .format(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)
166 features = []
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,
173 features=features,
174 alignment=0,
175 name=record.name+"_intf")
176
177
178 class Decoder(Elaboratable):
179 """Wishbone bus decoder.
180
181 An address decoder for subordinate Wishbone buses.
182
183 Parameters
184 ----------
185 addr_width : int
186 Address width. See :class:`Interface`.
187 data_width : int
188 Data width. See :class:`Interface`.
189 granularity : int or None
190 Granularity. Optional. See :class:`Interface`
191 features : iter(str)
192 Optional signal set. See :class:`Interface`.
193 alignment : int
194 Window alignment. Optional. See :class:`Interface`.
195
196 Attributes
197 ----------
198 bus : :class:`Interface`
199 Bus providing access to subordinate buses.
200 """
201 def __init__(self, *, addr_width, data_width, granularity=None, features=frozenset(),
202 alignment=0):
203 self.bus = Interface(addr_width=addr_width, data_width=data_width,
204 granularity=granularity, features=features,
205 alignment=alignment)
206 self._map = self.bus.memory_map
207 self._subs = dict()
208
209 def align_to(self, alignment):
210 """Align the implicit address of the next window.
211
212 See :meth:`MemoryMap.align_to` for details.
213 """
214 return self._map.align_to(alignment)
215
216 def add(self, sub_bus, *, addr=None, sparse=False):
217 """Add a window to a subordinate bus.
218
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.
225
226 See :meth:`MemoryMap.add_resource` for details.
227 """
228 if not isinstance(sub_bus, Interface):
229 raise TypeError("Subordinate bus must be an instance of wishbone.Interface, not {!r}"
230 .format(sub_bus))
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))
235 if not sparse:
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))
240 else:
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 "
244 "translation)"
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"
250 .format(opt_output))
251
252 self._subs[sub_bus.memory_map] = sub_bus
253 return self._map.add_window(sub_bus.memory_map, addr=addr, sparse=sparse)
254
255 def elaborate(self, platform):
256 m = Module()
257
258 ack_fanin = 0
259 err_fanin = 0
260 rty_fanin = 0
261 stall_fanin = 0
262
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]
266
267 m.d.comb += [
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),
273 ]
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))
280
281 with m.Case(sub_pat[:-log2_int(self.bus.data_width // self.bus.granularity)]):
282 m.d.comb += [
283 sub_bus.cyc.eq(self.bus.cyc),
284 self.bus.dat_r.eq(sub_bus.dat_r),
285 ]
286
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
294
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)
302
303 return m
304
305
306 class Arbiter(Elaboratable):
307 """Wishbone bus arbiter.
308
309 An arbiter for initiators (masters) to access a shared Wishbone bus.
310
311 Parameters
312 ----------
313 addr_width : int
314 Address width of the shared bus. See :class:`Interface`.
315 data_width : int
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`.
319 features : iter(str)
320 Optional signal set for the shared bus. See :class:`Interface`.
321 scheduler : str
322 Method for bus arbitration. Optional and defaults to "rr" (Round Robin, see
323 :class:`scheduler.RoundRobin`).
324
325 Attributes
326 ----------
327 bus : :class:`Interface`
328 Shared bus to which the selected initiator gains access.
329 """
330 def __init__(self, *, addr_width, data_width, granularity=None, features=frozenset(),
331 scheduler="rr"):
332 self.bus = Interface(addr_width=addr_width, data_width=data_width,
333 granularity=granularity, features=features)
334 self._itors = []
335 if scheduler not in ["rr"]:
336 raise ValueError("Scheduling mode must be \"rr\", not {!r}"
337 .format(scheduler))
338 self._scheduler = scheduler
339
340 def add(self, itor_bus):
341 """Add an initiator bus to the arbiter.
342
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
345 the arbiter.
346 """
347 if not isinstance(itor_bus, Interface):
348 raise TypeError("Initiator bus must be an instance of wishbone.Interface, not {!r}"
349 .format(itor_bus))
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"
366 .format(opt_output))
367
368 self._itors.append(itor_bus)
369
370 def elaborate(self, platform):
371 m = Module()
372
373 if self._scheduler == "rr":
374 m.submodules.scheduler = scheduler = RoundRobin(len(self._itors))
375 grant = Signal(range(len(self._itors)))
376
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
383 # peripheral.
384 bus_busy &= self.bus.lock | self.bus.stb
385
386 m.d.comb += [
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))
390 ]
391
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)
398
399 with m.Case(i):
400 ratio = itor_bus.granularity // self.bus.granularity
401 m.d.comb += [
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),
407 ]
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))
415
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))
423
424 return m
425
426
427 class InterconnectShared(Elaboratable):
428 """Wishbone bus interconnect module.
429
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.
435
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.
441
442 See Section 8.2.3 of Wishbone B4 for implemenation specifications.
443
444 Parameters
445 ----------
446 addr_width : int
447 Address width of the shared bus. See :class:`Interface`.
448 data_width : int
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`.
460 features : iter(str)
461 Optional signal set for the shared bus. See :class:`Interface`.
462 scheduler : str
463 Method for bus arbitration for the arbiter. Optional and defaults to "rr" (Round Robin, see
464 :class:`scheduler.RoundRobin`).
465 alignment : int
466 Window alignment for the decoder. Optional. See :class:`Interface`.
467
468 Attributes
469 ----------
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.
474 """
475 def __init__(self, *, addr_width, data_width, itors, targets, **kwargs):
476 self.addr_width = addr_width
477 self.data_width = data_width
478
479 self._itors = []
480 self._targets = []
481
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)
490 )
491 self._itors.append(master_interface)
492 else:
493 raise TypeError("Master {!r} must be a Wishbone interface"
494 .format(itor_bus))
495
496 for target_bus in targets:
497 self._targets.append(target_bus)
498
499 arbiter_kwargs = dict()
500 for name in ["granularity", "features", "scheduler"]:
501 if name in kwargs:
502 arbiter_kwargs[name] = kwargs[name]
503 self.arbiter = Arbiter(
504 addr_width=self.addr_width, data_width=self.data_width, **arbiter_kwargs
505 )
506 self.arbiter.bus.name = "arbiter_shared"
507 for itor_bus in self._itors:
508 self.arbiter.add(itor_bus)
509
510 decoder_kwargs = dict()
511 for name in ["granularity", "features", "alignment"]:
512 if name in kwargs:
513 decoder_kwargs[name] = kwargs[name]
514 self.decoder = Decoder(
515 addr_width=self.addr_width, data_width=self.data_width, **decoder_kwargs
516 )
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])
523 else:
524 raise TypeError("Target must be a Wishbone interface, "
525 "or a (Wishbone interface, start address) tuple, not {!r}"
526 .format(item))
527
528 def elaborate(self, platform):
529 m = Module()
530
531 m.submodules.arbiter = self.arbiter
532 m.submodules.decoder = self.decoder
533
534 m.d.comb += (
535 self._itors_convert_stmts +
536 self.arbiter.bus.connect(self.decoder.bus)
537 )
538
539 return m