Skip to content

Commit

Permalink
Merge pull request #302 from hdl/ppa
Browse files Browse the repository at this point in the history
Add benchmarking by default to the synthesize_rtl and place_and_route rules
  • Loading branch information
mikesinouye committed Mar 25, 2024
2 parents 6c938d7 + 501a637 commit 29db060
Show file tree
Hide file tree
Showing 5 changed files with 219 additions and 5 deletions.
4 changes: 3 additions & 1 deletion place_and_route/build_defs.bzl
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright 2021 Google LLC
# Copyright 2024 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
Expand All @@ -15,6 +15,7 @@
"""These build rules run automated place and route on a synthesized netlist"""

load("@rules_hdl//pdk:open_road_configuration.bzl", "assert_has_open_road_configuration")
load("//place_and_route:private/benchmark.bzl", "benchmark")
load("//place_and_route:private/clock_tree_synthesis.bzl", "clock_tree_synthesis")
load("//place_and_route:private/detailed_routing.bzl", "detailed_routing")
load("//place_and_route:private/export_def.bzl", "export_def")
Expand Down Expand Up @@ -50,6 +51,7 @@ def _place_and_route_impl(ctx):
output_files.append(output_def)
if step_name == ctx.attr.stop_after_step:
break
open_road_provider = benchmark(ctx, open_road_provider)

output_files.append(open_road_provider.output_db)
output_files.extend(open_road_provider.logs.to_list())
Expand Down
93 changes: 93 additions & 0 deletions place_and_route/private/benchmark.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# Copyright 2024 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Benchmark OpenROAD commands"""

load("//place_and_route:open_road.bzl", "OpenRoadInfo", "merge_open_road_info", "openroad_command", "timing_setup_commands")

def benchmark(ctx, open_road_info):
"""Benchmarks and reports metrics for a placed design.
Returns:
OpenRoadInfo: the openROAD info provider containing required input files and
and commands run.
Args:
ctx: Bazel rule ctx
open_road_info: OpenRoadInfo provider from a previous step.
"""

timing_setup_command_struct = timing_setup_commands(ctx)

inputs = timing_setup_command_struct.inputs

open_road_commands = timing_setup_command_struct.commands + [
"report_power",
"report_wns",
"report_tns",
"report_checks -path_delay min_max -format full_clock_expanded -fields {input_pin slew capacitance} -digits 3",
"report_check_types -max_slew -max_capacitance -max_fanout -violators",
"report_design_area",
"report_cell_usage",
]

command_output = openroad_command(
ctx,
commands = open_road_commands,
input_db = open_road_info.output_db,
inputs = inputs,
step_name = "benchmark",
)

benchmark_file = ctx.actions.declare_file(ctx.label.name + "_report.textproto")
benchmark_path = benchmark_file.path

cmds = [
"echo \"# proto-file: synthesis/performance_power_area.proto\" >> {out};".format(out = benchmark_path),
"echo \"# proto-message: bazel_rules_hdl.ppa.PerformancePowerAreaProto\n\" >> {out};".format(out = benchmark_path),
]
prefix = "metric=$(cat {log} | awk ".format(log = command_output.log_file.path)
suffix = "; echo \"{field} $metric\" >> {out};"
awk_cmds = [
("worst_slack_max:", "'/wns/ {{ print $2 }}')"),
("total_negative_slack_max:", "'/tns/ {{ print $2 }}')"),
("power_total {", "'')"),
(" internal_package_watts:", "'/Total/ {{ intern_power=$2 }} END {{ print intern_power }}')"),
(" switching_package_watts:", "'/Total/ {{ switch_power=$3 }} END {{ print switch_power }}')"),
(" total_package_watts:", "'/Total/ {{ total_power=$5 }} END {{ print total_power }}')"),
("}", "'')"),
("area_micro_meters_squared:", "'/Design area/ {{ print $3 }}')"),
("area_utilization_percentage:", "-F '[ %]' '/Design area/ {{ print $5 }}')"),
("num_combinational_gates:", "'/Complex combinational cells:/ {{ print $4 }}')"),
("num_flops:", "'/Sequential cells:/ {{ print $3 }}')"),
("num_buffers:", "'/Buffer/ {{ buffers=$2; exit }} END {{ print buffers }}')"),
("num_timing_buffers:", "'/Timing Repair/ {{ print $4 }}')"),
]
cmds.extend([prefix + cmd + suffix.format(field = field, out = benchmark_path) for field, cmd in awk_cmds])

ctx.actions.run_shell(
outputs = [benchmark_file],
command = "".join(cmds),
mnemonic = "BenchmarkingDesign",
inputs = [command_output.log_file],
)

current_action_open_road_info = OpenRoadInfo(
commands = open_road_commands,
input_files = depset(inputs),
output_db = command_output.db,
logs = depset([command_output.log_file, benchmark_file]),
)

return merge_open_road_info(open_road_info, current_action_open_road_info)
20 changes: 19 additions & 1 deletion synthesis/BUILD
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright 2021 Google LLC
# Copyright 2024 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
Expand All @@ -14,7 +14,10 @@

# Synthesis tool package.

