|
13 | 13 | from enum import Enum, IntEnum, StrEnum, auto |
14 | 14 | from math import ceil, floor |
15 | 15 | from typing import ( |
16 | | - Any, Annotated, NamedTuple, Generic, Self, |
| 16 | + Any, Annotated, NamedTuple, Iterator, Generic, Self, |
17 | 17 | TYPE_CHECKING |
18 | 18 | ) |
19 | 19 | from typing_extensions import ( |
@@ -130,32 +130,47 @@ class IOModel(IOModelOptions): |
130 | 130 | width: int |
131 | 131 | direction: Annotated[io.Direction, PlainSerializer(lambda x: x.value)] |
132 | 132 |
|
133 | | -def io_annotation_schema(): |
134 | | - class Model(pydantic.BaseModel): |
135 | | - data_td: IOModel |
| 133 | +def amaranth_annotate(model: Type[TypedDict], schema_id: str): |
| 134 | + def annotation_schema(): |
| 135 | + class Model(pydantic.BaseModel): |
| 136 | + data_td: model |
136 | 137 |
|
137 | | - PydanticModel = TypeAdapter(IOModel) |
138 | | - schema = PydanticModel.json_schema() |
139 | | - schema['$schema'] = "https://json-schema.org/draft/2020-12/schema" |
140 | | - schema['$id'] = IO_ANNOTATION_SCHEMA |
141 | | - return schema |
| 138 | + PydanticModel = TypeAdapter(model) |
| 139 | + schema = PydanticModel.json_schema() |
| 140 | + schema['$schema'] = "https://json-schema.org/draft/2020-12/schema" |
| 141 | + schema['$id'] = schema_id |
| 142 | + return schema |
142 | 143 |
|
| 144 | + class Annotation(meta.Annotation): |
| 145 | + "Generated annotation class" |
| 146 | + schema = annotation_schema() |
143 | 147 |
|
144 | | -class _IOAnnotation(meta.Annotation): |
145 | | - "Infrastructure for `Amaranth annotations <amaranth.lib.meta.Annotation>`" |
146 | | - schema = io_annotation_schema() |
| 148 | + def __init__(self, model:IOModel): |
| 149 | + self._model = model |
147 | 150 |
|
148 | | - def __init__(self, model:IOModel): |
149 | | - self._model = model |
| 151 | + @property |
| 152 | + def origin(self): # type: ignore |
| 153 | + return self._model |
| 154 | + |
| 155 | + def as_json(self): # type: ignore |
| 156 | + return TypeAdapter(IOModel).dump_python(self._model) |
| 157 | + |
| 158 | + def annotations(self, *args): # type: ignore |
| 159 | + annotations = wiring.Signature.annotations(self, *args) # type: ignore |
| 160 | + |
| 161 | + io_annotation = Annotation(self._model) |
| 162 | + return annotations + (io_annotation,) # type: ignore |
150 | 163 |
|
151 | | - @property |
152 | | - def origin(self): # type: ignore |
153 | | - return self._model |
154 | 164 |
|
155 | | - def as_json(self): # type: ignore |
156 | | - return TypeAdapter(IOModel).dump_python(self._model) |
157 | 165 |
|
158 | 166 |
|
| 167 | + def decorator(klass): |
| 168 | + klass.annotations = annotations |
| 169 | + klass.__repr__ |
| 170 | + return klass |
| 171 | + return decorator |
| 172 | + |
| 173 | +@amaranth_annotate(IOModel, IO_ANNOTATION_SCHEMA) |
159 | 174 | class IOSignature(wiring.Signature): |
160 | 175 | """An :py:obj:`Amaranth Signature <amaranth.lib.wiring.Signature>` used to decorate wires that would usually be brought out onto a port on the package. |
161 | 176 | This class is generally not directly used. Instead, you would typically utilize the more specific |
@@ -225,17 +240,9 @@ def options(self) -> IOModelOptions: |
225 | 240 | """ |
226 | 241 | return self._model |
227 | 242 |
|
228 | | - def annotations(self, *args): # type: ignore |
229 | | - annotations = wiring.Signature.annotations(self, *args) # type: ignore |
230 | | - |
231 | | - io_annotation = _IOAnnotation(self._model) |
232 | | - return annotations + (io_annotation,) # type: ignore |
233 | | - |
234 | | - |
235 | 243 | def __repr__(self): |
236 | 244 | return f"IOSignature({','.join('{0}={1!r}'.format(k,v) for k,v in self._model.items())})" |
237 | 245 |
|
238 | | - |
239 | 246 | def OutputIOSignature(width: int, **kwargs: Unpack[IOModelOptions]): |
240 | 247 | """This creates an :py:obj:`Amaranth Signature <amaranth.lib.wiring.Signature>` which is then used to decorate package output signals |
241 | 248 | intended for connection to the physical pads of the integrated circuit package. |
|
0 commit comments