Skip to content

Commit 1958998

Browse files
committed
Add openframe package support
1 parent 896f711 commit 1958998

20 files changed

+423
-219
lines changed

chipflow_lib/_appresponse.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
from dataclasses import dataclass
2+
3+
from pydantic import BaseModel, PlainSerializer, model_serializer
4+
5+
@dataclass
6+
class OmitIfNone:
7+
pass
8+
9+
class AppResponseModel(BaseModel):
10+
@model_serializer
11+
def _serialize(self):
12+
skip_if_none = set()
13+
serialize_aliases = dict()
14+
15+
# Gather fields that should omit if None
16+
for name, field_info in self.model_fields.items():
17+
if any(
18+
isinstance(metadata, OmitIfNone) for metadata in field_info.metadata
19+
):
20+
skip_if_none.add(name)
21+
elif field_info.serialization_alias:
22+
serialize_aliases[name] = field_info.serialization_alias
23+
24+
serialized = dict()
25+
26+
for name, value in self:
27+
# Skip serializing None if it was marked with "OmitIfNone"
28+
if value is None and name in skip_if_none:
29+
continue
30+
serialize_key = serialize_aliases.get(name, name)
31+
32+
# Run Annotated PlainSerializer
33+
for metadata in self.model_fields[name].metadata:
34+
if isinstance(metadata, PlainSerializer):
35+
value = metadata.func(value)
36+
37+
serialized[serialize_key] = value
38+
39+
return serialized

chipflow_lib/config_models.py

Lines changed: 7 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,63 +1,23 @@
11
# SPDX-License-Identifier: BSD-2-Clause
2-
import re
3-
from typing import Dict, Optional, Literal, Any, List
2+
from typing import Dict, Optional, Any, List
43

5-
from pydantic import BaseModel, model_validator, ValidationInfo, field_validator
4+
from pydantic import BaseModel
65

7-
from .platforms.utils import Process
6+
from .platforms._internal import PACKAGE_DEFINITIONS, Process, Voltage
87

9-
class PadConfig(BaseModel):
10-
"""Configuration for a pad in chipflow.toml."""
11-
type: Literal["io", "i", "o", "oe", "clock", "reset", "power", "ground"]
12-
loc: str
138

14-
@model_validator(mode="after")
15-
def validate_loc_format(self):
16-
"""Validate that the location is in the correct format."""
17-
if not re.match(r"^[NSWE]?[0-9]+$", self.loc):
18-
raise ValueError(f"Invalid location format: {self.loc}, expected format: [NSWE]?[0-9]+")
19-
return self
9+
def known_package(package: str):
10+
if package not in PACKAGE_DEFINITIONS.keys():
11+
raise ValueError(f"{package} is not a valid package type. Valid package types are {PACKAGE_DEFINITIONS.keys()}")
2012

21-
@classmethod
22-
def validate_pad_dict(cls, v: dict, info: ValidationInfo):
23-
"""Custom validation for pad dicts from TOML that may not have all fields."""
24-
if isinstance(v, dict):
25-
# Handle legacy format - if 'type' is missing but should be inferred from context
26-
if 'loc' in v and 'type' not in v:
27-
if info.field_name == 'power':
28-
v['type'] = 'power'
29-
30-
# Map legacy 'clk' type to 'clock' to match our enum
31-
if 'type' in v and v['type'] == 'clk':
32-
v['type'] = 'clock'
33-
34-
return v
35-
return v
36-
37-
38-
Voltage = float
3913

4014
class SiliconConfig(BaseModel):
4115
"""Configuration for silicon in chipflow.toml."""
4216
process: 'Process'
43-
package: Literal["caravel", "cf20", "pga144"]
17+
package: str
4418
power: Dict[str, Voltage] = {}
4519
debug: Optional[Dict[str, bool]] = None
4620
# This is still kept around to allow forcing pad locations.
47-
pads: Optional[Dict[str, PadConfig]] = {}
48-
49-
@field_validator('pads', 'power', mode='before')
50-
@classmethod
51-
def validate_pad_dicts(cls, v, info: ValidationInfo):
52-
"""Pre-process pad dictionaries to handle legacy format."""
53-
if isinstance(v, dict):
54-
result = {}
55-
for key, pad_dict in v.items():
56-
# Apply the pad validator with context about which field we're in
57-
validated_pad = PadConfig.validate_pad_dict(pad_dict, info)
58-
result[key] = validated_pad
59-
return result
60-
return v
6121

6222

6323
class ChipFlowConfig(BaseModel):

chipflow_lib/pin_lock.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from pprint import pformat
77

88
from . import _parse_config, _ensure_chipflow_root, ChipFlowError
9-
from .platforms import top_components, LockFile, PACKAGE_DEFINITIONS
9+
from .platforms._internal import top_components, LockFile, PACKAGE_DEFINITIONS
1010

