int _mon_check_callbacks() { t_cb_data cb_data; cb_data.reason = cbEndOfSimulation; cb_data.cb_rtn = NULL; cb_data.user_data = 0; cb_data.value = NULL; vpiHandle vh = vpi_register_cb(&cb_data); CHECK_RESULT_NZ(vh); PLI_INT32 status = vpi_remove_cb(vh); CHECK_RESULT_NZ(status); return 0; }
/* * ro sync cb routine */ int cbwrsync_rtn(p_cb_data cbp) { vpi_printf("*** rw sync time wake up at %d\n", cbp->time->low); /* not sure if legal to usurp t_cb_data since removed when event happens */ cbp->reason = cbReadOnlySynch; cbp->cb_rtn = cbrosync_rtn; cbp->obj = NULL; cbp->time->high = 0; cbp->time->low = 0; cbp->value = NULL; cbp->index = 0; vpi_register_cb(cbp); return(0); }
static PLI_INT32 simbus_poll_calltf(char*my_name) { int poll_state; s_vpi_value value; vpiHandle sys = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, sys); vpiHandle bus_h = vpi_scan(argv); assert(bus_h); value.format = vpiIntVal; vpi_get_value(bus_h, &value); int bus = value.value.integer; assert(bus >= 0 && bus < MAX_INSTANCES); vpiHandle trig = vpi_scan(argv); assert(trig); vpi_free_object(argv); DEBUG(SIMBUS_DEBUG_CALLS, "Call $poll(%d...)\n", bus); poll_state = check_readable(bus); value.format = vpiScalarVal; value.value.scalar = poll_state? vpi1 : vpi0; vpi_put_value(trig, &value, 0, vpiNoDelay); if (poll_state == 0) { struct t_cb_data cb_data; struct t_vpi_time cb_time; cb_time.type = vpiSuppressTime; cb_data.reason = cbReadWriteSynch; cb_data.cb_rtn = poll_for_simbus_bus; cb_data.time = &cb_time; vpi_register_cb(&cb_data); instance_table[bus].trig = trig; } else { instance_table[bus].trig = 0; } DEBUG(SIMBUS_DEBUG_CALLS, "return $poll(%d...)\n", bus); return 0; }
static void vhdl_register(void) { s_vpi_systf_data tf_data; s_cb_data cb; vpiHandle res; tf_data.type = vpiSysFunc; tf_data.sysfunctype = vpiSizedFunc; tf_data.calltf = ivlh_attribute_event_calltf; tf_data.compiletf = ivlh_attribute_event_compiletf; tf_data.sizetf = ivlh_attribute_event_sizetf; tf_data.tfname = func_names[EVENT]; tf_data.user_data = (PLI_BYTE8*) EVENT; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysFunc; tf_data.sysfunctype = vpiSizedFunc; tf_data.calltf = ivlh_attribute_event_calltf; tf_data.compiletf = ivlh_attribute_event_compiletf; tf_data.sizetf = ivlh_attribute_event_sizetf; tf_data.tfname = func_names[RISING_EDGE]; tf_data.user_data = (PLI_BYTE8*) RISING_EDGE; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); tf_data.type = vpiSysFunc; tf_data.sysfunctype = vpiSizedFunc; tf_data.calltf = ivlh_attribute_event_calltf; tf_data.compiletf = ivlh_attribute_event_compiletf; tf_data.sizetf = ivlh_attribute_event_sizetf; tf_data.tfname = func_names[FALLING_EDGE]; tf_data.user_data = (PLI_BYTE8*) FALLING_EDGE; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); /* Create a callback to clear the monitor data memory when the * simulator finishes. */ cb.time = NULL; cb.reason = cbEndOfSimulation; cb.cb_rtn = cleanup_mdata; cb.user_data = NULL; cb.obj = NULL; vpi_register_cb(&cb); }
/* * register call backs */ void register_cbs(void) { vpiHandle href; struct t_cb_data *ecbp; struct t_cb_data cbrec; /* notice cb records must be in global storage */ ecbp = &cbrec; ecbp->reason = cbPLIError; ecbp->cb_rtn = my_error_handler; ecbp->obj = NULL; ecbp->time = NULL; ecbp->value = NULL; ecbp->user_data = NULL; /* probably should check for error here */ if ((href = vpi_register_cb(ecbp)) == NULL) vpi_printf("**ERR: PLI 2.0 can not register error handler callback.\n"); }
int tf_isetdelay(PLI_INT32 delay, void*ss) { vpiHandle sys = (vpiHandle)ss; int unit = vpi_get(vpiTimeUnit, sys); int prec = vpi_get(vpiTimePrecision, 0); struct t_cb_data cb; struct t_vpi_time ct; if (pli_trace) { fprintf(pli_trace, "%s: tf_isetdelay(%d, ...)" " <unit=%d, prec=%d>;\n", vpi_get_str(vpiName, sys), (int)delay, unit, prec); } /* Convert the delay from the UNITS of the specified task/function to the precision of the simulation. */ assert(unit >= prec); while (unit > prec) { PLI_INT32 tmp = delay * 10; assert(tmp > delay); delay = tmp; unit -= 1; } /* Create a VPI callback to schedule the delay. */ ct.type = vpiSimTime; ct.high = 0; ct.low = delay; cb.reason = cbAfterDelay; cb.cb_rtn = delay_callback; cb.obj = 0; cb.time = &ct; cb.value = 0; cb.user_data = 0; vpi_register_cb(&cb); return 0; }
/* * Register all the functions with Verilog. */ static void va_math_register(void) { s_cb_data cb_data; s_vpi_systf_data tf_data; vpiHandle res; unsigned idx; /* Register the single argument functions. */ tf_data.type = vpiSysFunc; tf_data.sysfunctype = vpiRealFunc; tf_data.calltf = va_single_argument_calltf; tf_data.compiletf = va_single_argument_compiletf; tf_data.sizetf = 0; for (idx=0; va_single_data[idx].name != 0; idx++) { tf_data.tfname = va_single_data[idx].name; tf_data.user_data = (PLI_BYTE8 *) &va_single_data[idx]; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); } /* Register the double argument functions. */ tf_data.type = vpiSysFunc; tf_data.sysfunctype = vpiRealFunc; tf_data.calltf = va_double_argument_calltf; tf_data.compiletf = va_double_argument_compiletf; tf_data.sizetf = 0; for (idx=0; va_double_data[idx].name != 0; idx++) { tf_data.tfname = va_double_data[idx].name; tf_data.user_data = (PLI_BYTE8 *) &va_double_data[idx]; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); } /* We need to clean up the userdata. */ cb_data.reason = cbEndOfSimulation; cb_data.time = 0; cb_data.cb_rtn = sys_end_of_simulation; cb_data.user_data = "system"; vpi_register_cb(&cb_data); }
static PLI_INT32 CallbackRegister(s_cb_data *data) { vpiHandle hand; s_cb_data cb_data; s_vpi_time timerec = { vpiSimTime, 0, 0, 0 }; hand = vpi_handle_by_name("test.e", 0); assert(hand); cb_data.time = &timerec; cb_data.value = 0; cb_data.user_data = (char *)hand; cb_data.obj = hand; cb_data.reason = cbValueChange; cb_data.cb_rtn = Callback; Handle = vpi_register_cb(&cb_data); return (0); }
PLI_INT32 tf_irosynchronize(void*obj) { vpiHandle sys = (vpiHandle)obj; p_pli_data pli = vpi_get_userdata(sys); s_cb_data cb; s_vpi_time ti = {vpiSuppressTime, 0, 0}; cb.reason = cbReadOnlySynch; cb.cb_rtn = callback; cb.obj = sys; cb.time = &ti; cb.user_data = (char *)pli; vpi_register_cb(&cb); if (pli_trace) fprintf(pli_trace, "tf_irosynchronize(%p) --> %d\n", obj, 0); return 0; }
static int variable_cb_1(p_cb_data cause) { struct t_cb_data cb; struct vcd_info*info = (struct vcd_info*)cause->user_data; if (dump_is_off) return 0; if (dump_header_pending()) return 0; if (info->scheduled) return 0; if (!vcd_dmp_list) { cb = *cause; cb.reason = cbReadOnlySynch; cb.cb_rtn = variable_cb_2; vpi_register_cb(&cb); } info->scheduled = 1; info->dmp_next = vcd_dmp_list; vcd_dmp_list = info; return 0; }
/* * check each hello call, in struct as short but passed as int * * dynamic nature of PLI 2.0 requires checking just before simulation * because need to wait until simulation data structure is built */ PLI_INT32 hello_setup(char *data) { vpiHandle href; struct t_cb_data *cbp; struct t_cb_data cbrec; vpi_printf("... executing vpi_ systf compiletf routine.\n"); cbp = &cbrec; cbp->reason = cbEndOfCompile; cbp->cb_rtn = hello_chk; cbp->obj = NULL; cbp->time = NULL; cbp->value = NULL; cbp->user_data = NULL; /* probably should check for error here */ if ((href = vpi_register_cb(cbp)) == NULL) vpi_printf( "**ERR: $hello PLI 2.0 task cannot register end of compile check routine"); /* if not registered will be no call backs */ return(0); }
void acc_vcl_add(handle obj, PLI_INT32(*consumer)(p_vc_record), void*data, PLI_INT32 vcl_flag) { struct vcl_record*cur; struct t_cb_data cb; switch (vpi_get(vpiType, obj)) { case vpiNet: case vpiReg: cur = malloc(sizeof (struct vcl_record)); cur->obj = obj; cur->consumer = consumer; cur->user_data = data; cur->vcl_flag = vcl_flag; cur->next = vcl_list; vcl_list = cur; cb.reason = cbValueChange; cb.cb_rtn = vcl_value_callback; cb.obj = obj; cb.time = 0; cb.value = 0; cb.user_data = (void*)cur; cur->callback = vpi_register_cb(&cb); if (pli_trace) { fprintf(pli_trace, "acc_vcl_add(<%s>, ..., %p, %d)\n", vpi_get_str(vpiFullName, obj), data, (int)vcl_flag); } break; default: vpi_printf("XXXX acc_vcl_add(<type=%d>, ..., %d);\n", (int)vpi_get(vpiType, obj), (int)vcl_flag); break; } }
static PLI_INT32 test_next_calltf(PLI_BYTE8 *name) #endif { vpiHandle sys, argv, value; (void)name; /* Parameter is not used. */ sys = vpi_handle(vpiSysTfCall, 0); assert(sys); argv = vpi_iterate(vpiArgument, sys); assert(argv); for (value = vpi_scan(argv) ; value ; value = vpi_scan(argv)) { s_cb_data cb; cb.reason = cbNextSimTime; cb.cb_rtn = next_sim_time_callback; cb.user_data = (char*)value; vpi_register_cb(&cb); } return 0; }
/********************************************************************** * $my_pow Registration Data * (add this function name to the vlog_startup_routines array) *********************************************************************/ void PLIbook_pow_register(void) { s_vpi_systf_data tf_data; s_cb_data cb_data_s; vpiHandle callback_handle; tf_data.type = vpiSysFunc; tf_data.sysfunctype = vpiSysFuncSized; tf_data.tfname = "$my_pow"; tf_data.calltf = PLIbook_PowCalltf; tf_data.compiletf = PLIbook_PowCompiletf; tf_data.sizetf = PLIbook_PowSizetf; tf_data.user_data = NULL; vpi_register_systf(&tf_data); cb_data_s.reason = cbStartOfSimulation; cb_data_s.cb_rtn = PLIbook_PowStartOfSim; cb_data_s.obj = NULL; cb_data_s.time = NULL; cb_data_s.value = NULL; cb_data_s.user_data = NULL; callback_handle = vpi_register_cb(&cb_data_s); vpi_free_object(callback_handle); /* don't need callback handle */ }
PLI_INT32 CompileTF(PLI_BYTE8 *user_data) #endif { s_cb_data cb_data; vpiHandle call_h=vpi_handle(vpiSysTfCall,NULL); vpiHandle arg_i,arg_h; (void)user_data; /* Parameter is not used. */ // Get First Argument and Setup Value Change Callback arg_i=vpi_iterate(vpiArgument,call_h); arg_h=vpi_scan(arg_i); vpi_free_object(arg_i); cb_data.reason = cbValueChange; cb_data.cb_rtn = ValueChange; cb_data.value = NULL; cb_data.time = NULL; cb_data.user_data = NULL; cb_data.obj = arg_h; vpi_register_cb(&cb_data); return(0); }
void setup_finish_callbacks(void) { // here we setup and install callbacks for // simulation finish static s_vpi_time time_s = {vpiScaledRealTime, 0, 0, 0}; static s_vpi_value value_s = {.format = vpiBinStrVal}; static s_cb_data cb_data_s = { cbEndOfSimulation, // end of simulation (void *)sim_finish_callback, NULL, &time_s, &value_s, 0, NULL }; cb_data_s.obj = NULL; /* trigger object */ cb_data_s.user_data = NULL; // actual call to register the callback vpi_register_cb(&cb_data_s); }
static void scan_item(unsigned depth, vpiHandle item, int skip) { struct t_cb_data cb; struct vcd_info* info; const char* name; const char* ident; int nexus_id; /* list of types to iterate upon */ int i; static int types[] = { /* Value */ vpiNet, vpiReg, vpiVariables, /* Scope */ vpiFunction, vpiModule, vpiNamedBegin, vpiNamedFork, vpiTask, -1 }; switch (vpi_get(vpiType, item)) { case vpiMemoryWord: if (vpi_get(vpiConstantSelect, item) == 0) { /* Turn a non-constant array word select into a * constant word select. */ vpiHandle array = vpi_handle(vpiParent, item); PLI_INT32 idx = vpi_get(vpiIndex, item); item = vpi_handle_by_index(array, idx); } case vpiIntegerVar: case vpiBitVar: case vpiByteVar: case vpiShortIntVar: case vpiIntVar: case vpiLongIntVar: case vpiTimeVar: case vpiReg: case vpiNet: /* An array word is implicitly escaped so look for an * escaped identifier that this could conflict with. */ if (vpi_get(vpiType, item) == vpiMemoryWord && vpi_handle_by_name(vpi_get_str(vpiFullName, item), 0)) { vpi_printf("LXT2 warning: dumping array word %s will " "conflict with an escaped identifier.\n", vpi_get_str(vpiFullName, item)); } if (skip || vpi_get(vpiAutomatic, item)) break; name = vpi_get_str(vpiName, item); nexus_id = vpi_get(_vpiNexusId, item); if (nexus_id) { ident = find_nexus_ident(nexus_id); } else { ident = 0; } if (!ident) { char*tmp = create_full_name(name); ident = strdup_sh(&name_heap, tmp); free(tmp); if (nexus_id) set_nexus_ident(nexus_id, ident); info = new_vcd_info(); info->item = item; info->sym = lxt2_wr_symbol_add(dump_file, ident, 0 /* array rows */, vpi_get(vpiLeftRange, item), vpi_get(vpiRightRange, item), LXT2_WR_SYM_F_BITS); info->dmp_next = 0; cb.time = 0; cb.user_data = (char*)info; cb.value = NULL; cb.obj = item; cb.reason = cbValueChange; cb.cb_rtn = variable_cb_1; info->cb = vpi_register_cb(&cb); } else { char *n = create_full_name(name); lxt2_wr_symbol_alias(dump_file, ident, n, vpi_get(vpiSize, item)-1, 0); free(n); } break; case vpiRealVar: if (skip || vpi_get(vpiAutomatic, item)) break; name = vpi_get_str(vpiName, item); { char*tmp = create_full_name(name); ident = strdup_sh(&name_heap, tmp); free(tmp); } info = new_vcd_info(); info->item = item; info->sym = lxt2_wr_symbol_add(dump_file, ident, 0 /* array rows */, vpi_get(vpiSize, item)-1, 0, LXT2_WR_SYM_F_DOUBLE); info->dmp_next = 0; cb.time = 0; cb.user_data = (char*)info; cb.value = NULL; cb.obj = item; cb.reason = cbValueChange; cb.cb_rtn = variable_cb_1; info->cb = vpi_register_cb(&cb); break; case vpiModule: case vpiNamedBegin: case vpiTask: case vpiFunction: case vpiNamedFork: if (depth > 0) { int nskip; vpiHandle argv; const char* fullname = vpi_get_str(vpiFullName, item); #if 0 vpi_printf("LXT2 info: scanning scope %s, %u levels\n", fullname, depth); #endif nskip = vcd_scope_names_test(fullname); if (!nskip) vcd_scope_names_add(fullname); else vpi_printf("LXT2 warning: ignoring signals in " "previously scanned scope %s\n", fullname); name = vpi_get_str(vpiName, item); push_scope(name); for (i=0; types[i]>0; i++) { vpiHandle hand; argv = vpi_iterate(types[i], item); while (argv && (hand = vpi_scan(argv))) { scan_item(depth-1, hand, nskip); } } pop_scope(); } break; default: vpi_printf("LXT2 warning: $dumpvars: Unsupported parameter " "type (%s)\n", vpi_get_str(vpiType, item)); } }
static void scan_item(unsigned depth, vpiHandle item, int skip) { struct t_cb_data cb; struct vcd_info* info; const char *type; const char *name; const char *fullname; const char *prefix; const char *ident; int nexus_id; unsigned size; PLI_INT32 item_type; /* Get the displayed type for the various $var and $scope types. */ /* Not all of these are supported now, but they should be in a * future development version. */ item_type = vpi_get(vpiType, item); switch (item_type) { case vpiNamedEvent: type = "event"; break; case vpiIntVar: case vpiIntegerVar: type = "integer"; break; case vpiParameter: type = "parameter"; break; /* Icarus converts realtime to real. */ case vpiRealVar: type = "real"; break; case vpiMemoryWord: case vpiBitVar: case vpiByteVar: case vpiShortIntVar: case vpiLongIntVar: case vpiReg: type = "reg"; break; /* Icarus converts a time to a plain register. */ case vpiTimeVar: type = "time"; break; case vpiNet: switch (vpi_get(vpiNetType, item)) { case vpiWand: type = "wand"; break; case vpiWor: type = "wor"; break; case vpiTri: type = "tri"; break; case vpiTri0: type = "tri0"; break; case vpiTri1: type = "tri1"; break; case vpiTriReg: type = "trireg"; break; case vpiTriAnd: type = "triand"; break; case vpiTriOr: type = "trior"; break; case vpiSupply1: type = "supply1"; break; case vpiSupply0: type = "supply0"; break; default: type = "wire"; break; } break; case vpiNamedBegin: type = "begin"; break; case vpiGenScope: type = "begin"; break; case vpiNamedFork: type = "fork"; break; case vpiFunction: type = "function"; break; case vpiModule: type = "module"; break; case vpiTask: type = "task"; break; default: vpi_printf("VCD warning: $dumpvars: Unsupported argument " "type (%s)\n", vpi_get_str(vpiType, item)); return; } /* Do some special processing/checking on array words. Dumping * array words is an Icarus extension. */ if (item_type == vpiMemoryWord) { /* Turn a non-constant array word select into a constant * word select. */ if (vpi_get(vpiConstantSelect, item) == 0) { vpiHandle array = vpi_handle(vpiParent, item); PLI_INT32 idx = vpi_get(vpiIndex, item); item = vpi_handle_by_index(array, idx); } /* An array word is implicitly escaped so look for an * escaped identifier that this could conflict with. */ /* This does not work as expected since we always find at * least the array word. We likely need a custom routine. */ if (vpi_get(vpiType, item) == vpiMemoryWord && vpi_handle_by_name(vpi_get_str(vpiFullName, item), 0)) { vpi_printf("VCD warning: array word %s will conflict " "with an escaped identifier.\n", vpi_get_str(vpiFullName, item)); } } fullname = vpi_get_str(vpiFullName, item); /* Generate the $var or $scope commands. */ switch (item_type) { case vpiParameter: vpi_printf("VCD sorry: $dumpvars: can not dump parameters.\n"); break; case vpiNamedEvent: case vpiIntegerVar: case vpiBitVar: case vpiByteVar: case vpiShortIntVar: case vpiIntVar: case vpiLongIntVar: case vpiRealVar: case vpiMemoryWord: case vpiReg: case vpiTimeVar: case vpiNet: /* If we are skipping all signal or this is in an automatic * scope then just return. */ if (skip || vpi_get(vpiAutomatic, item)) return; /* Skip this signal if it has already been included. * This can only happen for implicitly given signals. */ if (vcd_names_search(&vcd_var, fullname)) return; /* Declare the variable in the VCD file. */ name = vpi_get_str(vpiName, item); prefix = is_escaped_id(name) ? "\\" : ""; /* Some signals can have an alias so handle that. */ nexus_id = vpi_get(_vpiNexusId, item); ident = 0; if (nexus_id) ident = find_nexus_ident(nexus_id); if (!ident) { ident = strdup(vcdid); gen_new_vcd_id(); if (nexus_id) set_nexus_ident(nexus_id, ident); /* Add a callback for the signal. */ info = malloc(sizeof(*info)); info->time.type = vpiSimTime; info->item = item; info->ident = ident; info->scheduled = 0; cb.time = &info->time; cb.user_data = (char*)info; cb.value = NULL; cb.obj = item; cb.reason = cbValueChange; cb.cb_rtn = variable_cb_1; info->dmp_next = 0; info->next = vcd_list; vcd_list = info; info->cb = vpi_register_cb(&cb); } /* Named events do not have a size, but other tools use * a size of 1 and some viewers do not accept a width of * zero so we will also use a width of one for events. */ if (item_type == vpiNamedEvent) size = 1; else size = vpi_get(vpiSize, item); fprintf(dump_file, "$var %s %u %s %s%s", type, size, ident, prefix, name); /* Add a range for vectored values. */ if (size > 1 || vpi_get(vpiLeftRange, item) != 0) { fprintf(dump_file, " [%i:%i]", (int)vpi_get(vpiLeftRange, item), (int)vpi_get(vpiRightRange, item)); } fprintf(dump_file, " $end\n"); break; case vpiModule: case vpiGenScope: case vpiFunction: case vpiTask: case vpiNamedBegin: case vpiNamedFork: if (depth > 0) { /* list of types to iterate upon */ static int types[] = { /* Value */ vpiNamedEvent, vpiNet, /* vpiParameter, */ vpiReg, vpiVariables, /* Scope */ vpiFunction, vpiGenScope, vpiModule, vpiNamedBegin, vpiNamedFork, vpiTask, -1 }; int i; int nskip = (vcd_names_search(&vcd_tab, fullname) != 0); /* We have to always scan the scope because the * depth could be different for this call. */ if (nskip) { vpi_printf("VCD warning: ignoring signals in " "previously scanned scope %s.\n", fullname); } else { vcd_names_add(&vcd_tab, fullname); } name = vpi_get_str(vpiName, item); fprintf(dump_file, "$scope %s %s $end\n", type, name); for (i=0; types[i]>0; i++) { vpiHandle hand; vpiHandle argv = vpi_iterate(types[i], item); while (argv && (hand = vpi_scan(argv))) { scan_item(depth-1, hand, nskip); } } /* Sort any signals that we added above. */ fprintf(dump_file, "$upscope $end\n"); } break; } }
/* * Register veriusertfs routines/wrappers. Iterate over the tfcell * array, registering each function. */ void veriusertfs_register_table(p_tfcell vtable) { static int need_EOS_cb = 1; const char*path; p_tfcell tf; s_vpi_systf_data tf_data; p_pli_data data; static char trace_buf[1024]; if (!pli_trace && (path = getenv("PLI_TRACE"))) { if (strcmp(path,"-") == 0) pli_trace = stdout; else { pli_trace = fopen(path, "w"); if (!pli_trace) { perror(path); exit(1); } } setvbuf(pli_trace, trace_buf, _IOLBF, sizeof(trace_buf)); } for (tf = vtable; tf; tf++) { /* last element */ if (tf->type == 0) break; /* force forwref true */ if (!tf->forwref) { vpi_printf("veriusertfs: %s, forcing forwref = true\n", tf->tfname); } /* squirrel away veriusertfs in persistent user_data */ data = calloc(1, sizeof(s_pli_data)); udata_count += 1; udata_store = (p_pli_data*)realloc(udata_store, udata_count*sizeof(p_pli_data*)); udata_store[udata_count-1] = data; if (need_EOS_cb) { s_cb_data cb_data; cb_data.reason = cbEndOfSimulation; cb_data.time = 0; cb_data.cb_rtn = sys_end_of_simulation; cb_data.user_data = "system"; vpi_register_cb(&cb_data); need_EOS_cb = 0; } data->tf = tf; /* Build a VPI system task/function structure, and point it to the pli_data that represents this function. Supply wrapper functions for the system task actions. */ memset(&tf_data, 0, sizeof(s_vpi_systf_data)); switch (tf->type) { case usertask: tf_data.type = vpiSysTask; break; case userfunction: tf_data.sysfunctype = vpiIntFunc; tf_data.type = vpiSysFunc; break; case userrealfunction: tf_data.sysfunctype = vpiRealFunc; tf_data.type = vpiSysFunc; break; default: vpi_printf("veriusertfs: %s, unsupported type %d\n", tf->tfname, tf->type); continue; } tf_data.tfname = tf->tfname; tf_data.compiletf = compiletf; tf_data.calltf = calltf; tf_data.sizetf = (PLI_INT32 (*)(PLI_BYTE8 *))tf->sizetf; tf_data.user_data = (char *)data; if (pli_trace) { fprintf(pli_trace, "Registering system %s:\n", tf->type == usertask ? "task" : "function"); fprintf(pli_trace, " tfname : %s\n", tf->tfname); if (tf->data) fprintf(pli_trace, " data : %d\n", tf->data); if (tf->checktf) fprintf(pli_trace, " checktf: %p\n", tf->checktf); if (tf->sizetf) fprintf(pli_trace, " sizetf : %p\n", tf->sizetf); if (tf->calltf) fprintf(pli_trace, " calltf : %p\n", tf->calltf); if (tf->misctf) fprintf(pli_trace, " misctf : %p\n", tf->misctf); } /* register */ vpi_register_systf(&tf_data); } return; }
/* * This function calls the veriusertfs checktf and sets up all the * callbacks misctf requires. */ static PLI_INT32 compiletf(char *data) { p_pli_data pli; p_tfcell tf; s_cb_data cb_data; vpiHandle call_h, arg_i, arg_h; p_pli_data dp; int rtn = 0; /* cast back from opaque */ pli = (p_pli_data)data; tf = pli->tf; /* get call handle */ call_h = vpi_handle(vpiSysTfCall, NULL); /* Attach the pli_data structure to the vpi handle of the system task. This is how I manage the map from vpiHandle to PLI1 pli data. We do it here (instead of during register) because this is the first that I have both the vpiHandle and the pli_data. */ vpi_put_userdata(call_h, pli); /* default cb_data */ memset(&cb_data, 0, sizeof(s_cb_data)); cb_data.cb_rtn = callback; cb_data.user_data = data; /* register EOS misctf callback */ cb_data.reason = cbEndOfSimulation; cb_data.obj = call_h; vpi_register_cb(&cb_data); /* If there is a misctf function, then create a value change callback for all the arguments. In the tf_* API, misctf functions get value change callbacks, controlled by the tf_asyncon and tf_asyncoff functions. */ if (tf->misctf && ((arg_i = vpi_iterate(vpiArgument, call_h)) != NULL)) { int paramvc = 1; cb_data.reason = cbValueChange; while ((arg_h = vpi_scan(arg_i)) != NULL) { /* replicate user_data for each instance */ dp = calloc(1, sizeof(s_pli_data)); memcpy(dp, cb_data.user_data, sizeof(s_pli_data)); dp->paramvc = paramvc++; cb_data.user_data = (char *)dp; cb_data.obj = arg_h; vpi_register_cb(&cb_data); } } /* * Since we are in compiletf, checktf and misctf need to * be executed. Check runs first to match other simulators. */ if (tf->checktf) { if (pli_trace) { fprintf(pli_trace, "Call %s->checktf(reason_checktf)\n", tf->tfname); } rtn = tf->checktf(tf->data, reason_checktf); } if (tf->misctf) { if (pli_trace) { fprintf(pli_trace, "Call %s->misctf" "(user_data=%d, reason=%d, paramvc=%d)\n", tf->tfname, tf->data, reason_endofcompile, 0); } tf->misctf(tf->data, reason_endofcompile, 0); } return rtn; }
static PLI_INT32 readonly_callback(p_cb_data cb_data) { vpiHandle net_iter, net_handle, cb_h; s_cb_data cb_data_s; s_vpi_time verilog_time_s; s_vpi_value value_s; s_vpi_time time_s; char buf[MAXLINE]; int n; int i; char *myhdl_time_string; myhdl_time64_t delay; static int start_flag = 1; if (start_flag) { start_flag = 0; n = write(wpipe, "START", 5); // vpi_printf("INFO: RO cb at start-up\n"); if ((n = read(rpipe, buf, MAXLINE)) == 0) { vpi_printf("ABORT from RO cb at start-up\n"); vpi_control(vpiFinish, 1); /* abort simulation */ } assert(n > 0); } buf[0] = '\0'; verilog_time_s.type = vpiSimTime; vpi_get_time(NULL, &verilog_time_s); verilog_time = timestruct_to_time(&verilog_time_s); if (verilog_time != (pli_time * 1000 + delta)) { vpi_printf("%u %u\n", verilog_time_s.high, verilog_time_s.low ); vpi_printf("%llu %llu %d\n", verilog_time, pli_time, delta); } /* Icarus 0.7 fails on this assertion beyond 32 bits due to a bug */ // assert(verilog_time == pli_time * 1000 + delta); assert( (verilog_time & 0xFFFFFFFF) == ( (pli_time * 1000 + delta) & 0xFFFFFFFF ) ); sprintf(buf, "%llu ", pli_time); net_iter = vpi_iterate(vpiArgument, to_myhdl_systf_handle); value_s.format = vpiHexStrVal; i = 0; while ((net_handle = vpi_scan(net_iter)) != NULL) { if (changeFlag[i]) { strcat(buf, vpi_get_str(vpiName, net_handle)); strcat(buf, " "); vpi_get_value(net_handle, &value_s); strcat(buf, value_s.value.str); strcat(buf, " "); changeFlag[i] = 0; } i++; vpi_free_object(net_handle); // done with this one } //vpi_free_object(net_iter); n = write(wpipe, buf, strlen(buf)); if ((n = read(rpipe, buf, MAXLINE)) == 0) { // vpi_printf("ABORT from RO cb\n"); vpi_control(vpiFinish, 1); /* abort simulation */ return(0); } assert(n > 0); buf[n] = '\0'; /* save copy for later callback */ strcpy(bufcp, buf); myhdl_time_string = strtok(buf, " "); myhdl_time = (myhdl_time64_t) strtoull(myhdl_time_string, (char **) NULL, 10); delay = (myhdl_time - pli_time) * 1000; assert(delay >= 0); assert(delay <= 0xFFFFFFFF); if (delay > 0) { // schedule cbAfterDelay callback assert(delay > delta); delay -= delta; /* Icarus 20030518 runs RO callbacks when time has already advanced */ /* Therefore, one had to compensate for the prescheduled delta callback */ /* delay -= 1; */ /* Icarus 20031009 has a different scheduler, more correct I believe */ /* compensation is no longer necessary */ delta = 0; pli_time = myhdl_time; // register cbAfterDelay callback // time_s.type = vpiSimTime; time_s.high = 0; time_s.low = (PLI_UINT32) delay; cb_data_s.reason = cbAfterDelay; cb_data_s.user_data = NULL; cb_data_s.cb_rtn = delay_callback; cb_data_s.obj = NULL; cb_data_s.time = &time_s; cb_data_s.value = NULL; cb_h = vpi_register_cb(&cb_data_s); vpi_free_object(cb_h); } else { delta++; assert(delta < 1000); } return(0); }
static void scan_item(unsigned depth, vpiHandle item, int skip) { struct t_cb_data cb; struct vcd_info* info; enum fstVarType type = FST_VT_MAX; enum fstScopeType stype = FST_ST_MAX; enum fstVarDir dir; const char *name; const char *fullname; char *escname; const char *ident; fstHandle new_ident; int nexus_id; unsigned size; PLI_INT32 item_type; /* Get the displayed type for the various $var and $scope types. */ /* Not all of these are supported now, but they should be in a * future development version. */ item_type = vpi_get(vpiType, item); switch (item_type) { case vpiNamedEvent: type = FST_VT_VCD_EVENT; break; case vpiIntVar: case vpiIntegerVar: type = FST_VT_VCD_INTEGER; break; case vpiParameter: type = FST_VT_VCD_PARAMETER; break; /* Icarus converts realtime to real. */ case vpiRealVar: type = FST_VT_VCD_REAL; break; case vpiMemoryWord: case vpiBitVar: case vpiByteVar: case vpiShortIntVar: case vpiLongIntVar: case vpiReg: type = FST_VT_VCD_REG; break; /* Icarus converts a time to a plain register. */ case vpiTimeVar: type = FST_VT_VCD_TIME; break; case vpiNet: switch (vpi_get(vpiNetType, item)) { case vpiWand: type = FST_VT_VCD_WAND; break; case vpiWor: type = FST_VT_VCD_WOR; break; case vpiTri: type = FST_VT_VCD_TRI; break; case vpiTri0: type = FST_VT_VCD_TRI0; break; case vpiTri1: type = FST_VT_VCD_TRI1; break; case vpiTriReg: type = FST_VT_VCD_TRIREG; break; case vpiTriAnd: type = FST_VT_VCD_TRIAND; break; case vpiTriOr: type = FST_VT_VCD_TRIOR; break; case vpiSupply1: type = FST_VT_VCD_SUPPLY1; break; case vpiSupply0: type = FST_VT_VCD_SUPPLY0; break; default: type = FST_VT_VCD_WIRE; break; } break; case vpiNamedBegin: stype = FST_ST_VCD_BEGIN; break; case vpiNamedFork: stype = FST_ST_VCD_FORK; break; case vpiFunction: stype = FST_ST_VCD_FUNCTION; break; case vpiGenScope: stype = FST_ST_VCD_GENERATE; break; case vpiModule: stype = FST_ST_VCD_MODULE; break; case vpiTask: stype = FST_ST_VCD_TASK; break; default: vpi_printf("FST warning: $dumpvars: Unsupported argument " "type (%s)\n", vpi_get_str(vpiType, item)); return; } /* Do some special processing/checking on array words. Dumping * array words is an Icarus extension. */ if (item_type == vpiMemoryWord) { /* Turn a non-constant array word select into a constant * word select. */ if (vpi_get(vpiConstantSelect, item) == 0) { vpiHandle array = vpi_handle(vpiParent, item); PLI_INT32 idx = vpi_get(vpiIndex, item); item = vpi_handle_by_index(array, idx); } /* An array word is implicitly escaped so look for an * escaped identifier that this could conflict with. */ /* This does not work as expected since we always find at * least the array word. We likely need a custom routine. */ if (vpi_get(vpiType, item) == vpiMemoryWord && vpi_handle_by_name(vpi_get_str(vpiFullName, item), 0)) { vpi_printf("FST warning: array word %s will conflict " "with an escaped identifier.\n", vpi_get_str(vpiFullName, item)); } } fullname = vpi_get_str(vpiFullName, item); /* Generate the $var or $scope commands. */ switch (item_type) { case vpiParameter: vpi_printf("FST sorry: $dumpvars: can not dump parameters.\n"); break; case vpiNamedEvent: case vpiIntegerVar: case vpiBitVar: case vpiByteVar: case vpiShortIntVar: case vpiIntVar: case vpiLongIntVar: case vpiRealVar: case vpiMemoryWord: case vpiReg: case vpiTimeVar: case vpiNet: /* If we are skipping all signal or this is in an automatic * scope then just return. */ if (skip || vpi_get(vpiAutomatic, item)) return; /* Skip this signal if it has already been included. * This can only happen for implicitly given signals. */ if (vcd_names_search(&fst_var, fullname)) return; /* Declare the variable in the FST file. */ name = vpi_get_str(vpiName, item); if (is_escaped_id(name)) { escname = malloc(strlen(name) + 2); sprintf(escname, "\\%s", name); } else escname = strdup(name); /* Some signals can have an alias so handle that. */ nexus_id = vpi_get(_vpiNexusId, item); ident = 0; if (nexus_id) ident = find_nexus_ident(nexus_id); /* Named events do not have a size, but other tools use * a size of 1 and some viewers do not accept a width of * zero so we will also use a width of one for events. */ if (item_type == vpiNamedEvent) size = 1; else size = vpi_get(vpiSize, item); /* The FST format supports a port direction so if the net * is a port set the direction to one of the following: * FST_VD_INPUT, FST_VD_OUTPUT or FST_VD_INOUT */ dir = FST_VD_IMPLICIT; if (size > 1 || vpi_get(vpiLeftRange, item) != 0) { char *buf = malloc(strlen(escname) + 65); sprintf(buf, "%s [%i:%i]", escname, (int)vpi_get(vpiLeftRange, item), (int)vpi_get(vpiRightRange, item)); new_ident = fstWriterCreateVar(dump_file, type, dir, size, buf, (fstHandle)(long)ident); free(buf); } else { new_ident = fstWriterCreateVar(dump_file, type, dir, size, escname, (fstHandle)(long)ident); } free(escname); if (!ident) { if (nexus_id) set_nexus_ident(nexus_id, (const char *)(long)new_ident); /* Add a callback for the signal. */ info = malloc(sizeof(*info)); info->time.type = vpiSimTime; info->item = item; info->handle = new_ident; info->scheduled = 0; cb.time = &info->time; cb.user_data = (char*)info; cb.value = NULL; cb.obj = item; cb.reason = cbValueChange; cb.cb_rtn = variable_cb_1; info->dmp_next = 0; info->next = vcd_list; vcd_list = info; info->cb = vpi_register_cb(&cb); } break; case vpiModule: case vpiGenScope: case vpiFunction: case vpiTask: case vpiNamedBegin: case vpiNamedFork: if (depth > 0) { char *instname; char *defname = NULL; /* list of types to iterate upon */ static int types[] = { /* Value */ vpiNamedEvent, vpiNet, /* vpiParameter, */ vpiReg, vpiVariables, /* Scope */ vpiFunction, vpiGenScope, vpiModule, vpiNamedBegin, vpiNamedFork, vpiTask, -1 }; int i; int nskip = (vcd_names_search(&fst_tab, fullname) != 0); /* We have to always scan the scope because the * depth could be different for this call. */ if (nskip) { vpi_printf("FST warning: ignoring signals in " "previously scanned scope %s.\n", fullname); } else { vcd_names_add(&fst_tab, fullname); } /* Set the file and line information for this scope. * Everything has instance information. Only a module * has separate definition information. */ instname = vpi_get_str(vpiFile, item); fstWriterSetSourceInstantiationStem(dump_file, instname, (int)vpi_get(vpiLineNo, item), 0); if (item_type == vpiModule) { fstWriterSetSourceStem(dump_file, vpi_get_str(vpiDefFile, item), (int)vpi_get(vpiDefLineNo, item), 0); } else { fstWriterSetSourceStem(dump_file, instname, (int)vpi_get(vpiLineNo, item), 0); } /* This must be done before the other name is fetched * and the string must always be freed */ if (item_type == vpiModule) { defname = strdup(vpi_get_str(vpiDefName, item)); } name = vpi_get_str(vpiName, item); /* If the two names match only use the vpiName. */ if (defname && (strcmp(defname, name) == 0)) { free(defname); defname = NULL; } fstWriterSetScope(dump_file, stype, name, defname); free(defname); for (i=0; types[i]>0; i++) { vpiHandle hand; vpiHandle argv = vpi_iterate(types[i], item); while (argv && (hand = vpi_scan(argv))) { scan_item(depth-1, hand, nskip); } } /* Sort any signals that we added above. */ fstWriterSetUpscope(dump_file); } break; } }
static PLI_INT32 poll_for_simbus_bus(struct t_cb_data*cb) { int nfds = 0; int idx; int rc; fd_set read_set; FD_ZERO(&read_set); for (idx = 0 ; idx < MAX_INSTANCES ; idx += 1) { if (instance_table[idx].trig == 0) continue; if (instance_table[idx].fd > nfds) nfds = instance_table[idx].fd; FD_SET(instance_table[idx].fd, &read_set); } if (nfds == 0) return 0; rc = select(nfds+1, &read_set, 0, 0, 0); assert(rc != 0); if (rc < 0) { vpi_printf("ERROR:poll_for_simbus_bus:%s\n", sys_errlist[errno]); vpi_control(vpiFinish, 1); return 0; } assert(rc > 0); for (idx = 0 ; idx < MAX_INSTANCES ; idx += 1) { s_vpi_value value; if (instance_table[idx].trig == 0) continue; if (! FD_ISSET(instance_table[idx].fd, &read_set)) continue; /* This fd is readable, so try to read some data, and check if a command is complete. If not, then continue waiting. Otherwise, let this one be completed. */ consume_readable_data(idx); if (!check_readable(idx)) continue; value.format = vpiScalarVal; value.value.scalar = vpi1; vpi_put_value(instance_table[idx].trig, &value, 0, vpiNoDelay); instance_table[idx].trig = 0; } /* Check and see if there are still triggers waiting. If so then reschedule this callback. */ nfds += 1; for (idx = 0 ; idx < MAX_INSTANCES ; idx += 1) { if (instance_table[idx].trig == 0) continue; nfds += 1; } if (nfds > 0) { struct t_cb_data cb_data; struct t_vpi_time cb_time; cb_time.type = vpiSuppressTime; cb_data.reason = cbReadWriteSynch; cb_data.cb_rtn = poll_for_simbus_bus; cb_data.time = &cb_time; vpi_register_cb(&cb_data); } return 0; }
static void scan_item(unsigned depth, vpiHandle item, int skip) { struct t_cb_data cb; struct vcd_info* info; const char* type; const char* name; const char* ident; int nexus_id; /* list of types to iterate upon */ int i; static int types[] = { /* Value */ vpiNet, vpiReg, vpiVariables, /* Scope */ vpiFunction, vpiModule, vpiNamedBegin, vpiNamedFork, vpiTask, -1 }; switch (vpi_get(vpiType, item)) { case vpiMemory: /* don't know how to watch memories. */ break; case vpiNamedEvent: /* There is nothing in named events to dump. */ break; case vpiNet: type = "wire"; if(0){ case vpiIntegerVar: case vpiTimeVar: case vpiReg: type = "reg"; } if (skip) break; name = vpi_get_str(vpiName, item); nexus_id = vpi_get(_vpiNexusId, item); if (nexus_id) { ident = find_nexus_ident(nexus_id); } else { ident = 0; } if (!ident) { ident = strdup(vcdid); gen_new_vcd_id(); if (nexus_id) set_nexus_ident(nexus_id, ident); info = malloc(sizeof(*info)); info->time.type = vpiSimTime; info->item = item; info->ident = ident; info->scheduled = 0; cb.time = &info->time; cb.user_data = (char*)info; cb.value = NULL; cb.obj = item; cb.reason = cbValueChange; cb.cb_rtn = variable_cb_1; info->next = vcd_list; info->dmp_next = 0; vcd_list = info; info->cb = vpi_register_cb(&cb); } fprintf(dump_file, "$var %s %u %s %s", type, vpi_get(vpiSize, item), ident, name); /* FIXME if (vpi_get(vpiVector, item) */ if (vpi_get(vpiSize, item) > 1 || vpi_get(vpiLeftRange, item) != 0) { fprintf(dump_file, "[%i:%i]", vpi_get(vpiLeftRange, item), vpi_get(vpiRightRange, item)); } fprintf(dump_file, " $end\n"); break; case vpiRealVar: if (skip) break; /* Declare the variable in the VCD file. */ name = vpi_get_str(vpiName, item); ident = strdup(vcdid); gen_new_vcd_id(); fprintf(dump_file, "$var real 1 %s %s $end\n", ident, name); /* Add a callback for the variable. */ info = malloc(sizeof(*info)); info->time.type = vpiSimTime; info->item = item; info->ident = ident; info->scheduled = 0; cb.time = &info->time; cb.user_data = (char*)info; cb.value = NULL; cb.obj = item; cb.reason = cbValueChange; cb.cb_rtn = variable_cb_1; info->next = vcd_list; info->dmp_next = 0; vcd_list = info; info->cb = vpi_register_cb(&cb); break; case vpiModule: type = "module"; if(0){ case vpiNamedBegin: type = "begin"; }if(0){ case vpiTask: type = "task"; }if(0){ case vpiFunction: type = "function"; }if(0){ case vpiNamedFork: type = "fork"; } if (depth > 0) { int nskip; vpiHandle argv; const char* fullname = vpi_get_str(vpiFullName, item); #if 0 vpi_mcd_printf(1, "VCD info:" " scanning scope %s, %u levels\n", fullname, depth); #endif nskip = 0 != vcd_names_search(&vcd_tab, fullname); if (!nskip) vcd_names_add(&vcd_tab, fullname); else vpi_mcd_printf(1, "VCD warning:" " ignoring signals" " in previously scanned scope %s\n", fullname); name = vpi_get_str(vpiName, item); fprintf(dump_file, "$scope %s %s $end\n", type, name); for (i=0; types[i]>0; i++) { vpiHandle hand; argv = vpi_iterate(types[i], item); while (argv && (hand = vpi_scan(argv))) { scan_item(depth-1, hand, nskip); } } fprintf(dump_file, "$upscope $end\n"); } break; default: vpi_mcd_printf(1, "VCD Error: $dumpvars: Unsupported parameter " "type (%d)\n", vpi_get(vpiType, item)); } }
static PLI_INT32 to_myhdl_calltf(PLI_BYTE8 *user_data) { vpiHandle net_iter, net_handle, cb_h; char buf[MAXLINE]; char s[MAXWIDTH]; int n; int i; int *id; s_cb_data cb_data_s; s_vpi_time verilog_time_s; s_vpi_time time_s; s_vpi_value value_s; static int to_myhdl_flag = 0; if (to_myhdl_flag) { vpi_printf("ERROR: $to_myhdl called more than once\n"); vpi_control(vpiFinish, 1); /* abort simulation */ return(0); } to_myhdl_flag = 1; init_pipes(); verilog_time_s.type = vpiSimTime; vpi_get_time(NULL, &verilog_time_s); verilog_time = timestruct_to_time(&verilog_time_s); if (verilog_time != 0) { vpi_printf("ERROR: $to_myhdl should be called at time 0\n"); vpi_control(vpiFinish, 1); /* abort simulation */ return(0); } sprintf(buf, "TO 0 "); pli_time = 0; delta = 0; time_s.type = vpiSuppressTime; value_s.format = vpiSuppressVal; cb_data_s.reason = cbValueChange; cb_data_s.cb_rtn = change_callback; cb_data_s.time = &time_s; cb_data_s.value = &value_s; // value_s.format = vpiHexStrVal; i = 0; to_myhdl_systf_handle = vpi_handle(vpiSysTfCall, NULL); net_iter = vpi_iterate(vpiArgument, to_myhdl_systf_handle); while ((net_handle = vpi_scan(net_iter)) != NULL) { if (i == MAXARGS) { vpi_printf("ERROR: $to_myhdl max #args (%d) exceeded\n", MAXARGS); vpi_control(vpiFinish, 1); /* abort simulation */ } strcat(buf, vpi_get_str(vpiName, net_handle)); strcat(buf, " "); sprintf(s, "%d ", vpi_get(vpiSize, net_handle)); strcat(buf, s); changeFlag[i] = 0; id = malloc(sizeof(int)); *id = i; cb_data_s.user_data = (PLI_BYTE8 *)id; cb_data_s.obj = net_handle; cb_h = vpi_register_cb(&cb_data_s); vpi_free_object(cb_h); i++; vpi_free_object(net_handle); } //vpi_free_object(net_iter); n = write(wpipe, buf, strlen(buf)); if ((n = read(rpipe, buf, MAXLINE)) == 0) { vpi_printf("ABORT from $to_myhdl\n"); vpi_control(vpiFinish, 1); /* abort simulation */ return(0); } buf[n] = '\0'; assert(n > 0); // register read-only callback // time_s.type = vpiSimTime; time_s.high = 0; time_s.low = 0; cb_data_s.reason = cbReadOnlySynch; cb_data_s.user_data = NULL; cb_data_s.cb_rtn = readonly_callback; cb_data_s.obj = NULL; cb_data_s.time = &time_s; cb_data_s.value = NULL; cb_h = vpi_register_cb(&cb_data_s); vpi_free_object(cb_h); // pre-register delta cycle callback // delta = 0; time_s.type = vpiSimTime; time_s.high = 0; time_s.low = 1; cb_data_s.reason = cbAfterDelay; cb_data_s.user_data = NULL; cb_data_s.cb_rtn = delta_callback; cb_data_s.obj = NULL; cb_data_s.time = &time_s; cb_data_s.value = NULL; cb_h = vpi_register_cb(&cb_data_s); vpi_free_object(cb_h); return(0); }