Skip to content

Commit a47d4fd

Browse files
authored
Merge pull request #4810 from oharboe/auto-memories-explicit
AUTO_MEMORIES cleanup: read the files blackboxes.txt names, no globs or existence checks
2 parents bfcc189 + 4254294 commit a47d4fd

10 files changed

Lines changed: 140 additions & 111 deletions

File tree

‎docs/user/AutoMemories.md‎

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,14 @@ into `$(RESULTS_DIR)/memories_inferred.json`, and
2929

3030
Synthesis (canonicalization) blackboxes the converted modules so the
3131
liberty view wins over their behavioral bodies; floorplan through final
32-
read the generated `.lib`/`.lef` alongside `ADDITIONAL_LIBS`/
33-
`ADDITIONAL_LEFS`. Everything downstream keys off the files above —
34-
nothing else passes between the generator and the flow. That file-based
35-
handoff is what lets build systems (e.g. bazel-orfs) declare the
36-
generated artifacts as ordinary stage outputs and transitive
37-
dependencies of the remaining stages.
32+
read the `<m>.lib`/`<m>.lef` of each module `blackboxes.txt` lists,
33+
alongside `ADDITIONAL_LIBS`/`ADDITIONAL_LEFS`. Everything downstream keys
34+
off the files above, by the names the lists give: a file in `memories/`
35+
that no list names is not read, and a list that is missing stops the
36+
step that reads it. Nothing else passes between the generator and the
37+
flow. That file-based handoff is what lets build systems (e.g.
38+
bazel-orfs) declare the generated artifacts as ordinary stage outputs
39+
and transitive dependencies of the remaining stages.
3840

3941
Memories that are *not* converted stay in the netlist and synthesize to
4042
flip-flops, like any other RTL.