1111
# logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)
1212
logger = logging.getLogger(__name__)

chipflow_lib/platforms/__init__.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,16 @@
66
77
"""
88

9-
from .silicon import *
10-
from .sim import *
11-
from .utils import *
9+
from .silicon import SiliconPlatformPort, SiliconPlatform
10+
from .sim import SimPlatform
11+
from .utils import (
12+
IO_ANNOTATION_SCHEMA, IOSignature, IOModel,
13+
OutputIOSignature, InputIOSignature, BidirIOSignature,
14+
)
15+
from ._packages import PACKAGE_DEFINITIONS
1216

1317
__all__ = ['IO_ANNOTATION_SCHEMA', 'IOSignature', 'IOModel',
1418
'OutputIOSignature', 'InputIOSignature', 'BidirIOSignature',
15-
'load_pinlock', "PACKAGE_DEFINITIONS", 'top_components', 'LockFile',
16-
'Package', 'PortMap', 'Port', 'Process',
17-
'GAPackageDef', 'QuadPackageDef', 'BareDiePackageDef', 'BasePackageDef',
18-
'BringupPins', 'JTAGPins', 'PowerPins',
1919
'SiliconPlatformPort', 'SiliconPlatform',
20-
'SimPlatform']
20+
'SimPlatform',
21+
'PACKAGE_DEFINITIONS']
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
from .silicon import *
2+
from .sim import *
3+
from .utils import *
4+
from ._packages import *
5+
__all__ = ['IO_ANNOTATION_SCHEMA', 'IOSignature', 'IOModel',
6+
'OutputIOSignature', 'InputIOSignature', 'BidirIOSignature',
7+
'load_pinlock', "PACKAGE_DEFINITIONS", 'top_components', 'LockFile',
8+
'Package', 'PortMap', 'Port', 'Process',
9+
'GAPackageDef', 'QuadPackageDef', 'BareDiePackageDef', 'BasePackageDef',
10+
'BringupPins', 'JTAGPins', 'PowerPins',
11+
'SiliconPlatformPort', 'SiliconPlatform',
12+
'SimPlatform']
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
from typing import List, NamedTuple, Optional, Literal
2+
3+
from .utils import Voltage, PowerPins, LinearAllocPackageDef, BringupPins
4+
5+
class OFPin(NamedTuple):
6+
pin: int
7+
kind: str
8+
idx: int = 0
9+
voltage: Optional[Voltage] = None
10+
name: Optional[str] = None
11+
12+
OF_GPIO = [
13+
OFPin(31, "gpio", 0), # gpio[31]
14+
OFPin(32, "gpio", 1), # gpio[32]
15+
OFPin(33, "gpio", 2), # gpio[33]
16+
OFPin(34, "gpio", 3), # gpio[34]
17+
OFPin(35, "gpio", 4), # gpio[35]
18+
OFPin(36, "gpio", 5), # gpio[36]
19+
OFPin(37, "gpio", 6), # gpio[37]
20+
OFPin(41, "gpio", 7), # gpio[41]
21+
OFPin(42, "gpio", 8), # gpio[42]
22+
OFPin(43, "gpio", 9), # gpio[43]
23+
OFPin(44, "gpio", 10), # gpio[44]
24+
OFPin(45, "gpio", 11), # gpio[45]
25+
OFPin(46, "gpio", 12), # gpio[46]
26+
OFPin(48, "gpio", 13), # gpio[48]
27+
OFPin(50, "gpio", 14), # gpio[50]
28+
OFPin(51, "gpio", 15), # gpio[51]
29+
OFPin(53, "gpio", 16), # gpio[53]
30+
OFPin(54, "gpio", 17), # gpio[54]
31+
OFPin(55, "gpio", 18), # gpio[55]
32+
OFPin(57, "gpio", 19), # gpio[57]
33+
OFPin(58, "gpio", 20), # gpio[58]
34+
OFPin(59, "gpio", 21), # gpio[59]
35+
OFPin(60, "gpio", 22), # gpio[60]
36+
OFPin(61, "gpio", 23), # gpio[61]
37+
OFPin(62, "gpio", 24), # gpio[62]
38+
OFPin(2, "gpio", 25), # gpio[2]
39+
OFPin(3, "gpio", 26), # gpio[3]
40+
OFPin(4, "gpio", 27), # gpio[4]
41+
OFPin(5, "gpio", 28), # gpio[5]
42+
OFPin(6, "gpio", 29), # gpio[6]
43+
OFPin(7, "gpio", 30), # gpio[7]
44+
OFPin(8, "gpio", 31), # gpio[8]
45+
OFPin(11, "gpio", 32), # gpio[11]
46+
OFPin(12, "gpio", 33), # gpio[12]
47+
OFPin(13, "gpio", 34), # gpio[13]
48+
OFPin(14, "gpio", 35), # gpio[14]
49+
OFPin(15, "gpio", 36), # gpio[15]
50+
OFPin(16, "gpio", 37), # gpio[16]
51+
# OFPin(22, "gpio", 38) is assigned as clock
52+
# OFPin(24, "gpio", 39) is assigned as heartbeat
53+
OFPin(25, "gpio", 40), # gpio[25]
54+
OFPin(26, "gpio", 41), # gpio[26]
55+
OFPin(27, "gpio", 42), # gpio[27]
56+
OFPin(28, "gpio", 43), # gpio[28]
57+
]
58+
59+
OF_CLOCK_PIN = OFPin(22, "gpio", 38)
60+
OF_HEARTBEAT_PIN = OFPin(24, "gpio", 39)
61+
OF_RESET_PIN = OFPin(21, "resetbi")
62+
63+
OF_CORE_POWER = [
64+
(OFPin(18,"vcc", voltage=1.8, name="d"), # Power, Digital power supply
65+
OFPin(23,"vss", name="d")), # Digital power ground
66+
]
67+
68+
OF_OTHER_POWER= [
69+
(OFPin(30,"vdd", voltage=3.3, name="a"), # Power, Analog power supply
70+
OFPin(20,"vss", name="a")), # Analog power ground
71+
72+
(OFPin(49,"vcc", voltage=1.8, name="d1"), # Power, Digital power supply
73+
OFPin(39,"vss", name="d1")), # Digital power ground
74+
75+
(OFPin(17,"vdd", voltage=3.3, name="io"), # Power, ESD and padframe power supply
76+
OFPin(29,"vss", name="io")), # ESD and padframe ground
77+
78+
(OFPin(64,"vdd", voltage=3.3, name="io"), # Power, ESD and padframe power supply
79+
OFPin(56,"vss", name="io")), # ESD and padframe ground
80+
81+
(OFPin(63,"vcc", voltage=1.8, name="d2"), # Power, Digital power supply
82+
OFPin(10,"vss", name="d2")), # Digital power ground
83+
84+
(OFPin(40,"vdd", voltage=3.3, name="a1"), # Power, Analog power supply
85+
OFPin(38,"vss", name="a1")), # Analog power ground
86+
87+
(OFPin(47,"vdd", voltage=3.3, name="a1"), # Power, Analog power supply
88+
OFPin(52,"vss", name="a1")), # Analog power ground
89+
90+
(OFPin(9,"vdd", voltage=3.3, name="a2"), # Power, Analog power supply
91+
OFPin(1,"vss", name="a2")), # Analog power ground
92+
]
93+
94+
OF_OTHER = [
95+
OFPin(19, "NC") # Not connected
96+
]
97+
98+
class OpenframePackageDef(LinearAllocPackageDef):
99+
100+
name: str = "Openframe"
101+
package_type: Literal["OpenframePackageDef"] = "OpenframePackageDef"
102+
def model_post_init(self, __context):
103+
self._ordered_pins = OF_GPIO
104+
105+
super().model_post_init(__context)
106+
107+
108+
@property
109+
def _core_power(self) -> List[PowerPins]:
110+
pps = []
111+
112+
for power, ground in OF_CORE_POWER:
113+
pp = PowerPins(power=power, ground=ground, voltage=power.voltage)
114+
pps.append(pp)
115+
116+
return pps
117+
118+
@property
119+
def bringup_pins(self) -> BringupPins:
120+
return BringupPins(
121+
core_power=self._core_power,
122+
core_clock=OF_CLOCK_PIN,
123+
core_reset=OF_RESET_PIN,
124+
core_heartbeat=OF_HEARTBEAT_PIN,
125+
)
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
from .utils import QuadPackageDef, BareDiePackageDef, GAPackageDef, Package
2+
from ._openframe import OpenframePackageDef
3+
4+
# Add any new package types to both PACKAGE_DEFINITIONS and the PackageDef union
5+
PACKAGE_DEFINITIONS = {
6+
"pga144": QuadPackageDef(name="pga144", width=36, height=36),
7+
"cf20": BareDiePackageDef(name="cf20", width=7, height=3),
8+
"openframe": OpenframePackageDef()
9+
}
10+
11+
Package.model_rebuild()

chipflow_lib/platforms/silicon.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ def __init__(self,
8888
if self._direction in (io.Direction.Output, io.Direction.Bidir):
8989
self._o = Signal(port.width, name=f"{component}_{name}__o")
9090
if self._direction is io.Direction.Bidir:
91-
if "all_have_oe" in self._iomodel and self._iomodel["all_have_oe"]:
91+
if "individual_oe" in self._iomodel and self._iomodel["individual_oe"]:
9292
self._oe = Signal(port.width, name=f"{component}_{name}__oe", init=-1)
9393
else:
9494
self._oe = Signal(1, name=f"{component}_{name}__oe", init=-1)
@@ -148,7 +148,7 @@ def __len__(self):
148148
return len(self.o)
149149
if self._direction is io.Direction.Bidir:
150150
assert len(self.i) == len(self.o)
151-
if 'all_have_oe' in self._iomodel and self._iomodel["all_have_oe"]:
151+
if 'individual_oe' in self._iomodel and self._iomodel["individual_oe"]:
152152
assert len(self.o) == len(self.oe)
153153
else:
154154
assert len(self.oe) == 1
@@ -279,14 +279,14 @@ def instantiate_ports(self, m: Module):
279279
self._ports[port.port_name] = SiliconPlatformPort(component, name, port)
280280

281281
for clock in pinlock.port_map.get_clocks():
282-
domain = name=clock.iomodel['clock_domain_o']
282+
domain = name=clock.iomodel['clock_domain']
283283
setattr(m.domains, domain, ClockDomain(name=domain))
284284
clk_buffer = io.Buffer("i", self._ports[clock.port_name])
285285
setattr(m.submodules, "clk_buffer_" + domain, clk_buffer)
286286
m.d.comb += ClockSignal().eq(clk_buffer.i) #type: ignore[reportAttributeAccessIssue]
287287

288288
for reset in pinlock.port_map.get_resets():
289-
domain = name=clock.iomodel['clock_domain_o']
289+
domain = name=clock.iomodel['clock_domain']
290290
rst_buffer = io.Buffer("i", self._ports[reset.port_name])
291291
setattr(m.submodules, reset.port_name, rst_buffer)
292292
setattr(m.submodules, reset.port_name + "_sync", FFSynchronizer(rst_buffer.i, ResetSignal())) #type: ignore[reportAttributeAccessIssue]

chipflow_lib/platforms/sim.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# SPDX-License-Identifier: BSD-2-Clause
22

3+
import logging
34
import os
45
import sys
56
from pathlib import Path
@@ -14,6 +15,7 @@
1415

1516

1617
__all__ = ["SimPlatform"]
18+
logger = logging.getLogger(__name__)
1719

1820

1921
class SimPlatform:
@@ -42,6 +44,7 @@ def build(self, e):
4244
if port.direction is io.Direction.Bidir:
4345
ports.append((f"io${port_name}$oe", port.oe, PortDirection.Output))
4446

47+
print("elaborating design")
4548
output = rtlil.convert(e, name="sim_top", ports=ports, platform=self)
4649

4750
top_rtlil = Path(self.build_dir) / "sim_soc.il"
@@ -73,19 +76,26 @@ def instantiate_ports(self, m: Module):
7376
for component, iface in pinlock.port_map.ports.items():
7477
for k, v in iface.items():
7578
for name, port in v.items():
79+
logger.debug(f"Instantiating port {port.port_name}: {port}")
7680
invert = port.invert if port.invert else False
7781
self._ports[port.port_name] = io.SimulationPort(port.direction, port.width, invert=invert, name=f"{component}-{name}")
7882

7983
for clock in pinlock.port_map.get_clocks():
80-
setattr(m.domains, clock.port_name, ClockDomain(name=clock.port_name))
84+
assert 'clock_domain' in clock.iomodel
85+
domain = clock.iomodel['clock_domain']
86+
logger.debug(f"Instantiating clock buffer for {clock.port_name}, domain {domain}")
87+
setattr(m.domains, domain, ClockDomain(name=domain))
8188
clk_buffer = io.Buffer(clock.direction, self._ports[clock.port_name])
8289
setattr(m.submodules, "clk_buffer_" + clock.port_name, clk_buffer)
8390
m.d.comb += ClockSignal().eq(clk_buffer.i) # type: ignore[reportAttributeAccessIssue]
8491

8592
for reset in pinlock.port_map.get_resets():
93+
assert 'clock_domain' in reset.iomodel
94+
domain = reset.iomodel['clock_domain']
95+
logger.debug(f"Instantiating reset synchronizer for {reset.port_name}, domain {domain}")
8696
rst_buffer = io.Buffer(reset.direction, self._ports[clock.port_name])
8797
setattr(m.submodules, reset.port_name, rst_buffer)
88-
ffsync = FFSynchronizer(rst_buffer.i, ResetSignal(name=reset.port_name)) # type: ignore[reportAttributeAccessIssue]
98+
ffsync = FFSynchronizer(rst_buffer.i, ResetSignal()) # type: ignore[reportAttributeAccessIssue]
8999
setattr(m.submodules, reset.port_name + "_sync", ffsync)
90100

91101
self._pinlock = pinlock

0 commit comments

Comments
 (0)