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