Skip to content
Draft
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
5 changes: 3 additions & 2 deletions hw/bitstream/vivado/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
load("@rules_pkg//pkg:mappings.bzl", "pkg_filegroup", "pkg_files")
load("@rules_pkg//pkg:tar.bzl", "pkg_tar")
load("//rules:const.bzl", "KEY_AUTHENTICITY")
load("//rules:fusesoc.bzl", "fusesoc_build")
load("//rules:fusesoc.bzl", "fusesoc_hash_and_build")
load("//rules:otp.bzl", "get_otp_images")
load("//rules:bitstreams.bzl", "bitstream_manifest_fragment")

Expand All @@ -26,7 +26,7 @@ _CW340_TESTROM_PATH = "{}/$(location {})".format(_PREFIX, _CW340_TESTROM)
_FPGA_PATH_TMPL = "lowrisc_systems_{}_0.1/synth-vivado/{}"

# CW340 bitstream
fusesoc_build(
fusesoc_hash_and_build(
name = "fpga_cw340",
testonly = True,
srcs = [
Expand All @@ -38,6 +38,7 @@ fusesoc_build(
flags = [
"--BootRomInitFile=" + _CW340_TESTROM_PATH,
],
hash_dir = _FPGA_PATH_TMPL.format("chip_earlgrey_cw340", "src/"),
output_groups = {
"bitstream": [_FPGA_PATH_TMPL.format("chip_earlgrey_cw340", "lowrisc_systems_chip_earlgrey_cw340_0.1.bit")],
"mmi": [_FPGA_PATH_TMPL.format("chip_earlgrey_cw340", "memories.mmi")],
Expand Down
48 changes: 48 additions & 0 deletions rules/files.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -116,3 +116,51 @@ copy_files = rule(
},
executable = True,
)

def _hash_file_map_fname(file):
return file.path

def _hash_files(ctx):
inputs = ctx.files.src
if ctx.attr.output_group:
inputs = getattr(ctx.attr.src[OutputGroupInfo], ctx.attr.output_group).to_list()

hash_file = ctx.actions.declare_file(ctx.label.name)

args = ctx.actions.args()

# This will automatically recursively expand directories which in particular handles
# all the complexity of the various bazel symlinks.
args.add_all(
inputs,
map_each = _hash_file_map_fname,
expand_directories = True,
)

# Hash the content of the files. We sort them by filename to ensure a deterministic order.
# We also hash the file names themselves together with the content.
ctx.actions.run_shell(
inputs = inputs,
outputs = [hash_file],
command = "echo \"$@\" | sort | xargs tail -v -n +1 | sha1sum > \"$HASH_FILE\"",

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was running out of time and create a bash command line which at least works but it would be better to use a zero separator I think, I couldn't figure out how to do that nicely.

arguments = [args],
env = {"HASH_FILE": hash_file.path},
Comment on lines +145 to +147

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've had problems using sort for determinism because of the locale being different on my laptop versus CI.

Would it be sensible to set LC_ALL=en_US.UTF-8 in the env?

)

return [DefaultInfo(files = depset([hash_file]))]

hash_files = rule(
implementation = _hash_files,
doc = """Hash the content of the file and produce a file containing that hash.
If the src is a directory, its content will be hashes recursively.""",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit:

Suggested change
If the src is a directory, its content will be hashes recursively.""",
If the src is a directory, its content will be hashed recursively.""",

attrs = {
"src": attr.label(
mandatory = True,
allow_files = True,
doc = "Target producing file outputs",
),
"output_group": attr.string(
doc = "Output group to use (optional)",
),
},
)
39 changes: 35 additions & 4 deletions rules/fusesoc.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

load("@bazel_skylib//lib:dicts.bzl", "dicts")
load("@nonhermetic//:env.bzl", "BIN_PATHS", "ENV")
load("//rules:files.bzl", "hash_files")

"""Rules for running FuseSoC.

Expand Down Expand Up @@ -73,10 +74,9 @@ def _fusesoc_build_impl(ctx):

args.add("run")
args.add(ctx.attr.target, format = "--target=%s")
args.add_all([
"--setup",
"--build",
])
args.add("--setup")
if not ctx.attr.setup_only:
args.add("--build")
args.add(out_dir, format = "--build-root=%s")

args.add_all(ctx.attr.systems)
Expand Down Expand Up @@ -129,6 +129,7 @@ fusesoc_build = rule(
directory.
""",
),
"setup_only": attr.bool(default = False, doc = "If set, only --setup will be passed to fusesoc"),
"verilator_options": attr.label(),
"make_options": attr.label(),
"_fusesoc": attr.label(
Expand All @@ -138,3 +139,33 @@ fusesoc_build = rule(
),
},
)

def fusesoc_hash_and_build(
name,
hash_dir,
# All other attributes of fusesoc_build
**kwargs):
"""
This rule is similar to fusesoc_build but in addition, it will also create a target named
`{name}_hash` containing the hash of all input files listed by fusesoc. The `hash_dir` argument
is the name of subdirectory of the fusesoc build directory which needs to be hashed.
"""
fusesoc_build(
name = name,
**kwargs
)

kwargs["output_groups"] = {
"files_to_hash": [hash_dir],
}
fusesoc_build(
name = name + "_files_to_hash",
setup_only = True,
**kwargs
)
hash_files(
name = name + "_hash",
src = ":{}_files_to_hash".format(name),
output_group = "files_to_hash",
testonly = kwargs.get("testonly", False),
)
Loading