periph._event → periph.event
[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, *, addr=None, alignment=None):
122 """Request a CSR bank.
123
124 Arguments
125 ---------
126 addr : int or None
127 Address of the bank. If ``None``, the implicit next address will be used.
128 Otherwise, the exact specified address (which must be a multiple of
129 ``2 ** max(alignment, bridge_alignment)``) will be used.
130 alignment : int or None
131 Alignment of the bank. If not specified, the bridge alignment is used.
132 See :class:`nmigen_soc.csr.Multiplexer` for details.
133
134 Return value
135 ------------
136 An instance of :class:`CSRBank`.
137 """
138 bank = CSRBank(name_prefix=self.name)
139 self._csr_banks.append((bank, addr, alignment))
140 return bank
141
142 def window(self, *, addr_width, data_width, granularity=None, features=frozenset(),
143 alignment=0, addr=None, sparse=None):
144 """Request a window to a subordinate bus.
145
146 See :meth:`nmigen_soc.wishbone.Decoder.add` for details.
147
148 Return value
149 ------------
150 An instance of :class:`nmigen_soc.wishbone.Interface`.
151 """
152 window = wishbone.Interface(addr_width=addr_width, data_width=data_width,
153 granularity=granularity, features=features)
154 granularity_bits = log2_int(data_width // window.granularity)
155 window.memory_map = MemoryMap(addr_width=addr_width + granularity_bits,
156 data_width=window.granularity, alignment=alignment)
157 self._windows.append((window, addr, sparse))
158 return window
159
160 def event(self, *, mode="level", name=None, src_loc_at=0):
161 """Request an event source.
162
163 See :class:`EventSource` for details.
164
165 Return value
166 ------------
167 An instance of :class:`EventSource`.
168 """
169 event = EventSource(mode=mode, name=name, src_loc_at=1 + src_loc_at)
170 self._events.append(event)
171 return event
172
173 def bridge(self, *, data_width=8, granularity=None, features=frozenset(), alignment=0):
174 """Request a bridge to the resources of the peripheral.
175
176 See :class:`PeripheralBridge` for details.
177
178 Return value
179 ------------
180 A :class:`PeripheralBridge` providing access to local resources.
181 """
182 return PeripheralBridge(self, data_width=data_width, granularity=granularity,
183 features=features, alignment=alignment)
184
185 def iter_csr_banks(self):
186 """Iterate requested CSR banks and their parameters.
187
188 Yield values
189 ------------
190 A tuple ``bank, addr, alignment`` describing the bank and its parameters.
191 """
192 for bank, addr, alignment in self._csr_banks:
193 yield bank, addr, alignment
194
195 def iter_windows(self):
196 """Iterate requested windows and their parameters.
197
198 Yield values
199 ------------
200 A tuple ``window, addr, sparse`` descr
201 given to :meth:`Peripheral.window`.
202 """
203 for window, addr, sparse in self._windows:
204 yield window, addr, sparse
205
206 def iter_events(self):
207 """Iterate requested event sources.
208
209 Yield values
210 ------------
211 An instance of :class:`EventSource`.
212 """
213 for event in self._events:
214 yield event
215
216
217 class CSRBank:
218 """CSR register bank.
219
220 Parameters
221 ----------
222 name_prefix : str
223 Name prefix of the bank registers.
224 """
225 def __init__(self, *, name_prefix=""):
226 self._name_prefix = name_prefix
227 self._csr_regs = []
228
229 def csr(self, width, access, *, addr=None, alignment=None, name=None,
230 src_loc_at=0):
231 """Request a CSR register.
232
233 Parameters
234 ----------
235 width : int
236 Width of the register. See :class:`nmigen_soc.csr.Element`.
237 access : :class:`Access`
238 Register access mode. See :class:`nmigen_soc.csr.Element`.
239 addr : int
240 Address of the register. See :meth:`nmigen_soc.csr.Multiplexer.add`.
241 alignment : int
242 Register alignment. See :class:`nmigen_soc.csr.Multiplexer`.
243 name : str
244 Name of the register. If ``None`` (default) the name is inferred from the variable
245 name this register is assigned to.
246
247 Return value
248 ------------
249 An instance of :class:`nmigen_soc.csr.Element`.
250 """
251 if name is not None and not isinstance(name, str):
252 raise TypeError("Name must be a string, not {!r}".format(name))
253 name = name or tracer.get_var_name(depth=2 + src_loc_at).lstrip("_")
254
255 elem_name = "{}_{}".format(self._name_prefix, name)
256 elem = csr.Element(width, access, name=elem_name)
257 self._csr_regs.append((elem, addr, alignment))
258 return elem
259
260 def iter_csr_regs(self):
261 """Iterate requested CSR registers and their parameters.
262
263 Yield values
264 ------------
265 A tuple ``elem, addr, alignment`` describing the register and its parameters.
266 """
267 for elem, addr, alignment in self._csr_regs:
268 yield elem, addr, alignment
269
270
271 class PeripheralBridge(Elaboratable):
272 """Peripheral bridge.
273
274 A bridge providing access to the registers and windows of a peripheral, and support for
275 interrupt requests from its event sources.
276
277 Event managment is performed by an :class:`InterruptSource` submodule.
278
279 Parameters
280 ---------
281 periph : :class:`Peripheral`
282 The peripheral whose resources are exposed by this bridge.
283 data_width : int
284 Data width. See :class:`nmigen_soc.wishbone.Interface`.
285 granularity : int or None
286 Granularity. See :class:`nmigen_soc.wishbone.Interface`.
287 features : iter(str)
288 Optional signal set. See :class:`nmigen_soc.wishbone.Interface`.
289 alignment : int
290 Resource alignment. See :class:`nmigen_soc.memory.MemoryMap`.
291
292 Attributes
293 ----------
294 bus : :class:`nmigen_soc.wishbone.Interface`
295 Wishbone bus providing access to the resources of the peripheral.
296 irq : :class:`IRQLine`, out
297 Interrupt request. It is raised if any event source is enabled and has a pending
298 notification.
299 """
300 def __init__(self, periph, *, data_width, granularity, features, alignment):
301 if not isinstance(periph, Peripheral):
302 raise TypeError("Peripheral must be an instance of Peripheral, not {!r}"
303 .format(periph))
304
305 self._wb_decoder = wishbone.Decoder(addr_width=1, data_width=data_width,
306 granularity=granularity,
307 features=features, alignment=alignment)
308
309 self._csr_subs = []
310
311 for bank, bank_addr, bank_alignment in periph.iter_csr_banks():
312 if bank_alignment is None:
313 bank_alignment = alignment
314 csr_mux = csr.Multiplexer(addr_width=1, data_width=8, alignment=bank_alignment)
315 for elem, elem_addr, elem_alignment in bank.iter_csr_regs():
316 if elem_alignment is None:
317 elem_alignment = alignment
318 csr_mux.add(elem, addr=elem_addr, alignment=elem_alignment, extend=True)
319
320 csr_bridge = WishboneCSRBridge(csr_mux.bus, data_width=data_width)
321 self._wb_decoder.add(csr_bridge.wb_bus, addr=bank_addr, extend=True)
322 self._csr_subs.append((csr_mux, csr_bridge))
323
324 for window, window_addr, window_sparse in periph.iter_windows():
325 self._wb_decoder.add(window, addr=window_addr, sparse=window_sparse, extend=True)
326
327 events = list(periph.iter_events())
328 if len(events) > 0:
329 self._int_src = InterruptSource(events, name="{}_ev".format(periph.name))
330 self.irq = self._int_src.irq
331
332 csr_mux = csr.Multiplexer(addr_width=1, data_width=8, alignment=alignment)
333 csr_mux.add(self._int_src.status, extend=True)
334 csr_mux.add(self._int_src.pending, extend=True)
335 csr_mux.add(self._int_src.enable, extend=True)
336
337 csr_bridge = WishboneCSRBridge(csr_mux.bus, data_width=data_width)
338 self._wb_decoder.add(csr_bridge.wb_bus, extend=True)
339 self._csr_subs.append((csr_mux, csr_bridge))
340 else:
341 self._int_src = None
342 self.irq = None
343
344 self.bus = self._wb_decoder.bus
345
346 def elaborate(self, platform):
347 m = Module()
348
349 for i, (csr_mux, csr_bridge) in enumerate(self._csr_subs):
350 m.submodules[ "csr_mux_{}".format(i)] = csr_mux
351 m.submodules["csr_bridge_{}".format(i)] = csr_bridge
352
353 if self._int_src is not None:
354 m.submodules._int_src = self._int_src
355
356 m.submodules.wb_decoder = self._wb_decoder
357
358 return m