From e637d4fb75d8e27440b523001ef6a6348df1fd0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=98yvind=20Harboe?= Date: Fri, 9 Oct 2026 10:31:24 +0200 Subject: [PATCH 1/2] FakeRAM asap7: a macro at least one power-strap pitch wide MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A generated macro's power pins are horizontal M4 rails; the platform's macro grid connects them to the core's vertical M5 straps, one per net every 5.4 um (grid_strategy-M1-M2-M5-M6.tcl). The backend sizes a macro from its bit cells alone, with a floor on height and none on width, so a small memory can come out narrower than the strap pitch, land between two straps of a net, and fail pdngen for the whole design. In asap7/tage_update, with the column mux of #4659, the 64x16 SRAM is 4.18 um wide and floorplan stops with PDN-0179, "Unable to repair all channels". The process gets a min_width_um floor beside the existing height floor, and the asap7 backend sets it to one pitch plus one strap plus the rails' inset at both edges, 5.616 um: a strap of each net then crosses the rails wherever the macro is placed. Wider macros are unchanged. The 64x16 SRAM becomes 5.70 um wide and tage_update gets through PDN. Co-Authored-By: Claude Opus 5.5 Signed-off-by: Øyvind Harboe --- tools/FakeRAM2.0/orfs_asap7/generate.py | 9 +++++++++ tools/FakeRAM2.0/test/macro_dim_test.py | 14 ++++++++++++++ tools/FakeRAM2.0/utils/class_process.py | 4 ++++ 3 files changed, 27 insertions(+) diff --git a/tools/FakeRAM2.0/orfs_asap7/generate.py b/tools/FakeRAM2.0/orfs_asap7/generate.py index be30f8f927..7e665955eb 100644 --- a/tools/FakeRAM2.0/orfs_asap7/generate.py +++ b/tools/FakeRAM2.0/orfs_asap7/generate.py @@ -23,6 +23,15 @@ "column_mux_factor": 1, "snap_width_nm": 190, "snap_height_nm": 1400, + # A macro's power pins are horizontal M4 rails, which the platform's + # macro grid connects to the core's vertical M5 straps + # (openRoad/pdn/grid_strategy-M1-M2-M5-M6.tcl: each net every 5.4 um, + # 0.12 um wide). A narrower macro can sit between two straps of a + # net, its grid gets no shapes and pdngen fails the whole design + # (PDN-0233). One pitch plus a strap, plus the rails' 0.048 um inset + # at both edges, puts a strap of each net across the rails wherever + # the macro lands. + "min_width_um": 5.4 + 0.12 + 2 * 0.048, } ASAP7_TIMING_CONFIG = { diff --git a/tools/FakeRAM2.0/test/macro_dim_test.py b/tools/FakeRAM2.0/test/macro_dim_test.py index 849fc04516..be7040e4a9 100755 --- a/tools/FakeRAM2.0/test/macro_dim_test.py +++ b/tools/FakeRAM2.0/test/macro_dim_test.py @@ -71,6 +71,20 @@ def test_macro_dim_add_height(self): self.assertAlmostEqual(height, exp_height, delta=self._delta) self.assertAlmostEqual(width, exp_width, delta=self._delta) + def test_macro_dim_min_width(self): + """A narrow macro is widened to min_width_um; a wide one is not""" + + process_data = TestUtils.get_base_process_data().copy() + process_data["min_width_um"] = 10.0 + process = Process(process_data) + # 39 bits at 2048 deep is 5.054 um wide, below the floor + width, height = process.get_macro_dimensions(39, 2048, 1, 0) + self.assertAlmostEqual(width, 10.0, delta=self._delta) + self.assertAlmostEqual(height, 663.552, delta=self._delta) + # four banks make it 20.216 um wide, above the floor + width, _ = process.get_macro_dimensions(39, 2048, 4, 0) + self.assertAlmostEqual(width, 5.054 * 4, delta=self._delta) + def test_macro_dim_invalid_banks(self): """Tests detection that an invalid bank value was given""" diff --git a/tools/FakeRAM2.0/utils/class_process.py b/tools/FakeRAM2.0/utils/class_process.py index 487a853e6a..b9107216b1 100644 --- a/tools/FakeRAM2.0/utils/class_process.py +++ b/tools/FakeRAM2.0/utils/class_process.py @@ -60,6 +60,9 @@ def __init__(self, json_data): self._calc_y_step() self.bitcell_width_um = json_data.get("bitcell_width_um", None) self.bitcell_height_um = json_data.get("bitcell_height_um", None) + # The narrowest macro the platform's power grid can reach; 0 for + # no floor. See ASAP7_PROCESS_CONFIG in orfs_asap7/generate.py. + self.min_width_um = json_data.get("min_width_um", 0.0) # Set to True when we want to use the process parameters or bitcell # sizes to calculate the macro dimensions. Set to False for spreadsheet @@ -118,6 +121,7 @@ def get_macro_dimensions(self, width_in_bits, depth, num_banks, additional_heigh total_width = all_bitcell_width * 1.2 total_height += additional_height + total_width = max(total_width, self.min_width_um) return (total_width, total_height) From ca63907cf5a1c83fc26dc3bb769a3c42ad96a457 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=98yvind=20Harboe?= Date: Fri, 9 Oct 2026 10:36:36 +0200 Subject: [PATCH 2/2] FakeRAM: refuse a min_width_um that is not a width MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Review: an explicit null would reach max() as None. Rather than read null as no floor, the process config is refused, as the file already refuses a pin pitch that is off the track grid: min_width_um must be a non-negative number. The comment on the asap7 floor names PDN-0179 as well as PDN-0233, the error asap7/tage_update met. Co-Authored-By: Claude Opus 5.5 Signed-off-by: Øyvind Harboe --- tools/FakeRAM2.0/orfs_asap7/generate.py | 2 +- tools/FakeRAM2.0/test/macro_dim_test.py | 9 +++++++++ tools/FakeRAM2.0/utils/class_process.py | 8 ++++++++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/tools/FakeRAM2.0/orfs_asap7/generate.py b/tools/FakeRAM2.0/orfs_asap7/generate.py index 7e665955eb..e1ba6eacb6 100644 --- a/tools/FakeRAM2.0/orfs_asap7/generate.py +++ b/tools/FakeRAM2.0/orfs_asap7/generate.py @@ -28,7 +28,7 @@ # (openRoad/pdn/grid_strategy-M1-M2-M5-M6.tcl: each net every 5.4 um, # 0.12 um wide). A narrower macro can sit between two straps of a # net, its grid gets no shapes and pdngen fails the whole design - # (PDN-0233). One pitch plus a strap, plus the rails' 0.048 um inset + # (PDN-0233, or PDN-0179 when it cannot repair the channels). One pitch plus a strap, plus the rails' 0.048 um inset # at both edges, puts a strap of each net across the rails wherever # the macro lands. "min_width_um": 5.4 + 0.12 + 2 * 0.048, diff --git a/tools/FakeRAM2.0/test/macro_dim_test.py b/tools/FakeRAM2.0/test/macro_dim_test.py index be7040e4a9..11d1b5317d 100755 --- a/tools/FakeRAM2.0/test/macro_dim_test.py +++ b/tools/FakeRAM2.0/test/macro_dim_test.py @@ -85,6 +85,15 @@ def test_macro_dim_min_width(self): width, _ = process.get_macro_dimensions(39, 2048, 4, 0) self.assertAlmostEqual(width, 5.054 * 4, delta=self._delta) + def test_min_width_must_be_a_width(self): + """A min_width_um that is not a non-negative number is refused""" + + for bad in (None, "5.6", -1.0): + process_data = TestUtils.get_base_process_data().copy() + process_data["min_width_um"] = bad + with self.assertRaises(Exception): + Process(process_data) + def test_macro_dim_invalid_banks(self): """Tests detection that an invalid bank value was given""" diff --git a/tools/FakeRAM2.0/utils/class_process.py b/tools/FakeRAM2.0/utils/class_process.py index b9107216b1..b66e5e2f5e 100644 --- a/tools/FakeRAM2.0/utils/class_process.py +++ b/tools/FakeRAM2.0/utils/class_process.py @@ -63,6 +63,14 @@ def __init__(self, json_data): # The narrowest macro the platform's power grid can reach; 0 for # no floor. See ASAP7_PROCESS_CONFIG in orfs_asap7/generate.py. self.min_width_um = json_data.get("min_width_um", 0.0) + if ( + not isinstance(self.min_width_um, (int, float)) + or isinstance(self.min_width_um, bool) + or self.min_width_um < 0 + ): + raise Exception( + "min_width_um %r is not a width in um" % (self.min_width_um,) + ) # Set to True when we want to use the process parameters or bitcell # sizes to calculate the macro dimensions. Set to False for spreadsheet