diff --git a/include/verilated_vpi.cpp b/include/verilated_vpi.cpp index b78ecf329..e4273ba30 100644 --- a/include/verilated_vpi.cpp +++ b/include/verilated_vpi.cpp @@ -2233,11 +2233,6 @@ static bool vl_vpi_parse_indices(std::string& name, std::vector& indi VlVpiBitRange* bitRange = nullptr) { if (name.empty() || name.back() != ']') return false; - // Collapse consecutive spaces into single spaces - name.erase( - std::unique(name.begin(), name.end(), [](char a, char b) { return a == ' ' && b == ' '; }), - name.end()); - // Only parse brackets after the last escaped identifier's terminating space size_t escapeSpacePos = std::string::npos; const size_t backslashPos = name.rfind('\\'); @@ -2307,12 +2302,16 @@ vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) { if (VL_UNLIKELY(!namep)) return nullptr; VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_handle_by_name %s %p\n", namep, scope);); - // Parse any array indices and optional bit range from the name - // e.g., "mem[0][3][2]" or "signal[15:8]" or "mem[0][3][15:8]" std::string scopeAndName = namep; + + // Collapse consecutive spaces into single spaces (can occur with escaped identifiers) + scopeAndName.erase(std::unique(scopeAndName.begin(), scopeAndName.end(), + [](char a, char b) { return a == ' ' && b == ' '; }), + scopeAndName.end()); + static thread_local std::vector indices; VlVpiBitRange bitRange; - const bool hasIndices = vl_vpi_parse_indices(scopeAndName, indices, &bitRange); + bool hasIndices = false; const VerilatedVar* varp = nullptr; const VerilatedScope* scopep; @@ -2328,6 +2327,9 @@ vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) { { // This doesn't yet follow the hierarchy in the proper way bool isPackage = false; + + // Scopes in generate blocks can also end with an index, so look it up first before + // interpreting the trailing brackets as array indices or bit selects. scopep = Verilated::threadContextp()->scopeFind(scopeAndName.c_str()); if (scopep) { // Whole thing found as a scope if (scopep->type() == VerilatedScope::SCOPE_MODULE) { @@ -2367,6 +2369,11 @@ vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) { scopename = scopeAndName.substr(0, pos); if (scopename == "$unit") scopename = "\\$unit "; } + + // Parse any array indices and optional bit range from the name + // e.g., "mem[0][3][2]" or "signal[15:8]" or "mem[0][3][15:8]" + hasIndices = vl_vpi_parse_indices(basename, indices, &bitRange); + if (prevpos == std::string::npos) { // scopename is a toplevel (no '.' separator), so search in our TOP ports first. scopep = Verilated::threadContextp()->scopeFind("TOP"); diff --git a/test_regress/t/t_vpi_var.cpp b/test_regress/t/t_vpi_var.cpp index 80eaacf8a..9c1686c2d 100644 --- a/test_regress/t/t_vpi_var.cpp +++ b/test_regress/t/t_vpi_var.cpp @@ -9,6 +9,7 @@ // //************************************************************************* +#include "vpi_user.h" #ifdef IS_VPI #include "sv_vpi_user.h" @@ -1215,6 +1216,38 @@ int _mon_check_multi_index() { CHECK_RESULT(vpi_get(vpiSize, vh_two_escapes_ps), 4); } + // vpi_handle_by_name with generated signal + { + // Retrieve signal + TestVpiHandle vh_generated = vpi_handle_by_name((PLI_BYTE8*)"t.gen[0].gen_sig", NULL); + CHECK_RESULT_NZ(vh_generated); + CHECK_RESULT(vpi_get(vpiType, vh_generated), vpiReg); + CHECK_RESULT(vpi_get(vpiSize, vh_generated), 8); + vpi_get_value(vh_generated, &v); + CHECK_RESULT(v.value.integer, 0xAB); + + // Single bit indexing + TestVpiHandle vh_generated_bit + = vpi_handle_by_name((PLI_BYTE8*)"t.gen[0].gen_sig[3]", NULL); + CHECK_RESULT_NZ(vh_generated_bit); + CHECK_RESULT(vpi_get(vpiType, vh_generated_bit), vpiReg); + CHECK_RESULT(vpi_get(vpiSize, vh_generated_bit), 1); + vpi_get_value(vh_generated_bit, &v); + CHECK_RESULT(v.value.integer, 1); + + // Generated scope + TestVpiHandle vh_generated_scope = vpi_handle_by_name((PLI_BYTE8*)"t.subs[1]", NULL); + CHECK_RESULT_NZ(vh_generated_scope); + CHECK_RESULT(vpi_get(vpiType, vh_generated_scope), vpiGenScope); + + // Signal in generated instance + TestVpiHandle vh_generated_inst + = vpi_handle_by_name((PLI_BYTE8*)"t.subs[1].subsub.subsig1", NULL); + CHECK_RESULT_NZ(vh_generated_inst); + CHECK_RESULT(vpi_get(vpiType, vh_generated_inst), vpiReg); + CHECK_RESULT(vpi_get(vpiSize, vh_generated_inst), 1); + } + // vpi_handle_by_name with array indexing { TestVpiHandle vh_1d = vpi_handle_by_name((PLI_BYTE8*)"t.quads[2]", nullptr); diff --git a/test_regress/t/t_vpi_var.v b/test_regress/t/t_vpi_var.v index c84ff3f44..df522a3dc 100644 --- a/test_regress/t/t_vpi_var.v +++ b/test_regress/t/t_vpi_var.v @@ -76,6 +76,12 @@ extern "C" int mon_check(); logic [31:0] some_mem [4] /* verilator public_flat_rd */ = {0, 0, 0, 432}; + generate + for (genvar i = 0; i < 1; i++) begin : gen + wire [7:0] gen_sig /*verilator public_flat_rw*/ = 8'hAB; + end + endgenerate + sub sub(); // Test loop diff --git a/test_regress/t/t_vpi_var2.v b/test_regress/t/t_vpi_var2.v index 8d9910853..d4dd09164 100644 --- a/test_regress/t/t_vpi_var2.v +++ b/test_regress/t/t_vpi_var2.v @@ -94,6 +94,12 @@ extern "C" int mon_check(); logic [31:0] some_mem [4] = {0, 0, 0, 432}; /*verilator public_off*/ + generate + for (genvar i = 0; i < 1; i++) begin : gen + wire [7:0] gen_sig /*verilator public_flat_rw*/ = 8'hAB; + end + endgenerate + sub sub(); // Test loop diff --git a/test_regress/t/t_vpi_var3.v b/test_regress/t/t_vpi_var3.v index f9d16a7d5..34434ee40 100644 --- a/test_regress/t/t_vpi_var3.v +++ b/test_regress/t/t_vpi_var3.v @@ -74,6 +74,12 @@ extern "C" int mon_check(); localparam int nullptr = 123; logic [31:0] some_mem [4] = {0, 0, 0, 432}; + generate + for (genvar i = 0; i < 1; i++) begin : gen + wire [7:0] gen_sig /*verilator public_flat_rw*/ = 8'hAB; + end + endgenerate + sub sub(); // Test loop