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"]
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
)