Skip to content

Commit 0b0644e

Browse files
committed
docs/csr/reg: add guide-level documentation.
Also, fix failing doctests.
1 parent 4b4824d commit 0b0644e

File tree

8 files changed

+307
-96
lines changed

8 files changed

+307
-96
lines changed

amaranth_soc/csr/bus.py

-2
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,8 @@ class Access(enum.Enum):
3636

3737
#: Read-only mode.
3838
R = "r"
39-
4039
#: Write-only mode.
4140
W = "w"
42-
4341
#: Read/write mode.
4442
RW = "rw"
4543

amaranth_soc/csr/reg.py

+32-36
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,14 @@ class FieldPort(wiring.PureInterface):
3131

3232
class Access(enum.Enum):
3333
"""Field access mode."""
34+
35+
#: Read-only mode.
3436
R = "r"
37+
#: Write-only mode.
3538
W = "w"
39+
#: Read/write mode.
3640
RW = "rw"
41+
#: Not connected.
3742
NC = "nc"
3843

3944
def readable(self):
@@ -42,7 +47,7 @@ def readable(self):
4247
Returns
4348
-------
4449
:class:`bool`
45-
``True`` if `self` is equal to :attr:`R` or :attr:`RW`.
50+
``True`` if equal to :attr:`R` or :attr:`RW`.
4651
"""
4752
return self == self.R or self == self.RW
4853

@@ -52,7 +57,7 @@ def writable(self):
5257
Returns
5358
-------
5459
:class:`bool`
55-
``True`` if `self` is equal to :attr:`W` or :attr:`RW`.
60+
``True`` if equal to :attr:`W` or :attr:`RW`.
5661
"""
5762
return self == self.W or self == self.RW
5863

@@ -178,7 +183,7 @@ class Field:
178183
179184
Arguments
180185
---------
181-
action_cls : :class:`FieldAction` subclass
186+
action_cls : subclass of :class:`FieldAction`
182187
The type of field action to be instantiated by :meth:`Field.create`.
183188
*args : :class:`tuple`
184189
Positional arguments passed to ``action_cls.__init__``.
@@ -211,22 +216,16 @@ class FieldAction(wiring.Component):
211216
Arguments
212217
---------
213218
shape : :ref:`shape-like object <lang-shapelike>`
214-
Shape of the field. See :class:`FieldPort.Signature`.
219+
Shape of the field.
215220
access : :class:`FieldPort.Access`
216-
Field access mode. See :class:`FieldPort.Signature`.
217-
members : iterable of (:class:`str`, :class:`amaranth.lib.wiring.Member`) key/value pairs
218-
Signature members. Optional, defaults to ``()``. A :class:`FieldPort.Signature` member
219-
named 'port' and oriented as input is always present in addition to these members.
221+
Field access mode.
222+
members : iterable of (:class:`str`, :class:`amaranth.lib.wiring.Member`) pairs, optional
223+
Additional signature members.
220224
221225
Members
222226
-------
223227
port : :py:`In(csr.reg.FieldPort.Signature(shape, access))`
224228
Field port.
225-
226-
Raises
227-
------
228-
:exc:`ValueError`
229-
If the key 'port' is used in ``members``.
230229
"""
231230
def __init__(self, shape, access, members=()):
232231
members = dict(members)
@@ -247,7 +246,7 @@ class FieldActionMap(Mapping):
247246
fields : :class:`dict` of :class:`str` to (:class:`Field` or :class:`dict` or :class:`list`)
248247
Register fields. Fields are instantiated according to their type:
249248
250-
- a :class:`Field` is instantiated as a :class:`FieldAction` (see :meth:`Field.create`);
249+
- a :class:`Field` is instantiated as a :class:`FieldAction`;
251250
- a :class:`dict` is instantiated as a :class:`FieldActionMap`;
252251
- a :class:`list` is instantiated as a :class:`FieldActionArray`.
253252
"""
@@ -360,7 +359,7 @@ class FieldActionArray(Sequence):
360359
fields : :class:`list` of (:class:`Field` or :class:`dict` or :class:`list`)
361360
Register fields. Fields are instantiated according to their type:
362361
363-
- a :class:`Field` is instantiated as a :class:`FieldAction` (see :meth:`Field.create`);
362+
- a :class:`Field` is instantiated as a :class:`FieldAction`;
364363
- a :class:`dict` is instantiated as a :class:`FieldActionMap`;
365364
- a :class:`list` is instantiated as a :class:`FieldActionArray`.
366365
"""
@@ -404,7 +403,7 @@ def __len__(self):
404403
return len(self._fields)
405404

406405
def flatten(self):
407-
"""Iterate recursively over the field array.
406+
"""Recursively iterate over the field array.
408407
409408
Yields
410409
------
@@ -423,17 +422,17 @@ def flatten(self):
423422

424423

425424
class Register(wiring.Component):
426-
_doc_template = """
427-
A CSR register.
425+
"""A CSR register.
428426
429427
Arguments
430428
---------
431-
fields : :class:`dict` or :class:`list` or :class:`Field`
432-
Collection of register fields. If ``None`` (default), a dict is populated from Python
433-
:term:`variable annotations <python:variable annotation>`. ``fields`` is used to create
429+
fields : :class:`dict` or :class:`list` or :class:`Field`, optional
430+
Collection of register fields. If omitted, a dict is populated from Python :term:`variable
431+
annotations <python:variable annotation>`. ``fields`` is used to create
434432
a :class:`FieldActionMap`, :class:`FieldActionArray`, or :class:`FieldAction`,
435433
depending on its type (:class:`dict`, :class:`list`, or :class:`Field`).
436-
{arguments}
434+
access : :class:`~.csr.bus.Element.Access`
435+
Element access mode.
437436
438437
Members
439438
-------
@@ -451,11 +450,6 @@ class Register(wiring.Component):
451450
If ``element.access`` is not writable and at least one field is writable.
452451
"""
453452

