wishbone.bus: add Arbiter.
[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
8
9 __all__ = ["CycleType", "BurstTypeExt", "Interface", "Decoder", "Arbiter"]
10
11
12 class CycleType(Enum):
13 """Wishbone Registered Feedback cycle type."""
14 CLASSIC = 0b000
15 CONST_BURST = 0b001
16 INCR_BURST = 0b010
17 END_OF_BURST = 0b111
18
19
20 class BurstTypeExt(Enum):
21 """Wishbone Registered Feedback burst type extension."""
22 LINEAR = 0b00
23 WRAP_4 = 0b01
24 WRAP_8 = 0b10
25 WRAP_16 = 0b11
26
27
28 class Interface(Record):
29 """Wishbone interface.
30
31 See the `Wishbone specification <https://opencores.org/howto/wishbone>`_ for description
32 of the Wishbone signals. The ``RST_I`` and ``CLK_I`` signals are provided as a part of
33 the clock domain that drives the interface.
34
35 Note that the data width of the underlying memory map of the interface is equal to port
36 granularity, not port size. If port granularity is less than port size, then the address width
37 of the underlying memory map is extended to reflect that.
38
39 Parameters
40 ----------
41 addr_width : int
42 Width of the address signal.
43 data_width : int
44 Width of the data signals ("port size" in Wishbone terminology).
45 One of 8, 16, 32, 64.
46 granularity : int
47 Granularity of select signals ("port granularity" in Wishbone terminology).
48 One of 8, 16, 32, 64.
49 features : iter(str)
50 Selects the optional signals that will be a part of this interface.
51 alignment : int
52 Resource and window alignment. See :class:`MemoryMap`.
53 name : str
54 Name of the underlying record.
55
56 Attributes
57 ----------
58 The correspondence between the nMigen-SoC signals and the Wishbone signals changes depending
59 on whether the interface acts as an initiator or a target.
60
61 adr : Signal(addr_width)
62 Corresponds to Wishbone signal ``ADR_O`` (initiator) or ``ADR_I`` (target).
63 dat_w : Signal(data_width)
64 Corresponds to Wishbone signal ``DAT_O`` (initiator) or ``DAT_I`` (target).
65 dat_r : Signal(data_width)
66 Corresponds to Wishbone signal ``DAT_I`` (initiator) or ``DAT_O`` (target).
67 sel : Signal(data_width // granularity)
68 Corresponds to Wishbone signal ``SEL_O`` (initiator) or ``SEL_I`` (target).
69 cyc : Signal()
70 Corresponds to Wishbone signal ``CYC_O`` (initiator) or ``CYC_I`` (target).
71 stb : Signal()
72 Corresponds to Wishbone signal ``STB_O`` (initiator) or ``STB_I`` (target).
73 we : Signal()
74 Corresponds to Wishbone signal ``WE_O`` (initiator) or ``WE_I`` (target).
75 ack : Signal()
76 Corresponds to Wishbone signal ``ACK_I`` (initiator) or ``ACK_O`` (target).
77 err : Signal()
78 Optional. Corresponds to Wishbone signal ``ERR_I`` (initiator) or ``ERR_O`` (target).
79 rty : Signal()
80 Optional. Corresponds to Wishbone signal ``RTY_I`` (initiator) or ``RTY_O`` (target).
81 stall : Signal()
82 Optional. Corresponds to Wishbone signal ``STALL_I`` (initiator) or ``STALL_O`` (target).
83 lock : Signal()
84 Optional. Corresponds to Wishbone signal ``LOCK_O`` (initiator) or ``LOCK_I`` (target).
85 cti : Signal()
86 Optional. Corresponds to Wishbone signal ``CTI_O`` (initiator) or ``CTI_I`` (target).
87 bte : Signal()
88 Optional. Corresponds to Wishbone signal ``BTE_O`` (initiator) or ``BTE_I`` (target).
89 """
90 def __init__(self, *, addr_width, data_width, granularity=None, features=frozenset(),
91 alignment=0, name=None):
92 if not isinstance(addr_width, int) or addr_width < 0:
93 raise ValueError("Address width must be a non-negative integer, not {!r}"
94 .format(addr_width))
95 if data_width not in (8, 16, 32, 64):
96 raise ValueError("Data width must be one of 8, 16, 32, 64, not {!r}"
97 .format(data_width))
98 if granularity is None:
99 granularity = data_width
100 elif granularity not in (8, 16, 32, 64):
101 raise ValueError("Granularity must be one of 8, 16, 32, 64, not {!r}"
102 .format(granularity))
103 if granularity > data_width:
104 raise ValueError("Granularity {} may not be greater than data width {}"
105 .format(granularity, data_width))
106 self.addr_width = addr_width
107 self.data_width = data_width
108 self.granularity = granularity
109 granularity_bits = log2_int(data_width // granularity)
110 self.memory_map = MemoryMap(addr_width=max(1, addr_width + granularity_bits),
111 data_width=data_width >> granularity_bits,
112 alignment=alignment)
113
114 features = set(features)
115 unknown = features - {"rty", "err", "stall", "lock", "cti", "bte"}
116 if unknown:
117 raise ValueError("Optional signal(s) {} are not supported"
118 .format(", ".join(map(repr, unknown))))
119 layout = [
120 ("adr", addr_width, Direction.FANOUT),
121 ("dat_w", data_width, Direction.FANOUT),
122 ("dat_r", data_width, Direction.FANIN),
123 ("sel", data_width // granularity, Direction.FANOUT),
124 ("cyc", 1, Direction.FANOUT),
125 ("stb", 1, Direction.FANOUT),
126 ("we", 1, Direction.FANOUT),
127 ("ack", 1, Direction.FANIN),
128 ]
129 if "err" in features:
130 layout += [("err", 1, Direction.FANIN)]
131 if "rty" in features:
132 layout += [("rty", 1, Direction.FANIN)]
133 if "stall" in features:
134 layout += [("stall", 1, Direction.FANIN)]
135 if "lock" in features:
136 layout += [("lock", 1, Direction.FANOUT)]
137 if "cti" in features:
138 layout += [("cti", CycleType, Direction.FANOUT)]
139 if "bte" in features:
140 layout += [("bte", BurstTypeExt, Direction.FANOUT)]
141 super().__init__(layout, name=name, src_loc_at=1)
142
143
144 class Decoder(Elaboratable):
145 """Wishbone bus decoder.
146
147 An address decoder for subordinate Wishbone buses.
148
149 Parameters
150 ----------
151 addr_width : int
152 Address width. See :class:`Interface`.
153 data_width : int
154 Data width. See :class:`Interface`.
155 granularity : int
156 Granularity. See :class:`Interface`
157 features : iter(str)
158 Optional signal set. See :class:`Interface`.
159 alignment : int
160 Window alignment. See :class:`Interface`.
161
162 Attributes
163 ----------
164 bus : :class:`Interface`
165 CSR bus providing access to subordinate buses.
166 """
167 def __init__(self, *, addr_width, data_width, granularity=None, features=frozenset(),
168 alignment=0):
169 self.bus = Interface(addr_width=addr_width, data_width=data_width,
170 granularity=granularity, features=features,
171 alignment=alignment)
172 self._map = self.bus.memory_map
173 self._subs = dict()
174
175 def align_to(self, alignment):
176 """Align the implicit address of the next window.
177
178 See :meth:`MemoryMap.align_to` for details.
179 """
180 return self._map.align_to(alignment)
181
182 def add(self, sub_bus, *, addr=None, sparse=False):
183 """Add a window to a subordinate bus.
184
185 The decoder can perform either sparse or dense address translation. If dense address
186 translation is used (the default), the subordinate bus must have the same data width as
187 the decoder; the window will be contiguous. If sparse address translation is used,
188 the subordinate bus may have data width less than the data width of the decoder;
189 the window may be discontiguous. In either case, the granularity of the subordinate bus
190 must be equal to or less than the granularity of the decoder.
191
192 See :meth:`MemoryMap.add_resource` for details.
193 """
194 if not isinstance(sub_bus, Interface):
195 raise TypeError("Subordinate bus must be an instance of wishbone.Interface, not {!r}"
196 .format(sub_bus))
197 if sub_bus.granularity > self.bus.granularity:
198 raise ValueError("Subordinate bus has granularity {}, which is greater than the "
199 "decoder granularity {}"
200 .format(sub_bus.granularity, self.bus.granularity))
201 if not sparse:
202 if sub_bus.data_width != self.bus.data_width:
203 raise ValueError("Subordinate bus has data width {}, which is not the same as "
204 "decoder data width {} (required for dense address translation)"
205 .format(sub_bus.data_width, self.bus.data_width))
206 else:
207 if sub_bus.granularity != sub_bus.data_width:
208 raise ValueError("Subordinate bus has data width {}, which is not the same as "
209 "subordinate bus granularity {} (required for sparse address "
210 "translation)"
211 .format(sub_bus.data_width, sub_bus.granularity))
212 for opt_output in {"err", "rty", "stall"}:
213 if hasattr(sub_bus, opt_output) and not hasattr(self.bus, opt_output):
214 raise ValueError("Subordinate bus has optional output {!r}, but the decoder "
215 "does not have a corresponding input"
216 .format(opt_output))
217
218 self._subs[sub_bus.memory_map] = sub_bus
219 return self._map.add_window(sub_bus.memory_map, addr=addr, sparse=sparse)
220
221 def elaborate(self, platform):
222 m = Module()
223
224 ack_fanin = 0
225 err_fanin = 0
226 rty_fanin = 0
227 stall_fanin = 0
228
229 with m.Switch(self.bus.adr):
230 for sub_map, (sub_pat, sub_ratio) in self._map.window_patterns():
231 sub_bus = self._subs[sub_map]
232
233 m.d.comb += [
234 sub_bus.adr.eq(self.bus.adr << log2_int(sub_ratio)),
235 sub_bus.dat_w.eq(self.bus.dat_w),
236 sub_bus.sel.eq(Cat(Repl(sel, sub_ratio) for sel in self.bus.sel)),
237 sub_bus.we.eq(self.bus.we),
238 sub_bus.stb.eq(self.bus.stb),
239 ]
240 if hasattr(sub_bus, "lock"):
241 m.d.comb += sub_bus.lock.eq(getattr(self.bus, "lock", 0))
242 if hasattr(sub_bus, "cti"):
243 m.d.comb += sub_bus.cti.eq(getattr(self.bus, "cti", CycleType.CLASSIC))
244 if hasattr(sub_bus, "bte"):
245 m.d.comb += sub_bus.bte.eq(getattr(self.bus, "bte", BurstTypeExt.LINEAR))
246
247 with m.Case(sub_pat[:-log2_int(self.bus.data_width // self.bus.granularity)]):
248 m.d.comb += [
249 sub_bus.cyc.eq(self.bus.cyc),
250 self.bus.dat_r.eq(sub_bus.dat_r),
251 ]
252 ack_fanin |= sub_bus.ack
253 if hasattr(sub_bus, "err"):
254 err_fanin |= sub_bus.err
255 if hasattr(sub_bus, "rty"):
256 rty_fanin |= sub_bus.rty
257 if hasattr(sub_bus, "stall"):
258 stall_fanin |= sub_bus.stall
259
260 m.d.comb += self.bus.ack.eq(ack_fanin)
261 if hasattr(self.bus, "err"):
262 m.d.comb += self.bus.err.eq(err_fanin)
263 if hasattr(self.bus, "rty"):
264 m.d.comb += self.bus.rty.eq(rty_fanin)
265 if hasattr(self.bus, "stall"):
266 m.d.comb += self.bus.stall.eq(stall_fanin)
267
268 return m
269
270
271 class Arbiter(Elaboratable):
272 """Wishbone bus arbiter.
273
274 A round-robin arbiter for initiators accessing a shared Wishbone bus.
275
276 Parameters
277 ----------
278 addr_width : int
279 Address width. See :class:`Interface`.
280 data_width : int
281 Data width. See :class:`Interface`.
282 granularity : int
283 Granularity. See :class:`Interface`
284 features : iter(str)
285 Optional signal set. See :class:`Interface`.
286
287 Attributes
288 ----------
289 bus : :class:`Interface`
290 Shared Wishbone bus.
291 """
292 def __init__(self, *, addr_width, data_width, granularity=None, features=frozenset()):
293 self.bus = Interface(addr_width=addr_width, data_width=data_width,
294 granularity=granularity, features=features)
295 self._itors = []
296
297 def add(self, itor_bus):
298 """Add an initiator bus to the arbiter.
299
300 The initiator bus must have the same address width and data width as the arbiter. The
301 granularity of the initiator bus must be greater than or equal to the granularity of
302 the arbiter.
303 """
304 if not isinstance(itor_bus, Interface):
305 raise TypeError("Initiator bus must be an instance of wishbone.Interface, not {!r}"
306 .format(itor_bus))
307 if itor_bus.addr_width != self.bus.addr_width:
308 raise ValueError("Initiator bus has address width {}, which is not the same as "
309 "arbiter address width {}"
310 .format(itor_bus.addr_width, self.bus.addr_width))
311 if itor_bus.granularity < self.bus.granularity:
312 raise ValueError("Initiator bus has granularity {}, which is lesser than the "
313 "arbiter granularity {}"
314 .format(itor_bus.granularity, self.bus.granularity))
315 if itor_bus.data_width != self.bus.data_width:
316 raise ValueError("Initiator bus has data width {}, which is not the same as "
317 "arbiter data width {}"
318 .format(itor_bus.data_width, self.bus.data_width))
319 for opt_output in {"lock", "cti", "bte"}:
320 if hasattr(itor_bus, opt_output) and not hasattr(self.bus, opt_output):
321 raise ValueError("Initiator bus has optional output {!r}, but the arbiter "
322 "does not have a corresponding input"
323 .format(opt_output))
324
325 self._itors.append(itor_bus)
326
327 def elaborate(self, platform):
328 m = Module()
329
330 requests = Signal(len(self._itors))
331 grant = Signal(range(len(self._itors)))
332 m.d.comb += requests.eq(Cat(itor_bus.cyc for itor_bus in self._itors))
333
334 bus_busy = self.bus.cyc
335 if hasattr(self.bus, "lock"):
336 # If LOCK is not asserted, we also wait for STB to be deasserted before granting bus
337 # ownership to the next initiator. If we didn't, the next bus owner could receive
338 # an ACK (or ERR, RTY) from the previous transaction when targeting the same
339 # peripheral.
340 bus_busy &= self.bus.lock | self.bus.stb
341
342 with m.If(~bus_busy):
343 with m.Switch(grant):
344 for i in range(len(requests)):
345 with m.Case(i):
346 for pred in reversed(range(i)):
347 with m.If(requests[pred]):
348 m.d.sync += grant.eq(pred)
349 for succ in reversed(range(i + 1, len(requests))):
350 with m.If(requests[succ]):
351 m.d.sync += grant.eq(succ)
352
353 with m.Switch(grant):
354 for i, itor_bus in enumerate(self._itors):
355 m.d.comb += itor_bus.dat_r.eq(self.bus.dat_r)
356 if hasattr(itor_bus, "stall"):
357 itor_bus_stall = Signal(reset=1)
358 m.d.comb += itor_bus.stall.eq(itor_bus_stall)
359
360 with m.Case(i):
361 ratio = itor_bus.granularity // self.bus.granularity
362 m.d.comb += [
363 self.bus.adr.eq(itor_bus.adr),
364 self.bus.dat_w.eq(itor_bus.dat_w),
365 self.bus.sel.eq(Cat(Repl(sel, ratio) for sel in itor_bus.sel)),
366 self.bus.we.eq(itor_bus.we),
367 self.bus.stb.eq(itor_bus.stb),
368 ]
369 m.d.comb += self.bus.cyc.eq(itor_bus.cyc)
370 if hasattr(self.bus, "lock"):
371 m.d.comb += self.bus.lock.eq(getattr(itor_bus, "lock", 1))
372 if hasattr(self.bus, "cti"):
373 m.d.comb += self.bus.cti.eq(getattr(itor_bus, "cti", CycleType.CLASSIC))
374 if hasattr(self.bus, "bte"):
375 m.d.comb += self.bus.bte.eq(getattr(itor_bus, "bte", BurstTypeExt.LINEAR))
376
377 m.d.comb += itor_bus.ack.eq(self.bus.ack)
378 if hasattr(itor_bus, "err"):
379 m.d.comb += itor_bus.err.eq(getattr(self.bus, "err", 0))
380 if hasattr(itor_bus, "rty"):
381 m.d.comb += itor_bus.rty.eq(getattr(self.bus, "rty", 0))
382 if hasattr(itor_bus, "stall"):
383 m.d.comb += itor_bus_stall.eq(getattr(self.bus, "stall", ~self.bus.ack))
384
385 return m