|
7 | 7 | from ..hdl.ast import Shape, ShapeCastable, Const, Signal, Value, ValueCastable
|
8 | 8 | from ..hdl.ir import Elaboratable
|
9 | 9 | from .._utils import final
|
| 10 | +from .meta import Annotation |
10 | 11 |
|
11 | 12 |
|
12 | 13 | __all__ = ["In", "Out", "Signature", "PureInterface", "connect", "flipped", "Component"]
|
@@ -705,6 +706,16 @@ def members(self):
|
705 | 706 | """
|
706 | 707 | return self.__members
|
707 | 708 |
|
| 709 | + @members.setter |
| 710 | + def members(self, new_members): |
| 711 | + # The setter is called when `sig.members += ...` is used. |
| 712 | + if new_members is not self.__members: |
| 713 | + raise AttributeError("property 'members' of 'Signature' object cannot be set") |
| 714 | + |
| 715 | + @property |
| 716 | + def annotations(self): |
| 717 | + return () |
| 718 | + |
708 | 719 | def __eq__(self, other):
|
709 | 720 | """Compare this signature with another.
|
710 | 721 |
|
@@ -1657,3 +1668,175 @@ def signature(self):
|
1657 | 1668 | can be used to customize a component's signature.
|
1658 | 1669 | """
|
1659 | 1670 | return self.__signature
|
| 1671 | + |
| 1672 | + @property |
| 1673 | + def metadata(self): |
| 1674 | + return ComponentMetadata(self) |
| 1675 | + |
| 1676 | + |
| 1677 | +class ComponentMetadata(Annotation): |
| 1678 | + name = "org.amaranth-lang.amaranth.component" |
| 1679 | + schema = { |
| 1680 | + "$schema": "https://json-schema.org/draft/2020-12/schema", |
| 1681 | + "$id": "https://amaranth-lang.org/schema/amaranth/0.5/component.json", |
| 1682 | + "type": "object", |
| 1683 | + "properties": { |
| 1684 | + "interface": { |
| 1685 | + "type": "object", |
| 1686 | + "properties": { |
| 1687 | + "members": { |
| 1688 | + "type": "object", |
| 1689 | + "patternProperties": { |
| 1690 | + "^[A-Za-z][A-Za-z0-9_]*$": { |
| 1691 | + "oneOf": [ |
| 1692 | + { |
| 1693 | + "type": "object", |
| 1694 | + "properties": { |
| 1695 | + "type": { |
| 1696 | + "enum": ["port"], |
| 1697 | + }, |
| 1698 | + "name": { |
| 1699 | + "type": "string", |
| 1700 | + "pattern": "^[A-Za-z][A-Za-z0-9_]*$", |
| 1701 | + }, |
| 1702 | + "dir": { |
| 1703 | + "enum": ["in", "out"], |
| 1704 | + }, |
| 1705 | + "width": { |
| 1706 | + "type": "integer", |
| 1707 | + "minimum": 0, |
| 1708 | + }, |
| 1709 | + "signed": { |
| 1710 | + "type": "boolean", |
| 1711 | + }, |
| 1712 | + "reset": { |
| 1713 | + "type": "string", |
| 1714 | + "pattern": "^[+-]?[0-9]+$", |
| 1715 | + }, |
| 1716 | + }, |
| 1717 | + "additionalProperties": False, |
| 1718 | + "required": [ |
| 1719 | + "type", |
| 1720 | + "name", |
| 1721 | + "dir", |
| 1722 | + "width", |
| 1723 | + "signed", |
| 1724 | + "reset", |
| 1725 | + ], |
| 1726 | + }, |
| 1727 | + { |
| 1728 | + "type": "object", |
| 1729 | + "properties": { |
| 1730 | + "type": { |
| 1731 | + "enum": ["interface"], |
| 1732 | + }, |
| 1733 | + "members": { |
| 1734 | + "$ref": "#/properties/interface/properties/members", |
| 1735 | + }, |
| 1736 | + "annotations": { |
| 1737 | + "type": "object", |
| 1738 | + }, |
| 1739 | + }, |
| 1740 | + "additionalProperties": False, |
| 1741 | + "required": [ |
| 1742 | + "type", |
| 1743 | + "members", |
| 1744 | + "annotations", |
| 1745 | + ], |
| 1746 | + }, |
| 1747 | + ], |
| 1748 | + }, |
| 1749 | + }, |
| 1750 | + "additionalProperties": False, |
| 1751 | + }, |
| 1752 | + "annotations": { |
| 1753 | + "type": "object", |
| 1754 | + }, |
| 1755 | + }, |
| 1756 | + "additionalProperties": False, |
| 1757 | + "required": [ |
| 1758 | + "members", |
| 1759 | + "annotations", |
| 1760 | + ], |
| 1761 | + }, |
| 1762 | + }, |
| 1763 | + "additionalProperties": False, |
| 1764 | + "required": [ |
| 1765 | + "interface", |
| 1766 | + ] |
| 1767 | + } |
| 1768 | + |
| 1769 | + """Component metadata. |
| 1770 | +
|
| 1771 | + A description of the interface and annotations of a :class:`Component`, which can be exported |
| 1772 | + as a JSON object. |
| 1773 | +
|
| 1774 | + Parameters |
| 1775 | + ---------- |
| 1776 | + origin : :class:`Component` |
| 1777 | + The component described by this metadata instance. |
| 1778 | +
|
| 1779 | + Raises |
| 1780 | + ------ |
| 1781 | + :exc:`TypeError` |
| 1782 | + If ``origin`` is not a :class:`Component`. |
| 1783 | + """ |
| 1784 | + def __init__(self, origin): |
| 1785 | + if not isinstance(origin, Component): |
| 1786 | + raise TypeError(f"Origin must be a Component object, not {origin!r}") |
| 1787 | + self._origin = origin |
| 1788 | + |
| 1789 | + @property |
| 1790 | + def origin(self): |
| 1791 | + return self._origin |
| 1792 | + |
| 1793 | + def as_json(self): |
| 1794 | + """Translate to JSON. |
| 1795 | +
|
| 1796 | + Returns |
| 1797 | + ------- |
| 1798 | + :class:`Mapping` |
| 1799 | + A JSON representation of :attr:`ComponentMetadata.origin`, with a hierarchical |
| 1800 | + description of its interface ports and annotations. |
| 1801 | + """ |
| 1802 | + def describe_member(member, *, path): |
| 1803 | + assert isinstance(member, Member) |
| 1804 | + if member.is_port: |
| 1805 | + cast_shape = Shape.cast(member.shape) |
| 1806 | + return { |
| 1807 | + "type": "port", |
| 1808 | + "name": "__".join(path), |
| 1809 | + "dir": "in" if member.flow == In else "out", |
| 1810 | + "width": cast_shape.width, |
| 1811 | + "signed": cast_shape.signed, |
| 1812 | + "reset": str(member._reset_as_const.value), |
| 1813 | + } |
| 1814 | + elif member.is_signature: |
| 1815 | + return { |
| 1816 | + "type": "interface", |
| 1817 | + "members": { |
| 1818 | + name: describe_member(sub_member, path=(*path, name)) |
| 1819 | + for name, sub_member in member.signature.members.items() |
| 1820 | + }, |
| 1821 | + "annotations": { |
| 1822 | + annotation.name: annotation.as_json() |
| 1823 | + for annotation in member.signature.annotations |
| 1824 | + }, |
| 1825 | + } |
| 1826 | + else: |
| 1827 | + assert False # :nocov: |
| 1828 | + |
| 1829 | + instance = { |
| 1830 | + "interface": { |
| 1831 | + "members": { |
| 1832 | + name: describe_member(member, path=(name,)) |
| 1833 | + for name, member in self.origin.signature.members.items() |
| 1834 | + }, |
| 1835 | + "annotations": { |
| 1836 | + annotation.name: annotation.as_json() |
| 1837 | + for annotation in self.origin.signature.annotations |
| 1838 | + }, |
| 1839 | + }, |
| 1840 | + } |
| 1841 | + self.validate(instance) |
| 1842 | + return instance |
0 commit comments