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
34 changes: 27 additions & 7 deletions docs/BSV_MIGRATION_EXAMPLES.md
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,10 @@ bsv_bluesim_tests(

### Key Changes

1. **RDL rule**: `rdl()` → `rdl_file()`, `sources` → `src` (singular)
1. **RDL rule**: `rdl()` → `rdl_file()`, `sources` → `src` (singular). Buck2 requires
the target name to end in `_rdl` and the `src` basename to match it, so
`rdl('foo_registers', sources = ['foo.rdl'])` becomes
`rdl_file(name = "foo_rdl", src = "foo.rdl")`.
2. **Load statements**: Added both `bsv.bzl` and `rdl.bzl` imports
3. **Dependency structure**: Remains identical (Buck2 handles transitive deps automatically)
4. **Generated files**: Same syntax for referencing generated BSV files (`:I2CCoreRegsPkg#I2CCoreRegs.bsv`)
Expand Down Expand Up @@ -495,7 +498,7 @@ bsv_library(
)

rdl_file(
name = "ignition_controller_registers",
name = "ignition_controller_rdl",
src = "ignition_controller.rdl",
outputs = [
"IgnitionControllerRegisters.bsv",
Expand All @@ -508,10 +511,10 @@ rdl_file(
bsv_library(
name = "ControllerRegisters",
srcs = [
":ignition_controller_registers#IgnitionControllerRegisters.bsv",
":ignition_controller_rdl[bsv]",
],
deps = [
":ignition_controller_registers",
":ignition_controller_rdl",
"//hdl/ip/bsv:RegCommon",
],
)
Expand Down Expand Up @@ -843,12 +846,29 @@ rdl_file(
# BSV library uses only the .bsv output via sub-target
bsv_library(
name = "MyRegs",
srcs = [":my_regs_rdl[bsv]"], # ← Use [bsv] sub-target, not full target
deps = ["//hdl/ip/bsv:RegCommon"],
srcs = [":my_regs_rdl[bsv]"], # <- Use [bsv] sub-target, not full target
deps = [
":my_regs_rdl", # <- ALSO depend on the full target, see below
"//hdl/ip/bsv:RegCommon",
],
)
```

**Common mistake:** Using the full RDL target causes BSC to try compiling `.html` and `.json` files.
**Common mistake #1:** Using the full RDL target in `srcs` causes BSC to try compiling
`.html` and `.json` files. Only the `[bsv]` sub-target belongs there.

**Common mistake #2:** Omitting the RDL target from `deps`. The RDL target must appear
in **both** places, and they do different jobs:

- `srcs = [":my_regs_rdl[bsv]"]` gives BSC the generated `.bsv` and nothing else.
- `deps = [":my_regs_rdl"]` carries the *providers*. `srcs` entries are plain
artifacts with no providers attached, so this is the only edge that propagates
`RDLDocMaps` — the register-map docs that `collect_rdl_maps` copies into the
`maps/` directory next to a bitstream. Leave it out and the bitstream silently
ships with no register maps.

`bsv_library` fails analysis if you forget, so this is enforced rather than
convention.

### Issue: Custom bsc_flags not working

Expand Down
2 changes: 2 additions & 0 deletions hdl/ip/bsv/ignition/BUCK
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ rdl_file(
outputs = [
"IgnitionControllerRegisters.bsv",
"ignition_controller.html",
"ignition_controller.adoc",
"ignition_controller.json",
],
src = "ignition_controller.rdl",
Expand All @@ -126,6 +127,7 @@ bsv_library(
name = "ControllerRegisters",
srcs = [":ignition_controller_rdl[bsv]"],
deps = [
":ignition_controller_rdl",
"//hdl/ip/bsv:RegCommon",
],
)
Expand Down
3 changes: 2 additions & 1 deletion hdl/projects/cosmo_seq/BUCK
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ rdl_file(
"//hdl/ip/vhd/i2c/io_expanders/PCA9506ish:pca9506_regs_rdl",
],
outputs = [
"cosmo_seq_top.html",
"cosmo_seq_top.adoc",
"cosmo_seq_top.html",
"cosmo_seq_top.json"
]
)
Expand Down
1 change: 1 addition & 0 deletions hdl/projects/gimlet/sequencer/BUCK
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ bsv_library(
name = "GimletSeqFpgaRegs",
srcs = [":gimlet_seq_fpga_regs_rdl[bsv]"],
deps = [
":gimlet_seq_fpga_regs_rdl",
"//hdl/ip/bsv:RegCommon",
],
)
Expand Down
1 change: 1 addition & 0 deletions hdl/projects/minibar/BUCK
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ rdl_file(
outputs = [
"MinibarRegsPkg.bsv",
"minibar_controller.html",
"minibar_controller.adoc",
"minibar_controller.json",
],
)
Expand Down
2 changes: 2 additions & 0 deletions hdl/projects/sidecar/mainboard/BUCK
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ rdl_file(
outputs = [
"SidecarMainboardControllerReg.bsv",
"sidecar_mainboard_controller.html",
"sidecar_mainboard_controller.adoc",
"sidecar_mainboard_controller.json",
],
)
Expand All @@ -24,6 +25,7 @@ bsv_library(
":sidecar_mainboard_controller_rdl[bsv]",
],
deps = [
":sidecar_mainboard_controller_rdl",
"//hdl/ip/bsv:RegCommon",
],
)
Expand Down
1 change: 1 addition & 0 deletions hdl/projects/sidecar/qsfp_x32/BUCK
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ rdl_file(
outputs = [
"QsfpX32ControllerRegsPkg.bsv",
"qsfp_x32_controller.html",
"qsfp_x32_controller.adoc",
"qsfp_x32_controller.json",
],
)
Expand Down
42 changes: 40 additions & 2 deletions tools/bsv.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,39 @@ load(":bsv_common.bzl", "BSVFileInfo", "BSVLibraryInfo", "BSVVerilogInfo", "BSVS
load(":hdl_common.bzl", "RDLBSVPkgs", "propagate_rdl_maps", "collect_rdl_maps")
# Toolchain accessed via RunInfo - no custom provider needed

def _check_rdl_srcs_are_deps(ctx: AnalysisContext):
"""Require an rdl_file referenced from srcs to also be in deps.

RDL register-map docs ride on the RDLDocMaps provider, and srcs entries arrive
as bare Artifacts that carry no providers. Referencing :foo_rdl[bsv] from srcs
without also listing :foo_rdl in deps silently drops the docs, and the bitstream
ends up with no maps/ directory. Catch it here rather than three rules
downstream, where the only symptom is a missing file.
"""
dep_targets = [d.label.raw_target() for d in ctx.attrs.deps]
for src in ctx.attrs.srcs:
# Source files and unbound artifacts have no owner to check.
if src.is_source or src.owner == None:
continue
# rdl.bzl enforces the _rdl suffix on every rdl_file target, so a name
# check is exactly as reliable as that naming convention.
if not src.owner.name.endswith("_rdl"):
continue
if src.owner.raw_target() not in dep_targets:
fail(
("{}: srcs references `{}`, generated by `{}`, but `{}` is not in " +
"deps. Add it to deps (keep the [bsv] sub-target in srcs) so its " +
"RDL register-map docs reach the bitstream's maps/ directory.")
.format(ctx.label, src.basename, src.owner.raw_target(),
src.owner.raw_target()),
)


def _bsv_library_impl(ctx: AnalysisContext) -> list[Provider]:
"""Compile BSV sources to .bo object files"""

_check_rdl_srcs_are_deps(ctx)

# Get toolchain
bsc = ctx.attrs._toolchain[RunInfo]

Expand Down Expand Up @@ -527,7 +557,7 @@ def _bsv_nextpnr_ice40_bitstream_impl(ctx: AnalysisContext) -> list[Provider]:
pnr_log = ctx.actions.declare_output("nextpnr.log")

# Run nextpnr-ice40
pnr_cmd = cmd_args(hidden = maps)
pnr_cmd = cmd_args()
pnr_cmd.add(ctx.attrs._nextpnr_ice40[RunInfo])
pnr_cmd.add("--{}".format(ctx.attrs.family)) # e.g., --up5k
pnr_cmd.add("--package", ctx.attrs.package) # e.g., sg48
Expand Down Expand Up @@ -559,9 +589,13 @@ def _bsv_nextpnr_ice40_bitstream_impl(ctx: AnalysisContext) -> list[Provider]:
return [
DefaultInfo(
default_output = bit_file,
# Register maps are not inputs to anything, so they need to be named
# here or buck2 would never build them.
other_outputs = maps,
sub_targets = {
"asc": [DefaultInfo(default_output = asc_file)],
"json": [DefaultInfo(default_output = yosys_json)],
"maps": [DefaultInfo(default_outputs = maps)],
}
),
]
Expand Down Expand Up @@ -598,7 +632,7 @@ def _bsv_nextpnr_ecp5_bitstream_impl(ctx: AnalysisContext) -> list[Provider]:
pnr_log = ctx.actions.declare_output("nextpnr.log")

# Run nextpnr-ecp5
pnr_cmd = cmd_args(hidden = maps)
pnr_cmd = cmd_args()
pnr_cmd.add(ctx.attrs._nextpnr_ecp5[RunInfo])
pnr_cmd.add("--{}".format(ctx.attrs.family)) # e.g., --25k, --45k, --85k
pnr_cmd.add("--package", ctx.attrs.package) # e.g., CABGA381, CSFBGA285
Expand Down Expand Up @@ -630,9 +664,13 @@ def _bsv_nextpnr_ecp5_bitstream_impl(ctx: AnalysisContext) -> list[Provider]:
return [
DefaultInfo(
default_output = bit_file,
# Register maps are not inputs to anything, so they need to be named
# here or buck2 would never build them.
other_outputs = maps,
sub_targets = {
"config": [DefaultInfo(default_output = config_file)],
"json": [DefaultInfo(default_output = yosys_json)],
"maps": [DefaultInfo(default_outputs = maps)],
}
),
]
Expand Down
2 changes: 1 addition & 1 deletion tools/fpga_releaser/archive_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ def get_relevant_files_from_buck_zip(fpga_name, zip):
zip_names.append(item.filename)
if item.filename.endswith(".bit"):
zip_names.append(item.filename)
if "maps/" in item.filename and (item.filename.endswith(".json") or item.filename.endswith(".html")):
if "maps/" in item.filename and item.filename.endswith((".json", ".html", ".adoc")):
zip_names.append(item.filename)
if item.filename.endswith("nextpnr.log"):
zip_names.append(item.filename)
Expand Down
4 changes: 3 additions & 1 deletion tools/fpga_releaser/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@
parser.add_argument("--skip-gh", default=False, action="store_true", help="Skip doing GH release. Note that doing this still generates release metadata that just will be wrong")
parser.add_argument("--zip", default=None, help="Path to zip file to use instead of downloading from GitHub")

hubris_ignore = [".html", ".log", ".rpt"]
# Register map docs go to the GH release but not into hubris, which has no
# consumer for them.
hubris_ignore = [".html", ".adoc", ".log", ".rpt"]

def main():
"""
Expand Down
49 changes: 23 additions & 26 deletions tools/hdl_common.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -15,45 +15,42 @@ def rdl_project_as_args(value: Artifact):
RDLTSet = transitive_set(args_projections={"args": rdl_project_as_args})

RDLFileInfo = provider(fields={"set": provider_field(RDLTSet)})
RDLHtmlMaps = provider(fields=["files"])
RDLJsonMaps = provider(fields=["files"])
RDLDocMaps = provider(fields=["files"])
RDLBSVPkgs = provider(fields=["files"])

# Extensions copied next to a bitstream in maps/. Kept explicit rather than
# "everything in RDLDocMaps" so adding a new doc format stays a deliberate
# decision about what ships with a build. Pairs with the filter in
# tools/fpga_releaser/archive_parser.py.
_MAPS_DIR_EXTENSIONS = [".json", ".html", ".adoc"]


def propagate_rdl_maps(deps):
"""Collect RDL map providers from deps and return providers to propagate them."""
providers = []
html_maps = []
json_maps = []
"""Collect RDL doc-map providers from deps and return providers to propagate them."""
files = []
for x in deps:
if x.get(RDLHtmlMaps):
html_maps.extend(x[RDLHtmlMaps].files)
if x.get(RDLJsonMaps):
json_maps.extend(x[RDLJsonMaps].files)
if len(html_maps) > 0:
providers.append(RDLHtmlMaps(files=html_maps))
if len(json_maps) > 0:
providers.append(RDLJsonMaps(files=json_maps))
return providers
if x.get(RDLDocMaps):
files.extend(x[RDLDocMaps].files)
if len(files) > 0:
# Dedup at every level so the list can't grow with the number of distinct
# dep paths through a diamond-shaped graph.
return [RDLDocMaps(files=list(set(files)))]
return []


def collect_rdl_maps(ctx, dep):
"""Copy RDL map files from dep into a maps/ output subdirectory.
"""Copy RDL doc-map files from dep into a maps/ output subdirectory.

Returns a list of copy artifacts suitable for use as hidden inputs
to force Buck2 to materialize them.
"""
maps = []
json_maps = dep.get(RDLJsonMaps)
if json_maps != None:
for file in set(json_maps.files):
new_file = ctx.actions.declare_output("maps", file.basename)
maps.append(ctx.actions.copy_file(new_file, file))
html_maps = dep.get(RDLHtmlMaps)
if html_maps != None:
for file in set(html_maps.files):
new_file = ctx.actions.declare_output("maps", file.basename)
maps.append(ctx.actions.copy_file(new_file, file))
doc_maps = dep.get(RDLDocMaps)
if doc_maps == None:
return maps
for file in set([f for f in doc_maps.files if f.extension in _MAPS_DIR_EXTENSIONS]):
new_file = ctx.actions.declare_output("maps", file.basename)
maps.append(ctx.actions.copy_file(new_file, file))
return maps


Expand Down
20 changes: 10 additions & 10 deletions tools/rdl.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,7 @@ load(
"HDLFileInfo",
"HDLFileInfoTSet",
"VHDLFileInfo",
"RDLHtmlMaps",
"RDLJsonMaps",
"RDLDocMaps",
"RDLBSVPkgs",
)

Expand Down Expand Up @@ -60,7 +59,7 @@ def _rdl_file_impl(ctx):
# In general, our convention is <src_base_name>.<file_extension> but we want "_pkg" to be appended
# in the VHDL case. BSV allows flexible naming to support CamelCase package names.
for out in ctx.attrs.outputs:
# Allow .vhd, .bsv, .json, and .html outputs in buck
# Allow .vhd, .bsv, .json, .html, and .adoc outputs in buck
if out.endswith(".vhd"):
expected_name = src_base_name + "_pkg.vhd"
if out != expected_name:
Expand All @@ -77,8 +76,12 @@ def _rdl_file_impl(ctx):
expected_name = src_base_name + ".html"
if out != expected_name:
fail("HTML output {} does not match expected filename {}".format(out, expected_name))
elif out.endswith(".adoc"):
expected_name = src_base_name + ".adoc"
if out != expected_name:
fail("AsciiDoc output {} does not match expected filename {}".format(out, expected_name))
else:
fail("Output {} does not have an expected extension (.vhd, .bsv, .json, .html)".format(out))
fail("Output {} does not have an expected extension (.vhd, .bsv, .json, .html, .adoc)".format(out))
# Get the rdl python executable since we'll be using it for
# for generating our outputs
rdl_gen_py = ctx.attrs._rdl_gen[RunInfo]
Expand Down Expand Up @@ -109,12 +112,9 @@ def _rdl_file_impl(ctx):
all_gen_vhdl = ctx.actions.tset(HDLFileInfoTSet, children=gen_vhdl_tset)
providers.append(HDLFileInfo(set_all=all_gen_vhdl))

html_maps = [x for x in outs if x.extension == ".html"]
if len(html_maps) > 0:
providers.append(RDLHtmlMaps(files=html_maps))
json_maps = [x for x in outs if x.extension == ".json"]
if len(json_maps) > 0:
providers.append(RDLJsonMaps(files=json_maps))
doc_maps = [x for x in outs if x.extension in [".html", ".json", ".adoc"]]
if len(doc_maps) > 0:
providers.append(RDLDocMaps(files=doc_maps))
bsv_pkgs = [x for x in outs if x.extension == ".bsv"]
if len(bsv_pkgs) > 0:
providers.append(RDLBSVPkgs(files=bsv_pkgs))
Expand Down
Loading
Loading