Skip to content

Commit 6a7d6b1

Browse files
committed
wip
1 parent 7c20b03 commit 6a7d6b1

File tree

7 files changed

+402
-33
lines changed

7 files changed

+402
-33
lines changed

chipflow_lib/__init__.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,59 @@
11
# 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
218

319
class ChipFlowError(Exception):
420
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

chipflow_lib/cli.py

Lines changed: 36 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,8 @@ def _ensure_chipflow_root():
4949
],
5050
"additionalProperties": False,
5151
"properties": {
52-
"project_id": {
53-
"type": "integer",
52+
"project_name": {
53+
"type": "string",
5454
},
5555
"top": {
5656
"type": "object",
@@ -73,30 +73,14 @@ def _ensure_chipflow_root():
7373
"pad_ring": {
7474
"enum": ["caravel", "cf20", "pga144"]
7575
},
76-
"pads": {
76+
"pads": { "$ref": "#/$defs/pin" },
77+
"clocks": {
7778
"type": "object",
78-
"additionalProperties": False,
79-
"minProperties": 1,
80-
"patternProperties": {
81-
".+": {
82-
"type": "object",
83-
"required": [
84-
"type",
85-
"loc",
86-
],
87-
"additionalProperties": False,
88-
"properties": {
89-
"type": {
90-
"enum": ["io", "i", "o", "oe", "clk"]
91-
},
92-
"loc": {
93-
"type": "string",
94-
"pattern": "^[NSWE]?[0-9]+$"
95-
},
96-
}
97-
}
98-
}
79+
"patternPropertues": {
80+
".+": { "type": "string"}
81+
},
9982
},
83+
"reset": { "type": "string"},
10084
"power": {
10185
"type": "object",
10286
"additionalProperties": False,
@@ -116,9 +100,36 @@ def _ensure_chipflow_root():
116100
}
117101
}
118102
},
119-
}
103+
104+
},
120105
},
121106
},
107+
},
108+
},
109+
"$defs": {
110+
"pin": {
111+
"type": "object",
112+
"additionalProperties": False,
113+
"minProperties": 1,
114+
"patternProperties": {
115+
".+": {
116+
"type": "object",
117+
"required": [
118+
"type",
119+
"loc",
120+
],
121+
"additionalProperties": False,
122+
"properties": {
123+
"type": {
124+
"enum": ["io", "i", "o", "oe", "clk"]
125+
},
126+
"loc": {
127+
"type": "string",
128+
"pattern": "^[NSWE]?[0-9]+$"
129+
},
130+
}
131+
}
132+
}
122133
}
123134
}
124135
}

