Skip to content

Commit 8a080b2

Browse files
committed
wip
1 parent 8a63eb2 commit 8a080b2

File tree

8 files changed

+261
-152
lines changed

8 files changed

+261
-152
lines changed

chipflow_lib/__init__.py

Lines changed: 1 addition & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,2 @@
1-
# SPDX-License-Identifier: BSD-2-Clause
2-
from dataclasses import dataclass
3-
from typing import Dict, Union, Any
4-
5-
from amaranth import (
6-
Module,
7-
Elaboratable,
8-
Signal,
9-
ClockDomain,
10-
ClockSignal,
11-
ResetSignal
12-
)
13-
14-
from amaranth.lib import io
15-
from amaranth.lib.cdc import FFSynchronizer
16-
from amaranth.lib.wiring import Component
17-
from .platforms.iostream import PortSignature, IOShape
18-
191
class ChipFlowError(Exception):
20-
pass
21-
22-
def make_hashable(cls):
23-
def __hash__(self):
24-
return hash(id(self))
25-
26-
def __eq__(self, obj):
27-
return id(self) == id(obj)
28-
29-
cls.__hash__ = __hash__
30-
cls.__eq__ = __eq__
31-
return cls
32-
33-
34-
@make_hashable
35-
@dataclass
36-
class Heartbeat(Component):
37-
clock_domain: str = "sync"
38-
counter_size: int = 23
39-
name: str = "heartbeat"
40-
41-
def pins():
42-
return IOShape({
43-
'heartbeat': {'heartbeat': ('o', 1)}
44-
})
45-
46-
def __init__(self):
47-
super().__init__(PortSignature({}))
48-
self._ioshape = self.__class__.pins()
49-
50-
def elaborate(self, platform):
51-
m = Module()
52-
# Heartbeat LED (to confirm clock/reset alive)
53-
heartbeat_ctr = Signal(self.counter_size)
54-
getattr(m.d, self.clock_domain).__iadd__(heartbeat_ctr.eq(heartbeat_ctr + 1))
55-
56-
heartbeat_buffer = io.Buffer("o", self.ports.heartbeat)
57-
m.submodules.heartbeat_buffer = heartbeat_buffer
58-
m.d.comb += heartbeat_buffer.o.eq(heartbeat_ctr[-1])
59-
return m
2+
pass

chipflow_lib/cli.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,8 @@ def run(argv=sys.argv[1:]):
174174
raise ChipFlowError(f"Encountered error while building CLI argument parser for "
175175
f"step `{step_name}`")
176176

177+
pins = parser.add_subparsers("pins")
178+
lock = lock.add_subparsers("lock")
177179
args = parser.parse_args(argv)
178180
try:
179181
steps[args.step].run_cli(args)

chipflow_lib/errors.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
class ChipFlowError(Exception):
2+
pass
3+

chipflow_lib/pin_lock.py

Lines changed: 104 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,12 @@
11
import json
2-
import logging
32
import re
4-
import sys
53

6-
from amaranth import Shape
4+
from pprint import pprint
75
from pathlib import Path
8-
from pprint import pformat
96

10-
from chipflow_lib.platforms.iostream import PORT_LAYOUT_SCHEMA
7+
from amaranth import Shape
118
from chipflow_lib.cli import _parse_config, _get_cls_by_reference
12-
13-
logger = logging.getLogger(__name__)
9+
from chipflow_lib.platforms import PACKAGE_DEFINITIONS
1410

1511
def has_consecutive_numbers(lst):
1612
if not lst:
@@ -69,114 +65,111 @@ def coalesce_triples(sig: dict) -> None:
6965
if sig['type'] == 'port':
7066
pass
7167

