| # Copyright lowRISC contributors. |
| # Licensed under the Apache License, Version 2.0, see LICENSE for details. |
| # SPDX-License-Identifier: Apache-2.0 |
| |
| load("@rules_cc//cc:action_names.bzl", "ACTION_NAMES") |
| load("@rules_cc//cc:find_cc_toolchain.bzl", "find_cc_toolchain") |
| load( |
| "@lowrisc_opentitan//rules:cc_side_outputs.bzl", |
| "rv_asm", |
| "rv_llvm_ir", |
| "rv_preprocess", |
| "rv_relink_with_linkmap", |
| ) |
| load( |
| "@lowrisc_opentitan//rules:rv.bzl", |
| "rv_rule", |
| _OPENTITAN_CPU = "OPENTITAN_CPU", |
| _OPENTITAN_PLATFORM = "OPENTITAN_PLATFORM", |
| _opentitan_transition = "opentitan_transition", |
| ) |
| |
| """Rules to build OpenTitan for the RISC-V target""" |
| |
| # 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 |
| |
| _targets_compatible_with = { |
| OPENTITAN_PLATFORM: [OPENTITAN_CPU], |
| } |
| |
| # This constant holds a dictionary of per-device dependencies which are used to |
| # generate slightly different binaries for each hardware target, including two |
| # simulation platforms (DV and Verilator), and two FPGA platforms (CW305 |
| # and CW310). |
| PER_DEVICE_DEPS = { |
| "sim_verilator": ["@//sw/device/lib/arch:sim_verilator"], |
| "sim_dv": ["@//sw/device/lib/arch:sim_dv"], |
| "fpga_cw305": ["@//sw/device/lib/arch:fpga_cw305"], |
| "fpga_cw310": ["@//sw/device/lib/arch:fpga_cw310"], |
| } |
| |
| # Default keys used to sign ROM_EXT and BL0 images for testing. |
| DEFAULT_SIGNING_KEYS = { |
| "fake_test_key_0": "@//sw/device/silicon_creator/rom/keys/fake:test_private_key_0", |
| "fake_dev_key_0": "@//sw/device/silicon_creator/rom/keys/fake:dev_private_key_0", |
| "fake_prod_key_0": "@//sw/device/silicon_creator/rom/keys/fake:prod_private_key_0", |
| "unauthorized_0": "@//sw/device/silicon_creator/rom/keys:unauthorized_private_key_0", |
| "fake_rom_ext_test_key_0": "@//sw/device/silicon_creator/rom_ext/keys/fake:rom_ext_test_private_key_0", |
| "fake_rom_ext_dev_key_0": "@//sw/device/silicon_creator/rom_ext/keys/fake:rom_ext_dev_private_key_0", |
| } |
| |
| def _obj_transform_impl(ctx): |
| cc_toolchain = find_cc_toolchain(ctx).cc |
| outputs = [] |
| for src in ctx.files.srcs: |
| binary = ctx.actions.declare_file( |
| "{}.{}".format( |
| src.basename.replace("." + src.extension, ""), |
| ctx.attr.suffix, |
| ), |
| ) |
| outputs.append(binary) |
| ctx.actions.run( |
| outputs = [binary], |
| inputs = [src] + cc_toolchain.all_files.to_list(), |
| arguments = [ |
| "--output-target", |
| ctx.attr.format, |
| src.path, |
| binary.path, |
| ], |
| executable = cc_toolchain.objcopy_executable, |
| ) |
| return [DefaultInfo(files = depset(outputs), data_runfiles = ctx.runfiles(files = outputs))] |
| |
| obj_transform = rv_rule( |
| implementation = _obj_transform_impl, |
| attrs = { |
| "srcs": attr.label_list(allow_files = True), |
| "suffix": attr.string(default = "bin"), |
| "format": attr.string(default = "binary"), |
| "_cc_toolchain": attr.label(default = Label("@bazel_tools//tools/cpp:current_cc_toolchain")), |
| }, |
| toolchains = ["@rules_cc//cc:toolchain_type"], |
| ) |
| |
| # A provider for device-specific archive files that hold binaries of SRAM programs. |
| ArchiveInfo = provider(fields = ["archive_infos"]) |
| |
| def _bin_to_archive_impl(ctx): |
| cc_infos = [] |
| cc_toolchain = find_cc_toolchain(ctx).cc |
| cc_info_dict = {} |
| num_devices = len(ctx.attr.devices) |
| num_binaries = len(ctx.attr.binaries) |
| if num_devices != num_binaries: |
| fail("Number of devices", num_devices, "must be equal to number of binaries", num_binaries) |
| for (device, binary_target) in zip(ctx.attr.devices, ctx.attr.binaries): |
| devname = "{}_{}".format(ctx.attr.name, device) |
| binary_file = binary_target.files.to_list()[0] |
| object_file = ctx.actions.declare_file("{}.o".format(devname)) |
| renamed_object_file = ctx.actions.declare_file("{}.renamed.o".format(devname)) |
| archive_file = ctx.actions.declare_file("{}.a".format(devname)) |
| |
| # Create a CcInfo to be able to use this rule as a dependency in other rules. |
| # See https://bazel.build/docs/integrating-with-rules-cc. |
| feature_configuration = cc_common.configure_features( |
| ctx = ctx, |
| cc_toolchain = cc_toolchain, |
| requested_features = ctx.features, |
| unsupported_features = ctx.disabled_features, |
| ) |
| action_name = ACTION_NAMES.cpp_link_executable |
| c_linker_path = cc_common.get_tool_for_action( |
| feature_configuration = feature_configuration, |
| action_name = action_name, |
| ) |
| c_link_variables = cc_common.create_link_variables( |
| feature_configuration = feature_configuration, |
| cc_toolchain = cc_toolchain, |
| output_file = object_file.path, |
| ) |
| command_line = cc_common.get_memory_inefficient_command_line( |
| feature_configuration = feature_configuration, |
| action_name = action_name, |
| variables = c_link_variables, |
| ) |
| env = cc_common.get_environment_variables( |
| feature_configuration = feature_configuration, |
| action_name = action_name, |
| variables = c_link_variables, |
| ) |
| linker_path = cc_common.get_tool_for_action( |
| feature_configuration = feature_configuration, |
| action_name = action_name, |
| ) |
| |
| # Create an object file that contains the binary. |
| ctx.actions.run( |
| executable = cc_toolchain.ld_executable, |
| arguments = [ |
| "-r", |
| "-b", |
| "binary", |
| "-o", |
| object_file.path, |
| binary_file.path, |
| ], |
| use_default_shell_env = False, |
| env = env, |
| inputs = depset( |
| direct = [binary_file], |
| transitive = [cc_toolchain.all_files], |
| ), |
| outputs = [object_file], |
| mnemonic = "CppLink", |
| ) |
| |
| # Rename symbols to make them more manageable. |
| sym_prefix = "_binary_" + binary_file.path.replace(".", "_").replace("/", "_").replace("-", "_") |
| suffixes = ["start", "end", "size"] |
| rename_args = [] |
| for suffix in suffixes: |
| old_name = "{}_{}".format(sym_prefix, suffix) |
| new_name = "_{}_{}".format(ctx.attr.archive_symbol_prefix, suffix) |
| rename_args.extend(["--redefine-sym", "{}={}".format(old_name, new_name)]) |
| rename_args.extend(["--rename-section", ".data=.data.sram_program"]) |
| rename_args.extend([object_file.path, renamed_object_file.path]) |
| ctx.actions.run( |
| executable = cc_toolchain.objcopy_executable, |
| arguments = rename_args, |
| use_default_shell_env = False, |
| env = env, |
| inputs = depset( |
| direct = [object_file], |
| transitive = [cc_toolchain.all_files], |
| ), |
| outputs = [renamed_object_file], |
| mnemonic = "RenameSymbols", |
| ) |
| |
| # Create an archive that contains the object file. |
| ctx.actions.run( |
| executable = cc_toolchain.ar_executable, |
| arguments = [ |
| "r", |
| archive_file.path, |
| renamed_object_file.path, |
| ], |
| use_default_shell_env = False, |
| env = env, |
| inputs = depset( |
| direct = [renamed_object_file], |
| transitive = [cc_toolchain.all_files], |
| ), |
| outputs = [archive_file], |
| mnemonic = "Archive", |
| ) |
| |
| cc_info_dict[device] = CcInfo( |
| compilation_context = cc_common.create_compilation_context( |
| headers = depset(direct = ctx.attr.hdrs[0].files.to_list()), |
| ), |
| linking_context = cc_common.create_linking_context( |
| linker_inputs = depset([cc_common.create_linker_input( |
| owner = ctx.label, |
| libraries = depset([cc_common.create_library_to_link( |
| actions = ctx.actions, |
| feature_configuration = feature_configuration, |
| cc_toolchain = cc_toolchain, |
| static_library = archive_file, |
| )]), |
| )]), |
| ), |
| ) |
| |
| return ArchiveInfo(archive_infos = cc_info_dict) |
| |
| bin_to_archive = rv_rule( |
| implementation = _bin_to_archive_impl, |
| attrs = { |
| "binaries": attr.label_list(allow_files = True), |
| "devices": attr.string_list(), |
| "hdrs": attr.label_list(allow_files = True), |
| "archive_symbol_prefix": attr.string(), |
| "_cc_toolchain": attr.label(default = Label("@bazel_tools//tools/cpp:current_cc_toolchain")), |
| }, |
| fragments = ["cpp"], |
| toolchains = ["@rules_cc//cc:toolchain_type"], |
| ) |
| |
| def _sign_bin_impl(ctx): |
| signed_image = ctx.actions.declare_file( |
| "{0}.{1}.signed.bin".format( |
| # Remove ".bin" from file basename. |
| ctx.file.bin.basename.replace("." + ctx.file.bin.extension, ""), |
| ctx.attr.key_name, |
| ), |
| ) |
| outputs = [signed_image] |
| |
| inputs = [ |
| ctx.file.bin, |
| ctx.file.key, |
| ctx.file._opentitantool, |
| ] |
| manifest = [] |
| if ctx.file.manifest: |
| manifest = ["--manifest={}".format(ctx.file.manifest.path)] |
| inputs.append(ctx.file.manifest) |
| |
| ctx.actions.run( |
| outputs = outputs, |
| inputs = inputs, |
| arguments = [ |
| "--rcfile=", |
| "image", |
| "manifest", |
| "update", |
| "--key-file={}".format(ctx.file.key.path), |
| "--sign", |
| "--output={}".format(signed_image.path), |
| ctx.file.bin.path, |
| ] + manifest, |
| executable = ctx.file._opentitantool.path, |
| ) |
| return [DefaultInfo( |
| files = depset(outputs), |
| data_runfiles = ctx.runfiles(files = outputs), |
| )] |
| |
| sign_bin = rv_rule( |
| implementation = _sign_bin_impl, |
| attrs = { |
| "bin": attr.label(allow_single_file = True), |
| "key": attr.label( |
| default = "@//sw/device/silicon_creator/rom/keys:test_private_key_0", |
| allow_single_file = True, |
| ), |
| "key_name": attr.string(), |
| "manifest": attr.label(allow_single_file = True), |
| # TODO(lowRISC/opentitan:#11199): explore other options to side-step the |
| # need for this transition, in order to build the ROM_EXT signer tool. |
| "platform": attr.string(default = "@local_config_platform//:host"), |
| "_opentitantool": attr.label( |
| default = "//sw/host/opentitantool:opentitantool", |
| allow_single_file = True, |
| ), |
| }, |
| ) |
| |
| def _elf_to_disassembly_impl(ctx): |
| cc_toolchain = find_cc_toolchain(ctx).cc |
| outputs = [] |
| for src in ctx.files.srcs: |
| disassembly = ctx.actions.declare_file( |
| "{}.dis".format( |
| src.basename.replace("." + src.extension, ""), |
| ), |
| ) |
| outputs.append(disassembly) |
| ctx.actions.run_shell( |
| tools = [ctx.file._cleanup_script], |
| outputs = [disassembly], |
| inputs = [src] + cc_toolchain.all_files.to_list(), |
| arguments = [ |
| cc_toolchain.objdump_executable, |
| src.path, |
| ctx.file._cleanup_script.path, |
| disassembly.path, |
| ], |
| execution_requirements = { |
| "no-sandbox": "1", |
| }, |
| command = "$1 -wx --disassemble --headers --line-numbers --disassemble-zeroes --source --visualize-jumps $2 | $3 > $4", |
| ) |
| return [DefaultInfo(files = depset(outputs), data_runfiles = ctx.runfiles(files = outputs))] |
| |
| elf_to_disassembly = rv_rule( |
| implementation = _elf_to_disassembly_impl, |
| attrs = { |
| "srcs": attr.label_list(allow_files = True), |
| "platform": attr.string(default = OPENTITAN_PLATFORM), |
| "_cleanup_script": attr.label( |
| allow_single_file = True, |
| default = Label("@//rules/scripts:expand_tabs.sh"), |
| ), |
| "_cc_toolchain": attr.label(default = Label("@bazel_tools//tools/cpp:current_cc_toolchain")), |
| }, |
| toolchains = ["@rules_cc//cc:toolchain_type"], |
| incompatible_use_toolchain_transition = True, |
| ) |
| |
| 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 = "@//hw/ip/rom_ctrl/util:scramble_image", |
| executable = True, |
| cfg = "exec", |
| ), |
| "_config": attr.label( |
| default = "@//hw/top_earlgrey/data:autogen/top_earlgrey.gen.hjson", |
| allow_single_file = True, |
| ), |
| }, |
| ) |
| |
| def _bin_to_vmem_impl(ctx): |
| outputs = [] |
| vmem = ctx.actions.declare_file("{}.{}.vmem".format( |
| # Remove ".bin" from file basename. |
| ctx.file.bin.basename.replace("." + ctx.file.bin.extension, ""), |
| ctx.attr.word_size, |
| )) |
| outputs.append(vmem) |
| ctx.actions.run( |
| outputs = [vmem], |
| inputs = [ |
| ctx.file.bin, |
| ], |
| arguments = [ |
| ctx.file.bin.path, |
| "--binary", |
| # Reverse the endianness of every word. |
| "--offset", |
| "0x0", |
| "--byte-swap", |
| str(ctx.attr.word_size // 8), |
| # Pad to word alignment. |
| "--fill", |
| "0xff", |
| "-within", |
| ctx.file.bin.path, |
| "-binary", |
| "-range-pad", |
| str(ctx.attr.word_size // 8), |
| # Output a VMEM file with specified word size |
| "--output", |
| vmem.path, |
| "--vmem", |
| str(ctx.attr.word_size), |
| ], |
| # This this executable is expected to be installed (as required by the |
| # srecord package in apt-requirements.txt). |
| executable = "srec_cat", |
| use_default_shell_env = True, |
| ) |
| return [DefaultInfo( |
| files = depset(outputs), |
| data_runfiles = ctx.runfiles(files = outputs), |
| )] |
| |
| bin_to_vmem = rv_rule( |
| implementation = _bin_to_vmem_impl, |
| attrs = { |
| "bin": attr.label(allow_single_file = True), |
| "word_size": attr.int( |
| default = 64, |
| doc = "Word size of VMEM file.", |
| mandatory = True, |
| values = [32, 64], |
| ), |
| }, |
| ) |
| |
| def _scramble_flash_vmem_impl(ctx): |
| outputs = [] |
| scrambled_vmem = ctx.actions.declare_file("{}.scr.vmem".format( |
| # Remove ".vmem" from file basename. |
| ctx.file.vmem.basename.replace("." + ctx.file.vmem.extension, ""), |
| )) |
| outputs.append(scrambled_vmem) |
| ctx.actions.run( |
| outputs = [scrambled_vmem], |
| inputs = [ |
| ctx.file.vmem, |
| ctx.executable._tool, |
| ], |
| arguments = [ |
| "--infile", |
| ctx.file.vmem.path, |
| "--outfile", |
| scrambled_vmem.path, |
| ], |
| executable = ctx.executable._tool, |
| ) |
| return [DefaultInfo( |
| files = depset(outputs), |
| data_runfiles = ctx.runfiles(files = outputs), |
| )] |
| |
| scramble_flash_vmem = rv_rule( |
| implementation = _scramble_flash_vmem_impl, |
| attrs = { |
| "vmem": attr.label(allow_single_file = True), |
| "_tool": attr.label( |
| default = "@//util/design:gen-flash-img", |
| executable = True, |
| cfg = "exec", |
| ), |
| }, |
| ) |
| |
| def _gen_sim_dv_logs_db_impl(ctx): |
| outputs = [] |
| for src in ctx.files.srcs: |
| if src.extension != "elf": |
| fail("can only generate DV logs database files from ELF files.") |
| logs_db = ctx.actions.declare_file("{}.logs.txt".format( |
| src.basename.replace("." + src.extension, ""), |
| )) |
| rodata = ctx.actions.declare_file("{}.rodata.txt".format( |
| src.basename.replace("." + src.extension, ""), |
| )) |
| outputs.append(logs_db) |
| outputs.append(rodata) |
| |
| ctx.actions.run( |
| outputs = outputs, |
| inputs = [src], |
| arguments = [ |
| "--elf-file", |
| src.path, |
| "--logs-fields-section", |
| ".logs.fields", |
| "--name", |
| src.basename.replace("." + src.extension, ""), |
| "--outdir", |
| logs_db.dirname, |
| ], |
| executable = ctx.executable._tool, |
| ) |
| return [DefaultInfo( |
| files = depset(outputs), |
| data_runfiles = ctx.runfiles(files = outputs), |
| )] |
| |
| gen_sim_dv_logs_db = rule( |
| implementation = _gen_sim_dv_logs_db_impl, |
| cfg = opentitan_transition, |
| attrs = { |
| "srcs": attr.label_list(allow_files = True), |
| "platform": attr.string(default = OPENTITAN_PLATFORM), |
| "_tool": attr.label( |
| default = "@//util/device_sw_utils:extract_sw_logs_db", |
| cfg = "exec", |
| executable = True, |
| ), |
| "_allowlist_function_transition": attr.label( |
| default = "@bazel_tools//tools/allowlists/function_transition_allowlist", |
| ), |
| }, |
| ) |
| |
| def _assemble_flash_image_impl(ctx): |
| output = ctx.actions.declare_file(ctx.attr.output) |
| outputs = [output] |
| inputs = [] |
| arguments = [ |
| "--rcfile=", |
| "image", |
| "assemble", |
| "--mirror", |
| "false", |
| "--output", |
| output.path, |
| ] |
| if ctx.attr.image_size: |
| arguments.append("--size={}".format(ctx.attr.image_size)) |
| for binary, offset in ctx.attr.binaries.items(): |
| inputs.extend(binary.files.to_list()) |
| arguments.append("{}@{}".format(binary.files.to_list()[0].path, offset)) |
| ctx.actions.run( |
| outputs = outputs, |
| inputs = inputs, |
| arguments = arguments, |
| executable = ctx.executable._opentitantool, |
| ) |
| return [DefaultInfo( |
| files = depset(outputs), |
| data_runfiles = ctx.runfiles(files = outputs), |
| )] |
| |
| assemble_flash_image = rv_rule( |
| implementation = _assemble_flash_image_impl, |
| attrs = { |
| "image_size": attr.int(default = 0, doc = "Size of the assembled image"), |
| "output": attr.string(), |
| "binaries": attr.label_keyed_string_dict(allow_empty = False), |
| "_opentitantool": attr.label( |
| default = "//sw/host/opentitantool:opentitantool", |
| allow_single_file = True, |
| executable = True, |
| cfg = "exec", |
| ), |
| }, |
| ) |
| |
| def opentitan_binary( |
| name, |
| platform = OPENTITAN_PLATFORM, |
| extract_sw_logs_db = False, |
| testonly = True, |
| **kwargs): |
| """A helper macro for generating OpenTitan binary artifacts. |
| |
| This macro is mostly a wrapper around cc_binary, but creates artifacts |
| compatible with OpenTitan binaries. The actual artifacts created are outputs |
| of the rules listed below. |
| Args: |
| @param name: The name of this rule. |
| @param platform: The target platform for the artifacts. |
| @param extract_sw_logs_db: Whether to emit a log database for DV testbench. |
| @param **kwargs: Arguments to forward to `cc_binary`. |
| Emits rules: |
| cc_binary named: <name>.elf |
| rv_preprocess named: <name>_preproc |
| rv_asm named: <name>_asm |
| rv_llvm_ir named: <name>_ll |
| rv_relink_with_map named: <name>_map |
| obj_transform named: <name>_bin |
| elf_to_dissassembly named: <name>_dis |
| Optionally: |
| gen_sim_dv_logs_db named: <name>_logs_db |
| Returns: |
| List of targets generated by all of the above rules. |
| """ |
| |
| copts = kwargs.pop("copts", []) + [ |
| "-nostdlib", |
| "-ffreestanding", |
| ] |
| linkopts = kwargs.pop("linkopts", []) + [ |
| "-nostartfiles", |
| "-nostdlib", |
| ] |
| deps = kwargs.pop("deps", []) |
| targets = [] |
| side_targets = [] |
| |
| native_binary_name = "{}.elf".format(name) |
| native.cc_binary( |
| name = native_binary_name, |
| deps = deps, |
| target_compatible_with = _targets_compatible_with[platform], |
| copts = copts, |
| linkopts = linkopts, |
| testonly = testonly, |
| **kwargs |
| ) |
| |
| preproc_name = "{}_{}".format(name, "preproc") |
| side_targets.append(preproc_name) |
| rv_preprocess( |
| name = preproc_name, |
| target = native_binary_name, |
| testonly = testonly, |
| ) |
| |
| asm_name = "{}_{}".format(name, "asm") |
| side_targets.append(asm_name) |
| rv_asm( |
| name = asm_name, |
| target = native_binary_name, |
| testonly = testonly, |
| ) |
| |
| ll_name = "{}_{}".format(name, "ll") |
| side_targets.append(ll_name) |
| rv_llvm_ir( |
| name = ll_name, |
| target = native_binary_name, |
| testonly = testonly, |
| ) |
| |
| map_name = "{}_{}".format(name, "map") |
| side_targets.append(map_name) |
| rv_relink_with_linkmap( |
| name = map_name, |
| target = native_binary_name, |
| testonly = testonly, |
| ) |
| |
| bin_name = "{}_{}".format(name, "bin") |
| targets.append(":" + bin_name) |
| obj_transform( |
| name = bin_name, |
| srcs = [native_binary_name], |
| platform = platform, |
| testonly = testonly, |
| ) |
| |
| dis_name = "{}_{}".format(name, "dis") |
| targets.append(":" + dis_name) |
| elf_to_disassembly( |
| name = dis_name, |
| srcs = [native_binary_name], |
| platform = platform, |
| testonly = testonly, |
| ) |
| |
| # Generate log message database for DV sim testbench |
| if extract_sw_logs_db: |
| logs_db_name = "{}_{}".format(name, "logs_db") |
| targets.append(":" + logs_db_name) |
| gen_sim_dv_logs_db( |
| name = logs_db_name, |
| srcs = [native_binary_name], |
| platform = platform, |
| testonly = testonly, |
| ) |
| |
| # Create a filegroup with just the sides targets. |
| native.filegroup( |
| name = name + "_side_targets", |
| srcs = side_targets, |
| testonly = testonly, |
| ) |
| |
| return targets |
| |
| def opentitan_rom_binary( |
| name, |
| devices = PER_DEVICE_DEPS.keys(), |
| platform = OPENTITAN_PLATFORM, |
| testonly = True, |
| **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: |
| @param name: The name of this rule. |
| @param devices: List of devices to build the target for. |
| @param platform: The target platform for the artifacts. |
| @param **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", []) |
| all_targets = [] |
| for device in devices: |
| if device not in PER_DEVICE_DEPS: |
| fail("invalid device; device must be in {}".format(PER_DEVICE_DEPS.keys())) |
| dev_deps = PER_DEVICE_DEPS[device] |
| 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, |
| **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, |
| word_size = 32, |
| ) |
| |
| # Generate Scrambled ROM VMEM |
| scr_vmem_name = "{}_scr_vmem".format(devname) |
| dev_targets.append(":" + scr_vmem_name) |
| elf_to_scrambled_rom_vmem( |
| name = scr_vmem_name, |
| srcs = [elf_name], |
| platform = platform, |
| testonly = testonly, |
| ) |
| |
| # 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 _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(), |
| }, |
| fragments = ["cpp"], |
| toolchains = ["@rules_cc//cc:toolchain_type"], |
| ) |
| |
| def opentitan_multislot_flash_binary( |
| name, |
| srcs, |
| image_size = 0, |
| devices = PER_DEVICE_DEPS.keys(), |
| platform = OPENTITAN_PLATFORM, |
| testonly = True): |
| """A helper macro for generating multislot OpenTitan binary flash images. |
| |
| This macro is mostly a wrapper around the `assemble_flash_image` rule, that |
| invokes `opentitantool` to stitch together multiple `opentitan_flash_binary` |
| images to create a single image for bootstrapping. Since bootstrap erases |
| the flash for programming this is the only way to load multiple |
| (A/B/Virtual) slots and (silicon creator, ROM_EXT, and owner, BL0) stages at |
| the same time. |
| Args: |
| @param name: The name of this rule. |
| @param srcs: A dictionary of `opentitan_flash_binary` targets (to stitch |
| together) as keys, and key/offset configurations as values. |
| @param image_size: The final flash image_size to pass to `opentitantool` |
| (optional). |
| @param devices: List of devices to build the target for. |
| @param platform: The target platform for the artifacts. |
| Emits rules: |
| For each device in per_device_deps entry: |
| rules emitted by `opentitan_binary` named: see `opentitan_binary` macro |
| assemble_flash_image named: <name>_<device>_bin_signed |
| bin_to_vmem named: <name>_<device>_vmem64_signed |
| scrambled_flash_vmem named: <name>_<device>_scr_vmem64_signed |
| 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. |
| """ |
| all_targets = [] |
| for device in devices: |
| if device not in PER_DEVICE_DEPS: |
| fail("invalid device; device must be in {}".format(PER_DEVICE_DEPS.keys())) |
| devname = "{}_{}".format(name, device) |
| dev_targets = [] |
| signed_dev_binaries = {} |
| for src, configs in srcs.items(): |
| if "key" not in configs: |
| fail("Missing signing key for binary: {}".format(src)) |
| if "offset" not in configs: |
| fail("Missing offset for binary: {}".format(src)) |
| signed_dev_binary = "{}_{}_bin_signed_{}".format( |
| src, |
| device, |
| configs["key"], |
| testonly = testonly, |
| ) |
| signed_dev_binaries[signed_dev_binary] = configs["offset"] |
| |
| # Assemble the signed binaries into a single binary. |
| signed_bin_name = "{}_bin_signed".format(devname) |
| dev_targets.append(":" + signed_bin_name) |
| assemble_flash_image( |
| name = signed_bin_name, |
| output = "{}.signed.bin".format(devname), |
| image_size = image_size, |
| binaries = signed_dev_binaries, |
| 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. |
| signed_vmem_name = "{}_vmem64_signed".format(devname) |
| dev_targets.append(":" + signed_vmem_name) |
| bin_to_vmem( |
| name = signed_vmem_name, |
| bin = signed_bin_name, |
| platform = platform, |
| testonly = testonly, |
| word_size = 64, # Backdoor-load VMEM image uses 64-bit words |
| ) |
| |
| # Scramble signed VMEM64. |
| scr_signed_vmem_name = "{}_scr_vmem64_signed".format(devname) |
| dev_targets.append(":" + scr_signed_vmem_name) |
| scramble_flash_vmem( |
| name = scr_signed_vmem_name, |
| vmem = signed_vmem_name, |
| platform = platform, |
| testonly = testonly, |
| ) |
| |
| # Create a filegroup with just the current device's targets. |
| native.filegroup( |
| name = devname, |
| srcs = dev_targets, |
| testonly = testonly, |
| ) |
| dev_targets.extend(dev_targets) |
| |
| # Create a filegroup with all assembled flash images. |
| native.filegroup( |
| name = name, |
| srcs = all_targets, |
| testonly = testonly, |
| ) |
| |
| def opentitan_flash_binary( |
| name, |
| devices = PER_DEVICE_DEPS.keys(), |
| platform = OPENTITAN_PLATFORM, |
| signing_keys = DEFAULT_SIGNING_KEYS, |
| signed = True, |
| testonly = True, |
| manifest = "//sw/device/silicon_creator/rom_ext:manifest_standard", |
| **kwargs): |
| """A helper macro for generating OpenTitan binary artifacts for flash. |
| |
| 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: |
| @param name: The name of this rule. |
| @param devices: List of devices to build the target for. |
| @param platform: The target platform for the artifacts. |
| @param signing_keys: The signing keys for to sign each BIN file with. |
| @param signed: Whether or not to emit signed binary/VMEM files. |
| @param manifest: Partially populated manifest to set boot stage/slot configs. |
| @param **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", []) |
| all_targets = [] |
| for device in devices: |
| if device not in PER_DEVICE_DEPS: |
| fail("invalid device; device must be in {}".format(PER_DEVICE_DEPS.keys())) |
| dev_deps = PER_DEVICE_DEPS[device] |
| devname = "{}_{}".format(name, device) |
| dev_targets = [] |
| |
| depname = "{}_deps".format(devname) |
| pick_correct_archive_for_device( |
| name = depname, |
| 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 = "{}_vmem64_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 = 64, # Backdoor-load VMEM image uses 64-bit words |
| ) |
| |
| # Scramble signed VMEM64. |
| scr_signed_vmem_name = "{}_scr_vmem64_signed_{}".format( |
| devname, |
| key_name, |
| ) |
| dev_targets.append(":" + scr_signed_vmem_name) |
| scramble_flash_vmem( |
| name = scr_signed_vmem_name, |
| 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 = "{}_vmem64".format(devname) |
| dev_targets.append(":" + vmem_name) |
| bin_to_vmem( |
| name = vmem_name, |
| bin = bin_name, |
| platform = platform, |
| testonly = testonly, |
| word_size = 64, # Backdoor-load VMEM image uses 64-bit words |
| ) |
| |
| # Scramble VMEM64. |
| scr_vmem_name = "{}_scr_vmem64".format(devname) |
| dev_targets.append(":" + scr_vmem_name) |
| scramble_flash_vmem( |
| name = scr_vmem_name, |
| vmem = vmem_name, |
| platform = platform, |
| testonly = testonly, |
| ) |
| |
| # 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 opentitan_ram_binary( |
| name, |
| archive_symbol_prefix, |
| devices = PER_DEVICE_DEPS.keys(), |
| platform = OPENTITAN_PLATFORM, |
| testonly = True, |
| **kwargs): |
| """A helper macro for generating OpenTitan binary artifacts for RAM. |
| |
| 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: |
| @param name: The name of this rule. |
| @param archive_symbol_prefix: Prefix used to rename symbols in the binary. |
| @param devices: List of devices to build the target for. |
| @param platform: The target platform for the artifacts. |
| @param **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_archive named: <name> |
| """ |
| deps = kwargs.pop("deps", []) |
| hdrs = kwargs.pop("hdrs", []) |
| binaries = [] |
| for device in devices: |
| if device not in PER_DEVICE_DEPS: |
| fail("invalid device; device must be in {}".format(PER_DEVICE_DEPS.keys())) |
| dev_deps = PER_DEVICE_DEPS[device] |
| devname = "{}_{}".format(name, device) |
| dev_targets = [] |
| |
| # Generate the binary. |
| dev_targets.extend(opentitan_binary( |
| name = devname, |
| deps = deps + dev_deps, |
| extract_sw_logs_db = device == "sim_dv", |
| testonly = testonly, |
| **kwargs |
| )) |
| bin_name = "{}_{}".format(devname, "bin") |
| binaries.append(":" + bin_name) |
| |
| # Generate Un-scrambled RAM VMEM (for testing SRAM injection in DV) |
| 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 = 32, |
| ) |
| |
| # Create a filegroup with just the current device's targets. |
| native.filegroup( |
| name = devname, |
| srcs = dev_targets, |
| testonly = testonly, |
| ) |
| |
| # Generate the archive file. |
| bin_to_archive( |
| name = name, |
| hdrs = hdrs, |
| binaries = binaries, |
| devices = PER_DEVICE_DEPS.keys(), |
| archive_symbol_prefix = archive_symbol_prefix, |
| testonly = testonly, |
| ) |