Skip to content

Commit b418cde

Browse files
robtaylorclaude
andcommitted
Extract packaging module from platforms/_utils.py
Phase 3 of the refactoring: create a dedicated packaging module with focused sub-modules for pin definitions, port descriptions, lock files, and package definitions. **New Modules Created:** - chipflow_lib/packaging/pins.py: Pin dataclasses (PowerPins, JTAGPins, BringupPins) - chipflow_lib/packaging/port_desc.py: Port description models (PortDesc, PortMap) - chipflow_lib/packaging/lockfile.py: Lock file models (LockFile, Package) - chipflow_lib/packaging/allocation.py: Pin allocation algorithms - chipflow_lib/packaging/base.py: Base classes (BasePackageDef, LinearAllocPackageDef) - chipflow_lib/packaging/standard.py: Standard packages (QuadPackageDef, BareDiePackageDef) - chipflow_lib/packaging/grid_array.py: Grid array packages (GAPackageDef) - chipflow_lib/packaging/openframe.py: Openframe package (moved from platforms/) - chipflow_lib/packaging/utils.py: Utility functions (load_pinlock) - chipflow_lib/packaging/__init__.py: Public API exports **Changes:** - Updated platforms/_utils.py to import from packaging module instead of defining locally - Removed ~850 lines of duplicate code from platforms/_utils.py - All tests passing (38 passed, 11 skipped) **Benefits:** - Better organization: Related packaging code now grouped together - Smaller files: Multiple focused ~200-300 line files instead of one 1131-line monster - Cleaner imports: `from chipflow_lib.packaging import QuadPackageDef` - Easier maintenance: Clear module boundaries 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
1 parent f77a528 commit b418cde

File tree

18 files changed

+16975
-862
lines changed

18 files changed

+16975
-862
lines changed

KODING.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# ChipFlow Library Coding Standards
2+
3+
## Build/Test/Lint Commands
4+
5+
- Run tests: `pdm run test`
6+
- Run tests with coverage: `pdm run test-cov`
7+
- Run tests with HTML coverage report: `pdm run test-cov-html`
8+
- Run documentation tests: `pdm run test-docs`
9+
- Run linting: `pdm run lint`
10+
- Build documentation: `pdm run docs`
11+
- Run a single test file: `pdm run pytest tests/test_cli.py`
12+
- Run a single test function: `pdm run pytest tests/test_cli.py::test_command_name`
13+
14+
## Code Style Guidelines
15+
16+
- **Imports:** Follow standard Python import conventions (e.g., `import module`, `from module import name`). Group imports: standard library, third-party, local application.
17+
- **Formatting:** Use Ruff for automated formatting (`pdm lint`). Always lint before finishing a task. Adhere to PEP 8 guidelines. Max line length is generally 88 characters but Ruff config doesn't enforce this strictly.
18+
- **Typing:** Use type hints for all function signatures and variables where appropriate. Run type checking if available (currently not configured in `pyproject.toml`).
19+
- **Naming Conventions:**
20+
- Variables and functions: `snake_case`
21+
- Classes: `PascalCase`
22+
- Constants: `UPPER_SNAKE_CASE`
23+
- **Error Handling:** Use specific exception types. Avoid catching generic `Exception`. Log errors appropriately.
24+
- **Documentation:** Write docstrings for all public modules, classes, functions, and methods following Google style docstrings.

