diff --git a/xls/eco/codegen_scheduled_main.cc b/xls/eco/codegen_scheduled_main.cc new file mode 100644 index 0000000000..d74a8e0102 --- /dev/null +++ b/xls/eco/codegen_scheduled_main.cc @@ -0,0 +1,167 @@ +// Copyright 2024 The XLS Authors +// +// 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. +#include + +#include +#include // NOLINT +#include +#include +#include +#include +#include +#include + +#include "absl/flags/flag.h" +#include "absl/log/check.h" +#include "absl/log/log.h" +#include "absl/status/status.h" +#include "absl/strings/str_format.h" +#include "xls/codegen/module_signature.h" +#include "xls/common/exit_status.h" +#include "xls/common/file/filesystem.h" +#include "xls/common/init_xls.h" +#include "xls/common/status/ret_check.h" +#include "xls/common/status/status_macros.h" +#include "xls/dev_tools/tool_timeout.h" +#include "xls/ir/function_base.h" +#include "xls/ir/ir_parser.h" +#include "xls/ir/verifier.h" +#include "xls/scheduling/pipeline_schedule.h" +#include "xls/scheduling/pipeline_schedule.pb.h" +#include "xls/tools/codegen.h" +#include "xls/tools/codegen_flags.h" +#include "xls/tools/codegen_flags.pb.h" +#include "xls/tools/scheduling_options_flags.h" +#include "xls/tools/scheduling_options_flags.pb.h" + +static constexpr std::string_view kUsage = R"( +Generates Verilog RTL from a given IR file and a schedule proto. Writes a Verilog file and a module +signature describing the module interface to a specified location. Example +invocations: + +Emit combinational module: + codegen_main --generator=combinational --output_verilog_path=DIR IR_FILE + +Emit a feed-forward pipelined module: + codegen_main --generator=pipeline \ + --clock_period_ps=500 \ + --pipeline_stages=7 \ + --input_schedule_path=SCHEDULE_FILE \ + IR_FILE +)"; +ABSL_FLAG(std::string, input_schedule_path, "", + "Path to the IR file to patch."); // NOLINT +namespace xls { +namespace { +absl::Status RealMain(std::string_view ir_path) { + auto timeout = StartTimeoutTimer(); + if (ir_path == "-") { + ir_path = "/dev/stdin"; + } + XLS_ASSIGN_OR_RETURN(std::string ir_contents, GetFileContents(ir_path)); + XLS_ASSIGN_OR_RETURN(std::unique_ptr p, + Parser::ParsePackage(ir_contents, ir_path)); + + XLS_ASSIGN_OR_RETURN(CodegenFlagsProto codegen_flags_proto, + GetCodegenFlags()); + if (!codegen_flags_proto.top().empty()) { + XLS_RETURN_IF_ERROR(p->SetTopByName(codegen_flags_proto.top())); + } + + XLS_RET_CHECK(p->GetTop().has_value()) + << "Package " << p->name() << " needs a top function/proc."; + auto main = [&p]() -> FunctionBase* { return p->GetTop().value(); }; + + XLS_ASSIGN_OR_RETURN( + SchedulingOptionsFlagsProto scheduling_options_flags_proto, + GetSchedulingOptionsFlagsProto()); + XLS_ASSIGN_OR_RETURN(PackagePipelineSchedulesProto schedule_proto, + ParseTextProtoFile( + absl::GetFlag(FLAGS_input_schedule_path))); + XLS_ASSIGN_OR_RETURN( + PipelineScheduleOrGroup s, + PipelineSchedule::FromProto(p->GetTop().value(), schedule_proto)); + XLS_ASSIGN_OR_RETURN(CodegenResult r, + Codegen(p.get(), scheduling_options_flags_proto, + codegen_flags_proto, false, &s, nullptr)); + verilog::ModuleGeneratorResult result = r.module_generator_result; + std::optional schedule = + r.package_pipeline_schedules_proto; + + if (!absl::GetFlag(FLAGS_output_schedule_ir_path).empty()) { + XLS_RETURN_IF_ERROR( + SetFileContents(absl::GetFlag(FLAGS_output_schedule_ir_path), + main()->package()->DumpIr())); + } + + if (!absl::GetFlag(FLAGS_output_schedule_path).empty()) { + if (schedule.has_value()) { + XLS_RETURN_IF_ERROR(SetTextProtoFile( + absl::GetFlag(FLAGS_output_schedule_path), schedule.value())); + } else { + XLS_RETURN_IF_ERROR( + SetFileContents(absl::GetFlag(FLAGS_output_schedule_path), "")); + } + } + + if (!absl::GetFlag(FLAGS_output_block_ir_path).empty()) { + QCHECK_GE(p->blocks().size(), 1) << "There should be at least one block " + "in the package after generating " + "module text."; + XLS_RETURN_IF_ERROR(SetFileContents( + absl::GetFlag(FLAGS_output_block_ir_path), p->DumpIr())); + } + + if (!absl::GetFlag(FLAGS_output_signature_path).empty()) { + XLS_RETURN_IF_ERROR(SetTextProtoFile( + absl::GetFlag(FLAGS_output_signature_path), result.signature.proto())); + } + + const std::string& verilog_path = absl::GetFlag(FLAGS_output_verilog_path); + if (!verilog_path.empty()) { + for (int64_t i = 0; i < result.verilog_line_map.mapping_size(); ++i) { + result.verilog_line_map.mutable_mapping(i)->set_verilog_file( + verilog_path); + } + } + + const std::string& verilog_line_map_path = + absl::GetFlag(FLAGS_output_verilog_line_map_path); + if (!verilog_line_map_path.empty()) { + XLS_RETURN_IF_ERROR( + SetTextProtoFile(verilog_line_map_path, result.verilog_line_map)); + } + + if (verilog_path.empty()) { + std::cout << result.verilog_text; + } else { + XLS_RETURN_IF_ERROR(SetFileContents(verilog_path, result.verilog_text)); + } + return absl::OkStatus(); +} + +} // namespace +} // namespace xls + +int main(int argc, char** argv) { + std::vector positional_arguments = + xls::InitXls(kUsage, argc, argv); + + if (positional_arguments.size() != 1) { + LOG(QFATAL) << absl::StreamFormat("Expected invocation: %s IR_FILE", + argv[0]); + } + std::string_view ir_path = positional_arguments[0]; + return xls::ExitStatus(xls::RealMain(ir_path)); +} diff --git a/xls/eco/patch_ir.cc b/xls/eco/patch_ir.cc new file mode 100644 index 0000000000..36028ce229 --- /dev/null +++ b/xls/eco/patch_ir.cc @@ -0,0 +1,526 @@ +// Copyright 2020 The XLS Authors +// +// 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. +#include "xls/eco/patch_ir.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "absl/base/attributes.h" +#include "absl/flags/flag.h" +#include "absl/status/status.h" +#include "absl/status/statusor.h" +#include "absl/types/span.h" +#include "xls/common/file/filesystem.h" +#include "xls/common/status/status_macros.h" +#include "xls/eco/ir_patch.pb.h" +#include "xls/estimators/delay_model/delay_estimator.h" +#include "xls/ir/bits.h" +#include "xls/ir/function_base.h" +#include "xls/ir/node.h" +#include "xls/ir/node_util.h" +#include "xls/ir/nodes.h" +#include "xls/ir/op.h" +#include "xls/ir/package.h" +#include "xls/ir/source_location.h" +#include "xls/ir/type.h" +#include "xls/ir/value.h" +#include "xls/ir/value_utils.h" +#include "xls/ir/xls_type.pb.h" +#include "xls/ir/xls_value.pb.h" +#include "xls/scheduling/pipeline_schedule.h" +#include "xls/scheduling/run_pipeline_schedule.h" +#include "xls/scheduling/scheduling_options.h" +#include "xls/tools/codegen_flags.h" +#include "xls/tools/scheduling_options_flags.h" + +namespace xls { + +PatchIr::PatchIr(FunctionBase* function_base, xls_eco::IrPatchProto& patch) + : patch_(patch), function_base_(function_base), schedule_(std::nullopt) { + std::copy(patch_.edit_paths().begin(), patch_.edit_paths().end(), + std::back_inserter(sorted_edit_paths_)); + std::sort(sorted_edit_paths_.begin(), sorted_edit_paths_.end(), + [this](const xls_eco::EditPathProto& lhs, + const xls_eco::EditPathProto& rhs) { + return this->CompareEditPaths(lhs, rhs); + }); + package_ = function_base_->package(); +} +absl::StatusOr> PatchIr::MakeDummyNodes( + absl::Span types) { + std::vector dummy_nodes; + for (Type* type : types) { + XLS_ASSIGN_OR_RETURN(Node * dummy_node, MakeDummyNode(type)); + dummy_nodes.push_back(dummy_node); + } + return dummy_nodes; +} +absl::StatusOr PatchIr::MakeDummyNode(Type* type) { + XLS_ASSIGN_OR_RETURN(Node * dummy_node, + function_base_->MakeNodeWithName( + SourceInfo(), ZeroOfType(type), "Dummy")); + return dummy_node; +} +absl::Status PatchIr::UpdateNodeMaps(Node* n, absl::Span dummy_operands, + std::string_view node_name) { + auto& dummy_nodes = dummy_nodes_map_[n]; + dummy_nodes.insert(dummy_nodes.begin(), dummy_operands.begin(), + dummy_operands.end()); + patch_to_ir_node_map_[node_name] = n->GetName(); + return absl::OkStatus(); +} +absl::Status PatchIr::CleanupDummyNodes(Node* node) { + auto& dummy_nodes = dummy_nodes_map_[node]; + for (auto it = dummy_nodes.begin(); it != dummy_nodes.end();) { + Node* dummy_node = *it; + XLS_RETURN_IF_ERROR(function_base_->RemoveNode(dummy_node)); + it = dummy_nodes.erase(it); + } + return absl::OkStatus(); +} + +absl::StatusOr PatchIr::GetProtoBitCount(const TypeProto& type) { + XLS_ASSIGN_OR_RETURN(Type * t, package_->GetTypeFromProto(type)); + XLS_ASSIGN_OR_RETURN(BitsType * b, t->AsBits()); + return b->bit_count(); +} + +absl::Status PatchIr::PatchContainsNode(std::string_view node_name) { + if (patch_to_ir_node_map_.find(node_name) != patch_to_ir_node_map_.end()) + return absl::OkStatus(); + return absl::NotFoundError("Patch does not contain the node."); +} + +absl::Status PatchIr::ApplyPatch() { + // XLS_RETURN_IF_ERROR(IsolateReturnNode()); + for (const xls_eco::EditPathProto& edit_path : sorted_edit_paths_) { + XLS_RETURN_IF_ERROR(ApplyPath(edit_path)); + } + // XLS_RETURN_IF_ERROR(RestoreReturnNode()); + XLS_RETURN_IF_ERROR(ValidatePatch()); + return absl::OkStatus(); +} + +absl::Status PatchIr::ApplyPath(const xls_eco::EditPathProto& edit_path) { + switch (edit_path.operation()) { + case xls_eco::Operation::DELETE: + XLS_RETURN_IF_ERROR(edit_path.has_node_edit_path() + ? ApplyDeletePath(edit_path.node_edit_path()) + : ApplyDeletePath(edit_path.edge_edit_path())); + break; + case xls_eco::Operation::INSERT: + XLS_RETURN_IF_ERROR(edit_path.has_node_edit_path() + ? ApplyInsertPath(edit_path.node_edit_path()) + : ApplyInsertPath(edit_path.edge_edit_path())); + break; + case xls_eco::Operation::UPDATE: + XLS_RETURN_IF_ERROR(edit_path.has_node_edit_path() + ? ApplyUpdatePath(edit_path.node_edit_path()) + : ApplyUpdatePath(edit_path.edge_edit_path())); + break; + default: + return absl::InvalidArgumentError("Invalid operation"); + } + return absl::OkStatus(); +} +absl::Status PatchIr::ApplyDeletePath( + const xls_eco::NodeEditPathProto& node_delete) { + XLS_ASSIGN_OR_RETURN(Node * n, + function_base_->GetNode(node_delete.node().name())); + XLS_RETURN_IF_ERROR(function_base_->RemoveNode(n)); + XLS_RETURN_IF_ERROR(CleanupDummyNodes(n)); + return absl::OkStatus(); +} +absl::Status PatchIr::ApplyDeletePath( + const xls_eco::EdgeEditPathProto& edge_delete) { + XLS_ASSIGN_OR_RETURN(Node * from_node, + function_base_->GetNode(edge_delete.edge().from_node())); + XLS_ASSIGN_OR_RETURN(Node * to_node, + function_base_->GetNode(edge_delete.edge().to_node())); + XLS_ASSIGN_OR_RETURN(Node * dummy_node, MakeDummyNode(from_node->GetType())); + dummy_nodes_map_[to_node].push_back(dummy_node); + XLS_RETURN_IF_ERROR(to_node->ReplaceOperandNumber(edge_delete.edge().index(), + dummy_node, false)); + return absl::OkStatus(); +} +absl::Status PatchIr::ApplyInsertPath( + const xls_eco::NodeEditPathProto& node_insert) { + const xls_eco::NodeProto& patch_node = node_insert.node(); + if (patch_to_ir_op_map_.find(patch_node.op()) == patch_to_ir_op_map_.end()) { + std::cerr << "Error! Unsupported operation: " << patch_node.op() << '\n'; + return absl::InvalidArgumentError("Unsupported operation"); + } + const Op op = patch_to_ir_op_map_.at(patch_node.op()); + std::vector operand_types = {}; + if (patch_node.operand_data_types_size() > 0) { + std::transform(patch_node.operand_data_types().begin(), + patch_node.operand_data_types().end(), + std::back_inserter(operand_types), + [&](const TypeProto& type) { + return package_->GetTypeFromProto(type).value(); + }); + } + XLS_ASSIGN_OR_RETURN(std::vector dummy_operands, + MakeDummyNodes(absl::MakeSpan(operand_types))); + Node* n = nullptr; + switch (op) { + case (Op::kLiteral): { + XLS_ASSIGN_OR_RETURN(Value v, + Value::FromProto(patch_node.unique_args(0).value())); + XLS_ASSIGN_OR_RETURN(n, + function_base_->MakeNode(SourceInfo(), v)); + XLS_RETURN_IF_ERROR( + UpdateNodeMaps(n, absl::MakeSpan(dummy_operands), patch_node.name())); + break; + } + case (Op::kSignExt): { + XLS_ASSIGN_OR_RETURN(n, + function_base_->MakeNode( + SourceInfo(), dummy_operands.front(), + patch_node.unique_args(0).new_bit_count(), op)); + XLS_RETURN_IF_ERROR( + UpdateNodeMaps(n, absl::MakeSpan(dummy_operands), patch_node.name())); + break; + } + case (Op::kBitSlice): { + XLS_ASSIGN_OR_RETURN(int64_t width, + GetProtoBitCount(patch_node.data_type())); + XLS_ASSIGN_OR_RETURN(n, function_base_->MakeNode( + SourceInfo(), dummy_operands.front(), + patch_node.unique_args(0).start(), width)); + XLS_RETURN_IF_ERROR( + UpdateNodeMaps(n, absl::MakeSpan(dummy_operands), patch_node.name())); + break; + } + case (Op::kTuple): { + XLS_ASSIGN_OR_RETURN( + n, function_base_->MakeNode(SourceInfo(), + absl::MakeSpan(dummy_operands))); + XLS_RETURN_IF_ERROR( + UpdateNodeMaps(n, absl::MakeSpan(dummy_operands), patch_node.name())); + break; + } + case (Op::kSMul): + ABSL_FALLTHROUGH_INTENDED; + case (Op::kUMul): { + XLS_ASSIGN_OR_RETURN(int64_t width, + GetProtoBitCount(patch_node.data_type())); + XLS_ASSIGN_OR_RETURN(n, function_base_->MakeNode( + SourceInfo(), dummy_operands.front(), + dummy_operands[1], width, op)); + XLS_RETURN_IF_ERROR( + UpdateNodeMaps(n, absl::MakeSpan(dummy_operands), patch_node.name())); + break; + } + case (Op::kAdd): + ABSL_FALLTHROUGH_INTENDED; + case (Op::kSub): + ABSL_FALLTHROUGH_INTENDED; + case (Op::kULe): + ABSL_FALLTHROUGH_INTENDED; + case (Op::kULt): + ABSL_FALLTHROUGH_INTENDED; + case (Op::kUGt): + ABSL_FALLTHROUGH_INTENDED; + case (Op::kNe): + ABSL_FALLTHROUGH_INTENDED; + case (Op::kEq): + ABSL_FALLTHROUGH_INTENDED; + case (Op::kShll): { + XLS_ASSIGN_OR_RETURN( + n, function_base_->MakeNode( + SourceInfo(), dummy_operands.front(), dummy_operands[1], op)); + XLS_RETURN_IF_ERROR( + UpdateNodeMaps(n, absl::MakeSpan(dummy_operands), patch_node.name())); + break; + } + case (Op::kConcat): { + XLS_ASSIGN_OR_RETURN( + n, function_base_->MakeNode(SourceInfo(), + absl::MakeSpan(dummy_operands))); + XLS_RETURN_IF_ERROR( + UpdateNodeMaps(n, absl::MakeSpan(dummy_operands), patch_node.name())); + break; + } + case (Op::kOrReduce): { + XLS_ASSIGN_OR_RETURN(n, function_base_->MakeNode( + SourceInfo(), dummy_operands.front(), op)); + XLS_RETURN_IF_ERROR( + UpdateNodeMaps(n, absl::MakeSpan(dummy_operands), patch_node.name())); + break; + } + case (Op::kNot): { + XLS_ASSIGN_OR_RETURN(n, function_base_->MakeNode( + SourceInfo(), dummy_operands.front(), op)); + XLS_RETURN_IF_ERROR( + UpdateNodeMaps(n, absl::MakeSpan(dummy_operands), patch_node.name())); + break; + } + case (Op::kOr): + ABSL_FALLTHROUGH_INTENDED; + case (Op::kNor): + ABSL_FALLTHROUGH_INTENDED; + case (Op::kNand): + ABSL_FALLTHROUGH_INTENDED; + case (Op::kAnd): { + XLS_ASSIGN_OR_RETURN( + n, function_base_->MakeNode( + SourceInfo(), absl::MakeSpan(dummy_operands), op)); + XLS_RETURN_IF_ERROR( + UpdateNodeMaps(n, absl::MakeSpan(dummy_operands), patch_node.name())); + break; + } + case (Op::kSel): { + std::vector cases; + std::optional default_value; + if (patch_node.unique_args_size() > 0 && + patch_node.unique_args(0).has_default_value()) { + default_value = dummy_operands.back(); + dummy_operands.pop_back(); + } + for (auto it = dummy_operands.begin() + 1; it != dummy_operands.end(); + ++it) { + cases.push_back(*it); + } + XLS_ASSIGN_OR_RETURN(n, function_base_->MakeNode