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.
@@ -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
  [![PyPI version](https://img.shields.io/pypi/v/chipfoundry-cli?color=blue)](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` (when not an openframe project), and `.cf/project.json` (when tracked).
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 non-openframe projects, `verilog/rtl/user_defines.v` is also uploaded when present.
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
  [![PyPI version](https://img.shields.io/pypi/v/chipfoundry-cli?color=blue)](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` (when not an openframe project), and `.cf/project.json` (when tracked).
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 non-openframe projects, `verilog/rtl/user_defines.v` is also uploaded when present.
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.6.0"
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