chipflow_lib/pin_lock.py

Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
import json
2+
import logging
3+
import re
4+
import sys
5+
6+
from amaranth import Shape
7+
from pathlib import Path
8+
from pprint import pformat
9+
10+
from chipflow_lib.platforms.iostream import PORT_LAYOUT_SCHEMA
11+
from chipflow_lib.cli import _parse_config, _get_cls_by_reference
12+
13+
logger = logging.getLogger(__name__)
14+
15+
def has_consecutive_numbers(lst):
16+
if not lst:
17+
return False
18+
lst.sort()
19+
return all(lst[i] + 1 == lst[i + 1] for i in range(len(lst) - 1))
20+
21+
def strip_pin_suffix(name):
22+
"""Strip _i, _o, and _oe suffixes from a pin name.
23+
24+
Args:
25+
name: Pin name string
26+
27+
Returns:
28+
Name with suffix removed
29+
"""
30+
return re.sub(r'(_i|_o|_oe)$', '', name)
31+
32+
def find_consecutive_sequence(lst, n):
33+
"""Find the next sequence of n consecutive numbers in a sorted list.
34+
35+
Args:
36+
lst: Sorted list of numbers
37+
n: Length of consecutive sequence to find
38+
39+
Returns:
40+
A slice indexing the first sequence of n consecutive numbers found within the given list
41+
or None if no such sequence exists
42+
"""
43+
if not lst or len(lst) < n:
44+
return None
45+
46+
for i in range(len(lst) - n + 1):
47+
if all(lst[i + j] + 1 == lst[i + j + 1] for j in range(n - 1)):
48+
return slice(i,i + n)
49+
return None
50+
51+
def signature_width(signature):
52+
width = 0
53+
obj = signature.create()
54+
for a,b,c in signature.flatten(obj):
55+
shape = Shape.cast(b.shape)
56+
width += shape.width
57+
return width
58+
59+
def member_width(member):
60+
if member.is_signature:
61+
return signature_width(member.signature)
62+
else:
63+
shape = Shape.cast(member.shape)
64+
return shape.width
65+
66+
MATCH_TRIPLE = re.compile(r'(_i|_o|_oe)$')
67+
68+
def coalesce_triples(sig: dict) -> None:
69+
if sig['type'] == 'port':
70+
pass
71+
72+
def count_pins(port):
73+
width = 0
74+
for _, v in port.items():
75+
if type(v) is dict:
76+
width += count_pins(v)
77+
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}")
92+
else:
93+
direction, width = v
94+
if width == 1:
95+
pin_map[n] = {'pin':pins[0], 'type':direction}
96+
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
137+
138+
139+
logging.basicConfig(level=logging.DEBUG, stream=sys.stdout)
140+
config = _parse_config()
141+
used_pins = set()
142+
pin_map = {}
143+
lockfile = Path('pins.lock')
144+
if lockfile.exists():
145+
with open(lockfile) as f:
146+
old_lock = json.load(f)
147+
old_map = old_lock["map"]
148+
else:
149+
old_lock = {}
150+
old_map = {}
151+
152+
for d, default in [("pads", "i"), ("power","pwr")]:
153+
logger.debug(f"Checking [chipflow.silicon.{d}]:")
154+
pin_map[d] = {}
155+
for k, v in config["chipflow"]["silicon"][d].items():
156+
pin = int(v['loc'])
157+
used_pins.add(pin)
158+
if d in old_map and k in old_map[d] and old_map[d][k]['pin'] != pin:
159+
print(f"chipflow.toml conflicts with pins.lock: "
160+
f"{k} had pin {old_map[d][k]}, now {pin}.")
161+
exit(1)
162+
pin_map[d][k] = {
163+
'pin': pin,
164+
'type': v['type'] if 'type' in v else None}
165+
166+
167+
logger.info(f'Pins in use:\n{pformat(sorted(used_pins))}')
168+
169+
unallocated = sorted(set(range(144)) - used_pins)
170+
171+
ports = {}
172+
top_components = config["chipflow"]["top"].items()
173+
component_configs = {}
174+
top = {}
175+
176+
for name, conf in top_components:
177+
if '.' in name:
178+
assert conf is dict
179+
logger.debug("Config found for {name}")
180+
component_configs[name.split('.')[0]] = conf
181+
182+
for name, ref in top_components:
183+
cls = _get_cls_by_reference(ref, context=f"top component: {name}")
184+
if name in component_configs:
185+
top[name] = cls(component_configs[name])
186+
else:
187+
top[name] = cls()
188+
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)
195+
196+
with open('pins.lock', 'w') as f:
197+
newlock = {'map': pin_map,
198+
'ports': ports}
199+
200+
json.dump(newlock, f, indent=2, sort_keys=True)
201+
#
202+
# obj = soc_top.signature.create()
203+
# for a,b,c in soc_top.signature.flatten(obj):
204+
# pin_name = '_'.join(a)
205+
# iface = a[0]
206+
# shape = Shape.cast(b.shape)
207+
# count = 0
208+
# for i in pin_map[iface]['pins']:
209+
# pin_name_i = f'{pin_name}_{count}'
210+
# count += 1
211+
# # print(f'pin name {pin_name} {b.flow}')
212+
# pin_map[iface]['members'][pin_name_i]={'pin':i, 'dir':b.flow}
213+
#
214+
# # pprint(pin_map)

0 commit comments

Comments
 (0)