| # Copyright lowRISC contributors. | 
 | # Licensed under the Apache License, Version 2.0, see LICENSE for details. | 
 | # SPDX-License-Identifier: Apache-2.0 | 
 |  | 
 | """Rules to build Matcha for the RiscV target""" | 
 |  | 
 | load( | 
 |     "@lowrisc_opentitan//rules:opentitan.bzl", | 
 |     "bin_to_vmem", | 
 |     "opentitan_binary", | 
 |     "scramble_flash_vmem", | 
 |     "sign_bin", | 
 | ) | 
 | load( | 
 |     "@lowrisc_opentitan//rules:rv.bzl", | 
 |     "rv_rule", | 
 |     _OPENTITAN_CPU = "OPENTITAN_CPU", | 
 |     _OPENTITAN_PLATFORM = "OPENTITAN_PLATFORM", | 
 |     _opentitan_transition = "opentitan_transition", | 
 | ) | 
 | load("@rules_cc//cc:find_cc_toolchain.bzl", "find_cc_toolchain") | 
 | load("@rules_pkg//:pkg.bzl", "pkg_tar") | 
 |  | 
 | # Re-exports of names from transition.bzl; many files in the repo use opentitan.bzl | 
 | # to get to them. | 
 | OPENTITAN_CPU = _OPENTITAN_CPU | 
 | OPENTITAN_PLATFORM = _OPENTITAN_PLATFORM | 
 | opentitan_transition = _opentitan_transition | 
 | KELVIN_PLATFORM = "//platforms/riscv32:kelvin" | 
 |  | 
 | # Currently set to secure core's IMC extensions. | 
 | SMC_PLATFORM = OPENTITAN_PLATFORM | 
 |  | 
 | # List of supported riscv core targets. | 
 | VERILATOR_CORE_TARGETS = { | 
 |     "secure_core": "@matcha//sw/device/lib/arch:sim_verilator", | 
 |     "smc": "@matcha//sw/device/lib/arch:smc_sim_verilator", | 
 | } | 
 |  | 
 | DV_CORE_TARGETS = { | 
 |     "secure_core": "@matcha//sw/device/lib/arch:sim_dv", | 
 |     "smc": "@matcha//sw/device/lib/arch:smc_sim_dv", | 
 | } | 
 |  | 
 | NEXUS_CORE_TARGETS = { | 
 |     "secure_core": "@matcha//sw/device/lib/arch:sc_fpga_nexus", | 
 |     "smc": "@matcha//sw/device/lib/arch:smc_fpga_nexus", | 
 | } | 
 |  | 
 | ASIC_CORE_TARGETS = { | 
 |     "secure_core": "@matcha//sw/device/lib/arch:sc_asic", | 
 |     "smc": "@matcha//sw/device/lib/arch:smc_asic", | 
 | } | 
 |  | 
 | MATCHA_COPTS = [ | 
 |     "-Werror", | 
 |     "-Wall", | 
 |     "-Wno-unused-function", | 
 | ] | 
 |  | 
 | # This helper function generates a dictionary of per-device dependencies which is used to | 
 | # generate slightly different binaries for each hardware target, including two | 
 | # simulation platforms (DV and Verilator), and one FPGA platform (Nexus), | 
 | # given a target core from VERILATOR_CORE_TARGETS. | 
 | def device_deps(core): | 
 |     per_device_deps = { | 
 |         "sim_verilator": [VERILATOR_CORE_TARGETS.get(core)], | 
 |         "sim_dv": [DV_CORE_TARGETS.get(core)], | 
 |         "fpga_nexus": [NEXUS_CORE_TARGETS.get(core)], | 
 |         "asic": [ASIC_CORE_TARGETS.get(core)], | 
 |     } | 
 |     return per_device_deps | 
 |  | 
 | def _elf_to_scrambled_rom_impl(ctx): | 
 |     outputs = [] | 
 |     for src in ctx.files.srcs: | 
 |         if src.extension != "elf": | 
 |             fail("only ROM images in the ELF format may be converted to the VMEM format and scrambled.") | 
 |         scrambled = ctx.actions.declare_file( | 
 |             "{}.39.scr.vmem".format( | 
 |                 # Remove ".elf" from file basename. | 
 |                 src.basename.replace("." + src.extension, ""), | 
 |             ), | 
 |         ) | 
 |         outputs.append(scrambled) | 
 |         ctx.actions.run( | 
 |             outputs = [scrambled], | 
 |             inputs = [ | 
 |                 src, | 
 |                 ctx.executable._scramble_tool, | 
 |                 ctx.file.config, | 
 |             ], | 
 |             arguments = [ | 
 |                 ctx.file.config.path, | 
 |                 src.path, | 
 |                 scrambled.path, | 
 |             ], | 
 |             executable = ctx.executable._scramble_tool, | 
 |         ) | 
 |     return [DefaultInfo( | 
 |         files = depset(outputs), | 
 |         data_runfiles = ctx.runfiles(files = outputs), | 
 |     )] | 
 |  | 
 | elf_to_scrambled_rom_vmem = rv_rule( | 
 |     implementation = _elf_to_scrambled_rom_impl, | 
 |     attrs = { | 
 |         "srcs": attr.label_list(allow_files = True), | 
 |         "_scramble_tool": attr.label( | 
 |             default = "@lowrisc_opentitan//hw/ip/rom_ctrl/util:scramble_image", | 
 |             executable = True, | 
 |             cfg = "exec", | 
 |         ), | 
 |         "config": attr.label( | 
 |             default = "@//hw/top_matcha:top_gen_rom_ctrl_hjson", | 
 |             allow_single_file = True, | 
 |         ), | 
 |     }, | 
 | ) | 
 |  | 
 | ArchiveInfo = provider(fields = ["archive_infos"]) | 
 |  | 
 | def _pick_correct_archive_for_device(ctx): | 
 |     cc_infos = [] | 
 |     for dep in ctx.attr.deps: | 
 |         if CcInfo in dep: | 
 |             cc_info = dep[CcInfo] | 
 |         elif ArchiveInfo in dep: | 
 |             cc_info = dep[ArchiveInfo].archive_infos[ctx.attr.device] | 
 |         else: | 
 |             fail("Expected either a CcInfo or an ArchiveInfo") | 
 |         cc_infos.append(cc_info) | 
 |     return [cc_common.merge_cc_infos(cc_infos = cc_infos)] | 
 |  | 
 | pick_correct_archive_for_device = rv_rule( | 
 |     implementation = _pick_correct_archive_for_device, | 
 |     attrs = { | 
 |         "deps": attr.label_list(allow_files = True), | 
 |         "device": attr.string(), | 
 |         "platform": attr.string(), | 
 |     }, | 
 |     fragments = ["cpp"], | 
 |     toolchains = ["@rules_cc//cc:toolchain_type"], | 
 | ) | 
 |  | 
 | def opentitan_rom_binary( | 
 |         name, | 
 |         per_device_deps = device_deps("secure_core"), | 
 |         platform = OPENTITAN_PLATFORM, | 
 |         testonly = False, | 
 |         tags = [], | 
 |         **kwargs): | 
 |     """A helper macro for generating OpenTitan binary artifacts for ROM. | 
 |  | 
 |     This macro is mostly a wrapper around the `opentitan_binary` macro, but also | 
 |     creates artifacts for each of the keys in `PER_DEVICE_DEPS`. The actual | 
 |     artifacts created are outputs of the rules emitted by the `opentitan_binary` | 
 |     macro and those listed below. | 
 |     Args: | 
 |       name: The name of this rule. | 
 |       per_device_deps: List of devices and device deps to build the target for. | 
 |       platform: The target platform for the artifacts. | 
 |       testonly: The target is built for test only. | 
 |       tags: List of tags to apply to each target. | 
 |       **kwargs: Arguments to forward to `opentitan_binary`. | 
 |     Emits rules: | 
 |       For each device in per_device_deps entry: | 
 |         rules emitted by `opentitan_binary` named: see `opentitan_binary` macro | 
 |         bin_to_rom_vmem                     named: <name>_<device>_vmem | 
 |         elf_to_scrambled_rom_vmem           named: <name>_<device>_scr_vmem | 
 |         filegroup                           named: <name>_<device> | 
 |           Containing all targets for a single device for the above generated rules. | 
 |         filegroup                           named: <name> | 
 |           Containing all targets across all devices for the above generated rules. | 
 |     """ | 
 |     deps = kwargs.pop("deps", []) | 
 |     top_target = kwargs.pop("top_target", "matcha") | 
 |  | 
 |     # Place the default copts first to allow per-target override. | 
 |     copts = MATCHA_COPTS + kwargs.pop("copts", []) | 
 |     kwargs.update({"copts": copts}) | 
 |  | 
 |     all_targets = [] | 
 |     for (device, dev_deps) in per_device_deps.items(): | 
 |         if device not in device_deps("secure_core"): | 
 |             fail("invalid device; device must be in {}".format(device_deps("secure_core").keys())) | 
 |         devname = "{}_{}".format(name, device) | 
 |         dev_targets = [] | 
 |  | 
 |         # Generate ELF, Binary, Disassembly, and (maybe) sim_dv logs database | 
 |         dev_targets.extend(opentitan_binary( | 
 |             name = devname, | 
 |             deps = deps + dev_deps, | 
 |             extract_sw_logs_db = device == "sim_dv", | 
 |             testonly = testonly, | 
 |             tags = tags, | 
 |             **kwargs | 
 |         )) | 
 |  | 
 |         # We need to generate VMEM files even for FPGA devices, because we use | 
 |         # them for bitstream splicing. | 
 |         elf_name = "{}.{}".format(devname, "elf") | 
 |         bin_name = "{}_{}".format(devname, "bin") | 
 |  | 
 |         # Generate Un-scrambled ROM VMEM | 
 |         vmem_name = "{}_vmem".format(devname) | 
 |         dev_targets.append(":" + vmem_name) | 
 |         bin_to_vmem( | 
 |             name = vmem_name, | 
 |             bin = bin_name, | 
 |             platform = platform, | 
 |             testonly = testonly, | 
 |             tags = tags, | 
 |             word_size = 32, | 
 |         ) | 
 |  | 
 |         # Generate Scrambled ROM VMEM | 
 |         scr_vmem_name = "{}_scr_vmem".format(devname) | 
 |         dev_targets.append(":" + scr_vmem_name) | 
 |         top_gen_hjson_target = "@//hw/top_{}:top_gen_rom_ctrl_hjson".format(top_target) | 
 |         elf_to_scrambled_rom_vmem( | 
 |             name = scr_vmem_name, | 
 |             srcs = [elf_name], | 
 |             platform = platform, | 
 |             testonly = testonly, | 
 |             tags = tags, | 
 |             config = top_gen_hjson_target, | 
 |         ) | 
 |  | 
 |         # Create a filegroup with just the current device's targets. | 
 |         native.filegroup( | 
 |             name = devname, | 
 |             srcs = dev_targets, | 
 |             testonly = testonly, | 
 |             tags = tags, | 
 |         ) | 
 |         all_targets.extend(dev_targets) | 
 |  | 
 |     # Create a filegroup with just all targets from all devices. | 
 |     native.filegroup( | 
 |         name = name, | 
 |         srcs = all_targets, | 
 |         testonly = testonly, | 
 |         tags = tags, | 
 |     ) | 
 |  | 
 | def flash_binary( | 
 |         name, | 
 |         per_device_deps = device_deps("secure_core"), | 
 |         platform = OPENTITAN_PLATFORM, | 
 |         signing_keys = { | 
 |             "fake_test_key_0": "@lowrisc_opentitan//sw/device/silicon_creator/rom/keys/fake:test_private_key_0", | 
 |         }, | 
 |         signed = False, | 
 |         sim_otp = None, | 
 |         testonly = False, | 
 |         manifest = None, | 
 |         var_name = None, | 
 |         word_size = 32, | 
 |         **kwargs): | 
 |     """A helper macro for generating binary artifacts for a target core. | 
 |  | 
 |     This macro is mostly a wrapper around the `opentitan_binary` macro, but also | 
 |     creates artifacts for each of the keys in `PER_DEVICE_DEPS`, and if signing | 
 |     is enabled, each of the keys in `signing_keys`. The actual artifacts created | 
 |     artifacts created are outputs of the rules emitted by the `opentitan_binary` | 
 |     macro and those listed below. | 
 |     Args: | 
 |       name: The name of this rule. | 
 |       per_device_deps: List of devices and device deps to build the target for. | 
 |       platform: The target platform for the artifacts. | 
 |       signing_keys: The signing keys for to sign each BIN file with. | 
 |       signed: Whether or not to emit signed binary/VMEM files. | 
 |       sim_otp: OTP image that contains flash scrambling keys / enablement flag | 
 |                       (only relevant for VMEM files built for sim targets). | 
 |       testonly: The target is built for test only. | 
 |       manifest: Partially populated manifest to set boot stage/slot configs. | 
 |       var_name: The variable name of the array in the c header file. | 
 |       word_size: word_size for vmem. For SEC (flash) it can be 64, but | 
 |                         SMC can only take 32. | 
 |       **kwargs: Arguments to forward to `opentitan_binary`. | 
 |     Emits rules: | 
 |       For each device in per_device_deps entry: | 
 |         rules emitted by `opentitan_binary` named: see `opentitan_binary` macro | 
 |         bin_to_vmem                         named: <name>_<device>_vmem64 | 
 |         scrambled_flash_vmem                named: <name>_<device>_scr_vmem64 | 
 |         Optionally: | 
 |           sign_bin             named: <name>_<device>_bin_signed_<key_name> | 
 |           bin_to_vmem          named: <name>_<device>_vmem64_signed_<key_name> | 
 |           scrambled_flash_vmem named: <name>_<device>_scr_vmem64_signed_<key_name> | 
 |         filegroup              named: <name>_<device> | 
 |           Containing all targets for a single device for the above generated rules. | 
 |         filegroup              named: <name> | 
 |           Containing all targets across all devices for the above generated rules. | 
 |     """ | 
 |     deps = kwargs.pop("deps", []) | 
 |  | 
 |     # Place the default copts first to allow per-target override. | 
 |     copts = MATCHA_COPTS + kwargs.pop("copts", []) | 
 |     kwargs.update({"copts": copts}) | 
 |     all_targets = [] | 
 |     for (device, dev_deps) in per_device_deps.items(): | 
 |         if device not in device_deps("secure_core"): | 
 |             fail("invalid device; device must be in {}".format(device_deps("secure_core").keys())) | 
 |         devname = "{}_{}".format(name, device) | 
 |         dev_targets = [] | 
 |  | 
 |         depname = "{}_deps".format(devname) | 
 |         _platform = "@matcha//platforms/riscv32:sparrow" if device == "asic" else platform | 
 |         pick_correct_archive_for_device( | 
 |             name = depname, | 
 |             platform = _platform, | 
 |             deps = deps + dev_deps, | 
 |             device = device, | 
 |             testonly = testonly, | 
 |         ) | 
 |  | 
 |         # Generate ELF, Binary, Disassembly, and (maybe) sim_dv logs database | 
 |         dev_targets.extend(opentitan_binary( | 
 |             name = devname, | 
 |             deps = [depname], | 
 |             extract_sw_logs_db = device == "sim_dv", | 
 |             testonly = testonly, | 
 |             **kwargs | 
 |         )) | 
 |         bin_name = "{}_{}".format(devname, "bin") | 
 |  | 
 |         # Sign BIN (if required) and generate scrambled VMEM images. | 
 |         if signed: | 
 |             if manifest == None: | 
 |                 fail("A 'manifest' must be provided in order to sign flash images.") | 
 |             for (key_name, key) in signing_keys.items(): | 
 |                 # Sign the Binary. | 
 |                 signed_bin_name = "{}_bin_signed_{}".format(devname, key_name) | 
 |                 dev_targets.append(":" + signed_bin_name) | 
 |                 sign_bin( | 
 |                     name = signed_bin_name, | 
 |                     bin = bin_name, | 
 |                     key = key, | 
 |                     key_name = key_name, | 
 |                     manifest = manifest, | 
 |                     testonly = testonly, | 
 |                 ) | 
 |  | 
 |                 # We only need to generate VMEM files for sim devices. | 
 |                 if device in ["sim_dv", "sim_verilator"]: | 
 |                     # Generate a VMEM64 from the signed binary. | 
 |                     signed_vmem_name = "{}_vmem_signed_{}".format( | 
 |                         devname, | 
 |                         key_name, | 
 |                     ) | 
 |                     dev_targets.append(":" + signed_vmem_name) | 
 |                     bin_to_vmem( | 
 |                         name = signed_vmem_name, | 
 |                         bin = signed_bin_name, | 
 |                         platform = platform, | 
 |                         testonly = testonly, | 
 |                         word_size = word_size, | 
 |                     ) | 
 |  | 
 |                     # Scramble / compute ECC for signed VMEM64. | 
 |                     scr_signed_vmem_name = "{}_scr_vmem_signed_{}".format( | 
 |                         devname, | 
 |                         key_name, | 
 |                     ) | 
 |                     dev_targets.append(":" + scr_signed_vmem_name) | 
 |                     scramble_flash_vmem( | 
 |                         name = scr_signed_vmem_name, | 
 |                         otp = sim_otp, | 
 |                         vmem = signed_vmem_name, | 
 |                         platform = platform, | 
 |                         testonly = testonly, | 
 |                     ) | 
 |  | 
 |         # We only need to generate VMEM files for sim devices. | 
 |         if device in ["sim_dv", "sim_verilator"]: | 
 |             # Generate a VMEM64 from the binary. | 
 |             vmem_name = "{}_vmem".format(devname) | 
 |             dev_targets.append(":" + vmem_name) | 
 |             bin_to_vmem( | 
 |                 name = vmem_name, | 
 |                 bin = bin_name, | 
 |                 platform = platform, | 
 |                 testonly = testonly, | 
 |                 word_size = word_size, | 
 |             ) | 
 |  | 
 |             # Scramble / compute ECC for VMEM. | 
 |             scr_vmem_name = "{}_scr_vmem".format(devname) | 
 |             dev_targets.append(":" + scr_vmem_name) | 
 |             scramble_flash_vmem( | 
 |                 name = scr_vmem_name, | 
 |                 otp = sim_otp, | 
 |                 vmem = vmem_name, | 
 |                 platform = platform, | 
 |                 testonly = testonly, | 
 |             ) | 
 |         if (device == "fpga_nexus" or device == "asic") and var_name: | 
 |             # Generate c header based on the bin file | 
 |             bin_c_header = "{}_bin_c".format(devname) | 
 |             dev_targets.append(":" + bin_c_header) | 
 |             bin_to_c_file( | 
 |                 name = bin_c_header, | 
 |                 srcs = [bin_name], | 
 |                 var_name = var_name, | 
 |             ) | 
 |  | 
 |         # Create a filegroup with just the current device's targets. | 
 |         native.filegroup( | 
 |             name = devname, | 
 |             srcs = dev_targets, | 
 |             testonly = testonly, | 
 |         ) | 
 |         all_targets.extend(dev_targets) | 
 |  | 
 |     # Create a filegroup with just all targets from all devices. | 
 |     native.filegroup( | 
 |         name = name, | 
 |         srcs = all_targets, | 
 |         testonly = testonly, | 
 |     ) | 
 |  | 
 | def sec_flash_binary( | 
 |         name, | 
 |         per_device_deps = device_deps("secure_core"), | 
 |         signing_keys = { | 
 |             "fake_test_key_0": "@lowrisc_opentitan//sw/device/silicon_creator/rom/keys/fake:test_private_key_0", | 
 |         }, | 
 |         signed = False, | 
 |         **kwargs): | 
 |     """A helper macro for generating secure core binary artifacts, using the flash_binary wrapper. | 
 |  | 
 |     Args: | 
 |       name: The name of this rule. | 
 |       signing_keys: The signing keys for to sign each BIN file with. | 
 |       signed: Whether or not to emit signed binary/VMEM files. | 
 |       per_device_deps: The deps for each of the hardware target. | 
 |       **kwargs: Arguments to forward to `flash_binary`. | 
 |     """ | 
 |  | 
 |     flash_binary( | 
 |         name = name, | 
 |         platform = OPENTITAN_PLATFORM, | 
 |         per_device_deps = per_device_deps, | 
 |         signing_keys = signing_keys, | 
 |         signed = signed, | 
 |         word_size = 64,  # Backdoor-load VMEM image uses 64-bit words | 
 |         **kwargs | 
 |     ) | 
 |  | 
 | def smc_flash_binary( | 
 |         name, | 
 |         per_device_deps = device_deps("smc"), | 
 |         signing_keys = { | 
 |             "fake_test_key_0": "@lowrisc_opentitan//sw/device/silicon_creator/rom/keys/fake:test_private_key_0", | 
 |         }, | 
 |         signed = False, | 
 |         var_name = "smc_bin", | 
 |         **kwargs): | 
 |     """A helper macro for generating SMC binary artifacts, using the flash_binary_wrapper. | 
 |  | 
 |     Args: | 
 |       name: The name of this rule. | 
 |       signing_keys: The signing keys for to sign each BIN file with. | 
 |       signed: Whether or not to emit signed binary/VMEM files. | 
 |       per_device_deps: The deps for each of the hardware target. | 
 |       var_name: The variable name of the array in the c header file. | 
 |       **kwargs: Arguments to forward to `flash_binary`. | 
 |  | 
 |     Emit rules: | 
 |       {name}_bin_c_header: A header file generated by bin_to_c_file for FPGA | 
 |     """ | 
 |  | 
 |     flash_binary( | 
 |         name = name, | 
 |         platform = SMC_PLATFORM, | 
 |         per_device_deps = per_device_deps, | 
 |         signing_keys = signing_keys, | 
 |         signed = signed, | 
 |         var_name = var_name, | 
 |         word_size = 32, | 
 |         **kwargs | 
 |     ) | 
 |  | 
 | def matcha_extflash_tar( | 
 |         name, | 
 |         sc_binary, | 
 |         smc_binary = None, | 
 |         kelvin_binary = None, | 
 |         data = None, | 
 |         **kwargs): | 
 |     """A rule for creating a tarball containing program code and resources. | 
 |  | 
 |     Args: | 
 |         name: The name of the output tarball. The .tar extension is automatically added. | 
 |         sc_binary: The binary for the secure core. This will be named matcha-tock-bundle.bin. | 
 |         smc_binary: The binary for the system management core. This will be named smc.bin, and is optional. | 
 |         kelvin_binary: The binary for the Kelvin core. This will be named kelvin.bin, and is optional. | 
 |         data: A dictionary of extra files to add to the archive. The key is the input file, and the value is filename in the archive. | 
 |         **kwargs: Arguments forwared to pkg_tar | 
 |     """ | 
 |  | 
 |     files = { | 
 |         sc_binary: "matcha-tock-bundle.bin", | 
 |     } | 
 |     if smc_binary: | 
 |         files[smc_binary] = "smc.bin" | 
 |     if kelvin_binary: | 
 |         files[kelvin_binary] = "kelvin.bin" | 
 |     if data: | 
 |         files.update(data) | 
 |     pkg_tar( | 
 |         name = name, | 
 |         files = files, | 
 |         **kwargs | 
 |     ) | 
 |  | 
 | def bin_to_c_file( | 
 |         name, | 
 |         srcs, | 
 |         var_name = "data", | 
 |         testonly = False, | 
 |         **kwargs): | 
 |     """A rule to merge binary files into a C include file. | 
 |  | 
 |     Args: | 
 |         name: The name of the target. | 
 |         srcs: The input binary source files. | 
 |         var_name: The variable name of the array in output C include file. | 
 |         testonly: The target is built for test only. | 
 |         **kwargs: Extra arguments forwards to genrule. | 
 |     """ | 
 |  | 
 |     xxd_cmd = """ | 
 |         xxd -i $< > $@; | 
 |         sed -i -e 's#\\w*_len#{}_len#g' $@; | 
 |         sed -i -e "s#\\w*\\[\\]#{}\\[\\]#g" $@; | 
 |         sed -i -e 's/unsigned/const unsigned/g' $@ | 
 |         """.format(var_name, var_name) | 
 |  | 
 |     outs = ["{}.{}".format(name, "h")] | 
 |  | 
 |     native.genrule( | 
 |         name = name, | 
 |         srcs = srcs, | 
 |         outs = outs, | 
 |         cmd = xxd_cmd, | 
 |         testonly = testonly, | 
 |         **kwargs | 
 |     ) | 
 |  | 
 | def _kelvin_transition_impl(settings, attr): | 
 |     return {"//command_line_option:platforms": KELVIN_PLATFORM} | 
 |  | 
 | kelvin_transition = transition( | 
 |     implementation = _kelvin_transition_impl, | 
 |     inputs = [], | 
 |     outputs = ["//command_line_option:platforms"], | 
 | ) | 
 |  | 
 | def kelvin_rule(**kwargs): | 
 |     """ | 
 |     A wrapper over rule() for creating rules that trigger | 
 |     the transition to the kelvin platform config. | 
 |     """ | 
 |     attrs = kwargs.pop("attrs", {}) | 
 |     if "platform" not in attrs: | 
 |         attrs["platform"] = attr.string(default = KELVIN_PLATFORM) | 
 |     attrs["_allowlist_function_transition"] = attr.label( | 
 |         default = "@bazel_tools//tools/allowlists/function_transition_allowlist", | 
 |     ) | 
 |  | 
 |     return rule( | 
 |         cfg = kelvin_transition, | 
 |         attrs = attrs, | 
 |         **kwargs | 
 |     ) | 
 |  | 
 | def _kelvin_binary_impl(ctx): | 
 |     """Implements compilation for kelvin executables. | 
 |  | 
 |     This rule compiles and links provided input into an executable | 
 |     suitable for use on the Kelvin core. Generates both an ELF | 
 |     and a BIN. | 
 |  | 
 |     Args: | 
 |         srcs: Input source files. | 
 |         deps: Target libraries that the binary depends upon. | 
 |         hdrs: Header files that are local to the binary. | 
 |         copts: Flags to pass along to the compiler. | 
 |         defines: Preprocessor definitions. | 
 |         linkopts: Flags to pass along to the linker. | 
 |  | 
 |     Output: | 
 |         OutputGroupsInfo to allow definition of filegroups | 
 |         containing the output ELF and BIN. | 
 |     """ | 
 |     cc_toolchain = find_cc_toolchain(ctx).cc | 
 |     feature_configuration = cc_common.configure_features( | 
 |         ctx = ctx, | 
 |         cc_toolchain = cc_toolchain, | 
 |         requested_features = ctx.features, | 
 |         unsupported_features = ctx.disabled_features, | 
 |     ) | 
 |     compilation_contexts = [] | 
 |     linking_contexts = [] | 
 |     for dep in ctx.attr.deps: | 
 |         if CcInfo in dep: | 
 |             compilation_contexts.append(dep[CcInfo].compilation_context) | 
 |             linking_contexts.append(dep[CcInfo].linking_context) | 
 |     (_compilation_context, compilation_outputs) = cc_common.compile( | 
 |         actions = ctx.actions, | 
 |         cc_toolchain = cc_toolchain, | 
 |         feature_configuration = feature_configuration, | 
 |         name = ctx.label.name, | 
 |         srcs = ctx.files.srcs, | 
 |         compilation_contexts = compilation_contexts, | 
 |         private_hdrs = ctx.files.hdrs, | 
 |         user_compile_flags = ctx.attr.copts, | 
 |         defines = ctx.attr.defines, | 
 |     ) | 
 |     linking_outputs = cc_common.link( | 
 |         name = "{}.elf".format(ctx.label.name), | 
 |         actions = ctx.actions, | 
 |         feature_configuration = feature_configuration, | 
 |         cc_toolchain = cc_toolchain, | 
 |         compilation_outputs = compilation_outputs, | 
 |         linking_contexts = linking_contexts, | 
 |         user_link_flags = ctx.attr.linkopts + [ | 
 |             "-Wl,-T,{}".format(ctx.file.linker_script.path), | 
 |             # binutils >= 2.39 checks the loadable segments RWX setting by default. | 
 |             "-Wl,--no-warn-rwx-segments", | 
 |         ], | 
 |         additional_inputs = depset([ctx.file.linker_script] + ctx.files.linker_script_includes), | 
 |         output_type = "executable", | 
 |     ) | 
 |  | 
 |     binary = ctx.actions.declare_file( | 
 |         "{}.bin".format( | 
 |             ctx.attr.name, | 
 |         ), | 
 |     ) | 
 |     ctx.actions.run( | 
 |         outputs = [binary], | 
 |         inputs = [linking_outputs.executable] + cc_toolchain.all_files.to_list(), | 
 |         arguments = [ | 
 |             "-g", | 
 |             "-O", | 
 |             "binary", | 
 |             linking_outputs.executable.path, | 
 |             binary.path, | 
 |         ], | 
 |         executable = cc_toolchain.objcopy_executable, | 
 |     ) | 
 |  | 
 |     return [ | 
 |         DefaultInfo( | 
 |             files = depset([linking_outputs.executable, binary]), | 
 |         ), | 
 |         OutputGroupInfo( | 
 |             all_files = depset([linking_outputs.executable, binary]), | 
 |             elf_file = depset([linking_outputs.executable]), | 
 |             bin_file = depset([binary]), | 
 |         ), | 
 |     ] | 
 |  | 
 | kelvin_binary_impl = kelvin_rule( | 
 |     implementation = _kelvin_binary_impl, | 
 |     attrs = { | 
 |         "srcs": attr.label_list(allow_files = True), | 
 |         "deps": attr.label_list(allow_empty = True, providers = [CcInfo]), | 
 |         "hdrs": attr.label_list(allow_files = [".h"], allow_empty = True), | 
 |         "copts": attr.string_list(), | 
 |         "defines": attr.string_list(), | 
 |         "linkopts": attr.string_list(), | 
 |         "linker_script": attr.label(allow_single_file = True), | 
 |         "linker_script_includes": attr.label_list(default = [], allow_files = True), | 
 |         "_cc_toolchain": attr.label(default = Label("@bazel_tools//tools/cpp:current_cc_toolchain")), | 
 |     }, | 
 |     fragments = ["cpp"], | 
 |     toolchains = ["@rules_cc//cc:toolchain_type"], | 
 | ) | 
 |  | 
 | def kelvin_binary(name, srcs, **kwargs): | 
 |     """A helper macro for generating binary artifacts for the kelvin core. | 
 |  | 
 |     This macro uses the kelvin toolchain, kelvin-specific starting asm, | 
 |     and kelvin linker script to build kelvin binaries. | 
 |  | 
 |     Args: | 
 |       name: The name of this rule. | 
 |       srcs: The c source files. | 
 |       **kwargs: Additional arguments forward to cc_binary. | 
 |     Emits rules: | 
 |       bin_to_vmem            named: <name>_vmem | 
 |         Generating the 256-bitwidth vmem output for the target. | 
 |       256_bitwidth_vmem      named: <name>.256.vmem | 
 |         Containing the 256-bitwidth vmem output. | 
 |       filegroup              named: <name>.bin | 
 |         Containing the binary output for the target. | 
 |       filegroup              named: <name>.elf | 
 |         Containing all elf output for the target. | 
 |       filegroup                           named: <name> | 
 |         Containing cc outputs and 256-bitwidth vmem target. | 
 |     """ | 
 |     srcs = srcs + [ | 
 |         "//sw/device/lib/testing/test_framework:kelvin_gloss.c", | 
 |         "//sw/device/lib/testing/test_framework:kelvin_start.S", | 
 |         "@lowrisc_opentitan//sw/device/lib/crt:crt.S", | 
 |     ] | 
 |     kelvin_cc_name = "{}_cc".format(name) | 
 |     kelvin_binary_impl( | 
 |         name = kelvin_cc_name, | 
 |         srcs = srcs, | 
 |         linker_script = "//sw/device/lib/testing/test_framework:kelvin.ld", | 
 |         linker_script_includes = [ | 
 |             "//hw/top_matcha/sw/autogen:top_matcha_memory.ld", | 
 |         ], | 
 |         **kwargs | 
 |     ) | 
 |  | 
 |     # Need to create the following filegroups to make the output discoverable. | 
 |     bin_name = "{}.bin".format(name) | 
 |     native.filegroup( | 
 |         name = bin_name, | 
 |         srcs = [kelvin_cc_name], | 
 |         output_group = "bin_file", | 
 |     ) | 
 |     elf_name = "{}.elf".format(name) | 
 |     native.filegroup( | 
 |         name = elf_name, | 
 |         srcs = [kelvin_cc_name], | 
 |         output_group = "elf_file", | 
 |     ) | 
 |  | 
 |     # Generate vmem file for dv testbench | 
 |     # Memory in ml_top has word size 256. srec_cat supports only up to 128. | 
 |     # Build a flow to transform 32-bitwidth format into 256-bitwidth format. | 
 |     vmem_32_name = "{}.32.vmem".format(name) | 
 |     vmem_256_name = "{}.256.vmem".format(name) | 
 |     bin_to_vmem( | 
 |         name = vmem_32_name, | 
 |         bin = bin_name, | 
 |         word_size = 32, | 
 |     ) | 
 |  | 
 |     cmd = """ | 
 |         $(location //util:gen_vmem_256) \ | 
 |             --input=$< \ | 
 |             --output=$@ | 
 |         """ | 
 |     vmem_name = "{}_vmem".format(name) | 
 |     native.genrule( | 
 |         name = vmem_name, | 
 |         srcs = [vmem_32_name], | 
 |         outs = [vmem_256_name], | 
 |         cmd = cmd, | 
 |         tools = ["@matcha//util:gen_vmem_256"], | 
 |     ) | 
 |     native.filegroup( | 
 |         name = name, | 
 |         srcs = [ | 
 |             bin_name, | 
 |             elf_name, | 
 |             vmem_name, | 
 |         ], | 
 |     ) |