periph: conform with nmigen-soc breaking changes.
[lambdasoc.git] / lambdasoc / periph / base.py
1 from nmigen import *
2 from nmigen import tracer
3 from nmigen.utils import log2_int
4
5 from nmigen_soc import csr, wishbone
6 from nmigen_soc.memory import MemoryMap
7 from nmigen_soc.csr.wishbone import WishboneCSRBridge
8
9
10 from .event import *
11
12
13 __all__ = ["Peripheral", "CSRBank", "PeripheralBridge"]
14
15
16 class Peripheral:
17 """Wishbone peripheral.
18
19 A helper class to reduce the boilerplate needed to control a peripheral with a Wishbone interface.
20 It provides facilities for instantiating CSR registers, requesting windows to subordinate busses
21 and sending interrupt requests to the CPU.
22
23 The ``Peripheral`` class is not meant to be instantiated as-is, but rather as a base class for
24 actual peripherals.
25
26 Usage example
27 -------------
28
29 ```
30 class ExamplePeripheral(Peripheral, Elaboratable):
31 def __init__(self):
32 super().__init__()
33 bank = self.csr_bank()
34 self._foo = bank.csr(8, "r")
35 self._bar = bank.csr(8, "w")
36
37 self._rdy = self.event(mode="rise")
38
39 self._bridge = self.bridge(data_width=32, granularity=8, alignment=2)
40 self.bus = self._bridge.bus
41 self.irq = self._bridge.irq
42
43 def elaborate(self, platform):
44 m = Module()
45 m.submodules.bridge = self._bridge
46 # ...
47 return m
48 ```
49
50 Arguments
51 ---------
52 name : str
53 Name of this peripheral. If ``None`` (default) the name is inferred from the variable
54 name this peripheral is assigned to.
55
56 Properties
57 ----------
58 name : str
59 Name of the peripheral.
60 """
61 def __init__(self, name=None, src_loc_at=1):
62 if name is not None and not isinstance(name, str):
63 raise TypeError("Name must be a string, not {!r}".format(name))
64 self.name = name or tracer.get_var_name(depth=2 + src_loc_at).lstrip("_")
65
66 self._csr_banks = []
67 self._windows = []
68 self._events = []
69
70 self._bus = None
71 self._irq = None
72
73 @property
74 def bus(self):
75 """Wishbone bus interface.
76
77 Return value
78 ------------
79 An instance of :class:`Interface`.
80
81 Exceptions
82 ----------
83 Raises :exn:`NotImplementedError` if the peripheral does not have a Wishbone bus.
84 """
85 if self._bus is None:
86 raise NotImplementedError("Peripheral {!r} does not have a bus interface"
87 .format(self))
88 return self._bus
89
90 @bus.setter
91 def bus(self, bus):
92 if not isinstance(bus, wishbone.Interface):
93 raise TypeError("Bus interface must be an instance of wishbone.Interface, not {!r}"
94 .format(bus))
95 self._bus = bus
96
97 @property
98 def irq(self):
99 """Interrupt request line.
100
101 Return value
102 ------------
103 An instance of :class:`IRQLine`.
104
105 Exceptions
106 ----------
107 Raises :exn:`NotImplementedError` if the peripheral does not have an IRQ line.
108 """
109 if self._irq is None:
110 raise NotImplementedError("Peripheral {!r} does not have an IRQ line"
111 .format(self))
112 return self._irq
113
114 @irq.setter
115 def irq(self, irq):
116 if not isinstance(irq, IRQLine):
117 raise TypeError("IRQ line must be an instance of IRQLine, not {!r}"
118 .format(irq))
119 self._irq = irq
120
121 def csr_bank(self, *, name=None, addr=None, alignment=None):
122 """Request a CSR bank.
123
124 Arguments
125 ---------
126 name : str
127 Optional. Bank name.
128 addr : int or None
129 Address of the bank. If ``None``, the implicit next address will be used.
130 Otherwise, the exact specified address (which must be a multiple of
131 ``2 ** max(alignment, bridge_alignment)``) will be used.
132 alignment : int or None
133 Alignment of the bank. If not specified, the bridge alignment is used.
134 See :class:`nmigen_soc.csr.Multiplexer` for details.
135
136 Return value
137 ------------
138 An instance of :class:`CSRBank`.
139 """
140 bank = CSRBank(name=name)
141 self._csr_banks.append((bank, addr, alignment))
142 return bank
143
144 def window(self, *, addr_width, data_width, granularity=None, features=frozenset(),
145 name=None, addr=None, sparse=None):
146 """Request a window to a subordinate bus.
147
148 See :meth:`nmigen_soc.wishbone.Decoder.add` for details.
149
150 Return value
151 ------------
152 An instance of :class:`nmigen_soc.wishbone.Interface`.
153 """
154 window = wishbone.Interface(addr_width=addr_width, data_width=data_width,
155 granularity=granularity, features=features, name=name)
156 self._windows.append((window, addr, sparse))
157 return window
158
159 def event(self, *, mode="level", name=None, src_loc_at=0):
160 """Request an event source.
161
162 See :class:`EventSource` for details.
163
164 Return value
165 ------------
166 An instance of :class:`EventSource`.
167 """
168 event = EventSource(mode=mode, name=name, src_loc_at=1 + src_loc_at)
169 self._events.append(event)
170 return event
171
172 def bridge(self, *, data_width=8, granularity=None, features=frozenset(), alignment=0):
173 """Request a bridge to the resources of the peripheral.
174
175 See :class:`PeripheralBridge` for details.
176
177 Return value
178 ------------
179 A :class:`PeripheralBridge` providing access to local resources.
180 """
181 return PeripheralBridge(self, data_width=data_width, granularity=granularity,
182 features=features, alignment=alignment)
183
184 def iter_csr_banks(self):
185 """Iterate requested CSR banks and their parameters.
186
187 Yield values
188 ------------
189 A tuple ``bank, addr, alignment`` describing the bank and its parameters.
190 """
191 for bank, addr, alignment in self._csr_banks:
192 yield bank, addr, alignment
193
194 def iter_windows(self):
195 """Iterate requested windows and their parameters.
196
197 Yield values
198 ------------
199 A tuple ``window, addr, sparse`` descr
200 given to :meth:`Peripheral.window`.
201 """
202 for window, addr, sparse in self._windows:
203 yield window, addr, sparse
204
205 def iter_events(self):
206 """Iterate requested event sources.
207
208 Yield values
209 ------------
210 An instance of :class:`EventSource`.
211 """
212 for event in self._events:
213 yield event
214
215
216 class CSRBank:
217 """CSR register bank.
218
219 Parameters
220 ----------
221 name : str
222 Optional. Name prefix of the bank registers.
223 """
224 def __init__(self, *, name=None):
225 if name is not None and not isinstance(name, str):
226 raise TypeError("Name must be a string, not {!r}".format(name))
227
228 self.name = name
229 self._csr_regs = []
230
231 def csr(self, width, access, *, addr=None, alignment=None, name=None,
232 src_loc_at=0):
233 """Request a CSR register.
234
235 Parameters
236 ----------
237 width : int
238 Width of the register. See :class:`nmigen_soc.csr.Element`.
239 access : :class:`Access`
240 Register access mode. See :class:`nmigen_soc.csr.Element`.
241 addr : int
242 Address of the register. See :meth:`nmigen_soc.csr.Multiplexer.add`.
243 alignment : int
244 Register alignment. See :class:`nmigen_soc.csr.Multiplexer`.
245 name : str
246 Name of the register. If ``None`` (default) the name is inferred from the variable
247 name this register is assigned to.
248
249 Return value
250 ------------
251 An instance of :class:`nmigen_soc.csr.Element`.
252 """
253 if name is not None and not isinstance(name, str):
254 raise TypeError("Name must be a string, not {!r}".format(name))
255 name = name or tracer.get_var_name(depth=2 + src_loc_at).lstrip("_")
256
257 if any(elem.name == name for (elem, _, _) in self._csr_regs):
258 raise Exception("CSR \"{}\" has already been defined".format(name))
259 elem = csr.Element(width, access, name=name)
260 self._csr_regs.append((elem, addr, alignment))
261 return elem
262
263 def iter_csr_regs(self):
264 """Iterate requested CSR registers and their parameters.
265
266 Yield values
267 ------------
268 A tuple ``elem, addr, alignment`` describing the register and its parameters.
269 """
270 for elem, addr, alignment in self._csr_regs:
271 yield elem, addr, alignment
272
273
274 class PeripheralBridge(Elaboratable):
275 """Peripheral bridge.
276
277 A bridge providing access to the registers and windows of a peripheral, and support for
278 interrupt requests from its event sources.
279
280 Event managment is performed by an :class:`InterruptSource` submodule.
281
282 Parameters
283 ---------
284 periph : :class:`Peripheral`
285 The peripheral whose resources are exposed by this bridge.
286 data_width : int
287 Data width. See :class:`nmigen_soc.wishbone.Interface`.
288 granularity : int or None
289 Granularity. See :class:`nmigen_soc.wishbone.Interface`.
290 features : iter(str)
291 Optional signal set. See :class:`nmigen_soc.wishbone.Interface`.
292 alignment : int
293 Resource alignment. See :class:`nmigen_soc.memory.MemoryMap`.
294
295 Attributes
296 ----------
297 bus : :class:`nmigen_soc.wishbone.Interface`
298 Wishbone bus providing access to the resources of the peripheral.
299 irq : :class:`IRQLine`, out
300 Interrupt request. It is raised if any event source is enabled and has a pending
301 notification.
302 """
303 def __init__(self, periph, *, data_width, granularity, features, alignment):
304 if not isinstance(periph, Peripheral):
305 raise TypeError("Peripheral must be an instance of Peripheral, not {!r}"
306 .format(periph))
307
308 self._wb_decoder = wishbone.Decoder(addr_width=1, data_width=data_width,
309 granularity=granularity,
310 features=features, alignment=alignment,
311 name=periph.name)
312
313 self._csr_subs = []
314
315 for bank, bank_addr, bank_alignment in periph.iter_csr_banks():
316 if bank_alignment is None:
317 bank_alignment = alignment
318 csr_mux = csr.Multiplexer(
319 addr_width=1, data_width=granularity, alignment=bank_alignment,
320 name=bank.name,
321 )
322 for elem, elem_addr, elem_alignment in bank.iter_csr_regs():
323 if elem_alignment is None:
324 elem_alignment = alignment
325 csr_mux.add(elem, addr=elem_addr, alignment=elem_alignment, extend=True)
326
327 csr_bridge = WishboneCSRBridge(csr_mux.bus, data_width=data_width)
328 self._wb_decoder.add(csr_bridge.wb_bus, addr=bank_addr, extend=True)
329 self._csr_subs.append((csr_mux, csr_bridge))
330
331 for window, window_addr, window_sparse in periph.iter_windows():
332 self._wb_decoder.add(window, addr=window_addr, sparse=window_sparse, extend=True)
333
334 events = list(periph.iter_events())
335 if len(events) > 0:
336 self._int_src = InterruptSource(events, name=periph.name)
337 self.irq = self._int_src.irq
338
339 csr_mux = csr.Multiplexer(addr_width=1, data_width=8, alignment=alignment, name="ev")
340 csr_mux.add(self._int_src.status, extend=True)
341 csr_mux.add(self._int_src.pending, extend=True)
342 csr_mux.add(self._int_src.enable, extend=True)
343
344 csr_bridge = WishboneCSRBridge(csr_mux.bus, data_width=data_width)
345 self._wb_decoder.add(csr_bridge.wb_bus, extend=True)
346 self._csr_subs.append((csr_mux, csr_bridge))
347 else:
348 self._int_src = None
349 self.irq = None
350
351 self.bus = self._wb_decoder.bus
352
353 def elaborate(self, platform):
354 m = Module()
355
356 for i, (csr_mux, csr_bridge) in enumerate(self._csr_subs):
357 m.submodules[ "csr_mux_{}".format(i)] = csr_mux
358 m.submodules["csr_bridge_{}".format(i)] = csr_bridge
359
360 if self._int_src is not None:
361 m.submodules._int_src = self._int_src
362
363 m.submodules.wb_decoder = self._wb_decoder
364
365 return m