csr.bus: use proper enum instead of ad-hoc string enumeration.
[nmigen-soc.git] / nmigen_soc / csr / bus.py
1 import enum
2 from nmigen import *
3
4
5 __all__ = ["Element", "Interface", "Decoder"]
6
7
8 class Element(Record):
9 class Access(enum.Enum):
10 """Register access mode.
11
12 Coarse access mode for the entire register. Individual fields can have more restrictive
13 access mode, e.g. R/O fields can be a part of an R/W register.
14 """
15 R = "r"
16 W = "w"
17 RW = "rw"
18
19 def readable(self):
20 return self == self.R or self == self.RW
21
22 def writable(self):
23 return self == self.W or self == self.RW
24
25 """Peripheral-side CSR interface.
26
27 A low-level interface to a single atomically readable and writable register in a peripheral.
28 This interface supports any register width and semantics, provided that both reads and writes
29 always succeed and complete in one cycle.
30
31 Parameters
32 ----------
33 width : int
34 Width of the register.
35 access : :class:`Access`
36 Register access mode.
37 name : str
38 Name of the underlying record.
39
40 Attributes
41 ----------
42 r_data : Signal(width)
43 Read data. Must be always valid, and is sampled when ``r_stb`` is asserted.
44 r_stb : Signal()
45 Read strobe. Registers with read side effects should perform the read side effect when this
46 strobe is asserted.
47 w_data : Signal(width)
48 Write data. Valid only when ``w_stb`` is asserted.
49 w_stb : Signal()
50 Write strobe. Registers should update their value or perform the write side effect when
51 this strobe is asserted.
52 """
53 def __init__(self, width, access, *, name=None, src_loc_at=0):
54 if not isinstance(width, int) or width < 0:
55 raise ValueError("Width must be a non-negative integer, not {!r}"
56 .format(width))
57 if not isinstance(access, Element.Access) and access not in ("r", "w", "rw"):
58 raise ValueError("Access mode must be one of \"r\", \"w\", or \"rw\", not {!r}"
59 .format(access))
60 self.width = width
61 self.access = Element.Access(access)
62
63 layout = []
64 if self.access.readable():
65 layout += [
66 ("r_data", width),
67 ("r_stb", 1),
68 ]
69 if self.access.writable():
70 layout += [
71 ("w_data", width),
72 ("w_stb", 1),
73 ]
74 super().__init__(layout, name=name, src_loc_at=1)
75
76
77 class Interface(Record):
78 """CPU-side CSR interface.
79
80 A low-level interface to a set of atomically readable and writable peripheral CSR registers.
81
82 Operation
83 ---------
84
85 CSR registers mapped to the CSR bus are split into chunks according to the bus data width.
86 Each chunk is assigned a consecutive address on the bus. This allows accessing CSRs of any
87 size using any datapath width.
88
89 When the first chunk of a register is read, the value of a register is captured, and reads
90 from subsequent chunks of the same register return the captured values. When any chunk except
91 the last chunk of a register is written, the written value is captured; a write to the last
92 chunk writes the captured value to the register. This allows atomically accessing CSRs larger
93 than datapath width.
94
95 Parameters
96 ----------
97 addr_width : int
98 Address width. At most ``(2 ** addr_width) * data_width`` register bits will be available.
99 data_width : int
100 Data width. Registers are accessed in ``data_width`` sized chunks.
101 name : str
102 Name of the underlying record.
103
104 Attributes
105 ----------
106 addr : Signal(addr_width)
107 Address for reads and writes.
108 r_data : Signal(data_width)
109 Read data. Valid on the next cycle after ``r_stb`` is asserted.
110 r_stb : Signal()
111 Read strobe. If ``addr`` points to the first chunk of a register, captures register value
112 and causes read side effects to be performed (if any). If ``addr`` points to any chunk
113 of a register, latches the captured value to ``r_data``. Otherwise, latches zero
114 to ``r_data``.
115 w_data : Signal(data_width)
116 Write data. Must be valid when ``w_stb`` is asserted.
117 w_stb : Signal()
118 Write strobe. If ``addr`` points to the last chunk of a register, writes captured value
119 to the register and causes write side effects to be performed (if any). If ``addr`` points
120 to any chunk of a register, latches ``w_data`` to the captured value. Otherwise, does
121 nothing.
122 """
123
124 def __init__(self, *, addr_width, data_width, name=None):
125 if not isinstance(addr_width, int) or addr_width <= 0:
126 raise ValueError("Address width must be a positive integer, not {!r}"
127 .format(addr_width))
128 if not isinstance(data_width, int) or data_width <= 0:
129 raise ValueError("Data width must be a positive integer, not {!r}"
130 .format(data_width))
131 self.addr_width = addr_width
132 self.data_width = data_width
133
134 super().__init__([
135 ("addr", addr_width),
136 ("r_data", data_width),
137 ("r_stb", 1),
138 ("w_data", data_width),
139 ("w_stb", 1),
140 ], name=name, src_loc_at=1)
141
142
143 class Decoder(Elaboratable):
144 """CSR bus decoder.
145
146 An address-based multiplexer for CSR registers implementing atomic updates.
147
148 Latency
149 -------
150
151 Writes are registered, and are performed 1 cycle after ``w_stb`` is asserted.
152
153 Alignment
154 ---------
155
156 Because the CSR bus conserves logic and routing resources, it is common to e.g. access
157 a CSR bus with an *n*-bit data path from a CPU with a *k*-bit datapath (*k>n*) in cases
158 where CSR access latency is less important than resource usage. In this case, two strategies
159 are possible for connecting the CSR bus to the CPU:
160 * The CPU could access the CSR bus directly (with no intervening logic other than simple
161 translation of control signals). In this case, the register alignment should be set
162 to 1, and each *w*-bit register would occupy *ceil(w/n)* addresses from the CPU
163 perspective, requiring the same amount of memory instructions to access.
164 * The CPU could also access the CSR bus through a width down-converter, which would issue
165 *k/n* CSR accesses for each CPU access. In this case, the register alignment should be
166 set to *k/n*, and each *w*-bit register would occupy *ceil(w/k)* addresses from the CPU
167 perspective, requiring the same amount of memory instructions to access.
168
169 If alignment is greater than 1, it affects which CSR bus write is considered a write to
170 the last register chunk. For example, if a 24-bit register is used with a 8-bit CSR bus and
171 a CPU with a 32-bit datapath, a write to this register requires 4 CSR bus writes to complete
172 and the 4th write is the one that actually writes the value to the register. This allows
173 determining write latency solely from the amount of addresses the register occupies in
174 the CPU address space, and the width of the CSR bus.
175
176 Parameters
177 ----------
178 addr_width : int
179 Address width. See :class:`Interface`.
180 data_width : int
181 Data width. See :class:`Interface`.
182 alignment : int
183 Register alignment. The address assigned to each register will be a multiple of
184 ``2 ** alignment``.
185
186 Attributes
187 ----------
188 bus : :class:`Interface`
189 CSR bus providing access to registers.
190 """
191 def __init__(self, *, addr_width, data_width, alignment=0):
192 self.bus = Interface(addr_width=addr_width, data_width=data_width)
193
194 if not isinstance(alignment, int) or alignment < 0:
195 raise ValueError("Alignment must be a non-negative integer, not {!r}"
196 .format(alignment))
197 self.alignment = alignment
198
199 self._next_addr = 0
200 self._elements = dict()
201
202 def add(self, element):
203 """Add a register.
204
205 Arguments
206 ---------
207 element : :class:`Element`
208 Interface of the register.
209
210 Return value
211 ------------
212 An ``(addr, size)`` tuple, where ``addr`` is the address assigned to the first chunk of
213 the register, and ``size`` is the amount of chunks it takes, which may be greater than
214 ``element.size // self.data_width`` due to alignment.
215 """
216 if not isinstance(element, Element):
217 raise TypeError("Element must be an instance of csr.Element, not {!r}"
218 .format(element))
219
220 addr = self.align_to(self.alignment)
221 self._next_addr += (element.width + self.bus.data_width - 1) // self.bus.data_width
222 size = self.align_to(self.alignment) - addr
223 self._elements[addr] = element, size
224 return addr, size
225
226 def align_to(self, alignment):
227 """Align the next register explicitly.
228
229 Arguments
230 ---------
231 alignment : int
232 Register alignment. The address assigned to the next register will be a multiple of
233 ``2 ** alignment`` or ``2 ** self.alignment``, whichever is greater.
234
235 Return value
236 ------------
237 Address of the next register.
238 """
239 if not isinstance(alignment, int) or alignment < 0:
240 raise ValueError("Alignment must be a non-negative integer, not {!r}"
241 .format(alignment))
242
243 align_chunks = 1 << alignment
244 if self._next_addr % align_chunks != 0:
245 self._next_addr += align_chunks - (self._next_addr % align_chunks)
246 return self._next_addr
247
248 def elaborate(self, platform):
249 m = Module()
250
251 # Instead of a straightforward multiplexer for reads, use a per-element address comparator,
252 # AND the shadow register chunk with the comparator output, and OR all of those together.
253 # If the toolchain doesn't already synthesize multiplexer trees this way, this trick can
254 # save a significant amount of logic, since e.g. one 4-LUT can pack one 2-MUX, but two
255 # 2-AND or 2-OR gates.
256 r_data_fanin = 0
257
258 for elem_addr, (elem, elem_size) in self._elements.items():
259 shadow = Signal(elem.width, name="{}__shadow".format(elem.name))
260 if elem.access.writable():
261 m.d.comb += elem.w_data.eq(shadow)
262
263 # Enumerate every address used by the register explicitly, rather than using
264 # arithmetic comparisons, since some toolchains (e.g. Yosys) are too eager to infer
265 # carry chains for comparisons, even with a constant. (Register sizes don't have
266 # to be powers of 2.)
267 with m.Switch(self.bus.addr):
268 for chunk_offset in range(elem_size):
269 chunk_slice = slice(chunk_offset * self.bus.data_width,
270 (chunk_offset + 1) * self.bus.data_width)
271 with m.Case(elem_addr + chunk_offset):
272 if elem.access.readable():
273 chunk_r_stb = Signal(self.bus.data_width,
274 name="{}__r_stb_{}".format(elem.name, chunk_offset))
275 r_data_fanin |= Mux(chunk_r_stb, shadow[chunk_slice], 0)
276 if chunk_offset == 0:
277 m.d.comb += elem.r_stb.eq(self.bus.r_stb)
278 with m.If(self.bus.r_stb):
279 m.d.sync += shadow.eq(elem.r_data)
280 # Delay by 1 cycle, allowing reads to be pipelined.
281 m.d.sync += chunk_r_stb.eq(self.bus.r_stb)
282
283 if elem.access.writable():
284 if chunk_offset == elem_size - 1:
285 # Delay by 1 cycle, avoiding combinatorial paths through
286 # the CSR bus and into CSR registers.
287 m.d.sync += elem.w_stb.eq(self.bus.w_stb)
288 with m.If(self.bus.w_stb):
289 m.d.sync += shadow[chunk_slice].eq(self.bus.w_data)
290
291 with m.Default():
292 m.d.sync += shadow.eq(0)
293
294 m.d.comb += self.bus.r_data.eq(r_data_fanin)
295
296 return m