load("@rules_cc//cc:defs.bzl", "cc_proto_library")
load("@rules_pkg//:pkg.bzl", "pkg_tar")
load("@rules_proto//proto:defs.bzl", "proto_library")
load("@rules_python//python:proto.bzl", "py_proto_library")

package(
default_applicable_licenses = ["//:package_license"],
Expand All @@ -31,3 +34,18 @@ pkg_tar(
include_runfiles = True,
strip_prefix = "./",
)

proto_library(
name = "performance_power_area_proto",
srcs = ["performance_power_area.proto"],
)

py_proto_library(
name = "performance_power_area_py_proto",
deps = [":performance_power_area_proto"],
)

cc_proto_library(
name = "performance_power_area_cc_proto",
deps = [":performance_power_area_proto"],
)
44 changes: 41 additions & 3 deletions synthesis/build_defs.bzl
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright 2021 Google LLC
# Copyright 2024 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -187,10 +187,12 @@ def _synthesize_design_impl(ctx):
toolchain = None,
)

benchmark_file = _benchmark_synth(ctx, log_file)

return [
DefaultInfo(
runfiles = ctx.runfiles(files = [output_file, log_file]),
files = depset([output_file, log_file]),
runfiles = ctx.runfiles(files = [output_file, log_file, benchmark_file]),
files = depset([output_file, log_file, benchmark_file]),
),
SynthesisInfo(
standard_cell_info = ctx.attr.standard_cells[StandardCellInfo],
Expand All @@ -207,6 +209,42 @@ def _synthesize_design_impl(ctx):
),
]

def _benchmark_synth(ctx, synth_log_file):
"""Computes and prints various metrics from the synthesis log.
Args:
ctx: The current rule's context object.
synth_log_file: Output log file from Yosys for parsing.
Returns:
Benchmark file with metrics.
"""
benchmark_file = ctx.actions.declare_file(ctx.label.name + "_report.textproto")
benchmark_path = benchmark_file.path

cat = "zcat" if ("log.gz" in synth_log_file.path) else "cat"

cmds = [
"echo \"# proto-file: synthesis/performance_power_area.proto\" >> {out};".format(out = benchmark_path),
"echo \"# proto-message: bazel_rules_hdl.ppa.PerformancePowerAreaProto\n\" >> {out};".format(out = benchmark_path),
]
prefix = "metric=$({cat} {log} | awk ".format(cat = cat, log = synth_log_file.path)
suffix = "; echo \"{field} $metric\" >> {out};"
awk_cmds = [
("area_micro_meters_squared:", "'/Chip area for/ {{ print $6 }}')"),
("num_total_cells:", "'/Number of cells/ {{ cells = $4 }} END {{print cells}}')"),
("num_flops:", "'/Flop count:/ {{ print $3 }}')"),
("longest_topological_path:", "-F '[=)]' '/Longest topological path/ {{ print $2}}')"),
]
cmds.extend([prefix + cmd + suffix.format(field = field, out = benchmark_path) for field, cmd in awk_cmds])

ctx.actions.run_shell(
outputs = [benchmark_file],
command = "".join(cmds),
mnemonic = "BenchmarkingSynthesis",
inputs = [synth_log_file],
)
return benchmark_file

def _synthesize_binary_impl(ctx):
script = ""
external_info = ctx.attr.synthesize_rtl_rule[ExternalSynthesisInfo]
Expand Down
63 changes: 63 additions & 0 deletions synthesis/performance_power_area.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
syntax = "proto2";

package bazel_rules_hdl.ppa;

message PerformancePowerAreaProto {
// ====== Performance ======
// The worst slack min.
optional float worst_slack_min = 1;
// The worst slack max.
optional float worst_slack_max = 2;
// The total negative slack max.
optional float total_negative_slack_max = 3;
// The clock skew.
optional float clock_skew = 4;
// The number of cells in the longest topological path.
optional int32 longest_topological_path = 5;
// The count of max slew violations.
optional float max_slew_violations = 6;
// The count of max fanout violations.
optional float max_fanout_violations = 7;
// The count of max capacitance violations.
optional float max_capacitance_violations = 8;

// ====== Power ======
optional Power power_total = 10;
optional Power power_sqeuential = 11;
optional Power power_combinational = 12;
optional Power power_macro = 13;
optional Power power_pad = 14;

// ====== Area ======
// The die area in um^2.
optional float area_micro_meters_squared = 20;
// The die area utilization percentage.
optional float area_utilization_percentage = 21;
// The die height in micro meters.
optional float die_height_micro_meters = 22;
// The die width in micro meters.
optional float die_width_micro_meters = 23;
// The total number of standard cells.
optional int32 num_total_cells = 24;
// The number of non-timing buffers.
optional int32 num_buffers = 25;
// The number of timing buffers.
optional int32 num_timing_buffers = 26;
// The number of flops.
optional int32 num_flops = 27;
// The number of combinational gates.
optional int32 num_combinational_gates = 28;
}

message Power {
// The unit scale such as micro (u), nano (n), etc.
optional string magnitude = 1;
// The internal power simulated
optional float internal_package_watts = 2;
// The switching power simulated
optional float switching_package_watts = 3;
// The leakage power simulated
optional float leakage_package_watts = 4;
// The total power simulated
optional float total_package_watts = 5;
}

0 comments on commit 29db060

Please sign in to comment.