454-
__doc__ = _doc_template.format(arguments="""
455-
access : :class:`~.csr.bus.Element.Access`
456-
Element access mode.
457-
""")
458-
459453
def __init_subclass__(cls, *, access=None, **kwargs):
460454
if access is not None:
461455
# TODO(py3.9): Remove this. Python 3.8 and below use cls.__name__ in the error message
@@ -593,22 +587,22 @@ def elaborate(self, platform):
593587
class Builder:
594588
"""CSR builder.
595589
596-
A CSR builder can organize a group of :class:`Register`\\ s within an address range and convert
597-
it to a :class:`~.memory.MemoryMap` for consumption by other SoC primitives.
590+
A CSR builder collects a group of :class:`Register`\\ s within an address range with the goal
591+
of producing a :class:`~.memory.MemoryMap` of the resulting layout.
598592
599593
Arguments
600594
---------
601595
addr_width : :class:`int`
602596
Address width.
603597
data_width : :class:`int`
604598
Data width.
605-
granularity : :class:`int`
606-
Granularity. Optional, defaults to 8 bits.
599+
granularity : :class:`int`, optional
600+
Granularity. Defaults to 8 bits.
607601
608602
Raises
609603
------
610604
:exc:`ValueError`
611-
If ``granularity`` is not a divisor of ``data_width``
605+
If ``data_width`` is not a multiple of ``granularity``.
612606
"""
613607
def __init__(self, *, addr_width, data_width, granularity=8):
614608
if not isinstance(addr_width, int) or addr_width <= 0:
@@ -668,7 +662,7 @@ def freeze(self):
668662
self._frozen = True
669663

670664
def add(self, name, reg, *, offset=None):
671-
"""Add a CSR register.
665+
"""Add a register.
672666
673667
Arguments
674668
---------
@@ -758,7 +752,11 @@ def Index(self, index):
758752
def as_memory_map(self):
759753
"""Build a memory map.
760754
761-
.. todo:: explain address/offset conversions
755+
If a register was added without an explicit ``offset``, the :ref:`implicit next address
756+
<memory-implicit-next-address>` of the memory map is used. Otherwise, the register address
757+
is ``offset * granularity // data_width``.
758+
759+
Registers are added to the memory map in the same order as they were added to the builder.
762760
763761
Returns
764762
-------
@@ -781,8 +779,6 @@ def as_memory_map(self):
781779
class Bridge(wiring.Component):
782780
"""CSR bridge.
783781
784-
.. todo:: extended summary
785-
786782
Arguments
787783
---------
788784
memory_map : :class:`~.memory.MemoryMap`

docs/csr.rst

-4
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
11
CSR
22
===
33

4-
.. warning::
5-
6-
This manual is a work in progress and is seriously incomplete!
7-
84
.. toctree::
95
:maxdepth: 2
106

docs/csr/_images/csr-bus.png

43.3 KB
Loading

docs/csr/action.rst

+63-6
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,76 @@
1-
CSR field actions
2-
-----------------
1+
CSR fields
2+
----------
33

4-
.. warning::
4+
.. py:module:: amaranth_soc.csr.action
55
6-
This manual is a work in progress and is seriously incomplete!
6+
The :mod:`amaranth_soc.csr.action` module provides built-in :class:`~amaranth_soc.csr.reg.FieldAction` implementations intended for common use cases, which are split in three categories: :ref:`basic fields <csr-action-basic>` for numerical values, :ref:`flag fields <csr-action-flag>` for arrays of bits, and :ref:`reserved fields <csr-action-reserved>` to serve as placeholders for compatibility.
77

