Skip to content

Commit 71eabe9

Browse files
Merge pull request #3702 from xlsynth:meheff/2026-01-18-conditional
PiperOrigin-RevId: 858774844
2 parents 7d688fc + 631d35a commit 71eabe9

File tree

6 files changed

+325
-1
lines changed

6 files changed

+325
-1
lines changed

xls/codegen/vast/vast.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2280,7 +2280,7 @@ using ModuleMember =
22802280
VerilogFunction*, // Function definition
22812281
Typedef*, Enum*, Cover*, ConcurrentAssertion*,
22822282
DeferredImmediateAssertion*, ModuleConditionalDirective*,
2283-
ModuleSection*,
2283+
ModuleSection*, Conditional*,
22842284
// Generate loop, can effectively generate more module members
22852285
// at elaboration time
22862286
GenerateLoop*, MacroStatement*>;

xls/codegen/vast/vast_test.cc

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2884,6 +2884,86 @@ TEST_P(VastTest, NestedGenerateLoop) {
28842884
endmodule)");
28852885
}
28862886

2887+
TEST_P(VastTest, ModuleScopeConditionalBasedOnParameterEquality) {
2888+
if (!UseSystemVerilog()) {
2889+
GTEST_SKIP();
2890+
}
2891+
VerilogFile f(GetFileType());
2892+
const SourceInfo si;
2893+
Module* m = f.AddModule("top", si);
2894+
2895+
// parameter A = 1; parameter B = 2;
2896+
ParameterRef* A = m->AddParameter("A", f.PlainLiteral(1, si), si);
2897+
ParameterRef* B = m->AddParameter("B", f.PlainLiteral(2, si), si);
2898+
2899+
// wire out;
2900+
XLS_ASSERT_OK_AND_ASSIGN(LogicRef * out,
2901+
m->AddWire("out", f.ScalarType(si), si));
2902+
(void)out; // referenced via inline verilog statements below.
2903+
2904+
// Generate-style conditional at module scope based on (A == B) / (A != B).
2905+
Conditional* c = m->Add<Conditional>(si, f.Equals(A, B, si));
2906+
c->consequent()->Add<ContinuousAssignment>(si, out, f.Literal(1, 1, si));
2907+
StatementBlock* else_block = c->AddAlternate(/*condition=*/nullptr);
2908+
else_block->Add<ContinuousAssignment>(si, out, f.Literal(0, 1, si));
2909+
2910+
EXPECT_EQ(m->Emit(nullptr),
2911+
R"(module top;
2912+
parameter A = 1;
2913+
parameter B = 2;
2914+
wire out;
2915+
if (A == B) begin
2916+
assign out = 1'h1;
2917+
end else begin
2918+
assign out = 1'h0;
2919+
end
2920+
endmodule)");
2921+
}
2922+
2923+
TEST_P(VastTest, GenerateLoopConditionalOnGenvarWithFallthrough) {
2924+
if (!UseSystemVerilog()) {
2925+
GTEST_SKIP();
2926+
}
2927+
VerilogFile f(GetFileType());
2928+
const SourceInfo si;
2929+
Module* m = f.AddModule("top", si);
2930+
2931+
XLS_ASSERT_OK_AND_ASSIGN(LogicRef * out,
2932+
m->AddWire("out", f.BitVectorType(3, si), si));
2933+
(void)out; // referenced via inline verilog statements below.
2934+
2935+
auto* loop = m->Add<GenerateLoop>(si, std::string("i"), f.PlainLiteral(0, si),
2936+
f.PlainLiteral(3, si), "g");
2937+
LogicRef* i = loop->genvar();
2938+
2939+
// Compare genvar to constants with else-if and fallthrough else.
2940+
Conditional* c =
2941+
loop->Add<Conditional>(si, f.Equals(i, f.PlainLiteral(0, si), si));
2942+
c->consequent()->Add<ContinuousAssignment>(si, f.Make<Index>(si, out, i),
2943+
f.Literal(0, 1, si));
2944+
StatementBlock* else_if =
2945+
c->AddAlternate(f.Equals(i, f.PlainLiteral(1, si), si));
2946+
else_if->Add<ContinuousAssignment>(si, f.Make<Index>(si, out, i),
2947+
f.Literal(1, 1, si));
2948+
StatementBlock* else_block = c->AddAlternate(/*condition=*/nullptr);
2949+
else_block->Add<ContinuousAssignment>(si, f.Make<Index>(si, out, i),
2950+
f.Make<XSentinel>(si, 1));
2951+
2952+
EXPECT_EQ(m->Emit(nullptr),
2953+
R"(module top;
2954+
wire [2:0] out;
2955+
for (genvar i = 0; i < 3; i = i + 1) begin : g
2956+
if (i == 0) begin
2957+
assign out[i] = 1'h0;
2958+
end else if (i == 1) begin
2959+
assign out[i] = 1'h1;
2960+
end else begin
2961+
assign out[i] = 1'dx;
2962+
end
2963+
end
2964+
endmodule)");
2965+
}
2966+
28872967
TEST_P(VastTest, ModuleParametersPortsNoIo) {
28882968
VerilogFile f(GetFileType());
28892969
Module* m = f.AddModule("top", SourceInfo());

xls/public/c_api_symbols.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,7 @@ xls_vast_generate_loop_add_always_comb
345345
xls_vast_generate_loop_add_always_ff
346346
xls_vast_generate_loop_add_blank_line
347347
xls_vast_generate_loop_add_comment
348+
xls_vast_generate_loop_add_conditional
348349
xls_vast_generate_loop_add_continuous_assignment
349350
xls_vast_generate_loop_add_generate_loop
350351
xls_vast_generate_loop_add_inline_verilog_statement
@@ -369,6 +370,7 @@ xls_vast_statement_block_add_blocking_assignment
369370
xls_vast_statement_block_add_case
370371
xls_vast_statement_block_add_comment_text
371372
xls_vast_statement_block_add_conditional
373+
xls_vast_statement_block_add_continuous_assignment
372374
xls_vast_statement_block_add_inline_text
373375
xls_vast_statement_block_add_nonblocking_assignment
374376
xls_vast_verilog_file_add_blank_line
@@ -419,6 +421,7 @@ xls_vast_verilog_file_make_width_cast
419421
xls_vast_verilog_module_add_always_at
420422
xls_vast_verilog_module_add_always_comb
421423
xls_vast_verilog_module_add_always_ff
424+
xls_vast_verilog_module_add_conditional
422425
xls_vast_verilog_module_add_generate_loop
423426
xls_vast_verilog_module_add_inout
424427
xls_vast_verilog_module_add_input

xls/public/c_api_vast.cc

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1240,6 +1240,18 @@ struct xls_vast_statement* xls_vast_statement_block_add_blocking_assignment(
12401240
return reinterpret_cast<xls_vast_statement*>(cpp_assignment);
12411241
}
12421242

1243+
struct xls_vast_statement* xls_vast_statement_block_add_continuous_assignment(
1244+
struct xls_vast_statement_block* block, struct xls_vast_expression* lhs,
1245+
struct xls_vast_expression* rhs) {
1246+
auto* cpp_block = reinterpret_cast<xls::verilog::StatementBlock*>(block);
1247+
auto* cpp_lhs = reinterpret_cast<xls::verilog::Expression*>(lhs);
1248+
auto* cpp_rhs = reinterpret_cast<xls::verilog::Expression*>(rhs);
1249+
xls::verilog::ContinuousAssignment* cpp_assignment =
1250+
cpp_block->Add<xls::verilog::ContinuousAssignment>(xls::SourceInfo(),
1251+
cpp_lhs, cpp_rhs);
1252+
return reinterpret_cast<xls_vast_statement*>(cpp_assignment);
1253+
}
1254+
12431255
struct xls_vast_conditional* xls_vast_statement_block_add_conditional(
12441256
struct xls_vast_statement_block* block, struct xls_vast_expression* cond) {
12451257
auto* cpp_block = reinterpret_cast<xls::verilog::StatementBlock*>(block);
@@ -1271,6 +1283,24 @@ struct xls_vast_statement_block* xls_vast_conditional_add_else(
12711283
return reinterpret_cast<xls_vast_statement_block*>(block);
12721284
}
12731285

1286+
struct xls_vast_conditional* xls_vast_verilog_module_add_conditional(
1287+
struct xls_vast_verilog_module* m, struct xls_vast_expression* cond) {
1288+
auto* cpp_module = reinterpret_cast<xls::verilog::Module*>(m);
1289+
auto* cpp_cond = reinterpret_cast<xls::verilog::Expression*>(cond);
1290+
xls::verilog::Conditional* cpp_if =
1291+
cpp_module->Add<xls::verilog::Conditional>(xls::SourceInfo(), cpp_cond);
1292+
return reinterpret_cast<xls_vast_conditional*>(cpp_if);
1293+
}
1294+
1295+
struct xls_vast_conditional* xls_vast_generate_loop_add_conditional(
1296+
struct xls_vast_generate_loop* loop, struct xls_vast_expression* cond) {
1297+
auto* cpp_loop = reinterpret_cast<xls::verilog::GenerateLoop*>(loop);
1298+
auto* cpp_cond = reinterpret_cast<xls::verilog::Expression*>(cond);
1299+
xls::verilog::Conditional* cpp_if =
1300+
cpp_loop->Add<xls::verilog::Conditional>(xls::SourceInfo(), cpp_cond);
1301+
return reinterpret_cast<xls_vast_conditional*>(cpp_if);
1302+
}
1303+
12741304
struct xls_vast_case_statement* xls_vast_statement_block_add_case(
12751305
struct xls_vast_statement_block* block,
12761306
struct xls_vast_expression* selector) {

xls/public/c_api_vast.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -509,6 +509,12 @@ struct xls_vast_statement* xls_vast_statement_block_add_blocking_assignment(
509509
struct xls_vast_statement_block* block, struct xls_vast_expression* lhs,
510510
struct xls_vast_expression* rhs);
511511

512+
// Adds a continuous assignment statement (assign lhs = rhs;) to a statement
513+
// block and returns a pointer to the created statement.
514+
struct xls_vast_statement* xls_vast_statement_block_add_continuous_assignment(
515+
struct xls_vast_statement_block* block, struct xls_vast_expression* lhs,
516+
struct xls_vast_expression* rhs);
517+
512518
struct xls_vast_statement* xls_vast_statement_block_add_comment_text(
513519
struct xls_vast_statement_block* block, const char* text);
514520

@@ -612,6 +618,14 @@ struct xls_vast_statement_block* xls_vast_conditional_add_else_if(
612618
struct xls_vast_statement_block* xls_vast_conditional_add_else(
613619
struct xls_vast_conditional* cond);
614620

621+
// Adds a conditional (if) with the given condition at module scope.
622+
struct xls_vast_conditional* xls_vast_verilog_module_add_conditional(
623+
struct xls_vast_verilog_module* m, struct xls_vast_expression* cond);
624+
625+
// Adds a conditional (if) inside a generate loop.
626+
struct xls_vast_conditional* xls_vast_generate_loop_add_conditional(
627+
struct xls_vast_generate_loop* loop, struct xls_vast_expression* cond);
628+
615629
// Adds a case statement with the given selector to a statement block and
616630
// returns a handle to the created case statement.
617631
struct xls_vast_case_statement* xls_vast_statement_block_add_case(

xls/public/c_api_vast_test.cc

Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1047,6 +1047,203 @@ endmodule
10471047
EXPECT_EQ(std::string_view{emitted}, kWantEmitted);
10481048
}
10491049

1050+
TEST(XlsCApiTest, VastModuleScopeConditional) {
1051+
const std::string_view kWantEmitted = R"(module top;
1052+
parameter A = 1;
1053+
parameter B = 2;
1054+
wire out;
1055+
if (A == B) begin
1056+
assign out = 1'h1;
1057+
end else begin
1058+
assign out = 1'h0;
1059+
end
1060+
endmodule
1061+
)";
1062+
1063+
xls_vast_verilog_file* f =
1064+
xls_vast_make_verilog_file(xls_vast_file_type_system_verilog);
1065+
ASSERT_NE(f, nullptr);
1066+
absl::Cleanup free_file([&] { xls_vast_verilog_file_free(f); });
1067+
1068+
xls_vast_verilog_module* m = xls_vast_verilog_file_add_module(f, "top");
1069+
ASSERT_NE(m, nullptr);
1070+
1071+
// parameter A = 1; parameter B = 2;
1072+
xls_vast_parameter_ref* A = xls_vast_verilog_module_add_parameter(
1073+
m, "A",
1074+
xls_vast_literal_as_expression(
1075+
xls_vast_verilog_file_make_plain_literal(f, 1)));
1076+
xls_vast_parameter_ref* B = xls_vast_verilog_module_add_parameter(
1077+
m, "B",
1078+
xls_vast_literal_as_expression(
1079+
xls_vast_verilog_file_make_plain_literal(f, 2)));
1080+
ASSERT_NE(A, nullptr);
1081+
ASSERT_NE(B, nullptr);
1082+
1083+
// wire out;
1084+
xls_vast_data_type* scalar = xls_vast_verilog_file_make_scalar_type(f);
1085+
ASSERT_NE(scalar, nullptr);
1086+
xls_vast_logic_ref* out = xls_vast_verilog_module_add_wire(m, "out", scalar);
1087+
ASSERT_NE(out, nullptr);
1088+
1089+
auto make_ubits_literal = [&](int64_t bit_count,
1090+
uint64_t value) -> xls_vast_expression* {
1091+
xls_bits* bits = nullptr;
1092+
char* error = nullptr;
1093+
absl::Cleanup free_error([&] { xls_c_str_free(error); });
1094+
EXPECT_TRUE(xls_bits_make_ubits(bit_count, value, &error, &bits))
1095+
<< (error ? error : "");
1096+
absl::Cleanup free_bits([&] { xls_bits_free(bits); });
1097+
xls_vast_literal* lit = nullptr;
1098+
EXPECT_TRUE(xls_vast_verilog_file_make_literal(
1099+
f, bits, xls_format_preference_hex, /*emit_bit_count=*/true, &error,
1100+
&lit))
1101+
<< (error ? error : "");
1102+
EXPECT_NE(lit, nullptr);
1103+
return xls_vast_literal_as_expression(lit);
1104+
};
1105+
1106+
// if (A == B) ...
1107+
xls_vast_expression* eq = xls_vast_verilog_file_make_binary(
1108+
f, xls_vast_parameter_ref_as_expression(A),
1109+
xls_vast_parameter_ref_as_expression(B), xls_vast_operator_kind_eq);
1110+
ASSERT_NE(eq, nullptr);
1111+
xls_vast_conditional* cond = xls_vast_verilog_module_add_conditional(m, eq);
1112+
ASSERT_NE(cond, nullptr);
1113+
1114+
xls_vast_statement_block* then_block =
1115+
xls_vast_conditional_get_then_block(cond);
1116+
ASSERT_NE(then_block, nullptr);
1117+
ASSERT_NE(xls_vast_statement_block_add_continuous_assignment(
1118+
then_block, xls_vast_logic_ref_as_expression(out),
1119+
make_ubits_literal(/*bit_count=*/1, /*value=*/1)),
1120+
nullptr);
1121+
1122+
xls_vast_statement_block* else_block = xls_vast_conditional_add_else(cond);
1123+
ASSERT_NE(else_block, nullptr);
1124+
ASSERT_NE(xls_vast_statement_block_add_continuous_assignment(
1125+
else_block, xls_vast_logic_ref_as_expression(out),
1126+
make_ubits_literal(/*bit_count=*/1, /*value=*/0)),
1127+
nullptr);
1128+
1129+
char* emitted = xls_vast_verilog_file_emit(f);
1130+
ASSERT_NE(emitted, nullptr);
1131+
absl::Cleanup free_emitted([&] { xls_c_str_free(emitted); });
1132+
EXPECT_EQ(std::string_view{emitted}, kWantEmitted);
1133+
}
1134+
1135+
TEST(XlsCApiTest, VastGenerateLoopConditional) {
1136+
const std::string_view kWantEmitted = R"(module top;
1137+
wire [2:0] out;
1138+
for (genvar i = 0; i < 3; i = i + 1) begin : g
1139+
if (i == 0) begin
1140+
assign out[i] = 1'h0;
1141+
end else if (i == 1) begin
1142+
assign out[i] = 1'h1;
1143+
end else begin
1144+
assign out[i] = 'X;
1145+
end
1146+
end
1147+
endmodule
1148+
)";
1149+
1150+
xls_vast_verilog_file* f =
1151+
xls_vast_make_verilog_file(xls_vast_file_type_system_verilog);
1152+
ASSERT_NE(f, nullptr);
1153+
absl::Cleanup free_file([&] { xls_vast_verilog_file_free(f); });
1154+
1155+
xls_vast_verilog_module* m = xls_vast_verilog_file_add_module(f, "top");
1156+
ASSERT_NE(m, nullptr);
1157+
1158+
xls_vast_data_type* u3 =
1159+
xls_vast_verilog_file_make_bit_vector_type(f, 3, /*is_signed=*/false);
1160+
xls_vast_logic_ref* out = xls_vast_verilog_module_add_wire(m, "out", u3);
1161+
ASSERT_NE(out, nullptr);
1162+
1163+
xls_vast_generate_loop* loop = xls_vast_verilog_module_add_generate_loop(
1164+
m, "i",
1165+
xls_vast_literal_as_expression(
1166+
xls_vast_verilog_file_make_plain_literal(f, 0)),
1167+
xls_vast_literal_as_expression(
1168+
xls_vast_verilog_file_make_plain_literal(f, 3)),
1169+
"g");
1170+
ASSERT_NE(loop, nullptr);
1171+
1172+
xls_vast_logic_ref* i_lr = xls_vast_generate_loop_get_genvar(loop);
1173+
ASSERT_NE(i_lr, nullptr);
1174+
1175+
// Helper to form out[i] (as expression).
1176+
xls_vast_indexable_expression* out_idxable =
1177+
xls_vast_logic_ref_as_indexable_expression(out);
1178+
ASSERT_NE(out_idxable, nullptr);
1179+
xls_vast_index* out_i = xls_vast_verilog_file_make_index(
1180+
f, out_idxable, xls_vast_logic_ref_as_expression(i_lr));
1181+
ASSERT_NE(out_i, nullptr);
1182+
xls_vast_expression* out_i_expr = xls_vast_index_as_expression(out_i);
1183+
ASSERT_NE(out_i_expr, nullptr);
1184+
1185+
auto make_ubits_literal = [&](int64_t bit_count,
1186+
uint64_t value) -> xls_vast_expression* {
1187+
xls_bits* bits = nullptr;
1188+
char* error = nullptr;
1189+
absl::Cleanup free_error([&] { xls_c_str_free(error); });
1190+
EXPECT_TRUE(xls_bits_make_ubits(bit_count, value, &error, &bits))
1191+
<< (error ? error : "");
1192+
absl::Cleanup free_bits([&] { xls_bits_free(bits); });
1193+
xls_vast_literal* lit = nullptr;
1194+
EXPECT_TRUE(xls_vast_verilog_file_make_literal(
1195+
f, bits, xls_format_preference_hex, /*emit_bit_count=*/true, &error,
1196+
&lit))
1197+
<< (error ? error : "");
1198+
EXPECT_NE(lit, nullptr);
1199+
return xls_vast_literal_as_expression(lit);
1200+
};
1201+
1202+
// if (i == 0) ...
1203+
xls_vast_expression* i_eq_0 = xls_vast_verilog_file_make_binary(
1204+
f, xls_vast_logic_ref_as_expression(i_lr),
1205+
xls_vast_literal_as_expression(
1206+
xls_vast_verilog_file_make_plain_literal(f, 0)),
1207+
xls_vast_operator_kind_eq);
1208+
ASSERT_NE(i_eq_0, nullptr);
1209+
xls_vast_conditional* cond =
1210+
xls_vast_generate_loop_add_conditional(loop, i_eq_0);
1211+
ASSERT_NE(cond, nullptr);
1212+
1213+
xls_vast_statement_block* then_block =
1214+
xls_vast_conditional_get_then_block(cond);
1215+
ASSERT_NE(then_block, nullptr);
1216+
ASSERT_NE(xls_vast_statement_block_add_continuous_assignment(
1217+
then_block, out_i_expr,
1218+
make_ubits_literal(/*bit_count=*/1, /*value=*/0)),
1219+
nullptr);
1220+
1221+
xls_vast_expression* i_eq_1 = xls_vast_verilog_file_make_binary(
1222+
f, xls_vast_logic_ref_as_expression(i_lr),
1223+
xls_vast_literal_as_expression(
1224+
xls_vast_verilog_file_make_plain_literal(f, 1)),
1225+
xls_vast_operator_kind_eq);
1226+
xls_vast_statement_block* else_if_block =
1227+
xls_vast_conditional_add_else_if(cond, i_eq_1);
1228+
ASSERT_NE(else_if_block, nullptr);
1229+
ASSERT_NE(xls_vast_statement_block_add_continuous_assignment(
1230+
else_if_block, out_i_expr,
1231+
make_ubits_literal(/*bit_count=*/1, /*value=*/1)),
1232+
nullptr);
1233+
1234+
xls_vast_statement_block* else_block = xls_vast_conditional_add_else(cond);
1235+
ASSERT_NE(else_block, nullptr);
1236+
ASSERT_NE(xls_vast_statement_block_add_continuous_assignment(
1237+
else_block, out_i_expr,
1238+
xls_vast_verilog_file_make_unsized_x_literal(f)),
1239+
nullptr);
1240+
1241+
char* emitted = xls_vast_verilog_file_emit(f);
1242+
ASSERT_NE(emitted, nullptr);
1243+
absl::Cleanup free_emitted([&] { xls_c_str_free(emitted); });
1244+
EXPECT_EQ(std::string_view{emitted}, kWantEmitted);
1245+
}
1246+
10501247
// Tests that we can assign a 128-bit output wire using a 128-bit literal
10511248
// value.
10521249
TEST(XlsCApiTest, ContinuousAssignmentOf128BitLiteral) {

0 commit comments

Comments
 (0)