diff --git a/docs/tutorials/FlowTutorial.md b/docs/tutorials/FlowTutorial.md index ecd403461b..4752e8eb72 100644 --- a/docs/tutorials/FlowTutorial.md +++ b/docs/tutorials/FlowTutorial.md @@ -311,7 +311,7 @@ abc.constr klayout.lyt klayout_tech.lef lib | `congestion.rpt` | `VDD.rpt` | `VSS.rpt` | | `5_route_drc.rpt` | `final_clocks.webp` | `final_placement.webp` | | `antenna.log` | `final_clocks.webp` | `final.webp` | -| `synth_stat.txt` | `synth_check.txt` | `final_resizer.webp` | +| `synth_stat.json` | `synth_check.txt` | `final_resizer.webp` | The table below briefly describes the reports directory files. @@ -325,7 +325,7 @@ The table below briefly describes the reports directory files. | `antenna.log` | Antenna check log report. | | `final_placement.webp` | Extracted image after final placement. | | `final.webp` | Extracted image after routing. | -| `synth_stat.txt` | Post synthesis design statistics log saved here. | +| `synth_stat.json` | Post synthesis design statistics in JSON format. | The flow completes with the message below by creating a merged final GDS file. diff --git a/flow/scripts/synth.tcl b/flow/scripts/synth.tcl index 8554f6d61c..fbe763b3c5 100644 --- a/flow/scripts/synth.tcl +++ b/flow/scripts/synth.tcl @@ -226,7 +226,7 @@ if { $::env(SYNTH_INSBUF) } { # Reports tee -o $::env(REPORTS_DIR)/synth_check.txt check -tee -o $::env(REPORTS_DIR)/synth_stat.txt stat {*}$lib_args +tee -o $::env(REPORTS_DIR)/synth_stat.json stat -json {*}$lib_args # check the design is composed exclusively of target cells, and # check for other problems diff --git a/flow/util/genMetrics.py b/flow/util/genMetrics.py index fbf746cdfc..07151a59ee 100755 --- a/flow/util/genMetrics.py +++ b/flow/util/genMetrics.py @@ -231,20 +231,50 @@ def extract_metrics( # Synthesis # ========================================================================= - # The new format (>= 0.57) is: cells - extractTagFromFile( - "synth__design__instance__count__stdcell", - metrics_dict, - "^\\s+(\\d+)\\s+[-0-9.]+\\s+cells$", - rptPath + "/synth_stat.txt", - ) + synth_stat_file = rptPath + "/synth_stat.json" + try: + with open(synth_stat_file) as f: + content = f.read() + # yosys may emit log messages (e.g. "Found gzip magic in file...") + # before the JSON when reading compressed liberty files. Skip them. + synth_stat = json.loads(content[content.index("{") :]) + modules = synth_stat.get("modules", {}) + # Prefer lookup by design name; yosys may prefix module names with a + # backslash in JSON output, so try both forms. Fall back to the last + # module in the output, which yosys emits last for the top-level module. + top_module = ( + modules.get(design) + or modules.get("\\" + design) + or (list(modules.values())[-1] if modules else None) + ) + + num_cells = top_module.get("num_cells") if top_module else None + if num_cells is None: + print( + "[WARN] Tag synth__design__instance__count__stdcell not found in {}.".format( + synth_stat_file + ), + "Will use N/A.", + ) + metrics_dict["synth__design__instance__count__stdcell"] = "N/A" + else: + metrics_dict["synth__design__instance__count__stdcell"] = int(num_cells) - extractTagFromFile( - "synth__design__instance__area__stdcell", - metrics_dict, - "Chip area for (?:top )?module.*: +(\\S+)", - rptPath + "/synth_stat.txt", - ) + area = top_module.get("area") if top_module else None + if area is None: + print( + "[WARN] Tag synth__design__instance__area__stdcell not found in {}.".format( + synth_stat_file + ), + "Will use N/A.", + ) + metrics_dict["synth__design__instance__area__stdcell"] = "N/A" + else: + metrics_dict["synth__design__instance__area__stdcell"] = float(area) + except (IOError, json.JSONDecodeError, ValueError): + print("[ERROR] Failed to open file:", synth_stat_file) + metrics_dict["synth__design__instance__count__stdcell"] = "ERR" + metrics_dict["synth__design__instance__area__stdcell"] = "ERR" # Clocks # =========================================================================