8-
.. py:module:: amaranth_soc.csr.action
8+
.. _csr-action-basic:
99

10-
The :mod:`amaranth_soc.csr.action` module provides built-in CSR field actions.
10+
Basic fields
11+
============
12+
13+
Such fields are either exclusively writable by a CSR bus initiator (e.g. :class:`W`, :class:`RW`) or the peripheral itself (e.g. :class:`R`). This effectively removes the possibility of a write conflict between a CSR bus initiator and the peripheral.
1114

1215
.. autoclass:: R()
1316
.. autoclass:: W()
1417
.. autoclass:: RW()
18+
19+
.. _csr-action-flag:
20+
21+
Flag fields
22+
===========
23+
24+
Flag fields may be concurrently written by a CSR bus initiator and the peripheral. Each bit of a flag field may be set or cleared independently of others.
25+
26+
Suggested use cases
27+
+++++++++++++++++++
28+
29+
- :class:`RW1C` flags may be used when a peripheral needs to notify the CPU of a given condition, such as an error or a pending interrupt. To acknowledge the notification, the CPU would then write 1 to the flag bit.
30+
31+
- :class:`RW1S` flags may be used for self-clearing bits, such as the enable bit of a one-shot timer. When the timer reaches its maximum value, it would automatically disable itself by clearing its enable bit.
32+
33+
- A pair of :class:`RW1C` and :class:`RW1S` flags may be used to target the same range of bits (e.g. that drives an array of GPIO pins). This allows a CSR bus initiator to set and clear bits in one write transaction (which is guaranteed to be atomic). If a single :class:`RW` field was used instead, a read-modify-write transaction would be needed, and would require locking to insure its atomicity in a multi-tasked environment.
34+
1535
.. autoclass:: RW1C()
1636
.. autoclass:: RW1S()
37+
38+
.. _csr-action-reserved:
39+
40+
Reserved fields
41+
===============
42+
43+
Reserved fields may be defined to provide placeholders for past, future or undocumented functions of a peripheral.
44+
45+
Suggested use cases
46+
+++++++++++++++++++
47+
48+
Reserved for future use (as value)
49+
..................................
50+
51+
A :class:`ResRAWL` field can be used as a placeholder to ensure forward compatibility of software binaries with future SoC revisions, where it may be replaced with a :ref:`basic field <csr-action-basic>`.
52+
53+
The value returned by reads (and written back) must have defined semantics (e.g. a no-op) that can be relied upon in future SoC revisions. When writing to this field, software drivers targeting the current SoC revision must set up an atomic read-modify-write transaction.
54+
55+
Reserved for future use (as flag)
56+
.................................
57+
58+
If a field is expected to be implemented as a :ref:`flag <csr-action-flag>` in a future SoC revision, it can be defined as a :class:`ResRAW0` field in the current revision to ensure forward compatibility of software binaries.
59+
60+
Software drivers targeting the current SoC revision should ignore the value returned by reads. Writing a value of 0 must be a no-op if the field is implemented in a future SoC revision.
61+
62+
Defined but deprecated
63+
......................
64+
65+
If a field was deprecated in a previous SoC revision, it can be replaced with a :class:`ResR0WA` field to ensure backward compatibility of software binaries.
66+
67+
The value of 0 returned by reads (and written back) must retain the semantics defined in the SoC revision where this field was deprecated.
68+
69+
Defined but unimplemented
70+
.........................
71+
72+
If a field is only implemented in some variants of a peripheral, it can be replaced by a :class:`ResR0W0` field in the others.
73+
1774
.. autoclass:: ResRAW0()
1875
.. autoclass:: ResRAWL()
1976
.. autoclass:: ResR0WA()

docs/csr/bus.rst

+27-18
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,17 @@ The :mod:`amaranth_soc.csr.bus` module contains primitives to implement and acce
1919
Introduction
2020
============
2121

22-
The CSR bus API provides unopinionated primitives for defining and connecting the *Control and Status Registers* of SoC peripherals, with an emphasis on safety and resource efficiency. It is composed of low-level :ref:`register interfaces <csr-bus-element>`, :ref:`register multiplexers <csr-bus-multiplexer>` that provide access to the registers of a peripheral, and :ref:`bus decoders <csr-bus-decoder>` that provide access to the registers of multiple peripherals.
22+
Overview
23+
++++++++
24+
25+
The CSR bus API provides unopinionated primitives for defining and connecting the *Control and Status Registers* of SoC peripherals, with an emphasis on safety and resource efficiency. It is composed of low-level :ref:`register interfaces <csr-bus-element>`, :ref:`multiplexers <csr-bus-multiplexer>` that provide access to the registers of a peripheral, and :ref:`bus decoders <csr-bus-decoder>` that provide access to subordinate bus interfaces.
26+
27+
This diagram shows a CSR bus decoder being used to provide access to the registers of two peripherals:
28+
29+
.. image:: _images/csr-bus.png
30+
31+
Examples
32+
========
2333

