CSR registers
The amaranth_soc.csr.reg
module provides a way to define and create CSR registers and register fields.
Introduction
Control and Status registers are commonly used as an interface between SoC peripherals and the firmware that operates them.
This module provides the following functionality:
Register field description and implementation via the
Field
andFieldAction
classes. Theamaranth_soc.csr.action
module provides a built-inFieldAction
subclasses for common use cases. If needed, users can implement their own subclasses.Composable layouts of register fields via
FieldActionMap
andFieldActionArray
. These classes are not meant to be instantiated directly, but are useful when introspecting the layout of a register.Register definitions via the
Register
class. The fields of a register can be provided as variable annotations or as instance parameters.A
Builder
class to organize registers of a peripheral into a hierarchy of clusters and arrays, to be converted into aMemoryMap
.A bridge between a CSR bus interface and the registers of a peripheral, via the
Bridge
class.
Examples
Defining a register declaratively
If its layout and access mode are known in advance, a register can be concisely defined using variable annotations:
class Status(csr.Register, access="rw"):
rdy: csr.Field(csr.action.R, 1)
err: csr.Field(csr.action.RW1C, 1)
_unimp: csr.Field(csr.action.ResR0W0, 6)
Note
By convention, names of reserved fields (such as _unimp
in the above example) should begin with an underscore.
Defining a register with instance parameters
If the layout or access mode of a register aren’t known until instantiation, a Register
subclass can override them in __init__
:
class Data(csr.Register):
def __init__(self, width=8, access="w"):
super().__init__(fields={"data": csr.Field(csr.action.W, width)},
access=access)
Defining a single-field register
In the previous example, the Data
register has a single field named "Data.data"
, which is redundant.
If no other fields are expected to be added in future revisions of the peripheral (or forward compatibility is not a concern), the field name can be omitted like so:
class Data(csr.Register, access="w"):
def __init__(self):
super().__init__(csr.Field(csr.action.W, 8))
Defining a register with nested fields
Hierarchical layouts of register fields can be expressed using lists and dicts:
class SetClr(csr.Register, access="r"):
pin: [{"set": csr.Field(csr.action.W, 1),
"clr": csr.Field(csr.action.W, 1)} for _ in range(8)]
Connecting registers to a CSR bus
In this example, the registers of FooPeripheral
are added to a Builder
to produce a memory map, and then bridged to a bus interface:
class FooPeripheral(wiring.Component):
class Ctrl(csr.Register, access="rw"):
enable: csr.Field(csr.action.RW, 1)
_unimp: csr.Field(csr.action.ResR0W0, 7)
class Data(csr.Register, access="r"):
def __init__(self, width):
super().__init__(csr.Field(csr.action.R, width))
def __init__(self):
regs = csr.Builder(addr_width=4, data_width=8)
reg_ctrl = regs.add("Ctrl", Ctrl())
reg_data = regs.add("Data", Data(width=32), offset=4)
self._bridge = csr.Bridge(regs.as_memory_map())
super().__init__({"csr_bus": In(csr.Signature(addr_width=4, data_width=8))})
self.csr_bus.memory_map = self._bridge.bus.memory_map
def elaborate(self, platform):
return Module() # ...
Defining a custom field action
If amaranth_soc.csr.action
built-ins do not cover a desired use case, a custom FieldAction
may provide an alternative.
This example shows a “read/write-0-to-set” field action:
class RW0S(csr.FieldAction):
def __init__(self, shape, init=0):
super().__init__(shape, access="rw", members={
"data": Out(shape),
"clear": In(shape),
})
self._storage = Signal(shape, init=init)
self._init = init
@property
def init(self):
return self._init
def elaborate(self, platform):
m = Module()
for i, storage_bit in enumerate(self._storage):
with m.If(self.clear[i]):
m.d.sync += storage_bit.eq(0)
with m.If(self.port.w_stb & ~self.port.w_data[i]):
m.d.sync += storage_bit.eq(1)
m.d.comb += [
self.port.r_data.eq(self._storage),
self.data.eq(self._storage),
]
return m
RW0S
can then be passed to Field
:
class Foo(csr.Register, access="rw"):
mask: csr.Field(RW0S, 8)
data: csr.Field(csr.action.RW, 8)
Fields
- class FieldPort.Access
Field access mode.
- R = 'r'
Read-only mode.
- W = 'w'
Write-only mode.
- RW = 'rw'
Read/write mode.
- NC = 'nc'
Not connected.
- class FieldPort.Signature
CSR register field port signature.
- Parameters:
shape (shape-like object) – Shape of the field.
access (
FieldPort.Access
) – Field access mode.
- Members:
r_data (
In(shape)
) – Read data. Must always be valid, and is sampled whenr_stb
is asserted.r_stb (
Out(1)
) – Read strobe. Fields with read side effects should perform them when this strobe is asserted.w_data (
Out(shape)
) – Write data. Valid only whenw_stb
is asserted.w_stb (
Out(1)
) – Write strobe. Fields should update their value or perform the write side effect when this strobe is asserted.
- create(*, path=None, src_loc_at=0)
Create a compatible interface.
See
amaranth.lib.wiring.Signature.create()
for details.- Return type:
- __eq__(other)
Compare signatures.
Two signatures are equal if they have the same shape and field access mode.
- class amaranth_soc.csr.reg.FieldPort
CSR register field port.
An interface between a
Register
and one of its fields.- Parameters:
signature (
FieldPort.Signature
) – Field port signature.path (iterable of
str
) – Path to the field port. Optional. Seeamaranth.lib.wiring.PureInterface
.
- class amaranth_soc.csr.reg.Field
Description of a CSR register field.
- Parameters:
action_cls (subclass of
FieldAction
) – The type of field action to be instantiated byField.create()
.*args (
tuple
) – Positional arguments passed toaction_cls.__init__
.**kwargs (
dict
) – Keyword arguments passed toaction_cls.__init__
.
- create()
Instantiate a field action.
- Returns:
The object returned by
action_cls(*args, **kwargs)
.- Return type:
Field actions
- class amaranth_soc.csr.reg.FieldAction
CSR register field action.
A component mediating access between a CSR bus and a range of bits within a
Register
.- Parameters:
shape (shape-like object) – Shape of the field.
access (
FieldPort.Access
) – Field access mode.members (iterable of (
str
,amaranth.lib.wiring.Member
) pairs, optional) – Additional signature members.
- Members:
port (
In(csr.reg.FieldPort.Signature(shape, access))
) – Field port.
- class amaranth_soc.csr.reg.FieldActionMap
A mapping of field actions.
- Parameters:
fields (
dict
ofstr
to (Field
ordict
orlist
)) –Register fields. Fields are instantiated according to their type:
a
Field
is instantiated as aFieldAction
;a
dict
is instantiated as aFieldActionMap
;a
list
is instantiated as aFieldActionArray
.
- __getitem__(key)
Access a field by name or index.
- Returns:
The field instance associated with
key
.- Return type:
- Raises:
KeyError – If there is no field instance associated with
key
.
- __getattr__(name)
Access a field by name.
- Returns:
The field instance associated with
name
.- Return type:
- Raises:
AttributeError – If the field map does not have a field instance associated with
name
.AttributeError – If
name
is reserved (i.e. starts with an underscore).
- flatten()
Recursively iterate over the field map.
- Yields:
iterable of
str
– Path of the field. It is prefixed by the name of every nestedFieldActionMap
orFieldActionArray
.FieldAction
– Field instance.
- class amaranth_soc.csr.reg.FieldActionArray
An array of CSR register fields.
- Parameters:
fields (
list
of (Field
ordict
orlist
)) –Register fields. Fields are instantiated according to their type:
a
Field
is instantiated as aFieldAction
;a
dict
is instantiated as aFieldActionMap
;a
list
is instantiated as aFieldActionArray
.
- __getitem__(key)
Access a field by index.
- Returns:
The field instance associated with
key
.- Return type:
- flatten()
Recursively iterate over the field array.
- Yields:
iterable of
str
– Path of the field. It is prefixed by the name of every nestedFieldActionMap
orFieldActionArray
.FieldAction
– Field instance.
Registers
- class amaranth_soc.csr.reg.Register
A CSR register.
- Parameters:
fields (
dict
orlist
orField
, optional) – Collection of register fields. If omitted, a dict is populated from Python variable annotations.fields
is used to create aFieldActionMap
,FieldActionArray
, orFieldAction
, depending on its type (dict
,list
, orField
).access (
Access
) – Element access mode.
- Members:
element (
In(csr.Element.Signature(shape, access))
) – Interface between thisRegister
and a CSR bus primitive.- Raises:
ValueError – If
fields
is notNone
and at least one variable annotation is aField
.ValueError – If
element.access
is not readable and at least one field is readable.ValueError – If
element.access
is not writable and at least one field is writable.
- field
Collection of field instances.
- Return type:
- f
Shorthand for
Register.field
.- Return type:
- __iter__()
Recursively iterate over the field collection.
- Yields:
iterable of
str
– Path of the field. It is prefixed by the name of every nestedFieldActionMap
orFieldActionArray
.FieldAction
– Field instance.
- class amaranth_soc.csr.reg.Builder
CSR builder.
A CSR builder collects a group of
Register
s within an address range with the goal of producing aMemoryMap
of the resulting layout.- Parameters:
- Raises:
ValueError – If
data_width
is not a multiple ofgranularity
.
- add(name, reg, *, offset=None)
Add a register.
- Parameters:
- Returns:
reg
, which is added to the builder. Its name isname
, prefixed by the names and indices of any parentCluster()
andIndex()
.- Return type:
- Raises:
ValueError – If the builder is frozen.
ValueError – If
reg
is already added to the builder.ValueError – If
offset
is not a multiple ofself.data_width // self.granularity
.
- as_memory_map()
Build a memory map.
If a register was added without an explicit
offset
, the implicit next address of the memory map is used. Otherwise, the register address isoffset * granularity // data_width
.Registers are added to the memory map in the same order as they were added to the builder.
- Return type:
- class amaranth_soc.csr.reg.Bridge
CSR bridge.
- Parameters:
- Members:
bus (
In(csr.Signature(memory_map.addr_width, memory_map.data_width))
) – CSR bus providing access to the contents ofmemory_map
.- Raises:
ValueError – If
memory_map
has windows.