chipfoundry-cli 2.7.1__py3-none-any.whl → 2.7.2__py3-none-any.whl

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,2 +1,2 @@
1
1
  """ChipFoundry CLI package: Automate project submission to SFTP."""
2
- __version__ = "2.7.1"
2
+ __version__ = "2.7.2"
@@ -41,6 +41,9 @@ LINT_PASSED = "passed"
41
41
  LINT_FAILED = "failed"
42
42
  LINT_SKIPPED = "skipped"
43
43
 
44
+ AREA_SOURCE_DIE = "die_area"
45
+ AREA_SOURCE_SYNTHESIS = "synthesis"
46
+
44
47
  _ELABORATE_ONLY_STRATEGIES = {
45
48
  STRATEGY_HIER_MACRO_FIRST,
46
49
  STRATEGY_HIER_MACRO_FIRST_LEGACY,
@@ -105,6 +108,30 @@ def die_area_mm2(config: Dict[str, Any]) -> Optional[Decimal]:
105
108
  return (Decimal(str(um2)) / Decimal("1000000")).quantize(Decimal("0.0001"))
106
109
 
107
110
 
111
+ def floorplan_area_is_fixed(config: Dict[str, Any]) -> bool:
112
+ """True when DIE_AREA is the macro footprint LibreLane will use.
113
+
114
+ ``FP_SIZING=relative`` sizes the die from synthesized cells. Absolute
115
+ sizing, and a DIE_AREA with no FP_SIZING, is a fixed footprint.
116
+ """
117
+ sizing = config.get("FP_SIZING")
118
+ if isinstance(sizing, str) and sizing.strip().lower() == "relative":
119
+ return False
120
+ return die_area_mm2(config) is not None
121
+
122
+
123
+ def _apply_die_area(row: Dict[str, Any], config: Dict[str, Any]) -> None:
124
+ """Record DIE_AREA. A fixed footprint is not replaced by synthesis later."""
125
+ area = die_area_mm2(config)
126
+ if area is None:
127
+ return
128
+ if floorplan_area_is_fixed(config):
129
+ row["estimated_area_mm2"] = str(area)
130
+ row["area_source"] = AREA_SOURCE_DIE
131
+ elif row.get("estimated_area_mm2") is None:
132
+ row["estimated_area_mm2"] = str(area)
133
+
134
+
108
135
  def _stem_from_view_path(value: Any) -> Optional[str]:
109
136
  if not isinstance(value, str) or not value.strip():
110
137
  return None
@@ -262,6 +289,7 @@ def scan_project(project_root: Path) -> Dict[str, Any]:
262
289
  "source": "cli_preview",
263
290
  "config_path": row["config_path"],
264
291
  "estimated_area_mm2": None,
292
+ "area_source": None,
265
293
  "gate_count": None,
266
294
  "instances": [],
267
295
  "integration_strategy": None,
@@ -283,7 +311,7 @@ def scan_project(project_root: Path) -> Dict[str, Any]:
283
311
  else:
284
312
  parent = design_name.strip()
285
313
 
286
- area = die_area_mm2(config)
314
+ is_new = parent not in macros_by_name
287
315
  parent_row = macros_by_name.setdefault(
288
316
  parent,
289
317
  {
@@ -292,19 +320,23 @@ def scan_project(project_root: Path) -> Dict[str, Any]:
292
320
  "is_top_level": False,
293
321
  "source": "cli_preview",
294
322
  "config_path": row["config_path"],
295
- "estimated_area_mm2": str(area) if area is not None else None,
323
+ "estimated_area_mm2": None,
324
+ "area_source": None,
296
325
  "gate_count": None,
297
326
  "instances": [],
298
327
  "integration_strategy": None,
299
328
  "lint_status": None,
300
329
  },
301
330
  )
302
- if parent_row["config_path"] != row["config_path"]:
331
+ if is_new or not parent_row.get("config_path") or parent_row["config_path"] == row["config_path"]:
332
+ if not is_new and not parent_row.get("config_path"):
333
+ parent_row["config_path"] = row["config_path"]
334
+ parent_row["notes"] = f"from {row['config_path']}"
335
+ _apply_die_area(parent_row, config)
336
+ else:
303
337
  notes.append(
304
338
  f"Multiple configs declare DESIGN_NAME {parent!r}; using {parent_row['config_path']}"
305
339
  )
306
- elif area is not None and parent_row["estimated_area_mm2"] is None:
307
- parent_row["estimated_area_mm2"] = str(area)
308
340
 
309
341
  macro_section = config.get("MACROS")
310
342
  if isinstance(macro_section, dict):
@@ -330,6 +362,7 @@ def scan_project(project_root: Path) -> Dict[str, Any]:
330
362
  "source": "cli_preview",
331
363
  "config_path": None,
332
364
  "estimated_area_mm2": None,
365
+ "area_source": None,
333
366
  "gate_count": None,
334
367
  "instances": [],
335
368
  "integration_strategy": STRATEGY_HARD_IP,
@@ -362,6 +395,7 @@ def scan_project(project_root: Path) -> Dict[str, Any]:
362
395
  "source": "cli_preview",
363
396
  "config_path": None,
364
397
  "estimated_area_mm2": None,
398
+ "area_source": None,
365
399
  "gate_count": None,
366
400
  "instances": [],
367
401
  "integration_strategy": STRATEGY_HARD_IP,
@@ -471,12 +505,13 @@ def apply_synth_metrics(
471
505
  macro["lint_status"] = LINT_FAILED
472
506
  if strategy_uses_gate_count(strategy):
473
507
  macro["gate_count"] = metrics["gate_count"]
474
- area = metrics.get("estimated_area_mm2")
475
- if area is not None:
476
- macro["estimated_area_mm2"] = str(area)
477
- elif macro.get("estimated_area_mm2") is None and metrics.get("estimated_area_mm2") is not None:
478
- # Keep DIE_AREA / catalog area; do not treat Yosys cells as gates.
479
- pass
508
+ # Cell area is the sum of synthesized gates. A fixed DIE_AREA is the
509
+ # footprint OpenLane will actually build.
510
+ if macro.get("area_source") != AREA_SOURCE_DIE:
511
+ area = metrics.get("estimated_area_mm2")
512
+ if area is not None:
513
+ macro["estimated_area_mm2"] = str(area)
514
+ macro["area_source"] = AREA_SOURCE_SYNTHESIS
480
515
 
481
516
 
482
517
  def leaf_summary(macros: Sequence[Dict[str, Any]]) -> Dict[str, Any]:
@@ -675,6 +710,7 @@ def build_preview_payload(
675
710
  "source": "cli_preview",
676
711
  "config_path": row.get("config_path"),
677
712
  "estimated_area_mm2": row.get("estimated_area_mm2"),
713
+ "area_source": row.get("area_source"),
678
714
  "gate_count": row.get("gate_count"),
679
715
  "instances": row.get("instances") or [],
680
716
  "integration_strategy": row.get("integration_strategy"),
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: chipfoundry-cli
3
- Version: 2.7.1
3
+ Version: 2.7.2
4
4
  Summary: CLI tool to automate ChipFoundry project submission to SFTP server
5
5
  License: Apache-2.0
6
6
  License-File: LICENSE
@@ -1,13 +1,13 @@
1
- chipfoundry_cli/__init__.py,sha256=wtw9to_LGW-jMarD-6_cMZnBM5oOXaR1FpI4Q1iR82s,89
1
+ chipfoundry_cli/__init__.py,sha256=hk8Lld7f5U8fnkyqT4PgWp34qe2KCg8Y2fF-E2DRdys,89
2
2
  chipfoundry_cli/check_refs.py,sha256=YHbtH4GSbX5xuWG02DeO4ErjXWDJDPOqUNiiMmzdZoc,1538
3
3
  chipfoundry_cli/librelane_run.py,sha256=Er8_oIW43rI9izJRBOD08aQwF12veQZd1cWyguIgzcA,14089
4
4
  chipfoundry_cli/main.py,sha256=2l4Q3AVFDzvsqe2rAa6hukwYnS8HMtgQsm88k1q8CE0,263872
5
- chipfoundry_cli/preview.py,sha256=IcTx8bx9ncLap1mCR8lHGfxymWYgYakmoWtO34RmEkY,34701
5
+ chipfoundry_cli/preview.py,sha256=FTJyso_iuh_4On5isz-nFQMcPGYiFyCRYNZdEj5u75g,36104
6
6
  chipfoundry_cli/remote_precheck_git.py,sha256=djJIAUCqJAhg6hIHRTtW7O6UnEGAbNjtW9B1rSQSiSg,13965
7
7
  chipfoundry_cli/utils.py,sha256=yh3doTLSxauFDQKTmibgc5TgpCd0emZ1QJBwkETqoDc,33688
8
8
  chipfoundry_cli/version_check.py,sha256=V_YkRpMvH3rF8roJlwx8PZh4KaD84_qy3gURYPC2W7o,6473
9
- chipfoundry_cli-2.7.1.dist-info/METADATA,sha256=SrxntuICbJg3JjkAaafwTwSLp4q_wC5CaSy0-n1KQCE,37551
10
- chipfoundry_cli-2.7.1.dist-info/WHEEL,sha256=L-WvLdSBvJWHXv5zrQxDvNRnIgoBzLUpcbuiw19KCuQ,88
11
- chipfoundry_cli-2.7.1.dist-info/entry_points.txt,sha256=Kz6Q2a6HSqWHsBUH3CbAOTI5esD9KGul2THYPYUZDwI,86
12
- chipfoundry_cli-2.7.1.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
13
- chipfoundry_cli-2.7.1.dist-info/RECORD,,
9
+ chipfoundry_cli-2.7.2.dist-info/METADATA,sha256=HMTi4DEeCCfLo1bgcbwCcspWEuaO-1r_qKBisl_q0sw,37551
10
+ chipfoundry_cli-2.7.2.dist-info/WHEEL,sha256=L-WvLdSBvJWHXv5zrQxDvNRnIgoBzLUpcbuiw19KCuQ,88
11
+ chipfoundry_cli-2.7.2.dist-info/entry_points.txt,sha256=Kz6Q2a6HSqWHsBUH3CbAOTI5esD9KGul2THYPYUZDwI,86
12
+ chipfoundry_cli-2.7.2.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
13
+ chipfoundry_cli-2.7.2.dist-info/RECORD,,