diff --git a/xls/codegen/vast/vast.h b/xls/codegen/vast/vast.h index 18876d3014..8f01c279ed 100644 --- a/xls/codegen/vast/vast.h +++ b/xls/codegen/vast/vast.h @@ -1477,11 +1477,19 @@ class LocalParam final : public VastNode { std::vector items_; }; +// An indexable expression that can be bit-sliced or indexed. +class IndexableExpression : public Expression { + public: + using Expression::Expression; + + bool IsIndexableExpression() const final { return true; } +}; + // Refers to a Parameter's definition for use in an expression. -class ParameterRef final : public Expression { +class ParameterRef final : public IndexableExpression { public: ParameterRef(Parameter* parameter, VerilogFile* file, const SourceInfo& loc) - : Expression(file, loc), parameter_(parameter) {} + : IndexableExpression(file, loc), parameter_(parameter) {} std::string Emit(LineInfo* line_info) const final { return parameter_->GetName(); @@ -1498,14 +1506,6 @@ class ParameterRef final : public Expression { Parameter* parameter_; }; -// An indexable expression that can be bit-sliced or indexed. -class IndexableExpression : public Expression { - public: - using Expression::Expression; - - bool IsIndexableExpression() const final { return true; } -}; - // Reference to the definition of a WireDef, RegDef, or LogicDef. class LogicRef final : public IndexableExpression { public: diff --git a/xls/codegen/vast/vast_test.cc b/xls/codegen/vast/vast_test.cc index 91b6914546..dcc5524157 100644 --- a/xls/codegen/vast/vast_test.cc +++ b/xls/codegen/vast/vast_test.cc @@ -21,14 +21,14 @@ #include #include -#include "gmock/gmock.h" -#include "gtest/gtest.h" #include "absl/container/flat_hash_map.h" #include "absl/status/status.h" #include "absl/status/status_matchers.h" #include "absl/strings/str_cat.h" #include "absl/strings/str_format.h" #include "absl/types/span.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" #include "xls/common/status/matchers.h" #include "xls/ir/bits.h" #include "xls/ir/fileno.h" @@ -2126,6 +2126,79 @@ TEST_P(VastTest, ArrayAssignmentPattern) { ->Emit(nullptr)); } +TEST_P(VastTest, ArrayParameters) { + if (!UseSystemVerilog()) { + GTEST_SKIP(); + } + VerilogFile f(GetFileType()); + Module* m = f.AddModule("top", SourceInfo()); + + const SourceInfo si; + // Literals used for the array assignment patterns below. + Expression* tick0 = f.Make(si); + + Def* p0_def = + f.Make(si, "P0", DataKind::kUser, + f.UnpackedArrayType(/*element_bit_count=*/1, {2}, si)); + m->AddParameter(p0_def, f.ArrayAssignmentPattern({tick0, tick0}, si), si); + + Def* p1_def = + f.Make(si, "P1", DataKind::kInt, + f.UnpackedArrayType(/*element_bit_count=*/1, {3}, si)); + m->AddParameter( + p1_def, + f.ArrayAssignmentPattern( + {f.PlainLiteral(1, si), f.PlainLiteral(2, si), f.PlainLiteral(3, si)}, + si), + si); + + Def* p2_def = + f.Make(si, "P2", DataKind::kLogic, + f.UnpackedArrayType(/*element_bit_count=*/8, {2}, si)); + m->AddParameter(p2_def, + f.ArrayAssignmentPattern( + {f.Literal(0x42, 8, si), f.Literal(0x43, 8, si)}, si), + si); + Expression* p3_row = + f.ArrayAssignmentPattern({f.PlainLiteral(1, si), f.PlainLiteral(2, si), + f.PlainLiteral(3, si), f.PlainLiteral(4, si)}, + si); + Def* p3_def = + f.Make(si, "P3", DataKind::kUser, + f.UnpackedArrayType(/*element_bit_count=*/1, {1, 4}, si)); + m->AddParameter(p3_def, f.ArrayAssignmentPattern({p3_row}, si), si); + Expression* p4_row0 = f.ArrayAssignmentPattern( + {f.PlainLiteral(1, si), f.PlainLiteral(2, si), f.PlainLiteral(3, si)}, + si); + Expression* p4_row1 = f.ArrayAssignmentPattern( + {f.PlainLiteral(4, si), f.PlainLiteral(5, si), f.PlainLiteral(6, si)}, + si); + Def* p4_def = + f.Make(si, "P4", DataKind::kInt, + f.UnpackedArrayType(/*element_bit_count=*/1, {2, 3}, si)); + m->AddParameter(p4_def, f.ArrayAssignmentPattern({p4_row0, p4_row1}, si), si); + Expression* p5_row0 = f.ArrayAssignmentPattern( + {f.Literal(0x42, 8, si), f.Literal(0x43, 8, si), f.Literal(0x44, 8, si)}, + si); + Expression* p5_row1 = f.ArrayAssignmentPattern( + {f.Literal(0x45, 8, si), f.Literal(0x46, 8, si), f.Literal(0x47, 8, si)}, + si); + Def* p5_def = + f.Make(si, "P5", DataKind::kLogic, + f.UnpackedArrayType(/*element_bit_count=*/8, {2, 3}, si)); + m->AddParameter(p5_def, f.ArrayAssignmentPattern({p5_row0, p5_row1}, si), si); + + EXPECT_EQ(m->Emit(nullptr), + R"(module top; + parameter P0[2] = '{'0, '0}; + parameter int P1[3] = '{1, 2, 3}; + parameter logic [7:0] P2[2] = '{8'h42, 8'h43}; + parameter P3[1][4] = '{'{1, 2, 3, 4}}; + parameter int P4[2][3] = '{'{1, 2, 3}, '{4, 5, 6}}; + parameter logic [7:0] P5[2][3] = '{'{8'h42, 8'h43, 8'h44}, '{8'h45, 8'h46, 8'h47}}; +endmodule)"); +} + TEST_P(VastTest, ModuleSections) { VerilogFile f(GetFileType()); Module* module = f.Make(SourceInfo(), "my_module"); diff --git a/xls/public/c_api_symbols.txt b/xls/public/c_api_symbols.txt index 40612a04ae..fad59652b3 100644 --- a/xls/public/c_api_symbols.txt +++ b/xls/public/c_api_symbols.txt @@ -363,6 +363,7 @@ xls_vast_logic_ref_get_name xls_vast_macro_ref_as_expression xls_vast_make_verilog_file xls_vast_parameter_ref_as_expression +xls_vast_parameter_ref_as_indexable_expression xls_vast_slice_as_expression xls_vast_statement_block_add_blank_line xls_vast_statement_block_add_blocking_assignment @@ -377,6 +378,7 @@ xls_vast_verilog_file_add_include xls_vast_verilog_file_add_module xls_vast_verilog_file_emit xls_vast_verilog_file_free +xls_vast_verilog_file_make_array_assignment_pattern xls_vast_verilog_file_make_binary xls_vast_verilog_file_make_bit_vector_type xls_vast_verilog_file_make_bit_vector_type_with_expression @@ -412,6 +414,7 @@ xls_vast_verilog_file_make_slice_i64 xls_vast_verilog_file_make_ternary xls_vast_verilog_file_make_type_cast xls_vast_verilog_file_make_unary +xls_vast_verilog_file_make_unpacked_array_type xls_vast_verilog_file_make_unsized_one_literal xls_vast_verilog_file_make_unsized_x_literal xls_vast_verilog_file_make_unsized_zero_literal diff --git a/xls/public/c_api_vast.cc b/xls/public/c_api_vast.cc index c3c7842a40..031b1e1394 100644 --- a/xls/public/c_api_vast.cc +++ b/xls/public/c_api_vast.cc @@ -401,6 +401,19 @@ struct xls_vast_data_type* xls_vast_verilog_file_make_packed_array_type( return reinterpret_cast(type); } +struct xls_vast_data_type* xls_vast_verilog_file_make_unpacked_array_type( + struct xls_vast_verilog_file* f, struct xls_vast_data_type* element_type, + const int64_t* unpacked_dims, size_t unpacked_dims_count) { + auto* cpp_file = reinterpret_cast(f); + auto* cpp_element_type = + reinterpret_cast(element_type); + absl::Span dims(unpacked_dims, unpacked_dims_count); + xls::verilog::DataType* type = + cpp_file->Make(xls::SourceInfo(), + cpp_element_type, dims); + return reinterpret_cast(type); +} + struct xls_vast_def* xls_vast_verilog_file_make_def( struct xls_vast_verilog_file* f, const char* name, xls_vast_data_kind kind, struct xls_vast_data_type* type) { @@ -412,6 +425,21 @@ struct xls_vast_def* xls_vast_verilog_file_make_def( return reinterpret_cast(def); } +struct xls_vast_expression* xls_vast_verilog_file_make_array_assignment_pattern( + struct xls_vast_verilog_file* f, struct xls_vast_expression** elements, + size_t element_count) { + auto* cpp_file = reinterpret_cast(f); + std::vector cpp_elements; + cpp_elements.reserve(element_count); + for (size_t i = 0; i < element_count; ++i) { + cpp_elements.push_back( + reinterpret_cast(elements[i])); + } + xls::verilog::Expression* expr = + cpp_file->ArrayAssignmentPattern(cpp_elements, xls::SourceInfo()); + return reinterpret_cast(expr); +} + void xls_vast_verilog_module_add_member_instantiation( struct xls_vast_verilog_module* m, struct xls_vast_instantiation* member) { auto* cpp_module = reinterpret_cast(m); @@ -687,6 +715,17 @@ xls_vast_logic_ref_as_indexable_expression( cpp_indexable_expression); } +struct xls_vast_indexable_expression* +xls_vast_parameter_ref_as_indexable_expression( + struct xls_vast_parameter_ref* parameter_ref) { + auto* cpp_parameter_ref = + reinterpret_cast(parameter_ref); + auto* cpp_indexable_expression = + static_cast(cpp_parameter_ref); + return reinterpret_cast( + cpp_indexable_expression); +} + struct xls_vast_logic_ref* xls_vast_generate_loop_get_genvar( struct xls_vast_generate_loop* loop) { auto* cpp_loop = reinterpret_cast(loop); diff --git a/xls/public/c_api_vast.h b/xls/public/c_api_vast.h index ab8df29a6a..f886a3d57e 100644 --- a/xls/public/c_api_vast.h +++ b/xls/public/c_api_vast.h @@ -152,6 +152,15 @@ struct xls_vast_data_type* xls_vast_verilog_file_make_packed_array_type( struct xls_vast_verilog_file* f, xls_vast_data_type* element_type, const int64_t* packed_dims, size_t packed_dims_count); +// Creates an unpacked array type. +// +// Example (SystemVerilog): +// element_type = bit_vector_type(8) and unpacked_dims = {2, 3} yields a type +// that emits like: `[7:0] [2][3]`. +struct xls_vast_data_type* xls_vast_verilog_file_make_unpacked_array_type( + struct xls_vast_verilog_file* f, xls_vast_data_type* element_type, + const int64_t* unpacked_dims, size_t unpacked_dims_count); + // -- Module::Add* void xls_vast_verilog_module_add_member_instantiation( @@ -349,6 +358,11 @@ struct xls_vast_concat* xls_vast_verilog_file_make_replicated_concat_i64( struct xls_vast_verilog_file* f, int64_t replication_count, struct xls_vast_expression** elements, size_t element_count); +// Creates an array assignment pattern expression: `'{a, b, c}`. +struct xls_vast_expression* xls_vast_verilog_file_make_array_assignment_pattern( + struct xls_vast_verilog_file* f, struct xls_vast_expression** elements, + size_t element_count); + struct xls_vast_slice* xls_vast_verilog_file_make_slice_i64( struct xls_vast_verilog_file* f, struct xls_vast_indexable_expression* subject, int64_t hi, int64_t lo); @@ -430,6 +444,10 @@ struct xls_vast_expression* xls_vast_indexable_expression_as_expression( struct xls_vast_indexable_expression* xls_vast_logic_ref_as_indexable_expression( struct xls_vast_logic_ref* logic_ref); + +struct xls_vast_indexable_expression* +xls_vast_parameter_ref_as_indexable_expression( + struct xls_vast_parameter_ref* parameter_ref); struct xls_vast_logic_ref* xls_vast_generate_loop_get_genvar( struct xls_vast_generate_loop* loop); diff --git a/xls/public/c_api_vast_test.cc b/xls/public/c_api_vast_test.cc index 743eeb1185..110fda4dcd 100644 --- a/xls/public/c_api_vast_test.cc +++ b/xls/public/c_api_vast_test.cc @@ -23,8 +23,8 @@ #include #include -#include "gtest/gtest.h" #include "absl/cleanup/cleanup.h" +#include "gtest/gtest.h" #include "xls/public/c_api.h" #include "xls/public/c_api_format_preference.h" @@ -982,6 +982,222 @@ endmodule EXPECT_EQ(std::string_view{emitted}, kWantEmitted); } +TEST(XlsCApiTest, VastArrayParametersWithDefAndAssignmentPattern) { + const std::string_view kWantEmitted = R"(module top; + parameter P0[2] = '{'0, '0}; + parameter int P1[3] = '{1, 2, 3}; + parameter logic [7:0] P2[2] = '{8'h42, 8'h43}; + parameter integer P3[1][4] = '{'{1, 2, 3, 4}}; + parameter int P4[2][3] = '{'{1, 2, 3}, '{4, 5, 6}}; +endmodule +)"; + + xls_vast_verilog_file* f = + xls_vast_make_verilog_file(xls_vast_file_type_system_verilog); + ASSERT_NE(f, nullptr); + absl::Cleanup free_file([&] { xls_vast_verilog_file_free(f); }); + + xls_vast_verilog_module* m = xls_vast_verilog_file_add_module(f, "top"); + ASSERT_NE(m, nullptr); + + auto make_assignment_pattern = + [&](std::vector elements) -> xls_vast_expression* { + return xls_vast_verilog_file_make_array_assignment_pattern( + f, elements.data(), elements.size()); + }; + + // Helper: make an N-bit hex literal like 8'h42. + auto make_hex_literal = [&](int64_t bit_count, + uint64_t value) -> xls_vast_expression* { + xls_bits* bits = nullptr; + char* error = nullptr; + absl::Cleanup free_error([&] { xls_c_str_free(error); }); + EXPECT_TRUE(xls_bits_make_ubits(bit_count, value, &error, &bits)) + << (error ? error : ""); + absl::Cleanup free_bits([&] { xls_bits_free(bits); }); + xls_vast_literal* lit = nullptr; + EXPECT_TRUE(xls_vast_verilog_file_make_literal( + f, bits, xls_format_preference_hex, + /*emit_bit_count=*/true, &error, &lit)) + << (error ? error : ""); + EXPECT_NE(lit, nullptr); + return xls_vast_literal_as_expression(lit); + }; + + // P0: parameter P0[2] = '{'0, '0}; + xls_vast_data_type* scalar = xls_vast_verilog_file_make_scalar_type(f); + ASSERT_NE(scalar, nullptr); + const int64_t p0_dims[] = {2}; + xls_vast_data_type* p0_type = + xls_vast_verilog_file_make_unpacked_array_type(f, scalar, p0_dims, 1); + ASSERT_NE(p0_type, nullptr); + xls_vast_def* p0_def = + xls_vast_verilog_file_make_def(f, "P0", xls_vast_data_kind_user, p0_type); + ASSERT_NE(p0_def, nullptr); + xls_vast_expression* tick0 = + xls_vast_verilog_file_make_unsized_zero_literal(f); + ASSERT_NE(tick0, nullptr); + xls_vast_expression* p0_rhs = make_assignment_pattern({tick0, tick0}); + ASSERT_NE(p0_rhs, nullptr); + ASSERT_NE(xls_vast_verilog_module_add_parameter_with_def(m, p0_def, p0_rhs), + nullptr); + + // P1: parameter int P1[3] = '{1, 2, 3}; + const int64_t p1_dims[] = {3}; + xls_vast_data_type* p1_type = + xls_vast_verilog_file_make_unpacked_array_type(f, scalar, p1_dims, 1); + ASSERT_NE(p1_type, nullptr); + xls_vast_def* p1_def = + xls_vast_verilog_file_make_def(f, "P1", xls_vast_data_kind_int, p1_type); + ASSERT_NE(p1_def, nullptr); + xls_vast_literal* one = xls_vast_verilog_file_make_plain_literal(f, 1); + xls_vast_literal* two = xls_vast_verilog_file_make_plain_literal(f, 2); + xls_vast_literal* three = xls_vast_verilog_file_make_plain_literal(f, 3); + xls_vast_literal* four = xls_vast_verilog_file_make_plain_literal(f, 4); + xls_vast_literal* five = xls_vast_verilog_file_make_plain_literal(f, 5); + xls_vast_literal* six = xls_vast_verilog_file_make_plain_literal(f, 6); + ASSERT_NE(one, nullptr); + ASSERT_NE(two, nullptr); + ASSERT_NE(three, nullptr); + ASSERT_NE(four, nullptr); + ASSERT_NE(five, nullptr); + ASSERT_NE(six, nullptr); + xls_vast_expression* p1_rhs = make_assignment_pattern( + {xls_vast_literal_as_expression(one), xls_vast_literal_as_expression(two), + xls_vast_literal_as_expression(three)}); + ASSERT_NE(p1_rhs, nullptr); + ASSERT_NE(xls_vast_verilog_module_add_parameter_with_def(m, p1_def, p1_rhs), + nullptr); + + // P2: parameter logic [7:0] P2[2] = '{8'h42, 8'h43}; + xls_vast_data_type* u8 = + xls_vast_verilog_file_make_bit_vector_type(f, 8, /*is_signed=*/false); + ASSERT_NE(u8, nullptr); + const int64_t p2_dims[] = {2}; + xls_vast_data_type* p2_type = + xls_vast_verilog_file_make_unpacked_array_type(f, u8, p2_dims, 1); + ASSERT_NE(p2_type, nullptr); + xls_vast_def* p2_def = xls_vast_verilog_file_make_def( + f, "P2", xls_vast_data_kind_logic, p2_type); + ASSERT_NE(p2_def, nullptr); + xls_vast_expression* p2_rhs = make_assignment_pattern( + {make_hex_literal(8, 0x42), make_hex_literal(8, 0x43)}); + ASSERT_NE(p2_rhs, nullptr); + ASSERT_NE(xls_vast_verilog_module_add_parameter_with_def(m, p2_def, p2_rhs), + nullptr); + + // P3: parameter integer P3[1][4] = '{'{1, 2, 3, 4}}; + const int64_t p3_dims[] = {1, 4}; + xls_vast_data_type* p3_type = + xls_vast_verilog_file_make_unpacked_array_type(f, scalar, p3_dims, 2); + ASSERT_NE(p3_type, nullptr); + xls_vast_def* p3_def = xls_vast_verilog_file_make_def( + f, "P3", xls_vast_data_kind_integer, p3_type); + ASSERT_NE(p3_def, nullptr); + xls_vast_expression* inner_1234 = make_assignment_pattern( + {xls_vast_literal_as_expression(one), xls_vast_literal_as_expression(two), + xls_vast_literal_as_expression(three), + xls_vast_literal_as_expression(four)}); + ASSERT_NE(inner_1234, nullptr); + xls_vast_expression* p3_rhs = make_assignment_pattern({inner_1234}); + ASSERT_NE(p3_rhs, nullptr); + ASSERT_NE(xls_vast_verilog_module_add_parameter_with_def(m, p3_def, p3_rhs), + nullptr); + + // P4: parameter int P4[2][3] = '{'{1, 2, 3}, '{4, 5, 6}}; + const int64_t p4_dims[] = {2, 3}; + xls_vast_data_type* p4_type = + xls_vast_verilog_file_make_unpacked_array_type(f, scalar, p4_dims, 2); + ASSERT_NE(p4_type, nullptr); + xls_vast_def* p4_def = + xls_vast_verilog_file_make_def(f, "P4", xls_vast_data_kind_int, p4_type); + ASSERT_NE(p4_def, nullptr); + xls_vast_expression* row_123 = make_assignment_pattern( + {xls_vast_literal_as_expression(one), xls_vast_literal_as_expression(two), + xls_vast_literal_as_expression(three)}); + xls_vast_expression* row_456 = + make_assignment_pattern({xls_vast_literal_as_expression(four), + xls_vast_literal_as_expression(five), + xls_vast_literal_as_expression(six)}); + ASSERT_NE(row_123, nullptr); + ASSERT_NE(row_456, nullptr); + xls_vast_expression* p4_rhs = make_assignment_pattern({row_123, row_456}); + ASSERT_NE(p4_rhs, nullptr); + ASSERT_NE(xls_vast_verilog_module_add_parameter_with_def(m, p4_def, p4_rhs), + nullptr); + + char* emitted = xls_vast_verilog_file_emit(f); + ASSERT_NE(emitted, nullptr); + absl::Cleanup free_emitted([&] { xls_c_str_free(emitted); }); + EXPECT_EQ(std::string_view{emitted}, kWantEmitted); +} + +TEST(XlsCApiTest, VastIndexUnpackedArrayParameter) { + const std::string_view kWantEmitted = R"(module top; + parameter P0[2] = '{'0, '1}; + wire w; + assign w = P0[0]; +endmodule +)"; + + xls_vast_verilog_file* f = + xls_vast_make_verilog_file(xls_vast_file_type_system_verilog); + ASSERT_NE(f, nullptr); + absl::Cleanup free_file([&] { xls_vast_verilog_file_free(f); }); + + xls_vast_verilog_module* m = xls_vast_verilog_file_add_module(f, "top"); + ASSERT_NE(m, nullptr); + + // parameter P0[2] = '{'0, '1}; + xls_vast_data_type* scalar = xls_vast_verilog_file_make_scalar_type(f); + ASSERT_NE(scalar, nullptr); + const int64_t p0_dims[] = {2}; + xls_vast_data_type* p0_type = + xls_vast_verilog_file_make_unpacked_array_type(f, scalar, p0_dims, 1); + ASSERT_NE(p0_type, nullptr); + xls_vast_def* p0_def = + xls_vast_verilog_file_make_def(f, "P0", xls_vast_data_kind_user, p0_type); + ASSERT_NE(p0_def, nullptr); + xls_vast_expression* tick0 = + xls_vast_verilog_file_make_unsized_zero_literal(f); + xls_vast_expression* tick1 = + xls_vast_verilog_file_make_unsized_one_literal(f); + ASSERT_NE(tick0, nullptr); + ASSERT_NE(tick1, nullptr); + std::vector p0_elems = {tick0, tick1}; + xls_vast_expression* p0_rhs = + xls_vast_verilog_file_make_array_assignment_pattern(f, p0_elems.data(), + p0_elems.size()); + ASSERT_NE(p0_rhs, nullptr); + xls_vast_parameter_ref* p0_ref = + xls_vast_verilog_module_add_parameter_with_def(m, p0_def, p0_rhs); + ASSERT_NE(p0_ref, nullptr); + + // wire w; + xls_vast_logic_ref* w = xls_vast_verilog_module_add_wire(m, "w", scalar); + ASSERT_NE(w, nullptr); + + // assign w = P0[0]; + xls_vast_indexable_expression* p0_indexable = + xls_vast_parameter_ref_as_indexable_expression(p0_ref); + ASSERT_NE(p0_indexable, nullptr); + xls_vast_index* p0_0 = + xls_vast_verilog_file_make_index_i64(f, p0_indexable, 0); + ASSERT_NE(p0_0, nullptr); + xls_vast_expression* rhs = xls_vast_index_as_expression(p0_0); + ASSERT_NE(rhs, nullptr); + xls_vast_continuous_assignment* assign = + xls_vast_verilog_file_make_continuous_assignment( + f, xls_vast_logic_ref_as_expression(w), rhs); + ASSERT_NE(assign, nullptr); + xls_vast_verilog_module_add_member_continuous_assignment(m, assign); + + char* emitted = xls_vast_verilog_file_emit(f); + ASSERT_NE(emitted, nullptr); + absl::Cleanup free_emitted([&] { xls_c_str_free(emitted); }); + EXPECT_EQ(std::string_view{emitted}, kWantEmitted); +} + TEST(XlsCApiTest, VastLocalparamKinds) { const std::string_view kWantEmitted = R"(module top; localparam Foo = 42;