Skip to content

Commit 96ddc2c

Browse files
committed
feat: Enable configuring of package pin allocation from the package definition
Also enables grid array and quad format packages
1 parent 02dcf9d commit 96ddc2c

File tree

14 files changed

+1055
-820
lines changed

14 files changed

+1055
-820
lines changed

chipflow_lib/config_models.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,16 @@ def validate_pad_dict(cls, v: dict, info: ValidationInfo):
3636
return v
3737

3838

39+
Voltage = float
40+
3941
class SiliconConfig(BaseModel):
4042
"""Configuration for silicon in chipflow.toml."""
4143
process: Process
4244
package: Literal["caravel", "cf20", "pga144"]
43-
pads: Dict[str, PadConfig] = {}
44-
power: Dict[str, PadConfig] = {}
45+
power: Dict[str, Voltage] = {}
4546
debug: Optional[Dict[str, bool]] = None
47+
# This is still kept around to allow forcing pad locations.
48+
pads: Optional[Dict[str, PadConfig]] = {}
4649

4750
@field_validator('pads', 'power', mode='before')
4851
@classmethod

chipflow_lib/pin_lock.py

Lines changed: 18 additions & 158 deletions
Original file line numberDiff line numberDiff line change
@@ -2,181 +2,41 @@
22
import inspect
33
import logging
44

5-
from pprint import pformat
65
from pathlib import Path
7-
from typing import Any, List, Dict, Tuple
8-
9-
from chipflow_lib import _parse_config, _ensure_chipflow_root, ChipFlowError
10-
from chipflow_lib.platforms import (
11-
PACKAGE_DEFINITIONS,
12-
PIN_ANNOTATION_SCHEMA,
13-
top_interfaces,
14-
LockFile,
15-
Package,
16-
PortMap,
17-
Port
18-
)
19-
from chipflow_lib.config_models import Config
6+
7+
from chipflow_lib import _parse_config, _ensure_chipflow_root
8+
from chipflow_lib.platforms import top_components, LockFile, PACKAGE_DEFINITIONS
209

2110
# logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)
2211
logger = logging.getLogger(__name__)
2312

2413

25-
def count_member_pins(name: str, member: Dict[str, Any]) -> int:
26-
"Counts the pins from amaranth metadata"
27-
logger.debug(
28-
f"count_pins {name} {member['type']} "
29-
f"{member['annotations'] if 'annotations' in member else 'no annotations'}"
30-
)
31-
if member['type'] == 'interface' and 'annotations' in member \
32-
and PIN_ANNOTATION_SCHEMA in member['annotations']:
33-
return member['annotations'][PIN_ANNOTATION_SCHEMA]['width']
34-
elif member['type'] == 'interface':
35-
width = 0
36-
for n, v in member['members'].items():
37-
width += count_member_pins('_'.join([name, n]), v)
38-
return width
39-
elif member['type'] == 'port':
40-
return member['width']
41-
42-
43-
def allocate_pins(name: str, member: Dict[str, Any], pins: List[str], port_name: str = None) -> Tuple[Dict[str, Port], List[str]]:
44-
"Allocate pins based of Amaranth member metadata"
45-
46-
if port_name is None:
47-
port_name = name
48-
49-
pin_map = {}
50-
51-
logger.debug(f"allocate_pins: name={name}, pins={pins}")
52-
logger.debug(f"member={pformat(member)}")
53-
54-
if member['type'] == 'interface' and 'annotations' in member \
55-
and PIN_ANNOTATION_SCHEMA in member['annotations']:
56-
logger.debug("matched IOSignature {sig}")
57-
sig = member['annotations'][PIN_ANNOTATION_SCHEMA]
58-
width = sig['width']
59-
options = sig['options']
60-
pin_map[name] = {'pins': pins[0:width],
61-
'direction': sig['direction'],
62-
'type': 'io',
63-
'port_name': port_name,
64-
'options': options}
65-
logger.debug(f"added '{name}':{pin_map[name]} to pin_map")
66-
return pin_map, pins[width:]
67-
elif member['type'] == 'interface':
68-
for k, v in member['members'].items():
69-
port_name = '_'.join([name, k])
70-
_map, pins = allocate_pins(k, v, pins, port_name=port_name)
71-
pin_map |= _map
72-
logger.debug(f"{pin_map},{_map}")
73-
return pin_map, pins
74-
elif member['type'] == 'port':
75-
logger.warning(f"Port '{name}' has no IOSignature, pin allocation likely to be wrong")
76-
width = member['width']
77-
pin_map[name] = {'pins': pins[0:width],
78-
'direction': member['dir'],
79-
'type': 'io',
80-
'port_name': port_name
81-
}
82-
logger.debug(f"added '{name}':{pin_map[name]} to pin_map")
83-
return pin_map, pins[width:]
84-
else:
85-
logging.debug(f"Shouldnt get here. member = {member}")
86-
assert False
87-
88-
8914
def lock_pins() -> None:
90-
# Get the config as dict for backward compatibility with top_interfaces
15+
# Get the config as dict for backward compatibility with top_components
9116
config_dict = _parse_config()
9217

