blob: 22423dac247ae640f07d48dfefe3bd1264afb48f [file] [log] [blame]
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Rules to build Kelvin SW objects"""
load("@rules_cc//cc:find_cc_toolchain.bzl", "find_cc_toolchain")
KELVIN_V2_PLATFORM = "//platforms:kelvin_v2"
KELVIN_V2_SEMIHOSTING_PLATFORM = "//platforms:kelvin_v2_semihosting"
def _kelvin_v2_transition_impl(_settings, attr):
if attr.semihosting:
return {"//command_line_option:platforms": KELVIN_V2_SEMIHOSTING_PLATFORM}
else:
return {"//command_line_option:platforms": KELVIN_V2_PLATFORM}
_kelvin_v2_transition = transition(
implementation = _kelvin_v2_transition_impl,
inputs = [],
outputs = ["//command_line_option:platforms"],
)
def _kelvin_v2_rule(**kwargs):
"""Kelvin-specific transition rule.
A wrapper over rule() for creating rules that trigger
the transition to the kelvin platform config.
Args:
**kwargs: params forwarded to the implementation.
Returns:
Kelvin transition rule.
"""
attrs = kwargs.pop("attrs", {})
attrs["_allowlist_function_transition"] = attr.label(
default = "@bazel_tools//tools/allowlists/function_transition_allowlist",
)
return rule(
cfg = _kelvin_v2_transition,
attrs = attrs,
**kwargs
)
def _kelvin_v2_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 an ELF.
Args:
ctx: context for the rules.
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)
if type(cc_toolchain) != 'CcToolchainInfo':
cc_toolchain = cc_toolchain.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)
sources = []
headers = []
for src in ctx.files.srcs:
if src.extension in ["h", "hh", "hpp"]:
headers.append(src)
else:
sources.append(src)
(_compilation_context, compilation_outputs) = cc_common.compile(
actions = ctx.actions,
cc_toolchain = cc_toolchain,
feature_configuration = feature_configuration,
name = ctx.label.name,
srcs = sources,
compilation_contexts = compilation_contexts,
private_hdrs = headers + 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),
],
additional_inputs = depset([ctx.file.linker_script] + ctx.files.linker_script_includes),
output_type = "executable",
)
out_bin = ctx.actions.declare_file("{}.bin".format(ctx.label.name))
objcopy_tool = cc_toolchain.objcopy_executable
ctx.actions.run(
outputs = [out_bin],
inputs = [linking_outputs.executable] + cc_toolchain.all_files.to_list(),
executable = objcopy_tool,
arguments = [
"-O",
"binary",
linking_outputs.executable.path,
out_bin.path,
],
mnemonic = "ObjCopy",
)
out_vmem = ctx.actions.declare_file("{}.vmem".format(ctx.label.name))
word_size = ctx.attr.word_size
srec_cat_vmem_args = [
out_bin.path,
"-binary",
"-byte-swap",
str(word_size // 8),
"-fill",
"0xff",
"-within",
out_bin.path,
"-binary",
"-range-pad",
str(word_size // 8),
"-o",
out_vmem.path,
"-vmem",
str(word_size),
]
ctx.actions.run(
outputs = [out_vmem],
inputs = [out_bin],
executable = "srec_cat",
arguments = srec_cat_vmem_args,
mnemonic = "SrecCat",
)
return [
DefaultInfo(
files = depset([linking_outputs.executable, out_bin, out_vmem]),
),
OutputGroupInfo(
all_files = depset([linking_outputs.executable, out_bin, out_vmem]),
elf_file = depset([linking_outputs.executable]),
bin_file = depset([out_bin]),
vmem_file = depset([out_vmem]),
),
]
_kelvin_v2_binary = _kelvin_v2_rule(
implementation = _kelvin_v2_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),
"semihosting": attr.bool(),
"word_size": attr.int(default = 32),
"_cc_toolchain": attr.label(default = Label("@bazel_tools//tools/cpp:current_cc_toolchain")),
},
fragments = ["cpp"],
toolchains = ["@rules_cc//cc:toolchain_type"],
)
def kelvin_v2_binary(
name,
srcs,
tags = [],
semihosting = False,
linker_script = "@kelvin_hw//toolchain:kelvin_tcm.ld",
word_size = 32,
**kwargs):
"""A helper macro for generating binary artifacts for the Kelvin V2 core.
This macro uses the kelvin_v2 toolchain, libgloss-htif,
and kelvin linker script to build kelvin binaries.
Args:
name: The name of this rule.
srcs: The c source files.
tags: build tags.
semihosting: Enable htif-style semihosting
linker_script: Linker script to construct the final binary.
**kwargs: Additional arguments forward to cc_binary.
Emits rules:
filegroup named: <name>.bin
Containing the binary output for the target.
filegroup named: <name>.elf
Containing all elf output for the target.
"""
deps = kwargs.pop("deps", [])
if not semihosting:
deps.append("//toolchain/crt")
_kelvin_v2_binary(
name = name,
srcs = srcs,
linker_script = linker_script,
semihosting = semihosting,
tags = tags,
deps = deps,
word_size = word_size,
**kwargs
)
native.filegroup(
name = "{}.elf".format(name),
srcs = [name],
output_group = "elf_file",
tags = tags,
)
native.filegroup(
name = "{}.vmem".format(name),
srcs = [name],
output_group = "vmem_file",
tags = tags,
)
native.filegroup(
name = "{}.bin".format(name),
srcs = [name],
output_group = "bin_file",
tags = tags,
)