2ae27331ff5e736b05abf34d1d353ace74c0c5d9
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 initiators (masters) to access a shared Wishbone bus.
313 Address width of the shared bus. See :class:`Interface`.
315 Data width of the shared bus. See :class:`Interface`.
317 Granularity of the shared bus. See :class:`Interface`.
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`).
326 bus : :class:`Interface`
327 Shared bus to which the selected initiator gains access.
329 def __init__(self
, *, addr_width
, data_width
, granularity
=None, features
=frozenset(),
331 self
.bus
= Interface(addr_width
=addr_width
, data_width
=data_width
,
332 granularity
=granularity
, features
=features
)
334 if scheduler
not in ["rr"]:
335 raise ValueError("Scheduling mode must be \"rr\", not {!r}"
337 self
._scheduler
= scheduler
339 def add(self
, itor_bus
):
340 """Add an initiator bus to the arbiter.
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
346 if not isinstance(itor_bus
, Interface
):
347 raise TypeError("Initiator bus must be an instance of wishbone.Interface, not {!r}"
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"
367 self
._itors
.append(itor_bus
)
369 def elaborate(self
, platform
):
372 if self
._scheduler
== "rr":
373 m
.submodules
.scheduler
= scheduler
= RoundRobin(len(self
._itors
))
374 grant
= Signal(range(len(self
._itors
)))
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
383 bus_busy
&= self
.bus
.lock | self
.bus
.stb
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
))
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
)
399 ratio
= itor_bus
.granularity
// self
.bus
.granularity
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
),
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
))
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
))
426 class InterconnectShared(Elaboratable
):
427 """Wishbone bus interconnect module.
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.
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.
441 See Section 8.2.3 of Wishbone B4 for implemenation specifications.
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.
455 Address width of the shared bus. See :class:`Interface`.
457 Data width of the shared bus. See :class:`Interface`.
459 Granularity of the shared bus. See :class:`Interface`
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
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
)
478 self
._itors
.append(master_interface
)
480 raise TypeError("Master {!r} must be a Wishbone interface"
483 for target_bus
in targets
:
484 self
._targets
.append(target_bus
)
486 arbiter_kwargs
= dict()
487 for name
in ["granularity", "features", "scheduler"]:
489 arbiter_kwargs
[name
] = kwargs
[name
]
490 self
.arbiter
= Arbiter(
491 addr_width
=self
.addr_width
, data_width
=self
.data_width
, **arbiter_kwargs
493 self
.arbiter
.bus
.name
= "arbiter_shared"
494 for itor_bus
in self
._itors
:
495 self
.arbiter
.add(itor_bus
)
497 decoder_kwargs
= dict()
498 for name
in ["granularity", "features", "alignment"]:
500 decoder_kwargs
[name
] = kwargs
[name
]
501 self
.decoder
= Decoder(
502 addr_width
=self
.addr_width
, data_width
=self
.data_width
, **decoder_kwargs
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])
511 raise TypeError("Target must be a Wishbone interface, "
512 "or a (Wishbone interface, start address) tuple, not {!r}"
515 def elaborate(self
, platform
):
518 m
.submodules
.arbiter
= self
.arbiter
519 m
.submodules
.decoder
= self
.decoder
522 self
._itors
_convert
_stmts
+
523 self
.arbiter
.bus
.connect(self
.decoder
.bus
)