diff --git a/xls/codegen/testdata/assertions_comb_multiple_ifdef_guards.svtxt b/xls/codegen/testdata/assertions_comb_multiple_ifdef_guards.svtxt index f0424221c7..35ad1007ac 100644 --- a/xls/codegen/testdata/assertions_comb_multiple_ifdef_guards.svtxt +++ b/xls/codegen/testdata/assertions_comb_multiple_ifdef_guards.svtxt @@ -2,26 +2,45 @@ module assertions_top( input wire [31:0] y, output wire [31:0] out ); - wire [30:0] add_197; - wire ult_204; - wire [31:0] literal_202; - wire [30:0] add_211; - wire ult_208; - wire nand_213; - wire nand_214; - assign add_197 = y[31:1] + 31'h7fff_fffb; - assign ult_204 = y < 32'h0000_0014; - assign literal_202 = 32'h0000_0004; - assign add_211 = y[31:1] + 31'h0000_000f; - assign ult_208 = y < 32'h0000_000a; - assign nand_213 = ~(y > 32'h0000_0009 & ult_204 & {add_197, y[0]} > literal_202); - assign nand_214 = ~(ult_208 & y > literal_202); - assign out = ult_208 ? {add_211, y[0]} : y & {32{ult_204}}; + function automatic [30:0] priority_sel_31b_2way (input reg [1:0] sel, input reg [30:0] case0, input reg [30:0] case1, input reg [30:0] default_value); + begin + unique casez (sel) + 2'b?1: begin + priority_sel_31b_2way = case0; + end + 2'b10: begin + priority_sel_31b_2way = case1; + end + 2'b00: begin + priority_sel_31b_2way = default_value; + end + default: begin + // Propagate X + priority_sel_31b_2way = 'X; + end + endcase + end + endfunction + wire [30:0] add_189; + wire ult_196; + wire ult_197; + wire [31:0] literal_193; + wire [30:0] add_203; + wire nand_206; + wire nand_207; + assign add_189 = y[31:1] + 31'h7fff_fffb; + assign ult_196 = y < 32'h0000_0014; + assign ult_197 = y < 32'h0000_000a; + assign literal_193 = 32'h0000_0004; + assign add_203 = y[31:1] + 31'h0000_000f; + assign nand_206 = ~(ult_197 & y > literal_193); + assign nand_207 = ~(ult_196 & y > 32'h0000_0009 & {add_189, y[0]} > literal_193); + assign out = {priority_sel_31b_2way({ult_196, ult_197}, add_203, y[31:1], 31'h0000_0000), ult_196 & y[0]}; `ifdef ASSERT_ON `ifndef SYNTHESIS - y_ge_than_21: assert final ($isunknown(ult_204) || ult_204) else $fatal(0, "Assertion failure via fail! @ xls/examples/assertions/assertions.x:32:14-32:37"); - __assertions__main_0___itok__assertions__main___itok__assertions__main_0___itok__assertions__func_0__32_x_less_than_5: assert final ($isunknown(nand_213) || nand_213) else $fatal(0, "Assertion failure via assert! @ xls/examples/assertions/assertions.x:21:12-21:40"); - __assertions__main_0___itok__assertions__main___itok__assertions__main_1___itok__assertions__func_0__32_x_less_than_5: assert final ($isunknown(nand_214) || nand_214) else $fatal(0, "Assertion failure via assert! @ xls/examples/assertions/assertions.x:21:12-21:40"); + y_ge_than_21: assert final ($isunknown(ult_196) || ult_196) else $fatal(0, "Assertion failure via fail! @ xls/examples/assertions/assertions.x:32:14-32:37"); + __assertions__main_0___itok__assertions__main___itok__assertions__main_0___itok__assertions__func_0__32_x_less_than_5: assert final ($isunknown(nand_206) || nand_206) else $fatal(0, "Assertion failure via assert! @ xls/examples/assertions/assertions.x:21:12-21:40"); + __assertions__main_0___itok__assertions__main___itok__assertions__main_1___itok__assertions__func_0__32_x_less_than_5: assert final ($isunknown(nand_207) || nand_207) else $fatal(0, "Assertion failure via assert! @ xls/examples/assertions/assertions.x:21:12-21:40"); `endif // SYNTHESIS `endif // ASSERT_ON endmodule diff --git a/xls/codegen/testdata/assertions_comb_no_ifdef_guards.svtxt b/xls/codegen/testdata/assertions_comb_no_ifdef_guards.svtxt index b1d1bd7b3f..7c03a8dcad 100644 --- a/xls/codegen/testdata/assertions_comb_no_ifdef_guards.svtxt +++ b/xls/codegen/testdata/assertions_comb_no_ifdef_guards.svtxt @@ -2,22 +2,41 @@ module assertions_top( input wire [31:0] y, output wire [31:0] out ); - wire [30:0] add_197; - wire ult_204; - wire [31:0] literal_202; - wire [30:0] add_211; - wire ult_208; - wire nand_213; - wire nand_214; - assign add_197 = y[31:1] + 31'h7fff_fffb; - assign ult_204 = y < 32'h0000_0014; - assign literal_202 = 32'h0000_0004; - assign add_211 = y[31:1] + 31'h0000_000f; - assign ult_208 = y < 32'h0000_000a; - assign nand_213 = ~(y > 32'h0000_0009 & ult_204 & {add_197, y[0]} > literal_202); - assign nand_214 = ~(ult_208 & y > literal_202); - assign out = ult_208 ? {add_211, y[0]} : y & {32{ult_204}}; - y_ge_than_21: assert final ($isunknown(ult_204) || ult_204) else $fatal(0, "Assertion failure via fail! @ xls/examples/assertions/assertions.x:32:14-32:37"); - __assertions__main_0___itok__assertions__main___itok__assertions__main_0___itok__assertions__func_0__32_x_less_than_5: assert final ($isunknown(nand_213) || nand_213) else $fatal(0, "Assertion failure via assert! @ xls/examples/assertions/assertions.x:21:12-21:40"); - __assertions__main_0___itok__assertions__main___itok__assertions__main_1___itok__assertions__func_0__32_x_less_than_5: assert final ($isunknown(nand_214) || nand_214) else $fatal(0, "Assertion failure via assert! @ xls/examples/assertions/assertions.x:21:12-21:40"); + function automatic [30:0] priority_sel_31b_2way (input reg [1:0] sel, input reg [30:0] case0, input reg [30:0] case1, input reg [30:0] default_value); + begin + unique casez (sel) + 2'b?1: begin + priority_sel_31b_2way = case0; + end + 2'b10: begin + priority_sel_31b_2way = case1; + end + 2'b00: begin + priority_sel_31b_2way = default_value; + end + default: begin + // Propagate X + priority_sel_31b_2way = 'X; + end + endcase + end + endfunction + wire [30:0] add_189; + wire ult_196; + wire ult_197; + wire [31:0] literal_193; + wire [30:0] add_203; + wire nand_206; + wire nand_207; + assign add_189 = y[31:1] + 31'h7fff_fffb; + assign ult_196 = y < 32'h0000_0014; + assign ult_197 = y < 32'h0000_000a; + assign literal_193 = 32'h0000_0004; + assign add_203 = y[31:1] + 31'h0000_000f; + assign nand_206 = ~(ult_197 & y > literal_193); + assign nand_207 = ~(ult_196 & y > 32'h0000_0009 & {add_189, y[0]} > literal_193); + assign out = {priority_sel_31b_2way({ult_196, ult_197}, add_203, y[31:1], 31'h0000_0000), ult_196 & y[0]}; + y_ge_than_21: assert final ($isunknown(ult_196) || ult_196) else $fatal(0, "Assertion failure via fail! @ xls/examples/assertions/assertions.x:32:14-32:37"); + __assertions__main_0___itok__assertions__main___itok__assertions__main_0___itok__assertions__func_0__32_x_less_than_5: assert final ($isunknown(nand_206) || nand_206) else $fatal(0, "Assertion failure via assert! @ xls/examples/assertions/assertions.x:21:12-21:40"); + __assertions__main_0___itok__assertions__main___itok__assertions__main_1___itok__assertions__func_0__32_x_less_than_5: assert final ($isunknown(nand_207) || nand_207) else $fatal(0, "Assertion failure via assert! @ xls/examples/assertions/assertions.x:21:12-21:40"); endmodule diff --git a/xls/dslx/ir_convert/BUILD b/xls/dslx/ir_convert/BUILD index 7310450167..bd77f1b9df 100644 --- a/xls/dslx/ir_convert/BUILD +++ b/xls/dslx/ir_convert/BUILD @@ -353,6 +353,7 @@ cc_library( "//xls/ir:value_utils", "//xls/ir:verifier", "//xls/ir:xls_ir_interface_cc_proto", + "@com_google_absl//absl/algorithm:container", "@com_google_absl//absl/container:btree", "@com_google_absl//absl/container:flat_hash_map", "@com_google_absl//absl/container:flat_hash_set", diff --git a/xls/dslx/ir_convert/function_converter.cc b/xls/dslx/ir_convert/function_converter.cc index 5eef2ba90b..a2088d4b8f 100644 --- a/xls/dslx/ir_convert/function_converter.cc +++ b/xls/dslx/ir_convert/function_converter.cc @@ -26,6 +26,7 @@ #include #include +#include "absl/algorithm/container.h" #include "absl/container/btree_set.h" #include "absl/container/flat_hash_map.h" #include "absl/log/check.h" @@ -4129,7 +4130,79 @@ absl::Status FunctionConverter::HandleStatement(const Statement* node) { return absl::OkStatus(); } +absl::Status FunctionConverter::HandleConditionalChain( + const Conditional* node) { + XLS_RET_CHECK(std::holds_alternative(node->alternate())); + + std::vector tests; + std::vector results; + const Conditional* cond = node; + std::optional higher_priority_true = std::nullopt; + while (true) { + XLS_RETURN_IF_ERROR(Visit(cond->test())); + XLS_ASSIGN_OR_RETURN(BValue test, Use(cond->test())); + { + ScopedControlPredicate scp( + this, [&](const PredicateFun& orig_control_predicate) { + BValue activated = orig_control_predicate(); + CHECK_EQ(activated.GetType()->AsBitsOrDie()->bit_count(), 1); + if (higher_priority_true.has_value()) { + return function_builder_->And( + {activated, test, + function_builder_->Not(*higher_priority_true)}); + } else { + return function_builder_->And(activated, test); + } + }); + XLS_RETURN_IF_ERROR(Visit(cond->consequent())); + } + XLS_ASSIGN_OR_RETURN(BValue result, Use(cond->consequent())); + tests.push_back(test); + results.push_back(result); + + if (higher_priority_true.has_value()) { + higher_priority_true = function_builder_->Or(*higher_priority_true, test); + } else { + higher_priority_true = test; + } + + if (!std::holds_alternative(cond->alternate())) { + break; + } + cond = std::get(cond->alternate()); + }; + + // We finished the final conditional in the chain, so what's left in the + // alternate is what we return if none of the tests are true. + { + ScopedControlPredicate scp( + this, [&](const PredicateFun& orig_control_predicate) { + BValue activated = orig_control_predicate(); + CHECK_EQ(activated.GetType()->AsBitsOrDie()->bit_count(), 1); + return function_builder_->And( + activated, function_builder_->Not(*higher_priority_true)); + }); + XLS_RETURN_IF_ERROR(Visit(ToExprNode(cond->alternate()))); + } + XLS_ASSIGN_OR_RETURN(BValue default_result, + Use(ToExprNode(cond->alternate()))); + + // Reverse the tests, since priority-select goes in LSB-first order. + absl::c_reverse(tests); + Def(node, [&](const SourceInfo& loc) -> BValue { + BValue selector = function_builder_->Concat(tests, loc); + return function_builder_->PrioritySelect( + selector, /*cases=*/results, /*default_value=*/default_result, loc); + }); + return absl::OkStatus(); +} + absl::Status FunctionConverter::HandleConditional(const Conditional* node) { + if (std::holds_alternative(node->alternate())) { + // This is a chained conditional. + return HandleConditionalChain(node); + } + XLS_RETURN_IF_ERROR(Visit(node->test())); XLS_ASSIGN_OR_RETURN(BValue arg0, Use(node->test())); diff --git a/xls/dslx/ir_convert/function_converter.h b/xls/dslx/ir_convert/function_converter.h index 4f7ab34287..494cceff59 100644 --- a/xls/dslx/ir_convert/function_converter.h +++ b/xls/dslx/ir_convert/function_converter.h @@ -426,6 +426,9 @@ class FunctionConverter { absl::Status HandleConditional(const Conditional* node); absl::Status HandleTupleIndex(const TupleIndex* node); + // Handles a conditional chain (where the alternate is another conditional). + absl::Status HandleConditionalChain(const Conditional* node); + // Handles invocation of a user-defined function (UDF). absl::Status HandleUdfInvocation(const Invocation* node, xls::Function* f, std::vector args);