Skip to content

Commit 0498d4e

Browse files
oharboeclaude
andcommitted
AUTO_MEMORIES: the generated macros reach the GDS merge
The GDS merge reads the final DEF through a KLayout technology built from the tech, standard-cell and ADDITIONAL_LEFS LEFs. The LEFs AUTO_MEMORIES generates were not among them, so the merge stopped at the first generated macro: Macro not found in LEF file: mbtb_entry_sram And a generated macro has no GDS, while a platform's GDS_ALLOW_EMPTY pattern (asap7: fakeram.*) only matches the platform's own memories, so with the LEF found, every generated macro would have been reported as an empty cell. With AUTO_MEMORIES=1, klayout.lyt now takes the generated LEFs, the ones load.tcl reads, and is rebuilt when they change; def2stream.py allows the modules listed in memories/blackboxes.txt to be empty, taking the first column of each line. Found with asap7/tage_update (#4595), which now writes 6_final.gds. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Øyvind Harboe <oyvind.harboe@zylin.com>
1 parent ef42174 commit 0498d4e

3 files changed

Lines changed: 84 additions & 0 deletions

File tree

‎flow/Makefile‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@ do-klayout:
204204
--template $(KLAYOUT_TECH_FILE) \
205205
--output $(OBJECTS_DIR)/klayout.lyt \
206206
--lef-files $(OBJECTS_DIR)/klayout_tech.lef $(SC_LEF) $(ADDITIONAL_LEFS) \
207+
$(if $(filter 1,$(AUTO_MEMORIES)),$(wildcard $(RESULTS_DIR)/memories/*.lef)) \
207208
--map-files $(wildcard $(FLOW_HOME)/platforms/$(PLATFORM)/*map)
208209

209210
$(OBJECTS_DIR)/klayout_wrap.lyt: $(KLAYOUT_TECH_FILE) $(OBJECTS_DIR)/klayout_tech.lef
@@ -285,6 +286,9 @@ $(RESULTS_DIR)/memories.json: $(RESULTS_DIR)/memories_inferred.json $(ADDITIONAL
285286

286287
yosys-dependencies: $(RESULTS_DIR)/memories.json
287288
$(RESULTS_DIR)/1_1_yosys_canonicalize.rtlil: $(RESULTS_DIR)/memories.json
289+
# The generated LEFs are part of the KLayout technology the GDS merge
290+
# reads the final DEF with.
291+
$(OBJECTS_DIR)/klayout.lyt: $(RESULTS_DIR)/memories.json
288292
endif
289293

290294
.PHONY: do-yosys

‎flow/test/test_def2stream.py‎

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from unittest.mock import MagicMock, patch, call
55
import sys
66
import os
7+
import tempfile
78

89
sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "util"))
910

@@ -320,6 +321,64 @@ def test_allow_empty_regex_no_match_still_errors(self):
320321

321322
self.assertEqual(errors, 1)
322323

324+
def test_auto_memories_cell_may_be_empty(self):
325+
"""A macro AUTO_MEMORIES generated has no GDS; it is not an error."""
326+
generated = make_mock_cell(
327+
"tage_entry_sram", cell_index=1, is_empty=True, parent_cells=1
328+
)
329+
other = make_mock_cell("other_cell", cell_index=2, is_empty=True)
330+
331+
top = make_mock_cell("test_design", cell_index=0)
332+
333+
pya_mod, main_layout, _, _ = make_mock_pya(
334+
cells_before_read=[],
335+
cells_after_read=[top],
336+
top_only_cells=[generated, other],
337+
)
338+
main_layout.cell.return_value = top
339+
340+
errors = def2stream.merge_gds(
341+
pya_mod=pya_mod,
342+
tech_file="/tmp/test.lyt",
343+
layer_map="",
344+
in_def="/tmp/test.def",
345+
design_name="test_design",
346+
in_files="",
347+
seal_file="",
348+
out_file="/tmp/out.gds",
349+
allow_empty_cells=frozenset(["tage_entry_sram"]),
350+
)
351+
352+
self.assertEqual(errors, 1)
353+
354+
355+
class TestAutoMemoriesCells(unittest.TestCase):
356+
def test_off_reads_nothing(self):
357+
self.assertEqual(
358+
def2stream.auto_memories_cells({"RESULTS_DIR": "/nonexistent"}),
359+
frozenset(),
360+
)
361+
362+
def test_names_from_blackboxes_txt(self):
363+
with tempfile.TemporaryDirectory() as results:
364+
os.mkdir(os.path.join(results, "memories"))
365+
with open(os.path.join(results, "memories", "blackboxes.txt"), "w") as f:
366+
# a name per line, optionally followed by more columns
367+
f.write("tage_entry_sram\nmbtb_entry_sram 491.400000\n\n")
368+
self.assertEqual(
369+
def2stream.auto_memories_cells(
370+
{"AUTO_MEMORIES": "1", "RESULTS_DIR": results}
371+
),
372+
frozenset(["tage_entry_sram", "mbtb_entry_sram"]),
373+
)
374+
375+
def test_missing_list_is_an_error(self):
376+
with tempfile.TemporaryDirectory() as results:
377+
with self.assertRaises(FileNotFoundError):
378+
def2stream.auto_memories_cells(
379+
{"AUTO_MEMORIES": "1", "RESULTS_DIR": results}
380+
)
381+
323382

324383
class TestOrphanCells(unittest.TestCase):
325384
def test_orphan_cell_is_error(self):

‎flow/util/def2stream.py‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ def merge_gds(
1818
seal_file,
1919
out_file,
2020
allow_empty="",
21+
allow_empty_cells=frozenset(),
2122
):
2223
"""Merge DEF and GDS/OAS files into a single stream file.
2324
@@ -31,6 +32,7 @@ def merge_gds(
3132
seal_file: Path to seal ring GDS/OAS file (empty string if none).
3233
out_file: Path to output GDS/OAS file.
3334
allow_empty: Regex pattern for cells allowed to be empty.
35+
allow_empty_cells: Names of cells allowed to be empty.
3436
3537
Returns:
3638
Number of errors encountered.
@@ -88,6 +90,11 @@ def merge_gds(
8890
i.name
8991
)
9092
)
93+
elif i.name in allow_empty_cells:
94+
print(
95+
"[WARNING] LEF Cell '{0}' ignored. AUTO_MEMORIES generated"
96+
" its views; it has no GDS.".format(i.name)
97+
)
9198
else:
9299
print(
93100
"[ERROR] LEF Cell '{0}' has no matching GDS/OAS cell."
@@ -128,6 +135,19 @@ def merge_gds(
128135
return errors
129136

130137

138+
def auto_memories_cells(environ):
139+
"""The modules AUTO_MEMORIES turned into macros.
140+
141+
Their LEF and liberty views are generated and they have no GDS, so
142+
they are empty in the merged layout by design.
143+
"""
144+
if environ.get("AUTO_MEMORIES") != "1":
145+
return frozenset()
146+
path = os.path.join(environ["RESULTS_DIR"], "memories", "blackboxes.txt")
147+
with open(path) as f:
148+
return frozenset(line.split()[0] for line in f if line.strip())
149+
150+
131151
# When run via klayout -r, globals tech_file, layer_map, in_def, etc.
132152
# are set by klayout's -rd mechanism.
133153
if pya is not None:
@@ -144,6 +164,7 @@ def merge_gds(
144164
seal_file=seal_file, # noqa: F821
145165
out_file=out_file, # noqa: F821
146166
allow_empty=os.environ.get("GDS_ALLOW_EMPTY", ""),
167+
allow_empty_cells=auto_memories_cells(os.environ),
147168
)
148169
)
149170
except NameError:

0 commit comments

Comments
 (0)