Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generate dashboard using test results #1137

Open
wants to merge 16 commits into
base: main
Choose a base branch
from
Open
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: 0 additions & 2 deletions dependency_support/initialize_external.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ load("@python39//:defs.bzl", python_interpreter_target = "interpreter")
load("@rules_7zip//:setup.bzl", "setup_7zip") # needed by rules_hdl
load("@rules_hdl//:init.bzl", rules_hdl_init = "init")
load("@rules_hdl//dependency_support:dependency_support.bzl", rules_hdl_dependency_support = "dependency_support")
load("@rules_hdl//toolchains/cpython:cpython_toolchain.bzl", rules_hdl_register_cpython_repository = "register_cpython_repository")
load("@rules_proto//proto:repositories.bzl", "rules_proto_dependencies", "rules_proto_toolchains")
load("@rules_python//python:pip.bzl", "pip_parse")
load("@rules_python//python:repositories.bzl", "py_repositories")
Expand All @@ -33,7 +32,6 @@ def initialize_external_repositories():
"""Calls set-up methods for external repositories that require that."""
bazel_skylib_workspace()
protobuf_deps()
rules_hdl_register_cpython_repository()
rules_hdl_init(python_interpreter_target = python_interpreter_target)
rules_hdl_dependency_support()
setup_7zip()
Expand Down
6 changes: 6 additions & 0 deletions dependency_support/pip_requirements.in
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ termcolor==1.1.0
psutil==5.7.0
portpicker==1.3.1
pyyaml==6.0.1
cocotb @ https://github.com/cocotb/cocotb/archive/a853db95b0019db6796a6803aa94304bde743e4e.zip
cocotb_bus==0.2.1
jsonschema==4.18.4
mdutils==1.6.0
mkdocs==1.5.2
mkdocs-material==9.2.8

# Note: numpy and scipy version availability seems to differ between Ubuntu
# versions that we want to support (e.g. 18.04 vs 20.04), so we accept a
Expand Down
451 changes: 449 additions & 2 deletions dependency_support/pip_requirements_lock.txt

Large diffs are not rendered by default.

8 changes: 2 additions & 6 deletions dependency_support/rules_hdl/workspace.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -38,18 +38,14 @@ def repo():
)

# Current as of 2023-12-05
git_hash = "37efe7ca7b8469454eacd3b70ef5fe5ddfea2cf4"
git_hash = "f9c7107c189fd7c82d4b21b755dcb93641141709"
archive_sha256 = "d32c5d3a0864e351ca8e6e40d3cf1bb4b78cfc85ea81b5c52bd44b23d274d321"

maybe(
http_archive,
name = "rules_hdl",
sha256 = archive_sha256,
strip_prefix = "bazel_rules_hdl-%s" % git_hash,
urls = [
"https://github.com/hdl/bazel_rules_hdl/archive/%s.tar.gz" % git_hash,
"https://github.com/antmicro/bazel_rules_hdl/archive/%s.tar.gz" % git_hash,
],
repo_mapping = {
"@rules_hdl_cpython": "@python39",
},
)
185 changes: 185 additions & 0 deletions xls/build_rules/dashboard.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
# Copyright 2023 The XLS Authors
#
# 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.

"""Generates HTML dashboard that aggregates test results.

User has to provide the output directory for the generated website using
the `-o` argument, for example:

`bazel run -- //xls/examples:dashboard -o my_dashboard_directory`
"""

def _prepare_test_related_attrs(tests, files):
"""Reads the tests and files attributes passed to the dashboard macro, and converts
it to the attributes accepted by the underlying bazel rule. Tests and parsers
are provided as a separate list of labels. All the relations between them and
additional information about the output files to parse is passed as a format
string that should be resolved in the underlying bazel rule. Note that
the provided format string depends on the order of test and parser labels.
"""

attr_tests = []
attr_parsers = []
attr_cmd_args = ""
for i, test in enumerate(tests):
test_key = "t{}".format(i)
attr_tests += [test["name"]]

output_parsers = test["output_parsers"] if "output_parsers" in test else []
for parser in output_parsers:
parseattr_key = "p{}".format(len(attr_parsers))
attr_parsers += [parser]
attr_cmd_args += " -p {{{}}},{{{}}}".format(test_key, parseattr_key)

file_parsers = test["file_parsers"] if "file_parsers" in test else {}
for parser, file in file_parsers.items():
parser_key = "p{}".format(len(attr_parsers))
attr_parsers += [parser]
attr_cmd_args += " -f {{{}}},{{{}}},{}".format(test_key, parser_key, file)

