From c367b671b60f6e8fb995dc2bd1e50a742f166f14 Mon Sep 17 00:00:00 2001 From: Ludwig Rogiers Date: Sat, 13 Jun 2020 08:38:01 +1000 Subject: [PATCH] Support VPI parameter and localparam (#2370) --- include/verilated.cpp | 4 +- include/verilated.h | 4 +- include/verilated_sym_props.h | 7 +- include/verilated_vpi.cpp | 938 +++++++++++++++----------------- src/V3EmitC.cpp | 15 +- src/V3EmitCSyms.cpp | 35 +- test_regress/t/t_vpi_get.cpp | 4 +- test_regress/t/t_vpi_memory.cpp | 14 +- test_regress/t/t_vpi_param.cpp | 276 ++++++++++ test_regress/t/t_vpi_param.pl | 31 ++ test_regress/t/t_vpi_param.v | 58 ++ 11 files changed, 871 insertions(+), 515 deletions(-) create mode 100644 test_regress/t/t_vpi_param.cpp create mode 100755 test_regress/t/t_vpi_param.pl create mode 100644 test_regress/t/t_vpi_param.v diff --git a/include/verilated.cpp b/include/verilated.cpp index f141f7861..c28ac450c 100644 --- a/include/verilated.cpp +++ b/include/verilated.cpp @@ -2603,7 +2603,7 @@ void VerilatedScope::exportInsert(int finalize, const char* namep, void* cb) VL_ } } -void VerilatedScope::varInsert(int finalize, const char* namep, void* datap, +void VerilatedScope::varInsert(int finalize, const char* namep, void* datap, bool isParam, VerilatedVarType vltype, int vlflags, int dims, ...) VL_MT_UNSAFE { // Grab dimensions // In the future we may just create a large table at emit time and @@ -2611,7 +2611,7 @@ void VerilatedScope::varInsert(int finalize, const char* namep, void* datap, if (!finalize) return; if (!m_varsp) m_varsp = new VerilatedVarNameMap(); - VerilatedVar var(namep, datap, vltype, static_cast(vlflags), dims); + VerilatedVar var(namep, datap, vltype, static_cast(vlflags), dims, isParam); va_list ap; va_start(ap, dims); diff --git a/include/verilated.h b/include/verilated.h index 81b65be68..e375146c0 100644 --- a/include/verilated.h +++ b/include/verilated.h @@ -335,8 +335,8 @@ public: // But internals only - called from VerilatedModule's void configure(VerilatedSyms* symsp, const char* prefixp, const char* suffixp, const char* identifier, vlsint8_t timeunit, const Type& type) VL_MT_UNSAFE; void exportInsert(int finalize, const char* namep, void* cb) VL_MT_UNSAFE; - void varInsert(int finalize, const char* namep, void* datap, VerilatedVarType vltype, - int vlflags, int dims, ...) VL_MT_UNSAFE; + void varInsert(int finalize, const char* namep, void* datap, bool isParam, + VerilatedVarType vltype, int vlflags, int dims, ...) VL_MT_UNSAFE; // ACCESSORS const char* name() const { return m_namep; } const char* identifier() const { return m_identifierp; } diff --git a/include/verilated_sym_props.h b/include/verilated_sym_props.h index e95b67ad6..51d154e18 100644 --- a/include/verilated_sym_props.h +++ b/include/verilated_sym_props.h @@ -232,13 +232,15 @@ class VerilatedVar : public VerilatedVarProps { void* m_datap; // Location of data const char* m_namep; // Name - slowpath protected: + bool m_isParam; friend class VerilatedScope; // CONSTRUCTORS VerilatedVar(const char* namep, void* datap, VerilatedVarType vltype, - VerilatedVarFlags vlflags, int dims) + VerilatedVarFlags vlflags, int dims, bool isParam) : VerilatedVarProps(vltype, vlflags, (dims > 0 ? 1 : 0), ((dims > 1) ? dims - 1 : 0)) , m_datap(datap) - , m_namep(namep) {} + , m_namep(namep) + , m_isParam(isParam) {} public: ~VerilatedVar() {} @@ -247,6 +249,7 @@ public: const VerilatedRange& range() const { return packed(); } // Deprecated const VerilatedRange& array() const { return unpacked(); } // Deprecated const char* name() const { return m_namep; } + bool isParam() const { return m_isParam; } }; #endif // Guard diff --git a/include/verilated_vpi.cpp b/include/verilated_vpi.cpp index cd3db9841..c29ad80a9 100644 --- a/include/verilated_vpi.cpp +++ b/include/verilated_vpi.cpp @@ -139,6 +139,32 @@ public: vlsint32_t num() const { return m_num; } }; +class VerilatedVpioParam : public VerilatedVpio { + const VerilatedVar* m_varp; + const VerilatedScope* m_scopep; + +public: + VerilatedVpioParam(const VerilatedVar* varp, const VerilatedScope* scopep) + : m_varp(varp) + , m_scopep(scopep) {} + + virtual ~VerilatedVpioParam() {} + + static inline VerilatedVpioParam* castp(vpiHandle h) { + return dynamic_cast(reinterpret_cast(h)); + } + virtual vluint32_t type() const { return vpiParameter; } + const VerilatedVar* varp() const { return m_varp; } + void* varDatap() const { return m_varp->datap(); } + const VerilatedScope* scopep() const { return m_scopep; } + virtual const char* name() const { return m_varp->name(); } + virtual const char* fullname() const { + static VL_THREAD_LOCAL std::string out; + out = std::string(m_scopep->name()) + "." + name(); + return out.c_str(); + } +}; + class VerilatedVpioRange : public VerilatedVpio { const VerilatedRange* m_range; vlsint32_t m_iteration; @@ -1096,7 +1122,12 @@ vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) { } } if (!varp) return NULL; - return (new VerilatedVpioVar(varp, scopep))->castVpiHandle(); + + if (varp->isParam()) { + return (new VerilatedVpioParam(varp, scopep))->castVpiHandle(); + } else { + return (new VerilatedVpioVar(varp, scopep))->castVpiHandle(); + } } vpiHandle vpi_handle_by_index(vpiHandle object, PLI_INT32 indx) { @@ -1327,583 +1358,506 @@ void vpi_get_delays(vpiHandle /*object*/, p_vpi_delay /*delay_p*/) { _VL_VPI_UNI void vpi_put_delays(vpiHandle /*object*/, p_vpi_delay /*delay_p*/) { _VL_VPI_UNIMP(); } // value processing +bool vl_check_format(const VerilatedVar* varp, const p_vpi_value valuep, const char* fullname, + bool isGetValue) { + bool status = true; + if ((valuep->format == vpiVectorVal) || (valuep->format == vpiBinStrVal) + || (valuep->format == vpiOctStrVal) || (valuep->format == vpiHexStrVal)) { + switch (varp->vltype()) { + case VLVT_UINT8: + case VLVT_UINT16: + case VLVT_UINT32: + case VLVT_UINT64: + case VLVT_WDATA: return status; + default: status = false; + } + } else if (valuep->format == vpiDecStrVal) { + switch (varp->vltype()) { + case VLVT_UINT8: + case VLVT_UINT16: + case VLVT_UINT32: + case VLVT_UINT64: return status; + default: status = false; + } + } else if (valuep->format == vpiStringVal) { + switch (varp->vltype()) { + case VLVT_UINT8: + case VLVT_UINT16: + case VLVT_UINT32: + case VLVT_UINT64: + case VLVT_WDATA: return status; + case VLVT_STRING: + if (isGetValue) { + return status; + } else { + status = false; + break; + } + default: status = false; + } + } else if (valuep->format == vpiIntVal) { + switch (varp->vltype()) { + case VLVT_UINT8: + case VLVT_UINT16: + case VLVT_UINT32: return status; + default: status = false; + } + } else if (valuep->format == vpiSuppressVal) { + return status; + } else { + status = false; + } + _VL_VPI_ERROR(__FILE__, __LINE__, "%s: Unsupported format (%s) for %s", VL_FUNC, + VerilatedVpiError::strFromVpiVal(valuep->format), fullname); + return status; +} -void vpi_get_value(vpiHandle object, p_vpi_value value_p) { +void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep, + const char* fullname) { + if (!vl_check_format(varp, valuep, fullname, true)) return; // Maximum required size is for binary string, one byte per bit plus null termination static VL_THREAD_LOCAL char outStr[1 + VL_MULS_MAX_WORDS * 32]; // cppcheck-suppress variableScope static VL_THREAD_LOCAL int outStrSz = sizeof(outStr) - 1; + // We used to presume vpiValue.format = vpiIntVal or if single bit vpiScalarVal + // This may cause backward compatibility issues with older code. + if (valuep->format == vpiVectorVal) { + // Vector pointer must come from our memory pool + // It only needs to persist until the next vpi_get_value + static VL_THREAD_LOCAL t_vpi_vecval out[VL_MULS_MAX_WORDS * 2]; + valuep->value.vector = out; + if (varp->vltype() == VLVT_UINT8) { + out[0].aval = *(reinterpret_cast(varDatap)); + out[0].bval = 0; + return; + } else if (varp->vltype() == VLVT_UINT16) { + out[0].aval = *(reinterpret_cast(varDatap)); + out[0].bval = 0; + return; + } else if (varp->vltype() == VLVT_UINT32) { + out[0].aval = *(reinterpret_cast(varDatap)); + out[0].bval = 0; + return; + } else if (varp->vltype() == VLVT_UINT64) { + QData data = *(reinterpret_cast(varDatap)); + out[1].aval = static_cast(data >> 32ULL); + out[1].bval = 0; + out[0].aval = static_cast(data); + out[0].bval = 0; + return; + } else if (varp->vltype() == VLVT_WDATA) { + int words = VL_WORDS_I(varp->packed().elements()); + if (VL_UNCOVERABLE(words >= VL_MULS_MAX_WORDS)) { + VL_FATAL_MT( + __FILE__, __LINE__, "", + "vpi_get_value with more than VL_MULS_MAX_WORDS; increase and recompile"); + } + WDataInP datap = (reinterpret_cast(varDatap)); + for (int i = 0; i < words; ++i) { + out[i].aval = datap[i]; + out[i].bval = 0; + } + return; + } + } else if (valuep->format == vpiBinStrVal) { + valuep->value.str = outStr; + int bits = varp->packed().elements(); + CData* datap = (reinterpret_cast(varDatap)); + int i; + if (bits > outStrSz) { + // limit maximum size of output to size of buffer to prevent overrun. + bits = outStrSz; + _VL_VPI_WARNING( + __FILE__, __LINE__, + "%s: Truncating string value of %s for %s" + " as buffer size (%d, VL_MULS_MAX_WORDS=%d) is less than required (%d)", + VL_FUNC, VerilatedVpiError::strFromVpiVal(valuep->format), fullname, outStrSz, + VL_MULS_MAX_WORDS, bits); + } + for (i = 0; i < bits; ++i) { + char val = (datap[i >> 3] >> (i & 7)) & 1; + outStr[bits - i - 1] = val ? '1' : '0'; + } + outStr[i] = '\0'; + return; + } else if (valuep->format == vpiOctStrVal) { + valuep->value.str = outStr; + int chars = (varp->packed().elements() + 2) / 3; + int bytes = VL_BYTES_I(varp->packed().elements()); + CData* datap = (reinterpret_cast(varDatap)); + int i; + if (chars > outStrSz) { + // limit maximum size of output to size of buffer to prevent overrun. + _VL_VPI_WARNING( + __FILE__, __LINE__, + "%s: Truncating string value of %s for %s" + " as buffer size (%d, VL_MULS_MAX_WORDS=%d) is less than required (%d)", + VL_FUNC, VerilatedVpiError::strFromVpiVal(valuep->format), fullname, outStrSz, + VL_MULS_MAX_WORDS, chars); + chars = outStrSz; + } + for (i = 0; i < chars; ++i) { + div_t idx = div(i * 3, 8); + int val = datap[idx.quot]; + if ((idx.quot + 1) < bytes) { + // if the next byte is valid or that in + // for when the required 3 bits straddle adjacent bytes + val |= datap[idx.quot + 1] << 8; + } + // align so least significant 3 bits represent octal char + val >>= idx.rem; + if (i == (chars - 1)) { + // most signifcant char, mask off non existant bits when vector + // size is not a multiple of 3 + unsigned int rem = varp->packed().elements() % 3; + if (rem) { + // generate bit mask & zero non existant bits + val &= (1 << rem) - 1; + } + } + outStr[chars - i - 1] = '0' + (val & 7); + } + outStr[i] = '\0'; + return; + } else if (valuep->format == vpiDecStrVal) { + valuep->value.str = outStr; + // outStrSz does not include NULL termination so add one + if (varp->vltype() == VLVT_UINT8) { + VL_SNPRINTF(outStr, outStrSz + 1, "%hhu", + static_cast(*(reinterpret_cast(varDatap)))); + return; + } else if (varp->vltype() == VLVT_UINT16) { + VL_SNPRINTF(outStr, outStrSz + 1, "%hu", + static_cast(*(reinterpret_cast(varDatap)))); + return; + } else if (varp->vltype() == VLVT_UINT32) { + VL_SNPRINTF(outStr, outStrSz + 1, "%u", + static_cast(*(reinterpret_cast(varDatap)))); + return; + } else if (varp->vltype() == VLVT_UINT64) { + VL_SNPRINTF(outStr, outStrSz + 1, "%llu", + static_cast(*(reinterpret_cast(varDatap)))); + return; + } + } else if (valuep->format == vpiHexStrVal) { + valuep->value.str = outStr; + int chars = (varp->packed().elements() + 3) >> 2; + CData* datap = (reinterpret_cast(varDatap)); + int i; + if (chars > outStrSz) { + // limit maximum size of output to size of buffer to prevent overrun. + _VL_VPI_WARNING( + __FILE__, __LINE__, + "%s: Truncating string value of %s for %s" + " as buffer size (%d, VL_MULS_MAX_WORDS=%d) is less than required (%d)", + VL_FUNC, VerilatedVpiError::strFromVpiVal(valuep->format), fullname, outStrSz, + VL_MULS_MAX_WORDS, chars); + chars = outStrSz; + } + for (i = 0; i < chars; ++i) { + char val = (datap[i >> 1] >> ((i & 1) << 2)) & 15; + if (i == (chars - 1)) { + // most signifcant char, mask off non existant bits when vector + // size is not a multiple of 4 + unsigned int rem = varp->packed().elements() & 3; + if (rem) { + // generate bit mask & zero non existant bits + val &= (1 << rem) - 1; + } + } + outStr[chars - i - 1] = "0123456789abcdef"[static_cast(val)]; + } + outStr[i] = '\0'; + return; + } else if (valuep->format == vpiStringVal) { + if (varp->vltype() == VLVT_STRING) { + valuep->value.str = reinterpret_cast(varDatap); + return; + } else { + valuep->value.str = outStr; + int bytes = VL_BYTES_I(varp->packed().elements()); + CData* datap = (reinterpret_cast(varDatap)); + int i; + if (bytes > outStrSz) { + // limit maximum size of output to size of buffer to prevent overrun. + _VL_VPI_WARNING( + __FILE__, __LINE__, + "%s: Truncating string value of %s for %s" + " as buffer size (%d, VL_MULS_MAX_WORDS=%d) is less than required (%d)", + VL_FUNC, VerilatedVpiError::strFromVpiVal(valuep->format), fullname, outStrSz, + VL_MULS_MAX_WORDS, bytes); + bytes = outStrSz; + } + for (i = 0; i < bytes; ++i) { + char val = datap[bytes - i - 1]; + // other simulators replace [leading?] zero chars with spaces, replicate here. + outStr[i] = val ? val : ' '; + } + outStr[i] = '\0'; + return; + } + } else if (valuep->format == vpiIntVal) { + if (varp->vltype() == VLVT_UINT8) { + valuep->value.integer = *(reinterpret_cast(varDatap)); + return; + } else if (varp->vltype() == VLVT_UINT16) { + valuep->value.integer = *(reinterpret_cast(varDatap)); + return; + } else if (varp->vltype() == VLVT_UINT32) { + valuep->value.integer = *(reinterpret_cast(varDatap)); + return; + } + } else if (valuep->format == vpiSuppressVal) { + return; + } + _VL_VPI_ERROR(__FILE__, __LINE__, "%s: Unsupported format (%s) as requested for %s", VL_FUNC, + VerilatedVpiError::strFromVpiVal(valuep->format), fullname); + return; +} + +void vpi_get_value(vpiHandle object, p_vpi_value valuep) { VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_get_value %p\n", object);); VerilatedVpiImp::assertOneCheck(); _VL_VPI_ERROR_RESET(); - if (VL_UNLIKELY(!value_p)) return; + if (VL_UNLIKELY(!valuep)) return; + if (VerilatedVpioVar* vop = VerilatedVpioVar::castp(object)) { - // We used to presume vpiValue.format = vpiIntVal or if single bit vpiScalarVal - // This may cause backward compatability issues with older code. - if (value_p->format == vpiVectorVal) { - // Vector pointer must come from our memory pool - // It only needs to persist until the next vpi_get_value - static VL_THREAD_LOCAL t_vpi_vecval out[VL_MULS_MAX_WORDS * 2]; - value_p->value.vector = out; - switch (vop->varp()->vltype()) { - case VLVT_UINT8: - out[0].aval = *(reinterpret_cast(vop->varDatap())); - out[0].bval = 0; - return; - case VLVT_UINT16: - out[0].aval = *(reinterpret_cast(vop->varDatap())); - out[0].bval = 0; - return; - case VLVT_UINT32: - out[0].aval = *(reinterpret_cast(vop->varDatap())); - out[0].bval = 0; - return; - case VLVT_WDATA: { - int words = VL_WORDS_I(vop->varp()->packed().elements()); - if (VL_UNCOVERABLE(words >= VL_MULS_MAX_WORDS)) { - VL_FATAL_MT( - __FILE__, __LINE__, "", - "vpi_get_value with more than VL_MULS_MAX_WORDS; increase and recompile"); - } - WDataInP datap = (reinterpret_cast(vop->varDatap())); - for (int i = 0; i < words; ++i) { - out[i].aval = datap[i]; - out[i].bval = 0; - } - return; - } - case VLVT_UINT64: { - QData data = *(reinterpret_cast(vop->varDatap())); - out[1].aval = static_cast(data >> 32ULL); - out[1].bval = 0; - out[0].aval = static_cast(data); - out[0].bval = 0; - return; - } - default: { - _VL_VPI_ERROR(__FILE__, __LINE__, "%s: Unsupported format (%s) for %s", VL_FUNC, - VerilatedVpiError::strFromVpiVal(value_p->format), vop->fullname()); - return; - } - } - } else if (value_p->format == vpiBinStrVal) { - value_p->value.str = outStr; - switch (vop->varp()->vltype()) { - case VLVT_UINT8: - case VLVT_UINT16: - case VLVT_UINT32: - case VLVT_UINT64: - case VLVT_WDATA: { - int bits = vop->varp()->packed().elements(); - CData* datap = (reinterpret_cast(vop->varDatap())); - int i; - if (bits > outStrSz) { - // limit maximum size of output to size of buffer to prevent overrun. - bits = outStrSz; - _VL_VPI_WARNING( - __FILE__, __LINE__, - "%s: Truncating string value of %s for %s" - " as buffer size (%d, VL_MULS_MAX_WORDS=%d) is less than required (%d)", - VL_FUNC, VerilatedVpiError::strFromVpiVal(value_p->format), - vop->fullname(), outStrSz, VL_MULS_MAX_WORDS, bits); - } - for (i = 0; i < bits; ++i) { - char val = (datap[i >> 3] >> (i & 7)) & 1; - outStr[bits - i - 1] = val ? '1' : '0'; - } - outStr[i] = '\0'; - return; - } - default: - _VL_VPI_ERROR(__FILE__, __LINE__, "%s: Unsupported format (%s) for %s", VL_FUNC, - VerilatedVpiError::strFromVpiVal(value_p->format), vop->fullname()); - return; - } - } else if (value_p->format == vpiOctStrVal) { - value_p->value.str = outStr; - switch (vop->varp()->vltype()) { - case VLVT_UINT8: - case VLVT_UINT16: - case VLVT_UINT32: - case VLVT_UINT64: - case VLVT_WDATA: { - int chars = (vop->varp()->packed().elements() + 2) / 3; - int bytes = VL_BYTES_I(vop->varp()->packed().elements()); - CData* datap = (reinterpret_cast(vop->varDatap())); - int i; - if (chars > outStrSz) { - // limit maximum size of output to size of buffer to prevent overrun. - _VL_VPI_WARNING( - __FILE__, __LINE__, - "%s: Truncating string value of %s for %s" - " as buffer size (%d, VL_MULS_MAX_WORDS=%d) is less than required (%d)", - VL_FUNC, VerilatedVpiError::strFromVpiVal(value_p->format), - vop->fullname(), outStrSz, VL_MULS_MAX_WORDS, chars); - chars = outStrSz; - } - for (i = 0; i < chars; ++i) { - div_t idx = div(i * 3, 8); - int val = datap[idx.quot]; - if ((idx.quot + 1) < bytes) { - // if the next byte is valid or that in - // for when the required 3 bits straddle adjacent bytes - val |= datap[idx.quot + 1] << 8; - } - // align so least significant 3 bits represent octal char - val >>= idx.rem; - if (i == (chars - 1)) { - // most signifcant char, mask off non existant bits when vector - // size is not a multiple of 3 - unsigned int rem = vop->varp()->packed().elements() % 3; - if (rem) { - // generate bit mask & zero non existant bits - val &= (1 << rem) - 1; - } - } - outStr[chars - i - 1] = '0' + (val & 7); - } - outStr[i] = '\0'; - return; - } - default: - strcpy(outStr, "0"); - _VL_VPI_ERROR(__FILE__, __LINE__, "%s: Unsupported format (%s) for %s", VL_FUNC, - VerilatedVpiError::strFromVpiVal(value_p->format), vop->fullname()); - return; - } - } else if (value_p->format == vpiDecStrVal) { - value_p->value.str = outStr; - switch (vop->varp()->vltype()) { - // outStrSz does not include NULL termination so add one - case VLVT_UINT8: - VL_SNPRINTF( - outStr, outStrSz + 1, "%hhu", - static_cast(*(reinterpret_cast(vop->varDatap())))); - return; - case VLVT_UINT16: - VL_SNPRINTF( - outStr, outStrSz + 1, "%hu", - static_cast(*(reinterpret_cast(vop->varDatap())))); - return; - case VLVT_UINT32: - VL_SNPRINTF( - outStr, outStrSz + 1, "%u", - static_cast(*(reinterpret_cast(vop->varDatap())))); - return; - case VLVT_UINT64: - VL_SNPRINTF( - outStr, outStrSz + 1, "%llu", - static_cast(*(reinterpret_cast(vop->varDatap())))); - return; - default: - strcpy(outStr, "-1"); - _VL_VPI_ERROR(__FILE__, __LINE__, - "%s: Unsupported format (%s) for %s, maximum limit is 64 bits", - VL_FUNC, VerilatedVpiError::strFromVpiVal(value_p->format), - vop->fullname()); - return; - } - } else if (value_p->format == vpiHexStrVal) { - value_p->value.str = outStr; - switch (vop->varp()->vltype()) { - case VLVT_UINT8: - case VLVT_UINT16: - case VLVT_UINT32: - case VLVT_UINT64: - case VLVT_WDATA: { - int chars = (vop->varp()->packed().elements() + 3) >> 2; - CData* datap = (reinterpret_cast(vop->varDatap())); - int i; - if (chars > outStrSz) { - // limit maximum size of output to size of buffer to prevent overrun. - _VL_VPI_WARNING( - __FILE__, __LINE__, - "%s: Truncating string value of %s for %s" - " as buffer size (%d, VL_MULS_MAX_WORDS=%d) is less than required (%d)", - VL_FUNC, VerilatedVpiError::strFromVpiVal(value_p->format), - vop->fullname(), outStrSz, VL_MULS_MAX_WORDS, chars); - chars = outStrSz; - } - for (i = 0; i < chars; ++i) { - char val = (datap[i >> 1] >> ((i & 1) << 2)) & 15; - if (i == (chars - 1)) { - // most signifcant char, mask off non existant bits when vector - // size is not a multiple of 4 - unsigned int rem = vop->varp()->packed().elements() & 3; - if (rem) { - // generate bit mask & zero non existant bits - val &= (1 << rem) - 1; - } - } - outStr[chars - i - 1] = "0123456789abcdef"[static_cast(val)]; - } - outStr[i] = '\0'; - return; - } - default: - _VL_VPI_ERROR(__FILE__, __LINE__, "%s: Unsupported format (%s) for %s", VL_FUNC, - VerilatedVpiError::strFromVpiVal(value_p->format), vop->fullname()); - return; - } - } else if (value_p->format == vpiStringVal) { - value_p->value.str = outStr; - switch (vop->varp()->vltype()) { - case VLVT_UINT8: - case VLVT_UINT16: - case VLVT_UINT32: - case VLVT_UINT64: - case VLVT_WDATA: { - int bytes = VL_BYTES_I(vop->varp()->packed().elements()); - CData* datap = (reinterpret_cast(vop->varDatap())); - int i; - if (bytes > outStrSz) { - // limit maximum size of output to size of buffer to prevent overrun. - _VL_VPI_WARNING( - __FILE__, __LINE__, - "%s: Truncating string value of %s for %s" - " as buffer size (%d, VL_MULS_MAX_WORDS=%d) is less than required (%d)", - VL_FUNC, VerilatedVpiError::strFromVpiVal(value_p->format), - vop->fullname(), outStrSz, VL_MULS_MAX_WORDS, bytes); - bytes = outStrSz; - } - for (i = 0; i < bytes; ++i) { - char val = datap[bytes - i - 1]; - // other simulators replace [leading?] zero chars with spaces, replicate here. - outStr[i] = val ? val : ' '; - } - outStr[i] = '\0'; - return; - } - default: - _VL_VPI_ERROR(__FILE__, __LINE__, "%s: Unsupported format (%s) for %s", VL_FUNC, - VerilatedVpiError::strFromVpiVal(value_p->format), vop->fullname()); - return; - } - } else if (value_p->format == vpiIntVal) { - switch (vop->varp()->vltype()) { - case VLVT_UINT8: - value_p->value.integer = *(reinterpret_cast(vop->varDatap())); - return; - case VLVT_UINT16: - value_p->value.integer = *(reinterpret_cast(vop->varDatap())); - return; - case VLVT_UINT32: - value_p->value.integer = *(reinterpret_cast(vop->varDatap())); - return; - case VLVT_WDATA: // FALLTHRU - case VLVT_UINT64: // FALLTHRU - default: - value_p->value.integer = 0; - _VL_VPI_ERROR(__FILE__, __LINE__, "%s: Unsupported format (%s) for %s", VL_FUNC, - VerilatedVpiError::strFromVpiVal(value_p->format), vop->fullname()); - return; - } - } else if (value_p->format == vpiSuppressVal) { - return; - } - _VL_VPI_ERROR(__FILE__, __LINE__, "%s: Unsupported format (%s) as requested for %s", - VL_FUNC, VerilatedVpiError::strFromVpiVal(value_p->format), vop->fullname()); + vl_get_value(vop->varp(), vop->varDatap(), valuep, vop->fullname()); + return; + } else if (VerilatedVpioParam* vop = VerilatedVpioParam::castp(object)) { + vl_get_value(vop->varp(), vop->varDatap(), valuep, vop->fullname()); return; } else if (VerilatedVpioConst* vop = VerilatedVpioConst::castp(object)) { - if (value_p->format == vpiIntVal) { - value_p->value.integer = vop->num(); + if (valuep->format == vpiIntVal) { + valuep->value.integer = vop->num(); return; } _VL_VPI_ERROR(__FILE__, __LINE__, "%s: Unsupported format (%s) for %s", VL_FUNC, - VerilatedVpiError::strFromVpiVal(value_p->format), vop->fullname()); + VerilatedVpiError::strFromVpiVal(valuep->format), vop->fullname()); return; } - _VL_VPI_ERROR(__FILE__, __LINE__, "%s: Unsupported vpiHandle (%p)", VL_FUNC, object); } -vpiHandle vpi_put_value(vpiHandle object, p_vpi_value value_p, p_vpi_time /*time_p*/, +vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_p*/, PLI_INT32 /*flags*/) { - VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_put_value %p %p\n", object, value_p);); + VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_put_value %p %p\n", object, valuep);); VerilatedVpiImp::assertOneCheck(); _VL_VPI_ERROR_RESET(); - if (VL_UNLIKELY(!value_p)) { + if (VL_UNLIKELY(!valuep)) { _VL_VPI_WARNING(__FILE__, __LINE__, "Ignoring vpi_put_value with NULL value pointer"); return 0; } if (VerilatedVpioVar* vop = VerilatedVpioVar::castp(object)) { VL_DEBUG_IF_PLI( VL_DBG_MSGF("- vpi: vpi_put_value name=%s fmt=%d vali=%d\n", vop->fullname(), - value_p->format, value_p->value.integer); + valuep->format, valuep->value.integer); VL_DBG_MSGF("- vpi: varp=%p putatp=%p\n", vop->varp()->datap(), vop->varDatap());); + if (VL_UNLIKELY(!vop->varp()->isPublicRW())) { _VL_VPI_WARNING(__FILE__, __LINE__, "Ignoring vpi_put_value to signal marked read-only," - " use public_flat_rw instead: ", + " use public_flat_rw instead: %s", vop->fullname()); return 0; } - if (value_p->format == vpiVectorVal) { - if (VL_UNLIKELY(!value_p->value.vector)) return NULL; - switch (vop->varp()->vltype()) { - case VLVT_UINT8: + if (!vl_check_format(vop->varp(), valuep, vop->fullname(), false)) return 0; + if (valuep->format == vpiVectorVal) { + if (VL_UNLIKELY(!valuep->value.vector)) return NULL; + if (vop->varp()->vltype() == VLVT_UINT8) { *(reinterpret_cast(vop->varDatap())) - = value_p->value.vector[0].aval & vop->mask(); + = valuep->value.vector[0].aval & vop->mask(); return object; - case VLVT_UINT16: + } else if (vop->varp()->vltype() == VLVT_UINT16) { *(reinterpret_cast(vop->varDatap())) - = value_p->value.vector[0].aval & vop->mask(); + = valuep->value.vector[0].aval & vop->mask(); return object; - case VLVT_UINT32: + } else if (vop->varp()->vltype() == VLVT_UINT32) { *(reinterpret_cast(vop->varDatap())) - = value_p->value.vector[0].aval & vop->mask(); + = valuep->value.vector[0].aval & vop->mask(); return object; - case VLVT_WDATA: { + } else if (vop->varp()->vltype() == VLVT_UINT64) { + *(reinterpret_cast(vop->varDatap())) = _VL_SET_QII( + valuep->value.vector[1].aval & vop->mask(), valuep->value.vector[0].aval); + return object; + } else if (vop->varp()->vltype() == VLVT_WDATA) { int words = VL_WORDS_I(vop->varp()->packed().elements()); WDataOutP datap = (reinterpret_cast(vop->varDatap())); for (int i = 0; i < words; ++i) { - datap[i] = value_p->value.vector[i].aval; + datap[i] = valuep->value.vector[i].aval; if (i == (words - 1)) datap[i] &= vop->mask(); } return object; } - case VLVT_UINT64: { - *(reinterpret_cast(vop->varDatap())) = _VL_SET_QII( - value_p->value.vector[1].aval & vop->mask(), value_p->value.vector[0].aval); - return object; - } - default: { - _VL_VPI_ERROR(__FILE__, __LINE__, "%s: Unsupported format (%s) for %s", VL_FUNC, - VerilatedVpiError::strFromVpiVal(value_p->format), vop->fullname()); - return NULL; - } - } - } else if (value_p->format == vpiBinStrVal) { - switch (vop->varp()->vltype()) { - case VLVT_UINT8: - case VLVT_UINT16: - case VLVT_UINT32: - case VLVT_UINT64: - case VLVT_WDATA: { - int bits = vop->varp()->packed().elements(); - int len = strlen(value_p->value.str); - CData* datap = (reinterpret_cast(vop->varDatap())); - for (int i = 0; i < bits; ++i) { - char set = (i < len) ? (value_p->value.str[len - i - 1] == '1') : 0; - // zero bits 7:1 of byte when assigning to bit 0, else - // or in 1 if bit set - if (i & 7) { - datap[i >> 3] |= set << (i & 7); - } else { - datap[i >> 3] = set; - } + } else if (valuep->format == vpiBinStrVal) { + int bits = vop->varp()->packed().elements(); + int len = strlen(valuep->value.str); + CData* datap = (reinterpret_cast(vop->varDatap())); + for (int i = 0; i < bits; ++i) { + char set = (i < len) ? (valuep->value.str[len - i - 1] == '1') : 0; + // zero bits 7:1 of byte when assigning to bit 0, else + // or in 1 if bit set + if (i & 7) { + datap[i >> 3] |= set << (i & 7); + } else { + datap[i >> 3] = set; } - return object; } - default: - _VL_VPI_ERROR(__FILE__, __LINE__, "%s: Unsupported format (%s) for %s", VL_FUNC, - VerilatedVpiError::strFromVpiVal(value_p->format), vop->fullname()); - return 0; - } - } else if (value_p->format == vpiOctStrVal) { - switch (vop->varp()->vltype()) { - case VLVT_UINT8: - case VLVT_UINT16: - case VLVT_UINT32: - case VLVT_UINT64: - case VLVT_WDATA: { - int chars = (vop->varp()->packed().elements() + 2) / 3; - int bytes = VL_BYTES_I(vop->varp()->packed().elements()); - int len = strlen(value_p->value.str); - CData* datap = (reinterpret_cast(vop->varDatap())); - div_t idx; - datap[0] = 0; // reset zero'th byte - for (int i = 0; i < chars; ++i) { - union { - char byte[2]; - short half; - } val; - idx = div(i * 3, 8); - if (i < len) { - // ignore illegal chars - char digit = value_p->value.str[len - i - 1]; - if (digit >= '0' && digit <= '7') { - val.half = digit - '0'; - } else { - _VL_VPI_WARNING( - __FILE__, __LINE__, - "%s: Non octal character '%c' in '%s' as value %s for %s", VL_FUNC, - digit, value_p->value.str, - VerilatedVpiError::strFromVpiVal(value_p->format), - vop->fullname()); - val.half = 0; - } + return object; + } else if (valuep->format == vpiOctStrVal) { + int chars = (vop->varp()->packed().elements() + 2) / 3; + int bytes = VL_BYTES_I(vop->varp()->packed().elements()); + int len = strlen(valuep->value.str); + CData* datap = (reinterpret_cast(vop->varDatap())); + div_t idx; + datap[0] = 0; // reset zero'th byte + for (int i = 0; i < chars; ++i) { + union { + char byte[2]; + short half; + } val; + idx = div(i * 3, 8); + if (i < len) { + // ignore illegal chars + char digit = valuep->value.str[len - i - 1]; + if (digit >= '0' && digit <= '7') { + val.half = digit - '0'; } else { + _VL_VPI_WARNING(__FILE__, __LINE__, + "%s: Non octal character '%c' in '%s' as value %s for %s", + VL_FUNC, digit, valuep->value.str, + VerilatedVpiError::strFromVpiVal(valuep->format), + vop->fullname()); val.half = 0; } - // align octal character to position within vector, note that - // the three bits may straddle a byte bounday so two byte wide - // assignments are made to adjacent bytes - but not if the least - // signifcant byte of the aligned value is the most significant - // byte of the destination. - val.half <<= idx.rem; - datap[idx.quot] |= val.byte[0]; // or in value - if ((idx.quot + 1) < bytes) { - datap[idx.quot + 1] = val.byte[1]; // this also resets - // all bits to 0 prior to or'ing above - } + } else { + val.half = 0; } - // mask off non existant bits in the most significant byte - if (idx.quot == (bytes - 1)) { - datap[idx.quot] &= vop->mask_byte(idx.quot); - } else if (idx.quot + 1 == (bytes - 1)) { - datap[idx.quot + 1] &= vop->mask_byte(idx.quot + 1); + // align octal character to position within vector, note that + // the three bits may straddle a byte boundary so two byte wide + // assignments are made to adjacent bytes - but not if the least + // significant byte of the aligned value is the most significant + // byte of the destination. + val.half <<= idx.rem; + datap[idx.quot] |= val.byte[0]; // or in value + if ((idx.quot + 1) < bytes) { + datap[idx.quot + 1] = val.byte[1]; // this also resets + // all bits to 0 prior to or'ing above } - // zero off remaining top bytes - for (int i = idx.quot + 2; i < bytes; ++i) datap[i] = 0; - return object; } - default: - _VL_VPI_ERROR(__FILE__, __LINE__, "%s: Unsupported format (%s) for %s", VL_FUNC, - VerilatedVpiError::strFromVpiVal(value_p->format), vop->fullname()); - return 0; + // mask off non-existent bits in the most significant byte + if (idx.quot == (bytes - 1)) { + datap[idx.quot] &= vop->mask_byte(idx.quot); + } else if (idx.quot + 1 == (bytes - 1)) { + datap[idx.quot + 1] &= vop->mask_byte(idx.quot + 1); } - } else if (value_p->format == vpiDecStrVal) { + // zero off remaining top bytes + for (int i = idx.quot + 2; i < bytes; ++i) datap[i] = 0; + return object; + } else if (valuep->format == vpiDecStrVal) { char remainder[16]; unsigned long long val; - int success = sscanf(value_p->value.str, "%30llu%15s", &val, remainder); + int success = sscanf(valuep->value.str, "%30llu%15s", &val, remainder); if (success < 1) { _VL_VPI_ERROR(__FILE__, __LINE__, "%s: Parsing failed for '%s' as value %s for %s", - VL_FUNC, value_p->value.str, - VerilatedVpiError::strFromVpiVal(value_p->format), vop->fullname()); + VL_FUNC, valuep->value.str, + VerilatedVpiError::strFromVpiVal(valuep->format), vop->fullname()); return 0; } if (success > 1) { - _VL_VPI_WARNING( - __FILE__, __LINE__, "%s: Trailing garbage '%s' in '%s' as value %s for %s", - VL_FUNC, remainder, value_p->value.str, - VerilatedVpiError::strFromVpiVal(value_p->format), vop->fullname()); + _VL_VPI_WARNING(__FILE__, __LINE__, + "%s: Trailing garbage '%s' in '%s' as value %s for %s", VL_FUNC, + remainder, valuep->value.str, + VerilatedVpiError::strFromVpiVal(valuep->format), vop->fullname()); } - switch (vop->varp()->vltype()) { - case VLVT_UINT8: + if (vop->varp()->vltype() == VLVT_UINT8) { *(reinterpret_cast(vop->varDatap())) = val & vop->mask(); - break; - case VLVT_UINT16: + return object; + } else if (vop->varp()->vltype() == VLVT_UINT16) { *(reinterpret_cast(vop->varDatap())) = val & vop->mask(); - break; - case VLVT_UINT32: + return object; + } else if (vop->varp()->vltype() == VLVT_UINT32) { *(reinterpret_cast(vop->varDatap())) = val & vop->mask(); - break; - case VLVT_UINT64: + return object; + } else if (vop->varp()->vltype() == VLVT_UINT64) { *(reinterpret_cast(vop->varDatap())) = val; (reinterpret_cast(vop->varDatap()))[1] &= vop->mask(); - break; - case VLVT_WDATA: - default: - _VL_VPI_ERROR(__FILE__, __LINE__, - "%s: Unsupported format (%s) for %s, maximum limit is 64 bits", - VL_FUNC, VerilatedVpiError::strFromVpiVal(value_p->format), - vop->fullname()); - return 0; + return object; } - return object; - } else if (value_p->format == vpiHexStrVal) { - switch (vop->varp()->vltype()) { - case VLVT_UINT8: - case VLVT_UINT16: - case VLVT_UINT32: - case VLVT_UINT64: - case VLVT_WDATA: { - int chars = (vop->varp()->packed().elements() + 3) >> 2; - CData* datap = (reinterpret_cast(vop->varDatap())); - char* val = value_p->value.str; - // skip hex ident if one is detected at the start of the string - if (val[0] == '0' && (val[1] == 'x' || val[1] == 'X')) val += 2; - int len = strlen(val); - for (int i = 0; i < chars; ++i) { - char hex; - // compute hex digit value - if (i < len) { - char digit = val[len - i - 1]; - if (digit >= '0' && digit <= '9') { - hex = digit - '0'; - } else if (digit >= 'a' && digit <= 'f') { - hex = digit - 'a' + 10; - } else if (digit >= 'A' && digit <= 'F') { - hex = digit - 'A' + 10; - } else { - _VL_VPI_WARNING( - __FILE__, __LINE__, - "%s: Non hex character '%c' in '%s' as value %s for %s", VL_FUNC, - digit, value_p->value.str, - VerilatedVpiError::strFromVpiVal(value_p->format), - vop->fullname()); - hex = 0; - } + } else if (valuep->format == vpiHexStrVal) { + int chars = (vop->varp()->packed().elements() + 3) >> 2; + CData* datap = (reinterpret_cast(vop->varDatap())); + char* val = valuep->value.str; + // skip hex ident if one is detected at the start of the string + if (val[0] == '0' && (val[1] == 'x' || val[1] == 'X')) val += 2; + int len = strlen(val); + for (int i = 0; i < chars; ++i) { + char hex; + // compute hex digit value + if (i < len) { + char digit = val[len - i - 1]; + if (digit >= '0' && digit <= '9') { + hex = digit - '0'; + } else if (digit >= 'a' && digit <= 'f') { + hex = digit - 'a' + 10; + } else if (digit >= 'A' && digit <= 'F') { + hex = digit - 'A' + 10; } else { + _VL_VPI_WARNING(__FILE__, __LINE__, + "%s: Non hex character '%c' in '%s' as value %s for %s", + VL_FUNC, digit, valuep->value.str, + VerilatedVpiError::strFromVpiVal(valuep->format), + vop->fullname()); hex = 0; } - // assign hex digit value to destination - if (i & 1) { - datap[i >> 1] |= hex << 4; - } else { - datap[i >> 1] = hex; // this also resets all - // bits to 0 prior to or'ing above of the msb - } + } else { + hex = 0; } - // apply bit mask to most significant byte - datap[(chars - 1) >> 1] &= vop->mask_byte((chars - 1) >> 1); - return object; - } - default: - _VL_VPI_ERROR(__FILE__, __LINE__, "%s: Unsupported format (%s) for %s", VL_FUNC, - VerilatedVpiError::strFromVpiVal(value_p->format), vop->fullname()); - return 0; - } - } else if (value_p->format == vpiStringVal) { - switch (vop->varp()->vltype()) { - case VLVT_UINT8: - case VLVT_UINT16: - case VLVT_UINT32: - case VLVT_UINT64: - case VLVT_WDATA: { - int bytes = VL_BYTES_I(vop->varp()->packed().elements()); - int len = strlen(value_p->value.str); - CData* datap = (reinterpret_cast(vop->varDatap())); - for (int i = 0; i < bytes; ++i) { - // prepend with 0 values before placing string the least signifcant bytes - datap[i] = (i < len) ? value_p->value.str[len - i - 1] : 0; + // assign hex digit value to destination + if (i & 1) { + datap[i >> 1] |= hex << 4; + } else { + datap[i >> 1] = hex; // this also resets all + // bits to 0 prior to or'ing above of the msb } - return object; } - default: - _VL_VPI_ERROR(__FILE__, __LINE__, "%s: Unsupported format (%s) for %s", VL_FUNC, - VerilatedVpiError::strFromVpiVal(value_p->format), vop->fullname()); - return 0; + // apply bit mask to most significant byte + datap[(chars - 1) >> 1] &= vop->mask_byte((chars - 1) >> 1); + return object; + } else if (valuep->format == vpiStringVal) { + int bytes = VL_BYTES_I(vop->varp()->packed().elements()); + int len = strlen(valuep->value.str); + CData* datap = (reinterpret_cast(vop->varDatap())); + for (int i = 0; i < bytes; ++i) { + // prepend with 0 values before placing string the least significant bytes + datap[i] = (i < len) ? valuep->value.str[len - i - 1] : 0; } - } else if (value_p->format == vpiIntVal) { - switch (vop->varp()->vltype()) { - case VLVT_UINT8: - *(reinterpret_cast(vop->varDatap())) - = vop->mask() & value_p->value.integer; + return object; + } else if (valuep->format == vpiIntVal) { + if (vop->varp()->vltype() == VLVT_UINT8) { + *(reinterpret_cast(vop->varDatap())) = vop->mask() & valuep->value.integer; return object; - case VLVT_UINT16: - *(reinterpret_cast(vop->varDatap())) - = vop->mask() & value_p->value.integer; + } else if (vop->varp()->vltype() == VLVT_UINT16) { + *(reinterpret_cast(vop->varDatap())) = vop->mask() & valuep->value.integer; return object; - case VLVT_UINT32: - *(reinterpret_cast(vop->varDatap())) - = vop->mask() & value_p->value.integer; + } else if (vop->varp()->vltype() == VLVT_UINT32) { + *(reinterpret_cast(vop->varDatap())) = vop->mask() & valuep->value.integer; return object; - case VLVT_WDATA: // FALLTHRU - case VLVT_UINT64: // FALLTHRU - default: - _VL_VPI_ERROR(__FILE__, __LINE__, "%s: Unsupported format (%s) for %s", VL_FUNC, - VerilatedVpiError::strFromVpiVal(value_p->format), vop->fullname()); - return 0; } } _VL_VPI_ERROR(__FILE__, __LINE__, "%s: Unsupported format (%s) as requested for %s", - VL_FUNC, VerilatedVpiError::strFromVpiVal(value_p->format), vop->fullname()); + VL_FUNC, VerilatedVpiError::strFromVpiVal(valuep->format), vop->fullname()); return NULL; + } else if (VerilatedVpioParam* vop = VerilatedVpioParam::castp(object)) { + _VL_VPI_WARNING(__FILE__, __LINE__, "%s: Ignoring vpi_put_value to vpiParameter: %s", + VL_FUNC, vop->fullname()); + return 0; + } else if (VerilatedVpioConst* vop = VerilatedVpioConst::castp(object)) { + _VL_VPI_WARNING(__FILE__, __LINE__, "%s: Ignoring vpi_put_value to vpiConstant: %s", + VL_FUNC, vop->fullname()); + return 0; } - _VL_VPI_ERROR(__FILE__, __LINE__, "%s: Unsupported format (%s) for ??", VL_FUNC, - VerilatedVpiError::strFromVpiVal(value_p->format)); + _VL_VPI_ERROR(__FILE__, __LINE__, "%s: Unsupported vpiHandle (%p)", VL_FUNC, object); return NULL; } diff --git a/src/V3EmitC.cpp b/src/V3EmitC.cpp index 258598ab0..be8de4508 100644 --- a/src/V3EmitC.cpp +++ b/src/V3EmitC.cpp @@ -3053,6 +3053,10 @@ void EmitCImp::emitInt(AstNodeModule* modp) { // support them. They also cause problems with GDB under GCC2.95. if (varp->isWide()) { // Unsupported for output putsDecoration("// enum WData " + varp->nameProtect() + " //wide"); + } else if (varp->isString()) { + puts("const std::string " + protect("var_" + varp->name()) + " = "); + iterateAndNextNull(varp->valuep()); + puts(";"); } else if (!VN_IS(varp->valuep(), Const)) { // Unsupported for output // putsDecoration("// enum ..... "+varp->nameProtect() // +"not simple value, see variable above instead"); @@ -3060,11 +3064,16 @@ void EmitCImp::emitInt(AstNodeModule* modp) { && VN_CAST(varp->dtypep(), BasicDType) ->isOpaque()) { // Can't put out e.g. doubles } else { - puts("enum "); - puts(varp->isQuad() ? "_QData" : "_IData"); + // enum + puts(varp->isQuad() ? "enum _QData" : "enum _IData"); puts("" + varp->nameProtect() + " { " + varp->nameProtect() + " = "); iterateAndNextNull(varp->valuep()); - puts("};"); + puts("};\n"); + // var + puts(varp->isQuad() ? "const QData" : "const IData"); + puts(" var_" + varp->nameProtect() + " = "); + iterateAndNextNull(varp->valuep()); + puts(";"); } puts("\n"); } diff --git a/src/V3EmitCSyms.cpp b/src/V3EmitCSyms.cpp index bc5ce4587..fb6221245 100644 --- a/src/V3EmitCSyms.cpp +++ b/src/V3EmitCSyms.cpp @@ -332,10 +332,7 @@ class EmitCSyms : EmitCBaseVisitor { virtual void visit(AstVar* nodep) VL_OVERRIDE { nameCheck(nodep); iterateChildren(nodep); - if (nodep->isSigUserRdPublic() - // The VPI functions require a pointer to allow modification, - // but parameters are constants - && !nodep->isParam()) { + if (nodep->isSigUserRdPublic()) { m_modVars.push_back(make_pair(m_modp, nodep)); } } @@ -794,16 +791,32 @@ void EmitCSyms::emitSymImp() { } puts(protect("__Vscope_" + it->second.m_scopeName) + ".varInsert(__Vfinal,"); putsQuoted(protect(it->second.m_varBasePretty)); - puts(", &("); + + std::string varName; if (modp->isTop()) { - puts(protectIf(scopep->nameDotless() + "p", scopep->protect())); - puts("->"); + varName += (protectIf(scopep->nameDotless() + "p", scopep->protect()) + "->"); } else { - puts(protectIf(scopep->nameDotless(), scopep->protect())); - puts("."); + varName += (protectIf(scopep->nameDotless(), scopep->protect()) + "."); } - puts(varp->nameProtect()); - puts("), "); + + if (varp->isParam()) { + varName += protect("var_" + varp->name()); + } else { + varName += protect(varp->name()); + } + + if (varp->isParam() && (varp->vlEnumType() == "VLVT_STRING")) { + puts(", const_cast(static_cast("); + puts(varName.c_str()); + puts(".c_str())), "); + } else { + puts(", const_cast(static_cast(&("); + puts(varName.c_str()); + puts("))), "); + } + + puts(varp->isParam() ? "true" : "false"); + puts(", "); puts(varp->vlEnumType()); // VLVT_UINT32 etc puts(","); puts(varp->vlEnumDir()); // VLVD_IN etc diff --git a/test_regress/t/t_vpi_get.cpp b/test_regress/t/t_vpi_get.cpp index 08d4dcfef..9a18662f6 100644 --- a/test_regress/t/t_vpi_get.cpp +++ b/test_regress/t/t_vpi_get.cpp @@ -81,7 +81,9 @@ unsigned int main_time = 0; #define CHECK_RESULT_CSTR_STRIP(got, exp) CHECK_RESULT_CSTR(got + strspn(got, " "), exp) static int _mon_check_props(TestVpiHandle& handle, int size, int direction, int scalar, int type) { - s_vpi_value value = {.format = vpiIntVal, .value = {.integer = 0}}; + s_vpi_value value; + value.format = vpiIntVal; + value.value.integer = 0; // check size of object int vpisize = vpi_get(vpiSize, handle); CHECK_RESULT(vpisize, size); diff --git a/test_regress/t/t_vpi_memory.cpp b/test_regress/t/t_vpi_memory.cpp index e3a85982b..60891c5a6 100644 --- a/test_regress/t/t_vpi_memory.cpp +++ b/test_regress/t/t_vpi_memory.cpp @@ -82,7 +82,9 @@ unsigned int main_time = 0; int _mon_check_range(TestVpiHandle& handle, int size, int left, int right) { TestVpiHandle iter_h, left_h, right_h; - s_vpi_value value = {.format = vpiIntVal, .value = {.integer = 0}}; + s_vpi_value value; + value.format = vpiIntVal; + value.value.integer = 0; // check size of object int vpisize = vpi_get(vpiSize, handle); CHECK_RESULT(vpisize, size); @@ -108,7 +110,11 @@ int _mon_check_memory() { int cnt; TestVpiHandle mem_h, lcl_h, side_h; vpiHandle iter_h; // Icarus does not like auto free of iterator handles - s_vpi_value value = {.format = vpiIntVal, .value = {.integer = 0}}; + s_vpi_value value; + value.format = vpiIntVal; + value.value.integer = 0; + s_vpi_error_info e; + vpi_printf((PLI_BYTE8*)"Check memory vpi ...\n"); mem_h = vpi_handle_by_name((PLI_BYTE8*)TestSimulator::rooted("mem0"), NULL); CHECK_RESULT_NZ(mem_h); @@ -169,6 +175,10 @@ int _mon_check_memory() { vpi_get_value(side_h, &value); CHECK_RESULT(value.value.integer, 1); + // check writing to vpiConstant + vpi_put_value(side_h, &value, NULL, vpiNoDelay); + CHECK_RESULT_NZ(vpi_chk_error(&e)); + return 0; // Ok } diff --git a/test_regress/t/t_vpi_param.cpp b/test_regress/t/t_vpi_param.cpp new file mode 100644 index 000000000..f2b5f4e10 --- /dev/null +++ b/test_regress/t/t_vpi_param.cpp @@ -0,0 +1,276 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// +// Copyright 2010-2011 by Wilson Snyder. This program is free software; you can +// redistribute it and/or modify it under the terms of either the GNU +// Lesser General Public License Version 3 or the Perl Artistic License +// Version 2.0. +// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 +// +//************************************************************************* + +#ifdef IS_VPI + +#include "vpi_user.h" +#include + +#else + +#include "Vt_vpi_param.h" +#include "verilated.h" +#include "svdpi.h" + +#include "Vt_vpi_param__Dpi.h" + +#include "verilated_vpi.h" +#include "verilated_vcd_c.h" + +#endif + +#include +#include +#include + +#include "TestSimulator.h" +#include "TestVpi.h" + +// __FILE__ is too long +#define FILENM "t_vpi_param.cpp" + +#define DEBUG \ + if (0) printf + +unsigned int main_time = 0; + +//====================================================================== + +#define CHECK_RESULT_VH(got, exp) \ + if ((got) != (exp)) { \ + printf("%%Error: %s:%d: GOT = %p EXP = %p\n", FILENM, __LINE__, (got), (exp)); \ + return __LINE__; \ + } + +#define CHECK_RESULT_NZ(got) \ + if (!(got)) { \ + printf("%%Error: %s:%d: GOT = NULL EXP = !NULL\n", FILENM, __LINE__); \ + return __LINE__; \ + } + +// Use cout to avoid issues with %d/%lx etc +#define CHECK_RESULT(got, exp) \ + if ((got) != (exp)) { \ + std::cout << std::dec << "%Error: " << FILENM << ":" << __LINE__ << ": GOT = " << (got) \ + << " EXP = " << (exp) << std::endl; \ + return __LINE__; \ + } + +#define CHECK_RESULT_HEX(got, exp) \ + if ((got) != (exp)) { \ + std::cout << std::dec << "%Error: " << FILENM << ":" << __LINE__ << std::hex \ + << ": GOT = " << (got) << " EXP = " << (exp) << std::endl; \ + return __LINE__; \ + } + +#define CHECK_RESULT_CSTR(got, exp) \ + if (strcmp((got), (exp))) { \ + printf("%%Error: %s:%d: GOT = '%s' EXP = '%s'\n", FILENM, __LINE__, \ + (got) ? (got) : "", (exp) ? (exp) : ""); \ + return __LINE__; \ + } + +#define CHECK_RESULT_CSTR_STRIP(got, exp) CHECK_RESULT_CSTR(got + strspn(got, " "), exp) + + +int check_param_int(std::string name, PLI_INT32 format, int exp_value, bool verbose) { + int vpi_type; + TestVpiHandle param_h; + s_vpi_value value; + value.format = format; + value.value.integer = 0; + s_vpi_error_info e; + const char* p; + + vpi_printf((PLI_BYTE8*)"Check parameter %s vpi ...\n", name.c_str()); + param_h = vpi_handle_by_name((PLI_BYTE8*)TestSimulator::rooted(name.c_str()), NULL); + CHECK_RESULT_NZ(param_h); + vpi_type = vpi_get(vpiType, param_h); + CHECK_RESULT(vpi_type, vpiParameter); + if (verbose) {vpi_printf((PLI_BYTE8*)" vpiType: %s (%d)\n", vpi_get_str(vpiType, param_h), vpi_type); } + + // attributes + p = vpi_get_str(vpiName, param_h); + CHECK_RESULT_CSTR(p, name.c_str()); + p = vpi_get_str(vpiFullName, param_h); + CHECK_RESULT_CSTR(p, std::string("t." + name).c_str()); + p = vpi_get_str(vpiType, param_h); + CHECK_RESULT_CSTR(p, "vpiParameter"); + vpi_type = vpi_get(vpiLocalParam, param_h); + CHECK_RESULT_NZ(vpi_chk_error(&e)); + if (verbose && vpi_chk_error(&e)) {vpi_printf((PLI_BYTE8*)" vpi_chk_error: %s\n", e.message); } + + // values + if (verbose) {vpi_printf((PLI_BYTE8*)" Try writing value to %s ...\n", name.c_str()); } + value.value.integer = exp_value; + vpi_put_value(param_h, &value, NULL, vpiNoDelay); + CHECK_RESULT_NZ(vpi_chk_error(&e)); + if (verbose && vpi_chk_error(&e)) {vpi_printf((PLI_BYTE8*)" vpi_chk_error: %s\n", e.message); } + + if (verbose) {vpi_printf((PLI_BYTE8*)" Try reading value of %s ...\n", name.c_str()); } + vpi_get_value(param_h, &value); + CHECK_RESULT_NZ(!vpi_chk_error(&e)); + if (verbose && vpi_chk_error(&e)) {vpi_printf((PLI_BYTE8*)" vpi_chk_error: %s\n", e.message); } + if (verbose) {vpi_printf((PLI_BYTE8*)" value of %s: %d\n", name.c_str(), value.value.integer); } + CHECK_RESULT(value.value.integer, exp_value); + + return 0; +} + +int check_param_str(std::string name, PLI_INT32 format, std::string exp_value, bool verbose) { + int vpi_type; + TestVpiHandle param_h; + s_vpi_value value; + value.format = format; + value.value.integer = 0; + s_vpi_error_info e; + const char* p; + + vpi_printf((PLI_BYTE8*)"Check parameter %s vpi ...\n", name.c_str()); + param_h = vpi_handle_by_name((PLI_BYTE8*)TestSimulator::rooted(name.c_str()), NULL); + CHECK_RESULT_NZ(param_h); + vpi_type = vpi_get(vpiType, param_h); + CHECK_RESULT(vpi_type, vpiParameter); + if (verbose) {vpi_printf((PLI_BYTE8*)" vpiType: %s (%d)\n", vpi_get_str(vpiType, param_h), vpi_type); } + + // attributes + p = vpi_get_str(vpiName, param_h); + CHECK_RESULT_CSTR(p, name.c_str()); + p = vpi_get_str(vpiFullName, param_h); + CHECK_RESULT_CSTR(p, std::string("t." + name).c_str()); + p = vpi_get_str(vpiType, param_h); + CHECK_RESULT_CSTR(p, "vpiParameter"); + vpi_type = vpi_get(vpiLocalParam, param_h); + CHECK_RESULT_NZ(vpi_chk_error(&e)); + if (verbose && vpi_chk_error(&e)) {vpi_printf((PLI_BYTE8*)" vpi_chk_error: %s\n", e.message); } + + // values + if (verbose) {vpi_printf((PLI_BYTE8*)" Try writing value to %s ...\n", name.c_str()); } + value.value.str = (PLI_BYTE8*) exp_value.c_str(); + vpi_put_value(param_h, &value, NULL, vpiNoDelay); + CHECK_RESULT_NZ(vpi_chk_error(&e)); + if (verbose && vpi_chk_error(&e)) {vpi_printf((PLI_BYTE8*)" vpi_chk_error: %s\n", e.message); } + + if (verbose) {vpi_printf((PLI_BYTE8*)" Try reading value of %s ...\n", name.c_str()); } + vpi_get_value(param_h, &value); + CHECK_RESULT_NZ(!vpi_chk_error(&e)); + if (verbose && vpi_chk_error(&e)) {vpi_printf((PLI_BYTE8*)" vpi_chk_error: %s\n", e.message); } + if (verbose) {vpi_printf((PLI_BYTE8*)" value of %s: %s\n", name.c_str(), value.value.str); } + CHECK_RESULT_CSTR(value.value.str, exp_value.c_str()); + + return 0; +} + +int _mon_check_param() { + int status = 0; +#ifdef TEST_VERBOSE + bool verbose = true; +#else + bool verbose = false; +#endif + + status += check_param_int("WIDTH", vpiIntVal, 32, verbose); + status += check_param_int("DEPTH", vpiIntVal, 16, verbose); + status += check_param_str("PARAM_LONG", vpiHexStrVal, "fedcba9876543210", verbose); + status += check_param_str("PARAM_STR", vpiStringVal, "'some string value'", verbose); + return status; +} + +int mon_check() { + // Callback from initial block in monitor + if (int status = _mon_check_param()) return status; + return 0; // Ok +} + +//====================================================================== + +#ifdef IS_VPI + +static int mon_check_vpi() { + vpiHandle href = vpi_handle(vpiSysTfCall, 0); + s_vpi_value vpi_value; + + vpi_value.format = vpiIntVal; + vpi_value.value.integer = mon_check(); + vpi_put_value(href, &vpi_value, NULL, vpiNoDelay); + + return 0; +} + +static s_vpi_systf_data vpi_systf_data[] = {{vpiSysFunc, vpiIntFunc, (PLI_BYTE8*)"$mon_check", + (PLI_INT32(*)(PLI_BYTE8*))mon_check_vpi, 0, 0, 0}, + 0}; + +// cver entry +void vpi_compat_bootstrap(void) { + p_vpi_systf_data systf_data_p; + systf_data_p = &(vpi_systf_data[0]); + while (systf_data_p->type != 0) + vpi_register_systf(systf_data_p++); +} + +// icarus entry +void (*vlog_startup_routines[])() = {vpi_compat_bootstrap, 0}; + +#else + +double sc_time_stamp() { return main_time; } +int main(int argc, char** argv, char** env) { + double sim_time = 1100; + Verilated::commandArgs(argc, argv); + Verilated::debug(0); + // we're going to be checking for these errors do don't crash out + Verilated::fatalOnVpiError(0); + + VM_PREFIX* topp = new VM_PREFIX(""); // Note null name - we're flattening it out + +#ifdef VERILATOR +#ifdef TEST_VERBOSE + Verilated::scopesDump(); +#endif +#endif + +#if VM_TRACE + Verilated::traceEverOn(true); + VL_PRINTF("Enabling waves...\n"); + VerilatedVcdC* tfp = new VerilatedVcdC; + topp->trace(tfp, 99); + tfp->open(VL_STRINGIFY(TEST_OBJ_DIR) "/simx.vcd"); +#endif + + topp->eval(); + topp->clk = 0; + main_time += 10; + + while (sc_time_stamp() < sim_time && !Verilated::gotFinish()) { + main_time += 1; + topp->eval(); + VerilatedVpi::callValueCbs(); + topp->clk = !topp->clk; + // mon_do(); +#if VM_TRACE + if (tfp) tfp->dump(main_time); +#endif + } + if (!Verilated::gotFinish()) { + vl_fatal(FILENM, __LINE__, "main", "%Error: Timeout; never got a $finish"); + } + topp->final(); + +#if VM_TRACE + if (tfp) tfp->close(); +#endif + + VL_DO_DANGLING(delete topp, topp); + exit(0L); +} + +#endif diff --git a/test_regress/t/t_vpi_param.pl b/test_regress/t/t_vpi_param.pl new file mode 100755 index 000000000..f6900846a --- /dev/null +++ b/test_regress/t/t_vpi_param.pl @@ -0,0 +1,31 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2010 by Wilson Snyder. This program is free software; you +# can redistribute it and/or modify it under the terms of either the GNU +# Lesser General Public License Version 3 or the Perl Artistic License +# Version 2.0. +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +scenarios(simulator => 1); + +skip("Known compiler limitation") + if $Self->cxx_version =~ /\(GCC\) 4.4/; + +compile( + make_top_shell => 0, + make_main => 0, + make_pli => 1, + iv_flags2 => ["-g2005-sv -D USE_VPI_NOT_DPI"], + v_flags2 => ["+define+USE_VPI_NOT_DPI"], + verilator_flags2 => ["-CFLAGS '-DVL_DEBUG -ggdb' --exe --vpi --no-l2name $Self->{t_dir}/t_vpi_param.cpp"], + ); + +execute( + iv_pli => 1, + check_finished => 1 + ); + +ok(1); +1; diff --git a/test_regress/t/t_vpi_param.v b/test_regress/t/t_vpi_param.v new file mode 100644 index 000000000..082658ff6 --- /dev/null +++ b/test_regress/t/t_vpi_param.v @@ -0,0 +1,58 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// Copyright 2010 by Wilson Snyder. This program is free software; you can +// redistribute it and/or modify it under the terms of either the GNU +// Lesser General Public License Version 3 or the Perl Artistic License +// Version 2.0. +// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +`ifdef USE_VPI_NOT_DPI +//We call it via $c so we can verify DPI isn't required - see bug572 +`else +import "DPI-C" context function integer mon_check(); +`endif + + +module t #( + parameter int WIDTH /* verilator public_flat_rd */ = 32 + ) (/*AUTOARG*/ + // Inputs + clk + ); + +`ifdef VERILATOR +`systemc_header +extern "C" int mon_check(); +`verilog +`endif + + input clk; + + localparam int DEPTH /* verilator public_flat_rd */ = 16; + localparam longint PARAM_LONG /* verilator public_flat_rd */ = 64'hFEDCBA9876543210; + localparam string PARAM_STR /* verilator public_flat_rd */ = "'some string value'"; + + reg [WIDTH-1:0] mem0 [DEPTH:1] /*verilator public_flat_rw @(posedge clk) */; + integer i, status; + + // Test loop + initial begin +`ifdef VERILATOR + status = $c32("mon_check()"); +`endif +`ifdef IVERILOG + status = $mon_check(); +`endif +`ifndef USE_VPI_NOT_DPI + status = mon_check(); +`endif + + if (status!=0) begin + $write("%%Error: t_vpi_param.cpp:%0d: C Test failed\n", status); + $stop; + end + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule : t