3 from nmigen
.hdl
.rec
import Direction
4 from nmigen
.utils
import log2_int
6 from ..memory
import MemoryMap
9 __all__
= ["CycleType", "BurstTypeExt", "Interface", "Decoder", "Arbiter"]
12 class CycleType(Enum
):
13 """Wishbone Registered Feedback cycle type."""
20 class BurstTypeExt(Enum
):
21 """Wishbone Registered Feedback burst type extension."""
28 class Interface(Record
):
29 """Wishbone interface.
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.
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.
42 Width of the address signal.
44 Width of the data signals ("port size" in Wishbone terminology).
47 Granularity of select signals ("port granularity" in Wishbone terminology).
50 Selects the optional signals that will be a part of this interface.
52 Resource and window alignment. See :class:`MemoryMap`.
54 Name of the underlying record.
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.
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).
70 Corresponds to Wishbone signal ``CYC_O`` (initiator) or ``CYC_I`` (target).
72 Corresponds to Wishbone signal ``STB_O`` (initiator) or ``STB_I`` (target).
74 Corresponds to Wishbone signal ``WE_O`` (initiator) or ``WE_I`` (target).
76 Corresponds to Wishbone signal ``ACK_I`` (initiator) or ``ACK_O`` (target).
78 Optional. Corresponds to Wishbone signal ``ERR_I`` (initiator) or ``ERR_O`` (target).
80 Optional. Corresponds to Wishbone signal ``RTY_I`` (initiator) or ``RTY_O`` (target).
82 Optional. Corresponds to Wishbone signal ``STALL_I`` (initiator) or ``STALL_O`` (target).
84 Optional. Corresponds to Wishbone signal ``LOCK_O`` (initiator) or ``LOCK_I`` (target).
86 Optional. Corresponds to Wishbone signal ``CTI_O`` (initiator) or ``CTI_I`` (target).
88 Optional. Corresponds to Wishbone signal ``BTE_O`` (initiator) or ``BTE_I`` (target).
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}"
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}"
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
,
114 features
= set(features
)
115 unknown
= features
- {"rty", "err", "stall", "lock", "cti", "bte"}
117 raise ValueError("Optional signal(s) {} are not supported"
118 .format(", ".join(map(repr, unknown
))))
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
),
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)
144 class Decoder(Elaboratable
):
145 """Wishbone bus decoder.
147 An address decoder for subordinate Wishbone buses.
152 Address width. See :class:`Interface`.
154 Data width. See :class:`Interface`.
156 Granularity. See :class:`Interface`
158 Optional signal set. See :class:`Interface`.
160 Window alignment. See :class:`Interface`.
164 bus : :class:`Interface`
165 CSR bus providing access to subordinate buses.
167 def __init__(self
, *, addr_width
, data_width
, granularity
=None, features
=frozenset(),
169 self
.bus
= Interface(addr_width
=addr_width
, data_width
=data_width
,
170 granularity
=granularity
, features
=features
,
172 self
._map
= self
.bus
.memory_map
175 def align_to(self
, alignment
):
176 """Align the implicit address of the next window.
178 See :meth:`MemoryMap.align_to` for details.
180 return self
._map
.align_to(alignment
)
182 def add(self
, sub_bus
, *, addr
=None, sparse
=False):
183 """Add a window to a subordinate bus.
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.
192 See :meth:`MemoryMap.add_resource` for details.
194 if not isinstance(sub_bus
, Interface
):
195 raise TypeError("Subordinate bus must be an instance of wishbone.Interface, not {!r}"
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
))
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
))
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 "
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"
218 self
._subs
[sub_bus
.memory_map
] = sub_bus
219 return self
._map
.add_window(sub_bus
.memory_map
, addr
=addr
, sparse
=sparse
)
221 def elaborate(self
, platform
):
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
]
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
),
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
))
247 with m
.Case(sub_pat
[:-log2_int(self
.bus
.data_width
// self
.bus
.granularity
)]):
249 sub_bus
.cyc
.eq(self
.bus
.cyc
),
250 self
.bus
.dat_r
.eq(sub_bus
.dat_r
),
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
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
)
271 class Arbiter(Elaboratable
):
272 """Wishbone bus arbiter.
274 A round-robin arbiter for initiators accessing a shared Wishbone bus.
279 Address width. See :class:`Interface`.
281 Data width. See :class:`Interface`.
283 Granularity. See :class:`Interface`
285 Optional signal set. See :class:`Interface`.
289 bus : :class:`Interface`
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
)
297 def add(self
, itor_bus
):
298 """Add an initiator bus to the arbiter.
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
304 if not isinstance(itor_bus
, Interface
):
305 raise TypeError("Initiator bus must be an instance of wishbone.Interface, not {!r}"
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"
325 self
._itors
.append(itor_bus
)
327 def elaborate(self
, platform
):
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
))
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
340 bus_busy
&= self
.bus
.lock | self
.bus
.stb
342 with m
.If(~bus_busy
):
343 with m
.Switch(grant
):
344 for i
in range(len(requests
)):
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
)
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
)
361 ratio
= itor_bus
.granularity
// self
.bus
.granularity
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
),
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
))
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
))