\

Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
# SPDX-License-Identifier: BSD-2-Clause
2+
3+
import re
4+
import sys
5+
6+
from dataclasses import dataclass
7+
from pathlib import Path
8+
from typing import (
9+
List, Tuple, Any, Self, Protocol, runtime_checkable,
10+
Literal, Union, TypeVar, Generic
11+
)
12+
13+
from typing_extensions import Unpack, TypedDict, NotRequired
14+
15+
from amaranth.lib import wiring
16+
from amaranth.lib.wiring import Out
17+
18+
from .. import _ensure_chipflow_root
19+
from ._utils import InputIOSignature, OutputIOSignature, BidirIOSignature, IOModelOptions, _chipflow_schema_uri
20+
from ._annotate import amaranth_annotate
21+
22+
SIM_ANNOTATION_SCHEMA = str(_chipflow_schema_uri("simulatable-interface", 0))
23+
DATA_SCHEMA = str(_chipflow_schema_uri("simulatable-data", 0))
24+
DRIVER_MODEL_SCHEMA = str(_chipflow_schema_uri("driver-model", 0))
25+
26+
class SimInterface(TypedDict):
27+
uid: str
28+
parameters: List[Tuple[str, Any]]
29+
30+
@runtime_checkable
31+
@dataclass
32+
class DataclassProtocol(Protocol):
33+
pass
34+
35+
36+
@dataclass
37+
class SoftwareBuild:
38+
"""
39+
This holds the information needed for building software and providing the built outcome
40+
"""
41+
42+
user_files: list[Path]
43+
offset: int
44+
filename: Path
45+
build_dir: Path
46+
type: Literal["SoftwareBuild"] = "SoftwareBuild"
47+
48+
def __init__(self, user_files: list[Path], *, offset=0):
49+
self.build_dir = _ensure_chipflow_root() / 'build' / 'software'
50+
self.filename = self.build_dir / 'software.bin'
51+
self.user_files = list(user_files)
52+
self.offset = offset
53+
54+
55+
_T_DataClass = TypeVar('_T_DataClass', bound=DataclassProtocol)
56+
class Data(TypedDict, Generic[_T_DataClass]):
57+
data: _T_DataClass
58+
59+
60+
class DriverModel(TypedDict):
61+
"""
62+
Options for `DriverSignature`
63+
64+
Attributes:
65+
component: The `wiring.Component` that this is the signature for.
66+
regs_struct: The name of the C struct that represents the registers of this component
67+
h_files: Header files for the driver
68+
c_files: C files for the driver
69+
regs_bus: The bus of this `Component` which contains its control registers
70+
include_dirs: any extra include directories needed by the driver
71+
"""
72+
component Annotated[
73+
wiring.Component,
74+
PlainSerializer(lambda x: {
75+
'name': x.__class__.__name__,
76+
'file': sys.modules[x.__module__].__file__, return_type=str),
77+
WrapValidator(lambda v, h: h(v.strip('Vv ') if isinstance(v, str) else h(v)))
78+
]
79+
80+
regs_struct: str
81+
h_files: NotRequired[list[Path]]
82+
c_files: NotRequired[list[Path]]
83+
include_dirs: NotRequired[list[Path]]
84+
regs_bus: NotRequired[str]
85+
_base_path: NotRequired[Path] # gets filled by the decorator to the base directory where the Component was defined
86+
87+
88+
_VALID_UID = re.compile('[a-zA-Z_.]').search
89+
90+
def _unpack_dict(d: dict) -> str:
91+
params = [ f"{k}={repr(v)}" for k,v in d.items()]
92+
return ', '.join(params)
93+
94+
"""
95+
Attributes:
96+
__chipflow_parameters__: list of tuples (name, value).
97+
It is expected that a model that takes parameters is implmemted as a template, with the parameters in the order
98+
given.
99+
"""
100+
def simulatable_interface(base="com.chipflow.chipflow_lib"):
101+
def decorate(klass):
102+
assert _VALID_UID(base)
103+
dec = amaranth_annotate(SimInterface, SIM_ANNOTATION_SCHEMA)
104+
klass = dec(klass)
105+
106+
def new_init(self,*args, **kwargs):
107+
original_init(self, *args, **kwargs)
108+
self.__chipflow_annotation__ = {
109+
"uid": klass.__chipflow_uid__,
110+
"parameters": self.__chipflow_parameters__(),
111+
}
112+
113+
def repr(self) -> str:
114+
return f"{klass.__name__}({_unpack_dict(self.__chipflow_parameters__())}, {_unpack_dict(self._options)})"
115+
116+
original_init = klass.__init__
117+
klass.__init__ = new_init
118+
klass.__chipflow_uid__ = f"{base}.{klass.__name__}"
119+
if not hasattr(klass, '__chipflow_parameters__'):
120+
klass.__chipflow_parameters__ = lambda self: []
121+
if not klass.__repr__:
122+
klass.__repr__ = repr
123+
return klass
124+
return decorate
125+
126+
127+
@simulatable_interface()
128+
class JTAGSignature(wiring.Signature):
129+
def __init__(self, **kwargs: Unpack[IOModelOptions]):
130+
super().__init__({
131+
"trst": Out(InputIOSignature(1)),
132+
"tck": Out(InputIOSignature(1)),
133+
"tms": Out(InputIOSignature(1)),
134+
"tdi": Out(InputIOSignature(1)),
135+
"tdo": Out(OutputIOSignature(1)),
136+
})
137+
138+
139+
@simulatable_interface()
140+
class SPISignature(wiring.Signature):
141+
def __init__(self, **kwargs: Unpack[IOModelOptions]):
142+
super().__init__({
143+
"sck": Out(OutputIOSignature(1)),
144+
"copi": Out(OutputIOSignature(1)),
145+
"cipo": Out(InputIOSignature(1)),
146+
"csn": Out(OutputIOSignature(1)),
147+
})
148+
149+
@simulatable_interface()
150+
class QSPIFlashSignature(wiring.Signature):
151+
def __init__(self, **kwargs: Unpack[IOModelOptions]):
152+
super().__init__({
153+
"clk": Out(OutputIOSignature(1)),
154+
"csn": Out(OutputIOSignature(1)),
155+
"d": Out(BidirIOSignature(4, individual_oe=True)),
156+
})
157+
158+
@simulatable_interface()
159+
class UARTSignature(wiring.Signature):
160+
def __init__(self, **kwargs: Unpack[IOModelOptions]):
161+
super().__init__({
162+
"tx": Out(OutputIOSignature(1)),
163+
"rx": Out(InputIOSignature(1)),
164+
})
165+
166+
@simulatable_interface()
167+
class I2CSignature(wiring.Signature):
168+
def __init__(self, **kwargs: Unpack[IOModelOptions]):
169+
super().__init__({
170+
"scl": Out(BidirIOSignature(1)),
171+
"sda": Out(BidirIOSignature(1))
172+
})
173+
self._options = kwargs
174+
175+
176+
@simulatable_interface()
177+
class GPIOSignature(wiring.Signature):
178+
179+
def __init__(self, pin_count=1, **kwargs: Unpack[IOModelOptions]):
180+
self._pin_count = pin_count
181+
self._options = kwargs
182+
kwargs['individual_oe'] = True
183+
super().__init__({
184+
"gpio": Out(BidirIOSignature(pin_count, **kwargs))
185+
})
186+
187+
def __chipflow_parameters__(self):
188+
return [('pin_count',self._pin_count)]
189+
190+
191+
def attach_data(c: wiring.Component, data: DataclassProtocol):
192+
data_dict: Data = {'data':data}
193+
setattr(c.signature, '__chipflow_data__', data_dict)
194+
amaranth_annotate(Data, DATA_SCHEMA, '__chipflow_data__', decorate_object=True)(c.signature)
195+
196+
197+
class DriverSignature(wiring.Signature):
198+
199+
def __init__(self, members, **kwargs: Unpack[DriverModel]):
200+
201+
definition_file = sys.modules[kwargs['component'].__module__].__file__
202+
assert definition_file
203+
base_path = Path(definition_file).parent.absolute()
204+
kwargs['_base_path'] = base_path
205+
if 'regs_bus' not in kwargs:
206+
kwargs['regs_bus'] = 'bus'
207+
208+
include_dirs = []
209+
if 'include_dirs' in kwargs:
210+
for i in kwargs['include_dirs']:
211+
if not Path(i).is_absolute():
212+
include_dirs.append(base_path / i)
213+
else:
214+
include_dirs.append(Path(i))
215+
216+
kwargs['include_dirs'] = include_dirs
217+
218+
self.__chipflow_driver_model__ = kwargs
219+
amaranth_annotate(DriverModel, DRIVER_MODEL_SCHEMA, '__chipflow_driver_model__', decorate_object=True)(self)
220+
super().__init__(members=members)
221+
222+
223+

0 commit comments

Comments
 (0)