Skip to content

Commit bf8126e

Browse files
committed
Generalise adding an Amaranth annotation via a TypedDict
1 parent 693db33 commit bf8126e

File tree

1 file changed

+34
-27
lines changed

1 file changed

+34
-27
lines changed

chipflow_lib/platforms/_utils.py

Lines changed: 34 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
from enum import Enum, IntEnum, StrEnum, auto
1414
from math import ceil, floor
1515
from typing import (
16-
Any, Annotated, NamedTuple, Generic, Self,
16+
Any, Annotated, NamedTuple, Iterator, Generic, Self,
1717
TYPE_CHECKING
1818
)
1919
from typing_extensions import (
@@ -130,32 +130,47 @@ class IOModel(IOModelOptions):
130130
width: int
131131
direction: Annotated[io.Direction, PlainSerializer(lambda x: x.value)]
132132

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
136137

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
142143

144+
class Annotation(meta.Annotation):
145+
"Generated annotation class"
146+
schema = annotation_schema()
143147

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
147150

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
150163

151-
@property
152-
def origin(self): # type: ignore
153-
return self._model
154164

155-
def as_json(self): # type: ignore
156-
return TypeAdapter(IOModel).dump_python(self._model)
157165

158166

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)
159174
class IOSignature(wiring.Signature):
160175
"""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.
161176
This class is generally not directly used. Instead, you would typically utilize the more specific
@@ -225,17 +240,9 @@ def options(self) -> IOModelOptions:
225240
"""
226241
return self._model
227242

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-
235243
def __repr__(self):
236244
return f"IOSignature({','.join('{0}={1!r}'.format(k,v) for k,v in self._model.items())})"
237245

238-
239246
def OutputIOSignature(width: int, **kwargs: Unpack[IOModelOptions]):
240247
"""This creates an :py:obj:`Amaranth Signature <amaranth.lib.wiring.Signature>` which is then used to decorate package output signals
241248
intended for connection to the physical pads of the integrated circuit package.

0 commit comments

Comments
 (0)