Skip to content

Commit f7dff2f

Browse files
authored
Merge pull request #4660 from oharboe/auto-memories-gds-merge
AUTO_MEMORIES: the generated macros reach the GDS merge
2 parents a987304 + 9f61db1 commit f7dff2f

3 files changed

Lines changed: 111 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
@@ -286,6 +287,9 @@ $(RESULTS_DIR)/memories.json: $(RESULTS_DIR)/memories_inferred.json $(ADDITIONAL
286287

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

291295
.PHONY: do-yosys

‎flow/test/test_def2stream.py‎

Lines changed: 76 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,81 @@ 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+
# the flow always writes it; a missing list is a broken flow, not
377+
# a design without generated macros
378+
with tempfile.TemporaryDirectory() as results:
379+
with self.assertRaisesRegex(RuntimeError, "blackboxes.txt is missing"):
380+
def2stream.auto_memories_cells(
381+
{"AUTO_MEMORIES": "1", "RESULTS_DIR": results}
382+
)
383+
384+
def test_missing_results_dir_is_an_error(self):
385+
with self.assertRaisesRegex(RuntimeError, "RESULTS_DIR is not set"):
386+
def2stream.auto_memories_cells({"AUTO_MEMORIES": "1"})
387+
388+
def test_empty_list_allows_nothing(self):
389+
with tempfile.TemporaryDirectory() as results:
390+
os.mkdir(os.path.join(results, "memories"))
391+
open(os.path.join(results, "memories", "blackboxes.txt"), "w").close()
392+
self.assertEqual(
393+
def2stream.auto_memories_cells(
394+
{"AUTO_MEMORIES": "1", "RESULTS_DIR": results}
395+
),
396+
frozenset(),
397+
)
398+
323399

324400
class TestOrphanCells(unittest.TestCase):
325401
def test_orphan_cell_is_error(self):

‎flow/util/def2stream.py‎

Lines changed: 31 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,29 @@ 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+
# do-auto-memories always writes the list, empty when nothing was
147+
# converted; without it the merge cannot tell a generated macro from
148+
# a cell whose GDS is missing.
149+
results_dir = environ.get("RESULTS_DIR")
150+
if not results_dir:
151+
raise RuntimeError("AUTO_MEMORIES=1 but RESULTS_DIR is not set")
152+
path = os.path.join(results_dir, "memories", "blackboxes.txt")
153+
if not os.path.isfile(path):
154+
raise RuntimeError(
155+
f"AUTO_MEMORIES=1 but {path} is missing; do-auto-memories writes it"
156+
)
157+
with open(path, encoding="utf-8") as f:
158+
return frozenset(line.split()[0] for line in f if line.strip())
159+
160+
131161
# When run via klayout -r, globals tech_file, layer_map, in_def, etc.
132162
# are set by klayout's -rd mechanism.
133163
if pya is not None:
@@ -144,6 +174,7 @@ def merge_gds(
144174
seal_file=seal_file, # noqa: F821
145175
out_file=out_file, # noqa: F821
146176
allow_empty=os.environ.get("GDS_ALLOW_EMPTY", ""),
177+
allow_empty_cells=auto_memories_cells(os.environ),
147178
)
148179
)
149180
except NameError:

0 commit comments

Comments
 (0)