attr_data = []
for i, data in enumerate(files):
attr_data += [data["name"]]
file_parsers = data["file_parsers"] if "file_parsers" in data else {}
for parser, file in file_parsers.items():
parser_key = "p{}".format(len(attr_parsers))
attr_parsers += [parser]
attr_cmd_args += " -d {},{{{}}}".format(file, parser_key)


return (attr_tests, attr_parsers, attr_data, attr_cmd_args)

def _resolve_test_related_attrs(ctx, attr_tests, attr_parsers, attr_cmd_args):
"""Resolves the format string passed as an argument to the bazel rule.
It relies on the order of test and parser labels.
"""

keys = {}
for i, test in enumerate(attr_tests):
test_key = "t{}".format(i)
value = str(test.label)
keys.update({test_key: value})

for i, parser in enumerate(attr_parsers):
parser_key = "p{}".format(i)
parser_files = [x for x in parser.files.to_list() if x.is_source]
if len(parser_files) > 1:
fail("Parsers passed as attributes should be a single executable")
[parser_file] = parser_files
keys.update({parser_key: parser_file.short_path})

return attr_cmd_args.format(**keys)

def _create_command(ctx):
"""Creates command for the script executed when invoking bazel run for
a dashboard generation"""

return 'python {dashboard} -r {root_dir} -w {working_dir} -t "{title}" {args} "$@"'.format(
dashboard = ctx.executable._dashboard.short_path,
root_dir = "$BUILD_WORKSPACE_DIRECTORY",
working_dir = "$BUILD_WORKING_DIRECTORY",
title = ctx.attr.title,
args = _resolve_test_related_attrs(
ctx,
ctx.attr.tests,
ctx.attr.parsers,
ctx.attr.cmd_args,
),
)

def _collect_transitive_files(ctx):
"""Collects transitive dependencies that are required for the run executable"""

py_toolchain = ctx.toolchains["@bazel_tools//tools/python:toolchain_type"].py3_runtime
return depset(
direct = [py_toolchain.interpreter],
transitive = [
dep[PyInfo].transitive_sources
for dep in ctx.attr.parsers
if PyInfo in dep
] +
[ctx.attr._dashboard[PyInfo].transitive_sources] +
[py_toolchain.files],
)

def _generate_dashboard_impl(ctx):
"""Implementation of the dashboard generation rule"""

executable = ctx.actions.declare_file("{}_run.sh".format(ctx.attr.name))
command = _create_command(ctx)

ctx.actions.write(output = executable, content = command)
py_toolchain = ctx.toolchains["@bazel_tools//tools/python:toolchain_type"].py3_runtime

return [
DefaultInfo(
executable = executable,
files = depset(ctx.files.tests),
runfiles = ctx.runfiles(
files = ctx.files.parsers + ctx.files.data,
transitive_files = _collect_transitive_files(ctx),
).merge_all(
[ctx.attr._dashboard.default_runfiles] +
[dep.default_runfiles for dep in ctx.attr.parsers],
),
),
]

_generate_dashboard_attrs = {
"tests": attr.label_list(
doc = "Tests that will be run to generate the dashboard",
),
"parsers": attr.label_list(
doc = "Parsers to run on test output",
),
"data": attr.label_list(
doc = "Data generated by other rules required for dashboard generation",
),
"cmd_args": attr.string(
doc = "Command for the dashboard generation tool",
),
"title": attr.string(
doc = "Title of the dashboard",
),
"_dashboard": attr.label(
cfg = "exec",
executable = True,
doc = "Script for running tests and parsing their outputs",
default = Label("//xls/tools/dashboard:dashboard"),
),
}

generate_dashboard = rule(
implementation = _generate_dashboard_impl,
attrs = _generate_dashboard_attrs,
executable = True,
toolchains = ["@bazel_tools//tools/python:toolchain_type"],
)

def dashboard(name, title, tests, files):
"""Macro used to provide clear interface for the user. It converts the
information provided by the user to the attributes understood by the
underlying dashboard generation rule.
"""

attr_tests, attr_parsers, attr_data, attr_cmd_args = _prepare_test_related_attrs(tests, files)
generate_dashboard(
name = name,
title = title,
tests = attr_tests,
data = attr_data,
parsers = attr_parsers,
cmd_args = attr_cmd_args,
testonly = True,
)
106 changes: 101 additions & 5 deletions xls/examples/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@

