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