72-
def count_pins(port):
73-
width = 0
74-
for _, v in port.items():
75-
if type(v) is dict:
68+
def count_pins(member):
69+
if member['type'] == 'interface':
70+
width = 0
71+
for _, v in member['members'].items():
7672
width += count_pins(v)
73+
return width
74+
elif member['type'] == 'port':
75+
return member['width']
76+
77+
def allocate_pins(name, member, pins, pin_map):
78+
print(f"allocate_pins: {pins}")
79+
if member['type'] == 'interface':
80+
for k, v in member['members'].items():
81+
n = '_'.join([name,k])
82+
pins = allocate_pins(n, v, pins, pin_map)
83+
return pins
84+
elif member['type'] == 'port':
85+
name = name
86+
width = member['width']
87+
if width == 1:
88+
pin_map[name] = {'pin':pins[0], 'type':member['dir']}
7789
else:
78-
width += v[1]
79-
return width
80-
81-
82-
def allocate_pins(name, port, pins):
83-
pin_map = {}
84-
logger.debug(f"allocate_pins: name={name}, port={port}, pins={pins}")
85-
for k, v in port.items():
86-
n = '_'.join([name,k])
87-
logger.debug(f"{k},{v},{n}")
88-
if type(v) is dict:
89-
_map, pins = allocate_pins(n, v, pins)
90-
pin_map |= _map
91-
logger.debug(f"{pin_map},{_map}")
90+
pin_map[name] = {'start':pins[0],
91+
'end':pins[width-1],
92+
'type':member['dir']}
93+
return pins[width:]
94+
95+
96+
def check_pins(name, member, old_map, new_map):
97+
if member['type'] == 'interface':
98+
for k, v in member['members'].items():
99+
n = '_'.join([name,k])
100+
check_pins(n, v, old_map, new_map)
101+
elif member['type'] == 'port':
102+
width = member['width']
103+
if width == 1:
104+
assert 'pin' in old_map[name]
92105
else:
93-
direction, width = v
106+
assert 'start' in old_map[name]
107+
assert 'end' in old_map[name]
108+
assert old_map[name]['end'] - old_map[name]['start'] + 1 == width
109+
new_map[name] = old_map[name]
110+
111+
def connect_pins(pin_map):
112+
for k,v in interfaces.items():
113+
if member['type'] == 'interface':
114+
for k, v in member['members'].items():
115+
n = '_'.join([name,k])
116+
check_pins(n, v, old_map, new_map)
117+
elif member['type'] == 'port':
118+
width = member['width']
94119
if width == 1:
95-
pin_map[n] = {'pin':pins[0], 'type':direction}
120+
assert 'pin' in old_map[name]
96121
else:
97-
pin_map[n] = {'start':pins[0],
98-
'end':pins[width-1],
99-
'type':direction}
100-
logger.debug(f"pin_map[{n}]={pin_map[n]}")
101-
pins = pins[width:]
102-
return pin_map, pins
103-
104-
105-
def assign_pins(ports, old_lock, unallocated):
106-
old_ports = old_lock["ports"] if "ports" in old_lock else {}
107-
old_map = old_lock["map"]["ports"] if "map" in old_lock else {}
108-
pin_map = {}
109-
110-
# we try to keep pins together for each port
111-
for k,v in ports.items():
112-
logger.debug(f"Port {k}:\n{pformat(v)}")
113-
width = count_pins(v)
114-
logger.debug(f"member {k} total width = {width}")
115-
116-
if k in old_ports:
117-
logger.debug(f"{k} already has pins defined")
118-
if width != count_pins(old_ports[k]):
119-
raise Exception("Port {k} has changed size. Use -c to allocate new pins non-contigously")
120-
_map = old_map[k]
121-
old_pins = [v['pin'] for v in old_map[k].values()]
122-
logger.debug("old pins = {old_pins}")
123-
unallocated = sorted(list(set(unallocated) - set(old_pins)))
124-
else:
125-
pins = find_consecutive_sequence(unallocated, width)
126-
logger.debug(f"allocated range: {pins}")
127-
if pins is None:
128-
raise Exception(f"Error allocating pins for {k},{v} in {ports}")
129-
130-
131-
newpins = unallocated[pins]
132-
unallocated[pins] = []
133-
_map,_ = allocate_pins(k, v, newpins)
134-
135-
pin_map[k] = _map
136-
return pin_map
122+
assert 'start' in old_map[name]
123+
assert 'end' in old_map[name]
124+
assert old_map[name]['end'] - old_map[name]['start'] + 1 == width
125+
new_map[name] = old_map[name]
137126

138127

139-
logging.basicConfig(level=logging.DEBUG, stream=sys.stdout)
140128
config = _parse_config()
141129
used_pins = set()
142130
pin_map = {}
143131
lockfile = Path('pins.lock')
144132
if lockfile.exists():
145133
with open(lockfile) as f:
146134
old_lock = json.load(f)
147-
old_map = old_lock["map"]
135+
old_interfaces = old_lock['interfaces']
136+
old_map = old_lock['map']
148137
else:
149-
old_lock = {}
150138
old_map = {}
139+
old_interfaces = {}
140+
141+
package = config["chipflow"]["silicon"]["padring"]
151142

