Chris Frantz | 9b34e4a | 2021-11-24 17:03:12 -0800 | [diff] [blame] | 1 | # Copyright lowRISC contributors. |
| 2 | # Licensed under the Apache License, Version 2.0, see LICENSE for details. |
| 3 | # SPDX-License-Identifier: Apache-2.0 |
| 4 | |
| 5 | load("//rules:opentitan.bzl", "OPENTITAN_PLATFORM", "opentitan_transition") |
| 6 | load("//rules:bugfix.bzl", "find_cc_toolchain") |
| 7 | |
| 8 | def _otbn_binary(ctx): |
| 9 | """The build process for otbn resources currently invokes |
| 10 | `//hw/ip/otbn/util/otbn-{as,ld,...}` to build the otbn resource. |
| 11 | These programs are python scripts which translate otbn special |
| 12 | instructions into the proper opcode sequences and _then_ invoke the normal |
| 13 | `rv32-{as,ld,...}` programs to produce the resource. These "native" |
| 14 | otbn resources are the `otbn_objs` and `elf` output groups. |
| 15 | |
| 16 | In order to make the otbn resource useful to the the main CPU, the |
| 17 | otbn resource needs to be included as a blob of data that the main |
| 18 | CPU can dump into the otbn `imem` area and ask otbn to execute it. |
| 19 | `util/otbn-build.py` does this with some objcopy-fu, emitting |
| 20 | `foo.rv32embed.o`. Bazel's `cc_*` rules really want dependency objects |
| 21 | expressed as archives rather than raw object files, so I've modified |
| 22 | `otbn-build` to also emit an archive file. |
| 23 | |
| 24 | _Morally_, the otbn resource is a data dependency. However the |
| 25 | practical meaning of a `data` dependency in bazel is a file made |
| 26 | available at runtime, which is not how we're using the otbn resource. |
| 27 | The closest analog is something like `cc_embed_data`, which is like |
| 28 | a data dependency that needs to be linked into the main program. |
| 29 | We achieve by having `otbn-build` emit a conventional RV32I library |
| 30 | that other rules can depend on in their `deps`. |
| 31 | """ |
| 32 | cc_toolchain = find_cc_toolchain(ctx) |
| 33 | objs = [ |
| 34 | ctx.actions.declare_file(src.basename.replace("." + src.extension, ".o")) |
| 35 | for src in ctx.files.srcs |
| 36 | ] |
| 37 | elf = ctx.actions.declare_file(ctx.attr.name + ".elf") |
| 38 | rv32embed = ctx.actions.declare_file(ctx.attr.name + ".rv32embed.o") |
| 39 | archive = ctx.actions.declare_file(ctx.attr.name + ".rv32embed.a") |
| 40 | outputs = objs + [elf, rv32embed, archive] |
| 41 | |
| 42 | # Note: the toolchain config doesn"t appear to have a good way to get |
| 43 | # access to the assembler. We should be able to access it via the |
| 44 | # the compiler, but I had trouble with //hw/ip/otbn/util/otbn-as invoking |
| 45 | # the compiler as assembler. |
| 46 | assembler = [f for f in cc_toolchain.all_files.to_list() if f.basename.endswith("as")][0] |
| 47 | |
| 48 | ctx.actions.run( |
| 49 | outputs = outputs, |
| 50 | inputs = (ctx.files.srcs + |
| 51 | cc_toolchain.all_files.to_list() + |
| 52 | ctx.files._otbn_as + |
| 53 | ctx.files._otbn_ld + |
| 54 | ctx.files._otbn_data + |
| 55 | ctx.files._wrapper), |
| 56 | env = { |
| 57 | "OTBN_AS": ctx.file._otbn_as.path, |
| 58 | "OTBN_LD": ctx.file._otbn_ld.path, |
| 59 | "RV32_TOOL_AS": assembler.path, |
| 60 | "RV32_TOOL_AR": cc_toolchain.ar_executable, |
| 61 | "RV32_TOOL_LD": cc_toolchain.ld_executable, |
| 62 | "RV32_TOOL_OBJCOPY": cc_toolchain.objcopy_executable, |
| 63 | }, |
| 64 | arguments = [ |
| 65 | "--app-name={}".format(ctx.attr.name), |
| 66 | "--archive", |
| 67 | "--out-dir={}".format(elf.dirname), |
| 68 | ] + [src.path for src in ctx.files.srcs], |
| 69 | executable = ctx.file._wrapper, |
| 70 | ) |
| 71 | |
| 72 | feature_configuration = cc_common.configure_features( |
| 73 | ctx = ctx, |
| 74 | cc_toolchain = cc_toolchain, |
| 75 | requested_features = ctx.features, |
| 76 | unsupported_features = ctx.disabled_features, |
| 77 | ) |
| 78 | |
| 79 | return [ |
| 80 | DefaultInfo(files = depset(outputs), data_runfiles = ctx.runfiles(files = outputs)), |
| 81 | OutputGroupInfo( |
| 82 | otbn_objs = depset(objs), |
| 83 | elf = depset([elf]), |
| 84 | rv32embed = depset([rv32embed]), |
| 85 | archive = depset([archive]), |
| 86 | ), |
| 87 | # Emit a CcInfo provider so that this rule can be a dependency in other |
| 88 | # cc_* rules. |
| 89 | CcInfo( |
| 90 | linking_context = cc_common.create_linking_context( |
| 91 | linker_inputs = depset([cc_common.create_linker_input( |
| 92 | owner = ctx.label, |
| 93 | libraries = depset([cc_common.create_library_to_link( |
| 94 | actions = ctx.actions, |
| 95 | feature_configuration = feature_configuration, |
| 96 | cc_toolchain = cc_toolchain, |
| 97 | static_library = archive, |
| 98 | )]), |
| 99 | )]), |
| 100 | ), |
| 101 | ), |
| 102 | ] |
| 103 | |
| 104 | otbn_binary = rule( |
| 105 | implementation = _otbn_binary, |
| 106 | cfg = opentitan_transition, |
| 107 | attrs = { |
| 108 | "srcs": attr.label_list(allow_files = True), |
| 109 | "platform": attr.string(default = OPENTITAN_PLATFORM), |
| 110 | "_cc_toolchain": attr.label(default = Label("@bazel_tools//tools/cpp:current_cc_toolchain")), |
| 111 | "_otbn_as": attr.label(default = "//hw/ip/otbn/util:otbn-as", allow_single_file = True), |
| 112 | "_otbn_ld": attr.label(default = "//hw/ip/otbn/util:otbn-ld", allow_single_file = True), |
| 113 | "_otbn_data": attr.label(default = "//hw/ip/otbn/data:all_files", allow_files = True), |
| 114 | "_wrapper": attr.label(default = "//util:otbn_build.py", allow_single_file = True), |
| 115 | "_allowlist_function_transition": attr.label( |
| 116 | default = "@bazel_tools//tools/allowlists/function_transition_allowlist", |
| 117 | ), |
| 118 | }, |
| 119 | fragments = ["cpp"], |
| 120 | toolchains = ["@rules_cc//cc:toolchain_type"], |
| 121 | incompatible_use_toolchain_transition = True, |
| 122 | ) |