Skip to content

Commit af7bda2

Browse files
committed
docs/csr/bus: add guide-level documentation.
1 parent f21b31e commit af7bda2

File tree

3 files changed

+335
-78
lines changed

3 files changed

+335
-78
lines changed

amaranth_soc/csr/bus.py

+42-65
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212

1313
class Element(wiring.PureInterface):
14-
"""Peripheral-side CSR interface.
14+
"""CSR register interface.
1515
1616
A low-level interface to a single atomically readable and writable register in a peripheral.
1717
This interface supports any register width and semantics, provided that both reads and writes
@@ -24,7 +24,7 @@ class Element(wiring.PureInterface):
2424
access : :class:`Element.Access`
2525
Register access mode.
2626
path : iterable of :class:`str`
27-
Path to this CSR interface. Optional. See :class:`amaranth.lib.wiring.PureInterface`.
27+
Path to this interface. Optional. See :class:`amaranth.lib.wiring.PureInterface`.
2828
"""
2929

3030
class Access(enum.Enum):
@@ -33,8 +33,14 @@ class Access(enum.Enum):
3333
Coarse access mode for the entire register. Individual fields can have more restrictive
3434
access mode, e.g. R/O fields can be a part of an R/W register.
3535
"""
36+
37+
#: Read-only mode.
3638
R = "r"
39+
40+
#: Write-only mode.
3741
W = "w"
42+
43+
#: Read/write mode.
3844
RW = "rw"
3945

4046
def readable(self):
@@ -43,7 +49,7 @@ def readable(self):
4349
Returns
4450
-------
4551
:class:`bool`
46-
``True`` if `self` is equal to :attr:`R` or :attr:`RW`.
52+
``True`` if equal to :attr:`R` or :attr:`RW`.
4753
"""
4854
return self == self.R or self == self.RW
4955

@@ -53,12 +59,12 @@ def writable(self):
5359
Returns
5460
-------
5561
:class:`bool`
56-
``True`` if `self` is equal to :attr:`W` or :attr:`RW`.
62+
``True`` if equal to :attr:`W` or :attr:`RW`.
5763
"""
5864
return self == self.W or self == self.RW
5965

6066
class Signature(wiring.Signature):
61-
"""Peripheral-side CSR signature.
67+
"""CSR register signature.
6268
6369
Arguments
6470
---------
@@ -179,7 +185,7 @@ def __repr__(self):
179185

180186

181187
class Signature(wiring.Signature):
182-
"""CPU-side CSR signature.
188+
"""CSR bus signature.
183189
184190
Arguments
185191
---------
@@ -272,22 +278,10 @@ def __repr__(self):
272278

273279

