Skip to content

Commit bcc5d02

Browse files
committed
wip
1 parent 0f05c98 commit bcc5d02

File tree

7 files changed

+94
-68
lines changed

7 files changed

+94
-68
lines changed

chipflow_lib/__init__.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ def _ensure_chipflow_root():
4848
return _ensure_chipflow_root.root
4949

5050

51-
def _parse_config():
51+
def _parse_config() -> 'ChipFlowConfig':
5252
"""Parse the chipflow.toml configuration file."""
5353
chipflow_root = _ensure_chipflow_root()
5454
config_file = Path(chipflow_root) / "chipflow.toml"
@@ -60,17 +60,15 @@ def _parse_config():
6060
raise ChipFlowError(f"TOML Error found when loading {config_file}: {e.msg} at line {e.lineno}, column {e.colno}")
6161

6262

63-
def _parse_config_file(config_file):
63+
def _parse_config_file(config_file) -> 'ChipFlowConfig':
6464
"""Parse a specific chipflow.toml configuration file."""
65-
from .config_models import Config
6665

6766
with open(config_file, "rb") as f:
6867
config_dict = tomli.load(f)
6968

7069
try:
7170
# Validate with Pydantic
72-
Config.model_validate(config_dict) # Just validate the config_dict
73-
return config_dict # Return the original dict for backward compatibility
71+
return Config.model_validate(config_dict) # Just validate the config_dict
7472
except ValidationError as e:
7573
# Format Pydantic validation errors in a user-friendly way
7674
error_messages = []
@@ -81,3 +79,6 @@ def _parse_config_file(config_file):
8179

8280
error_str = "\n".join(error_messages)
8381
raise ChipFlowError(f"Validation error in chipflow.toml:\n{error_str}")
82+
83+
84+
from .config_models import Config

chipflow_lib/cli.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,14 @@ def run(argv=sys.argv[1:]):
2626
commands = {}
2727
commands["pin"] = PinCommand(config)
2828

29-
for step_name, step_reference in config["chipflow"]["steps"].items():
30-
step_cls = _get_cls_by_reference(step_reference, context=f"step `{step_name}`")
31-
try:
32-
commands[step_name] = step_cls(config)
33-
except Exception:
34-
raise ChipFlowError(f"Encountered error while initializing step `{step_name}` "
35-
f"using `{step_reference}`")
29+
if config.chipflow.steps:
30+
for step_name, step_reference in config.chipflow.steps.items():
31+
step_cls = _get_cls_by_reference(step_reference, context=f"step `{step_name}`")
32+
try:
33+
commands[step_name] = step_cls(config)
34+
except Exception:
35+
raise ChipFlowError(f"Encountered error while initializing step `{step_name}` "
36+
f"using `{step_reference}`")
3637

