Skip to content

Commit 2c703f2

Browse files
committed
Add annotations for memory maps, wishbone and CSR primitives.
1 parent dff6abd commit 2c703f2

File tree

6 files changed

+583
-6
lines changed

6 files changed

+583
-6
lines changed

amaranth_soc/csr/bus.py

+133-2
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
from collections import defaultdict
22
from amaranth import *
3-
from amaranth.lib import enum, wiring
3+
from amaranth.lib import enum, wiring, meta
44
from amaranth.lib.wiring import In, Out, flipped
55
from amaranth.utils import log2_int
66

77
from ..memory import MemoryMap
88

99

10-
__all__ = ["Element", "Signature", "Interface", "Decoder", "Multiplexer"]
10+
__all__ = ["Element", "Signature", "Annotation", "Interface", "Decoder", "Multiplexer"]
1111

1212

1313
class Element(wiring.Interface):
@@ -81,6 +81,10 @@ def width(self):
8181
def access(self):
8282
return self._access
8383

84+
@property
85+
def annotations(self):
86+
return (*super().annotations, Element.Annotation(self))
87+
8488
@classmethod
8589
def check_parameters(cls, width, access):
8690
"""Validate signature parameters.
@@ -125,6 +129,65 @@ def __eq__(self, other):
125129
def __repr__(self):
126130
return f"csr.Element.Signature({self.members!r})"
127131

132+
class Annotation(meta.Annotation):
133+
name = "org.amaranth-lang.amaranth-soc.csr-element"
134+
schema = {
135+
"$schema": "https://json-schema.org/draft/2020-12/schema",
136+
"$id": "https://amaranth-lang.org/schema/amaranth-soc/0.1/csr-element.json",
137+
"type": "object",
138+
"properties": {
139+
"width": {
140+
"type": "integer",
141+
"minimum": 0,
142+
},
143+
"access": {
144+
"enum": ["r", "w", "rw"],
145+
},
146+
},
147+
"additionalProperties": False,
148+
"required": [
149+
"width",
150+
"access",
151+
],
152+
}
153+
154+
"""Peripheral-side CSR signature annotation.
155+
156+
Parameters
157+
----------
158+
origin : :class:`Element.Signature`
159+
The signature described by this annotation instance.
160+
161+
Raises
162+
------
163+
:exc:`TypeError`
164+
If ``origin`` is not a :class:`Element.Signature`.
165+
"""
166+
def __init__(self, origin):
167+
if not isinstance(origin, Element.Signature):
168+
raise TypeError(f"Origin must be a csr.Element.Signature object, not {origin!r}")
169+
self._origin = origin
170+
171+
@property
172+
def origin(self):
173+
return self._origin
174+
175+
def as_json(self):
176+
"""Translate to JSON.
177+
178+
Returns
179+
-------
180+
:class:`dict`
181+
A JSON representation of :attr:`~Element.Annotation.origin`, describing its width
182+
and access mode.
183+
"""
184+
instance = {
185+
"width": self.origin.width,
186+
"access": self.origin.access.value,
187+
}
188+
self.validate(instance)
189+
return instance
190+
128191
"""Peripheral-side CSR interface.
129192
130193
A low-level interface to a single atomically readable and writable register in a peripheral.
@@ -238,6 +301,13 @@ def memory_map(self, memory_map):
238301
f"same as bus interface data width {self.data_width}")
239302
self._memory_map = memory_map
240303

304+
@property
305+
def annotations(self):
306+
annotations = [*super().annotations, Annotation(self)]
307+
if self._memory_map is not None:
308+
annotations += self._memory_map.annotations
309+
return annotations
310+
241311
@classmethod
242312
def check_parameters(cls, *, addr_width, data_width):
243313
"""Validate signature parameters.
@@ -280,6 +350,67 @@ def __repr__(self):
280350
return f"csr.Signature({self.members!r})"
281351

282352

353+
class Annotation(meta.Annotation):
354+
name = "org.amaranth-lang.amaranth-soc.csr"
355+
schema = {
356+
"$schema": "https://json-schema.org/draft/2020-12/schema",
357+
"$id": "https://amaranth-lang.org/schema/amaranth-soc/0.1/csr.json",
358+
"type": "object",
359+
"properties": {
360+
"addr_width": {
361+
"type": "integer",
362+
"minimum": 0,
363+
},
364+
"data_width": {
365+
"type": "integer",
366+
"minimum": 0,
367+
},
368+
},
369+
"additionalProperties": False,
370+
"required": [
371+
"addr_width",
372+
"data_width",
373+
],
374+
}
375+
376+
"""CPU-side CSR signature annotation.
377+
378+
Parameters
379+
----------
380+
origin : :class:`Signature`
381+
The signature described by this annotation instance.
382+
383+
Raises
384+
------
385+
:exc:`TypeError`
386+
If ``origin`` is not a :class:`Signature`.
387+
"""
388+
def __init__(self, origin):
389+
if not isinstance(origin, Signature):
390+
raise TypeError(f"Origin must be a csr.Signature object, not {origin!r}")
391+
self._origin = origin
392+
393+
@property
394+
def origin(self):
395+
return self._origin
396+
397+
def as_json(self):
398+
"""Translate to JSON.
399+
400+
Returns
401+
-------
402+
:class:`dict`
403+
A JSON representation of :attr:`~Annotation.origin`, describing its address width
404+
and data width.
405+
"""
406+
instance = {
407+
"addr_width": self.origin.addr_width,
408+
"data_width": self.origin.data_width,
409+
}
410+
self.validate(instance)
411+
return instance
412+
413+
283414
class Interface(wiring.Interface):
284415
"""CPU-side CSR interface.
285416

