Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/user/AutoMemories.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ into `$(RESULTS_DIR)/memories_inferred.json`, and
| `$(RESULTS_DIR)/memories/<m>.lib` | Generated Liberty view per converted memory. |
| `$(RESULTS_DIR)/memories/<m>_pre_layout.lib` | Ideal-clock (zero clock-tree insertion) variant for pre-CTS consumers that select lib files themselves. The Makefile flow uses `<m>.lib` throughout. |
| `$(RESULTS_DIR)/memories/<m>.lef` | Abstract LEF per converted memory. |
| `$(RESULTS_DIR)/memories/blackboxes.txt` | Names of the converted modules — what synthesis blackboxes. |
| `$(RESULTS_DIR)/memories/blackboxes.txt` | `<module> <area>` per converted module — what synthesis blackboxes, and the area in um² its `.lib` states, which `SYNTH_MINIMUM_KEEP_SIZE` costs it by. |

Synthesis (canonicalization) blackboxes the converted modules so the
liberty view wins over their behavioral bodies; floorplan through final
Expand Down
5 changes: 3 additions & 2 deletions flow/scripts/memories/gen_memories.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@
<out-dir>/<m>.lib Liberty view per converted memory
<out-dir>/<m>_pre_layout.lib ideal-clock variant for pre-CTS consumers
<out-dir>/<m>.lef abstract LEF per converted memory
<out-dir>/blackboxes.txt converted module names, one per line —
what synthesis blackboxes
<out-dir>/blackboxes.txt `<module> <area>` per line, area in um^2 as
the .lib states it — what synthesis
blackboxes and the cost it gives each

Everything downstream consumes these files; nothing else is passed
between the generator and the flow.
Expand Down
19 changes: 17 additions & 2 deletions flow/scripts/memories/gen_memories_test.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#!/usr/bin/env python3
import json
import re
import sys
import tempfile
import unittest
Expand Down Expand Up @@ -118,14 +119,28 @@ def test_flow_contract(self):
"mem_128x32_pre_layout.lib",
],
)
self.assertEqual((mems_dir / "blackboxes.txt").read_text(), "mem_128x32\n")
# one `<module> <area>` line; the area is the one the .lib states,
# which synthesis costs the blackbox by
[line] = (mems_dir / "blackboxes.txt").read_text().splitlines()
name, area = line.split()
self.assertEqual(name, "mem_128x32")
lib_area = re.search(
r"^\s*area\s*:\s*([0-9.]+)\s*;",
(mems_dir / "mem_128x32.lib").read_text(),
re.M,
).group(1)
self.assertEqual(float(area), float(lib_area))
self.assertGreater(float(area), 0)

def test_additional_memories_forces_conversion(self):
force = Path(self.tmp.name) / "force.memories"
force.write_text(FORCE_TAGS)
code, d = self.run_generator(memories_files=[force])
self.assertEqual(code, 0)
blackboxes = (d / "memories" / "blackboxes.txt").read_text().split()
blackboxes = [
line.split()[0]
for line in (d / "memories" / "blackboxes.txt").read_text().splitlines()
]
self.assertEqual(sorted(blackboxes), ["cache_tags", "mem_128x32"])
self.assertTrue((d / "memories" / "cache_tags.lib").exists())
self.assertTrue((d / "memories" / "cache_tags.lef").exists())
Expand Down
57 changes: 53 additions & 4 deletions flow/scripts/synth_preamble.tcl
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,11 @@ proc read_checkpoint { file } {
}

# AUTO_MEMORIES: memory modules whose generated liberty view replaces
# their behavioral body during synthesis. The list is produced by
# scripts/memories/gen_memories.py before canonicalization; see
# their behavioral body during synthesis, each with that view's area in
# um^2. The list is produced by scripts/memories/gen_memories.py before
# canonicalization, one `<module> <area>` per line; see
# docs/user/AutoMemories.md.
proc auto_memories_blackboxes { } {
proc auto_memories_areas { } {
if { ![env_var_equals AUTO_MEMORIES 1] } {
return {}
}
Expand All @@ -59,7 +60,25 @@ proc auto_memories_blackboxes { } {
set fh [open $f r]
set content [string map {\r ""} [read $fh]]
close $fh
return [regexp -all -inline {\S+} $content]
set areas [dict create]
foreach line [split $content "\n"] {
if { [string trim $line] == "" } {
continue
}
set fields [regexp -all -inline {\S+} $line]
if {
[llength $fields] != 2
|| ![string is double -strict [lindex $fields 1]]
} {
error "$f: expected `<module> <area>`, got `$line`"
}
dict set areas {*}$fields
}
return $areas
}

proc auto_memories_blackboxes { } {
return [dict keys [auto_memories_areas]]
}

proc read_design_sources { } {
Expand Down Expand Up @@ -248,4 +267,34 @@ proc convert_liberty_areas { } {
set gate_eq [expr int($area / $found_cell_area)]
rtlil::set_attr -mod -uint $box gate_cost_equivalent $gate_eq
}

# An AUTO_MEMORIES macro is a blackbox from the RTL's own module, not a
# Liberty cell: it costs its generated view's area, or
# keep_hierarchy -min_cost refuses it ("Missing cost information on
# instanced blackbox"). Its view is not read in its place, as its pins
# need not be the module's (FakeRAM uses its own names).
# One name per line, not a Tcl list: yosys lists an escaped module with
# its leading backslash (`\1d_ram`), which list parsing reads as an
# escape, and blackboxes.txt names it as read_slang takes it (`1d_ram`).
set modules {}
foreach line [split [tee -q -s result.string select -list-mod =*] "\n"] {
set line [string trim $line]
if { $line ne "" } {
lappend modules [regsub {^\\} $line {}]
}
}
dict for {m area} [auto_memories_areas] {
if { [lsearch -exact $modules $m] < 0 } {
# slang imports a blackboxed module only where it is instanced
continue
}
if { ![rtlil::has_attr -mod $m blackbox] } {
# e.g. slang's per-instance `<definition>$<instance path>`, which
# --blackboxed-module does not match: the body was elaborated
error "AUTO_MEMORIES macro $m is in the design but was not\
blackboxed; its name does not match the module's definition"
}
rtlil::set_attr -mod -uint $m gate_cost_equivalent \
[expr { int($area / $found_cell_area) }]
}
}
9 changes: 6 additions & 3 deletions tools/FakeRAM2.0/orfs_asap7/generate.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ def run_orfs_asap7(platform: str, out_dir: Path, json_path: Path):
process = Process(ASAP7_PROCESS_CONFIG)
timing_data = TimingData(ASAP7_TIMING_CONFIG)

blackboxes = []
for m in converted:
name = m["name"]
bits = m.get("bits", 32)
Expand Down Expand Up @@ -82,7 +83,9 @@ def run_orfs_asap7(platform: str, out_dir: Path, json_path: Path):
with open(lef_path, "w") as f:
LefExporter(ram).export(f)

(out_dir / "blackboxes.txt").write_text(
"".join(f"{m['name']}\n" for m in converted)
)
# the area the .lib states, beside the name: synthesis costs the
# blackbox by it
blackboxes.append(f"{name} {ram.physical.get_area(False):.6f}\n")

(out_dir / "blackboxes.txt").write_text("".join(blackboxes))
return 0
Loading