‎flow/Makefile‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -204,8 +204,8 @@ 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)) \
208-
--map-files $(wildcard $(FLOW_HOME)/platforms/$(PLATFORM)/*map)
207+
--map-files $(wildcard $(FLOW_HOME)/platforms/$(PLATFORM)/*map) \
208+
$(if $(filter 1,$(AUTO_MEMORIES)),--blackboxes $(RESULTS_DIR)/memories/blackboxes.txt)
209209

210210
$(OBJECTS_DIR)/klayout_wrap.lyt: $(KLAYOUT_TECH_FILE) $(OBJECTS_DIR)/klayout_tech.lef
211211
$(UNSET_AND_MAKE) do-klayout_wrap

‎flow/scripts/load.tcl‎

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,9 @@ proc load_design { design_file } {
2323
read_lef $lef
2424
}
2525
}
26-
# AUTO_MEMORIES: abstract LEFs generated pre-synthesis; globbed
27-
# because the file names are only known at run time.
28-
if { [env_var_equals AUTO_MEMORIES 1] } {
29-
foreach lef [glob -nocomplain $::env(RESULTS_DIR)/memories/*.lef] {
30-
read_lef $lef
31-
}
26+
# AUTO_MEMORIES: abstract LEFs generated pre-synthesis
27+
foreach lef [auto_memories_views .lef] {
28+
read_lef $lef
3229
}
3330
read_verilog $::env(RESULTS_DIR)/$design_file
3431
log_cmd link_design {*}[hier_options] $::env(DESIGN_NAME)

‎flow/scripts/macro_place.tcl‎

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,8 @@ source_step_tcl PRE MACRO_PLACE
77
# after macro placement, and the rows are then cut MACRO_ROWS_HALO_X/Y
88
# around every other macro: a halo that wide keeps them off its core,
99
# which fills its outline to the edge.
10-
set regfile_list "$::env(RESULTS_DIR)/memories/regfiles.txt"
11-
if { [env_var_equals AUTO_MEMORIES 1] && [file exists $regfile_list] } {
12-
set f [open $regfile_list r]
13-
set regfile_masters [split [string trim [read $f]] "\n"]
14-
close $f
10+
set regfile_masters [auto_memories_names regfiles.txt]
11+
if { [llength $regfile_masters] > 0 } {
1512
set dbu [[ord::get_db_tech] getDbUnitsPerMicron]
1613
set halo {}
1714
foreach v {MACRO_ROWS_HALO_X MACRO_ROWS_HALO_Y} {

‎flow/scripts/read_liberty.tcl‎

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,21 +18,15 @@ if { [env_var_exists_and_non_empty CORNERS] } {
1818
}
1919

2020
# AUTO_MEMORIES: liberty views generated pre-synthesis by
21-
# scripts/memories/gen_memories.py. The file names are only known at
22-
# run time, so they are globbed here rather than threaded through
23-
# LIB_FILES. The _pre_layout variants (ideal clock) are for pre-CTS
24-
# consumers that select lib files themselves.
25-
if { [env_var_equals AUTO_MEMORIES 1] } {
26-
foreach libFile [glob -nocomplain $::env(RESULTS_DIR)/memories/*.lib] {
27-
if { [string match *_pre_layout.lib $libFile] } {
28-
continue
29-
}
30-
if { [env_var_exists_and_non_empty CORNERS] } {
31-
foreach corner $::env(CORNERS) {
32-
log_cmd read_liberty -corner $corner $libFile
33-
}
34-
} else {
35-
log_cmd read_liberty $libFile
21+
# scripts/memories/gen_memories.py, named by blackboxes.txt. The
22+
# _pre_layout variants (ideal clock) are for pre-CTS consumers that
23+
# select lib files themselves.
24+
foreach libFile [auto_memories_views .lib] {
25+
if { [env_var_exists_and_non_empty CORNERS] } {
26+
foreach corner $::env(CORNERS) {
27+
log_cmd read_liberty -corner $corner $libFile
3628
}
29+
} else {
30+
log_cmd read_liberty $libFile
3731
}
3832
}

‎flow/scripts/regfile_dissolve.tcl‎

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -72,14 +72,7 @@ proc regfile_internal_net { net placed } {
7272
return 1
7373
}
7474

75-
set regfile_list "$::env(RESULTS_DIR)/memories/regfiles.txt"
76-
set regfile_modules {}
77-
if { [env_var_equals AUTO_MEMORIES 1] && [file exists $regfile_list] } {
78-
set f [open $regfile_list r]
79-
set regfile_modules [split [string trim [read $f]] "\n"]
80-
close $f
81-
set regfile_modules [lsearch -all -inline -not -exact $regfile_modules ""]
82-
}
75+
set regfile_modules [auto_memories_names regfiles.txt]
8376
if { [llength $regfile_modules] > 0 } {
8477
set block [ord::get_db_block]
8578
set db [ord::get_db]

‎flow/scripts/synth_preamble.tcl‎

Lines changed: 5 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -52,75 +52,12 @@ source $::env(SCRIPTS_DIR)/memories/check_blackboxed.tcl
5252
source $::env(SCRIPTS_DIR)/memories/max_bits.tcl
5353
source $::env(SCRIPTS_DIR)/memories/drop_parameters.tcl
5454

55-
proc auto_memories_areas { } {
56-
if { ![env_var_equals AUTO_MEMORIES 1] } {
57-
return {}
58-
}
59-
set f "$::env(RESULTS_DIR)/memories/blackboxes.txt"
60-
if { ![file exists $f] } {
61-
error "AUTO_MEMORIES=1 but $f is missing;\
62-
the do-auto-memories step must run before synthesis"
63-
}
64-
set fh [open $f r]
65-
set content [string map {\r ""} [read $fh]]
66-
close $fh
67-
set areas [dict create]
68-
foreach line [split $content "\n"] {
69-
if { [string trim $line] == "" } {
70-
continue
71-
}
72-
set fields [regexp -all -inline {\S+} $line]
73-
if {
74-
[llength $fields] != 2
75-
|| ![string is double -strict [lindex $fields 1]]
76-
} {
77-
error "$f: expected `<module> <area>`, got `$line`"
78-
}
79-
dict set areas {*}$fields
80-
}
81-
return $areas
82-
}
83-
8455
proc auto_memories_blackboxes { } {
8556
return [dict keys [auto_memories_areas]]
8657
}
8758

88-
# AUTO_MEMORIES: register files inlined (AUTO_MEMORIES_REGFILES not in
89-
# AUTO_MEMORIES_MACRO_PLACE): the RTL module is read as a blackbox and its
90-
# generated netlist of standard cells is read in its place.
91-
proc auto_memories_inline { } {
92-
if { ![env_var_equals AUTO_MEMORIES 1] } {
93-
return {}
94-
}
95-
set f "$::env(RESULTS_DIR)/memories/inline.txt"
96-
if { ![file exists $f] } {
97-
return {}
98-
}
99-
set fh [open $f r]
100-
set content [read $fh]
101-
close $fh
102-
return [regexp -all -inline {\S+} $content]
103-
}
104-
105-
# AUTO_MEMORIES: the RAMs the register file wrappers instantiate as
106-
# macros. They are in blackboxes.txt but are no module of the sources:
107-
# they arrive with the wrappers' netlists, after read_slang.
108-
proc auto_memories_regfile_cores { } {
109-
if { ![env_var_equals AUTO_MEMORIES 1] } {
110-
return {}
111-
}
112-
set f "$::env(RESULTS_DIR)/memories/regfile_cores.txt"
113-
if { ![file exists $f] } {
114-
return {}
115-
}
116-
set fh [open $f r]
117-
set content [read $fh]
118-
close $fh
119-
return [regexp -all -inline {\S+} $content]
120-
}
121-
12259
proc read_auto_memories_inline { } {
123-
foreach m [auto_memories_inline] {
60+
foreach m [auto_memories_names inline.txt] {
12461
# A parameterized RTL module (ibex_register_file_ff #(...)) is
12562
# instantiated with parameters the generated netlist has no
12663
# declaration for, which hierarchy refuses. The netlist is that module
@@ -187,15 +124,15 @@ proc read_design_sources { } {
187124

188125
# Blackbox AUTO_MEMORIES-detected memory modules so their generated
189126
# liberty view wins over their behavioral bodies.
190-
foreach m [concat [auto_memories_blackboxes] [auto_memories_inline]] {
127+
foreach m [concat [auto_memories_blackboxes] [auto_memories_names inline.txt]] {
191128
lappend slang_args --blackboxed-module "$m"
192129
}
193130

194131
# Add user arguments
195132
lappend slang_args {*}$::env(SYNTH_SLANG_ARGS)
196133

197134
yosys read_slang {*}$slang_args
198-
set cores [auto_memories_regfile_cores]
135+
set cores [auto_memories_names regfile_cores.txt]
199136
set sourced {}
200137
foreach m [auto_memories_blackboxes] {
201138
if { [lsearch -exact $cores $m] < 0 } {
@@ -226,7 +163,7 @@ proc read_design_sources { } {
226163
if { [env_var_exists_and_non_empty SYNTH_BLACKBOXES] } {
227164
error "Non-empty SYNTH_BLACKBOXES unsupported with HDL frontend \"verific\""
228165
}
229-
if { [llength [concat [auto_memories_blackboxes] [auto_memories_inline]]] > 0 } {
166+
if { [llength [concat [auto_memories_blackboxes] [auto_memories_names inline.txt]]] > 0 } {
230167
error "AUTO_MEMORIES unsupported with HDL frontend \"$::env(SYNTH_HDL_FRONTEND)\""
231168
}
232169
} elseif { ![env_var_exists_and_non_empty SYNTH_HDL_FRONTEND] } {
@@ -262,7 +199,7 @@ proc read_design_sources { } {
262199
}
263200
hierarchy -check -top $::env(DESIGN_NAME)
264201
}
265-
if { [llength [auto_memories_inline]] > 0 } {
202+
if { [llength [auto_memories_names inline.txt]] > 0 } {
266203
read_auto_memories_inline
267204
hierarchy -check -top $::env(DESIGN_NAME)
268205
}

‎flow/scripts/util.tcl‎

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,62 @@ proc env_var_exists_and_non_empty { env_var } {
112112
return [expr { [info exists ::env($env_var)] && ![string equal $::env($env_var) ""] }]
113113
}
114114

115+
# AUTO_MEMORIES: do-auto-memories writes into $RESULTS_DIR/memories the
116+
# lists below, and every later step reads the files they name, never
117+
# what a directory happens to hold.
118+
#
119+
# blackboxes.txt, `<module> <area>` a line: the macros, each with its
120+
# <module>.lib and <module>.lef, and the area synthesis costs it by.
121+
proc auto_memories_areas { } {
122+
if { ![env_var_equals AUTO_MEMORIES 1] } {
123+
return {}
124+
}
125+
set f "$::env(RESULTS_DIR)/memories/blackboxes.txt"
126+
set fh [open $f r]
127+
set content [string map {\r ""} [read $fh]]
128+
close $fh
129+
set areas [dict create]
130+
foreach line [split $content "\n"] {
131+
if { [string trim $line] == "" } {
132+
continue
133+
}
134+
set fields [regexp -all -inline {\S+} $line]
135+
if {
136+
[llength $fields] != 2
137+
|| ![string is double -strict [lindex $fields 1]]
138+
} {
139+
error "$f: expected `<module> <area>`, got `$line`"
140+
}
141+
dict set areas {*}$fields
142+
}
143+
return $areas
144+
}
145+
146+
# A list of module names, one a line:
147+
# - inline.txt: register files whose generated netlist (<module>.v)
148+
# replaces the RTL module's body in synthesis.
149+
# - regfile_cores.txt: the RAMs the register file wrappers instantiate
150+
# as macros; in blackboxes.txt, but no module of the sources.
151+
# - regfiles.txt: register files that dissolve into their cells after
152+
# macro placement (<module>.v, <module>.place).
153+
proc auto_memories_names { list } {
154+
if { ![env_var_equals AUTO_MEMORIES 1] } {
155+
return {}
156+
}
157+
set f "$::env(RESULTS_DIR)/memories/$list"
158+
set fh [open $f r]
159+
set content [read $fh]
160+
close $fh
161+
return [regexp -all -inline {\S+} $content]
162+
}
163+
164+
# The view of every macro in blackboxes.txt, by extension (.lib, .lef).
165+
proc auto_memories_views { ext } {
166+
return [lmap m [dict keys [auto_memories_areas]] {
167+
file join $::env(RESULTS_DIR) memories $m$ext
168+
}]
169+
}
170+
115171
proc append_env_var { list_name var_name prefix has_arg } {
116172
upvar $list_name list
117173
if {

‎flow/test/test_generate_klayout_tech.py‎

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,5 +226,34 @@ def test_sky130hd(self):
226226
)
227227

228228

229+
class TestBlackboxLefs(unittest.TestCase):
230+
def test_lists_the_lef_of_each_macro(self):
231+
with tempfile.TemporaryDirectory() as d:
232+
blackboxes = os.path.join(d, "blackboxes.txt")
233+
with open(blackboxes, "w") as f:
234+
f.write("ram_a 12.5\n\nram_b 3.0\n")
235+
self.assertEqual(
236+
generate_klayout_tech.blackbox_lefs(blackboxes),
237+
[os.path.join(d, "ram_a.lef"), os.path.join(d, "ram_b.lef")],
238+
)
239+
240+
def test_ignores_a_lef_the_list_does_not_name(self):
241+
with tempfile.TemporaryDirectory() as d:
242+
blackboxes = os.path.join(d, "blackboxes.txt")
243+
with open(blackboxes, "w") as f:
244+
f.write("ram_a 12.5\n")
245+
for name in ("ram_a.lef", "stale.lef"):
246+
open(os.path.join(d, name), "w").close()
247+
self.assertEqual(
248+
generate_klayout_tech.blackbox_lefs(blackboxes),
249+
[os.path.join(d, "ram_a.lef")],
250+
)
251+
252+
def test_missing_list_fails(self):
253+
with tempfile.TemporaryDirectory() as d:
254+
with self.assertRaises(FileNotFoundError):
255+
generate_klayout_tech.blackbox_lefs(os.path.join(d, "blackboxes.txt"))
256+
257+
229258
if __name__ == "__main__":
230259
unittest.main()

‎flow/util/generate_klayout_tech.py‎

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,21 @@ def replace_map_files(content, map_files):
3030
return content
3131

3232

33+
def blackbox_lefs(blackboxes):
34+
"""The LEF of every macro AUTO_MEMORIES' blackboxes.txt lists.
35+
36+
blackboxes.txt has a `<module> <area>` line per macro, and
37+
do-auto-memories writes <module>.lef beside it.
38+
"""
39+
directory = os.path.dirname(blackboxes)
40+
with open(blackboxes, encoding="utf-8") as f:
41+
return [
42+
os.path.join(directory, line.split()[0] + ".lef")
43+
for line in f
44+
if line.strip()
45+
]
46+
47+
3348
def generate_klayout_tech(template_lyt, output_lyt, lef_files, map_files=None):
3449
"""Generate a klayout .lyt file from a platform template.
3550
@@ -76,12 +91,21 @@ def main():
7691
parser.add_argument(
7792
"--map-files", nargs="*", default=[], help="Map files to include"
7893
)
94+
parser.add_argument(
95+
"--blackboxes",
96+
help="AUTO_MEMORIES' blackboxes.txt; the LEF of each macro it lists"
97+
" is included after --lef-files",
98+
)
7999
args = parser.parse_args()
80100

101+
lef_files = list(args.lef_files)
102+
if args.blackboxes:
103+
lef_files += blackbox_lefs(args.blackboxes)
104+
81105
generate_klayout_tech(
82106
template_lyt=args.template,
83107
output_lyt=args.output,
84-
lef_files=args.lef_files,
108+
lef_files=lef_files,
85109
map_files=args.map_files,
86110
)
87111

0 commit comments

Comments
 (0)