diff --git a/xls/codegen/vast/BUILD b/xls/codegen/vast/BUILD index f3e0891f19..5f422ea0a2 100644 --- a/xls/codegen/vast/BUILD +++ b/xls/codegen/vast/BUILD @@ -33,6 +33,7 @@ cc_library( ":verilog_keywords", "//xls/common:indent", "//xls/common:visitor", + "//xls/common/status:ret_check", "//xls/common/status:status_macros", "//xls/ir:bits", "//xls/ir:bits_ops", diff --git a/xls/codegen/vast/vast.cc b/xls/codegen/vast/vast.cc index 5b9d2a0806..808eb1cc34 100644 --- a/xls/codegen/vast/vast.cc +++ b/xls/codegen/vast/vast.cc @@ -38,6 +38,7 @@ #include "absl/types/variant.h" #include "xls/codegen/vast/verilog_keywords.h" #include "xls/common/indent.h" +#include "xls/common/status/ret_check.h" #include "xls/common/status/status_macros.h" #include "xls/common/visitor.h" #include "xls/ir/bits_ops.h" @@ -729,37 +730,49 @@ LogicRef* Module::AddPortDef(ModulePortDirection direction, Def* def, } LogicRef* Module::AddInputInternal(std::string_view name, DataType* type, - const SourceInfo& loc) { - return AddPortDef( - ModulePortDirection::kInput, - type->IsUserDefined() - ? static_cast(file()->Make(loc, name, type)) - : static_cast(file()->Make(loc, name, type)), - loc); + DataKind data_kind, const SourceInfo& loc) { + Def* def; + if (type->IsUserDefined()) { + def = static_cast(file()->Make(loc, name, type)); + } else if (data_kind == DataKind::kWire) { + def = static_cast(file()->Make(loc, name, type)); + } else { + CHECK_EQ(data_kind, DataKind::kLogic); + def = static_cast(file()->Make(loc, name, type)); + } + return AddPortDef(ModulePortDirection::kInput, def, loc); } LogicRef* Module::AddOutputInternal(std::string_view name, DataType* type, - const SourceInfo& loc) { - return AddPortDef( - ModulePortDirection::kOutput, - type->IsUserDefined() - ? static_cast(file()->Make(loc, name, type)) - : static_cast(file()->Make(loc, name, type)), - loc); + DataKind data_kind, const SourceInfo& loc) { + Def* def; + if (type->IsUserDefined()) { + def = static_cast(file()->Make(loc, name, type)); + } else if (data_kind == DataKind::kWire) { + def = static_cast(file()->Make(loc, name, type)); + } else { + CHECK_EQ(data_kind, DataKind::kLogic); + def = static_cast(file()->Make(loc, name, type)); + } + return AddPortDef(ModulePortDirection::kOutput, def, loc); } absl::StatusOr Module::AddInput(std::string_view name, DataType* type, - const SourceInfo& loc) { + const SourceInfo& loc, + DataKind data_kind) { + XLS_RET_CHECK(data_kind == DataKind::kWire || data_kind == DataKind::kLogic); XLS_RETURN_IF_ERROR(NoteDefined(&defined_names_, name, "input")); - return AddInputInternal(name, type, loc); + return AddInputInternal(name, type, data_kind, loc); } absl::StatusOr Module::AddOutput(std::string_view name, DataType* type, - const SourceInfo& loc) { + const SourceInfo& loc, + DataKind data_kind) { + XLS_RET_CHECK(data_kind == DataKind::kWire || data_kind == DataKind::kLogic); XLS_RETURN_IF_ERROR(NoteDefined(&defined_names_, name, "output")); - return AddOutputInternal(name, type, loc); + return AddOutputInternal(name, type, data_kind, loc); } LogicRef* Module::AddRegInternal(std::string_view name, DataType* type, @@ -796,6 +809,17 @@ absl::StatusOr Module::AddWire(std::string_view name, DataType* type, return AddWireInternal(name, type, loc, section); } +absl::StatusOr Module::AddLogic(std::string_view name, + DataType* type, + const SourceInfo& loc, + ModuleSection* section) { + XLS_RETURN_IF_ERROR(NoteDefined(&defined_names_, name, "logic")); + if (section == nullptr) { + section = &top_; + } + return file()->Make(loc, section->Add(loc, name, type)); +} + LogicRef* Module::AddWireInternal(std::string_view name, DataType* type, Expression* init, const SourceInfo& loc, ModuleSection* section) { diff --git a/xls/codegen/vast/vast.h b/xls/codegen/vast/vast.h index 6240c3f2ab..b09ce660c6 100644 --- a/xls/codegen/vast/vast.h +++ b/xls/codegen/vast/vast.h @@ -2277,10 +2277,15 @@ class Module final : public VastNode { template inline T* Add(const SourceInfo& loc, Args&&... args); + // Add an input or output port to the module. `data_kind` must be either kWire + // or kLogic and indicates whether to use `wire` or `logic` when defining the + // port if `type` is a bit vector or scalar type. absl::StatusOr AddInput(std::string_view name, DataType* type, - const SourceInfo& loc); + const SourceInfo& loc, + DataKind data_kind = DataKind::kWire); absl::StatusOr AddOutput(std::string_view name, DataType* type, - const SourceInfo& loc); + const SourceInfo& loc, + DataKind data_kind = DataKind::kWire); absl::StatusOr AddReg(std::string_view name, DataType* type, const SourceInfo& loc, @@ -2289,6 +2294,10 @@ class Module final : public VastNode { absl::StatusOr AddWire(std::string_view name, DataType* type, const SourceInfo& loc, ModuleSection* section = nullptr); + absl::StatusOr AddLogic(std::string_view name, DataType* type, + const SourceInfo& loc, + ModuleSection* section = nullptr); + // Variation of AddWire that takes an initializer expression. absl::StatusOr AddWire(std::string_view name, DataType* type, Expression* init, const SourceInfo& loc, @@ -2326,9 +2335,9 @@ class Module final : public VastNode { // Adds a (wire) port to this module with the given name and type. Returns // a reference to that wire. LogicRef* AddInputInternal(std::string_view name, DataType* type, - const SourceInfo& loc); + DataKind data_kind, const SourceInfo& loc); LogicRef* AddOutputInternal(std::string_view name, DataType* type, - const SourceInfo& loc); + DataKind data_kind, const SourceInfo& loc); // Adds a reg/wire definition to the module with the given type and, for regs, // initialized with the given value. Returns a reference to the definition. diff --git a/xls/codegen/vast/vast_test.cc b/xls/codegen/vast/vast_test.cc index 9a83131b05..9f63d26227 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" @@ -1553,6 +1553,34 @@ TEST_P(VastTest, InstantiationWithEmptyPort) { );)"); } +TEST_P(VastTest, ModuleWithLogicDataKind) { + if (GetFileType() != FileType::kSystemVerilog) { + GTEST_SKIP(); + } + + VerilogFile f(GetFileType()); + Module* m = f.AddModule("top", SourceInfo()); + + XLS_ASSERT_OK(m->AddInput("a", f.ScalarType(SourceInfo()), SourceInfo(), + DataKind::kLogic) + .status()); + XLS_ASSERT_OK(m->AddOutput("y", f.BitVectorType(32, SourceInfo()), + SourceInfo(), DataKind::kLogic) + .status()); + + XLS_ASSERT_OK( + m->AddLogic("foo", f.BitVectorType(8, SourceInfo()), SourceInfo()) + .status()); + + EXPECT_EQ(m->Emit(nullptr), + R"(module top( + input logic a, + output logic [31:0] y +); + logic [7:0] foo; +endmodule)"); +} + TEST_P(VastTest, TemplateInstantiationTest) { VerilogFile f(GetFileType()); auto* a_def = diff --git a/xls/public/c_api_symbols.txt b/xls/public/c_api_symbols.txt index 62b0e2d620..ab3e186c3f 100644 --- a/xls/public/c_api_symbols.txt +++ b/xls/public/c_api_symbols.txt @@ -290,6 +290,7 @@ xls_vast_logic_ref_get_name xls_vast_make_verilog_file xls_vast_parameter_ref_as_expression xls_vast_slice_as_expression +xls_vast_statement_block_add_blocking_assignment xls_vast_statement_block_add_nonblocking_assignment xls_vast_verilog_file_add_include xls_vast_verilog_file_add_module @@ -317,8 +318,12 @@ xls_vast_verilog_file_make_slice_i64 xls_vast_verilog_file_make_ternary xls_vast_verilog_file_make_unary xls_vast_verilog_module_add_always_at +xls_vast_verilog_module_add_always_comb xls_vast_verilog_module_add_always_ff xls_vast_verilog_module_add_input +xls_vast_verilog_module_add_logic +xls_vast_verilog_module_add_logic_input +xls_vast_verilog_module_add_logic_output xls_vast_verilog_module_add_member_comment xls_vast_verilog_module_add_member_continuous_assignment xls_vast_verilog_module_add_member_instantiation diff --git a/xls/public/c_api_vast.cc b/xls/public/c_api_vast.cc index 3b1b60730b..f4a5908ebc 100644 --- a/xls/public/c_api_vast.cc +++ b/xls/public/c_api_vast.cc @@ -163,6 +163,28 @@ struct xls_vast_logic_ref* xls_vast_verilog_module_add_output( return reinterpret_cast(logic_ref.value()); } +struct xls_vast_logic_ref* xls_vast_verilog_module_add_logic_input( + struct xls_vast_verilog_module* m, const char* name, + struct xls_vast_data_type* type) { + auto* cpp_module = reinterpret_cast(m); + auto* cpp_type = reinterpret_cast(type); + absl::StatusOr logic_ref = cpp_module->AddInput( + name, cpp_type, xls::SourceInfo(), xls::verilog::DataKind::kLogic); + CHECK_OK(logic_ref.status()); + return reinterpret_cast(logic_ref.value()); +} + +struct xls_vast_logic_ref* xls_vast_verilog_module_add_logic_output( + struct xls_vast_verilog_module* m, const char* name, + struct xls_vast_data_type* type) { + auto* cpp_module = reinterpret_cast(m); + auto* cpp_type = reinterpret_cast(type); + absl::StatusOr logic_ref = cpp_module->AddOutput( + name, cpp_type, xls::SourceInfo(), xls::verilog::DataKind::kLogic); + CHECK_OK(logic_ref.status()); + return reinterpret_cast(logic_ref.value()); +} + struct xls_vast_logic_ref* xls_vast_verilog_module_add_wire( struct xls_vast_verilog_module* m, const char* name, struct xls_vast_data_type* type) { @@ -609,6 +631,23 @@ bool xls_vast_verilog_module_add_always_at( return true; } +bool xls_vast_verilog_module_add_always_comb( + struct xls_vast_verilog_module* m, + struct xls_vast_always_base** out_always_comb, char** error_out) { + auto* cpp_module = reinterpret_cast(m); + xls::verilog::AlwaysComb* cpp_always_comb = + cpp_module->Add(xls::SourceInfo()); + if (cpp_always_comb == nullptr) { + *error_out = xls::ToOwnedCString( + "Failed to create always_comb block in Verilog module."); + *out_always_comb = nullptr; + return false; + } + *out_always_comb = reinterpret_cast(cpp_always_comb); + *error_out = nullptr; + return true; +} + bool xls_vast_verilog_module_add_reg(struct xls_vast_verilog_module* m, const char* name, struct xls_vast_data_type* type, @@ -629,6 +668,25 @@ bool xls_vast_verilog_module_add_reg(struct xls_vast_verilog_module* m, return true; } +bool xls_vast_verilog_module_add_logic( + struct xls_vast_verilog_module* m, const char* name, + struct xls_vast_data_type* type, struct xls_vast_logic_ref** out_logic_ref, + char** error_out) { + auto* cpp_module = reinterpret_cast(m); + auto* cpp_data_type = reinterpret_cast(type); + absl::StatusOr cpp_logic_ref_status = + cpp_module->AddLogic(name, cpp_data_type, xls::SourceInfo()); + if (!cpp_logic_ref_status.ok()) { + *error_out = xls::ToOwnedCString(cpp_logic_ref_status.status().ToString()); + *out_logic_ref = nullptr; + return false; + } + *out_logic_ref = + reinterpret_cast(cpp_logic_ref_status.value()); + *error_out = nullptr; + return true; +} + struct xls_vast_expression* xls_vast_verilog_file_make_pos_edge( struct xls_vast_verilog_file* f, struct xls_vast_expression* signal_expr) { auto* cpp_file = reinterpret_cast(f); @@ -674,6 +732,18 @@ struct xls_vast_statement* xls_vast_statement_block_add_nonblocking_assignment( return reinterpret_cast(cpp_assignment); } +struct xls_vast_statement* xls_vast_statement_block_add_blocking_assignment( + struct xls_vast_statement_block* block, struct xls_vast_expression* lhs, + struct xls_vast_expression* rhs) { + auto* cpp_block = reinterpret_cast(block); + auto* cpp_lhs = reinterpret_cast(lhs); + auto* cpp_rhs = reinterpret_cast(rhs); + xls::verilog::BlockingAssignment* cpp_assignment = + cpp_block->Add(xls::SourceInfo(), + cpp_lhs, cpp_rhs); + return reinterpret_cast(cpp_assignment); +} + struct xls_vast_module_port** xls_vast_verilog_module_get_ports( struct xls_vast_verilog_module* m, size_t* out_count) { CHECK(out_count != nullptr); diff --git a/xls/public/c_api_vast.h b/xls/public/c_api_vast.h index c7f0d123bd..8f33d34b17 100644 --- a/xls/public/c_api_vast.h +++ b/xls/public/c_api_vast.h @@ -143,6 +143,13 @@ struct xls_vast_logic_ref* xls_vast_verilog_module_add_input( struct xls_vast_logic_ref* xls_vast_verilog_module_add_output( struct xls_vast_verilog_module* m, const char* name, struct xls_vast_data_type* type); +// Adds input/output ports using SystemVerilog `logic` for the declaration. +struct xls_vast_logic_ref* xls_vast_verilog_module_add_logic_input( + struct xls_vast_verilog_module* m, const char* name, + struct xls_vast_data_type* type); +struct xls_vast_logic_ref* xls_vast_verilog_module_add_logic_output( + struct xls_vast_verilog_module* m, const char* name, + struct xls_vast_data_type* type); struct xls_vast_logic_ref* xls_vast_verilog_module_add_wire( struct xls_vast_verilog_module* m, const char* name, struct xls_vast_data_type* type); @@ -324,6 +331,12 @@ struct xls_vast_statement* xls_vast_statement_block_add_nonblocking_assignment( struct xls_vast_statement_block* block, struct xls_vast_expression* lhs, struct xls_vast_expression* rhs); +// Adds a blocking assignment statement (lhs = rhs) to a statement block and +// returns a pointer to the created statement. +struct xls_vast_statement* xls_vast_statement_block_add_blocking_assignment( + struct xls_vast_statement_block* block, struct xls_vast_expression* lhs, + struct xls_vast_expression* rhs); + // Emits/formats the contents of the given verilog file to a string. // // Note: caller owns the returned string, to be freed by `xls_c_str_free`. @@ -351,6 +364,13 @@ bool xls_vast_verilog_module_add_always_at( size_t sensitivity_list_count, struct xls_vast_always_base** out_always_at, char** error_out); +// Adds an always_comb block to the module (SystemVerilog). +// Returns true on success. On failure, returns false and sets error_out. The +// caller is responsible for freeing error_out if it is not NULL. +bool xls_vast_verilog_module_add_always_comb( + struct xls_vast_verilog_module* m, + struct xls_vast_always_base** out_always_comb, char** error_out); + // Adds a register (reg) definition to the module. // Returns true on success. On failure, returns false and sets error_out. // The caller is responsible for freeing error_out if it is not NULL. @@ -360,6 +380,14 @@ bool xls_vast_verilog_module_add_reg(struct xls_vast_verilog_module* m, struct xls_vast_logic_ref** out_reg_ref, char** error_out); +// Adds a `logic` variable definition to the module. +// Returns true on success. On failure, returns false and sets error_out. +// The caller is responsible for freeing error_out if it is not NULL. +bool xls_vast_verilog_module_add_logic( + struct xls_vast_verilog_module* m, const char* name, + struct xls_vast_data_type* type, struct xls_vast_logic_ref** out_logic_ref, + char** error_out); + // Creates a positive edge expression (e.g., "posedge clk"). // 'signal_expr' is typically a logic_ref_as_expression. struct xls_vast_expression* xls_vast_verilog_file_make_pos_edge( diff --git a/xls/public/c_api_vast_test.cc b/xls/public/c_api_vast_test.cc index bd694e562b..d0b6bb88fd 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" @@ -1035,6 +1035,83 @@ endmodule INSTANTIATE_TEST_SUITE_P(VastSequentialBlockTestSuite, VastSequentialBlockTest, testing::Bool()); +TEST(XlsCApiTest, VastAlwaysComb) { + 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, "test_module"); + ASSERT_NE(m, nullptr); + + xls_vast_data_type* scalar_type = xls_vast_verilog_file_make_scalar_type(f); + ASSERT_NE(scalar_type, nullptr); + xls_vast_logic_ref* a_ref = + xls_vast_verilog_module_add_logic_input(m, "a", scalar_type); + ASSERT_NE(a_ref, nullptr); + xls_vast_logic_ref* b_ref = + xls_vast_verilog_module_add_logic_output(m, "b", scalar_type); + ASSERT_NE(b_ref, nullptr); + + char* error_out = nullptr; + xls_vast_data_type* u32 = + xls_vast_verilog_file_make_bit_vector_type(f, 32, /*is_signed=*/false); + ASSERT_NE(u32, nullptr); + xls_vast_logic_ref* c_ref = + xls_vast_verilog_module_add_logic_input(m, "c", u32); + ASSERT_NE(c_ref, nullptr); + xls_vast_logic_ref* tmp_ref = nullptr; + ASSERT_TRUE( + xls_vast_verilog_module_add_logic(m, "tmp", u32, &tmp_ref, &error_out)); + ASSERT_EQ(error_out, nullptr); + ASSERT_NE(tmp_ref, nullptr); + + xls_vast_always_base* always_comb_block = nullptr; + ASSERT_TRUE(xls_vast_verilog_module_add_always_comb(m, &always_comb_block, + &error_out)); + ASSERT_EQ(error_out, nullptr); + ASSERT_NE(always_comb_block, nullptr); + + xls_vast_statement_block* stmt_block = + xls_vast_always_base_get_statement_block(always_comb_block); + ASSERT_NE(stmt_block, nullptr); + xls_vast_expression* a_expr = xls_vast_logic_ref_as_expression(a_ref); + ASSERT_NE(a_expr, nullptr); + xls_vast_expression* b_expr = xls_vast_logic_ref_as_expression(b_ref); + ASSERT_NE(b_expr, nullptr); + xls_vast_expression* c_expr = xls_vast_logic_ref_as_expression(c_ref); + ASSERT_NE(c_expr, nullptr); + xls_vast_expression* tmp_expr = xls_vast_logic_ref_as_expression(tmp_ref); + ASSERT_NE(tmp_expr, nullptr); + xls_vast_statement* assign_stmt = + xls_vast_statement_block_add_blocking_assignment(stmt_block, a_expr, + b_expr); + ASSERT_NE(assign_stmt, nullptr); + xls_vast_statement* assign_tmp_to_c = + xls_vast_statement_block_add_blocking_assignment(stmt_block, tmp_expr, + c_expr); + ASSERT_NE(assign_tmp_to_c, nullptr); + + char* emitted = xls_vast_verilog_file_emit(f); + ASSERT_NE(emitted, nullptr); + absl::Cleanup free_emitted([&] { xls_c_str_free(emitted); }); + + const std::string_view kWant = R"(module test_module( + input logic a, + output logic b, + input logic [31:0] c +); + logic [31:0] tmp; + always_comb begin + a = b; + tmp = c; + end +endmodule +)"; + EXPECT_EQ(std::string_view{emitted}, kWant); +} + // Tests enumeration and inspection of module ports. TEST(XlsCApiTest, VastModulePortEnumerationAndInspection) { xls_vast_verilog_file* f =