diff --git a/.buckconfig b/.buckconfig index c65bd54e..16785f17 100644 --- a/.buckconfig +++ b/.buckconfig @@ -17,4 +17,13 @@ target_platform_detector_spec = target:root//...->prelude//platforms:default ignore = .git [build] -execution_platforms = prelude//platforms:default \ No newline at end of file +execution_platforms = prelude//platforms:default + +[buck2_re_client] +engine_address = grpc://172.21.252.165:50051 +action_cache_address = grpc://172.21.252.165:50051 +cas_address = grpc://172.21.252.165:50051 +tls = false +instance_name = main +enabled = false +capabilities = true \ No newline at end of file diff --git a/.buckconfig_cache b/.buckconfig_cache new file mode 100644 index 00000000..ed0cc1e5 --- /dev/null +++ b/.buckconfig_cache @@ -0,0 +1,29 @@ +[repositories] +root = . +prelude = prelude +toolchains = toolchains +none = none + +[repository_aliases] +config = prelude +fbcode = none +fbsource = none +buck = none + +[parser] +target_platform_detector_spec = target:root//...->prelude//platforms:default + +[project] +ignore = .git + +[build] +execution_platforms = prelude//platforms:default + +[buck2_re_client] +engine_address = grpc://172.21.252.165:50051 +action_cache_address = grpc://172.21.252.165:50051 +cas_address = grpc://172.21.252.165:50051 +tls = false +instance_name = main +enabled = true +capabilities = true \ No newline at end of file diff --git a/hdl/projects/cosmo_seq/BUCK b/hdl/projects/cosmo_seq/BUCK index dd5b0060..f901fa4e 100644 --- a/hdl/projects/cosmo_seq/BUCK +++ b/hdl/projects/cosmo_seq/BUCK @@ -1,6 +1,6 @@ load("//tools:hdl.bzl", "vhdl_unit", "black_box") load("//tools:rdl.bzl", "rdl_file") -load("//tools:vivado.bzl", "vivado_bitstream") +load("//tools:vivado.bzl", "vivado_bitstream", "vivado_ip") rdl_file( name = "cosmo_seq_top_rdl", @@ -44,6 +44,13 @@ vhdl_unit( standard = "2019", ) +vivado_ip( + name = "cosmo_pll_ip", + tcl = "xilinx_ip_gen/cosmo_pll_ip_gen.tcl", + module_name = "cosmo_pll", + part = "xc7s100fgga484-1", +) + vhdl_unit( name = "cosmo_seq_top", @@ -71,7 +78,8 @@ vivado_bitstream( top_entity_name="cosmo_seq_top", top= ":cosmo_seq_top", part= "xc7s100fgga484-1", + ip=[":cosmo_pll_ip"], constraints=glob(["*.xdc"]), - pre_synth_tcl_files=glob(["xilinx_ip_gen/*.tcl"]), + #pre_synth_tcl_files=glob(["xilinx_ip_gen/*.tcl"]), #post_synth_tcl_files=glob(["*ila.tcl"]), ) \ No newline at end of file diff --git a/hdl/projects/cosmo_seq/xilinx_ip_gen/cosmo_pll_ip_gen.tcl b/hdl/projects/cosmo_seq/xilinx_ip_gen/cosmo_pll_ip_gen.tcl new file mode 100644 index 00000000..afb14963 --- /dev/null +++ b/hdl/projects/cosmo_seq/xilinx_ip_gen/cosmo_pll_ip_gen.tcl @@ -0,0 +1,31 @@ +# Generate the PLL (copied from tcl console using the IP generator) +# with name and dir adjusted + +# Get tclargs here +set name [lindex $argv 0] +set dir [lindex $argv 1] + +# generate IP +create_ip -name clk_wiz -vendor xilinx.com -library ip -version 6.0 -module_name $name -dir $dir +set_property -dict [list \ + CONFIG.CLKIN1_JITTER_PS {200.0} \ + CONFIG.CLKOUT1_JITTER {154.207} \ + CONFIG.CLKOUT1_PHASE_ERROR {164.985} \ + CONFIG.CLKOUT1_REQUESTED_OUT_FREQ {125.000} \ + CONFIG.CLKOUT2_JITTER {142.107} \ + CONFIG.CLKOUT2_PHASE_ERROR {164.985} \ + CONFIG.CLKOUT2_REQUESTED_OUT_FREQ {200} \ + CONFIG.CLKOUT2_USED {true} \ + CONFIG.CLK_OUT1_PORT {clk_125m} \ + CONFIG.CLK_OUT2_PORT {clk_200m} \ + CONFIG.Component_Name {$name} \ + CONFIG.MMCM_CLKFBOUT_MULT_F {20.000} \ + CONFIG.MMCM_CLKIN1_PERIOD {20.000} \ + CONFIG.MMCM_CLKIN2_PERIOD {10.0} \ + CONFIG.MMCM_CLKOUT0_DIVIDE_F {8.000} \ + CONFIG.MMCM_CLKOUT1_DIVIDE {5} \ + CONFIG.NUM_OUT_CLKS {2} \ + CONFIG.PRIMARY_PORT {clk_50m} \ + CONFIG.PRIM_IN_FREQ {50} \ +] [get_ips $name] +synth_ip [get_ips $name] \ No newline at end of file diff --git a/tools/hdl.bzl b/tools/hdl.bzl index ef5ba815..8822d17c 100644 --- a/tools/hdl.bzl +++ b/tools/hdl.bzl @@ -90,12 +90,12 @@ def _hdl_unit_impl(ctx: AnalysisContext) -> list[Provider]: cmd.add(vunit_gen) cmd.add("--input", ctx.attrs.srcs[0]) cmd.add("--output", out_codec_pkg.as_output()) - ctx.actions.run(cmd, category="vunit_codec_gen") + ctx.actions.run(cmd, category="vunit_codec_gen", local_only = True, allow_cache_upload=True) providers.append(GenVHDLInfo(src=out_codec_pkg)) # do VUnit stuff here if this is a test bench - # Note that this is not acutally generated in the buck_out/ folder - # After playing with this a bit, putting the vunitout folder in the + # Note that this is not actually generated in the buck_out/ folder + # After playing with this a bit, putting the vunit-out folder in the # buck_out/ folder can be done (see info below) but is annoying since # we get a new buck_out/ each time the input changes meaning we have # to re-compile everything. We *could* attempt to do more here in buck @@ -125,7 +125,7 @@ def _hdl_unit_impl(ctx: AnalysisContext) -> list[Provider]: cmd.add("--output", out_run_py.as_output()) if ctx.attrs.simulator: cmd.add("--simulator", ctx.attrs.simulator) - ctx.actions.run(cmd, category="vunit") + ctx.actions.run(cmd, category="vunit", local_only = True, allow_cache_upload=True) # Left here as an example of how to put the vunit_out # folder into buck_out. This turns out to be a bit annoying diff --git a/tools/rdl.bzl b/tools/rdl.bzl index 017d48fb..89540101 100644 --- a/tools/rdl.bzl +++ b/tools/rdl.bzl @@ -84,6 +84,8 @@ def _rdl_file_impl(ctx): ctx.actions.run( rdl_out_gen, category="rdl", + local_only = True, + allow_cache_upload=True, ) # Build TSets for the generated VHDL files as a list of VHDLFileInfo diff --git a/tools/replatform/defs.bzl b/tools/replatform/defs.bzl new file mode 100644 index 00000000..ca6710bb --- /dev/null +++ b/tools/replatform/defs.bzl @@ -0,0 +1,64 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# +# This source code is licensed under both the MIT license found in the +# LICENSE-MIT file in the root directory of this source tree and the Apache +# License, Version 2.0 found in the LICENSE-APACHE file in the root directory +# of this source tree. + +def _execution_platform_impl(ctx: AnalysisContext) -> list[Provider]: + constraints = dict() + constraints.update(ctx.attrs.cpu_configuration[ConfigurationInfo].constraints) + constraints.update(ctx.attrs.os_configuration[ConfigurationInfo].constraints) + cfg = ConfigurationInfo(constraints = constraints, values = {}) + + name = ctx.label.raw_target() + platform = ExecutionPlatformInfo( + label = name, + configuration = cfg, + executor_config = CommandExecutorConfig( + local_enabled = True, + remote_enabled = False, + use_windows_path_separators = ctx.attrs.use_windows_path_separators, + ), + ) + + return [ + DefaultInfo(), + platform, + PlatformInfo(label = str(name), configuration = cfg), + ExecutionPlatformRegistrationInfo(platforms = [platform]), + ] + +execution_platform = rule( + impl = _execution_platform_impl, + attrs = { + "cpu_configuration": attrs.dep(providers = [ConfigurationInfo]), + "os_configuration": attrs.dep(providers = [ConfigurationInfo]), + "use_windows_path_separators": attrs.bool(), + }, +) + +def _host_cpu_configuration() -> str: + arch = host_info().arch + if arch.is_aarch64: + return "prelude//cpu:arm64" + elif arch.is_arm: + return "prelude//cpu:arm32" + elif arch.is_i386: + return "prelude//cpu:x86_32" + else: + return "prelude//cpu:x86_64" + +def _host_os_configuration() -> str: + os = host_info().os + if os.is_macos: + return "prelude//os:macos" + elif os.is_windows: + return "prelude//os:windows" + else: + return "prelude//os:linux" + +host_configuration = struct( + cpu = _host_cpu_configuration(), + os = _host_os_configuration(), +) diff --git a/tools/vivado.bzl b/tools/vivado.bzl index f53ff584..09bf602c 100644 --- a/tools/vivado.bzl +++ b/tools/vivado.bzl @@ -22,34 +22,77 @@ load( ":compress.bzl", "compress_bitstream", ) -VivadoConstraintInfo = provider( +VivadoIPInfo = provider( fields={ - "srcs": provider_field(Artifact), + "checkpoint": provider_field(Artifact), + "xci": provider_field(Artifact), + "name": provider_field(str, default=""), } ) -VivadoCheckpointInfo = provider( - fields={ - "checkpoint": provider_field(Artifact), - "name":provider_field(str, default=""), + +def _vivado_ip(ctx): + """ We're doing some machinations here to try to get a checkpoint and xci file generated by Vivado that we can cache + Vivado's a bit annoying in that it just generates piles of files and you can't strictly specify them. What we want + here is the checkpoint (.dcp) and the xci file so we can read those into the project later. When using read_ip, + Vivado should figure out a checkpoint exists and not rebuild it if it doesn't need to do so. + """ + module_name = ctx.attrs.module_name + tcl = ctx.attrs.tcl + part = ctx.attrs.part + + dir_name = "{}_ip/{}".format(module_name, module_name) + + # generate a tcl file that sets up an IP compile. + # this wraps the user's script to get outputs we can use. + vivado_ip_tcl = ctx.actions.declare_output("{}.tcl".format(module_name)) + py_ip_tcl_gen = ctx.attrs._vivado_ip[RunInfo] + cmd = cmd_args() + cmd.add(py_ip_tcl_gen) + cmd.add("--output", vivado_ip_tcl.as_output()) + ctx.actions.run(cmd, category="vivado_ip_tcl_{}_gen".format(module_name), allow_cache_upload=True) + + # Now we have a tcl file that we can run in vivado to generate the IP. + # Make buck2 expect a checkpoint file and an xci file in a subdirectory of our choosing. + checkpoint = ctx.actions.declare_output(dir_name, "{}.dcp".format(module_name)) + xci = ctx.actions.declare_output(dir_name, "{}.xci".format(module_name)) + # Run the vivado command + # Since the tcl above will be the same all the time, we need this action to be re-run + # if the inputs change. + cmd = cmd_args(hidden=[tcl, module_name, part]) - }) + cmd.add(ctx.attrs._toolchain[RunInfo]) + cmd.add("-mode", "batch") + cmd.add("-source", vivado_ip_tcl) + # This is a bit of a hack. Vivado is going to generate these (and more!) regardless of what we + # specify here but this makes buck2 happy since we're "using" the outputs. + cmd.add("-tclargs", module_name, checkpoint.as_output(), xci.as_output(), tcl, part) + ctx.actions.run(cmd, category="vivado_ip_gen", allow_cache_upload=True) -def _vivado_constraint_impl(ctx): - # No dep management to worry about so just collect the things - # and return a ConstraintProvider - return VivadoConstraintInfo( - srcs=ctx.attrs.srcs - ) + return [ + VivadoIPInfo(checkpoint=checkpoint, xci=xci, name=module_name), + DefaultInfo(default_output=xci), + ] -vivado_constraint = rule( - impl=_vivado_constraint_impl, +vivado_ip = rule( + impl=_vivado_ip, attrs={ - "srcs": attrs.list(attrs.source(doc="Expected one or more XDC sources")), + "module_name": attrs.string(), + "tcl": attrs.source(doc="TCL file to generate the IP. expected args are module_name arg[0], output_dir arg[1]"), + "part": attrs.string(doc="Vivado-compatible FPGA string"), + "_vivado_ip": attrs.exec_dep( + doc="Generate a Vivado ip generation tcl for this project", + default="root//tools/vivado_ip:vivado_ip", + ), + "_toolchain": attrs.toolchain_dep( + doc="Vivado", + default="toolchains//:vivado", + ), } ) + def _vivado_bitstream(ctx): synth = synthesize(ctx) opt = optimize(ctx, synth) @@ -88,6 +131,7 @@ def synthesize(ctx): # Get list of all sources from the dep tree via the tset in HDLFileInfo source_files_tset = ctx.attrs.top[HDLFileInfo].set_all source_files = source_files_tset.project_as_json("json", ordering="postorder") + ip_xci = [x[VivadoIPInfo].xci for x in ctx.attrs.ip if x.get(VivadoIPInfo)] out_json = { "flow": "synthesis", @@ -95,6 +139,7 @@ def synthesize(ctx): "max_threads": 8, "synth_args": "", "sources": source_files, + "ip": ip_xci, "constraints": constraints, "top_name": ctx.attrs.top_entity_name, "pre_synth_tcl_files": ctx.attrs.pre_synth_tcl_files, @@ -147,7 +192,7 @@ def synthesize(ctx): # Run vivado - ctx.actions.run(vivado, category="vivado_{}".format(flow)) + ctx.actions.run(vivado, category="vivado_{}".format(flow), allow_cache_upload=True) providers.append(DefaultInfo(default_output=checkpoint)) return providers @@ -182,7 +227,7 @@ def optimize(ctx, input_checkpoint): # because we're using the inputs to generate a tcl that *just* lists them, # irrespective of their content, we make the checkpoint a hidden input - # so that if it changesthis step is re-run rather than just relying + # so that if it changes, this step is re-run rather than just relying # on cache. We need this step to run if the input file content, or # constraint file content changes @@ -198,7 +243,7 @@ def optimize(ctx, input_checkpoint): vivado.add(debug_probes.as_output()) - ctx.actions.run(vivado, category="vivado_{}".format(flow)) + ctx.actions.run(vivado, category="vivado_{}".format(flow), allow_cache_upload=True) providers.append(DefaultInfo(default_output=out_checkpoint)) return providers @@ -236,7 +281,7 @@ def place(ctx, input_checkpoint, optimize=False): utilization_report.as_output() ) - ctx.actions.run(vivado, category="vivado_{}".format(flow)) + ctx.actions.run(vivado, category="vivado_{}".format(flow), allow_cache_upload=True) providers.append(DefaultInfo(default_output=out_checkpoint)) return providers @@ -281,7 +326,7 @@ def route(ctx, input_checkpoint): ) - ctx.actions.run(vivado, category="vivado_{}".format(flow)) + ctx.actions.run(vivado, category="vivado_{}".format(flow), allow_cache_upload=True) providers.append(DefaultInfo(default_output=out_checkpoint)) return providers @@ -305,7 +350,7 @@ def bitstream(ctx, input_checkpoint): # because we're using the inputs to generate a tcl that *just* lists them, # irrespective of their content, we make the checkpoint a hidden input - # so that if it changesthis step is re-run rather than just relying + # so that if it changes this step is re-run rather than just relying # on cache. We need this step to run if the input file content, or # constraint file content changes @@ -316,7 +361,7 @@ def bitstream(ctx, input_checkpoint): bitstream_bin.as_output(), ) - ctx.actions.run(vivado, category="vivado_{}".format(flow)) + ctx.actions.run(vivado, category="vivado_{}".format(flow), allow_cache_upload=True) providers.append(DefaultInfo(default_output=bitstream_bin)) return providers @@ -332,7 +377,7 @@ def _vivado_tcl_gen_common(ctx, flow, json): cmd.add("--input", in_json_file) cmd.add("--output", vivado_flow_tcl.as_output()) - ctx.actions.run(cmd, category="vivado_tcl_{}_gen".format(flow)) + ctx.actions.run(cmd, category="vivado_tcl_{}_gen".format(flow), allow_cache_upload=True) return vivado_flow_tcl, in_json_file @@ -361,6 +406,13 @@ vivado_bitstream = rule( attrs={ "top_entity_name": attrs.string(), "top": attrs.dep(doc="Expected top HDL unit"), + "ip": attrs.list( + attrs.dep( + doc="Vivado IPs to include in the project. These are generated by vivado_ip", + providers=[VivadoIPInfo], + ), + default=[], + ), "part": attrs.string(doc="Vivado-compatible FPGA string"), "constraints": attrs.list(attrs.source(doc="Part constraint files"), default=[]), "pre_synth_tcl_files": attrs.list(attrs.source(doc="TCL files for project to source for things like ip"), default=[]), diff --git a/tools/vivado_gen/templates/synth.jinja2 b/tools/vivado_gen/templates/synth.jinja2 index 8349c1c1..c385cf5b 100644 --- a/tools/vivado_gen/templates/synth.jinja2 +++ b/tools/vivado_gen/templates/synth.jinja2 @@ -20,6 +20,11 @@ set_part {{project.part}} # Turn inferred latch warnings into errors +# Load any generated IPs +{% for ip in project.ip %} +read_ip {{ip.absolute().as_posix()}} +{% endfor %} + # Load the sources {% for source in project.sources %} {% set suffix = source.path.suffix %} @@ -35,6 +40,7 @@ read_ip {{source.path.absolute().as_posix()}} {% endif %} {% endfor %} + ## source pre-synth user tcl here {% for file in project.pre_synth_tcl_files %} source {{file.absolute().as_posix()}} diff --git a/tools/vivado_gen/vivado_gen.py b/tools/vivado_gen/vivado_gen.py index fc04e4cb..ad0b4d41 100644 --- a/tools/vivado_gen/vivado_gen.py +++ b/tools/vivado_gen/vivado_gen.py @@ -34,6 +34,7 @@ def __init__(self, pre_synth_tcl_files: list, post_synth_tcl_files: list, sources: List[Source], + ip: list, synth_args: List[str], debug_probes: str, report_file: str, @@ -48,6 +49,7 @@ def __init__(self, self.post_synth_tcl_files = [Path(x) for x in post_synth_tcl_files] self.debug_probes = debug_probes self.sources = sources + self.ip = ip self.synth_args = synth_args self.report_file = report_file self.max_threads = max_threads @@ -56,13 +58,13 @@ def __init__(self, @classmethod def from_dict(cls, inputs): srcs = inputs.get("sources", []) - sources = [] + filtered_sources = [] for file in srcs: # Drop any non-synth files here if not file.get("is_synth"): print("Dropping non-synth file {}".format(file)) continue - sources.append(Source(Path(file.get("artifact")), file.get("library"), file.get("standard"))) + filtered_sources.append(Source(Path(file.get("artifact")), file.get("library"), file.get("standard"))) return cls( flow=inputs.get("flow"), @@ -72,7 +74,8 @@ def from_dict(cls, inputs): pre_synth_tcl_files=inputs.get("pre_synth_tcl_files", []), post_synth_tcl_files=inputs.get("post_synth_tcl_files", []), debug_probes=inputs.get("debug_probes", ""), - sources=sources, + sources=filtered_sources, + ip=[Path(x) for x in inputs.get("ip", [])], synth_args=inputs.get("synth_args"), report_file=inputs.get("report_file"), max_threads=inputs.get("max_threads"), diff --git a/tools/vivado_ip/BUCK b/tools/vivado_ip/BUCK new file mode 100644 index 00000000..f7a6aaa4 --- /dev/null +++ b/tools/vivado_ip/BUCK @@ -0,0 +1,13 @@ +python_binary( + name = 'vivado_ip', + main = 'vivado_ip.py', + deps = [':vivado_ip_lib'], + visibility = ["PUBLIC"], +) + +python_library( + name = 'vivado_ip_lib', + srcs = glob(["*.py"]), + base_module = "vivado_ip", + resources = glob(["templates/*.jinja2"]) +) \ No newline at end of file diff --git a/tools/vivado_ip/templates/ip_gen.jinja2 b/tools/vivado_ip/templates/ip_gen.jinja2 new file mode 100644 index 00000000..ff3e6bc5 --- /dev/null +++ b/tools/vivado_ip/templates/ip_gen.jinja2 @@ -0,0 +1,32 @@ +# Vivado. +# +# Expects 5 arguments: +# [0]: ip module name +# [1]: expected xci name # We have to pass these in even though we can't specify them to vivado for buck2 as these are "outputs" +# [2]: expected checkpoint name +# [3]: user's tcl file to run +# [4]: target part (if blank use default) + +# Get tclargs here +set name [lindex $argv 0] +set part [lindex $argv 4] +# Need to go up 2 levels due to how vivado generates stuff +set dir [file dirname [file dirname [lindex $argv 1]]] +set user_tcl [lindex $argv 3] + +# Vivado won't generate into the same folder, it continues appending _ to the end of the folder name so we blow away the folder +# here. +file delete -force [file dirname [lindex $argv 1]] + +# This is needed to generate IP +if {${part} eq "" } { + create_project -in_memory +} else { + create_project -in_memory -part $part +} + +# Clear out argv and change them to what our user's want +# Expects users expect only 2 arguments, the module name and the output directory: +set argv [list $name $dir] +# Run the IP generation TCL +source $user_tcl \ No newline at end of file diff --git a/tools/vivado_ip/vivado_ip.py b/tools/vivado_ip/vivado_ip.py new file mode 100644 index 00000000..e2b75263 --- /dev/null +++ b/tools/vivado_ip/vivado_ip.py @@ -0,0 +1,37 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. +# +# Copyright 2024 Oxide Computer Company + +import argparse +import json +from pathlib import Path +from jinja2 import Environment, PackageLoader + +from typing import List + +parser = argparse.ArgumentParser() +parser.add_argument("--output", dest="output", help="Explicit output list") + +args = parser.parse_args() + +def main(): + + # Build jinja env + env = Environment( + loader=PackageLoader("vivado_ip"), + lstrip_blocks=True, + trim_blocks=True, + ) + template = env.get_template("ip_gen.jinja2") + + # Now render the template and write it out + content = template.render( + ) + with open(args.output, mode="w", encoding="utf-8") as message: + message.write(content) + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/tools/yosys.bzl b/tools/yosys.bzl index 213b3283..9ea788f1 100644 --- a/tools/yosys.bzl +++ b/tools/yosys.bzl @@ -74,7 +74,7 @@ def yosys_vhdl_synth(ctx): cmd.add("--output", yosys_py.as_output()) - ctx.actions.run(cmd, category="yosys_synth_gen") + ctx.actions.run(cmd, category="yosys_synth_gen", local_only = True, allow_cache_upload=True) yosys_synth_log = ctx.actions.declare_output("synth.log") yosys_ghdl_warns = ctx.actions.declare_output("ghdl_stderr.log") @@ -86,7 +86,7 @@ def yosys_vhdl_synth(ctx): yosys_synth_cmd.add("--ghdl_stderr", yosys_ghdl_warns.as_output()) - ctx.actions.run(yosys_synth_cmd, category="yosys_run") + ctx.actions.run(yosys_synth_cmd, category="yosys_run", local_only = True,allow_cache_upload=True) providers.append(DefaultInfo(default_output=yosys_json)) return providers @@ -109,7 +109,7 @@ def ice40_nextpnr(ctx, yoys_providers): for nextpnr_arg in ctx.attrs.nextpnr_args: cmd.add(nextpnr_arg) - ctx.actions.run(cmd, category="next_pnr") + ctx.actions.run(cmd, category="next_pnr", local_only = True,allow_cache_upload=True) providers.append(DefaultInfo(default_output=asc)) return providers @@ -129,7 +129,7 @@ def icepack(ctx, next_pnr_providers): cmd.add(asc) cmd.add( bit.as_output()) - ctx.actions.run(cmd, category="icepak") + ctx.actions.run(cmd, category="icepak", local_only = True, allow_cache_upload=True) providers.append(DefaultInfo(default_output=bit)) return providers