# Build rules for XLS examples.

load("@rules_hdl//verilog:providers.bzl", "verilog_library")
load("@rules_hdl//gds_write:build_defs.bzl", "gds_write")
load("@rules_hdl//place_and_route:build_defs.bzl", "place_and_route")
load("@rules_hdl//synthesis:build_defs.bzl", "benchmark_synth", "synthesize_rtl")
load("@rules_hdl//verilog:providers.bzl", "verilog_library")
load(
"//xls/build_rules:xls_build_defs.bzl",
"cc_xls_ir_jit_wrapper",
Expand All @@ -32,6 +33,7 @@ load(
"xls_ir_opt_ir",
"xls_ir_verilog",
)
load("//xls/build_rules:dashboard.bzl", "dashboard")
load("//xls/examples:list_filegroup_files.bzl", "list_filegroup_files")

package(
Expand Down Expand Up @@ -454,7 +456,16 @@ verilog_library(
)

synthesize_rtl(
name = "find_index_5000ps_model_unit_verilog_synth",
name = "find_index_5000ps_model_unit_verilog_synth_sky130",
top_module = "find_index",
deps = [
":find_index_5000ps_model_unit_verilog",
],
)

synthesize_rtl(
name = "find_index_5000ps_model_unit_verilog_synth_asap7",
standard_cells = "@org_theopenroadproject_asap7sc7p5t_27//:asap7-sc7p5t_rev27_rvt",
top_module = "find_index",
deps = [
":find_index_5000ps_model_unit_verilog",
Expand Down Expand Up @@ -693,15 +704,35 @@ verilog_library(
)

place_and_route(
name = "find_index_place_and_route",
name = "find_index_place_and_route_sky130",
# ~64 MhZ
clock_period = "15.42857143",
core_padding_microns = 30,
core_padding_microns = 10,
placement_density = "0.8",
synthesized_rtl = ":find_index_5000ps_model_unit_verilog_synth",
synthesized_rtl = ":find_index_5000ps_model_unit_verilog_synth_sky130",
target_die_utilization_percentage = "30",
)

gds_write(
name = "find_index_gds_sky130",
implemented_rtl = ":find_index_place_and_route_sky130",
)

place_and_route(
name = "find_index_place_and_route_asap7",
# ~64 MhZ
clock_period = "15.42857143",
core_padding_microns = 2,
placement_density = "0.8",
synthesized_rtl = ":find_index_5000ps_model_unit_verilog_synth_asap7",
target_die_utilization_percentage = "30",
)

gds_write(
name = "find_index_gds_asap7",
implemented_rtl = ":find_index_place_and_route_asap7",
)

xls_dslx_library(
name = "apfloat_fmac_dslx",
srcs = ["apfloat_fmac.x"],
Expand Down Expand Up @@ -751,3 +782,68 @@ filegroup(
srcs = glob(["*.x"]),
visibility = ["//xls:xls_internal"],
)

xls_dslx_library(
name = "passthrough_dslx",
srcs = [
"passthrough.x",
],
)

xls_dslx_verilog(
name = "passthrough_verilog",
codegen_args = {
"module_name": "passthrough",
"delay_model": "unit",
"pipeline_stages": "1",
"reset": "rst",
"use_system_verilog": "false",
"streaming_channel_data_suffix": "_data",
},
dslx_top = "Passthrough",
library = "passthrough_dslx",
verilog_file = "passthrough.v",
)

xls_dslx_test(
name = "passthrough_test",
dslx_test_args = {
"compare": "none",
},
library = ":passthrough_dslx",
)

py_test(
name = "passthrough_cocotb_test",
srcs = ["passthrough_cocotb_test.py"],
data = [
":passthrough_verilog",
"@com_icarus_iverilog//:iverilog",
"@com_icarus_iverilog//:vvp",
],
deps = [
"//xls/common:runfiles",
"//xls/common:test_base",
"//xls/simulation/cocotb:cocotb_xls",
],
)

dashboard(
name = "passthrough_dashboard",
title = "Passthrough Dashboard",
tests = [
{
"name": "passthrough_cocotb_test",
"file_parsers": {
"//xls/tools/dashboard:cocotb_results_xml_parser": "sim_build/results.xml"
},
},
{
"name": "passthrough_test",
"output_parsers": [
"//xls/tools/dashboard:dslx_test_parser",
],
},
],
files = []
)
Loading
Loading