5 __all__
= ["Element", "Interface", "Decoder"]
9 class Access(enum
.Enum
):
10 """Register access mode.
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.
20 return self
== self
.R
or self
== self
.RW
23 return self
== self
.W
or self
== self
.RW
25 """Peripheral-side CSR interface.
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.
34 Width of the register.
35 access : :class:`Access`
38 Name of the underlying record.
42 r_data : Signal(width)
43 Read data. Must be always valid, and is sampled when ``r_stb`` is asserted.
45 Read strobe. Registers with read side effects should perform the read side effect when this
47 w_data : Signal(width)
48 Write data. Valid only when ``w_stb`` is asserted.
50 Write strobe. Registers should update their value or perform the write side effect when
51 this strobe is asserted.
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}"
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}"
61 self
.access
= Element
.Access(access
)
64 if self
.access
.readable():
69 if self
.access
.writable():
74 super().__init
__(layout
, name
=name
, src_loc_at
=1)
77 class Interface(Record
):
78 """CPU-side CSR interface.
80 A low-level interface to a set of atomically readable and writable peripheral CSR registers.
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.
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
98 Address width. At most ``(2 ** addr_width) * data_width`` register bits will be available.
100 Data width. Registers are accessed in ``data_width`` sized chunks.
102 Name of the underlying record.
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.
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
115 w_data : Signal(data_width)
116 Write data. Must be valid when ``w_stb`` is asserted.
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
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}"
128 if not isinstance(data_width
, int) or data_width
<= 0:
129 raise ValueError("Data width must be a positive integer, not {!r}"
131 self
.addr_width
= addr_width
132 self
.data_width
= data_width
135 ("addr", addr_width
),
136 ("r_data", data_width
),
138 ("w_data", data_width
),
140 ], name
=name
, src_loc_at
=1)
143 class Decoder(Elaboratable
):
146 An address-based multiplexer for CSR registers implementing atomic updates.
151 Writes are registered, and are performed 1 cycle after ``w_stb`` is asserted.
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.
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.
179 Address width. See :class:`Interface`.
181 Data width. See :class:`Interface`.
183 Register alignment. The address assigned to each register will be a multiple of
188 bus : :class:`Interface`
189 CSR bus providing access to registers.
191 def __init__(self
, *, addr_width
, data_width
, alignment
=0):
192 self
.bus
= Interface(addr_width
=addr_width
, data_width
=data_width
)
194 if not isinstance(alignment
, int) or alignment
< 0:
195 raise ValueError("Alignment must be a non-negative integer, not {!r}"
197 self
.alignment
= alignment
200 self
._elements
= dict()
202 def add(self
, element
):
207 element : :class:`Element`
208 Interface of the register.
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.
216 if not isinstance(element
, Element
):
217 raise TypeError("Element must be an instance of csr.Element, not {!r}"
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
226 def align_to(self
, alignment
):
227 """Align the next register explicitly.
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.
237 Address of the next register.
239 if not isinstance(alignment
, int) or alignment
< 0:
240 raise ValueError("Alignment must be a non-negative integer, not {!r}"
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
248 def elaborate(self
, platform
):
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.
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
)
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
)
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
)
292 m
.d
.sync
+= shadow
.eq(0)
294 m
.d
.comb
+= self
.bus
.r_data
.eq(r_data_fanin
)