/* * Copyright (c) 2001-2020 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU * General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* * vpiReg handles are handled here. These objects represent vectors of * .var objects that can be manipulated by the VPI module. */ # include "compile.h" # include "vpi_priv.h" # include "vvp_net_sig.h" # include "vvp_island.h" # include "schedule.h" # include "statistics.h" # include "config.h" #ifdef CHECK_WITH_VALGRIND # include "vvp_cleanup.h" #endif # include # include # include # include # include # include # include #ifdef CHECK_WITH_VALGRIND # include #endif # include "ivl_alloc.h" /* * Hex digits that represent 4-value bits of Verilog are not as * trivially obvious to display as if the bits were the usual 2-value * bits. So, although it is possible to write a function that * generates a correct character for 4*4-value bits, it is easier to * just perform the lookup in a table. This only takes 256 bytes, * which is not many executable instructions:-) * * The table is calculated at compile time, therefore, by the * draw_tt.c program. */ extern const char hex_digits[256]; extern const char oct_digits[64]; /* * The string values need a result buf to hold the results. This * buffer can be reused for that purpose. Whenever I have a need, the * need_result_buf function makes sure that need can be met. */ void *need_result_buf(unsigned cnt, vpi_rbuf_t type) { static void*result_buf[2] = {0, 0}; static size_t result_buf_size[2] = {0, 0}; if (type == RBUF_DEL) { free(result_buf[RBUF_VAL]); result_buf[RBUF_VAL] = 0; result_buf_size[RBUF_VAL] = 0; free(result_buf[RBUF_STR]); result_buf[RBUF_STR] = 0; result_buf_size[RBUF_STR] = 0; return 0; } cnt = (cnt + 0x0fff) & ~0x0fff; if (result_buf_size[type] == 0) { result_buf[type] = malloc(cnt); result_buf_size[type] = cnt; } else if (result_buf_size[type] < cnt) { result_buf[type] = realloc(result_buf[type], cnt); result_buf_size[type] = cnt; } return result_buf[type]; } char *simple_set_rbuf_str(const char *s1) { char *res = (char *) need_result_buf(strlen(s1)+1, RBUF_STR); if (res) strcpy(res,s1); return res; } char *generic_get_str(int code, vpiHandle ref, const char *name, const char *index) { size_t len = strlen(name) + 1; /* include space for null termination */ char *bn = NULL; if (code == vpiFullName) { bn = strdup(vpi_get_str(code,ref)); len += strlen(bn) + 1; /* include space for "." separator */ } if (index != NULL) len += strlen(index) + 2; /* include space for brackets */ char *res = (char *) need_result_buf(len, RBUF_STR); if (!res) { free(bn); return NULL; } *res=0; /* start with nothing */ /* if this works, I can make it more efficient later */ if (bn != NULL) { strcat(res, bn); strcat(res, "."); free(bn); } strcat(res, name); if (index != NULL) { strcat(res, "["); strcat(res, index); strcat(res, "]"); } return res; } static vpiHandle fill_in_net4(struct __vpiSignal*obj, __vpiScope*scope, const char*name, int msb, int lsb, bool signed_flag, vvp_net_t*node); static vpiHandle fill_in_var4(struct __vpiSignal*obj, const char*name, int msb, int lsb, bool signed_flag, vvp_net_t*node) { // Variable declarations are always resolved immediately, // so we can assume they belong in the current scope. return fill_in_net4(obj, vpip_peek_current_scope(), name, msb, lsb, signed_flag, node); } /* * The standard formatting/conversion routines. * They work with full or partial signals. */ static void format_vpiBinStrVal(vvp_signal_value*sig, int base, unsigned wid, s_vpi_value*vp) { char *rbuf = (char *) need_result_buf(wid+1, RBUF_VAL); long end = base + (signed)wid; long offset = end - 1; long ssize = (signed)sig->value_size(); for (long idx = base ; idx < end ; idx += 1) { if (idx < 0 || idx >= ssize) { rbuf[offset-idx] = 'x'; } else { rbuf[offset-idx] = vvp_bit4_to_ascii(sig->value(idx)); } } rbuf[wid] = 0; vp->value.str = rbuf; } static void format_vpiOctStrVal(vvp_signal_value*sig, int base, unsigned wid, s_vpi_value*vp) { unsigned dwid = (wid + 2) / 3; char *rbuf = (char *) need_result_buf(dwid+1, RBUF_VAL); long end = base + (signed)wid; long ssize = (signed)sig->value_size(); unsigned val = 0; rbuf[dwid] = 0; for (long idx = base ; idx < end ; idx += 1) { unsigned bit = 0; if (idx < 0 || idx >= ssize) { bit = 2; // BIT4_X } else { switch (sig->value(idx)) { case BIT4_0: bit = 0; break; case BIT4_1: bit = 1; break; case BIT4_X: bit = 2; break; case BIT4_Z: bit = 3; break; } } val |= bit << 2*((idx-base) % 3); if ((idx-base) % 3 == 2) { dwid -= 1; rbuf[dwid] = oct_digits[val]; val = 0; } } /* Fill in X or Z if they are the only thing in the value. */ switch (wid % 3) { case 1: if (val == 2) val = 42; else if (val == 3) val = 63; break; case 2: if (val == 10) val = 42; else if (val == 15) val = 63; break; } if (dwid > 0) rbuf[0] = oct_digits[val]; vp->value.str = rbuf; } static void format_vpiHexStrVal(vvp_signal_value*sig, int base, unsigned wid, s_vpi_value*vp) { unsigned dwid = (wid + 3) / 4; char *rbuf = (char *) need_result_buf(dwid+1, RBUF_VAL); long end = base + (signed)wid; long ssize = (signed)sig->value_size(); unsigned val = 0; rbuf[dwid] = 0; for (long idx = base ; idx < end ; idx += 1) { unsigned bit = 0; if (idx < 0 || idx >= ssize) { bit = 2; // BIT4_X } else { switch (sig->value(idx)) { case BIT4_0: bit = 0; break; case BIT4_1: bit = 1; break; case BIT4_X: bit = 2; break; case BIT4_Z: bit = 3; break; } } val |= bit << 2*((idx-base) % 4); if ((idx-base) % 4 == 3) { dwid -= 1; rbuf[dwid] = hex_digits[val]; val = 0; } } /* Fill in X or Z if they are the only thing in the value. */ switch (wid % 4) { case 1: if (val == 2) val = 170; else if (val == 3) val = 255; break; case 2: if (val == 10) val = 170; else if (val == 15) val = 255; break; case 3: if (val == 42) val = 170; else if (val == 63) val = 255; break; } if (dwid > 0) rbuf[0] = hex_digits[val]; vp->value.str = rbuf; } static void format_vpiDecStrVal(vvp_signal_value*sig, int base, unsigned wid, int signed_flag, s_vpi_value*vp) { unsigned hwid = (sig->value_size()+2) / 3 + 1; char *rbuf = (char *) need_result_buf(hwid, RBUF_VAL); long ssize = (signed)sig->value_size(); long end = base + (signed)wid; /* Do we have an end outside of the real signal vector. */ if (base < 0 || end > ssize) { bool all_x = true; if (end > ssize) end = ssize; if (base < 0) base = 0; for (long idx = base ; idx < end ; idx += 1) { if (sig->value(idx) != BIT4_X) { all_x = false; break; } } if (all_x) { rbuf[0] = 'x'; } else { rbuf[0] = 'X'; } rbuf[1] = 0; vp->value.str = rbuf; return; } vvp_vector4_t vec4; if (base == 0 && end == ssize) { sig->vec4_value(vec4); } else { vvp_vector4_t tmp; sig->vec4_value(tmp); vec4 = tmp.subvalue(base, wid); } vpip_vec4_to_dec_str(vec4, rbuf, hwid, signed_flag); vp->value.str = rbuf; } static void format_vpiIntVal(vvp_signal_value*sig, int base, unsigned wid, int signed_flag, s_vpi_value*vp) { vvp_vector4_t tmp; sig->vec4_value(tmp); vvp_vector4_t sub = tmp.subvalue(base, wid); // Normally, we'd be OK with just using long in the call to // vector4_to_value, but some compilers seem to take long as // distinct from int32_t AND int64_t. Since the condition is // constant, the compiler should eliminate the dead code. if (sizeof(vp->value.integer) == sizeof(int32_t)) { int32_t val = 0; vector4_to_value(sub, val, signed_flag, false); vp->value.integer = val; } else { assert(sizeof(vp->value.integer) == sizeof(int64_t)); int64_t val = 0; vector4_to_value(sub, val, signed_flag, false); vp->value.integer = val; } } static void format_vpiRealVal(vvp_signal_value*sig, int base, unsigned wid, int signed_flag, s_vpi_value*vp) { vvp_vector4_t vec4(wid); long ssize = (signed)sig->value_size(); long end = base + (signed)wid; if (end > ssize) end = ssize; for (long idx = (base < 0) ? 0 : base ; idx < end ; idx += 1) { vec4.set_bit(idx-base, sig->value(idx)); } vp->value.real = 0.0; vector4_to_value(vec4, vp->value.real, signed_flag); } static void format_vpiStringVal(vvp_signal_value*sig, int base, unsigned wid, s_vpi_value*vp) { /* The result will use a character for each 8 bits of the vector. Add one extra character for the highest bits that don't form an 8 bit group. */ char *rbuf = (char *) need_result_buf(wid/8 + ((wid&7)!=0) + 1, RBUF_VAL); char *cp = rbuf; char tmp = 0; for (long idx = base+(signed)wid-1; idx >= base; idx -= 1) { tmp <<= 1; if (idx >=0 && idx < (signed)sig->value_size() && sig->value(idx) == BIT4_1) { tmp |= 1; } if (((idx-base)&7)==0){ /* Skip leading nulls. */ if (tmp == 0 && cp == rbuf) continue; /* Nulls in the middle get turned into spaces. */ *cp++ = tmp ? tmp : ' '; tmp = 0; } } *cp++ = 0; vp->value.str = rbuf; } static void format_vpiScalarVal(vvp_signal_value*sig, int base, s_vpi_value*vp) { if (base >= 0 && base < (signed)sig->value_size()) { switch (sig->value(base)) { case BIT4_0: vp->value.scalar = vpi0; break; case BIT4_1: vp->value.scalar = vpi1; break; case BIT4_X: { vvp_scalar_t strn = sig->scalar_value(base); if (strn.strength0() == 1) vp->value.scalar = vpiH; else if (strn.strength1() == 1) vp->value.scalar = vpiL; else vp->value.scalar = vpiX; break; } case BIT4_Z: vp->value.scalar = vpiZ; break; } } else { vp->value.scalar = vpiX; } } static void format_vpiStrengthVal(vvp_signal_value*sig, int base, unsigned wid, s_vpi_value*vp) { long end = base + (signed)wid; s_vpi_strengthval*op; op = (s_vpi_strengthval*) need_result_buf(wid * sizeof(s_vpi_strengthval), RBUF_VAL); for (long idx = base ; idx < end ; idx += 1) { if (idx >=0 && idx < (signed)sig->value_size()) { vvp_scalar_t val = sig->scalar_value(idx); /* vvp_scalar_t strengths are 0-7, but the vpi strength is bit0-bit7. This gets the vpi form of the strengths from the vvp_scalar_t strengths. */ unsigned s0 = 1 << val.strength0(); unsigned s1 = 1 << val.strength1(); switch (val.value()) { case BIT4_0: op[idx-base].logic = vpi0; op[idx-base].s0 = s0|s1; op[idx-base].s1 = 0; break; case BIT4_1: op[idx-base].logic = vpi1; op[idx-base].s0 = 0; op[idx-base].s1 = s0|s1; break; case BIT4_X: op[idx-base].logic = vpiX; op[idx-base].s0 = s0; op[idx-base].s1 = s1; break; case BIT4_Z: op[idx-base].logic = vpiZ; op[idx-base].s0 = vpiHiZ; op[idx-base].s1 = vpiHiZ; break; } } else { op[idx-base].logic = vpiX; op[idx-base].s0 = vpiStrongDrive; op[idx-base].s1 = vpiStrongDrive; } } vp->value.strength = op; } static void format_vpiVectorVal(vvp_signal_value*sig, int base, unsigned wid, s_vpi_value*vp) { long end = base + (signed)wid; unsigned int obit = 0; unsigned hwid = (wid + 31)/32; s_vpi_vecval *op = (p_vpi_vecval) need_result_buf(hwid * sizeof(s_vpi_vecval), RBUF_VAL); vp->value.vector = op; op->aval = op->bval = 0; for (long idx = base ; idx < end ; idx += 1) { if (base >= 0 && base < (signed)sig->value_size()) { switch (sig->value(idx)) { case BIT4_0: op->aval &= ~(1 << obit); op->bval &= ~(1 << obit); break; case BIT4_1: op->aval |= (1 << obit); op->bval &= ~(1 << obit); break; case BIT4_X: op->aval |= (1 << obit); op->bval |= (1 << obit); break; case BIT4_Z: op->aval &= ~(1 << obit); op->bval |= (1 << obit); break; } } else { /* BIT4_X */ op->aval |= (1 << obit); op->bval |= (1 << obit); } obit++; if (!(obit % 32)) { op += 1; if ((op - vp->value.vector) < (ptrdiff_t)hwid) op->aval = op->bval = 0; obit = 0; } } } /* * implement vpi_get for vpiReg objects. */ static int signal_get(int code, vpiHandle ref) { struct __vpiSignal*rfp = dynamic_cast<__vpiSignal*>(ref); assert(rfp); switch (code) { case vpiLineNo: return 0; // Not implemented for now! case vpiSigned: return rfp->signed_flag != 0; case vpiArray: return rfp->is_netarray != 0; case vpiIndex: // This only works while we have a single index. if (rfp->is_netarray) { s_vpi_value vp; vp.format = vpiIntVal; vpi_get_value(rfp->id.index, &vp); return vp.value.integer; } else { return vpiUndefined; } case vpiSize: return rfp->width(); case vpiNetType: if (ref->get_type_code()==vpiNet) return vpiWire; else return vpiUndefined; case vpiLeftRange: return rfp->msb.get_value(); case vpiRightRange: return rfp->lsb.get_value(); case vpiScalar: return (rfp->msb.get_value() == 0 && rfp->lsb.get_value() == 0); case vpiVector: return (rfp->msb.get_value() != rfp->lsb.get_value()); case vpiAutomatic: return vpip_scope(rfp)->is_automatic() ? 1 : 0; #ifdef BR916_STOPGAP_FIX case _vpiFromThr: return _vpiNoThr; #endif // This private property must return zero when undefined. case _vpiNexusId: if (rfp->msb.get_value() == rfp->lsb.get_value()) return (int) (uintptr_t) rfp->node; else return 0; default: fprintf(stderr, "VPI error: unknown signal_get property %d.\n", code); return vpiUndefined; } } static char* signal_get_str(int code, vpiHandle ref) { struct __vpiSignal*rfp = dynamic_cast<__vpiSignal*>(ref); assert(rfp); if (code == vpiFile) { // Not implemented for now! return simple_set_rbuf_str(file_names[0]); } if ((code != vpiName) && (code != vpiFullName)) return NULL; char *nm, *ixs; if (rfp->is_netarray) { nm = strdup(vpi_get_str(vpiName, rfp->within.parent)); s_vpi_value vp; vp.format = vpiDecStrVal; vpi_get_value(rfp->id.index, &vp); ixs = vp.value.str; /* do I need to strdup() this? */ } else { nm = strdup(rfp->id.name); ixs = NULL; } /* The scope information is added here for vpiFullName. */ char *rbuf = generic_get_str(code, vpip_scope(rfp), nm, ixs); free(nm); return rbuf; } static vpiHandle signal_get_handle(int code, vpiHandle ref) { struct __vpiSignal*rfp = dynamic_cast<__vpiSignal*>(ref); assert(rfp); switch (code) { case vpiParent: return rfp->is_netarray? rfp->within.parent : 0; case vpiIndex: return rfp->is_netarray? rfp->id.index : 0; case vpiLeftRange: return &rfp->msb; case vpiRightRange: return &rfp->lsb; case vpiScope: return vpip_scope(rfp); case vpiModule: return vpip_module(vpip_scope(rfp)); } return 0; } static vpiHandle signal_iterate(int code, vpiHandle ref) { struct __vpiSignal*rfp = dynamic_cast<__vpiSignal*>(ref); assert(rfp); if (code == vpiIndex) { return rfp->is_netarray ? rfp->id.index->vpi_iterate(code) : 0; } return 0; } void __vpiSignal::make_bits() { assert(bits == NULL); bits = new struct __vpiBit[width() + 1]; // Make word[-1] point to the parent. bits->parent = this; // Now point to word[0]. bits += 1; // Update each bit to point to the base struct __vpiBit*base = bits; for (unsigned idx = 0; idx < width(); idx += 1) { base[idx].bit0 = base; int real_idx; if (msb.get_value() >= lsb.get_value()) { real_idx = idx + lsb.get_value(); } else { real_idx = lsb.get_value() - idx; } base[idx].index = new __vpiDecConst(real_idx); } } vpiHandle __vpiSignal::get_index(int idx) { /* Check to see if the index is in range. */ if (msb.get_value() >= lsb.get_value()) { if ((idx > msb.get_value()) || (idx < lsb.get_value())) return 0; } else { if ((idx < msb.get_value()) || (idx > lsb.get_value())) return 0; } /* Normalize the index */ unsigned norm_idx; if (msb.get_value() >= lsb.get_value()) { norm_idx = idx - lsb.get_value(); } else { norm_idx = lsb.get_value() - idx; } if (bits == NULL) make_bits(); return &(bits[norm_idx].as_bit); } void __vpiSignal::get_bit_value(struct __vpiBit*bit, p_vpi_value vp) { unsigned index = bit->get_norm_index(); vvp_signal_value*vsig = dynamic_cast(node->fil); assert(vsig); if (vp->format == vpiObjTypeVal) { vp->format = vpiIntVal; } switch (vp->format) { case vpiBinStrVal: format_vpiBinStrVal(vsig, index, 1, vp); break; case vpiOctStrVal: format_vpiOctStrVal(vsig, index, 1, vp); break; case vpiDecStrVal: format_vpiDecStrVal(vsig, index, 1, false, vp); break; case vpiHexStrVal: format_vpiHexStrVal(vsig, index, 1, vp); break; case vpiStringVal: format_vpiStringVal(vsig, index, 1, vp); break; case vpiIntVal: format_vpiIntVal(vsig, index, 1, false, vp); break; case vpiRealVal: format_vpiRealVal(vsig, index, 1, false, vp); break; case vpiScalarVal: format_vpiScalarVal(vsig, index, vp); break; case vpiStrengthVal: format_vpiStrengthVal(vsig, index, 1, vp); break; case vpiVectorVal: format_vpiVectorVal(vsig, index, 1, vp); break; default: fprintf(stderr, "internal error: get_value() " "type %d is not implemented for bit " "select %s[%d] in scope %s.\n", (int)vp->format, vpi_get_str(vpiName), bit->get_index(), vpip_scope(this)->scope_name()); assert(0); }; } vpiHandle __vpiSignal::put_bit_value(struct __vpiBit*bit, p_vpi_value vp, int flags) { unsigned index = bit->get_norm_index(); vvp_net_ptr_t dest(node, 0); vvp_vector4_t val = vec4_from_vpi_value(vp, 1); if ((flags == vpiForceFlag) || (flags == vpiReleaseFlag)) { fprintf(stderr, "Sorry: vpi_put_value() for %s does not " "currently support force/release.\n", bit->as_bit.vpi_get_str(vpiFullName)); return NULL; } if ((get_type_code() == vpiNet) && !dynamic_cast(node->fun)) { node->send_vec4_pv(val, index, 1, width(), vthread_get_wt_context()); } else { vvp_send_vec4_pv(dest, val, index, 1, width(), vthread_get_wt_context()); } // This is not a scheduled event so there is no event to return return NULL; } static vpiHandle signal_index(int idx, vpiHandle ref) { struct __vpiSignal*rfp = dynamic_cast<__vpiSignal*>(ref); assert(rfp); /* We can only get the bit for a net or reg. */ PLI_INT32 type = vpi_get(vpiType, ref); if ((type != vpiNet) && (type != vpiReg)) return 0; return rfp->get_index(idx); } unsigned __vpiSignal::width(void) const { unsigned wid = (msb.get_value() >= lsb.get_value()) ? (msb.get_value() - lsb.get_value() + 1) : (lsb.get_value() - msb.get_value() + 1); return wid; } /* * The get_value method reads the values of the functors and returns * the vector to the caller. This causes no side-effect, and reads the * variables like a %load would. */ static void signal_get_value(vpiHandle ref, s_vpi_value*vp) { struct __vpiSignal*rfp = dynamic_cast<__vpiSignal*>(ref); assert(rfp); unsigned wid = rfp->width(); vvp_signal_value*vsig = dynamic_cast(rfp->node->fil); assert(vsig); switch (vp->format) { case vpiIntVal: format_vpiIntVal(vsig, 0, wid, rfp->signed_flag, vp); break; case vpiScalarVal: format_vpiScalarVal(vsig, 0, vp); break; case vpiStrengthVal: format_vpiStrengthVal(vsig, 0, wid, vp); break; case vpiBinStrVal: format_vpiBinStrVal(vsig, 0, wid, vp); break; case vpiHexStrVal: format_vpiHexStrVal(vsig, 0, wid, vp); break; case vpiOctStrVal: format_vpiOctStrVal(vsig, 0, wid, vp); break; case vpiDecStrVal: format_vpiDecStrVal(vsig, 0, wid, rfp->signed_flag, vp); break; case vpiStringVal: format_vpiStringVal(vsig, 0, wid, vp); break; case vpiVectorVal: format_vpiVectorVal(vsig, 0, wid, vp); break; case vpiRealVal: format_vpiRealVal(vsig, 0, wid, rfp->signed_flag, vp); break; case vpiObjTypeVal: if (wid == 1) { vp->format = vpiScalarVal; format_vpiScalarVal(vsig, 0, vp); } else { vp->format = vpiVectorVal; format_vpiVectorVal(vsig, 0, wid, vp); } break; default: fprintf(stderr, "vvp internal error: get_value: " "value type %d not implemented." " Signal is %s in scope %s\n", (int)vp->format, vpi_get_str(vpiName, ref), vpip_scope(rfp)->scope_name()); assert(0); } } /* * The put_value method writes the value into the vector, and returns * the affected ref. This operation works much like the %set or * %assign instructions and causes all the side-effects that the * equivalent instruction would cause. */ static vvp_vector4_t from_stringval(const char*str, unsigned wid) { unsigned idx; const char*cp; cp = str + strlen(str); idx = 0; vvp_vector4_t val(wid, BIT4_0); while ((idx < wid) && (cp > str)) { unsigned byte = *--cp; int bit; for (bit = 0 ; (bit < 8) && (idx < wid) ; bit += 1) { if (byte & 1) val.set_bit(idx, BIT4_1); byte >>= 1; idx += 1; } } return val; } static vpiHandle signal_put_value(vpiHandle ref, s_vpi_value*vp, int flags) { unsigned wid; struct __vpiSignal*rfp = dynamic_cast<__vpiSignal*>(ref); assert(rfp); vvp_net_ptr_t dest(rfp->node, 0); bool net_flag = ref->get_type_code()==vpiNet; /* If this is a release, then we are not really putting a value. Instead, issue a release "command" to the signal node to cause it to release a forced value. Note that if this net is attached to an island, we need to rerun the calculations immediately so we can return the released value. */ if (flags == vpiReleaseFlag) { assert(rfp->node->fil); rfp->node->fil->force_unlink(); rfp->node->fil->release(dest, net_flag); rfp->node->fun->force_flag(true); signal_get_value(ref, vp); return ref; } /* Make a vvp_vector4_t vector to receive the translated value that we are going to poke. This will get populated differently depending on the format. */ wid = (rfp->msb.get_value() >= rfp->lsb.get_value()) ? (rfp->msb.get_value() - rfp->lsb.get_value() + 1) : (rfp->lsb.get_value() - rfp->msb.get_value() + 1); vvp_vector4_t val = vec4_from_vpi_value(vp, wid); if (flags == vpiForceFlag) { vvp_vector2_t mask (vvp_vector2_t::FILL1, wid); rfp->node->force_vec4(val, mask); } else if (net_flag && !dynamic_cast(rfp->node->fun)) { rfp->node->send_vec4(val, vthread_get_wt_context()); } else { vvp_send_vec4(dest, val, vthread_get_wt_context()); } return ref; } vvp_vector4_t vec4_from_vpi_value(s_vpi_value*vp, unsigned wid) { vvp_vector4_t val (wid, BIT4_0); switch (vp->format) { case vpiIntVal: { long vpi_val = vp->value.integer; for (unsigned idx = 0 ; idx < wid ; idx += 1) { vvp_bit4_t bit = (vpi_val & 1) ? BIT4_1 : BIT4_0; val.set_bit(idx, bit); vpi_val >>= 1; } break; } case vpiVectorVal: for (unsigned idx = 0 ; idx < wid ; idx += 1) { unsigned long aval = vp->value.vector[idx/32].aval; unsigned long bval = vp->value.vector[idx/32].bval; aval >>= idx%32; bval >>= idx%32; int bitmask = (aval&1) | ((bval<<1)&2); vvp_bit4_t bit = scalar_to_bit4(bitmask); val.set_bit(idx, bit); } break; case vpiBinStrVal: vpip_bin_str_to_vec4(val, vp->value.str); break; case vpiOctStrVal: vpip_oct_str_to_vec4(val, vp->value.str); break; case vpiDecStrVal: vpip_dec_str_to_vec4(val, vp->value.str); break; case vpiHexStrVal: vpip_hex_str_to_vec4(val, vp->value.str); break; case vpiScalarVal: val.set_bit(0, scalar_to_bit4(vp->value.scalar)); break; case vpiStringVal: val = from_stringval(vp->value.str, wid); break; case vpiRealVal: val = vvp_vector4_t(wid, vp->value.real); break; default: fprintf(stderr, "vvp internal error: put_value: " "value type %d not implemented here.\n", (int)vp->format); assert(0); } return val; } int __vpiSignal::vpi_get(int code) { return signal_get(code, this); } char* __vpiSignal::vpi_get_str(int code) { return signal_get_str(code, this); } void __vpiSignal::vpi_get_value(p_vpi_value val) { signal_get_value(this, val); } vpiHandle __vpiSignal::vpi_put_value(p_vpi_value val, int flags) { return signal_put_value(this, val, flags); } vpiHandle __vpiSignal::vpi_handle(int code) { return signal_get_handle(code, this); } vpiHandle __vpiSignal::vpi_iterate(int code) { return signal_iterate(code, this); } vpiHandle __vpiSignal::vpi_index(int idx) { return signal_index(idx, this); } struct signal_reg : public __vpiSignal { inline signal_reg() { } int get_type_code(void) const { return vpiReg; } }; struct signal_integer : public __vpiSignal { inline signal_integer() { } int get_type_code(void) const { return vpiIntegerVar; } }; struct signal_net : public __vpiSignal { inline signal_net() { } int get_type_code(void) const { return vpiNet; } }; struct signal_byte : public __vpiSignal { inline signal_byte() { } int get_type_code(void) const { return vpiByteVar; } }; struct signal_bitvar : public __vpiSignal { inline signal_bitvar() { } int get_type_code(void) const { return vpiBitVar; } }; struct signal_shortint : public __vpiSignal { inline signal_shortint() { } int get_type_code(void) const { return vpiShortIntVar; } }; struct signal_int : public __vpiSignal { inline signal_int() { } int get_type_code(void) const { return vpiIntVar; } }; struct signal_longint : public __vpiSignal { inline signal_longint() { } int get_type_code(void) const { return vpiLongIntVar; } }; /* * Construct a vpiIntegerVar object. Indicate the type using a flag * to minimize the code modifications. Icarus implements integers * as 'reg signed [31:0]'. */ vpiHandle vpip_make_int4(const char*name, int msb, int lsb, vvp_net_t*vec) { __vpiSignal*obj = new signal_integer; return fill_in_var4(obj, name, msb, lsb, true, vec); } /* * Construct the two-state SystemVerilog variables. */ vpiHandle vpip_make_int2(const char*name, int msb, int lsb, bool signed_flag, vvp_net_t*vec) { __vpiSignal*obj; // All unsigned 2-state variables are a vpiBitVar. All 2-state // variables with a non-zero lsb are also a vpiBitVar. if ((! signed_flag) || (lsb != 0) ) { obj = new signal_bitvar; } else { // These could also be bit declarations with matching // information, but for now they get the apparent type. switch (msb) { case 7: obj = new signal_byte; break; case 15: obj = new signal_shortint; break; case 31: obj = new signal_int; break; case 63: obj = new signal_longint; break; default: // Every other type of bit vector is a vpiBitVar with // array dimensions. obj = new signal_bitvar; break; } } return fill_in_var4(obj, name, msb, lsb, signed_flag, vec); } /* * Construct a vpiReg/vpiLogicVar object. It's like a net, except for the type. */ vpiHandle vpip_make_var4(const char*name, int msb, int lsb, bool signed_flag, vvp_net_t*vec) { __vpiSignal*obj = new signal_reg; return fill_in_var4(obj, name, msb, lsb, signed_flag, vec); } #ifdef CHECK_WITH_VALGRIND static struct vpiSignal_plug **signal_pool = 0; static unsigned signal_pool_count = 0; static unsigned long signal_count = 0; static unsigned long signal_dels = 0; #endif struct vpiSignal_plug { unsigned char space[sizeof (struct __vpiSignal)]; #ifdef CHECK_WITH_VALGRIND struct vpiSignal_plug *pool; #endif }; void* __vpiSignal::operator new(size_t siz) { #ifdef CHECK_WITH_VALGRIND assert(siz == sizeof(struct vpiSignal_plug) - sizeof(struct vpiSignal_plug *)); #else assert(siz == sizeof(struct vpiSignal_plug)); #endif static struct vpiSignal_plug*alloc_array = 0; static unsigned alloc_index = 0; const unsigned alloc_count = 512; if ((alloc_array == 0) || (alloc_index == alloc_count)) { alloc_array = (struct vpiSignal_plug*) calloc(alloc_count, sizeof(struct vpiSignal_plug)); alloc_index = 0; #ifdef CHECK_WITH_VALGRIND VALGRIND_MAKE_MEM_NOACCESS(alloc_array, alloc_count * sizeof(struct vpiSignal_plug)); VALGRIND_CREATE_MEMPOOL(alloc_array, 0, 1); signal_pool_count += 1; signal_pool = (vpiSignal_plug **) realloc(signal_pool, signal_pool_count*sizeof(vpiSignal_plug **)); signal_pool[signal_pool_count-1] = alloc_array; #endif } struct vpiSignal_plug*cur = alloc_array + alloc_index; #ifdef CHECK_WITH_VALGRIND VALGRIND_MEMPOOL_ALLOC(alloc_array, cur, sizeof(struct vpiSignal_plug)); cur->pool = alloc_array; signal_count += 1; #endif alloc_index += 1; return cur; } void __vpiSignal::operator delete(void*) { assert(0); } #ifdef CHECK_WITH_VALGRIND void signal_delete(vpiHandle item) { struct __vpiSignal *obj = static_cast<__vpiSignal *> (item); assert(obj->node->fil); obj->node->fil->clear_all_callbacks(); vvp_net_delete(obj->node); if (obj->bits) { for (unsigned idx=0; idxwidth(); idx+=1) { delete obj->bits[idx].index; } obj->bits -= 1; delete [] obj->bits; } signal_dels += 1; VALGRIND_MEMPOOL_FREE(reinterpret_cast(obj)->pool, obj); } void signal_pool_delete() { if (RUNNING_ON_VALGRIND && (signal_count != signal_dels)) { fflush(NULL); VALGRIND_PRINTF("Error: vvp missed deleting %ld of %lu signal(s).", (long) signal_count - signal_dels, signal_count); } for (unsigned idx = 0; idx < signal_pool_count; idx += 1) { VALGRIND_DESTROY_MEMPOOL(signal_pool[idx]); free(signal_pool[idx]); } free(signal_pool); signal_pool = 0; signal_pool_count = 0; } #endif /* * Construct a vpiNet object. Give the object specified dimensions, * and point to the specified functor for the lsb. * * The name is the PLI name for the object. If it is an array it is * []. */ static vpiHandle fill_in_net4(struct __vpiSignal*obj, __vpiScope*scope, const char*name, int msb, int lsb, bool signed_flag, vvp_net_t*node) { obj->id.name = name? vpip_name_string(name) : 0; obj->msb = __vpiDecConst(msb); obj->lsb = __vpiDecConst(lsb); obj->signed_flag = signed_flag? 1 : 0; obj->is_netarray = 0; obj->node = node; // Place this object within a scope. If this object is // attached to an array, then this value will be replaced with // the handle to the parent. obj->within.scope = scope; count_vpi_nets += 1; return obj; } vpiHandle vpip_make_net4(__vpiScope*scope, const char*name, int msb, int lsb, bool signed_flag, vvp_net_t*node) { struct __vpiSignal*obj = new signal_net; return fill_in_net4(obj, scope, name, msb, lsb, signed_flag, node); } static int PV_get_base(struct __vpiPV*rfp) { /* We return from the symbol base if it is defined. */ if (rfp->sbase != 0) { s_vpi_value val; /* Check to see if the value is defined. */ val.format = vpiVectorVal; vpi_get_value(rfp->sbase, &val); int words = (vpi_get(vpiSize, rfp->sbase)-1)/32 + 1; for(int idx = 0; idx < words; idx += 1) { /* Return INT_MIN to indicate an X base. */ if (val.value.vector[idx].bval != 0) return INT_MIN; } /* The value is defined so get and return it. */ val.format = vpiIntVal; vpi_get_value(rfp->sbase, &val); return val.value.integer; } /* If the width is zero then tbase is the constant. */ return rfp->tbase; } static int PV_get(int code, vpiHandle ref) { struct __vpiPV*rfp = dynamic_cast<__vpiPV*>(ref); assert(rfp); int rval = 0; switch (code) { case vpiLineNo: return 0; // Not implemented for now! case vpiSigned: return 0; // A part/bit select is always unsigned! case vpiSize: return rfp->width; /* This is like the &A<> in array.cc. */ case vpiConstantSelect: return rfp->sbase == 0; case vpiLeftRange: rval += rfp->width - 1; // fallthrough case vpiRightRange: rval += vpi_get(vpiRightRange, rfp->parent) + PV_get_base(rfp); return rval; case vpiAutomatic: return vpi_get(vpiAutomatic, rfp->parent); #if defined(CHECK_WITH_VALGRIND) || defined(BR916_STOPGAP_FIX) case _vpiFromThr: return _vpi_at_PV; #endif default: fprintf(stderr, "PV_get: property %d is unknown\n", code); } return 0; } static char* PV_get_str(int code, vpiHandle ref) { struct __vpiPV*rfp = dynamic_cast<__vpiPV*>(ref); assert(rfp); switch (code) { case vpiFile: // Not implemented for now! return simple_set_rbuf_str(file_names[0]); case vpiName: case vpiFullName: { const char*nm = vpi_get_str(code, rfp->parent); size_t len = 256+strlen(nm); char *full = (char *) malloc(len); snprintf(full, len, "%s[%d:%d]", nm, (int)vpi_get(vpiLeftRange, ref), (int)vpi_get(vpiRightRange, ref)); full[len-1] = 0; char *res = simple_set_rbuf_str(full); free(full); return res; } default: fprintf(stderr, "PV_get_str: property %d is unknown.\n", code); } return 0; } static void PV_get_value(vpiHandle ref, p_vpi_value vp) { struct __vpiPV*rfp = dynamic_cast<__vpiPV*>(ref); assert(rfp); vvp_signal_value*sig = dynamic_cast(rfp->net->fil); assert(sig); switch (vp->format) { case vpiIntVal: format_vpiIntVal(sig, PV_get_base(rfp), rfp->width, 0, vp); break; case vpiBinStrVal: format_vpiBinStrVal(sig, PV_get_base(rfp), rfp->width, vp); break; case vpiOctStrVal: format_vpiOctStrVal(sig, PV_get_base(rfp), rfp->width, vp); break; case vpiHexStrVal: format_vpiHexStrVal(sig, PV_get_base(rfp), rfp->width, vp); break; case vpiDecStrVal: format_vpiDecStrVal(sig, PV_get_base(rfp), rfp->width, 0, vp); break; case vpiStringVal: format_vpiStringVal(sig, PV_get_base(rfp), rfp->width, vp); break; case vpiScalarVal: format_vpiScalarVal(sig, PV_get_base(rfp), vp); break; case vpiStrengthVal: format_vpiStrengthVal(sig, PV_get_base(rfp), rfp->width, vp); break; case vpiVectorVal: format_vpiVectorVal(sig, PV_get_base(rfp), rfp->width, vp); break; case vpiRealVal: format_vpiRealVal(sig, PV_get_base(rfp), rfp->width, 0, vp); break; default: fprintf(stderr, "vvp internal error: PV_get_value: " "value type %d not implemented. Signal is %s.\n", (int)vp->format, vpi_get_str(vpiFullName, rfp->parent)); assert(0); } } static vpiHandle PV_put_value(vpiHandle ref, p_vpi_value vp, int flags) { struct __vpiPV*rfp = dynamic_cast<__vpiPV*>(ref); assert(rfp); vvp_signal_value*sig = dynamic_cast(rfp->net->fil); assert(sig); unsigned sig_size = sig->value_size(); unsigned width = rfp->width; int base = PV_get_base(rfp); if (base >= (signed) sig_size) return 0; if (base + (signed) width < 0) return 0; vvp_vector4_t val; if (flags != vpiReleaseFlag) { val = vec4_from_vpi_value(vp, width); } /* * If the base is less than zero then trim off any unneeded * lower bits. */ if (base < 0) { width += base; if (flags != vpiReleaseFlag) { val = val.subvalue(-base, width); } base = 0; } /* * If the value is wider than the signal then trim off any * unneeded upper bits. */ if (base+width > sig_size) { width = sig_size - base; if (flags != vpiReleaseFlag) { val = val.subvalue(0, width); } } assert(rfp->parent); bool net_flag = rfp->parent->get_type_code()==vpiNet; bool full_sig = base == 0 && width == sig_size; vvp_net_ptr_t dest(rfp->net, 0); /* If this is a release, then we are not really putting a value. Instead, issue a release "command" to the signal node to cause it to release a forced value. Note that if this net is attached to an island, we need to rerun the calculations immediately so we can return the released value.*/ if (flags == vpiReleaseFlag) { assert(rfp->net->fil); // XXXX Can't really do this if this is a partial release? rfp->net->fil->force_unlink(); if (full_sig) { rfp->net->fil->release(dest, net_flag); } else { rfp->net->fil->release_pv(dest, base, width, net_flag); } rfp->net->fun->force_flag(true); PV_get_value(ref, vp); return ref; } if (flags == vpiForceFlag) { if (full_sig) { vvp_vector2_t mask (vvp_vector2_t::FILL1, sig_size); rfp->net->force_vec4(val, mask); } else { vvp_vector2_t mask (vvp_vector2_t::FILL0, sig_size); for (unsigned idx = 0 ; idx < width ; idx += 1) mask.set_bit(base+idx, 1); vvp_vector4_t tmp (sig_size, BIT4_Z); // vvp_net_t::force_vec4 propagates all the bits of the // forced vector value, regardless of the mask. This // ensures the unforced bits retain their current value. sig->vec4_value(tmp); tmp.set_vec(base, val); rfp->net->force_vec4(tmp, mask); } } else if (net_flag && !dynamic_cast(rfp->net->fun)) { if (full_sig) { rfp->net->send_vec4(val, vthread_get_wt_context()); } else { rfp->net->send_vec4_pv(val, base, width, sig_size, vthread_get_wt_context()); } } else { if (full_sig) { vvp_send_vec4(dest, val, vthread_get_wt_context()); } else { vvp_send_vec4_pv(dest, val, base, width, sig_size, vthread_get_wt_context()); } } return 0; } static vpiHandle PV_get_handle(int code, vpiHandle ref) { struct __vpiPV*rfp = dynamic_cast<__vpiPV*>(ref); assert(rfp); switch (code) { case vpiParent: return rfp->parent; case vpiScope: return vpi_handle(vpiScope, rfp->parent); case vpiModule: return vpi_handle(vpiModule, rfp->parent); } return 0; } inline __vpiPV::__vpiPV() { } int __vpiPV::get_type_code(void) const { return vpiPartSelect; } int __vpiPV::vpi_get(int code) { return PV_get(code, this); } char* __vpiPV::vpi_get_str(int code) { return PV_get_str(code, this); } void __vpiPV::vpi_get_value(p_vpi_value val) { PV_get_value(this, val); } vpiHandle __vpiPV::vpi_put_value(p_vpi_value val, int flags) { return PV_put_value(this, val, flags); } vpiHandle __vpiPV::vpi_handle(int code) { return PV_get_handle(code, this); } vpiHandle vpip_make_PV(char*var, int base, int width) { struct __vpiPV*obj = new __vpiPV; compile_vpi_lookup(&obj->parent, strdup(var)); obj->sbase = 0; obj->tbase = base; obj->width = (unsigned) width; obj->net = 0; functor_ref_lookup(&obj->net, var); return obj; } vpiHandle vpip_make_PV(char*var, char*symbol, int width) { struct __vpiPV*obj = new __vpiPV; compile_vpi_lookup(&obj->parent, strdup(var)); compile_vpi_lookup(&obj->sbase, symbol); obj->tbase = 0; obj->width = (unsigned) width; obj->net = 0; functor_ref_lookup(&obj->net, var); return obj; } vpiHandle vpip_make_PV(char*var, vpiHandle handle, int width) { struct __vpiPV*obj = new __vpiPV; compile_vpi_lookup(&obj->parent, strdup(var)); obj->sbase = handle; obj->tbase = 0; obj->width = (unsigned) width; obj->net = 0; functor_ref_lookup(&obj->net, var); return obj; } #ifdef CHECK_WITH_VALGRIND void PV_delete(vpiHandle item) { struct __vpiPV *obj = dynamic_cast<__vpiPV*>(item); if (obj->sbase) { switch (obj->sbase->get_type_code()) { case vpiMemoryWord: if (vpi_get(_vpiFromThr, obj->sbase) == _vpi_at_A) { A_delete(obj->sbase); } break; case vpiPartSelect: assert(vpi_get(_vpiFromThr, obj->sbase) == _vpi_at_PV); PV_delete(obj->sbase); break; } } assert(obj->net->fil); obj->net->fil->clear_all_callbacks(); delete obj; } #endif