lib.enum: add Enum wrappers that allow specifying shape.

See #756 and amaranth-lang/rfcs#3.
This commit is contained in:
Catherine 2023-02-20 22:58:38 +00:00
parent ef2e9fa809
commit 57612f1dce
10 changed files with 343 additions and 40 deletions

View file

@ -22,6 +22,17 @@ Apply the following changes to code written against Amaranth 0.3 to migrate it t
While code that uses the features listed as deprecated below will work in Amaranth 0.4, they will be removed in the next version.
Implemented RFCs
----------------
.. _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 3`_: Enumeration shapes
* `RFC 4`_: Constant-castable expressions
* `RFC 5`_: Remove Const.normalize
Language changes
----------------
@ -30,19 +41,24 @@ Language changes
* Added: :class:`ShapeCastable`, similar to :class:`ValueCastable`.
* Added: :meth:`Value.as_signed` and :meth:`Value.as_unsigned` can be used on left-hand side of assignment (with no difference in behavior).
* Added: :meth:`Const.cast`, evaluating constant-castable values and returning a :class:`Const`. (`RFC 4`_)
* Added: :meth:`Const.cast`. (`RFC 4`_)
* Added: :meth:`Value.matches` and ``with m.Case():`` accept any constant-castable objects. (`RFC 4`_)
* 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: ``Value.matches()`` with an empty list of patterns returns ``Const(1)`` rather than ``Const(0)``, to match ``with m.Case():``.
* Changed: :class:`Cat` accepts instances of classes derived from both :class:`enum.Enum` and :class:`int` (including :class:`enum.IntEnum`) without warning.
* 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.
* 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`.
* Removed: (deprecated in 0.3) support for ``# nmigen:`` linter instructions at the beginning of file.
.. _RFC 4: https://amaranth-lang.org/rfcs/0004-const-castable-exprs.html
.. _RFC 5: https://amaranth-lang.org/rfcs/0005-remove-const-normalize.html
Standard library changes
------------------------
.. currentmodule:: amaranth.lib
* Added: :mod:`amaranth.lib.enum`.
Toolchain changes

View file

@ -27,9 +27,17 @@ intersphinx_mapping = {"python": ("https://docs.python.org/3", None)}
todo_include_todos = True
autodoc_member_order = "bysource"
autodoc_default_options = {
"members": True
}
autodoc_preserve_defaults = True
napoleon_google_docstring = False
napoleon_numpy_docstring = True
napoleon_use_ivar = True
napoleon_include_init_with_doc = True
napoleon_include_special_with_doc = True
napoleon_custom_sections = ["Platform overrides"]
html_theme = "sphinx_rtd_theme"

View file

@ -183,11 +183,7 @@ Specifying a shape with a range is convenient for counters, indexes, and all oth
Shapes from enumerations
------------------------
Casting a shape from an :class:`enum.Enum` subclass ``E``:
* fails if any of the enumeration members have non-integer values,
* has a width large enough to represent both ``min(m.value for m in E)`` and ``max(m.value for m in E)``, and
* is signed if either ``min(m.value for m in E)`` or ``max(m.value for m in E)`` are negative, unsigned otherwise.
Casting a shape from an :class:`enum.Enum` subclass requires all of the enumeration members to have :ref:`constant-castable <lang-constcasting>` values. The shape has a width large enough to represent the value of every member, and is signed only if there is a member with a negative value.
Specifying a shape with an enumeration is convenient for finite state machines, multiplexers, complex control signals, and all other values whose width is derived from a few distinct choices they must be able to fit:
@ -208,9 +204,27 @@ Specifying a shape with an enumeration is convenient for finite state machines,
>>> Shape.cast(Direction)
unsigned(2)
The :mod:`amaranth.lib.enum` module extends the standard enumerations such that their shape can be specified explicitly when they are defined:
.. testsetup::
import amaranth.lib.enum
.. testcode::
class Funct4(amaranth.lib.enum.Enum, shape=unsigned(4)):
ADD = 0
SUB = 1
MUL = 2
.. doctest::
>>> Shape.cast(Funct4)
unsigned(4)
.. note::
The enumeration does not have to subclass :class:`enum.IntEnum`; it only needs to have integers as values of every member. Using enumerations based on :class:`enum.Enum` rather than :class:`enum.IntEnum` prevents unwanted implicit conversion of enum members to integers.
The enumeration does not have to subclass :class:`enum.IntEnum` or have :class:`int` as one of its base classes; it only needs to have integers as values of every member. Using enumerations based on :class:`enum.Enum` rather than :class:`enum.IntEnum` prevents unwanted implicit conversion of enum members to integers.
.. _lang-valuecasting:

View file

@ -8,6 +8,7 @@ Standard library
.. toctree::
:maxdepth: 2
stdlib/enum
stdlib/coding
stdlib/cdc
stdlib/fifo
stdlib/fifo

43
docs/stdlib/enum.rst Normal file
View file

@ -0,0 +1,43 @@
Enumerations
############
.. py:module:: amaranth.lib.enum
The :mod:`amaranth.lib.enum` module is a drop-in replacement for the standard :mod:`enum` module that provides extended :class:`Enum`, :class:`IntEnum`, :class:`Flag`, and :class:`IntFlag` classes with the ability to specify a shape explicitly.
A shape can be specified for an enumeration with the ``shape=`` keyword argument:
.. testsetup::
from amaranth import *
.. testcode::
from amaranth.lib import enum
class Funct4(enum.Enum, shape=4):
ADD = 0
SUB = 1
MUL = 2
.. doctest::
>>> Shape.cast(Funct4)
unsigned(4)
This module is a drop-in replacement for the standard :mod:`enum` module, and re-exports all of its members (not just the ones described below). In an Amaranth project, all ``import enum`` statements may be replaced with ``from amaranth.lib import enum``.
Metaclass
=========
.. autoclass:: EnumMeta()
Base classes
============
.. autoclass:: Enum()
.. autoclass:: IntEnum()
.. autoclass:: Flag()
.. autoclass:: IntFlag()