wishbone: fix that RoundRobin might assert CYC indefinitely
[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 selecting the Wishbone master from several devices.
309
310 Parameters
311 ----------
312 addr_width : int
313 Address width. See :class:`Interface`.
314 data_width : int
315 Data width. See :class:`Interface`.
316 granularity : int
317 Granularity. See :class:`Interface`.
318 features : iter(str)
319 Optional signal set. See :class:`Interface`.
320 alignment : int
321 Window alignment. See :class:`Interface`.
322
323 Attributes
324 ----------
325 bus : :class:`Interface`
326 Bus providing access to the selected master.
327 """
328 def __init__(self, *, addr_width, data_width, granularity=None, features=frozenset(),
329 alignment=0, scheduler="rr"):
330 self.bus = Interface(addr_width=addr_width, data_width=data_width,
331 granularity=granularity, features=features,
332 alignment=alignment)
333 self._masters = dict()
334 if scheduler not in ["rr"]:
335 raise ValueError("Scheduling mode must be \"rr\", not {!r}"
336 .format(scheduler))
337 self._scheduler = scheduler
338 self._next_index = 0
339
340 def add(self, master_bus):
341 """Add a device bus to the list of master candidates
342 """
343 if not isinstance(master_bus, Interface):
344 raise TypeError("Master bus must be an instance of wishbone.Interface, not {!r}"
345 .format(master_bus))
346 if master_bus.granularity != self.bus.granularity:
347 raise ValueError("Master bus has granularity {}, which is not the same as "
348 "arbiter granularity {}"
349 .format(master_bus.granularity, self.bus.granularity))
350 if master_bus.data_width != self.bus.data_width:
351 raise ValueError("Master bus has data width {}, which is not the same as "
352 "arbiter data width {})"
353 .format(master_bus.data_width, self.bus.data_width))
354 for opt_output in {"err", "rty", "stall"}:
355 if hasattr(master_bus, opt_output) and not hasattr(self.bus, opt_output):
356 raise ValueError("Master bus has optional output {!r}, but the arbiter "
357 "does not have a corresponding input"
358 .format(opt_output))
359
360 self._masters[master_bus.memory_map] = self._next_index, master_bus
361 self._next_index += 1
362
363 def elaborate(self, platform):
364 m = Module()
365
366 if self._scheduler == "rr":
367 m.submodules.scheduler = scheduler = RoundRobin(self._next_index)
368 grant = Signal(self._next_index)
369 m.d.comb += [
370 # CYC should not be indefinitely asserted. (See RECOMMENDATION 3.05, Wishbone B4)
371 scheduler.stb.eq(~self.bus.cyc),
372 grant.eq(scheduler.grant)
373 ]
374
375 for signal_name, (_, signal_direction) in self.bus.layout.fields.items():
376 # FANOUT signals: only mux the granted master with the interface
377 if signal_direction == Direction.FANOUT:
378 master_signals = Array(getattr(master_bus, signal_name)
379 for __, (___, master_bus)
380 in self._masters.items())
381 m.d.comb += getattr(self.bus, signal_name).eq(master_signals[grant])
382 # FANIN signals: ACK and ERR are ORed to all masters;
383 # all other signals are asserted to the granted master only
384 if signal_direction == Direction.FANIN:
385 for __, (index, master_bus) in self._masters.items():
386 source = getattr(self.bus, signal_name)
387 dest = getattr(master_bus, signal_name)
388 if signal_name in ["ack", "err"]:
389 m.d.comb += dest.eq(source & (grant == index))
390 else:
391 m.d.comb += dest.eq(source)
392
393 master_requests = [master_bus.cyc & ~master_bus.ack
394 for __, (___, master_bus) in self._masters.items()]
395 m.d.comb += scheduler.request.eq(Cat(*master_requests))
396
397 return m
398
399
400 class InterconnectShared(Elaboratable):
401 """
402 """
403 def __init__(self, shared_bus, masters, targets):
404 self.addr_width = shared_bus.addr_width
405 self.data_width = shared_bus.data_width
406 self.granularity = shared_bus.granularity
407 self._features = shared_bus._features
408 self._alignment = shared_bus._alignment
409
410 self._masters = []
411 self._targets = []
412
413 self._masters_convert_stmts = []
414 for master_bus in masters:
415 if isinstance(master_bus, Interface):
416 self._masters.append(master_bus)
417 elif isinstance(master_bus, Record):
418 master_interface = Interface.from_pure_record(master_bus)
419 self._masters_convert_stmts.append(
420 master_bus.connect(master_interface)
421 )
422 self._masters.append(master_interface)
423 else:
424 raise TypeError("Master {!r} must be a Wishbone interface"
425 .format(master_bus))
426
427 for target_bus in targets:
428 self._targets.append(target_bus)
429
430 self.arbiter = Arbiter(
431 addr_width=self.addr_width,
432 data_width=self.data_width,
433 granularity=self.granularity,
434 features=self._features,
435 alignment=self._alignment
436 )
437 for master_bus in self._masters:
438 self.arbiter.add(master_bus)
439
440 self.decoder = Decoder(
441 addr_width=self.addr_width,
442 data_width=self.data_width,
443 granularity=self.granularity,
444 features=self._features,
445 alignment=self._alignment
446 )
447 for item in self._targets:
448 if isinstance(item, Interface):
449 self.decoder.add(item)
450 elif isinstance(item, tuple) and len(item) == 2:
451 self.decoder.add(item[0], addr=item[1])
452 else:
453 raise TypeError("Target must be a Wishbone interface, "
454 "or a (Wishbone interface, start address) tuple, not {!r}"
455 .format(item))
456
457 def elaborate(self, platform):
458 m = Module()
459
460 m.submodules.arbiter = self.arbiter
461 m.submodules.decoder = self.decoder
462
463 m.d.comb += (
464 self._masters_convert_stmts +
465 self.arbiter.bus.connect(self.decoder.bus)
466 )
467
468 return m