Skip to content

Commit 2f47e70

Browse files
authored
RFC #57: Single-Field Register Definition and Usage Shortcut.
2 parents 2c7ad0a + e5654ba commit 2f47e70

File tree

1 file changed

+173
-0
lines changed

1 file changed

+173
-0
lines changed

text/0057-single-field-shortcut.md

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
- Start Date: 2024-03-15
2+
- RFC PR: [amaranth-lang/rfcs#57](https://github.com/amaranth-lang/rfcs/pull/57)
3+
- Amaranth SoC Issue: [amaranth-lang/amaranth-soc#78](https://github.com/amaranth-lang/amaranth-soc/issues/78)
4+
5+
# Single-Field Register Definition and Usage Shortcut
6+
7+
## Summary
8+
[summary]: #summary
9+
10+
Add a shortcut to `csr.Register` to enable easier and cleaner definition and use
11+
of registers which contain exactly one `csr.Field`.
12+
13+
## Motivation
14+
[motivation]: #motivation
15+
16+
Currently, use of a `csr.Register` in an amaranth-soc peripheral requires creation of a map (or list) of `csr.Field`s, which actually represent the register data and control access to each part. This is commonly done by creating a `Register` subclass. Each `Field` also needs a name (or index) in the `Register`. However, many registers in peripherals only contain one field, so the extra name and subclass are unnecessary and redundant. This RFC introduces a shortcut for constructing and using a `Register` containing exactly one un-named `Field`.
17+
18+
## Guide-level explanation
19+
[guide-level-explanation]: #guide-level-explanation
20+
21+
Consider the following simple peripheral which generates a toggling signal on bits of an output corresponding to bits set in a register:
22+
23+
```python
24+
from amaranth import *
25+
from amaranth.lib import wiring
26+
from amaranth.lib.wiring import In, Out
27+
from amaranth_soc import csr
28+
29+
class Toggle(wiring.Component):
30+
bus: In(csr.Signature(addr_width=1, data_width=32))
31+
toggle: Out(32)
32+
33+
class Toggle(csr.Register, access="rw"):
34+
toggle: csr.Field(csr.action.RW, 32)
35+
36+
def __init__(self):
37+
self._toggle = Toggle()
38+
39+
# ... bridge setup ...
40+
41+
def elaborate(self, platform):
42+
m = Module()
43+
44+
# ... bridge setup ...
45+
46+
for i in range(32):
47+
with m.If(self._toggle.f.toggle.data[i]):
48+
m.d.sync += self.toggle[i].eq(~self.toggle[i])
49+
with m.Else():
50+
m.d.sync += self.toggle[i].eq(0)
51+
52+
return m
53+
```
54+
55+
The `toggle` name is used, among other ways, to name the register class, to name
56+
the field within, and to access that field on the register instance during
57+
elaboration.
58+
59+
This can be simplified by passing the `csr.Field` directly to the `csr.Register`
60+
without enclosing it in a dict/list and naming/indexing it, or subclassing
61+
`csr.Register`. This also causes the `.f` member to access that Field directly
62+
instead of needing to provide a name/index.
63+
64+
This simplifies the design as follows, reducing clutter and redundancy:
65+
66+
```python
67+
from amaranth import *
68+
from amaranth.lib import wiring
69+
from amaranth.lib.wiring import In, Out
70+
from amaranth_soc import csr
71+
72+
class Toggle(wiring.Component):
73+
bus: In(csr.Signature(addr_width=1, data_width=32))
74+
toggle: Out(32)
75+
76+
def __init__(self):
77+
# Register can take the Field directly, eliminating the subclass
78+
self._toggle = csr.Register(csr.Field(csr.action.RW, 32), access="rw")
79+
80+
# ... bridge setup ...
81+
82+
def elaborate(self, platform):
83+
m = Module()
84+
85+
# ... bridge setup ...
86+
87+
for i in range(32):
88+
# FieldAction accessed directly, no need to specify `toggle` again
89+
with m.If(self._toggle.f.data[i]):
90+
m.d.sync += self.toggle[i].eq(~self.toggle[i])
91+
with m.Else():
92+
m.d.sync += self.toggle[i].eq(0)
93+
94+
return m
95+
```
96+
97+
## Reference-level explanation
98+
[reference-level-explanation]: #reference-level-explanation
99+
100+
(with reference to [RFC #16](https://github.com/amaranth-lang/rfcs/blob/main/text/0016-soc-csr-regs.md))
101+
102+
#### `csr.reg.Register`
103+
104+
The `csr.reg.Register` class describes a CSR register `Component`, with:
105+
- a `.__init__(self, fields=None, access=None)` constructor, where:
106+
* `fields` is either:
107+
- a `dict` that will be instantiated as a `FieldActionMap`;
108+
- a `list` that will be instantiated as a `FieldActionArray`;
109+
- a `Field` that will be instantiated as a `FieldAction`;
110+
- `None`; in this case a `FieldActionMap` is instantiated from `Field` objects in variable annotations.
111+
* `access` is either a `csr.Element.Access` value, or `None`.
112+
- a `.field` property, returning the instantiated `FieldActionMap`/`FieldActionArray`/`FieldAction`;
113+
- a `.f` property, as a shorthand to `self.field`;
114+
- a `.__iter__(self)` method that yields, for each contained field, a tuple containing its path (as a tuple of names or indices) and its instantiated `FieldAction`. If only a single `Field` was supplied, its path is always `tuple()`.
115+
116+
## Drawbacks
117+
[drawbacks]: #drawbacks
118+
119+
* A novice might not understand that it's possible to create a `Register` with multiple `Fields` if exposed only to code which uses single-`Field` `Register`s.
120+
* There is a difference between a `Register` with one named/indexed `Field` and with one unnamed `Field`.
121+
* Library code will be made more confusing by having `fields` refer to possibly only one field.
122+
* Downstream code will have to deal with a third type of object returned by the
123+
`.f` property which behaves unlike the current two.
124+
* BSP/doc generators will have to intelligently render a `Field` with an empty name and path (likely by omitting them, possibly by reusing the register's name).
125+
* An empty name is prohibited by `FieldActionMap`, so empty names are a novel
126+
problem.
127+
* The register does not know its own name independently of its container, so this may be difficult in practice.
128+
* Adding a second `Field` to a register created this way will break all accesses to the `.f` property and likely all BSP users (though the actual bus transactions to the first `Field` will not change).
129+
* This could be especially entertaining if the new `Field` names happen to
130+
be the same as properties of the first `Field`'s `FieldAction`.
131+
* BSP users will have to change all constants/functions named after just the
132+
register to instead use the register + the field name.
133+
134+
## Rationale and alternatives
135+
[rationale-and-alternatives]: #rationale-and-alternatives
136+
137+
* Simple and easily explainable change, easy to migrate code to, though no
138+
migration is even necessary
139+
* Minimal change to the library's existing code
140+
* Nicely reduces redundant names, types, and properties
141+
* Not strictly necessary, not having it will just make peripheral code a little
142+
more cluttered
143+
* Users could instead be taught to eliminate the subclass by passing a dict to
144+
the `Register` constructor and assign the `.f` property of interest to a
145+
local variable to de-clutter code
146+
147+
## Prior art
148+
[prior-art]: #prior-art
149+
150+
AVR microcontrollers have many registers where the field name is the same as the register name, though this is not always the case for single-field registers. Application code usually uses the register name in this case and ignores the field name.
151+
152+
STM32 microcontrollers are similar, but the field name is usually a suffix of the register name, as the field name is not expected to be globally unique. However, the generated BSP files do appear to always contain the field name at least somewhere.
153+
154+
Field arrays are already in some sense anonymous, being named by an integer instead of a string.
155+
156+
More experienced input is welcomed here.
157+
158+
## Unresolved questions
159+
[unresolved-questions]: #unresolved-questions
160+
161+
* ~~Should we change `fields` to `field` in the constructor and properties? This could break existing code.~~
162+
* The property will be `.field` and the constructor will be `fields=`.
163+
* ~~Should we add `field` too? This would access the same object/s as `fields` and so would make available the option to use the right plurality, though it would be more code and wouldn't force it.~~
164+
* The property will be `.field` and the constructor will be `fields=`.
165+
* ~~Should we force a `Register` with a single `Field` to always have that one be un-named? Would create backwards compatibility problems, but reduce ambiguity.~~
166+
* No, would cause backwards compatibility problems, which we don't want.
167+
* ~~Should we go further? `Register` could take a single `FieldAction` and automatically wrap it in a `Field` (and inherit its access mode).~~
168+
* No, unnecessarily convenient and not yet proved necessary by experience.
169+
170+
## Future possibilities
171+
[future-possibilities]: #future-possibilities
172+
173+
None obvious, this is a pretty small and self-contained idea.

0 commit comments

Comments
 (0)