From 55ed05775c94441badf4f8c245ee0ba080a28abb Mon Sep 17 00:00:00 2001 From: Johan Euphrosine Date: Wed, 15 Apr 2026 22:32:58 -0700 Subject: [PATCH] xls/{dslx,ir,codegen}: add per-channel fifo wrapper configuration dslx: ```dslx #[channel(depth=2, fifo_wrapper="my_custom_fifo")] let (tx, rx) = chan("my_channel"); ``` verilog: ```verilog my_custom_fifo #( .Width(32), .Depth(2) ) my_channel_fifo ( // ... ); ``` PiperOrigin-RevId: 900514827 --- xls/codegen/verilog_conversion.cc | 9 ++++++-- xls/codegen/verilog_conversion_test.cc | 12 +++++++++++ xls/dslx/frontend/parser.cc | 10 +++++++-- xls/dslx/frontend/parser_test.cc | 16 ++++++++++++++ xls/ir/channel.cc | 15 +++++++++++-- xls/ir/channel.h | 10 ++++++--- xls/ir/channel.proto | 1 + xls/ir/channel_test.cc | 16 ++++++++++++++ xls/ir/instantiation.cc | 29 ++++++++++++++++++-------- xls/ir/ir_parser.cc | 11 +++++++++- 10 files changed, 110 insertions(+), 19 deletions(-) diff --git a/xls/codegen/verilog_conversion.cc b/xls/codegen/verilog_conversion.cc index 9f756a5b21..caa9b58cf4 100644 --- a/xls/codegen/verilog_conversion.cc +++ b/xls/codegen/verilog_conversion.cc @@ -1147,8 +1147,13 @@ class BlockGenerator { return a.port_name < b.port_name; }); - std::string_view wrapper_name = - have_data ? options_.fifo_module() : options_.nodata_fifo_module(); + std::string wrapper_name; + if (fifo_instantiation->fifo_config().fifo_wrapper().has_value()) { + wrapper_name = *fifo_instantiation->fifo_config().fifo_wrapper(); + } else { + wrapper_name = have_data ? options_.fifo_module() + : options_.nodata_fifo_module(); + } if (wrapper_name.empty()) { return absl::InvalidArgumentError(absl::StrFormat( "No FIFO module specified, but %sFIFO instantiation required.", diff --git a/xls/codegen/verilog_conversion_test.cc b/xls/codegen/verilog_conversion_test.cc index bcab651866..4947029410 100644 --- a/xls/codegen/verilog_conversion_test.cc +++ b/xls/codegen/verilog_conversion_test.cc @@ -1992,6 +1992,18 @@ TEST_P(VerilogConversionTest, MultiProcWithInternalNoDataFifo) { absl_testing::IsOkAndHolds(output_values)); } +TEST_P(VerilogConversionTest, MultiProcWithCustomFifoWrapper) { + FifoConfig fifo_config(/*depth=*/1, /*bypass=*/false, + /*register_push_outputs=*/false, + /*register_pop_outputs=*/false, + /*fifo_wrapper=*/"my_custom_fifo"); + XLS_ASSERT_OK_AND_ASSIGN(CodegenResult result, + MakeMultiProc(fifo_config, + /*data_width=*/32)); + + EXPECT_THAT(result.verilog_text, ::testing::HasSubstr("my_custom_fifo")); +} + TEST_P(VerilogConversionTest, MultiProcDirectConnect) { XLS_ASSERT_OK_AND_ASSIGN(CodegenResult result, MakeMultiProc(kDepth0Fifo.config, diff --git a/xls/dslx/frontend/parser.cc b/xls/dslx/frontend/parser.cc index 112d665497..8f1ada2db7 100644 --- a/xls/dslx/frontend/parser.cc +++ b/xls/dslx/frontend/parser.cc @@ -687,6 +687,7 @@ absl::StatusOr Parser::ParseExprAttribute(Bindings& bindings, std::optional register_pop_outputs; std::optional input_flop_kind; std::optional output_flop_kind; + std::optional fifo_wrapper; auto parse_kv_pair = [&]() -> absl::StatusOr { XLS_ASSIGN_OR_RETURN(Token key_tok, PopTokenOrError(TokenKind::kIdentifier)); @@ -778,6 +779,10 @@ absl::StatusOr Parser::ParseExprAttribute(Bindings& bindings, flop_kind.status().message(), value)); } output_flop_kind.emplace(*flop_kind); + } else if (key == "fifo_wrapper") { + XLS_ASSIGN_OR_RETURN(Token value_tok, + PopTokenOrError(TokenKind::kString)); + fifo_wrapper.emplace(value_tok.GetStringValue()); } else if (key == "strictness") { return ParseErrorStatus(key_tok.span(), "Channel strictness must be specified via " @@ -797,13 +802,14 @@ absl::StatusOr Parser::ParseExprAttribute(Bindings& bindings, XLS_RETURN_IF_ERROR(DropTokenOrError(TokenKind::kCBrack)); std::optional fifo_config; if (depth.has_value() || bypass.has_value() || - register_push_outputs.has_value() || register_pop_outputs.has_value()) { + register_push_outputs.has_value() || register_pop_outputs.has_value() || + fifo_wrapper.has_value()) { depth = depth.value_or(1); bypass = bypass.value_or(*depth == 0 ? true : false); register_push_outputs = register_push_outputs.value_or(false); register_pop_outputs = register_pop_outputs.value_or(false); fifo_config.emplace(*depth, *bypass, *register_push_outputs, - *register_pop_outputs); + *register_pop_outputs, fifo_wrapper); } ChannelConfig channel_config; if (fifo_config.has_value() || input_flop_kind.has_value() || diff --git a/xls/dslx/frontend/parser_test.cc b/xls/dslx/frontend/parser_test.cc index b545e03801..d89d829283 100644 --- a/xls/dslx/frontend/parser_test.cc +++ b/xls/dslx/frontend/parser_test.cc @@ -4380,6 +4380,22 @@ proc AProc { })"); } +TEST_F(ParserTest, ChannelAttributeFifoWrapper) { + RoundTrip(R"(#![feature(channel_attributes)] + +proc AProc { + a: chan out; + b: chan in; + config() { + let (a, b) = + #[channel(depth=1, bypass=false, register_push_outputs=false, register_pop_outputs=false, fifo_wrapper="custom_fifo")] + chan("foo"); + } + init {} + next(_: ()) {} +})"); +} + TEST_F(ParserTest, CannotUseOldDepthSyntaxWithChannelAttributes) { constexpr std::string_view kProgram = (R"(#![feature(channel_attributes)] diff --git a/xls/ir/channel.cc b/xls/ir/channel.cc index 273408980a..70eaa4af00 100644 --- a/xls/ir/channel.cc +++ b/xls/ir/channel.cc @@ -47,9 +47,13 @@ namespace xls { if (!proto.has_depth()) { return absl::InvalidArgumentError("FifoConfigProto.depth is required."); } + std::optional fifo_wrapper; + if (proto.has_fifo_wrapper()) { + fifo_wrapper = proto.fifo_wrapper(); + } return FifoConfig(proto.depth(), proto.bypass(), - proto.register_push_outputs(), - proto.register_pop_outputs()); + proto.register_push_outputs(), proto.register_pop_outputs(), + fifo_wrapper); } FifoConfigProto FifoConfig::ToProto(int64_t width) const { FifoConfigProto proto; @@ -58,6 +62,9 @@ FifoConfigProto FifoConfig::ToProto(int64_t width) const { proto.set_bypass(bypass_); proto.set_register_push_outputs(register_push_outputs_); proto.set_register_pop_outputs(register_pop_outputs_); + if (fifo_wrapper_.has_value()) { + proto.set_fifo_wrapper(*fifo_wrapper_); + } return proto; } @@ -106,6 +113,10 @@ std::vector> FifoConfig::GetDslxKwargs() {"register_push_outputs", register_push_outputs_ ? "true" : "false"}); kwargs.push_back( {"register_pop_outputs", register_pop_outputs_ ? "true" : "false"}); + if (fifo_wrapper_.has_value()) { + kwargs.push_back( + {"fifo_wrapper", absl::StrCat("\"", *fifo_wrapper_, "\"")}); + } return kwargs; } diff --git a/xls/ir/channel.h b/xls/ir/channel.h index c1d2dc25e5..d72d27d9a8 100644 --- a/xls/ir/channel.h +++ b/xls/ir/channel.h @@ -54,16 +54,19 @@ enum class ChannelKind : uint8_t { class FifoConfig { public: constexpr FifoConfig(int64_t depth, bool bypass, bool register_push_outputs, - bool register_pop_outputs) + bool register_pop_outputs, + std::optional fifo_wrapper = std::nullopt) : depth_(depth), bypass_(bypass), register_push_outputs_(register_push_outputs), - register_pop_outputs_(register_pop_outputs) {} + register_pop_outputs_(register_pop_outputs), + fifo_wrapper_(std::move(fifo_wrapper)) {} int64_t depth() const { return depth_; } bool bypass() const { return bypass_; } bool register_push_outputs() const { return register_push_outputs_; } bool register_pop_outputs() const { return register_pop_outputs_; } + std::optional fifo_wrapper() const { return fifo_wrapper_; } absl::Status Validate() const; bool operator==(const FifoConfig& other) const = default; @@ -84,7 +87,7 @@ class FifoConfig { friend H AbslHashValue(H h, const FifoConfig& config) { return H::combine(std::move(h), config.depth_, config.bypass_, config.register_push_outputs_, - config.register_pop_outputs_); + config.register_pop_outputs_, config.fifo_wrapper_); } private: @@ -92,6 +95,7 @@ class FifoConfig { bool bypass_; bool register_push_outputs_; bool register_pop_outputs_; + std::optional fifo_wrapper_; }; enum class FlopKind : int8_t { diff --git a/xls/ir/channel.proto b/xls/ir/channel.proto index d219a489a6..03d575ee70 100644 --- a/xls/ir/channel.proto +++ b/xls/ir/channel.proto @@ -22,6 +22,7 @@ message FifoConfigProto { optional bool bypass = 3; optional bool register_push_outputs = 4; optional bool register_pop_outputs = 5; + optional string fifo_wrapper = 6; } // The kinds of flops a channels input/output may have. diff --git a/xls/ir/channel_test.cc b/xls/ir/channel_test.cc index 065d7d9471..93ad066b10 100644 --- a/xls/ir/channel_test.cc +++ b/xls/ir/channel_test.cc @@ -188,6 +188,22 @@ TEST(ChannelTest, StreamingChannelWithFifoConfigSerializesFifoConfigCorrectly) { HasSubstr("register_pop_outputs=true"))); } +TEST(ChannelTest, StreamingChannelWithFifoWrapper) { + Package p("my_package"); + StreamingChannel ch( + "my_channel", 42, ChannelOps::kSendReceive, p.GetBitsType(32), {}, + ChannelConfig(FifoConfig(/*depth=*/123, /*bypass=*/true, + /*register_push_outputs=*/true, + /*register_pop_outputs=*/false, + /*fifo_wrapper=*/"custom_fifo")), + FlowControl::kNone, ChannelStrictness::kProvenMutuallyExclusive); + + EXPECT_EQ(ch.name(), "my_channel"); + EXPECT_EQ(ch.GetFifoDepth(), 123); + ASSERT_TRUE(ch.channel_config().fifo_config().has_value()); + EXPECT_EQ(ch.channel_config().fifo_config()->fifo_wrapper(), "custom_fifo"); +} + TEST(ChannelTest, StreamingToStringParses) { Package p("my_package"); std::vector initial_values = { diff --git a/xls/ir/instantiation.cc b/xls/ir/instantiation.cc index 882e4667a2..94b950993e 100644 --- a/xls/ir/instantiation.cc +++ b/xls/ir/instantiation.cc @@ -365,18 +365,29 @@ absl::StatusOr FifoInstantiation::GetOutputPort( } std::string FifoInstantiation::ToString() const { - std::string channel_str; + std::vector args; + args.push_back(absl::StrFormat("data_type=%s", data_type_->ToString())); + args.push_back(absl::StrFormat("depth=%d", fifo_config_.depth())); + args.push_back( + absl::StrFormat("bypass=%s", fifo_config_.bypass() ? "true" : "false")); + args.push_back( + absl::StrFormat("register_push_outputs=%s", + fifo_config_.register_push_outputs() ? "true" : "false")); + args.push_back( + absl::StrFormat("register_pop_outputs=%s", + fifo_config_.register_pop_outputs() ? "true" : "false")); + + if (fifo_config_.fifo_wrapper().has_value()) { + args.push_back( + absl::StrFormat("fifo_wrapper=%s", *fifo_config_.fifo_wrapper())); + } if (channel_name_.has_value()) { - channel_str = absl::StrFormat("channel=%s, ", *channel_name_); + args.push_back(absl::StrFormat("channel=%s", *channel_name_)); } + args.push_back("kind=fifo"); - return absl::StrFormat( - "instantiation %s(data_type=%s, depth=%d, bypass=%s, " - "register_push_outputs=%s, register_pop_outputs=%s, %skind=fifo)", - name(), data_type_->ToString(), fifo_config_.depth(), - fifo_config_.bypass() ? "true" : "false", - fifo_config_.register_push_outputs() ? "true" : "false", - fifo_config_.register_pop_outputs() ? "true" : "false", channel_str); + return absl::StrFormat("instantiation %s(%s)", name(), + absl::StrJoin(args, ", ")); } DelayLineInstantiation::DelayLineInstantiation( diff --git a/xls/ir/ir_parser.cc b/xls/ir/ir_parser.cc index 758bc57e18..e0b53f411b 100644 --- a/xls/ir/ir_parser.cc +++ b/xls/ir/ir_parser.cc @@ -1676,6 +1676,14 @@ absl::StatusOr Parser::ParseInstantiation(Block* block) { return absl::OkStatus(); }; + std::optional fifo_wrapper; + handlers["fifo_wrapper"] = [&]() -> absl::Status { + XLS_ASSIGN_OR_RETURN(Token wrapper_token, + scanner_.PopTokenOrError(LexicalTokenType::kIdent)); + fifo_wrapper = wrapper_token.value(); + return absl::OkStatus(); + }; + XLS_RETURN_IF_ERROR(ParseKeywordArguments(handlers, /*mandatory_keywords=*/{"kind"})); @@ -1752,7 +1760,8 @@ absl::StatusOr Parser::ParseInstantiation(Block* block) { FifoConfig(/*depth=*/*depth, /*bypass=*/*bypass, /*register_push_outputs=*/*register_push_outputs, - /*register_pop_outputs=*/*register_pop_outputs), + /*register_pop_outputs=*/*register_pop_outputs, + /*fifo_wrapper=*/fifo_wrapper), *data_type, channel); }