3738
parser = argparse.ArgumentParser(
3839
prog="chipflow",

chipflow_lib/config_models.py

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

55
from pydantic import BaseModel, model_validator, ValidationInfo, field_validator
66

@@ -39,7 +39,7 @@ def validate_pad_dict(cls, v: dict, info: ValidationInfo):
3939

4040
class SiliconConfig(BaseModel):
4141
"""Configuration for silicon in chipflow.toml."""
42-
process: Process
42+
process: 'Process'
4343
package: Literal["caravel", "cf20", "pga144"]
4444
power: Dict[str, Voltage] = {}
4545
debug: Optional[Dict[str, bool]] = None
@@ -60,11 +60,6 @@ def validate_pad_dicts(cls, v, info: ValidationInfo):
6060
return v
6161

6262

63-
class StepsConfig(BaseModel):
64-
"""Configuration for steps in chipflow.toml."""
65-
silicon: str
66-
67-
6863
class ChipFlowConfig(BaseModel):
6964
"""Root configuration for chipflow.toml."""
7065
project_name: str

chipflow_lib/pin_lock.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,7 @@
1313

1414

1515
def lock_pins() -> None:
16-
# Get the config as dict for backward compatibility with top_components
17-
config_dict = _parse_config()
16+
config = _parse_config()
1817

1918
# Parse with Pydantic for type checking and strong typing
2019

@@ -28,11 +27,11 @@ def lock_pins() -> None:
2827
print(f"Locking pins: {'using pins.lock' if lockfile.exists() else ''}")
2928

3029
# Get package definition from dict instead of Pydantic model
31-
package_name = config_dict["chipflow"]["silicon"]["package"]
30+
package_name = config.chipflow.silicon.package
3231
package_def = PACKAGE_DEFINITIONS[package_name]
33-
process = config_dict["chipflow"]["silicon"]["process"]
32+
process = config.chipflow.silicon.process
3433

35-
top = top_components(config_dict)
34+
top = top_components(config)
3635

3736
# Use the PackageDef to allocate the pins:
3837
for name, component in top.items():

chipflow_lib/platforms/silicon.py

Lines changed: 27 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ def elaborate(self, platform):
6060

6161
heartbeat_buffer = io.Buffer("o", self.ports.heartbeat)
6262
m.submodules.heartbeat_buffer = heartbeat_buffer
63-
m.d.comb += heartbeat_buffer.o.eq(heartbeat_ctr[-1])
63+
m.d.comb += heartbeat_buffer.o.eq(heartbeat_ctr[-1]) # type: ignore
6464
return m
6565

6666

@@ -71,10 +71,10 @@ def __init__(self,
7171
port: Port,
7272
*,
7373
invert: bool = False):
74-
self._direction = io.Direction(port.direction)
74+
self._direction = io.Direction(port.iomodel['direction'])
7575
self._invert = invert
76-
self._model = port.model
77-
self._pins = port.pins
76+
self._iomodel = port.iomodel
77+
self._pins = port.pins if port.pins else []
7878

7979
# Initialize signal attributes to None
8080
self._i = None
@@ -87,20 +87,20 @@ def __init__(self,
8787
if self._direction in (io.Direction.Output, io.Direction.Bidir):
8888
self._o = Signal(port.width, name=f"{component}_{name}__o")
8989
if self._direction is io.Direction.Bidir:
90-
if "all_have_oe" in self._options and self._options["all_have_oe"]:
90+
if "all_have_oe" in self._iomodel and self._iomodel["all_have_oe"]:
9191
self._oe = Signal(port.width, name=f"{component}_{name}__oe", init=-1)
9292
else:
9393
self._oe = Signal(1, name=f"{component}_{name}__oe", init=-1)
9494
elif self._direction is io.Direction.Output:
9595
# Always create an _oe for output ports
9696
self._oe = Signal(1, name=f"{component}_{name}__oe", init=-1)
9797

98-
logger.debug(f"Created SiliconPlatformPort {name}, width={len(port.pins)},dir{self._direction}")
98+
logger.debug(f"Created SiliconPlatformPort {name}, width={len(self._pins)},dir{self._direction}")
9999

100100
def wire(self, m: Module, interface: PureInterface):
101-
assert self._direction == interface.signature.direction
101+
assert self._direction == interface.signature.direction #type: ignore
102102
if hasattr(interface, 'i'):
103-
m.d.comb += interface.i.eq(self.i)
103+
m.d.comb += interface.i.eq(self.i) # type: ignore
104104
for d in ['o', 'oe']:
105105
if hasattr(interface, d):
106106
m.d.comb += getattr(self, d).eq(getattr(interface, d))
@@ -141,16 +141,16 @@ def invert(self):
141141

142142
def __len__(self):
143143
if self._direction is io.Direction.Input:
144-
return len(self._i)
144+
return len(self.i)
145145
if self._direction is io.Direction.Output:
146-
return len(self._o)
146+
return len(self.o)
147147
if self._direction is io.Direction.Bidir:
148-
assert len(self._i) == len(self._o)
149-
if self._options["all_have_oe"]:
150-
assert len(self.o) == len(self._oe)
148+
assert len(self.i) == len(self.o)
149+
if 'all_have_oe' in self._iomodel and self._iomodel["all_have_oe"]:
150+
assert len(self.o) == len(self.oe)
151151
else:
152-
assert len(self._oe) == 1
153-
return len(self._i)
152+
assert len(self.oe) == 1
153+
return len(self.i)
154154
assert False # :nocov:
155155

156156
def __getitem__(self, key):
@@ -160,7 +160,7 @@ def __getitem__(self, key):
160160
result._oe = None if self._oe is None else self._oe[key]
161161
result._invert = self._invert
162162
result._direction = self._direction
163-
result._options = self._options
163+
result._iomodel = self._iomodel
164164
result._pins = self._pins
165165
return result
166166

@@ -171,7 +171,7 @@ def __invert__(self):
171171
result._oe = self._oe
172172
result._invert = not self._invert
173173
result._direction = self._direction
174-
result._options = self._options
174+
result._iomodel = self._iomodel
175175
result._pins = self._pins
176176
return result
177177

@@ -183,7 +183,7 @@ def __add__(self, other):
183183
result._oe = None if direction is io.Direction.Input else Cat(self._oe, other._oe)
184184
result._invert = self._invert
185185
result._direction = direction
186-
result._options = self._options
186+
result._iomodel = self._iomodel
187187
result._pins = self._pins + other._pins
188188
return result
189189

@@ -194,6 +194,10 @@ def __repr__(self):
194194

195195

196196
class IOBuffer(io.Buffer):
197+
o: Signal
198+
i: Signal
199+
oe: Signal
200+
197201
def elaborate(self, platform):
198202
if not isinstance(self.port, SiliconPlatformPort):
199203
raise TypeError(f"Cannot elaborate SiliconPlatform buffer with port {self.port!r}")
@@ -224,6 +228,9 @@ def elaborate(self, platform):
224228

225229

226230
class FFBuffer(io.FFBuffer):
231+
i: Signal
232+
o: Signal
233+
oe: Signal
227234
def elaborate(self, platform):
228235
if not isinstance(self.port, SiliconPlatformPort):
229236
raise TypeError(f"Cannot elaborate SiliconPlatform buffer with port {self.port!r}")
@@ -268,10 +275,7 @@ def instantiate_ports(self, m: Module):
268275
for name, port in v.items():
269276
self._ports[port.port_name] = SiliconPlatformPort(component, name, port)
270277

271-
for clock, name in self._config["chipflow"]["clocks"].items():
272-
if name not in pinlock.package.clocks:
273-
raise ChipFlowError("Unable to find clock {name} in pinlock")
274-
278+
for clock, name in self._config.chipflow.clocks.items():
275279
port_data = pinlock.package.clocks[name]
276280
port = SiliconPlatformPort(component, name, port_data, invert=True)
277281
self._ports[name] = port
@@ -283,7 +287,7 @@ def instantiate_ports(self, m: Module):
283287
setattr(m.submodules, "clk_buffer_" + clock, clk_buffer)
284288
m.d.comb += ClockSignal().eq(clk_buffer.i)
285289

286-
for reset, name in self._config["chipflow"]["resets"].items():
290+
for reset, name in self._config.chipflow.resets.items():
287291
port_data = pinlock.package.resets[name]
288292
port = SiliconPlatformPort(component, name, port_data)
289293
self._ports[name] = port

chipflow_lib/platforms/utils.py

Lines changed: 43 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@
1414
from typing import (
1515
Any, Dict, List, Set,
1616
Tuple, Optional, Union, Literal,
17-
Annotated, NamedTuple, Self
17+
Annotated, NamedTuple, Self,
18+
TYPE_CHECKING
1819
)
1920
from typing_extensions import (
2021
TypedDict, Unpack, NotRequired
@@ -34,6 +35,8 @@
3435

3536
from .. import ChipFlowError, _ensure_chipflow_root, _get_cls_by_reference
3637

38+
if TYPE_CHECKING:
39+
from ..config_models import Config
3740

3841
__all__ = ['IO_ANNOTATION_SCHEMA', 'IOSignature',
3942
'OutputIOSignature', 'InputIOSignature', 'BidirIOSignature',
@@ -101,7 +104,7 @@ class IOModel(_IOModelOptions):
101104
"""
102105

103106
width: int
104-
direction: NotRequired[Annotated[io.Direction, PlainSerializer(lambda x: x.value)]]
107+
direction: Annotated[io.Direction, PlainSerializer(lambda x: x.value)]
105108

106109
def io_annotation_schema():
107110
class Model(pydantic.BaseModel):
@@ -555,8 +558,34 @@ def _get_package(self) -> Package:
555558
assert self is not Self
556559
return Package(type=self) # type: ignore
557560

561+
def _allocate_bringup(self, config: 'Config') -> Component:
562+
cds = set(config.chipflow.clock_domains) if config.chipflow.clock_domains else set()
563+
cds.discard('sync')
564+
565+
d: Interface = { 'sync-clk': Port(type='clock',
566+
pins=[self.bringup_pins.core_clock],
567+
port_name='sync-clk',
568+
iomodel=IOModel(width=1, direction=io.Direction.Input)
569+
),
570+
'sync-rst': Port(type='reset',
571+
pins=[self.bringup_pins.core_reset],
572+
port_name='sync-rst',
573+
iomodel=IOModel(width=1, direction=io.Direction.Input)
574+
)
575+
}
576+
assert config.chipflow.silicon
577+
if config.chipflow.silicon.debug and \
578+
config.chipflow.silicon.debug['heartbeat']:
579+
d['heartbeat'] = Port(type='heartbeat',
580+
pins=[self.bringup_pins.core_heartbeat],
581+
port_name='heartbeat',
582+
iomodel=IOModel(width=1, direction=io.Direction.Output)
583+
)
584+
#TODO: JTAG
585+
return {'bringup_pins': d}
586+
558587
@abc.abstractmethod
559-
def allocate_pins(self, process: 'Process', lockfile: Optional[LockFile]) -> LockFile:
588+
def allocate_pins(self, config: 'Config', process: 'Process', lockfile: LockFile|None) -> LockFile:
560589
"""
561590
Allocate package pins to the registered component.
562591
Pins should be allocated in the most usable way for *users* of the packaged IC.
@@ -606,8 +635,10 @@ def model_post_init(self, __context):
606635
self._ordered_pins: List[Pin] = sorted(pins)
607636
return super().model_post_init(__context)
608637

609-
def allocate_pins(self, process: 'Process', lockfile: LockFile|None) -> LockFile:
638+
def allocate_pins(self, config: 'Config', process: 'Process', lockfile: LockFile|None) -> LockFile:
610639
portmap = _linear_allocate_components(self._interfaces, lockfile, self._allocate, set(self._ordered_pins))
640+
bringup_pins = self._allocate_bringup(config)
641+
portmap.ports['_core']=bringup_pins
611642
package = self._get_package()
612643
return LockFile(package=package, process=process, metadata=self._interfaces, port_map=portmap)
613644

@@ -687,8 +718,10 @@ def model_post_init(self, __context):
687718
self._ordered_pins: List[Pin] = sorted(pins)
688719
return super().model_post_init(__context)
689720

690-
def allocate_pins(self, process: 'Process', lockfile: LockFile|None) -> 'LockFile':
721+
def allocate_pins(self, config: 'Config', process: 'Process', lockfile: LockFile|None) -> LockFile:
691722
portmap = _linear_allocate_components(self._interfaces, lockfile, self._allocate, set(self._ordered_pins))
723+
bringup_pins = self._allocate_bringup(config)
724+
portmap.ports['_core']=bringup_pins
692725
package = self._get_package()
693726
return LockFile(package=package, process=process, metadata=self._interfaces, port_map=portmap)
694727

@@ -898,9 +931,10 @@ def sort_by_quadrant(pins: Set[GAPin]) -> List[Pin]:
898931

899932
return super().model_post_init(__context)
900933

901-
902-
def allocate_pins(self, process: 'Process', lockfile: LockFile|None) -> 'LockFile':
934+
def allocate_pins(self, config: 'Config', process: 'Process', lockfile: LockFile|None) -> LockFile:
903935
portmap = _linear_allocate_components(self._interfaces, lockfile, self._allocate, set(self._ordered_pins))
936+
bringup_pins = self._allocate_bringup(config)
937+
portmap.ports['_core']=bringup_pins
904938
package = self._get_package()
905939
return LockFile(package=package, process=process, metadata=self._interfaces, port_map=portmap)
906940

@@ -985,14 +1019,14 @@ def top_components(config):
9851019
result = {}
9861020

9871021
# First pass: collect component configs
988-
for name, conf in config["chipflow"]["top"].items():
1022+
for name, conf in config.chipflow.top.items():
9891023
if '.' in name:
9901024
assert isinstance(conf, dict)
9911025
logger.debug(f"Config found for {name}")
9921026
component_configs[name.split('.')[0]] = conf
9931027

9941028
# Second pass: instantiate components
995-
for name, ref in config["chipflow"]["top"].items():
1029+
for name, ref in config.chipflow.top.items():
9961030
if '.' not in name: # Skip component configs, only process actual components
9971031
cls = _get_cls_by_reference(ref, context=f"top component: {name}")
9981032
if name in component_configs:

0 commit comments

Comments
 (0)