lib.data: add reference documentation.
This commit is contained in:
parent
a5ffa38e64
commit
68e292c681
|
@ -12,6 +12,20 @@ __all__ = [
|
|||
|
||||
|
||||
class Field:
|
||||
"""Description of a data field.
|
||||
|
||||
The :class:`Field` class specifies the signedness and bit positions of a field in
|
||||
an Amaranth value.
|
||||
|
||||
:class:`Field` objects are immutable.
|
||||
|
||||
Attributes
|
||||
----------
|
||||
shape : :ref:`shape-castable <lang-shapecasting>`
|
||||
Shape of the field. When initialized or assigned, the object is stored as-is.
|
||||
offset : :class:`int`, >=0
|
||||
Index of the least significant bit of the field.
|
||||
"""
|
||||
def __init__(self, shape, offset):
|
||||
try:
|
||||
Shape.cast(shape)
|
||||
|
@ -34,9 +48,24 @@ class Field:
|
|||
|
||||
@property
|
||||
def width(self):
|
||||
"""Width of the field.
|
||||
|
||||
This property should be used over ``self.shape.width`` because ``self.shape`` can be
|
||||
an arbitrary :ref:`shape-castable <lang-shapecasting>` object, which may not have
|
||||
a ``width`` property.
|
||||
|
||||
Returns
|
||||
-------
|
||||
:class:`int`
|
||||
``Shape.cast(self.shape).width``
|
||||
"""
|
||||
return Shape.cast(self.shape).width
|
||||
|
||||
def __eq__(self, other):
|
||||
"""Compare fields.
|
||||
|
||||
Two fields are equal if they have the same shape and offset.
|
||||
"""
|
||||
return (isinstance(other, Field) and
|
||||
Shape.cast(self._shape) == Shape.cast(other.shape) and
|
||||
self._offset == other.offset)
|
||||
|
@ -46,9 +75,31 @@ class Field:
|
|||
|
||||
|
||||
class Layout(ShapeCastable, metaclass=ABCMeta):
|
||||
"""Description of a data layout.
|
||||
|
||||
The :ref:`shape-castable <lang-shapecasting>` :class:`Layout` interface associates keys
|
||||
(string names or integer indexes) with fields, giving identifiers to spans of bits in
|
||||
an Amaranth value.
|
||||
|
||||
It is an abstract base class; :class:`StructLayout`, :class:`UnionLayout`,
|
||||
:class:`ArrayLayout`, and :class:`FlexibleLayout` implement concrete layout rules.
|
||||
New layout rules can be defined by inheriting from this class.
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def cast(obj):
|
||||
"""Cast a shape-castable object to a layout."""
|
||||
"""Cast a :ref:`shape-castable <lang-shapecasting>` object to a layout.
|
||||
|
||||
This method performs a subset of the operations done by :meth:`Shape.cast`; it will
|
||||
recursively call ``.as_shape()``, but only until a layout is returned.
|
||||
|
||||
Raises
|
||||
------
|
||||
TypeError
|
||||
If ``obj`` cannot be converted to a :class:`Layout` instance.
|
||||
RecursionError
|
||||
If ``obj.as_shape()`` returns ``obj``.
|
||||
"""
|
||||
while isinstance(obj, ShapeCastable):
|
||||
if isinstance(obj, Layout):
|
||||
return obj
|
||||
|
@ -62,7 +113,13 @@ class Layout(ShapeCastable, metaclass=ABCMeta):
|
|||
|
||||
@staticmethod
|
||||
def of(obj):
|
||||
"""Extract the layout from a view."""
|
||||
"""Extract the layout that was used to create a view.
|
||||
|
||||
Raises
|
||||
------
|
||||
TypeError
|
||||
If ``obj`` is not a :class:`View` instance.
|
||||
"""
|
||||
if not isinstance(obj, View):
|
||||
raise TypeError("Object {!r} is not a data view"
|
||||
.format(obj))
|
||||
|
@ -70,21 +127,54 @@ class Layout(ShapeCastable, metaclass=ABCMeta):
|
|||
|
||||
@abstractmethod
|
||||
def __iter__(self):
|
||||
"""Iterate the layout, yielding ``(key, field)`` pairs. Keys may be strings or integers."""
|
||||
"""Iterate fields in the layout.
|
||||
|
||||
Yields
|
||||
------
|
||||
:class:`str` or :class:`int`
|
||||
Key (either name or index) for accessing the field.
|
||||
:class:`Field`
|
||||
Description of the field.
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def __getitem__(self, key):
|
||||
"""Retrieve the :class:`Field` associated with the ``key``, or raise ``KeyError``."""
|
||||
"""Retrieve a field from the layout.
|
||||
|
||||
size = abstractproperty()
|
||||
"""The number of bits in the representation defined by the layout."""
|
||||
Returns
|
||||
-------
|
||||
:class:`Field`
|
||||
The field associated with ``key``.
|
||||
|
||||
Raises
|
||||
------
|
||||
KeyError
|
||||
If there is no field associated with ``key``.
|
||||
"""
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def size(self):
|
||||
"""Size of the layout.
|
||||
|
||||
Returns
|
||||
-------
|
||||
:class:`int`
|
||||
The amount of bits required to store every field in the layout.
|
||||
"""
|
||||
|
||||
def as_shape(self):
|
||||
"""Convert the representation defined by the layout to an unsigned :class:`Shape`."""
|
||||
"""Shape of the layout.
|
||||
|
||||
Returns
|
||||
-------
|
||||
:class:`Shape`
|
||||
``unsigned(self.size)``
|
||||
"""
|
||||
return unsigned(self.size)
|
||||
|
||||
def __eq__(self, other):
|
||||
"""Compare the layout with another.
|
||||
"""Compare layouts.
|
||||
|
||||
Two layouts are equal if they have the same size and the same fields under the same names.
|
||||
The order of the fields is not considered.
|
||||
|
@ -123,6 +213,37 @@ class Layout(ShapeCastable, metaclass=ABCMeta):
|
|||
|
||||
|
||||
class StructLayout(Layout):
|
||||
"""Description of a structure layout.
|
||||
|
||||
The fields of a structure layout follow one another without any gaps, and the size of
|
||||
a structure layout is the sum of the sizes of its members.
|
||||
|
||||
For example, the following layout of a 16-bit value:
|
||||
|
||||
.. image:: _images/data/struct_layout.svg
|
||||
|
||||
can be described with:
|
||||
|
||||
.. testcode::
|
||||
|
||||
data.StructLayout({
|
||||
"first": 3,
|
||||
"second": 7,
|
||||
"third": 6
|
||||
})
|
||||
|
||||
.. note::
|
||||
|
||||
Structures that have padding can be described with a :class:`FlexibleLayout`. Alternately,
|
||||
padding can be added to the layout as fields called ``_1``, ``_2``, and so on. These fields
|
||||
won't be accessible as attributes or by using indexing.
|
||||
|
||||
Attributes
|
||||
----------
|
||||
members : mapping of :class:`str` to :ref:`shape-castable <lang-shapecasting>`
|
||||
Dictionary of structure members.
|
||||
"""
|
||||
|
||||
def __init__(self, members):
|
||||
self.members = members
|
||||
|
||||
|
@ -158,6 +279,14 @@ class StructLayout(Layout):
|
|||
|
||||
@property
|
||||
def size(self):
|
||||
"""Size of the structure layout.
|
||||
|
||||
Returns
|
||||
-------
|
||||
int
|
||||
Index of the most significant bit of the *last* field plus one; or zero if there are
|
||||
no fields.
|
||||
"""
|
||||
return max((field.offset + field.width for field in self._fields.values()), default=0)
|
||||
|
||||
def __repr__(self):
|
||||
|
@ -165,6 +294,30 @@ class StructLayout(Layout):
|
|||
|
||||
|
||||
class UnionLayout(Layout):
|
||||
"""Description of a union layout.
|
||||
|
||||
The fields of a union layout all start from bit 0, and the size of a union layout is the size
|
||||
of the largest of its members.
|
||||
|
||||
For example, the following layout of a 7-bit value:
|
||||
|
||||
.. image:: _images/data/union_layout.svg
|
||||
|
||||
can be described with:
|
||||
|
||||
.. testcode::
|
||||
|
||||
data.UnionLayout({
|
||||
"first": 3,
|
||||
"second": 7,
|
||||
"third": 6
|
||||
})
|
||||
|
||||
Attributes
|
||||
----------
|
||||
members : mapping of :class:`str` to :ref:`shape-castable <lang-shapecasting>`
|
||||
Dictionary of union members.
|
||||
"""
|
||||
def __init__(self, members):
|
||||
self.members = members
|
||||
|
||||
|
@ -198,6 +351,14 @@ class UnionLayout(Layout):
|
|||
|
||||
@property
|
||||
def size(self):
|
||||
"""Size of the union layout.
|
||||
|
||||
Returns
|
||||
-------
|
||||
int
|
||||
Index of the most significant bit of the *largest* field plus one; or zero if there are
|
||||
no fields.
|
||||
"""
|
||||
return max((field.width for field in self._fields.values()), default=0)
|
||||
|
||||
def __repr__(self):
|
||||
|
@ -205,6 +366,32 @@ class UnionLayout(Layout):
|
|||
|
||||
|
||||
class ArrayLayout(Layout):
|
||||
"""Description of an array layout.
|
||||
|
||||
The fields of an array layout follow one another without any gaps, and the size of an array
|
||||
layout is the size of its element multiplied by its length.
|
||||
|
||||
For example, the following layout of a 16-bit value:
|
||||
|
||||
.. image:: _images/data/array_layout.svg
|
||||
|
||||
can be described with::
|
||||
|
||||
.. testcode::
|
||||
|
||||
data.ArrayLayout(unsigned(4), 4)
|
||||
|
||||
.. note::
|
||||
|
||||
Arrays that have padding can be described with a :class:`FlexibleLayout`.
|
||||
|
||||
Attributes
|
||||
----------
|
||||
elem_shape : :ref:`shape-castable <lang-shapecasting>`
|
||||
Shape of an individual element.
|
||||
length : int
|
||||
Amount of elements.
|
||||
"""
|
||||
def __init__(self, elem_shape, length):
|
||||
self.elem_shape = elem_shape
|
||||
self.length = length
|
||||
|
@ -252,6 +439,13 @@ class ArrayLayout(Layout):
|
|||
|
||||
@property
|
||||
def size(self):
|
||||
"""Size of the array layout.
|
||||
|
||||
Returns
|
||||
-------
|
||||
int
|
||||
Size of an individual element multiplied by their amount.
|
||||
"""
|
||||
return Shape.cast(self._elem_shape).width * self.length
|
||||
|
||||
def __repr__(self):
|
||||
|
@ -259,12 +453,42 @@ class ArrayLayout(Layout):
|
|||
|
||||
|
||||
class FlexibleLayout(Layout):
|
||||
"""Description of a flexible layout.
|
||||
|
||||
The fields of a flexible layout can be located arbitrarily, and its size is explicitly defined.
|
||||
|
||||
For example, the following layout of a 16-bit value:
|
||||
|
||||
.. image:: _images/data/flexible_layout.svg
|
||||
|
||||
can be described with:
|
||||
|
||||
.. testcode::
|
||||
|
||||
data.FlexibleLayout(16, {
|
||||
"first": data.Field(unsigned(3), 1),
|
||||
"second": data.Field(unsigned(7), 0),
|
||||
"third": data.Field(unsigned(6), 10),
|
||||
0: data.Field(unsigned(1), 14)
|
||||
})
|
||||
|
||||
Both strings and integers can be used as names of flexible layout fields, so flexible layouts
|
||||
can be used to describe structures and arrays with arbitrary padding.
|
||||
|
||||
Attributes
|
||||
----------
|
||||
size : int
|
||||
Size of the layout.
|
||||
fields : mapping of :class:`str` or :class:`int` to :class:`Field`
|
||||
Fields defined in the layout.
|
||||
"""
|
||||
def __init__(self, size, fields):
|
||||
self.size = size
|
||||
self.fields = fields
|
||||
|
||||
@property
|
||||
def size(self):
|
||||
""":meta private:""" # work around Sphinx bug
|
||||
return self._size
|
||||
|
||||
@size.setter
|
||||
|
@ -319,6 +543,41 @@ class FlexibleLayout(Layout):
|
|||
|
||||
|
||||
class View(ValueCastable):
|
||||
"""A value viewed through the lens of a layout.
|
||||
|
||||
The :ref:`value-castable <lang-valuecasting>` class :class:`View` provides access to the fields
|
||||
of an underlying Amaranth value via the names or indexes defined in the provided layout.
|
||||
|
||||
Creating a view
|
||||
###############
|
||||
|
||||
When creating a view, either only the ``target`` argument, or any of the ``name``, ``reset``,
|
||||
``reset_less``, ``attrs``, or ``decoder`` arguments may be provided. If a target is provided,
|
||||
it is used as the underlying value. Otherwise, a new :class:`Signal` is created, and the rest
|
||||
of the arguments are passed to its constructor.
|
||||
|
||||
Accessing a view
|
||||
################
|
||||
|
||||
Slicing a view or accessing its attributes returns a part of the underlying value
|
||||
corresponding to the field with that index or name, which is always an Amaranth value, but
|
||||
it could also be a :class:`View` if the shape of the field is a :class:`Layout`, or
|
||||
an instance of the data class if the shape of the field is a class deriving from
|
||||
:class:`Struct` or :class:`Union`.
|
||||
|
||||
Slicing a view whose layout is an :class:`ArrayLayout` can be done with an index that is
|
||||
an Amaranth value instead of a constant integer. The returned element is chosen dynamically
|
||||
in that case.
|
||||
|
||||
Custom view classes
|
||||
###################
|
||||
|
||||
The :class:`View` class can be inherited from to define additional properties or methods on
|
||||
a view. The only two names that are reserved on instances of :class:`View` are :meth:`as_value`
|
||||
and :meth:`eq`, leaving the rest to the developer. The :class:`Struct` and :class:`Union`
|
||||
classes provided in this module are subclasses of :class:`View` that also provide a concise way
|
||||
to define a layout.
|
||||
"""
|
||||
def __init__(self, layout, target=None, *, name=None, reset=None, reset_less=None,
|
||||
attrs=None, decoder=None, src_loc_at=0):
|
||||
try:
|
||||
|
@ -356,12 +615,52 @@ class View(ValueCastable):
|
|||
|
||||
@ValueCastable.lowermethod
|
||||
def as_value(self):
|
||||
"""Get underlying value.
|
||||
|
||||
Returns
|
||||
-------
|
||||
:class:`Value`
|
||||
The ``target`` provided when constructing the view, or the :class:`Signal` that
|
||||
was created.
|
||||
"""
|
||||
return self.__target
|
||||
|
||||
def eq(self, other):
|
||||
"""Assign to the underlying value.
|
||||
|
||||
Returns
|
||||
-------
|
||||
:class:`Assign`
|
||||
``self.as_value().eq(other)``
|
||||
"""
|
||||
return self.as_value().eq(other)
|
||||
|
||||
def __getitem__(self, key):
|
||||
"""Slice the underlying value.
|
||||
|
||||
A field corresponding to ``key`` is looked up in the layout. If the field's shape is
|
||||
a :class:`Layout`, returns a :class:`View`. If it is a subclass of :class:`Struct` or
|
||||
:class:`Union`, returns an instance of that class. Otherwise, returns an unspecified
|
||||
Amaranth expression with the right shape.
|
||||
|
||||
Arguments
|
||||
---------
|
||||
key : :class:`str` or :class:`int` or :class:`ValueCastable`
|
||||
Name or index of a field.
|
||||
|
||||
Returns
|
||||
-------
|
||||
:class:`Value`, inout
|
||||
A slice of the underlying value defined by the field.
|
||||
|
||||
Raises
|
||||
------
|
||||
KeyError
|
||||
If the layout does not define a field corresponding to ``key``.
|
||||
TypeError
|
||||
If ``key`` is a value-castable object, but the layout of the view is not
|
||||
a :class:`ArrayLayout`.
|
||||
"""
|
||||
if isinstance(self.__layout, ArrayLayout):
|
||||
shape = self.__layout.elem_shape
|
||||
value = self.__target.word_select(key, Shape.cast(self.__layout.elem_shape).width)
|
||||
|
@ -383,6 +682,16 @@ class View(ValueCastable):
|
|||
return value
|
||||
|
||||
def __getattr__(self, name):
|
||||
"""Access a field of the underlying value.
|
||||
|
||||
Returns ``self[name]``.
|
||||
|
||||
Raises
|
||||
------
|
||||
AttributeError
|
||||
If the layout does not define a field called ``name``, or if ``name`` starts with
|
||||
an underscore.
|
||||
"""
|
||||
try:
|
||||
item = self[name]
|
||||
except KeyError:
|
||||
|
@ -459,8 +768,133 @@ class _Aggregate(View, metaclass=_AggregateMeta):
|
|||
|
||||
|
||||
class Struct(_Aggregate):
|
||||
"""Structures defined with annotations.
|
||||
|
||||
The :class:`Struct` base class is a subclass of :class:`View` that provides a concise way
|
||||
to describe the structure layout and reset values for the fields using Python
|
||||
:term:`variable annotations <python:variable annotation>`.
|
||||
|
||||
Any annotations containing :ref:`shape-castable <lang-shapecasting>` objects are used,
|
||||
in the order in which they appear in the source code, to construct a :class:`StructLayout`.
|
||||
The values assigned to such annotations are used to populate the reset value of the signal
|
||||
created by the view. Any other annotations are kept as-is.
|
||||
|
||||
.. testsetup::
|
||||
|
||||
from amaranth import *
|
||||
from amaranth.lib.data import *
|
||||
|
||||
As an example, a structure for `IEEE 754 single-precision floating-point format
|
||||
<https://en.wikipedia.org/wiki/Single-precision_floating-point_format>`_ can be defined as:
|
||||
|
||||
.. testcode::
|
||||
|
||||
class IEEE754Single(Struct):
|
||||
fraction: 23
|
||||
exponent: 8 = 0x7f
|
||||
sign: 1
|
||||
|
||||
def is_subnormal(self):
|
||||
return self.exponent == 0
|
||||
|
||||
The ``IEEE754Single`` class itself can be used where a :ref:`shape <lang-shapes>` is expected:
|
||||
|
||||
.. doctest::
|
||||
|
||||
>>> IEEE754Single.as_shape()
|
||||
StructLayout({'fraction': 23, 'exponent': 8, 'sign': 1})
|
||||
>>> Signal(IEEE754Single).width
|
||||
32
|
||||
|
||||
Instances of this class can be used where :ref:`values <lang-values>` are expected:
|
||||
|
||||
.. doctest::
|
||||
|
||||
>>> flt = IEEE754Single()
|
||||
>>> Signal(32).eq(flt)
|
||||
(eq (sig $signal) (sig flt))
|
||||
|
||||
Accessing shape-castable properties returns slices of the underlying value:
|
||||
|
||||
.. doctest::
|
||||
|
||||
>>> flt.fraction
|
||||
(slice (sig flt) 0:23)
|
||||
>>> flt.is_subnormal()
|
||||
(== (slice (sig flt) 23:31) (const 1'd0))
|
||||
|
||||
The reset values for individual fields can be overridden during instantiation:
|
||||
|
||||
.. doctest::
|
||||
|
||||
>>> hex(IEEE754Single().as_value().reset)
|
||||
'0x3f800000'
|
||||
>>> hex(IEEE754Single(reset={'sign': 1}).as_value().reset)
|
||||
'0xbf800000'
|
||||
>>> hex(IEEE754Single(reset={'exponent': 0}).as_value().reset)
|
||||
'0x0'
|
||||
|
||||
Classes inheriting from :class:`Struct` can be used as base classes. The only restrictions
|
||||
are that:
|
||||
|
||||
* Classes that do not define a layout cannot be instantiated or converted to a shape;
|
||||
* A layout can be defined exactly once in the inheritance hierarchy.
|
||||
|
||||
Behavior can be shared through inheritance:
|
||||
|
||||
.. testcode::
|
||||
|
||||
class HasChecksum(Struct):
|
||||
def checksum(self):
|
||||
bits = Value.cast(self)
|
||||
return sum(bits[n:n+8] for n in range(0, len(bits), 8))
|
||||
|
||||
class BareHeader(HasChecksum):
|
||||
address: 16
|
||||
length: 8
|
||||
|
||||
class HeaderWithParam(HasChecksum):
|
||||
address: 16
|
||||
length: 8
|
||||
param: 8
|
||||
|
||||
.. doctest::
|
||||
|
||||
>>> HasChecksum.as_shape()
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
TypeError: Aggregate class 'HasChecksum' does not have a defined shape
|
||||
>>> bare = BareHeader(); bare.checksum()
|
||||
(+ (+ (+ (const 1'd0) (slice (sig bare) 0:8)) (slice (sig bare) 8:16)) (slice (sig bare) 16:24))
|
||||
>>> param = HeaderWithParam(); param.checksum()
|
||||
(+ (+ (+ (+ (const 1'd0) (slice (sig param) 0:8)) (slice (sig param) 8:16)) (slice (sig param) 16:24)) (slice (sig param) 24:32))
|
||||
"""
|
||||
_AggregateMeta__layout_cls = StructLayout
|
||||
|
||||
|
||||
class Union(_Aggregate):
|
||||
"""Unions defined with annotations.
|
||||
|
||||
The :class:`Union` base class is a subclass of :class:`View` that provides a concise way
|
||||
to describe the union layout using Python :term:`variable annotations <python:variable
|
||||
annotation>`. It is very similar to the :class:`Struct` class, except that its layout
|
||||
is a :class:`UnionLayout`.
|
||||
|
||||
A :class:`Union` can have only one field with a specified reset value. If a reset value is
|
||||
explicitly provided during instantiation, it overrides the reset value specified with
|
||||
an annotation:
|
||||
|
||||
.. testcode::
|
||||
|
||||
class VarInt(Union):
|
||||
int8: 8
|
||||
int16: 16 = 0x100
|
||||
|
||||
.. doctest::
|
||||
|
||||
>>> VarInt().as_value().reset
|
||||
256
|
||||
>>> VarInt(reset={'int8': 10}).as_value().reset
|
||||
10
|
||||
"""
|
||||
_AggregateMeta__layout_cls = UnionLayout
|
||||
|
|
|
@ -25,10 +25,12 @@ While code that uses the features listed as deprecated below will work in Amaran
|
|||
Implemented RFCs
|
||||
----------------
|
||||
|
||||
.. _RFC 1: https://amaranth-lang.org/rfcs/0001-aggregate-data-structures.html
|
||||
.. _RFC 3: https://amaranth-lang.org/rfcs/0003-enumeration-shapes.html
|
||||
.. _RFC 4: https://amaranth-lang.org/rfcs/0004-const-castable-exprs.html
|
||||
.. _RFC 5: https://amaranth-lang.org/rfcs/0005-remove-const-normalize.html
|
||||
|
||||
* `RFC 1`_: Aggregate data structure library
|
||||
* `RFC 3`_: Enumeration shapes
|
||||
* `RFC 4`_: Constant-castable expressions
|
||||
* `RFC 5`_: Remove Const.normalize
|
||||
|
@ -46,7 +48,7 @@ Language changes
|
|||
* Changed: :meth:`Value.cast` casts :class:`ValueCastable` objects recursively.
|
||||
* Changed: :meth:`Value.cast` treats instances of classes derived from both :class:`enum.Enum` and :class:`int` (including :class:`enum.IntEnum`) as enumerations rather than integers.
|
||||
* Changed: :meth:`Value.matches` with an empty list of patterns returns ``Const(1)`` rather than ``Const(0)``, to match the behavior of ``with m.Case():``.
|
||||
* Changed: :class:`Cat` warns if an enumeration without an explicitly specified shape is used.
|
||||
* Changed: :class:`Cat` warns if an enumeration without an explicitly specified shape is used. (`RFC 3`_)
|
||||
* Deprecated: :meth:`Const.normalize`. (`RFC 5`_)
|
||||
* Removed: (deprecated in 0.1) casting of :class:`Shape` to and from a ``(width, signed)`` tuple.
|
||||
* Removed: (deprecated in 0.3) :class:`ast.UserValue`.
|
||||
|
@ -58,7 +60,8 @@ Standard library changes
|
|||
|
||||
.. currentmodule:: amaranth.lib
|
||||
|
||||
* Added: :mod:`amaranth.lib.enum`.
|
||||
* Added: :mod:`amaranth.lib.enum`. (`RFC 3`_)
|
||||
* Added: :mod:`amaranth.lib.data`. (`RFC 1`_)
|
||||
|
||||
|
||||
Toolchain changes
|
||||
|
|
|
@ -414,6 +414,14 @@ Signals assigned in a :ref:`combinatorial <lang-comb>` domain are not affected b
|
|||
True
|
||||
|
||||
|
||||
.. _lang-data:
|
||||
|
||||
Data structures
|
||||
===============
|
||||
|
||||
Amaranth provides aggregate data structures in the standard library module :mod:`amaranth.lib.data`.
|
||||
|
||||
|
||||
.. _lang-operators:
|
||||
|
||||
Operators
|
||||
|
|
|
@ -9,6 +9,7 @@ Standard library
|
|||
:maxdepth: 2
|
||||
|
||||
stdlib/enum
|
||||
stdlib/data
|
||||
stdlib/coding
|
||||
stdlib/cdc
|
||||
stdlib/fifo
|
||||
|
|
21
docs/stdlib/_images/data/array_layout.svg
Normal file
21
docs/stdlib/_images/data/array_layout.svg
Normal file
|
@ -0,0 +1,21 @@
|
|||
<!--
|
||||
Render the following with wavedrom-bitfield:
|
||||
|
||||
{
|
||||
"reg": [
|
||||
{"name": "[0]", "bits": 4},
|
||||
{"name": "[1]", "bits": 4},
|
||||
{"name": "[2]", "bits": 4},
|
||||
{"name": "[3]", "bits": 4},
|
||||
],
|
||||
"config": {
|
||||
"lanes": 1,
|
||||
"compact": true,
|
||||
"vflip": true,
|
||||
"hspace": 650
|
||||
}
|
||||
}
|
||||
|
||||
Then add 14 to height and viewBox.height.
|
||||
-->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="650" height="70" viewBox="0 0 650 70" class="WaveDrom"><g transform="translate(0.5,0.5)" text-anchor="middle" font-size="14" font-family="sans-serif" font-weight="normal"><g transform="translate(4,21)"><g stroke="black" stroke-width="1" stroke-linecap="round"><line x2="641"></line><line x1="641" x2="641" y2="31"></line><line x2="641" y1="31" y2="31"></line><line y2="31"></line><line x1="40" x2="40" y2="3"></line><line x1="40" x2="40" y1="31" y2="28"></line><line x1="80" x2="80" y2="3"></line><line x1="80" x2="80" y1="31" y2="28"></line><line x1="120" x2="120" y2="3"></line><line x1="120" x2="120" y1="31" y2="28"></line><line x1="160" x2="160" y2="31"></line><line x1="200" x2="200" y2="3"></line><line x1="200" x2="200" y1="31" y2="28"></line><line x1="240" x2="240" y2="3"></line><line x1="240" x2="240" y1="31" y2="28"></line><line x1="280" x2="280" y2="3"></line><line x1="280" x2="280" y1="31" y2="28"></line><line x1="321" x2="321" y2="31"></line><line x1="361" x2="361" y2="3"></line><line x1="361" x2="361" y1="31" y2="28"></line><line x1="401" x2="401" y2="3"></line><line x1="401" x2="401" y1="31" y2="28"></line><line x1="441" x2="441" y2="3"></line><line x1="441" x2="441" y1="31" y2="28"></line><line x1="481" x2="481" y2="31"></line><line x1="521" x2="521" y2="3"></line><line x1="521" x2="521" y1="31" y2="28"></line><line x1="561" x2="561" y2="3"></line><line x1="561" x2="561" y1="31" y2="28"></line><line x1="601" x2="601" y2="3"></line><line x1="601" x2="601" y1="31" y2="28"></line></g><g><g></g><g transform="translate(20,-11)"></g><g transform="translate(20,15)"><g transform="translate(60)"><text y="6"><tspan>[0]</tspan></text></g><g transform="translate(220)"><text y="6"><tspan>[1]</tspan></text></g><g transform="translate(381)"><text y="6"><tspan>[2]</tspan></text></g><g transform="translate(541)"><text y="6"><tspan>[3]</tspan></text></g></g><g transform="translate(20,39)"></g></g></g><g transform="translate(4,-3)"><g transform="translate(20,11)"><text y="6">0</text></g><g transform="translate(140,11)"><text y="6">3</text></g><g transform="translate(180,11)"><text y="6">4</text></g><g transform="translate(300,11)"><text y="6">7</text></g><g transform="translate(341,11)"><text y="6">8</text></g><g transform="translate(461,11)"><text y="6">11</text></g><g transform="translate(501,11)"><text y="6">12</text></g><g transform="translate(621,11)"><text y="6">15</text></g></g></g></svg>
|
After Width: | Height: | Size: 2.8 KiB |
27
docs/stdlib/_images/data/flexible_layout.svg
Normal file
27
docs/stdlib/_images/data/flexible_layout.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 8.7 KiB |
18
docs/stdlib/_images/data/rgb565_layout.svg
Normal file
18
docs/stdlib/_images/data/rgb565_layout.svg
Normal file
|
@ -0,0 +1,18 @@
|
|||
<!--
|
||||
Render the following with wavedrom-bitfield:
|
||||
|
||||
{
|
||||
"reg": [
|
||||
{"name": ".red", "bits": 5, type: 2},
|
||||
{"name": ".green", "bits": 6, type: 3},
|
||||
{"name": ".blue", "bits": 5, type: 4}
|
||||
],
|
||||
"config": {
|
||||
"lanes": 1,
|
||||
"compact": true,
|
||||
"vflip": true,
|
||||
"hspace": 650
|
||||
}
|
||||
}
|
||||
-->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="650" height="56" viewBox="0 0 650 56" class="WaveDrom"><g transform="translate(0.5,0.5)" text-anchor="middle" font-size="14" font-family="sans-serif" font-weight="normal"><g transform="translate(4,21)"><g stroke="black" stroke-width="1" stroke-linecap="round"><line x2="641"></line><line x1="641" x2="641" y2="31"></line><line x2="641" y1="31" y2="31"></line><line y2="31"></line><line x1="40" x2="40" y2="3"></line><line x1="40" x2="40" y1="31" y2="28"></line><line x1="80" x2="80" y2="3"></line><line x1="80" x2="80" y1="31" y2="28"></line><line x1="120" x2="120" y2="3"></line><line x1="120" x2="120" y1="31" y2="28"></line><line x1="160" x2="160" y2="3"></line><line x1="160" x2="160" y1="31" y2="28"></line><line x1="200" x2="200" y2="31"></line><line x1="240" x2="240" y2="3"></line><line x1="240" x2="240" y1="31" y2="28"></line><line x1="280" x2="280" y2="3"></line><line x1="280" x2="280" y1="31" y2="28"></line><line x1="321" x2="321" y2="3"></line><line x1="321" x2="321" y1="31" y2="28"></line><line x1="361" x2="361" y2="3"></line><line x1="361" x2="361" y1="31" y2="28"></line><line x1="401" x2="401" y2="3"></line><line x1="401" x2="401" y1="31" y2="28"></line><line x1="441" x2="441" y2="31"></line><line x1="481" x2="481" y2="3"></line><line x1="481" x2="481" y1="31" y2="28"></line><line x1="521" x2="521" y2="3"></line><line x1="521" x2="521" y1="31" y2="28"></line><line x1="561" x2="561" y2="3"></line><line x1="561" x2="561" y1="31" y2="28"></line><line x1="601" x2="601" y2="3"></line><line x1="601" x2="601" y1="31" y2="28"></line></g><g><g><rect width="200" height="31" field=".red" style="fill-opacity:0.1;fill:hsl(0,100%,50%)"></rect><rect x="200" width="240" height="31" field=".green" style="fill-opacity:0.1;fill:hsl(80,100%,50%)"></rect><rect x="441" width="200" height="31" field=".blue" style="fill-opacity:0.1;fill:hsl(170,100%,50%)"></rect></g><g transform="translate(20,-11)"></g><g transform="translate(20,15)"><g transform="translate(80)"><text y="6"><tspan>.red</tspan></text></g><g transform="translate(300)"><text y="6"><tspan>.green</tspan></text></g><g transform="translate(521)"><text y="6"><tspan>.blue</tspan></text></g></g><g transform="translate(20,39)"></g></g></g><g transform="translate(4,-3)"><g transform="translate(20,11)"><text y="6">0</text></g><g transform="translate(180,11)"><text y="6">4</text></g><g transform="translate(220,11)"><text y="6">5</text></g><g transform="translate(421,11)"><text y="6">10</text></g><g transform="translate(461,11)"><text y="6">11</text></g><g transform="translate(621,11)"><text y="6">15</text></g></g></g></svg>
|
After Width: | Height: | Size: 2.9 KiB |
20
docs/stdlib/_images/data/struct_layout.svg
Normal file
20
docs/stdlib/_images/data/struct_layout.svg
Normal file
|
@ -0,0 +1,20 @@
|
|||
<!--
|
||||
Render the following with wavedrom-bitfield:
|
||||
|
||||
{
|
||||
"reg": [
|
||||
{"name": ".first", "bits": 3},
|
||||
{"name": ".second", "bits": 7},
|
||||
{"name": ".third", "bits": 6}
|
||||
],
|
||||
"config": {
|
||||
"lanes": 1,
|
||||
"compact": true,
|
||||
"vflip": true,
|
||||
"hspace": 650
|
||||
}
|
||||
}
|
||||
|
||||
Then add 14 to height and viewBox.height.
|
||||
-->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="650" height="70" viewBox="0 0 650 70" class="WaveDrom"><g transform="translate(0.5,0.5)" text-anchor="middle" font-size="14" font-family="sans-serif" font-weight="normal"><g transform="translate(4,21)"><g stroke="black" stroke-width="1" stroke-linecap="round"><line x2="641"></line><line x1="641" x2="641" y2="31"></line><line x2="641" y1="31" y2="31"></line><line y2="31"></line><line x1="40" x2="40" y2="3"></line><line x1="40" x2="40" y1="31" y2="28"></line><line x1="80" x2="80" y2="3"></line><line x1="80" x2="80" y1="31" y2="28"></line><line x1="120" x2="120" y2="31"></line><line x1="160" x2="160" y2="3"></line><line x1="160" x2="160" y1="31" y2="28"></line><line x1="200" x2="200" y2="3"></line><line x1="200" x2="200" y1="31" y2="28"></line><line x1="240" x2="240" y2="3"></line><line x1="240" x2="240" y1="31" y2="28"></line><line x1="280" x2="280" y2="3"></line><line x1="280" x2="280" y1="31" y2="28"></line><line x1="321" x2="321" y2="3"></line><line x1="321" x2="321" y1="31" y2="28"></line><line x1="361" x2="361" y2="3"></line><line x1="361" x2="361" y1="31" y2="28"></line><line x1="401" x2="401" y2="31"></line><line x1="441" x2="441" y2="3"></line><line x1="441" x2="441" y1="31" y2="28"></line><line x1="481" x2="481" y2="3"></line><line x1="481" x2="481" y1="31" y2="28"></line><line x1="521" x2="521" y2="3"></line><line x1="521" x2="521" y1="31" y2="28"></line><line x1="561" x2="561" y2="3"></line><line x1="561" x2="561" y1="31" y2="28"></line><line x1="601" x2="601" y2="3"></line><line x1="601" x2="601" y1="31" y2="28"></line></g><g><g></g><g transform="translate(20,-11)"></g><g transform="translate(20,15)"><g transform="translate(40)"><text y="6"><tspan>.first</tspan></text></g><g transform="translate(240)"><text y="6"><tspan>.second</tspan></text></g><g transform="translate(501)"><text y="6"><tspan>.third</tspan></text></g></g><g transform="translate(20,39)"></g></g></g><g transform="translate(4,-3)"><g transform="translate(20,11)"><text y="6">0</text></g><g transform="translate(100,11)"><text y="6">2</text></g><g transform="translate(140,11)"><text y="6">3</text></g><g transform="translate(381,11)"><text y="6">9</text></g><g transform="translate(421,11)"><text y="6">10</text></g><g transform="translate(621,11)"><text y="6">15</text></g></g></g></svg>
|
After Width: | Height: | Size: 2.7 KiB |
22
docs/stdlib/_images/data/union_layout.svg
Normal file
22
docs/stdlib/_images/data/union_layout.svg
Normal file
|
@ -0,0 +1,22 @@
|
|||
<!--
|
||||
Render the following with wavedrom-bitfield:
|
||||
|
||||
{
|
||||
"reg": [
|
||||
{"name": ".third", "bits": 6},
|
||||
{"name": "", "bits": 1, "type": 1},
|
||||
{"name": ".second", "bits": 7},
|
||||
{"name": ".first", "bits": 3},
|
||||
{"name": "", "bits": 4, "type": 1},
|
||||
],
|
||||
"config": {
|
||||
"lanes": 3,
|
||||
"compact": true,
|
||||
"vflip": true,
|
||||
"hspace": 289.4375
|
||||
}
|
||||
}
|
||||
|
||||
Then add 14 to height and viewBox.height.
|
||||
-->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="289" height="132" viewBox="0 0 289 132" class="WaveDrom"><g transform="translate(0.5,0.5)" text-anchor="middle" font-size="14" font-family="sans-serif" font-weight="normal"><g transform="translate(4,83)"><g stroke="black" stroke-width="1" stroke-linecap="round"><line x2="280"></line><line x1="280" x2="280" y2="31"></line><line x2="280" y1="31" y2="31"></line><line y2="31"></line><line x1="40" x2="40" y2="3"></line><line x1="40" x2="40" y1="31" y2="28"></line><line x1="80" x2="80" y2="3"></line><line x1="80" x2="80" y1="31" y2="28"></line><line x1="120" x2="120" y2="3"></line><line x1="120" x2="120" y1="31" y2="28"></line><line x1="160" x2="160" y2="3"></line><line x1="160" x2="160" y1="31" y2="28"></line><line x1="200" x2="200" y2="3"></line><line x1="200" x2="200" y1="31" y2="28"></line><line x1="240" x2="240" y2="31"></line></g><g><g><rect x="240" width="40" height="31" field="" style="fill-opacity:0.1"></rect></g><g transform="translate(20,-11)"></g><g transform="translate(20,15)"><g transform="translate(100)"><text y="6"><tspan>.third</tspan></text></g><g transform="translate(240)"><text y="6"><tspan></tspan></text></g></g><g transform="translate(20,39)"></g></g></g><g transform="translate(4,52)"><g stroke="black" stroke-width="1" stroke-linecap="round"><line x2="280"></line><line x1="280" x2="280" y2="31"></line><line x2="280" y1="31" y2="31"></line><line y2="31"></line><line x1="40" x2="40" y2="3"></line><line x1="40" x2="40" y1="31" y2="28"></line><line x1="80" x2="80" y2="3"></line><line x1="80" x2="80" y1="31" y2="28"></line><line x1="120" x2="120" y2="3"></line><line x1="120" x2="120" y1="31" y2="28"></line><line x1="160" x2="160" y2="3"></line><line x1="160" x2="160" y1="31" y2="28"></line><line x1="200" x2="200" y2="3"></line><line x1="200" x2="200" y1="31" y2="28"></line><line x1="240" x2="240" y2="3"></line><line x1="240" x2="240" y1="31" y2="28"></line></g><g><g></g><g transform="translate(20,-11)"></g><g transform="translate(20,15)"><g transform="translate(120)"><text y="6"><tspan>.second</tspan></text></g></g><g transform="translate(20,39)"></g></g></g><g transform="translate(4,21)"><g stroke="black" stroke-width="1" stroke-linecap="round"><line x2="280"></line><line x1="280" x2="280" y2="31"></line><line x2="280" y1="31" y2="31"></line><line y2="31"></line><line x1="40" x2="40" y2="3"></line><line x1="40" x2="40" y1="31" y2="28"></line><line x1="80" x2="80" y2="3"></line><line x1="80" x2="80" y1="31" y2="28"></line><line x1="120" x2="120" y2="31"></line><line x1="160" x2="160" y2="3"></line><line x1="160" x2="160" y1="31" y2="28"></line><line x1="200" x2="200" y2="3"></line><line x1="200" x2="200" y1="31" y2="28"></line><line x1="240" x2="240" y2="3"></line><line x1="240" x2="240" y1="31" y2="28"></line></g><g><g><rect x="120" width="160" height="31" field="" style="fill-opacity:0.1"></rect></g><g transform="translate(20,-11)"></g><g transform="translate(20,15)"><g transform="translate(40)"><text y="6"><tspan>.first</tspan></text></g><g transform="translate(180)"><text y="6"><tspan></tspan></text></g></g><g transform="translate(20,39)"></g></g></g><g transform="translate(4,-3)"><g transform="translate(20,11)"><text y="6">0</text></g><g transform="translate(100,11)"><text y="6">2</text></g><g transform="translate(140,11)"><text y="6">3</text></g><g transform="translate(220,11)"><text y="6">5</text></g><g transform="translate(260,11)"><text y="6">6</text></g></g></g></svg>
|
After Width: | Height: | Size: 3.9 KiB |
228
docs/stdlib/data.rst
Normal file
228
docs/stdlib/data.rst
Normal file
|
@ -0,0 +1,228 @@
|
|||
Data structures
|
||||
###############
|
||||
|
||||
.. py:module:: amaranth.lib.data
|
||||
|
||||
The :mod:`amaranth.lib.data` module provides a way to describe the bitwise layout of values and a proxy class for accessing fields of values using the attribute access and indexing syntax.
|
||||
|
||||
|
||||
Introduction
|
||||
============
|
||||
|
||||
|
||||
Overview
|
||||
++++++++
|
||||
|
||||
This module provides four related facilities:
|
||||
|
||||
1. Low-level bitwise layout description via :class:`Field` and :class:`Layout`. These classes are rarely used directly, but are the foundation on which all other functionality is built. They are also useful for introspection.
|
||||
2. High-level bitwise layout description via :class:`StructLayout`, :class:`UnionLayout`, :class:`ArrayLayout`, and :class:`FlexibleLayout`. These classes are the ones most often used directly, in particular :class:`StructLayout` and :class:`ArrayLayout`.
|
||||
3. Data views via :class:`View` or its user-defined subclasses. This class is used to apply a layout description to a plain :class:`Value`, enabling structured access to its bits.
|
||||
4. Data classes :class:`Struct` and :class:`Union`. These classes are data views with a layout that is defined using Python :term:`variable annotations <python:variable annotation>` (also known as type annotations).
|
||||
|
||||
|
||||
Motivation
|
||||
++++++++++
|
||||
|
||||
The fundamental Amaranth type is a :class:`Value`: a sequence of bits that can also be used as a number. Manipulating values directly is sufficient for simple applications, but in more complex ones, values are often more than just a sequence of bits; they have well-defined internal structure.
|
||||
|
||||
.. testsetup::
|
||||
|
||||
from amaranth import *
|
||||
m = Module()
|
||||
|
||||
For example, consider a module that processes pixels, converting them from RGB to grayscale. The color pixel format is RGB565:
|
||||
|
||||
.. image:: _images/data/rgb565_layout.svg
|
||||
|
||||
This module could be implemented (using a fast but *very* approximate method) as follows:
|
||||
|
||||
.. testcode::
|
||||
|
||||
i_color = Signal(16)
|
||||
o_gray = Signal(8)
|
||||
|
||||
m.d.comb += o_gray.eq((i_color[0:5] + i_color[5:11] + i_color[11:16]) << 1)
|
||||
|
||||
While this implementation works, it is repetitive, error-prone, hard to read, and laborous to change; all because the color components are referenced using bit offsets. To improve it, the structure can be described with a :class:`Layout` so that the components can be referenced by name:
|
||||
|
||||
.. testcode::
|
||||
|
||||
from amaranth.lib import data, enum
|
||||
|
||||
rgb565_layout = data.StructLayout({
|
||||
"red": 5,
|
||||
"green": 6,
|
||||
"blue": 5
|
||||
})
|
||||
|
||||
i_color = data.View(rgb565_layout)
|
||||
o_gray = Signal(8)
|
||||
|
||||
m.d.comb += o_gray.eq((i_color.red + i_color.green + i_color.blue) << 1)
|
||||
|
||||
The :class:`View` is :ref:`value-castable <lang-valuecasting>` and can be used anywhere a plain value can be used. For example, it can be assigned to in the usual way:
|
||||
|
||||
.. testcode::
|
||||
|
||||
m.d.comb += i_color.eq(0) # everything is black
|
||||
|
||||
|
||||
Composing layouts
|
||||
+++++++++++++++++
|
||||
|
||||
Layouts are composable: a :class:`Layout` is a :ref:`shape <lang-shapes>` and can be used as a part of another layout. In this case, an attribute access through a view returns a view as well.
|
||||
|
||||
For example, consider a module that processes RGB pixels in groups of up to four at a time, provided by another module, and accumulates their average intensity:
|
||||
|
||||
.. testcode::
|
||||
|
||||
input_layout = data.StructLayout({
|
||||
"pixels": data.ArrayLayout(rgb565_layout, 4),
|
||||
"valid": 4
|
||||
})
|
||||
|
||||
i_stream = data.View(input_layout)
|
||||
r_accum = Signal(32)
|
||||
|
||||
m.d.sync += r_accum.eq(
|
||||
r_accum + sum((i_stream.pixels[n].red +
|
||||
i_stream.pixels[n].green +
|
||||
i_stream.pixels[n].blue)
|
||||
* i_stream.valid[n]
|
||||
for n in range(len(i_stream.valid))))
|
||||
|
||||
Note how the width of ``i_stream`` is never defined explicitly; it is instead inferred from the shapes of its fields.
|
||||
|
||||
In the previous section, the precise bitwise layout was important, since RGB565 is an interchange format. In this section however the exact bit positions do not matter, since the layout is only used internally to communicate between two modules in the same design. It is sufficient that both of them use the same layout.
|
||||
|
||||
|
||||
Defining layouts
|
||||
++++++++++++++++
|
||||
|
||||
Data layouts can be defined in a few different ways depending on the use case.
|
||||
|
||||
In case the data format is defined using a family of layouts instead of a single specific one, a function can be used:
|
||||
|
||||
.. testcode::
|
||||
|
||||
def rgb_layout(r_bits, g_bits, b_bits):
|
||||
return data.StructLayout({
|
||||
"red": unsigned(r_bits),
|
||||
"green": unsigned(g_bits),
|
||||
"blue": unsigned(b_bits)
|
||||
})
|
||||
|
||||
rgb565_layout = rgb_layout(5, 6, 5)
|
||||
rgb24_layout = rgb_layout(8, 8, 8)
|
||||
|
||||
In case the data has related operations or transformations, :class:`View` can be subclassed to define methods implementing them:
|
||||
|
||||
.. testcode::
|
||||
|
||||
class RGBLayout(data.View):
|
||||
def __init__(self, target=None, *, r_bits, g_bits, b_bits, **kwargs):
|
||||
super().__init__(layout=data.StructLayout({
|
||||
"red": unsigned(r_bits),
|
||||
"green": unsigned(g_bits),
|
||||
"blue": unsigned(b_bits)
|
||||
}, target=target, **kwargs))
|
||||
|
||||
def brightness(self):
|
||||
return (self.red + self.green + self.blue)[-8:]
|
||||
|
||||
Here, the ``RGBLayout`` class itself is :ref:`shape-castable <lang-shapecasting>` and can be used anywhere a shape is accepted.
|
||||
|
||||
In case the data format is static, :class:`Struct` (or :class:`Union`) can be subclassed instead of :class:`View`, to reduce the amount of boilerplate needed:
|
||||
|
||||
.. testcode::
|
||||
|
||||
class IEEE754Single(data.Struct):
|
||||
fraction: 23
|
||||
exponent: 8 = 0x7f
|
||||
sign: 1
|
||||
|
||||
def is_subnormal(self):
|
||||
return self.exponent == 0
|
||||
|
||||
|
||||
Discriminated unions
|
||||
++++++++++++++++++++
|
||||
|
||||
This module provides a :class:`UnionLayout`, which is rarely needed by itself, but is very useful in combination with a *discriminant*: a enumeration indicating which field of the union contains valid data.
|
||||
|
||||
For example, consider a module that can direct another module to perform one of a few operations, each of which requires its own parameters. The two modules could communicate through a channel with a layout like this:
|
||||
|
||||
.. testcode::
|
||||
|
||||
class Command(data.Struct):
|
||||
class Kind(enum.Enum):
|
||||
SET_ADDR = 0
|
||||
SEND_DATA = 1
|
||||
|
||||
valid : 1
|
||||
kind : Kind
|
||||
params : data.UnionLayout({
|
||||
"set_addr": data.StructLayout({
|
||||
"addr": unsigned(32)
|
||||
}),
|
||||
"send_data": data.StructLayout({
|
||||
"byte": unsigned(8)
|
||||
})
|
||||
})
|
||||
|
||||
Here, the shape of the ``Command`` is inferred, being large enough to accommodate the biggest of all defined parameter structures, and it is not necessary to manage it manually.
|
||||
|
||||
One module could submit a command with:
|
||||
|
||||
.. testcode::
|
||||
|
||||
cmd = Command()
|
||||
|
||||
m.d.comb += [
|
||||
cmd.valid.eq(1),
|
||||
cmd.kind.eq(Command.Kind.SET_ADDR),
|
||||
cmd.params.set_addr.addr.eq(0x00001234)
|
||||
]
|
||||
|
||||
The other would react to commands as follows:
|
||||
|
||||
.. testcode::
|
||||
|
||||
addr = Signal(32)
|
||||
|
||||
with m.If(cmd.valid):
|
||||
with m.Switch(cmd.kind):
|
||||
with m.Case(Command.Kind.SET_ADDR):
|
||||
m.d.sync += addr.eq(cmd.params.set_addr.addr)
|
||||
with m.Case(Command.Kind.SEND_DATA):
|
||||
...
|
||||
|
||||
|
||||
Modeling structured data
|
||||
========================
|
||||
|
||||
.. autoclass:: Field
|
||||
.. autoclass:: Layout
|
||||
|
||||
|
||||
Common data layouts
|
||||
===================
|
||||
|
||||
.. autoclass:: StructLayout
|
||||
.. autoclass:: UnionLayout
|
||||
.. autoclass:: ArrayLayout
|
||||
.. autoclass:: FlexibleLayout
|
||||
|
||||
|
||||
Data views
|
||||
==========
|
||||
|
||||
.. autoclass:: View
|
||||
|
||||
|
||||
Data classes
|
||||
============
|
||||
|
||||
.. autoclass:: Struct
|
||||
.. autoclass:: Union
|
Loading…
Reference in a new issue