Skip to content

Commit ee1fbd2

Browse files
committed
wip
1 parent 0344d87 commit ee1fbd2

File tree

7 files changed

+372
-325
lines changed

7 files changed

+372
-325
lines changed

chipflow_lib/pin_lock.py

Lines changed: 49 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
11
import inspect
22
import json
33
import logging
4-
import re
5-
import sys
64

75
from pprint import pformat
86
from pathlib import Path
97

10-
from amaranth import Shape
11-
from chipflow_lib import _parse_config, _get_cls_by_reference
12-
from chipflow_lib.platforms import PACKAGE_DEFINITIONS, PIN_ANNOTATION_SCHEMA
8+
from chipflow_lib import _parse_config
9+
from chipflow_lib.platforms import PACKAGE_DEFINITIONS, PIN_ANNOTATION_SCHEMA, top_interfaces
10+
from chipflow_lib.platforms.utils import LockFile, Package, Interface, Port
1311

1412
# logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)
1513
logger = logging.getLogger(__name__)
@@ -30,7 +28,7 @@ def count_pins(name, member):
3028

3129
def count_old_pins(d):
3230
match d:
33-
case {'type':t, 'pins':p}:
31+
case {'type':_, 'pins':p}:
3432
return len(p)
3533
case _:
3634
return sum([count_old_pins(v) for v in d.values()])
@@ -52,15 +50,14 @@ def allocate_pins(name, member, pins:list):
5250
'type':'io'}
5351
return pin_map, pins[width:]
5452
elif member['type'] == 'interface':
55-
logger.warning("member without PinSignature, pin allocation likely to be wrong")
5653
for k, v in member['members'].items():
5754
n = '_'.join([name,k])
5855
_map, pins = allocate_pins(n, v, pins)
5956
pin_map |= _map
6057
logger.debug(f"{pin_map},{_map}")
6158
return pin_map, pins
6259
elif member['type'] == 'port':
63-
logger.warning(f"Component `{name}` in this design is not using PinSignature")
60+
logger.warning(f"Port '{name}' has no PinSignature, pin allocation likely to be wrong")
6461
name = name
6562
width = member['width']
6663
direction = member['dir']
@@ -87,93 +84,79 @@ def check_pins(name, member, old_map, new_map):
8784
def lock_pins():
8885
config = _parse_config()
8986
used_pins = set()
90-
pin_map = {}
87+
newlock = LockFile()
88+
oldlock = None
9189
lockfile = Path('pins.lock')
9290
if lockfile.exists():
93-
with open(lockfile) as f:
94-
old_lock = json.load(f)
95-
old_map = old_lock['map']
96-
else:
97-
old_map = {}
91+
json_string = lockfile.read_text()
92+
oldlock = LockFile.model_validate_json(json_string)
93+
9894
print(f"Locking pins: {'using pins.lock' if lockfile.exists() else ''}")
9995
package_name = config["chipflow"]["silicon"]["pad_ring"]
10096

10197
if package_name not in PACKAGE_DEFINITIONS:
102-
logger.debug(f"Package '{package} is unknown")
98+
logger.debug(f"Package '{package_name} is unknown")
10399
package = PACKAGE_DEFINITIONS[package_name]
104100

101+
newlock.package.package_name = package_name
105102
for d in ("pads", "power"):
106103
logger.debug(f"Checking [chipflow.silicon.{d}]:")
104+
_map = {}
107105
for k, v in config["chipflow"]["silicon"][d].items():
108106
pin = str(v['loc'])
109107
used_pins.add(pin)
110-
if k in old_map and old_map[k]['pins'] != [pin]:
108+
if d in old_map and k in old_map[d] and old_map[d][k]['pins'] != [pin]:
111109
print(f"chipflow.toml conflicts with pins.lock: "
112110
f"{k} had pin {old_map[k]['pins']}, now {[pin]}.")
113111
exit(1)
114-
pin_map[k] = {
112+
_map[k] = {
115113
'pins': [pin],
116-
'type': v['type'] if 'type' in v else None}
114+
'type': v['type'] if 'type' in v else d}
115+
pin_map[d] = _map
117116

118117

119118
logger.debug(f'Pins in use: {sorted(used_pins)}')
120119

121120
unallocated = package.pins - used_pins
122121

123122
logger.debug(f"unallocated pins = {sorted(unallocated)}")
124-
interfaces = {}
125-
top_components = config["chipflow"]["top"].items()
126-
component_configs = {}
127-
top = {}
128-
129-
for name, conf in top_components:
130-
if '.' in name:
131-
assert conf is dict
132-
logger.debug("Config found for {name}")
133-
component_configs[name.split('.')[0]] = conf
134-
135-
for name, ref in top_components:
136-
cls = _get_cls_by_reference(ref, context=f"top component: {name}")
137-
if name in component_configs:
138-
top[name] = cls(component_configs[name])
139-
else:
140-
top[name] = cls()
141-
logger.debug(pformat(top[name].metadata.origin.signature.members))
142-
metadata = top[name].metadata.as_json()
143-
interfaces |= metadata['interface']['members']
144-
145-
logger.debug(f"All interfaces: {interfaces.keys()}")
146-
pin_map_interfaces = {}
123+
124+
_, interfaces = top_interfaces(config)
125+
126+
logger.debug(f"All interfaces: {[v.keys() for i in interfaces]}")
147127

