Skip to content

Commit 7772b47

Browse files
committed
docs/csr/bus: add guide-level documentation.
1 parent a40e28e commit 7772b47

File tree

3 files changed

+335
-78
lines changed

3 files changed

+335
-78
lines changed

amaranth_soc/csr/bus.py

Lines changed: 42 additions & 65 deletions
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
---------
@@ -172,7 +178,7 @@ def __repr__(self):
172178

173179

174180
class Signature(wiring.Signature):
175-
"""CPU-side CSR signature.
181+
"""CSR bus signature.
176182
177183
Arguments
178184
---------
@@ -265,22 +271,10 @@ def __repr__(self):
265271

266272

267273
class Interface(wiring.PureInterface):
268-
"""CPU-side CSR interface.
274+
"""CSR bus interface.
269275
270276
A low-level interface to a set of atomically readable and writable peripheral CSR registers.
271277
272-
.. note::
273-
274-
CSR registers mapped to the CSR bus are split into chunks according to the bus data width.
275-
Each chunk is assigned a consecutive address on the bus. This allows accessing CSRs of any
276-
size using any datapath width.
277-
278-
When the first chunk of a register is read, the value of a register is captured, and reads
279-
from subsequent chunks of the same register return the captured values. When any chunk
280-
except the last chunk of a register is written, the written value is captured; a write to
281-
the last chunk writes the captured value to the register. This allows atomically accessing
282-
CSRs larger than datapath width.
283-
284278
Arguments
285279
---------
286280
addr_width : :class:`int`
@@ -319,11 +313,15 @@ def data_width(self):
319313
def memory_map(self):
320314
"""Memory map of the bus.
321315
322-
.. todo:: setter
323-
324316
Returns
325317
-------
326318
:class:`~.memory.MemoryMap` or ``None``
319+
320+
Raises
321+
------
322+
:exc:`ValueError`
323+
If set to a memory map that does not have the same address and data widths as the bus
324+
interface.
327325
"""
328326
if self._memory_map is None:
329327
raise AttributeError(f"{self!r} does not have a memory map")
@@ -350,39 +348,33 @@ class Multiplexer(wiring.Component):
350348
351349
An address-based multiplexer for CSR registers implementing atomic updates.
352350
353-
This implementation assumes the following from the CSR bus:
354-
355-
* an initiator must have exclusive ownership over the multiplexer for the full duration of
356-
a register transaction;
357-
* an initiator must access a register in ascending order of addresses, but it may abort a
358-
transaction after any bus cycle.
359-
360351
Writes are registered, and are performed 1 cycle after ``w_stb`` is asserted.
361352
362353
.. note::
363354
364-
Because the CSR bus conserves logic and routing resources, it is common to e.g. access
365-
a CSR bus with an *n*-bit data path from a CPU with a *k*-bit datapath (*k>n*) in cases
366-
where CSR access latency is less important than resource usage.
367-
368-
In this case, two strategies are possible for connecting the CSR bus to the CPU:
369-
370-
* The CPU could access the CSR bus directly (with no intervening logic other than
371-
simple translation of control signals). In this case, the register alignment should
372-
be set to 1 (i.e. ``memory_map.alignment`` should be set to 0), and each *w*-bit
373-
register would occupy *ceil(w/n)* addresses from the CPU perspective, requiring the
374-
same amount of memory instructions to access.
375-
* The CPU could also access the CSR bus through a width down-converter, which would
376-
issue *k/n* CSR accesses for each CPU access. In this case, the register alignment
377-
should be set to *k/n*, and each *w*-bit register would occupy *ceil(w/k)* addresses
378-
from the CPU perspective, requiring the same amount of memory instructions to access.
379-
380-
If the register alignment (i.e. ``2 ** memory_map.alignment``) is greater than 1, it affects
381-
which CSR bus write is considered a write to the last register chunk. For example, if a 24-bit
382-
register is used with a 8-bit CSR bus and a CPU with a 32-bit datapath, a write to this
383-
register requires 4 CSR bus writes to complete, and the 4th write is the one that actually
384-
writes the value to the register. This allows determining write latency solely from the amount
385-
of addresses the register occupies in the CPU address space, and the width of the CSR bus.
355+
Because the CSR bus conserves logic and routing resources, it is common to e.g. bridge a CSR
356+
bus with a narrow *N*-bit datapath to a CPU with a wider *W*-bit datapath (*W>N*) in cases
357+
where CSR access latency is less important than resource usage.
358+
359+
In this case, two strategies are possible for connecting the CSR bus to the CPU:
360+
361+
* The CPU could access the CSR bus directly (with no intervening logic other than simple
362+
translation of control signals). The register alignment should be set to 1 (i.e.
363+
``memory_map.alignment`` should be 0), and each *R*-bit register would occupy
364+
*ceil(R/N)* addresses from the CPU perspective, requiring the same amount of memory
365+
instructions to access.
366+
367+
* The CPU could access the CSR bus through a width down-converter, which would issue
368+
*W/N* CSR accesses for each CPU access. The register alignment should be set to *W/N*,
369+
and each *R*-bit register would occupy *ceil(R/K)* addresses from the CPU perspective,
370+
requiring the same amount of memory instructions to access.
371+
372+
If the register alignment is greater than 1, it affects which CSR bus write is considered a
373+
write to the last register chunk. For example, if a 24-bit register is accessed through an
374+
8-bit CSR bus and a CPU with a 32-bit datapath, a write to this register requires 4 CSR bus
375+
writes to complete, and the last write is the one that actually writes the value to the
376+
register. This allows determining write latency solely from the amount of addresses occupied
377+
by the register in the CPU address space, and the CSR bus data width.
386378
387379
Arguments
388380
---------
@@ -674,19 +666,6 @@ class Decoder(wiring.Component):
674666
675667
An address decoder for subordinate CSR buses.
676668
677-
.. note::
678-
679-
Although there is no functional difference between adding a set of registers directly to
680-
a :class:`Multiplexer` and adding a set of registers to multiple :class:`Multiplexer`\\ s
681-
that are aggregated with a :class:`Decoder`, hierarchical CSR buses are useful for
682-
organizing a hierarchical design.
683-
684-
If many peripherals are directly served by a single :class:`Multiplexer`, a very large
685-
amount of ports will connect the peripheral registers with the :class:`Decoder`, and the
686-
cost of decoding logic would not be attributed to specific peripherals. With a
687-
:class:`Decoder`, only five signals per peripheral will be used, and the logic could be
688-
kept together with the peripheral.
689-
690669
Arguments
691670
---------
692671
addr_width : :class:`int`
@@ -724,8 +703,6 @@ def add(self, sub_bus, *, name=None, addr=None):
724703
725704
See :meth:`~.memory.MemoryMap.add_window` for details.
726705
727-
.. todo:: include exceptions raised in :meth:`~.memory.MemoryMap.add_window`
728-
729706
Returns
730707
-------
731708
:class:`tuple` of (:class:`int`, :class:`int`, :class:`int`)

0 commit comments

Comments
 (0)