3 from nmigen
.hdl
.rec
import Direction
4 from nmigen
.utils
import log2_int
6 from ..memory
import MemoryMap
7 from ..scheduler
import *
10 __all__
= ["CycleType", "BurstTypeExt", "Interface", "Decoder",
11 "Arbiter", "InterconnectShared"]
14 class CycleType(Enum
):
15 """Wishbone Registered Feedback cycle type."""
22 class BurstTypeExt(Enum
):
23 """Wishbone Registered Feedback burst type extension."""
30 class Interface(Record
):
31 """Wishbone interface.
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.
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.
44 Width of the address signal.
46 Width of the data signals ("port size" in Wishbone terminology).
49 Granularity of select signals ("port granularity" in Wishbone terminology).
52 Selects the optional signals that will be a part of this interface.
54 Resource and window alignment. See :class:`MemoryMap`.
56 Name of the underlying record.
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.
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).
72 Corresponds to Wishbone signal ``CYC_O`` (initiator) or ``CYC_I`` (target).
74 Corresponds to Wishbone signal ``STB_O`` (initiator) or ``STB_I`` (target).
76 Corresponds to Wishbone signal ``WE_O`` (initiator) or ``WE_I`` (target).
78 Corresponds to Wishbone signal ``ACK_I`` (initiator) or ``ACK_O`` (target).
80 Optional. Corresponds to Wishbone signal ``ERR_I`` (initiator) or ``ERR_O`` (target).
82 Optional. Corresponds to Wishbone signal ``RTY_I`` (initiator) or ``RTY_O`` (target).
84 Optional. Corresponds to Wishbone signal ``STALL_I`` (initiator) or ``STALL_O`` (target).
86 Optional. Corresponds to Wishbone signal ``LOCK_O`` (initiator) or ``LOCK_I`` (target).
88 Optional. Corresponds to Wishbone signal ``CTI_O`` (initiator) or ``CTI_I`` (target).
90 Optional. Corresponds to Wishbone signal ``BTE_O`` (initiator) or ``BTE_I`` (target).
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}"
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}"
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
,
117 self
._features
= set(features
)
118 unknown
= self
._features
- {"rty", "err", "stall", "lock", "cti", "bte"}
120 raise ValueError("Optional signal(s) {} are not supported"
121 .format(", ".join(map(repr, unknown
))))
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
),
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)
147 def from_pure_record(cls
, record
):
148 """Instantiate a :class:`wishbone.Interface` from a simple :class:`Record`
150 if not isinstance(record
, Record
):
151 raise TypeError("{!r} is not a 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
)
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
,
174 name
=record
.name
+"_intf")
177 class Decoder(Elaboratable
):
178 """Wishbone bus decoder.
180 An address decoder for subordinate Wishbone buses.
185 Address width. See :class:`Interface`.
187 Data width. See :class:`Interface`.
189 Granularity. See :class:`Interface`
191 Optional signal set. See :class:`Interface`.
193 Window alignment. See :class:`Interface`.
197 bus : :class:`Interface`
198 Bus providing access to subordinate buses.
200 def __init__(self
, *, addr_width
, data_width
, granularity
=None, features
=frozenset(),
202 self
.bus
= Interface(addr_width
=addr_width
, data_width
=data_width
,
203 granularity
=granularity
, features
=features
,
205 self
._map
= self
.bus
.memory_map
208 def align_to(self
, alignment
):
209 """Align the implicit address of the next window.
211 See :meth:`MemoryMap.align_to` for details.
213 return self
._map
.align_to(alignment
)
215 def add(self
, sub_bus
, *, addr
=None, sparse
=False):
216 """Add a window to a subordinate bus.
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.
225 See :meth:`MemoryMap.add_resource` for details.
227 if not isinstance(sub_bus
, Interface
):
228 raise TypeError("Subordinate bus must be an instance of wishbone.Interface, not {!r}"
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
))
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
))
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 "
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"
251 self
._subs
[sub_bus
.memory_map
] = sub_bus
252 return self
._map
.add_window(sub_bus
.memory_map
, addr
=addr
, sparse
=sparse
)
254 def elaborate(self
, platform
):
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
]
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
),
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
))
280 with m
.Case(sub_pat
[:-log2_int(self
.bus
.data_width
// self
.bus
.granularity
)]):
282 sub_bus
.cyc
.eq(self
.bus
.cyc
),
283 self
.bus
.dat_r
.eq(sub_bus
.dat_r
),
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
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
)
305 class Arbiter(Elaboratable
):
306 """Wishbone bus arbiter.
308 An arbiter for selecting the Wishbone master from several devices.
313 Address width. See :class:`Interface`.
315 Data width. See :class:`Interface`.
317 Granularity. See :class:`Interface`.
319 Optional signal set. See :class:`Interface`.
321 Window alignment. See :class:`Interface`.
325 bus : :class:`Interface`
326 Bus providing access to the selected master.
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
,
333 self
._masters
= dict()
334 if scheduler
not in ["rr"]:
335 raise ValueError("Scheduling mode must be \"rr\", not {!r}"
337 self
._scheduler
= scheduler
340 def add(self
, master_bus
):
341 """Add a device bus to the list of master candidates
343 if not isinstance(master_bus
, Interface
):
344 raise TypeError("Master bus must be an instance of wishbone.Interface, not {!r}"
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"
360 self
._masters
[master_bus
.memory_map
] = self
._next
_index
, master_bus
361 self
._next
_index
+= 1
363 def elaborate(self
, platform
):
366 if self
._scheduler
== "rr":
367 m
.submodules
.scheduler
= scheduler
= RoundRobin(self
._next
_index
)
368 grant
= Signal(self
._next
_index
)
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
)
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
))
391 m
.d
.comb
+= dest
.eq(source
)
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
))
400 class InterconnectShared(Elaboratable
):
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
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
)
422 self
._masters
.append(master_interface
)
424 raise TypeError("Master {!r} must be a Wishbone interface"
427 for target_bus
in targets
:
428 self
._targets
.append(target_bus
)
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
437 for master_bus
in self
._masters
:
438 self
.arbiter
.add(master_bus
)
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
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])
453 raise TypeError("Target must be a Wishbone interface, "
454 "or a (Wishbone interface, start address) tuple, not {!r}"
457 def elaborate(self
, platform
):
460 m
.submodules
.arbiter
= self
.arbiter
461 m
.submodules
.decoder
= self
.decoder
464 self
._masters
_convert
_stmts
+
465 self
.arbiter
.bus
.connect(self
.decoder
.bus
)