152-
for d, default in [("pads", "i"), ("power","pwr")]:
153-
logger.debug(f"Checking [chipflow.silicon.{d}]:")
154-
pin_map[d] = {}
143+
if package not in PACKAGE_DEFINITIONS:
144+
print(f"Package '{package} is unknown")
145+
146+
for d in ("pads", "power"):
147+
print(f"Checking [chipflow.silicon.{d}]:")
155148
for k, v in config["chipflow"]["silicon"][d].items():
156-
pin = int(v['loc'])
149+
pin = v['loc']
157150
used_pins.add(pin)
158-
if d in old_map and k in old_map[d] and old_map[d][k]['pin'] != pin:
151+
if k in old_map and old_map[k]['pin'] != pin:
159152
print(f"chipflow.toml conflicts with pins.lock: "
160-
f"{k} had pin {old_map[d][k]}, now {pin}.")
153+
f"{k} had pin {old_map[k]}, now {pin}.")
161154
exit(1)
162-
pin_map[d][k] = {
155+
pin_map[k] = {
163156
'pin': pin,
164157
'type': v['type'] if 'type' in v else None}
165158

166159

167-
logger.info(f'Pins in use:\n{pformat(sorted(used_pins))}')
160+
print(f'Pins in use: {sorted(used_pins)}')
168161

169-
unallocated = sorted(set(range(144)) - used_pins)
162+
unallocated = sorted(PACKAGE_DEFINITIONS[package] - used_pins)
170163

171-
ports = {}
164+
interfaces = {}
172165
top_components = config["chipflow"]["top"].items()
173166
component_configs = {}
174167
top = {}
175168

176169
for name, conf in top_components:
177170
if '.' in name:
178171
assert conf is dict
179-
logger.debug("Config found for {name}")
172+
print("Config found for {name}")
180173
component_configs[name.split('.')[0]] = conf
181174

182175
for name, ref in top_components:
@@ -186,16 +179,38 @@ def assign_pins(ports, old_lock, unallocated):
186179
else:
187180
top[name] = cls()
188181
metadata = top[name].metadata.as_json()
189-
logger.debug(f"{name}.metadata = {metadata}")
190-
ports |= metadata['interface']['annotations'][PORT_LAYOUT_SCHEMA]['ports']
191-
192-
logger.debug(f"All ports: {list(ports.keys())}")
193-
194-
pin_map["ports"] = assign_pins(ports, old_lock, unallocated)
182+
interfaces |= metadata['interface']['members']
183+
184+
print(f"All interfaces: {interfaces.keys()}")
185+
pin_map_interfaces = {}
186+
187+
# we try to keep pins together for each interface
188+
for k,v in interfaces.items():
189+
print(f"Interface {k}:")
190+
pprint(v)
191+
width = count_pins(v)
192+
print(f"member {k} total width = {width}")
193+
pins = find_consecutive_sequence(unallocated, width)
194+
print(f"allocated range: {pins}")
195+
if pins is None:
196+
print("ERROR")
197+
if k in old_interfaces:
198+
if old_interfaces[k]['start'] != pins.start or \
199+
old_interfaces[k]['end'] != pins.stop:
200+
# TODO: option to allocate new pins nonconsecutively
201+
print("top level interface has changed size")
202+
exit(1)
203+
check_pins(k, v, old_map, pin_map)
204+
pin_map_interfaces[k] = {'top': True, 'start':pins.start, 'end':pins.stop}
205+
else:
206+
pin_map_interfaces[k] = {'top': True, 'start':pins.start, 'end':pins.stop}
207+
newpins = unallocated[pins]
208+
unallocated[pins] = []
209+
allocate_pins(k, v, newpins, pin_map)
195210

196211
with open('pins.lock', 'w') as f:
197-
newlock = {'map': pin_map,
198-
'ports': ports}
212+
newlock = {'interfaces': pin_map_interfaces,
213+
'map': pin_map}
199214

200215
json.dump(newlock, f, indent=2, sort_keys=True)
201216
#

0 commit comments

Comments
 (0)