must not delay ack to wb request in SRAM
[nmigen-soc.git] / nmigen_soc / wishbone / bus.py
1 from enum import Enum
2 from nmigen import Record, Elaboratable, Module, Signal, Cat, Repl
3 from nmigen.hdl.rec import Direction
4 from nmigen.utils import log2_int
5
6 from nmigen_soc.memory import MemoryMap
7 from nmigen_soc.scheduler import RoundRobin
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, src_loc_at=0):
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,
229 features=None,
230 alignment=0):
231 if features is None:
232 features = frozenset()
233 self.bus = Interface(addr_width=addr_width, data_width=data_width,
234 granularity=granularity, features=features,
235 alignment=alignment)
236 self._map = self.bus.memory_map
237 self._subs = dict()
238
239 def align_to(self, alignment):
240 """Align the implicit address of the next window.
241
242 See :meth:`MemoryMap.align_to` for details.
243 """
244 return self._map.align_to(alignment)
245
246 def add(self, sub_bus, *, addr=None, sparse=False):
247 """Add a window to a subordinate bus.
248
249 The decoder can perform either sparse or dense address
250 translation. If dense address translation is used (the default),
251 the subordinate bus must have the same data width as the decoder;
252 the window will be contiguous. If sparse address translation is
253 used, the subordinate bus may have data width less than the data
254 width of the decoder; the window may be discontiguous. In either
255 case, the granularity of the subordinate bus must be equal to
256 or less than the granularity of the decoder.
257
258 See :meth:`MemoryMap.add_resource` for details.
259 """
260 if not isinstance(sub_bus, Interface):
261 raise TypeError("Subordinate bus must be an instance of "
262 "wishbone.Interface, not {!r}".format(sub_bus))
263 if sub_bus.granularity > self.bus.granularity:
264 raise ValueError("Subordinate bus has granularity {}, "
265 "which is greater than the "
266 "decoder granularity {}"
267 .format(sub_bus.granularity, self.bus.granularity))
268 if not sparse:
269 if sub_bus.data_width != self.bus.data_width:
270 raise ValueError("Subordinate bus has data width {}, "
271 "which is not the same as "
272 "decoder data width {} (required "
273 "for dense address translation)"
274 .format(sub_bus.data_width,
275 self.bus.data_width))
276 else:
277 if sub_bus.granularity != sub_bus.data_width:
278 raise ValueError("Subordinate bus has data width {}, "
279 "which is not the same as "
280 "subordinate bus granularity {} "
281 "(required for sparse address "
282 "translation)"
283 .format(sub_bus.data_width,
284 sub_bus.granularity))
285 for opt_output in {"err", "rty", "stall"}:
286 if hasattr(sub_bus, opt_output) and not hasattr(
287 self.bus, opt_output):
288 raise ValueError("Subordinate bus has optional output {!r}, "
289 "but the decoder "
290 "does not have a corresponding input"
291 .format(opt_output))
292
293 self._subs[sub_bus.memory_map] = sub_bus
294 return self._map.add_window(
295 sub_bus.memory_map, addr=addr, sparse=sparse)
296
297 def elaborate(self, platform):
298 m = Module()
299
300 ack_fanin = 0
301 err_fanin = 0
302 rty_fanin = 0
303 stall_fanin = 0
304
305 with m.Switch(self.bus.adr):
306 for sub_map, (sub_pat, sub_ratio) in self._map.window_patterns():
307 sub_bus = self._subs[sub_map]
308
309 m.d.comb += [
310 sub_bus.adr.eq(self.bus.adr << log2_int(sub_ratio)),
311 sub_bus.dat_w.eq(self.bus.dat_w),
312 sub_bus.sel.eq(Cat(Repl(sel, sub_ratio)
313 for sel in self.bus.sel)),
314 sub_bus.we.eq(self.bus.we),
315 sub_bus.stb.eq(self.bus.stb),
316 ]
317 if hasattr(sub_bus, "lock"):
318 m.d.comb += sub_bus.lock.eq(getattr(self.bus, "lock", 0))
319 if hasattr(sub_bus, "cti"):
320 m.d.comb += sub_bus.cti.eq(getattr(self.bus,
321 "cti",
322 CycleType.CLASSIC))
323 if hasattr(sub_bus, "bte"):
324 m.d.comb += sub_bus.bte.eq(getattr(self.bus,
325 "bte",
326 BurstTypeExt.LINEAR))
327
328 with m.Case(sub_pat[:-log2_int(self.bus.data_width //
329 self.bus.granularity)]):
330 m.d.comb += [
331 sub_bus.cyc.eq(self.bus.cyc),
332 self.bus.dat_r.eq(sub_bus.dat_r),
333 ]
334
335 ack_fanin |= sub_bus.ack
336 if hasattr(sub_bus, "err"):
337 err_fanin |= sub_bus.err
338 if hasattr(sub_bus, "rty"):
339 rty_fanin |= sub_bus.rty
340 if hasattr(sub_bus, "stall"):
341 stall_fanin |= sub_bus.stall
342
343 m.d.comb += self.bus.ack.eq(ack_fanin)
344 if hasattr(self.bus, "err"):
345 m.d.comb += self.bus.err.eq(err_fanin)
346 if hasattr(self.bus, "rty"):
347 m.d.comb += self.bus.rty.eq(rty_fanin)
348 if hasattr(self.bus, "stall"):
349 m.d.comb += self.bus.stall.eq(stall_fanin)
350
351 return m
352
353
354 class Arbiter(Elaboratable):
355 """Wishbone bus arbiter.
356
357 An arbiter for initiators (masters) to access a shared Wishbone bus.
358
359 Parameters
360 ----------
361 addr_width : int
362 Address width of the shared bus. See :class:`Interface`.
363 data_width : int
364 Data width of the shared bus. See :class:`Interface`.
365 granularity : int or None
366 Granularity of the shared bus. Optional. See :class:`Interface`.
367 features : iter(str)
368 Optional signal set for the shared bus. See :class:`Interface`.
369 scheduler : str
370 Method for bus arbitration. Optional and defaults to "rr"
371 (Round Robin, see :class:`scheduler.RoundRobin`).
372
373 Attributes
374 ----------
375 bus : :class:`Interface`
376 Shared bus to which the selected initiator gains access.
377 """
378
379 def __init__(self, *, addr_width, data_width, granularity=None,
380 features=None,
381 scheduler="rr"):
382 if features is None:
383 features = frozenset()
384 self.bus = Interface(addr_width=addr_width, data_width=data_width,
385 granularity=granularity, features=features)
386 self._itors = []
387 if scheduler not in ["rr"]:
388 raise ValueError("Scheduling mode must be \"rr\", not {!r}"
389 .format(scheduler))
390 self._scheduler = scheduler
391
392 def add(self, itor_bus):
393 """Add an initiator bus to the arbiter.
394
395 The initiator bus must have the same address width and data
396 width as the arbiter. The granularity of the initiator bus must
397 be greater than or equal to the granularity of the arbiter.
398 """
399 if not isinstance(itor_bus, Interface):
400 raise TypeError("Initiator bus must be an instance of "
401 "wishbone.Interface, not {!r}".format(itor_bus))
402 if itor_bus.addr_width != self.bus.addr_width:
403 raise ValueError("Initiator bus has address width {}, "
404 "which is not the same as "
405 "arbiter address width {}"
406 .format(itor_bus.addr_width, self.bus.addr_width))
407 if itor_bus.granularity < self.bus.granularity:
408 raise ValueError("Initiator bus has granularity {}, "
409 "which is lesser than the "
410 "arbiter granularity {}"
411 .format(itor_bus.granularity, self.bus.granularity))
412 if itor_bus.data_width != self.bus.data_width:
413 raise ValueError("Initiator bus has data width {}, "
414 "which is not the same as "
415 "arbiter data width {}"
416 .format(itor_bus.data_width, self.bus.data_width))
417 for opt_output in {"lock", "cti", "bte"}:
418 if hasattr(itor_bus, opt_output) and not hasattr(
419 self.bus, opt_output):
420 raise ValueError("Initiator bus has optional output {!r}, "
421 "but the arbiter "
422 "does not have a corresponding input"
423 .format(opt_output))
424
425 self._itors.append(itor_bus)
426
427 def elaborate(self, platform):
428 m = Module()
429
430 if self._scheduler == "rr":
431 m.submodules.scheduler = scheduler = RoundRobin(len(self._itors))
432 grant = Signal(range(len(self._itors)))
433
434 # CYC should not be indefinitely asserted. (See RECOMMENDATION 3.05,
435 # Wishbone B4)
436 bus_busy = self.bus.cyc
437 if hasattr(self.bus, "lock"):
438 # If LOCK is not asserted, we also wait for STB to be
439 # deasserted before granting bus ownership to the next
440 # initiator. If we didn't, the next bus owner could receive
441 # an ACK (or ERR, RTY) from the previous transaction when
442 # targeting the same peripheral.
443 bus_busy &= self.bus.lock | self.bus.stb
444
445 m.d.comb += [
446 scheduler.stb.eq(~bus_busy),
447 grant.eq(scheduler.grant),
448 scheduler.request.eq(Cat(itor_bus.cyc for itor_bus in self._itors))
449 ]
450
451 with m.Switch(grant):
452 for i, itor_bus in enumerate(self._itors):
453 m.d.comb += itor_bus.dat_r.eq(self.bus.dat_r)
454 if hasattr(itor_bus, "stall"):
455 itor_bus_stall = Signal(reset=1)
456 m.d.comb += itor_bus.stall.eq(itor_bus_stall)
457
458 with m.Case(i):
459 ratio = itor_bus.granularity // self.bus.granularity
460 m.d.comb += [
461 self.bus.adr.eq(itor_bus.adr),
462 self.bus.dat_w.eq(itor_bus.dat_w),
463 self.bus.sel.eq(Cat(Repl(sel, ratio)
464 for sel in itor_bus.sel)),
465 self.bus.we.eq(itor_bus.we),
466 self.bus.stb.eq(itor_bus.stb),
467 ]
468 m.d.comb += self.bus.cyc.eq(itor_bus.cyc)
469 if hasattr(self.bus, "lock"):
470 m.d.comb += self.bus.lock.eq(
471 getattr(itor_bus, "lock", 1))
472 if hasattr(self.bus, "cti"):
473 m.d.comb += self.bus.cti.eq(
474 getattr(itor_bus, "cti", CycleType.CLASSIC))
475 if hasattr(self.bus, "bte"):
476 m.d.comb += self.bus.bte.eq(
477 getattr(itor_bus, "bte", BurstTypeExt.LINEAR))
478
479 m.d.comb += itor_bus.ack.eq(self.bus.ack)
480 if hasattr(itor_bus, "err"):
481 m.d.comb += itor_bus.err.eq(
482 getattr(self.bus, "err", 0))
483 if hasattr(itor_bus, "rty"):
484 m.d.comb += itor_bus.rty.eq(
485 getattr(self.bus, "rty", 0))
486 if hasattr(itor_bus, "stall"):
487 m.d.comb += itor_bus_stall.eq(
488 getattr(self.bus, "stall", ~self.bus.ack))
489
490 return m
491
492
493 class InterconnectShared(Elaboratable):
494 """Wishbone bus interconnect module.
495
496 This is initialised using the following components:
497 (1) A shared Wishbone bus connecting multiple initiators (MASTERs) with
498 multiple targeted SLAVEs;
499 (2) A list of initiator Wishbone busses; and
500 (3) A list of SLAVE Wishbone busses targeted by the MASTERs.
501
502 This instantiates the following components:
503 (1) An arbiter (:class:`Arbiter`) controlling access of
504 multiple MASTERs to the shared bus; and
505 (2) A decoder (:class:`Decoder`) specifying which targeted SLAVE is
506 to be accessed using address translation on the shared bus.
507
508 See Section 8.2.3 of Wishbone B4 for implemenation specifications.
509
510 Parameters
511 ----------
512 addr_width : int
513 Address width of the shared bus. See :class:`Interface`.
514 data_width : int
515 Data width of the shared bus. See :class:`Interface`.
516 itors : list of (:class:`Interface` OR :class:`Record`)
517 List of MASTERs on the arbiter to request access to the shared bus.
518 If the item is a :class:`Record`, its fields must be named using the
519 convention of :class:`Interface`.
520 targets : list of (:class:`Interface` OR tuple of (:class:`Interface`, int))
521 List of SLAVEs on the decoder whose accesses are to be targeted
522 by the shared bus. If the item is a tuple of (intf, addr), the
523 :class:`Interface`-type intf is added at the (address width +
524 granularity bits)-wide address of the int-type addr.
525 granularity : int or None
526 Granularity of the shared bus. Optional. See :class:`Interface`.
527 features : iter(str)
528 Optional signal set for the shared bus. See :class:`Interface`.
529 scheduler : str
530 Method for bus arbitration for the arbiter. Optional and defaults
531 to "rr" (Round Robin, see :class:`scheduler.RoundRobin`).
532 alignment : int
533 Window alignment for the decoder. Optional. See :class:`Interface`.
534
535 Attributes
536 ----------
537 arbiter : :class:`Arbiter`
538 The arbiter that connects the list of MASTERs to a shared bus.
539 decoder : :class:`Decoder`
540 The decoder that connects the shared bus to the list of SLAVEs.
541 """
542
543 def __init__(self, *, addr_width, data_width, itors, targets, **kwargs):
544 self.addr_width = addr_width
545 self.data_width = data_width
546
547 self._itors = []
548 self._targets = []
549
550 self._itors_convert_stmts = []
551 for itor_bus in itors:
552 if isinstance(itor_bus, Interface):
553 self._itors.append(itor_bus)
554 elif isinstance(itor_bus, Record):
555 master_interface = Interface.from_pure_record(itor_bus)
556 self._itors_convert_stmts.append(
557 itor_bus.connect(master_interface)
558 )
559 self._itors.append(master_interface)
560 else:
561 raise TypeError("Master {!r} must be a Wishbone interface"
562 .format(itor_bus))
563
564 for target_bus in targets:
565 self._targets.append(target_bus)
566
567 arbiter_kwargs = dict()
568 for name in ["granularity", "features", "scheduler"]:
569 if name in kwargs:
570 arbiter_kwargs[name] = kwargs[name]
571 self.arbiter = Arbiter(
572 addr_width=self.addr_width, data_width=self.data_width,
573 **arbiter_kwargs
574 )
575 self.arbiter.bus.name = "arbiter_shared"
576 for itor_bus in self._itors:
577 self.arbiter.add(itor_bus)
578
579 decoder_kwargs = dict()
580 for name in ["granularity", "features", "alignment"]:
581 if name in kwargs:
582 decoder_kwargs[name] = kwargs[name]
583 self.decoder = Decoder(addr_width=self.addr_width,
584 data_width=self.data_width,
585 **decoder_kwargs)
586 self.decoder.bus.name = "decoder_shared"
587 for item in self._targets:
588 if isinstance(item, Interface):
589 self.decoder.add(item)
590 elif isinstance(item, tuple) and len(item) == 2:
591 self.decoder.add(item[0], addr=item[1])
592 else:
593 raise TypeError("Target must be a Wishbone interface, "
594 "or a (Wishbone interface, "
595 "start address) tuple, not {!r}"
596 .format(item))
597
598 def elaborate(self, platform):
599 m = Module()
600
601 m.submodules.arbiter = self.arbiter
602 m.submodules.decoder = self.decoder
603
604 m.d.comb += (
605 self._itors_convert_stmts +
606 self.arbiter.bus.connect(self.decoder.bus)
607 )
608
609 return m