chipfoundry-cli 2.6.0__tar.gz → 2.7.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- chipfoundry_cli-2.6.0/README.md → chipfoundry_cli-2.7.1/PKG-INFO +54 -5
- chipfoundry_cli-2.6.0/PKG-INFO → chipfoundry_cli-2.7.1/README.md +24 -32
- {chipfoundry_cli-2.6.0 → chipfoundry_cli-2.7.1}/chipfoundry_cli/__init__.py +1 -1
- {chipfoundry_cli-2.6.0 → chipfoundry_cli-2.7.1}/chipfoundry_cli/librelane_run.py +140 -0
- {chipfoundry_cli-2.6.0 → chipfoundry_cli-2.7.1}/chipfoundry_cli/main.py +400 -73
- {chipfoundry_cli-2.6.0 → chipfoundry_cli-2.7.1}/chipfoundry_cli/remote_precheck_git.py +6 -3
- {chipfoundry_cli-2.6.0 → chipfoundry_cli-2.7.1}/chipfoundry_cli/utils.py +55 -4
- {chipfoundry_cli-2.6.0 → chipfoundry_cli-2.7.1}/pyproject.toml +1 -1
- {chipfoundry_cli-2.6.0 → chipfoundry_cli-2.7.1}/LICENSE +0 -0
- {chipfoundry_cli-2.6.0 → chipfoundry_cli-2.7.1}/chipfoundry_cli/check_refs.py +0 -0
- {chipfoundry_cli-2.6.0 → chipfoundry_cli-2.7.1}/chipfoundry_cli/preview.py +0 -0
- {chipfoundry_cli-2.6.0 → chipfoundry_cli-2.7.1}/chipfoundry_cli/version_check.py +0 -0
|
@@ -1,3 +1,32 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: chipfoundry-cli
|
|
3
|
+
Version: 2.7.1
|
|
4
|
+
Summary: CLI tool to automate ChipFoundry project submission to SFTP server
|
|
5
|
+
License: Apache-2.0
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Author: ChipFoundry
|
|
8
|
+
Author-email: marwan.abbas@chipfoundry.io
|
|
9
|
+
Requires-Python: >=3.8.1,<4.0
|
|
10
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
19
|
+
Requires-Dist: cf-ipm
|
|
20
|
+
Requires-Dist: click (>=8.0.0,<9)
|
|
21
|
+
Requires-Dist: httpx (>=0.24.0,<1.0)
|
|
22
|
+
Requires-Dist: paramiko (>=3.0.0,<4)
|
|
23
|
+
Requires-Dist: rich (>=13,<14)
|
|
24
|
+
Requires-Dist: textual (>=0.40.0,<1)
|
|
25
|
+
Requires-Dist: toml (>=0.10,<1.0)
|
|
26
|
+
Project-URL: Homepage, https://chipfoundry.io
|
|
27
|
+
Project-URL: Repository, https://github.com/chipfoundry/cf-cli
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
29
|
+
|
|
1
30
|
# ChipFoundry CLI (`cf-cli`)
|
|
2
31
|
|
|
3
32
|
[](https://badge.fury.io/py/chipfoundry-cli)
|
|
@@ -112,6 +141,24 @@ If you already have a project directory with GDS files:
|
|
|
112
141
|
|
|
113
142
|
---
|
|
114
143
|
|
|
144
|
+
## Double-wide openframe projects
|
|
145
|
+
|
|
146
|
+
Double-wide projects (`type: double_wide`, detected from
|
|
147
|
+
`gds/double_wide_openframe_project_wrapper.gds`) use the same commands as
|
|
148
|
+
openframe projects, with these differences:
|
|
149
|
+
|
|
150
|
+
- `cf setup` clones the double-wide harness
|
|
151
|
+
([chipfoundry/double_wide_openframe](https://github.com/chipfoundry/double_wide_openframe))
|
|
152
|
+
into `caravel/` instead of caravel-lite, and skips the management core wrapper.
|
|
153
|
+
The harness ref comes from `harness_refs.double_wide` in `versions.json` (default `main`).
|
|
154
|
+
- `cf precheck` passes `caravel/` to cf-precheck as the golden root, since the
|
|
155
|
+
precheck image only ships the standard caravel.
|
|
156
|
+
- `cf verify` runs caravel-cocotb in OpenFrame mode. The harness supplies the
|
|
157
|
+
63-pad testbench (`verilog/rtl/toplevel_cocotb.v`) and include lists.
|
|
158
|
+
- `cf gpio-config` is not used: the user wrapper drives every pad control signal.
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
115
162
|
## Project Structure Requirements
|
|
116
163
|
|
|
117
164
|
Your project directory **must** contain:
|
|
@@ -120,6 +167,7 @@ Your project directory **must** contain:
|
|
|
120
167
|
- `user_project_wrapper.gds` (for digital projects)
|
|
121
168
|
- `user_analog_project_wrapper.gds` (for analog projects)
|
|
122
169
|
- `openframe_project_wrapper.gds` (for openframe projects)
|
|
170
|
+
- `double_wide_openframe_project_wrapper.gds` (for double-wide openframe projects)
|
|
123
171
|
- **Note**: Both compressed (`.gz`) and uncompressed (`.gds`) files are supported
|
|
124
172
|
- `verilog/rtl/user_defines.v` (required for digital/analog)
|
|
125
173
|
- `.cf/project.json` (optional; will be created/updated automatically)
|
|
@@ -617,7 +665,7 @@ Preconditions:
|
|
|
617
665
|
- Project has a GitHub repo URL (set via `cf init`, shown in the portal).
|
|
618
666
|
- The ChipFoundry GitHub App is installed on that repo (prompted in the portal).
|
|
619
667
|
- Your local `HEAD` has been pushed to `origin` on some branch (`git push`).
|
|
620
|
-
- Push-critical files at `HEAD` are clean: wrapper GDS, `verilog/rtl/user_defines.v` (
|
|
668
|
+
- Push-critical files at `HEAD` are clean: wrapper GDS, `verilog/rtl/user_defines.v` (digital and analog projects only), and `.cf/project.json` (when tracked).
|
|
621
669
|
|
|
622
670
|
What happens:
|
|
623
671
|
1. `cf push --remote` resolves your local HEAD SHA and checks it is reachable from a remote ref.
|
|
@@ -641,8 +689,8 @@ both port 22 and GitHub.
|
|
|
641
689
|
Preconditions:
|
|
642
690
|
- Project is linked to the platform (`cf init` or `cf link`) and you are logged in.
|
|
643
691
|
- Wrapper GDS exists locally under `gds/` (one of `user_project_wrapper.gds[.gz]`,
|
|
644
|
-
`user_analog_project_wrapper.gds[.gz]`, `openframe_project_wrapper.gds[.gz]`).
|
|
645
|
-
- For
|
|
692
|
+
`user_analog_project_wrapper.gds[.gz]`, `openframe_project_wrapper.gds[.gz]`, `double_wide_openframe_project_wrapper.gds[.gz]`).
|
|
693
|
+
- For digital and analog projects, `verilog/rtl/user_defines.v` is also uploaded when present.
|
|
646
694
|
- Outbound HTTPS to `*.s3.us-east-2.amazonaws.com` is allowed.
|
|
647
695
|
|
|
648
696
|
What happens:
|
|
@@ -863,7 +911,7 @@ The CLI tracks your project submission state through the `submission_state` fiel
|
|
|
863
911
|
|
|
864
912
|
1. **File Collection:**
|
|
865
913
|
- Checks for required GDS and Verilog files
|
|
866
|
-
- Auto-detects project type (digital, analog, openframe) based on GDS file name
|
|
914
|
+
- Auto-detects project type (digital, analog, openframe, double_wide) based on GDS file name
|
|
867
915
|
- **Supports both compressed and uncompressed GDS files**
|
|
868
916
|
|
|
869
917
|
2. **Configuration:**
|
|
@@ -932,7 +980,7 @@ cf config
|
|
|
932
980
|
# Initialize project (in your project directory)
|
|
933
981
|
cf init
|
|
934
982
|
# Project name (detected: my_awesome_project):
|
|
935
|
-
# Project type (digital/analog/openframe) (detected: digital):
|
|
983
|
+
# Project type (digital/analog/openframe/double_wide) (detected: digital):
|
|
936
984
|
|
|
937
985
|
# Upload your project
|
|
938
986
|
cf push
|
|
@@ -1050,3 +1098,4 @@ gds/user_project_wrapper.gds.gz # ← Remove this one
|
|
|
1050
1098
|
|
|
1051
1099
|
- For help, contact info@chipfoundry.io or visit [chipfoundry.io](https://chipfoundry.io)
|
|
1052
1100
|
- For bug reports or feature requests, open an issue on [GitHub](https://github.com/chipfoundry/cf-cli)
|
|
1101
|
+
|
|
@@ -1,29 +1,3 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: chipfoundry-cli
|
|
3
|
-
Version: 2.6.0
|
|
4
|
-
Summary: CLI tool to automate ChipFoundry project submission to SFTP server
|
|
5
|
-
Home-page: https://chipfoundry.io
|
|
6
|
-
License: Apache-2.0
|
|
7
|
-
Author: ChipFoundry
|
|
8
|
-
Author-email: marwan.abbas@chipfoundry.io
|
|
9
|
-
Requires-Python: >=3.8.1,<4.0
|
|
10
|
-
Classifier: License :: OSI Approved :: Apache Software License
|
|
11
|
-
Classifier: Programming Language :: Python :: 3
|
|
12
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
13
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
-
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
-
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
-
Requires-Dist: cf-ipm
|
|
18
|
-
Requires-Dist: click (>=8.0.0,<9)
|
|
19
|
-
Requires-Dist: httpx (>=0.24.0,<1.0)
|
|
20
|
-
Requires-Dist: paramiko (>=3.0.0,<4)
|
|
21
|
-
Requires-Dist: rich (>=13,<14)
|
|
22
|
-
Requires-Dist: textual (>=0.40.0,<1)
|
|
23
|
-
Requires-Dist: toml (>=0.10,<1.0)
|
|
24
|
-
Project-URL: Repository, https://github.com/chipfoundry/cf-cli
|
|
25
|
-
Description-Content-Type: text/markdown
|
|
26
|
-
|
|
27
1
|
# ChipFoundry CLI (`cf-cli`)
|
|
28
2
|
|
|
29
3
|
[](https://badge.fury.io/py/chipfoundry-cli)
|
|
@@ -138,6 +112,24 @@ If you already have a project directory with GDS files:
|
|
|
138
112
|
|
|
139
113
|
---
|
|
140
114
|
|
|
115
|
+
## Double-wide openframe projects
|
|
116
|
+
|
|
117
|
+
Double-wide projects (`type: double_wide`, detected from
|
|
118
|
+
`gds/double_wide_openframe_project_wrapper.gds`) use the same commands as
|
|
119
|
+
openframe projects, with these differences:
|
|
120
|
+
|
|
121
|
+
- `cf setup` clones the double-wide harness
|
|
122
|
+
([chipfoundry/double_wide_openframe](https://github.com/chipfoundry/double_wide_openframe))
|
|
123
|
+
into `caravel/` instead of caravel-lite, and skips the management core wrapper.
|
|
124
|
+
The harness ref comes from `harness_refs.double_wide` in `versions.json` (default `main`).
|
|
125
|
+
- `cf precheck` passes `caravel/` to cf-precheck as the golden root, since the
|
|
126
|
+
precheck image only ships the standard caravel.
|
|
127
|
+
- `cf verify` runs caravel-cocotb in OpenFrame mode. The harness supplies the
|
|
128
|
+
63-pad testbench (`verilog/rtl/toplevel_cocotb.v`) and include lists.
|
|
129
|
+
- `cf gpio-config` is not used: the user wrapper drives every pad control signal.
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
141
133
|
## Project Structure Requirements
|
|
142
134
|
|
|
143
135
|
Your project directory **must** contain:
|
|
@@ -146,6 +138,7 @@ Your project directory **must** contain:
|
|
|
146
138
|
- `user_project_wrapper.gds` (for digital projects)
|
|
147
139
|
- `user_analog_project_wrapper.gds` (for analog projects)
|
|
148
140
|
- `openframe_project_wrapper.gds` (for openframe projects)
|
|
141
|
+
- `double_wide_openframe_project_wrapper.gds` (for double-wide openframe projects)
|
|
149
142
|
- **Note**: Both compressed (`.gz`) and uncompressed (`.gds`) files are supported
|
|
150
143
|
- `verilog/rtl/user_defines.v` (required for digital/analog)
|
|
151
144
|
- `.cf/project.json` (optional; will be created/updated automatically)
|
|
@@ -643,7 +636,7 @@ Preconditions:
|
|
|
643
636
|
- Project has a GitHub repo URL (set via `cf init`, shown in the portal).
|
|
644
637
|
- The ChipFoundry GitHub App is installed on that repo (prompted in the portal).
|
|
645
638
|
- Your local `HEAD` has been pushed to `origin` on some branch (`git push`).
|
|
646
|
-
- Push-critical files at `HEAD` are clean: wrapper GDS, `verilog/rtl/user_defines.v` (
|
|
639
|
+
- Push-critical files at `HEAD` are clean: wrapper GDS, `verilog/rtl/user_defines.v` (digital and analog projects only), and `.cf/project.json` (when tracked).
|
|
647
640
|
|
|
648
641
|
What happens:
|
|
649
642
|
1. `cf push --remote` resolves your local HEAD SHA and checks it is reachable from a remote ref.
|
|
@@ -667,8 +660,8 @@ both port 22 and GitHub.
|
|
|
667
660
|
Preconditions:
|
|
668
661
|
- Project is linked to the platform (`cf init` or `cf link`) and you are logged in.
|
|
669
662
|
- Wrapper GDS exists locally under `gds/` (one of `user_project_wrapper.gds[.gz]`,
|
|
670
|
-
`user_analog_project_wrapper.gds[.gz]`, `openframe_project_wrapper.gds[.gz]`).
|
|
671
|
-
- For
|
|
663
|
+
`user_analog_project_wrapper.gds[.gz]`, `openframe_project_wrapper.gds[.gz]`, `double_wide_openframe_project_wrapper.gds[.gz]`).
|
|
664
|
+
- For digital and analog projects, `verilog/rtl/user_defines.v` is also uploaded when present.
|
|
672
665
|
- Outbound HTTPS to `*.s3.us-east-2.amazonaws.com` is allowed.
|
|
673
666
|
|
|
674
667
|
What happens:
|
|
@@ -889,7 +882,7 @@ The CLI tracks your project submission state through the `submission_state` fiel
|
|
|
889
882
|
|
|
890
883
|
1. **File Collection:**
|
|
891
884
|
- Checks for required GDS and Verilog files
|
|
892
|
-
- Auto-detects project type (digital, analog, openframe) based on GDS file name
|
|
885
|
+
- Auto-detects project type (digital, analog, openframe, double_wide) based on GDS file name
|
|
893
886
|
- **Supports both compressed and uncompressed GDS files**
|
|
894
887
|
|
|
895
888
|
2. **Configuration:**
|
|
@@ -958,7 +951,7 @@ cf config
|
|
|
958
951
|
# Initialize project (in your project directory)
|
|
959
952
|
cf init
|
|
960
953
|
# Project name (detected: my_awesome_project):
|
|
961
|
-
# Project type (digital/analog/openframe) (detected: digital):
|
|
954
|
+
# Project type (digital/analog/openframe/double_wide) (detected: digital):
|
|
962
955
|
|
|
963
956
|
# Upload your project
|
|
964
957
|
cf push
|
|
@@ -1076,4 +1069,3 @@ gds/user_project_wrapper.gds.gz # ← Remove this one
|
|
|
1076
1069
|
|
|
1077
1070
|
- For help, contact info@chipfoundry.io or visit [chipfoundry.io](https://chipfoundry.io)
|
|
1078
1071
|
- For bug reports or feature requests, open an issue on [GitHub](https://github.com/chipfoundry/cf-cli)
|
|
1079
|
-
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
"""ChipFoundry CLI package: Automate project submission to SFTP."""
|
|
2
|
-
__version__ = "2.
|
|
2
|
+
__version__ = "2.7.1"
|
|
@@ -228,6 +228,146 @@ def new_run_tag() -> str:
|
|
|
228
228
|
return datetime.now().strftime("%y_%m_%d_%H_%M")
|
|
229
229
|
|
|
230
230
|
|
|
231
|
+
def double_wide_flake_dir(project_root: Path) -> Optional[Path]:
|
|
232
|
+
"""The double_wide project's own flake.nix dir, pinning LibreLane 3.
|
|
233
|
+
|
|
234
|
+
double_wide ships its own flake (not cf-cli's `chipfoundry/openlane-2`
|
|
235
|
+
flake used by every other project type) because the flat wrapper needs
|
|
236
|
+
LibreLane 3's native PDN-to-pad routing. Returns None if the project
|
|
237
|
+
doesn't have one (e.g. it predates this, or a user deleted it).
|
|
238
|
+
"""
|
|
239
|
+
if (project_root / "flake.nix").is_file():
|
|
240
|
+
return project_root
|
|
241
|
+
return None
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
def double_wide_nix_available(flake_dir: Path, timeout: float = 20) -> bool:
|
|
245
|
+
"""Check that `nix develop` can resolve the project's pinned LibreLane 3 flake."""
|
|
246
|
+
if shutil.which("nix") is None:
|
|
247
|
+
return False
|
|
248
|
+
try:
|
|
249
|
+
result = subprocess.run(
|
|
250
|
+
["nix", "flake", "metadata", str(flake_dir), "--json"],
|
|
251
|
+
capture_output=True,
|
|
252
|
+
timeout=timeout,
|
|
253
|
+
)
|
|
254
|
+
return result.returncode == 0
|
|
255
|
+
except (OSError, subprocess.TimeoutExpired):
|
|
256
|
+
return False
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
def build_double_wide_librelane_command(
|
|
260
|
+
*,
|
|
261
|
+
project_root: Path,
|
|
262
|
+
flake_dir: Path,
|
|
263
|
+
config_file: Path,
|
|
264
|
+
pdk_root: Path,
|
|
265
|
+
pdk: str,
|
|
266
|
+
tag: str,
|
|
267
|
+
from_step: Optional[str] = None,
|
|
268
|
+
open_in_openroad: bool = False,
|
|
269
|
+
open_in_klayout: bool = False,
|
|
270
|
+
) -> Tuple[List[str], Dict[str, str]]:
|
|
271
|
+
"""Build the `nix develop` command that runs LibreLane 3 on a flat double_wide wrapper.
|
|
272
|
+
|
|
273
|
+
No `--manual-pdk`/`--pdk-root`/`--pdk` flags: LibreLane 3's default
|
|
274
|
+
(Ciel-managed) PDK resolution auto-fetches the pinned PDK commit into
|
|
275
|
+
`PDK_ROOT` on its own, the same project-local `dependencies/pdks` root
|
|
276
|
+
every other project type's PDK already lives under (Ciel keys PDK
|
|
277
|
+
versions by commit, so the two coexist without conflict).
|
|
278
|
+
"""
|
|
279
|
+
env = os.environ.copy()
|
|
280
|
+
env.update(
|
|
281
|
+
{
|
|
282
|
+
"PROJECT_ROOT": str(project_root),
|
|
283
|
+
"PDK_ROOT": str(pdk_root),
|
|
284
|
+
"PDK": pdk,
|
|
285
|
+
"LIBRELANE_RUN_TAG": tag,
|
|
286
|
+
}
|
|
287
|
+
)
|
|
288
|
+
|
|
289
|
+
librelane_args = []
|
|
290
|
+
if open_in_openroad:
|
|
291
|
+
librelane_args.extend(["--flow", "OpenInOpenROAD"])
|
|
292
|
+
elif open_in_klayout:
|
|
293
|
+
librelane_args.extend(["--flow", "OpenInKLayout"])
|
|
294
|
+
librelane_args.extend(["--ef-save-views-to", str(project_root), "--run-tag", tag])
|
|
295
|
+
if not from_step and not (open_in_openroad or open_in_klayout):
|
|
296
|
+
librelane_args.append("--overwrite")
|
|
297
|
+
if from_step:
|
|
298
|
+
librelane_args.extend(["--from", from_step])
|
|
299
|
+
librelane_args.append(str(config_file))
|
|
300
|
+
|
|
301
|
+
cmd = ["nix", "develop", str(flake_dir), "--command", "librelane", *librelane_args]
|
|
302
|
+
return cmd, env
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
def get_double_wide_valid_from_steps(
|
|
306
|
+
*, flake_dir: Path, config_file: Path, working_dir: Path
|
|
307
|
+
) -> Tuple[Optional[List[str]], Optional[str]]:
|
|
308
|
+
"""Resolve valid `--from` step names for a double_wide config via LibreLane 3."""
|
|
309
|
+
script = (
|
|
310
|
+
"import json, sys\n"
|
|
311
|
+
"from librelane.config import Config\n"
|
|
312
|
+
"from librelane.flows import Flow, SequentialFlow\n"
|
|
313
|
+
"cfg = sys.argv[1]\n"
|
|
314
|
+
"target = Flow.factory.get('Classic')\n"
|
|
315
|
+
"meta = Config.get_meta(cfg)\n"
|
|
316
|
+
"if meta:\n"
|
|
317
|
+
" if isinstance(meta.flow, str):\n"
|
|
318
|
+
" found = Flow.factory.get(meta.flow)\n"
|
|
319
|
+
" if found is None:\n"
|
|
320
|
+
" raise RuntimeError(f\"Unknown flow '{meta.flow}' in config metadata\")\n"
|
|
321
|
+
" target = found\n"
|
|
322
|
+
" elif isinstance(meta.flow, list):\n"
|
|
323
|
+
" target = SequentialFlow.make(meta.flow)\n"
|
|
324
|
+
" if meta.substituting_steps is not None:\n"
|
|
325
|
+
" if meta.flow is None:\n"
|
|
326
|
+
" raise RuntimeError('substituting_steps is set but flow is not defined')\n"
|
|
327
|
+
" if not issubclass(target, SequentialFlow):\n"
|
|
328
|
+
" raise RuntimeError('substituting_steps requires a sequential flow')\n"
|
|
329
|
+
" target = target.Substitute(meta.substituting_steps)\n"
|
|
330
|
+
"steps = []\n"
|
|
331
|
+
"seen = set()\n"
|
|
332
|
+
"for step in getattr(target, 'Steps', []) or []:\n"
|
|
333
|
+
" step_id = getattr(step, 'id', None)\n"
|
|
334
|
+
" if not step_id or step_id in seen:\n"
|
|
335
|
+
" continue\n"
|
|
336
|
+
" seen.add(step_id)\n"
|
|
337
|
+
" steps.append(step_id)\n"
|
|
338
|
+
"print('__CF_STEPS__' + json.dumps({'steps': steps}))\n"
|
|
339
|
+
)
|
|
340
|
+
try:
|
|
341
|
+
result = subprocess.run(
|
|
342
|
+
[
|
|
343
|
+
"nix",
|
|
344
|
+
"develop",
|
|
345
|
+
str(flake_dir),
|
|
346
|
+
"--command",
|
|
347
|
+
"python3",
|
|
348
|
+
"-c",
|
|
349
|
+
script,
|
|
350
|
+
str(config_file),
|
|
351
|
+
],
|
|
352
|
+
cwd=str(working_dir),
|
|
353
|
+
capture_output=True,
|
|
354
|
+
text=True,
|
|
355
|
+
timeout=120,
|
|
356
|
+
)
|
|
357
|
+
except (OSError, subprocess.TimeoutExpired) as exc:
|
|
358
|
+
return None, str(exc)
|
|
359
|
+
if result.returncode != 0:
|
|
360
|
+
return None, (result.stderr or result.stdout or "unknown error").strip()
|
|
361
|
+
marker = "__CF_STEPS__"
|
|
362
|
+
for line in result.stdout.splitlines():
|
|
363
|
+
if line.startswith(marker):
|
|
364
|
+
try:
|
|
365
|
+
return json.loads(line[len(marker):])["steps"], None
|
|
366
|
+
except Exception as exc:
|
|
367
|
+
return None, str(exc)
|
|
368
|
+
return None, "LibreLane did not report a step list"
|
|
369
|
+
|
|
370
|
+
|
|
231
371
|
def find_synth_stat_json(flow_root: Path, macro: str, run_tag: str) -> Optional[Path]:
|
|
232
372
|
"""Locate Yosys stat.json written by a synth-only or full LibreLane run."""
|
|
233
373
|
run_dir = flow_root / macro / "runs" / run_tag
|