274280
class Interface(wiring.PureInterface):
275-
"""CPU-side CSR interface.
281+
"""CSR bus interface.
276282
277283
A low-level interface to a set of atomically readable and writable peripheral CSR registers.
278284
279-
.. note::
280-
281-
CSR registers mapped to the CSR bus are split into chunks according to the bus data width.
282-
Each chunk is assigned a consecutive address on the bus. This allows accessing CSRs of any
283-
size using any datapath width.
284-
285-
When the first chunk of a register is read, the value of a register is captured, and reads
286-
from subsequent chunks of the same register return the captured values. When any chunk
287-
except the last chunk of a register is written, the written value is captured; a write to
288-
the last chunk writes the captured value to the register. This allows atomically accessing
289-
CSRs larger than datapath width.
290-
291285
Arguments
292286
---------
293287
addr_width : :class:`int`
@@ -326,11 +320,15 @@ def data_width(self):
326320
def memory_map(self):
327321
"""Memory map of the bus.
328322
329-
.. todo:: setter
330-
331323
Returns
332324
-------
333325
:class:`~.memory.MemoryMap` or ``None``
326+
327+
Raises
328+
------
329+
:exc:`ValueError`
330+
If set to a memory map that does not have the same address and data widths as the bus
331+
interface.
334332
"""
335333
if self._memory_map is None:
336334
raise AttributeError(f"{self!r} does not have a memory map")
@@ -357,39 +355,33 @@ class Multiplexer(wiring.Component):
357355
358356
An address-based multiplexer for CSR registers implementing atomic updates.
359357
360-
This implementation assumes the following from the CSR bus:
361-
362-
* an initiator must have exclusive ownership over the multiplexer for the full duration of
363-
a register transaction;
364-
* an initiator must access a register in ascending order of addresses, but it may abort a
365-
transaction after any bus cycle.
366-
367358
Writes are registered, and are performed 1 cycle after ``w_stb`` is asserted.
368359
369360
.. note::
370361
371-
Because the CSR bus conserves logic and routing resources, it is common to e.g. access
372-
a CSR bus with an *n*-bit data path from a CPU with a *k*-bit datapath (*k>n*) in cases
373-
where CSR access latency is less important than resource usage.
374-
375-
In this case, two strategies are possible for connecting the CSR bus to the CPU:
376-
377-
* The CPU could access the CSR bus directly (with no intervening logic other than
378-
simple translation of control signals). In this case, the register alignment should
379-
be set to 1 (i.e. ``memory_map.alignment`` should be set to 0), and each *w*-bit
380-
register would occupy *ceil(w/n)* addresses from the CPU perspective, requiring the
381-
same amount of memory instructions to access.
382-
* The CPU could also access the CSR bus through a width down-converter, which would
383-
issue *k/n* CSR accesses for each CPU access. In this case, the register alignment
384-
should be set to *k/n*, and each *w*-bit register would occupy *ceil(w/k)* addresses
385-
from the CPU perspective, requiring the same amount of memory instructions to access.
386-
387-
If the register alignment (i.e. ``2 ** memory_map.alignment``) is greater than 1, it affects
388-
which CSR bus write is considered a write to the last register chunk. For example, if a 24-bit
389-
register is used with a 8-bit CSR bus and a CPU with a 32-bit datapath, a write to this
390-
register requires 4 CSR bus writes to complete, and the 4th write is the one that actually
391-
writes the value to the register. This allows determining write latency solely from the amount
392-
of addresses the register occupies in the CPU address space, and the width of the CSR bus.
362+
Because the CSR bus conserves logic and routing resources, it is common to e.g. bridge a CSR
363+
bus with a narrow *N*-bit datapath to a CPU with a wider *W*-bit datapath (*W>N*) in cases
364+
where CSR access latency is less important than resource usage.
365+
366+
In this case, two strategies are possible for connecting the CSR bus to the CPU:
367+
368+
* The CPU could access the CSR bus directly (with no intervening logic other than simple
369+
translation of control signals). The register alignment should be set to 1 (i.e.
370+
``memory_map.alignment`` should be 0), and each *R*-bit register would occupy
371+
*ceil(R/N)* addresses from the CPU perspective, requiring the same amount of memory
372+
instructions to access.
373+
374+
* The CPU could access the CSR bus through a width down-converter, which would issue
375+
*W/N* CSR accesses for each CPU access. The register alignment should be set to *W/N*,
376+
and each *R*-bit register would occupy *ceil(R/K)* addresses from the CPU perspective,
377+
requiring the same amount of memory instructions to access.
378+
379+
If the register alignment is greater than 1, it affects which CSR bus write is considered a
380+
write to the last register chunk. For example, if a 24-bit register is accessed through an
381+
8-bit CSR bus and a CPU with a 32-bit datapath, a write to this register requires 4 CSR bus
382+
writes to complete, and the last write is the one that actually writes the value to the
383+
register. This allows determining write latency solely from the amount of addresses occupied
384+
by the register in the CPU address space, and the CSR bus data width.
393385
394386
Arguments
395387
---------
@@ -681,19 +673,6 @@ class Decoder(wiring.Component):
681673
682674
An address decoder for subordinate CSR buses.
683675
684-
.. note::
685-
686-
Although there is no functional difference between adding a set of registers directly to
687-
a :class:`Multiplexer` and adding a set of registers to multiple :class:`Multiplexer`\\ s
688-
that are aggregated with a :class:`Decoder`, hierarchical CSR buses are useful for
689-
organizing a hierarchical design.
690-
691-
If many peripherals are directly served by a single :class:`Multiplexer`, a very large
692-
amount of ports will connect the peripheral registers with the :class:`Decoder`, and the
693-
cost of decoding logic would not be attributed to specific peripherals. With a
694-
:class:`Decoder`, only five signals per peripheral will be used, and the logic could be
695-
kept together with the peripheral.
696-
697676
Arguments
698677
---------
699678
addr_width : :class:`int`
@@ -733,8 +712,6 @@ def add(self, sub_bus, *, addr=None):
733712
734713
See :meth:`~.memory.MemoryMap.add_window` for details.
735714
736-
.. todo:: include exceptions raised in :meth:`~.memory.MemoryMap.add_window`
737-
738715
Returns
739716
-------
740717
:class:`tuple` of (:class:`int`, :class:`int`, :class:`int`)

0 commit comments

Comments
 (0)