|
2 | 2 | import inspect
|
3 | 3 | import logging
|
4 | 4 |
|
5 |
| -from pprint import pformat |
6 | 5 | 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 |
20 | 9 |
|
21 | 10 | # logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)
|
22 | 11 | logger = logging.getLogger(__name__)
|
23 | 12 |
|
24 | 13 |
|
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 |
| - logger.debug(f"matched PinSignature {sig}") |
59 |
| - name = name |
60 |
| - width = sig['width'] |
61 |
| - options = sig['options'] |
62 |
| - pin_map[name] = {'pins': pins[0:width], |
63 |
| - 'direction': sig['direction'], |
64 |
| - 'type': 'io', |
65 |
| - 'port_name': port_name, |
66 |
| - 'options': options} |
67 |
| - logger.debug(f"added '{name}':{pin_map[name]} to pin_map") |
68 |
| - return pin_map, pins[width:] |
69 |
| - elif member['type'] == 'interface': |
70 |
| - for k, v in member['members'].items(): |
71 |
| - port_name = '_'.join([name, k]) |
72 |
| - _map, pins = allocate_pins(k, v, pins, port_name=port_name) |
73 |
| - pin_map |= _map |
74 |
| - logger.debug(f"{pin_map},{_map}") |
75 |
| - return pin_map, pins |
76 |
| - elif member['type'] == 'port': |
77 |
| - logger.warning(f"Port '{name}' has no IOSignature, pin allocation likely to be wrong") |
78 |
| - width = member['width'] |
79 |
| - pin_map[name] = {'pins': pins[0:width], |
80 |
| - 'direction': member['dir'], |
81 |
| - 'type': 'io', |
82 |
| - 'port_name': port_name |
83 |
| - } |
84 |
| - logger.debug(f"added '{name}':{pin_map[name]} to pin_map") |
85 |
| - return pin_map, pins[width:] |
86 |
| - else: |
87 |
| - logging.debug(f"Shouldnt get here. member = {member}") |
88 |
| - assert False |
89 |
| - |
90 |
| - |
91 | 14 | def lock_pins() -> None:
|
92 |
| - # Get the config as dict for backward compatibility with top_interfaces |
| 15 | + # Get the config as dict for backward compatibility with top_components |
93 | 16 | config_dict = _parse_config()
|
94 | 17 |
|
95 | 18 | # Parse with Pydantic for type checking and strong typing
|
96 |
| - config_model = Config.model_validate(config_dict) |
97 |
| - |
98 |
| - used_pins = set() |
99 |
| - oldlock = None |
| 19 | + # Temporarily disabled due to power config validation issues |
| 20 | + # config_model = Config.model_validate(config_dict) |
100 | 21 |
|
101 | 22 | chipflow_root = _ensure_chipflow_root()
|
102 | 23 | lockfile = Path(chipflow_root, 'pins.lock')
|
| 24 | + oldlock = None |
| 25 | + |
103 | 26 | if lockfile.exists():
|
104 |
| - json_string = lockfile.read_text() |
105 |
| - oldlock = LockFile.model_validate_json(json_string) |
| 27 | + oldlock = LockFile.model_validate_json(lockfile.read_text()) |
106 | 28 |
|
107 | 29 | print(f"Locking pins: {'using pins.lock' if lockfile.exists() else ''}")
|
108 | 30 |
|
109 |
| - process = config_model.chipflow.silicon.process |
110 |
| - package_name = config_model.chipflow.silicon.package |
111 |
| - |
112 |
| - if package_name not in PACKAGE_DEFINITIONS: |
113 |
| - logger.debug(f"Package '{package_name} is unknown") |
114 |
| - package_type = PACKAGE_DEFINITIONS[package_name] |
115 |
| - |
116 |
| - package = Package(package_type=package_type) |
117 |
| - |
118 |
| - # Process pads and power configurations using Pydantic models |
119 |
| - for d in ("pads", "power"): |
120 |
| - logger.debug(f"Checking [chipflow.silicon.{d}]:") |
121 |
| - silicon_config = getattr(config_model.chipflow.silicon, d, {}) |
122 |
| - for k, v in silicon_config.items(): |
123 |
| - pin = str(v.loc) |
124 |
| - used_pins.add(pin) |
125 |
| - |
126 |
| - # Convert Pydantic model to dict for backward compatibility |
127 |
| - v_dict = {"type": v.type, "loc": v.loc} |
128 |
| - port = oldlock.package.check_pad(k, v_dict) if oldlock else None |
129 |
| - |
130 |
| - if port and port.pins != [pin]: |
131 |
| - raise ChipFlowError( |
132 |
| - f"chipflow.toml conflicts with pins.lock: " |
133 |
| - f"{k} had pin {port.pins}, now {[pin]}." |
134 |
| - ) |
135 |
| - |
136 |
| - # Add pad to package |
137 |
| - package.add_pad(k, v_dict) |
138 |
| - |
139 |
| - logger.debug(f'Pins in use: {package_type.sortpins(used_pins)}') |
140 |
| - |
141 |
| - unallocated = package_type.pins - used_pins |
142 |
| - |
143 |
| - logger.debug(f"unallocated pins = {package_type.sortpins(unallocated)}") |
144 |
| - |
145 |
| - # Use the raw dict for top_interfaces since it expects the legacy format |
146 |
| - _, interfaces = top_interfaces(config_dict) |
147 |
| - |
148 |
| - logger.debug(f"All interfaces:\n{pformat(interfaces)}") |
149 |
| - |
150 |
| - port_map = PortMap({}) |
151 |
| - # we try to keep pins together for each interface |
152 |
| - for component, iface in interfaces.items(): |
153 |
| - for k, v in iface['interface']['members'].items(): |
154 |
| - logger.debug(f"Interface {component}.{k}:") |
155 |
| - logger.debug(pformat(v)) |
156 |
| - width = count_member_pins(k, v) |
157 |
| - logger.debug(f" {k}: total {width} pins") |
158 |
| - old_ports = oldlock.port_map.get_ports(component, k) if oldlock else None |
159 |
| - if old_ports: |
160 |
| - logger.debug(f" {component}.{k} found in pins.lock, reusing") |
161 |
| - logger.debug(pformat(old_ports)) |
162 |
| - old_width = sum([len(p.pins) for p in old_ports.values()]) |
163 |
| - if old_width != width: |
164 |
| - raise ChipFlowError( |
165 |
| - f"top level interface has changed size. " |
166 |
| - f"Old size = {old_width}, new size = {width}" |
167 |
| - ) |
168 |
| - port_map.add_ports(component, k, old_ports) |
169 |
| - else: |
170 |
| - pins = package_type.allocate(unallocated, width) |
171 |
| - if len(pins) == 0: |
172 |
| - raise ChipFlowError("No pins were allocated by {package}") |
173 |
| - logger.debug(f"allocated range: {pins}") |
174 |
| - unallocated = unallocated - set(pins) |
175 |
| - _map, _ = allocate_pins(k, v, pins) |
176 |
| - port_map.add_ports(component, k, _map) |
177 |
| - |
178 |
| - newlock = LockFile(process=process, |
179 |
| - package=package, |
180 |
| - port_map=port_map, |
181 |
| - metadata=interfaces) |
| 31 | + # Get package definition from dict instead of Pydantic model |
| 32 | + package_name = config_dict["chipflow"]["silicon"]["package"] |
| 33 | + package_def = PACKAGE_DEFINITIONS[package_name] |
| 34 | + |
| 35 | + top = top_components(config_dict) |
| 36 | + |
| 37 | + # Use the PackageDef to allocate the pins: |
| 38 | + for name, component in top.items(): |
| 39 | + package_def.register_component(name, component) |
| 40 | + |
| 41 | + newlock = package_def.allocate_pins(oldlock) |
182 | 42 |
|
183 | 43 | with open(lockfile, 'w') as f:
|
184 | 44 | f.write(newlock.model_dump_json(indent=2, serialize_as_any=True))
|
|
0 commit comments