amaranth_soc/memory.py

+171-1
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import bisect
22

3+
from amaranth.lib import meta
34
from amaranth.utils import bits_for
45

56

6-
__all__ = ["ResourceInfo", "MemoryMap"]
7+
__all__ = ["ResourceInfo", "MemoryMap", "MemoryMapAnnotation"]
78

89

910
class _RangeMap:
@@ -166,6 +167,10 @@ def __init__(self, *, addr_width, data_width, alignment=0, name=None):
166167
self._next_addr = 0
167168
self._frozen = False
168169

170+
@property
171+
def annotations(self):
172+
return (MemoryMapAnnotation(self),)
173+
169174
@property
170175
def addr_width(self):
171176
return self._addr_width
@@ -579,3 +584,168 @@ def decode_address(self, address):
579584
return assignment.decode_address((address - addr_range.start) // addr_range.step)
580585
else:
581586
assert False # :nocov:
587+
588+
589+
class MemoryMapAnnotation(meta.Annotation):
590+
name = "org.amaranth-lang.amaranth-soc.memory-map"
591+
schema = {
592+
"$schema": "https://json-schema.org/draft/2020-12/schema",
593+
"$id": "https://amaranth-lang.org/schema/amaranth-soc/0.1/memory-map.json",
594+
"type": "object",
595+
"properties": {
596+
"name": {
597+
"type": "string",
598+
},
599+
"addr_width": {
600+
"type": "integer",
601+
"minimum": 0,
602+
},
603+
"data_width": {
604+
"type": "integer",
605+
"minimum": 0,
606+
},
607+
"alignment": {
608+
"type": "integer",
609+
"minimum": 0,
610+
},
611+
"windows": {
612+
"type": "array",
613+
"items": {
614+
"type": "object",
615+
"properties": {
616+
"start": {
617+
"type": "integer",
618+
"minimum": 0,
619+
},
620+
"end": {
621+
"type": "integer",
622+
"minimum": 0,
623+
},
624+
"ratio": {
625+
"type": "integer",
626+
"minimum": 1,
627+
},
628+
"annotations": {
629+
"type": "object",
630+
"patternProperties": {
631+
"^.+$": { "$ref": "#" },
632+
},
633+
},
634+
},
635+
"additionalProperties": False,
636+
"required": [
637+
"start",
638+
"end",
639+
"ratio",
640+
"annotations",
641+
],
642+
},
643+
},
644+
"resources": {
645+
"type": "array",
646+
"items": {
647+
"type": "object",
648+
"properties": {
649+
"name": {
650+
"type": "string",
651+
},
652+
"start": {
653+
"type": "integer",
654+
"minimum": 0,
655+
},
656+
"end": {
657+
"type": "integer",
658+
"minimum": 0,
659+
},
660+
"annotations": {
661+
"type": "object",
662+
"patternProperties": {
663+
"^.+$": { "type": "object" },
664+
},
665+
},
666+
},
667+
"additionalProperties": False,
668+
"required": [
669+
"name",
670+
"start",
671+
"end",
672+
"annotations",
673+
],
674+
},
675+
},
676+
},
677+
"additionalProperties": False,
678+
"required": [
679+
"addr_width",
680+
"data_width",
681+
"alignment",
682+
"windows",
683+
"resources",
684+
],
685+
}
686+
687+
"""Memory map annotation.
688+
689+
Parameters
690+
----------
691+
origin : :class:`MemoryMap`
692+
The memory map described by this annotation instance. It is frozen as a side-effect of
693+
this instantiation.
694+
695+
Raises
696+
------
697+
:exc:`TypeError`
698+
If ``origin`` is not a :class:`MemoryMap`.
699+
"""
700+
def __init__(self, origin):
701+
if not isinstance(origin, MemoryMap):
702+
raise TypeError(f"Origin must be a MemoryMap object, not {origin!r}")
703+
origin.freeze()
704+
self._origin = origin
705+
706+
@property
707+
def origin(self):
708+
return self._origin
709+
710+
def as_json(self):
711+
"""Translate to JSON.
712+
713+
Returns
714+
-------
715+
:class:`dict`
716+
A JSON representation of :attr:`~MemoryMapAnnotation.origin`, describing its address width,
717+
data width, address range alignment, and a hierarchical description of its local windows
718+
and resources.
719+
"""
720+
instance = {}
721+
if self.origin.name is not None:
722+
instance["name"] = self.origin.name
723+
instance.update({
724+
"addr_width": self.origin.addr_width,
725+
"data_width": self.origin.data_width,
726+
"alignment": self.origin.alignment,
727+
"windows": [
728+
{
729+
"start": start,
730+
"end": end,
731+
"ratio": ratio,
732+
"annotations": {
733+
annotation.name: annotation.as_json()
734+
for annotation in window.annotations
735+
},
736+
} for window, (start, end, ratio) in self.origin.windows()
737+
],
738+
"resources": [
739+
{
740+
"name": name,
741+
"start": start,
742+
"end": end,
743+
"annotations": {
744+
annotation.name: annotation.as_json()
745+
for annotation in resource.signature.annotations
746+
},
747+
} for resource, name, (start, end) in self.origin.resources()
748+
],
749+
})
750+
self.validate(instance)
751+
return instance

0 commit comments

Comments
 (0)