148128
# we try to keep pins together for each interface
149-
for k,v in interfaces.items():
150-
logger.debug(f"Interface {k}:")
151-
logger.debug(pformat(v))
152-
width = count_pins(k, v)
153-
print(f" {k}: total {width} pins")
154-
if k in old_map:
155-
print(f" {k} found in pins.lock, reusing")
156-
logger.debug(pformat(old_map[k]))
157-
old_width = count_old_pins(old_map[k])
158-
if old_width != width:
159-
# TODO: option to allocate new pins nonconsecutively
160-
print(f"top level interface has changed size. Old size = {old_width}, new size = {width}")
161-
exit(1)
162-
pin_map[k] = old_map[k]
163-
else:
164-
pins = package.allocate(unallocated, width)
165-
if len(pins) == 0:
166-
print("ERROR: No pins were allocation by {package}")
167-
exit(1)
168-
logger.debug(f"allocated range: {pins}")
169-
unallocated = unallocated - set(pins)
170-
_map, _ = allocate_pins(k, v, pins)
171-
pin_map[k] = _map
129+
for component, iface in interfaces.items():
130+
pin_map[component] = {}
131+
for k,v in iface.items():
132+
logger.debug(f"Interface {component}.{k}:")
133+
logger.debug(pformat(v))
134+
width = count_pins(k, v)
135+
print(f" {k}: total {width} pins")
136+
if component in old_map and k in old_map[component]:
137+
print(f" {component}.{k} found in pins.lock, reusing")
138+
logger.debug(pformat(old_map[component][k]))
139+
old_width = count_old_pins(old_map[k])
140+
if old_width != width:
141+
# TODO: option to allocate new pins nonconsecutively
142+
print(f"top level interface has changed size. Old size = {old_width}, new size = {width}")
143+
exit(1)
144+
pin_map[component][k] = old_map[component][k]
145+
else:
146+
pins = package.allocate(unallocated, width)
147+
if len(pins) == 0:
148+
print("ERROR: No pins were allocation by {package}")
149+
exit(1)
150+
logger.debug(f"allocated range: {pins}")
151+
unallocated = unallocated - set(pins)
152+
_map, _ = allocate_pins(k, v, pins)
153+
pin_map[component][k] = _map
172154

173155
with open('pins.lock', 'w') as f:
174156
newlock = {'map': pin_map,
175-
'metadata': metadata}
157+
'metadata': interfaces}
176158

159+
assert _LockFile.model_validate(newlock)
177160
json.dump(newlock, f, indent=2, sort_keys=True)
178161

179162
class PinCommand:
@@ -182,7 +165,7 @@ def __init__(self, config):
182165