9318
# Parse with Pydantic for type checking and strong typing
94-
config_model = Config.model_validate(config_dict)
95-
96-
used_pins = set()
97-
oldlock = None
9819

9920
chipflow_root = _ensure_chipflow_root()
10021
lockfile = Path(chipflow_root, 'pins.lock')
22+
oldlock = None
23+
10124
if lockfile.exists():
102-
json_string = lockfile.read_text()
103-
oldlock = LockFile.model_validate_json(json_string)
25+
oldlock = LockFile.model_validate_json(lockfile.read_text())
10426

10527
print(f"Locking pins: {'using pins.lock' if lockfile.exists() else ''}")
10628

107-
process = config_model.chipflow.silicon.process
108-
package_name = config_model.chipflow.silicon.package
109-
110-
if package_name not in PACKAGE_DEFINITIONS:
111-
logger.debug(f"Package '{package_name} is unknown")
112-
package_type = PACKAGE_DEFINITIONS[package_name]
113-
114-
package = Package(package_type=package_type)
115-
116-
# Process pads and power configurations using Pydantic models
117-
for d in ("pads", "power"):
118-
logger.debug(f"Checking [chipflow.silicon.{d}]:")
119-
silicon_config = getattr(config_model.chipflow.silicon, d, {})
120-
for k, v in silicon_config.items():
121-
pin = str(v.loc)
122-
used_pins.add(pin)
123-
124-
# Convert Pydantic model to dict for backward compatibility
125-
v_dict = {"type": v.type, "loc": v.loc}
126-
port = oldlock.package.check_pad(k, v_dict) if oldlock else None
127-
128-
if port and port.pins != [pin]:
129-
raise ChipFlowError(
130-
f"chipflow.toml conflicts with pins.lock: "
131-
f"{k} had pin {port.pins}, now {[pin]}."
132-
)
133-
134-
# Add pad to package
135-
package.add_pad(k, v_dict)
136-
137-
logger.debug(f'Pins in use: {package_type.sortpins(used_pins)}')
138-
139-
unallocated = package_type.pins - used_pins
140-
141-
logger.debug(f"unallocated pins = {package_type.sortpins(unallocated)}")
142-
143-
# Use the raw dict for top_interfaces since it expects the legacy format
144-
_, interfaces = top_interfaces(config_dict)
145-
146-
logger.debug(f"All interfaces:\n{pformat(interfaces)}")
147-
148-
port_map = PortMap({})
149-
# we try to keep pins together for each interface
150-
for component, iface in interfaces.items():
151-
for k, v in iface['interface']['members'].items():
152-
logger.debug(f"Interface {component}.{k}:")
153-
logger.debug(pformat(v))
154-
width = count_member_pins(k, v)
155-
logger.debug(f" {k}: total {width} pins")
156-
old_ports = oldlock.port_map.get_ports(component, k) if oldlock else None
157-
if old_ports:
158-
logger.debug(f" {component}.{k} found in pins.lock, reusing")
159-
logger.debug(pformat(old_ports))
160-
old_width = sum([len(p.pins) for p in old_ports.values()])
161-
if old_width != width:
162-
raise ChipFlowError(
163-
f"top level interface has changed size. "
164-
f"Old size = {old_width}, new size = {width}"
165-
)
166-
port_map.add_ports(component, k, old_ports)
167-
else:
168-
pins = package_type.allocate(unallocated, width)
169-
if len(pins) == 0:
170-
raise ChipFlowError("No pins were allocated by {package}")
171-
logger.debug(f"allocated range: {pins}")
172-
unallocated = unallocated - set(pins)
173-
_map, _ = allocate_pins(k, v, pins)
174-
port_map.add_ports(component, k, _map)
175-
176-
newlock = LockFile(process=process,
177-
package=package,
178-
port_map=port_map,
179-
metadata=interfaces)
29+
# Get package definition from dict instead of Pydantic model
30+
package_name = config_dict["chipflow"]["silicon"]["package"]
31+
package_def = PACKAGE_DEFINITIONS[package_name]
32+
33+
top = top_components(config_dict)
34+
35+
# Use the PackageDef to allocate the pins:
36+
for name, component in top.items():
37+
package_def.register_component(name, component)
38+
39+
newlock = package_def.allocate_pins(oldlock)
18040

18141
with open(lockfile, 'w') as f:
18242
f.write(newlock.model_dump_json(indent=2, serialize_as_any=True))

chipflow_lib/platforms/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,4 @@
1212

1313
__all__ = ['PIN_ANNOTATION_SCHEMA', 'IOSignature',
1414
'OutputIOSignature', 'InputIOSignature', 'BidirIOSignature',
15-
'load_pinlock', "PACKAGE_DEFINITIONS", 'top_interfaces']
15+
'load_pinlock', "PACKAGE_DEFINITIONS", 'top_components']

0 commit comments

Comments
 (0)