2434
.. _csr-bus-element:
2535

@@ -93,16 +103,15 @@ The following example shows a very basic timer peripheral with an 8-bit CSR bus
93103
]
94104
return m
95105

96-
def __init__(self, name="timer"):
106+
def __init__(self):
97107
super().__init__({
98108
"csr_bus": In(csr.Signature(addr_width=3, data_width=8)),
99109
})
100110

101111
self._reg_cnt = self.Cnt()
102112
self._reg_rst = self.Rst()
103113

104-
self.csr_bus.memory_map = MemoryMap(addr_width=3, data_width=8, alignment=2,
105-
name=name)
114+
self.csr_bus.memory_map = MemoryMap(addr_width=3, data_width=8, alignment=2)
106115
self.csr_bus.memory_map.add_resource(self._reg_cnt, size=3, name=("cnt",))
107116
self.csr_bus.memory_map.add_resource(self._reg_rst, size=3, name=("rst",))
108117

@@ -132,8 +141,8 @@ The following example shows a very basic timer peripheral with an 8-bit CSR bus
132141
>>> timer = BasicTimer()
133142
>>> for res_info in timer.csr_bus.memory_map.all_resources():
134143
... print(res_info)
135-
ResourceInfo(path=(('cnt',),), start=0x0, end=0x4, width=8)
136-
ResourceInfo(path=(('rst',),), start=0x4, end=0x8, width=8)
144+
ResourceInfo(path=(Name('cnt'),), start=0x0, end=0x4, width=8)
145+
ResourceInfo(path=(Name('rst'),), start=0x4, end=0x8, width=8)
137146

138147

139148
Registers are always accessed atomically, regardless of their size. Each register is split into chunks according to the CSR bus data width, and each chunk is assigned a consecutive address on the bus.
@@ -264,28 +273,28 @@ In the following example, a CSR decoder provides access to the registers of two
264273

265274
.. testcode::
266275

267-
timer0 = BasicTimer(name="timer0")
268-
timer1 = BasicTimer(name="timer1")
276+
timer0 = BasicTimer()
277+
timer1 = BasicTimer()
269278

270279
csr_dec = csr.Decoder(addr_width=16, data_width=8)
271-
csr_dec.add(timer0.csr_bus, addr=0x0000)
272-
csr_dec.add(timer1.csr_bus, addr=0x1000)
280+
csr_dec.add(timer0.csr_bus, addr=0x0000, name="timer0")
281+
csr_dec.add(timer1.csr_bus, addr=0x1000, name="timer1")
273282

274283
.. doctest::
275284

276285
>>> for res_info in csr_dec.bus.memory_map.all_resources():
277286
... print(res_info)
278-
ResourceInfo(path=('timer0', ('cnt',)), start=0x0, end=0x4, width=8)
279-
ResourceInfo(path=('timer0', ('rst',)), start=0x4, end=0x8, width=8)
280-
ResourceInfo(path=('timer1', ('cnt',)), start=0x1000, end=0x1004, width=8)
281-
ResourceInfo(path=('timer1', ('rst',)), start=0x1004, end=0x1008, width=8)
287+
ResourceInfo(path=(Name('timer0'), Name('cnt')), start=0x0, end=0x4, width=8)
288+
ResourceInfo(path=(Name('timer0'), Name('rst')), start=0x4, end=0x8, width=8)
289+
ResourceInfo(path=(Name('timer1'), Name('cnt')), start=0x1000, end=0x1004, width=8)
290+
ResourceInfo(path=(Name('timer1'), Name('rst')), start=0x1004, end=0x1008, width=8)
282291

283292
Although there is no functional difference between adding a group of registers directly to a :class:`Multiplexer` and adding them to multiple :class:`Multiplexer`\ s that are aggregated with a :class:`Decoder`, hierarchical CSR buses are useful for organizing a hierarchical design.
284293

285294
If many peripherals are directly served by a single :class:`Multiplexer`, a very large amount of ports will connect the peripheral registers to the multiplexer, and the cost of decoding logic would not be attributed to specific peripherals. With a :class:`Decoder`, only five signals per peripheral will be used, and the logic could be kept together with the peripheral.
286295

287-
Register interface
288-
==================
296+
Register interfaces
297+
===================
289298

290299
.. autoclass:: amaranth_soc.csr.bus::Element.Access()
291300
:no-members:
@@ -307,8 +316,8 @@ Register interface
307316

308317
.. _csr-bus-interface:
309318

310-
Bus interface
311-
=============
319+
Bus interfaces
320+
==============
312321

313322
.. autoclass:: Signature()
314323
:no-members:

0 commit comments

Comments
 (0)