183166
def build_cli_parser(self, parser):
184167
action_argument = parser.add_subparsers(dest="action")
185-
prepare_subparser = action_argument.add_parser(
168+
action_argument.add_parser(
186169
"lock", help=inspect.getdoc(self.lock).splitlines()[0])
187170

188171
def run_cli(self, args):

chipflow_lib/platforms/__init__.py

Lines changed: 1 addition & 184 deletions
Original file line numberDiff line numberDiff line change
@@ -1,186 +1,3 @@
1-
import logging
2-
3-
from enum import Enum
4-
from dataclasses import dataclass
5-
from typing import Set, List, TypeVar, Dict
6-
7-
from amaranth import Shape
8-
from amaranth.lib import wiring, io, meta
9-
from amaranth.lib.wiring import In, Out
10-
from pydantic import BaseModel, ConfigDict
11-
from pprint import pformat
12-
13-
from .. import ChipFlowError
141
from .silicon import *
152
from .sim import *
16-
17-
logger = logging.getLogger(__name__)
18-
19-
def chipflow_schema_uri(name: str, version:int) -> str:
20-
return f"https://api.chipflow.com/schemas/{version}/{name}"
21-
22-
class PortGroup:
23-
"""Group of Amaranth library I/O ports.
24-
25-
This object is a stand-in for the object returned by the Amaranth :py:`platform.request()`
26-
function, as expected by the I/O cores.
27-
"""
28-
29-
def __init__(self, **kwargs):
30-
for name, port in kwargs.items():
31-
setattr(self, name, port)
32-
33-
def __getitem__(self, key):
34-
return self.__dict__[key]
35-
36-
def __setattr__(self, name, value):
37-
if not name.startswith("_"):
38-
assert value is None or isinstance(value, (io.PortLike, PortGroup))
39-
object.__setattr__(self, name, value)
40-
41-
def __repr__(self):
42-
attrs = []
43-
for name, value in self.__dict__.items():
44-
if not name.startswith('_'):
45-
attrs.append(f"{name}={value!r}")
46-
return f"{self.__class__.__name__}({', '.join(attrs)})"
47-
48-
class PinAnnotationModel(BaseModel):
49-
model_config = ConfigDict(use_enum_values=True)
50-
direction: io.Direction
51-
width: int
52-
53-
@classmethod
54-
def _annotation_schema(cls):
55-
schema = PinAnnotationModel.model_json_schema()
56-
schema['$schema'] = "https://json-schema.org/draft/2020-12/schema"
57-
schema['$id'] = chipflow_schema_uri("pin-annotation",0)
58-
return schema
59-
60-
def __init__(self, **kwargs):
61-
kwargs['url'] = chipflow_schema_uri("pin-annotation",0)
62-
super().__init__(**kwargs)
63-
64-
class PinAnnotation(meta.Annotation):
65-
schema = PinAnnotationModel._annotation_schema()
66-
67-
def __init__(self, **kwargs):
68-
self.model = PinAnnotationModel(**kwargs)
69-
70-
@property
71-
def origin(self): # type: ignore
72-
return self.model
73-
74-
def as_json(self): # type: ignore
75-
return self.model.model_dump()
76-
77-
class Schemas(Enum):
78-
PIN_ANNOTATION = str(chipflow_schema_uri("pin-annotation",0))
79-
PIN_ANNOTATION_SCHEMA = str(chipflow_schema_uri("pin-annotation",0))
80-
81-
class PinSignature(wiring.Signature):
82-
def __init__(self, direction, width=1, init=None):
83-
self._direction = direction
84-
self._width = width
85-
self._init = init
86-
match direction:
87-
case io.Direction.Bidir:
88-
sig = {
89-
"o": Out(width),
90-
"oe": Out(1),
91-
"i": In(width)
92-
}
93-
case io.Direction.Input:
94-
sig = {"i": In(width)}
95-
case io.Direction.Output:
96-
sig = {"o": Out(width)}
97-
case _:
98-
assert False
99-
100-
super().__init__(sig)
101-
102-
def annotations(self, *args):
103-
return wiring.Signature.annotations(self, *args) + (PinAnnotation(direction=self._direction, width=self._width),)
104-
105-
def __repr__(self):
106-
return f"PinSignature({self._direction}, {self._width})"
107-
108-
def OutputPinSignature(width, **kwargs):
109-
return PinSignature(io.Direction.Output, width=width, **kwargs)
110-
111-
def InputPinSignature(width, **kwargs):
112-
return PinSignature(io.Direction.Input, width=width, **kwargs)
113-
114-
def BidirPinSignature(width, **kwargs):
115-
return PinSignature(io.Direction.Bidir, width=width, **kwargs)
116-
117-
def group_consecutive_items(lst: List[int]) -> Dict[int, List[List[int]]]:
118-
if not lst:
119-
return {}
120-
121-
grouped = []
122-
current_group = [lst[0]]
123-
124-
for item in lst[1:]:
125-
if item == current_group[-1] + 1:
126-
current_group.append(item)
127-
else:
128-
grouped.append(current_group)
129-
current_group = [item]
130-
131-
grouped.append(current_group)
132-
d = {}
133-
for g in grouped:
134-
d.setdefault(len(g), []).append(g)
135-
return d
136-
137-
def find_contiguous_sequence(lst: List[int], total: int) -> List[int]:
138-
"""Find the next sequence of n consecutive numbers in a sorted list
139-
140-
Args:
141-
lst: Sorted list of numbers
142-
n: Length of consecutive sequence to find
143-
144-
Returns:
145-
A slice indexing the first sequence of n consecutive numbers found within the given list
146-
if unable to find a consecutive list, allocate as contigously as possible
147-
"""
148-
if not lst or len(lst) < total:
149-
raise ChipFlowError("Invalid request to find_contiguous_argument")
150-
151-
grouped = group_consecutive_items(lst)
152-
153-
ret = []
154-
n = total
155-
for k in sorted(grouped.keys(), reverse=True):
156-
for g in grouped[k]:
157-
assert (n+len(ret)==total)
158-
if k >= n:
159-
ret += g[0:min(n,k)]
160-
return ret
161-
else:
162-
n = n - k
163-
ret += g[0:k]
164-
165-
return ret
166-
167-
168-
@dataclass
169-
class PGAPackageDef:
170-
size: int
171-
172-
@property
173-
def pins(self) -> Set[str]:
174-
return set([str(i) for i in range(self.size-1)])
175-
176-
def allocate(self, available: Set[str], width:int) -> List[str]:
177-
avail_n = sorted([int(i) for i in available])
178-
logger.debug(f"PGAPackageDef.allocate {width} from {len(avail_n)} remaining")
179-
ret = find_contiguous_sequence(avail_n, width)
180-
logger.debug(f"PGAPackageDef.returned {ret}")
181-
assert(len(ret)==width)
182-
return [str(i) for i in ret]
183-
184-
PACKAGE_DEFINITIONS = {
185-
"pga144": PGAPackageDef(144)
186-
}
3+
from .utils import *

0 commit comments

Comments
 (0)