diff --git a/vpi/Makefile.in b/vpi/Makefile.in index 933709759..7fdb8a432 100644 --- a/vpi/Makefile.in +++ b/vpi/Makefile.in @@ -56,11 +56,10 @@ dep: # Object files for system.vpi O = sys_table.o sys_convert.o sys_deposit.o sys_display.o sys_fileio.o \ -sys_finish.o sys_icarus.o sys_plusargs.o sys_random.o sys_random_mti.o \ -sys_readmem.o sys_readmem_lex.o sys_scanf.o sys_sdf.o \ -sys_time.o sys_vcd.o sys_vcdoff.o vcd_priv.o \ -mt19937int.o sys_priv.o sdf_lexor.o sdf_parse.o stringheap.o \ -vams_simparam.o +sys_finish.o sys_icarus.o sys_plusargs.o sys_queue.o sys_random.o \ +sys_random_mti.o sys_readmem.o sys_readmem_lex.o sys_scanf.o sys_sdf.o \ +sys_time.o sys_vcd.o sys_vcdoff.o vcd_priv.o mt19937int.o sys_priv.o \ +sdf_lexor.o sdf_parse.o stringheap.o vams_simparam.o ifeq (@HAVE_LIBZ@,yes) ifeq (@HAVE_LIBBZ2@,yes) diff --git a/vpi/sys_icarus.c b/vpi/sys_icarus.c index d2c33430f..0c851f64e 100644 --- a/vpi/sys_icarus.c +++ b/vpi/sys_icarus.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008-2010 Cary R. (cygcary@yahoo.com) + * Copyright (C) 2008-2011 Cary R. (cygcary@yahoo.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -168,26 +168,6 @@ void sys_special_register(void) tf_data.user_data = "$sync$nor$plane"; vpi_register_systf(&tf_data); - tf_data.tfname = "$q_initialize"; - tf_data.user_data = "$q_initialize"; - vpi_register_systf(&tf_data); - - tf_data.tfname = "$q_add"; - tf_data.user_data = "$q_add"; - vpi_register_systf(&tf_data); - - tf_data.tfname = "$q_remove"; - tf_data.user_data = "$q_remove"; - vpi_register_systf(&tf_data); - - tf_data.tfname = "$q_full"; - tf_data.user_data = "$q_full"; - vpi_register_systf(&tf_data); - - tf_data.tfname = "$q_exam"; - tf_data.user_data = "$q_exam"; - vpi_register_systf(&tf_data); - tf_data.tfname = "$dumpports"; tf_data.user_data = "$dumpports"; vpi_register_systf(&tf_data); diff --git a/vpi/sys_queue.c b/vpi/sys_queue.c new file mode 100644 index 000000000..3a9d849a2 --- /dev/null +++ b/vpi/sys_queue.c @@ -0,0 +1,1438 @@ +/* + * Copyright (C) 2011 Cary R. (cygcary@yahoo.com) + * + * This program is free software; you can redistribute it and/or modify + * it 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. + */ + +#include "sys_priv.h" +#include +#include +#include + +/* + * The two queue types. + */ +#define IVL_QUEUE_FIFO 1 +#define IVL_QUEUE_LIFO 2 + +/* + * The statistical codes that can be passed to $q_exam(). + */ +#define IVL_QUEUE_LENGTH 1 +#define IVL_QUEUE_MEAN 2 +#define IVL_QUEUE_MAX_LENGTH 3 +#define IVL_QUEUE_SHORTEST 4 +#define IVL_QUEUE_LONGEST 5 +#define IVL_QUEUE_AVERAGE 6 + +/* + * All the values that can be returned by the queue tasks/function. + */ +#define IVL_QUEUE_OK 0 +#define IVL_QUEUE_FULL 1 +#define IVL_QUEUE_UNDEFINED_ID 2 +#define IVL_QUEUE_EMPTY 3 +#define IVL_QUEUE_UNSUPPORTED_TYPE 4 +#define IVL_QUEUE_INVALID_LENGTH 5 +#define IVL_QUEUE_DUPLICATE_ID 6 +#define IVL_QUEUE_OUT_OF_MEMORY 7 +/* Icarus specific status codes. */ +#define IVL_QUEUE_UNDEFINED_STAT_CODE 8 +#define IVL_QUEUE_VALUE_OVERFLOWED 9 +#define IVL_QUEUE_NO_STATISTICS 10 + +/* + * Routine to add the given time to the the total time (high/low). + */ +void add_to_wait_time(uint64_t *high, uint64_t *low, uint64_t time) +{ + uint64_t carry = 0U; + + if ((UINT64_MAX - *low) < time) carry = 1U; + *low += time; + assert((carry == 0U) || (*high < UINT64_MAX)); + *high += carry; +} + +/* + * Routine to divide the given total time (high/low) by the number of + * items to get the average. + */ +uint64_t calc_average_wait_time(uint64_t high, uint64_t low, uint64_t total) +{ + int bit = 64; + uint64_t result = 0U; + assert(total != 0U); + if (high == 0U) return (low/total); + + /* This is true by design, but since we can only return 64 bits + * make sure nothing went wrong. */ + assert(high < total); + + /* It's a big value so calculate the average the long way. */ + do { + /* Copy bits from low to high until we have a bit to place + * in the result or there are no bits left. */ + while ((bit >= 0) && (high < total)) { + high <<= 1; + high |= (low & 0x8000000000000000) != 0; + low <<= 1; + bit -= 1; + } + + /* If this is a valid bit, set the appropriate bit in the + * result and subtract the total from the current value. */ + if (bit >= 0) { + result |= UINT64_C(1) << bit; + high = high - total; + } + /* Loop until there are no bits left. */ + } while (bit > 0); + + return result; +} + +/* + * The data structure used for an individual queue element. It hold four + * state result for the jobs and inform fields along with the time that + * the element was added in base time units. + */ +typedef struct t_ivl_queue_elem { + uint64_t time; + s_vpi_vecval job; + s_vpi_vecval inform; +} s_ivl_queue_elem, *p_ivl_queue_elem; + +/* + * This structure is used to represent a specific queue. The time + * information is in base simulation units. + */ +typedef struct t_ivl_queue_base { + uint64_t shortest_wait_time; + uint64_t first_add_time; + uint64_t latest_add_time; + uint64_t wait_time_high; + uint64_t wait_time_low; + uint64_t number_of_adds; + p_ivl_queue_elem queue; + PLI_INT32 id; + PLI_INT32 length; + PLI_INT32 type; + PLI_INT32 head; + PLI_INT32 elems; + PLI_INT32 max_len; + PLI_INT32 have_shortest_statistic; +} s_ivl_queue_base, *p_ivl_queue_base; + +/* + * For now we keep the queues in a vector since there are likely not too many + * of them. We may need something more efficient later. + */ +static p_ivl_queue_base base = NULL; +static int64_t base_len = 0; + +/* + * This routine is called at the end of simulation to free the queue memory. + */ +static PLI_INT32 cleanup_queue(p_cb_data cause) +{ + PLI_INT32 idx; + (void) cause; /* Unused argument. */ + for (idx = 0; idx < base_len; idx += 1) free(base[idx].queue); + free(base); + base = NULL; + base_len = 0; + return 0; +} + +/* + * Add a new queue to the list, return 1 if there is not enough memory, + * otherwise return 0. + */ +static unsigned create_queue(PLI_INT32 id, PLI_INT32 type, PLI_INT32 length) +{ + p_ivl_queue_base new_base; + p_ivl_queue_elem queue; + + /* Allocate space for the new queue base. */ + base_len += 1; + new_base = (p_ivl_queue_base) realloc(base, + base_len*sizeof(s_ivl_queue_base)); + + /* If we ran out of memory then fix the length and return a fail. */ + if (new_base == NULL) { + base_len -= 1; + return 1; + } + base = new_base; + + /* Allocate space for the queue elements. */ + queue = (p_ivl_queue_elem) malloc(length*sizeof(s_ivl_queue_elem)); + + /* If we ran out of memory then fix the length and return a fail. */ + if (queue == NULL) { + base_len -= 1; + return 1; + } + + /* The memory was allocated so configure it. */ + base[base_len-1].queue = queue; + base[base_len-1].id = id; + base[base_len-1].length = length; + base[base_len-1].type = type; + base[base_len-1].head = 0; + base[base_len-1].elems = 0; + base[base_len-1].max_len = 0; + base[base_len-1].shortest_wait_time = UINT64_MAX; + base[base_len-1].first_add_time = 0U; + base[base_len-1].latest_add_time = 0U; + base[base_len-1].wait_time_high = 0U; + base[base_len-1].wait_time_low = 0U; + base[base_len-1].number_of_adds = 0U; + base[base_len-1].have_shortest_statistic = 0; + return 0; +} + +/* + * Check to see if the given queue is full. + */ +static unsigned is_queue_full(int64_t idx) +{ + if (base[idx].elems >= base[idx].length) return 1; + + return 0; +} + +/* + * Add the job and inform to the queue. Return 1 if the queue is full, + * otherwise return 0. + */ +static unsigned add_to_queue(int64_t idx, p_vpi_vecval job, + p_vpi_vecval inform) +{ + PLI_INT32 length = base[idx].length; + PLI_INT32 type = base[idx].type; + PLI_INT32 head = base[idx].head; + PLI_INT32 elems = base[idx].elems; + PLI_INT32 loc; + s_vpi_time cur_time; + uint64_t time; + + assert(elems <= length); + + /* If the queue is full we can't add anything. */ + if (elems == length) return 1; + + /* Increment the number of element since one will be added.*/ + base[idx].elems += 1; + + /* Save the job and inform to the queue. */ + if (type == IVL_QUEUE_LIFO) { + assert(head == 0); /* For a LIFO head must always be zero. */ + loc = elems; + } else { + assert(type == IVL_QUEUE_FIFO); + loc = head + elems; + if (loc >= length) loc -= length; + } + base[idx].queue[loc].job.aval = job->aval; + base[idx].queue[loc].job.bval = job->bval; + base[idx].queue[loc].inform.aval = inform->aval; + base[idx].queue[loc].inform.bval = inform->bval; + + /* Save the current time with this entry for the statistics. */ + cur_time.type = vpiSimTime; + vpi_get_time(NULL, &cur_time); + time = cur_time.high; + time <<= 32; + time |= cur_time.low; + base[idx].queue[loc].time = time; + + /* Increment the maximum length if needed. */ + if (base[idx].max_len == elems) base[idx].max_len += 1; + + /* Update the inter-arrival statistics. */ + assert(base[idx].number_of_adds < UINT64_MAX); + base[idx].number_of_adds += 1; + if (base[idx].number_of_adds == 1) base[idx].first_add_time = time; + base[idx].latest_add_time = time; + + return 0; +} + +/* + * Get the job and inform values from the queue. Return 1 if the queue is + * empty, otherwise return 0. + */ +static unsigned remove_from_queue(int64_t idx, p_vpi_vecval job, + p_vpi_vecval inform) +{ + PLI_INT32 type = base[idx].type; + PLI_INT32 head = base[idx].head; + PLI_INT32 elems = base[idx].elems - 1; + PLI_INT32 loc; + s_vpi_time cur_time; + uint64_t time; + + assert(elems >= -1); + + /* If the queue is empty we can't remove anything. */ + if (elems < 0) return 1; + + /* Decrement the number of element in the queue structure since one + * will be removed.*/ + base[idx].elems -= 1; + + /* Remove the job and inform from the queue. */ + if (type == IVL_QUEUE_LIFO) { + assert(head == 0); /* For a LIFO head must always be zero. */ + loc = elems; + } else { + assert(type == IVL_QUEUE_FIFO); + loc = head; + if (head + 1 == base[idx].length) base[idx].head = 0; + else base[idx].head += 1; + } + job->aval = base[idx].queue[loc].job.aval; + job->bval = base[idx].queue[loc].job.bval; + inform->aval = base[idx].queue[loc].inform.aval; + inform->bval = base[idx].queue[loc].inform.bval; + + /* Get the current simulation time. */ + cur_time.type = vpiSimTime; + vpi_get_time(NULL, &cur_time); + time = cur_time.high; + time <<= 32; + time |= cur_time.low; + + /* Set the shortest wait time if needed. */ + assert(time >= base[idx].queue[loc].time); + time -= base[idx].queue[loc].time; + if (time < base[idx].shortest_wait_time) { + base[idx].shortest_wait_time = time; + } + base[idx].have_shortest_statistic = 1; + + /* Add the current element wait time to the total wait time. */ + add_to_wait_time(&(base[idx].wait_time_high), &(base[idx].wait_time_low), + time); + + return 0; +} + +/* + * Return the current queue length. + */ +static PLI_INT32 get_current_queue_length(int64_t idx) +{ + return base[idx].elems; +} + +/* + * Return the maximum queue length. + */ +static PLI_INT32 get_maximum_queue_length(int64_t idx) +{ + return base[idx].max_len; +} + +/* + * Return the longest wait time in the queue in base simulation units. + * Make sure to check that there are elements in the queue before calling + * this routine. The caller will need to scale the time as appropriate. + */ +static uint64_t get_longest_queue_time(int64_t idx) +{ + s_vpi_time cur_time; + uint64_t time; + + /* Get the current simulation time. */ + cur_time.type = vpiSimTime; + vpi_get_time(NULL, &cur_time); + time = cur_time.high; + time <<= 32; + time |= cur_time.low; + + /* Subtract the element with the longest time (the head) from the + * current time. */ + time -= base[idx].queue[base[idx].head].time; + + return time; +} + +/* + * Check to see if there are inter-arrival time statistics. + */ +static unsigned have_interarrival_statistic(int64_t idx) +{ + return (base[idx].number_of_adds >= 2U); +} + +/* + * Return the mean inter-arrival time for the queue. This is just the + * latest add time minus the first add time divided be the number of time + * deltas (the number of adds - 1). + */ +static uint64_t get_mean_interarrival_time(int64_t idx) +{ + return ((base[idx].latest_add_time - base[idx].first_add_time) / + (base[idx].number_of_adds - 1U)); +} + +/* + * Check to see if there are shortest wait time statistics. + */ +static unsigned have_shortest_wait_statistic(int64_t idx) +{ + return (base[idx].have_shortest_statistic != 0); +} + +/* + * Return the shortest amount of time an element has waited in the queue. + */ +static uint64_t get_shortest_wait_time(int64_t idx) +{ + return base[idx].shortest_wait_time; +} + +/* + * Check to see if we have an average wait time statistics. + */ +static unsigned have_average_wait_statistic(int64_t idx) +{ + return (base[idx].number_of_adds >= 1U); +} + +/* + * Return the average wait time in the queue. + */ +static uint64_t get_average_wait_time(int64_t idx) +{ + PLI_INT32 length = base[idx].length; + PLI_INT32 loc = base[idx].head; + PLI_INT32 elems = base[idx].elems; + PLI_INT32 count; + /* Initialize the high and low time with the current total time. */ + uint64_t high = base[idx].wait_time_high; + uint64_t low = base[idx].wait_time_low; + s_vpi_time cur_time; + uint64_t time, add_time; + + /* Get the current simulation time. */ + cur_time.type = vpiSimTime; + vpi_get_time(NULL, &cur_time); + time = cur_time.high; + time <<= 32; + time |= cur_time.low; + + /* For each element still in the queue, add its wait time to the + * total wait time. */ + for (count = 0; count < elems; count += 1) { + add_time = base[idx].queue[loc].time; + add_to_wait_time(&high, &low, time-add_time); + + /* Move to the next element. */ + loc += 1; + if (loc == length) loc = 0; + } + + /* Return the average wait time. */ + return calc_average_wait_time(high, low, base[idx].number_of_adds); +} + +/* + * Check to see if the given id already exists. Return the index for the + * queue if it exists, otherwise return -1. + */ +static int64_t get_id_index(PLI_INT32 id) +{ + int64_t idx; + + for (idx = 0; idx < base_len; idx += 1) { + if (id == base[idx].id) return idx; + } + + return -1; +} + +/* + * Check to see if the given value is bit based and has 32 or fewer bits. + */ +static unsigned is_32_or_smaller_obj(vpiHandle obj) +{ + PLI_INT32 const_type; + unsigned rtn = 0; + + assert(obj); + + switch(vpi_get(vpiType, obj)) { + case vpiConstant: + case vpiParameter: + const_type = vpi_get(vpiConstType, obj); + if ((const_type != vpiRealConst) && + (const_type != vpiStringConst)) rtn = 1; + break; + + /* These can have valid 32 bit or smaller numeric values. */ + case vpiIntegerVar: + case vpiMemoryWord: + case vpiNet: + case vpiPartSelect: + case vpiReg: + rtn = 1; + break; + } + + /* The object must be 32 bits or smaller. */ + if (vpi_get(vpiSize, obj) > 32) rtn = 0; + + return rtn; +} + +/* + * Check to see if the argument is a variable that is exactly 32 bits in size. + */ +static void check_var_arg_32(vpiHandle arg, vpiHandle callh, + const char *name, const char *desc) +{ + assert(arg); + + switch (vpi_get(vpiType, arg)) { + case vpiMemoryWord: + case vpiPartSelect: + case vpiReg: // Check that we have exactly 32 bits. + if (vpi_get(vpiSize, arg) != 32) { + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s's %s (variable) argument must be 32 bits.\n", + name, desc); + vpi_control(vpiFinish, 1); + } + case vpiIntegerVar: + break; + default: + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s's %s argument must be a 32 bit variable.\n", + name, desc); + vpi_control(vpiFinish, 1); + } +} + +/* + * Check to see if the argument is a variable of at least 32 bits. + */ +static void check_var_arg_large(vpiHandle arg, vpiHandle callh, + const char *name, const char *desc) +{ + assert(arg); + + switch (vpi_get(vpiType, arg)) { + case vpiMemoryWord: + case vpiPartSelect: + case vpiReg: // Check that we have at least 32 bits. + if (vpi_get(vpiSize, arg) < 32) { + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s's %s (variable) argument must have at least " + "32 bits.\n", name, desc); + vpi_control(vpiFinish, 1); + } + case vpiIntegerVar: + case vpiTimeVar: + break; + default: + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s's %s argument must be a variable.\n", name, desc); + vpi_control(vpiFinish, 1); + } +} + +/* + * Check that the given number of arguments are numeric. + */ +static unsigned check_numeric_args(vpiHandle argv, unsigned count, + vpiHandle callh, const char *name) +{ + unsigned idx; + + /* Check that the first count arguments are numeric. Currently + * only three are needed/supported. */ + for (idx = 0; idx < count; idx += 1) { + char *loc; + vpiHandle arg = vpi_scan(argv); + + /* Get the name for this argument. */ + switch (idx) { + case 0: loc = "first"; break; + case 1: loc = "second"; break; + case 2: loc = "third"; break; + default: assert(0); + } + + /* Check that there actually is an argument. */ + if (! arg) { + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s requires a %s (<= 32 bit numeric) argument.\n", + name, loc); + vpi_control(vpiFinish, 1); + return 1; + } + + /* Check that it is no more than 32 bits. */ + if (! is_32_or_smaller_obj(arg)) { + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s's %s argument must be numeric (<= 32 bits).\n", + name, loc); + vpi_control(vpiFinish, 1); + } + } + + return 0; +} + +/* + * Check to see if the given argument is valid (does not have any X/Z bits). + * Return zero if it is valid and a positive value if it is invalid. + */ +static unsigned get_valid_32(vpiHandle arg, PLI_INT32 *value) +{ + PLI_INT32 size, mask; + s_vpi_value val; + + size = vpi_get(vpiSize, arg); + /* The compiletf routine should have already verified that this is + * <= 32 bits. */ + assert((size <= 32) && (size > 0)); + + /* Create a mask so that we only check the appropriate bits. */ + mask = UINT32_MAX >> (32 - size); + + /* Get the value and return the possible integer value in the value + * variable. Return the b-value bits to indicate if the value is + * undefined (has X/Z bit). */ + val.format = vpiVectorVal; + vpi_get_value(arg, &val); + + *value = val.value.vector->aval & mask; + /* If the argument is signed and less than 32 bit we need to sign + * extend the value. */ + if (vpi_get(vpiSigned, arg) && (size < 32)) { + if ((*value) & (1 << (size - 1))) *value |= ~mask; + } + return (val.value.vector->bval & mask); +} + +static void get_four_state(vpiHandle arg, p_vpi_vecval vec) +{ + PLI_INT32 size, mask; + s_vpi_value val; + + size = vpi_get(vpiSize, arg); + /* The compiletf routine should have already verified that this is + * <= 32 bits. */ + assert((size <= 32) && (size > 0)); + + /* Create a mask so that we only use the appropriate bits. */ + mask = UINT32_MAX >> (32 - size); + + /* Get the bits for the argument and save them in the return value. */ + val.format = vpiVectorVal; + vpi_get_value(arg, &val); + vec->aval = val.value.vector->aval & mask; + vec->bval = val.value.vector->bval & mask; + + /* If the argument is signed and less than 32 bit we need to sign + * extend the value. */ + if (vpi_get(vpiSigned, arg) && (size < 32)) { + if (vec->aval & (1 << (size - 1))) vec->aval |= ~mask; + if (vec->bval & (1 << (size - 1))) vec->bval |= ~mask; + } +} + +/* + * Fill the passed variable with x. + */ +static void fill_variable_with_x(vpiHandle var) +{ + s_vpi_value val; + PLI_INT32 words = ((vpi_get(vpiSize, var) - 1) / 32) + 1; + PLI_INT32 idx; + p_vpi_vecval val_ptr = (p_vpi_vecval) malloc(words*sizeof(s_vpi_vecval)); + + assert(val_ptr); + + /* Fill the vector with X. */ + for (idx = 0; idx < words; idx += 1) { + val_ptr[idx].aval = 0xffffffff; + val_ptr[idx].bval = 0xffffffff; + } + + /* Put the vector to the variable. */ + val.format = vpiVectorVal; + val.value.vector = val_ptr; + vpi_put_value(var, &val, 0, vpiNoDelay); + free(val_ptr); +} + +/* + * Fill the passed variable with the passed value if it fits. If it doesn't + * fit then set all bits to one and return that the value is too big instead + * of the normal OK. The value is a time and needs to be scaled to the + * calling module's timescale. + */ +static PLI_INT32 fill_variable_with_scaled_time(vpiHandle var, uint64_t time) +{ + s_vpi_value val; + PLI_INT32 size = vpi_get(vpiSize, var); + PLI_INT32 is_signed = vpi_get(vpiSigned, var); + PLI_INT32 words = ((size - 1) / 32) + 1; + uint64_t max_val = 0; + uint64_t scale = 1; + uint64_t frac; + PLI_INT32 rtn, idx, units, prec; + p_vpi_vecval val_ptr = (p_vpi_vecval) malloc(words*sizeof(s_vpi_vecval)); + + assert(val_ptr); + assert(size >= 32); + assert(words > 0); + + /* Scale the variable to match the calling module's timescale. */ + prec = vpi_get(vpiTimePrecision, 0); + units = vpi_get(vpiTimeUnit, vpi_handle(vpiModule, var)); + assert(units >= prec); + while (units > prec) { + scale *= 10; + units -= 1; + } + frac = time % scale; + time /= scale; + if ((scale > 1) && (frac >= scale/2)) time += 1; + + /* Find the maximum value + 1 that can be put into the variable. */ + if (size < 64) { + max_val = 1; + max_val <<= (size - is_signed); + } + + /* If the time is too big to fit then return the maximum positive + * value and that the value overflowed. Otherwise, return the time + * and OK. */ + if (max_val && (time >= max_val)) { + /* For a single word only the MSB is cleared if signed. */ + if (words == 1) { + if (is_signed) { + val_ptr[0].aval = 0x7fffffff; + } else { + val_ptr[0].aval = 0xffffffff; + } + val_ptr[0].bval = 0x00000000; + /* For two words the lower word is filled with 1 and the top + * word has a size dependent fill if signed. */ + } else { + assert(words == 2); + val_ptr[0].aval = 0xffffffff; + val_ptr[0].bval = 0x00000000; + if (is_signed) { + val_ptr[1].aval = ~(UINT32_MAX >> (size - 32)); + } else { + val_ptr[1].aval = 0xffffffff; + } + val_ptr[1].bval = 0x00000000; + } + rtn = IVL_QUEUE_VALUE_OVERFLOWED; + } else { + /* Fill the vector with 0. */ + for (idx = 0; idx < words; idx += 1) { + val_ptr[idx].aval = 0x00000000; + val_ptr[idx].bval = 0x00000000; + } + /* Add the time to the vector. */ + switch (words) { + default: + val_ptr[1].aval = (time >> 32) & 0xffffffff; + case 1: + val_ptr[0].aval = time & 0xffffffff; + } + rtn = IVL_QUEUE_OK; + } + + /* Put the vector to the variable. */ + val.format = vpiVectorVal; + val.value.vector = val_ptr; + vpi_put_value(var, &val, 0, vpiNoDelay); + free(val_ptr); + + return rtn; +} + +/* + * Check that the given $q_initialize() call has valid arguments. + */ +static PLI_INT32 sys_q_initialize_compiletf(PLI_BYTE8 *name) +{ + vpiHandle callh = vpi_handle(vpiSysTfCall, 0); + vpiHandle argv = vpi_iterate(vpiArgument, callh); + vpiHandle arg; + + /* Check that there are arguments. */ + if (argv == 0) { + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s requires four arguments.\n", name); + vpi_control(vpiFinish, 1); + return 0; + } + + /* Check that the first three arguments (the id, type and maximum + * length) are numeric. */ + if (check_numeric_args(argv, 3, callh, name)) return 0; + + /* The fourth argument (the status) must be a variable. */ + arg = vpi_scan(argv); + if (! arg) { + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s requires a fourth (variable) argument.\n", name); + vpi_control(vpiFinish, 1); + return 0; + } + /* Check that the status argument is a 32 bit variable. */ + check_var_arg_32(arg, callh, name, "fourth"); + + /* Make sure there are no extra arguments. */ + check_for_extra_args(argv, callh, name, "four arguments", 0); + + return 0; +} + +/* + * The runtime code for $q_initialize(). + */ +static PLI_INT32 sys_q_initialize_calltf(PLI_BYTE8 *name) +{ + vpiHandle callh = vpi_handle(vpiSysTfCall, 0); + vpiHandle argv = vpi_iterate(vpiArgument, callh); + vpiHandle status; + PLI_INT32 id, type, length; + s_vpi_value val; + unsigned invalid_id, invalid_type, invalid_length; + + /* Get the id. */ + invalid_id = get_valid_32(vpi_scan(argv), &id); + + /* Get the queue type. */ + invalid_type = get_valid_32(vpi_scan(argv), &type); + + /* Get the queue maximum length. */ + invalid_length = get_valid_32(vpi_scan(argv), &length); + + /* Get the status variable. */ + status = vpi_scan(argv); + + /* We are done with the argument iterator so free it. */ + vpi_free_object(argv); + + /* If the id is invalid then return. */ + if (invalid_id) { + val.format = vpiIntVal; + val.value.integer = IVL_QUEUE_UNDEFINED_ID; + vpi_put_value(status, &val, 0, vpiNoDelay); + return 0; + } + + /* Verify that the type is valid. */ + if (invalid_type || ((type != IVL_QUEUE_FIFO) && + (type != IVL_QUEUE_LIFO))) { + val.format = vpiIntVal; + val.value.integer = IVL_QUEUE_UNSUPPORTED_TYPE; + vpi_put_value(status, &val, 0, vpiNoDelay); + return 0; + } + + /* Verify that the queue length is greater than zero. */ + if (invalid_length || (length <= 0)) { + val.format = vpiIntVal; + val.value.integer = IVL_QUEUE_INVALID_LENGTH; + vpi_put_value(status, &val, 0, vpiNoDelay); + return 0; + } + + /* Check that this is not a duplicate queue id. */ + if (get_id_index(id) >= 0) { + val.format = vpiIntVal; + val.value.integer = IVL_QUEUE_DUPLICATE_ID; + vpi_put_value(status, &val, 0, vpiNoDelay); + return 0; + } + + /* Create the queue and fail if we do not have enough memory. */ + if (create_queue(id, type, length)) { + val.format = vpiIntVal; + val.value.integer = IVL_QUEUE_OUT_OF_MEMORY; + vpi_put_value(status, &val, 0, vpiNoDelay); + return 0; + } + + /* The queue was initialized correctly so return OK. */ + val.format = vpiIntVal; + val.value.integer = IVL_QUEUE_OK; + vpi_put_value(status, &val, 0, vpiNoDelay); + return 0; +} + +/* + * Check that the given $q_add() call has valid arguments. + */ +static PLI_INT32 sys_q_add_compiletf(PLI_BYTE8 *name) +{ + vpiHandle callh = vpi_handle(vpiSysTfCall, 0); + vpiHandle argv = vpi_iterate(vpiArgument, callh); + vpiHandle arg; + + /* Check that there are arguments. */ + if (argv == 0) { + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s requires four arguments.\n", name); + vpi_control(vpiFinish, 1); + return 0; + } + + /* Check that the first three arguments (the id, job and information) + * are numeric. */ + if (check_numeric_args(argv, 3, callh, name)) return 0; + + /* The fourth argument (the status) must be a variable. */ + arg = vpi_scan(argv); + if (! arg) { + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s requires a fourth (variable) argument.\n", name); + vpi_control(vpiFinish, 1); + return 0; + } + /* Check that the status argument is a 32 bit variable. */ + check_var_arg_32(arg, callh, name, "fourth"); + + /* Make sure there are no extra arguments. */ + check_for_extra_args(argv, callh, name, "four arguments", 0); + + return 0; +} + +/* + * The runtime code for $q_add(). + */ +static PLI_INT32 sys_q_add_calltf(PLI_BYTE8 *name) +{ + vpiHandle callh = vpi_handle(vpiSysTfCall, 0); + vpiHandle argv = vpi_iterate(vpiArgument, callh); + vpiHandle status; + PLI_INT32 id; + int64_t idx; + s_vpi_vecval job, inform; + s_vpi_value val; + unsigned invalid_id; + + /* Get the id. */ + invalid_id = get_valid_32(vpi_scan(argv), &id); + + /* Get the job. */ + get_four_state(vpi_scan(argv), &job); + + /* Get the value. */ + get_four_state(vpi_scan(argv), &inform); + + /* Get the status variable. */ + status = vpi_scan(argv); + + /* We are done with the argument iterator so free it. */ + vpi_free_object(argv); + + /* Verify that the id is valid. */ + idx = get_id_index(id); + if (invalid_id || (idx < 0)) { + val.format = vpiIntVal; + val.value.integer = IVL_QUEUE_UNDEFINED_ID; + vpi_put_value(status, &val, 0, vpiNoDelay); + return 0; + } + + /* Add the data to the queue if it is not already full. */ + if (add_to_queue(idx, &job, &inform)) { + val.format = vpiIntVal; + val.value.integer = IVL_QUEUE_FULL; + vpi_put_value(status, &val, 0, vpiNoDelay); + return 0; + } + + /* The data was added to the queue so return OK. */ + val.format = vpiIntVal; + val.value.integer = IVL_QUEUE_OK; + vpi_put_value(status, &val, 0, vpiNoDelay); + return 0; +} + +/* + * Check that the given $q_remove() call has valid arguments. + */ +static PLI_INT32 sys_q_remove_compiletf(PLI_BYTE8 *name) +{ + vpiHandle callh = vpi_handle(vpiSysTfCall, 0); + vpiHandle argv = vpi_iterate(vpiArgument, callh); + vpiHandle arg; + + /* Check that there are arguments. */ + if (argv == 0) { + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s requires four arguments.\n", name); + vpi_control(vpiFinish, 1); + return 0; + } + + /* The first argument (the id) must be numeric. */ + if (! is_32_or_smaller_obj(vpi_scan(argv))) { + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s's first argument must be numeric (<= 32 bits).\n", + name); + vpi_control(vpiFinish, 1); + } + + /* The second argument (the job id) must be a variable. */ + arg = vpi_scan(argv); + if (! arg) { + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s requires a second (variable) argument.\n", name); + vpi_control(vpiFinish, 1); + return 0; + } + /* Check that the job id argument is a 32 bit variable. */ + check_var_arg_32(arg, callh, name, "second"); + + /* The third argument (the information id) must be a variable. */ + arg = vpi_scan(argv); + if (! arg) { + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s requires a third (variable) argument.\n", name); + vpi_control(vpiFinish, 1); + return 0; + } + /* Check that the information id argument is a 32 bit variable. */ + check_var_arg_32(arg, callh, name, "third"); + + /* The fourth argument (the status) must be a variable. */ + arg = vpi_scan(argv); + if (! arg) { + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s requires a fourth (variable) argument.\n", name); + vpi_control(vpiFinish, 1); + return 0; + } + /* Check that the status argument is a 32 bit variable. */ + check_var_arg_32(arg, callh, name, "fourth"); + + /* Make sure there are no extra arguments. */ + check_for_extra_args(argv, callh, name, "four arguments", 0); + + return 0; +} + +/* + * The runtime code for $q_remove(). + */ +static PLI_INT32 sys_q_remove_calltf(PLI_BYTE8 *name) +{ + vpiHandle callh = vpi_handle(vpiSysTfCall, 0); + vpiHandle argv = vpi_iterate(vpiArgument, callh); + vpiHandle job, inform, status; + PLI_INT32 id, idx; + s_vpi_vecval job_val, inform_val; + s_vpi_value val; + unsigned invalid_id; + + /* Get the id. */ + invalid_id = get_valid_32(vpi_scan(argv), &id); + + /* Get the job variable. */ + job = vpi_scan(argv); + + /* Get the inform variable. */ + inform = vpi_scan(argv); + + /* Get the status variable. */ + status = vpi_scan(argv); + + /* We are done with the argument iterator so free it. */ + vpi_free_object(argv); + + /* Verify that the id is valid. */ + idx = get_id_index(id); + if (invalid_id || (idx < 0)) { + fill_variable_with_x(job); + fill_variable_with_x(inform); + val.format = vpiIntVal; + val.value.integer = IVL_QUEUE_UNDEFINED_ID; + vpi_put_value(status, &val, 0, vpiNoDelay); + return 0; + } + + /* Remove the data from the queue if it is not already empty. */ + if (remove_from_queue(idx, &job_val, &inform_val)) { + fill_variable_with_x(job); + fill_variable_with_x(inform); + val.format = vpiIntVal; + val.value.integer = IVL_QUEUE_EMPTY; + vpi_put_value(status, &val, 0, vpiNoDelay); + return 0; + } + + val.format = vpiVectorVal; + val.value.vector = &job_val; + vpi_put_value(job, &val, 0, vpiNoDelay); + val.format = vpiVectorVal; + val.value.vector = &inform_val; + vpi_put_value(inform, &val, 0, vpiNoDelay); + + /* The data was added to the queue so return OK. */ + val.format = vpiIntVal; + val.value.integer = IVL_QUEUE_OK; + vpi_put_value(status, &val, 0, vpiNoDelay); + return 0; +} + +/* + * Check that the given $q_full() call has valid arguments. + */ +static PLI_INT32 sys_q_full_compiletf(PLI_BYTE8 *name) +{ + vpiHandle callh = vpi_handle(vpiSysTfCall, 0); + vpiHandle argv = vpi_iterate(vpiArgument, callh); + vpiHandle arg; + + /* Check that there are arguments. */ + if (argv == 0) { + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s requires two arguments.\n", name); + vpi_control(vpiFinish, 1); + return 0; + } + + /* The first argument (the id) must be numeric. */ + if (! is_32_or_smaller_obj(vpi_scan(argv))) { + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s's first argument must be numeric (<= 32 bits).\n", + name); + vpi_control(vpiFinish, 1); + } + + /* The second argument (the status) must be a variable. */ + arg = vpi_scan(argv); + if (! arg) { + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s requires a second (variable) argument.\n", name); + vpi_control(vpiFinish, 1); + return 0; + } + /* Check that the status argument is a 32 bit variable. */ + check_var_arg_32(arg, callh, name, "second"); + + /* Make sure there are no extra arguments. */ + check_for_extra_args(argv, callh, name, "two arguments", 0); + + return 0; +} + +/* + * The runtime code for $q_full(). + */ +static PLI_INT32 sys_q_full_calltf(PLI_BYTE8 *name) +{ + vpiHandle callh = vpi_handle(vpiSysTfCall, 0); + vpiHandle argv = vpi_iterate(vpiArgument, callh); + vpiHandle status; + PLI_INT32 id, idx; + s_vpi_value val; + unsigned invalid_id; + + /* Get the id. */ + invalid_id = get_valid_32(vpi_scan(argv), &id); + + /* Get the status variable. */ + status = vpi_scan(argv); + + /* We are done with the argument iterator so free it. */ + vpi_free_object(argv); + + /* Verify that the id is valid. */ + idx = get_id_index(id); + if (invalid_id || (idx < 0)) { + val.format = vpiIntVal; + val.value.integer = IVL_QUEUE_UNDEFINED_ID; + vpi_put_value(status, &val, 0, vpiNoDelay); + val.format = vpiIntVal; + val.value.integer = 2; /* An error value. */ + vpi_put_value(callh, &val, 0, vpiNoDelay); + return 0; + } + + /* Get the queue state and return it. */ + val.format = vpiIntVal; + if (is_queue_full(idx)) val.value.integer = 1; + else val.value.integer = 0; + vpi_put_value(callh, &val, 0, vpiNoDelay); + + /* The queue state was passed back so return OK. */ + val.format = vpiIntVal; + val.value.integer = IVL_QUEUE_OK; + vpi_put_value(status, &val, 0, vpiNoDelay); + return 0; +} + +/* + * Check that the given $q_exam() call has valid arguments. + */ +static PLI_INT32 sys_q_exam_compiletf(PLI_BYTE8 *name) +{ + vpiHandle callh = vpi_handle(vpiSysTfCall, 0); + vpiHandle argv = vpi_iterate(vpiArgument, callh); + vpiHandle arg; + + /* Check that there are arguments. */ + if (argv == 0) { + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s requires four arguments.\n", name); + vpi_control(vpiFinish, 1); + return 0; + } + + /* Check that the first two arguments (the id and code) are numeric. */ + if (check_numeric_args(argv, 2, callh, name)) return 0; + + /* The third argument (the value) must be a variable. */ + arg = vpi_scan(argv); + if (! arg) { + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s requires a third (variable) argument.\n", name); + vpi_control(vpiFinish, 1); + return 0; + } + /* Check that the value argument is a variable with at least + * 32 bits. */ + check_var_arg_large(arg, callh, name, "third"); + + /* The fourth argument (the status) must be a variable. */ + arg = vpi_scan(argv); + if (! arg) { + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s requires a fourth (variable) argument.\n", name); + vpi_control(vpiFinish, 1); + return 0; + } + /* Check that the status argument is a 32 bit variable. */ + check_var_arg_32(arg, callh, name, "fourth"); + + /* Make sure there are no extra arguments. */ + check_for_extra_args(argv, callh, name, "two arguments", 0); + + return 0; +} + +/* + * The runtime code for $q_exam(). + */ +static PLI_INT32 sys_q_exam_calltf(PLI_BYTE8 *name) +{ + vpiHandle callh = vpi_handle(vpiSysTfCall, 0); + vpiHandle argv = vpi_iterate(vpiArgument, callh); + vpiHandle value, status; + PLI_INT32 id, code, idx, rtn; + s_vpi_value val; + unsigned invalid_id, invalid_code; + + /* Get the id. */ + invalid_id = get_valid_32(vpi_scan(argv), &id); + + /* Get the code. */ + invalid_code = get_valid_32(vpi_scan(argv), &code); + + /* Get the value variable. */ + value = vpi_scan(argv); + + /* Get the status variable. */ + status = vpi_scan(argv); + + /* We are done with the argument iterator so free it. */ + vpi_free_object(argv); + + /* Verify that the id is valid. */ + idx = get_id_index(id); + if (invalid_id || (idx < 0)) { + fill_variable_with_x(value); + val.format = vpiIntVal; + val.value.integer = IVL_QUEUE_UNDEFINED_ID; + vpi_put_value(status, &val, 0, vpiNoDelay); + return 0; + } + + /* Verify that the code is valid. */ + if (invalid_code || (code <= 0) || (code > 6)) { + fill_variable_with_x(value); + val.format = vpiIntVal; + val.value.integer = IVL_QUEUE_UNDEFINED_STAT_CODE; + vpi_put_value(status, &val, 0, vpiNoDelay); + return 0; + } + + rtn = IVL_QUEUE_OK; + + /* Calculate the requested queue information. */ + switch (code) { + /* The current queue length. */ + case IVL_QUEUE_LENGTH: + val.format = vpiIntVal; + val.value.integer = get_current_queue_length(idx); + vpi_put_value(value, &val, 0, vpiNoDelay); + break; + /* The mean inter-arrival time. */ + case IVL_QUEUE_MEAN: + if (have_interarrival_statistic(idx) == 0) { + fill_variable_with_x(value); + rtn = IVL_QUEUE_NO_STATISTICS; + } else { + uint64_t time = get_mean_interarrival_time(idx); + rtn = fill_variable_with_scaled_time(value, time); + } + break; + /* The maximum queue length. */ + case IVL_QUEUE_MAX_LENGTH: + val.format = vpiIntVal; + val.value.integer = get_maximum_queue_length(idx); + vpi_put_value(value, &val, 0, vpiNoDelay); + break; + /* The shortest queue wait time ever. */ + case IVL_QUEUE_SHORTEST: + if (have_shortest_wait_statistic(idx) == 0) { + fill_variable_with_x(value); + rtn = IVL_QUEUE_NO_STATISTICS; + } else { + uint64_t time = get_shortest_wait_time(idx); + rtn = fill_variable_with_scaled_time(value, time); + } + break; + /* The longest wait time for elements still in the queue. */ + case IVL_QUEUE_LONGEST: + if (get_current_queue_length(idx) == 0) { + fill_variable_with_x(value); + rtn = IVL_QUEUE_NO_STATISTICS; + } else { + uint64_t time = get_longest_queue_time(idx); + rtn = fill_variable_with_scaled_time(value, time); + } + break; + /* The average queue wait time. */ + case IVL_QUEUE_AVERAGE: + if (have_average_wait_statistic(idx) == 0) { + fill_variable_with_x(value); + rtn = IVL_QUEUE_NO_STATISTICS; + } else { + uint64_t time = get_average_wait_time(idx); + rtn = fill_variable_with_scaled_time(value, time); + } + break; + default: + assert(0); + } + + /* The queue information was passed back so now return the status. */ + val.format = vpiIntVal; + val.value.integer = rtn; + vpi_put_value(status, &val, 0, vpiNoDelay); + return 0; +} + +/* + * Routine to register the system tasks/functions provided in this file. + */ +void sys_queue_register() +{ + s_vpi_systf_data tf_data; + s_cb_data cb; + + tf_data.type = vpiSysTask; + tf_data.tfname = "$q_initialize"; + tf_data.calltf = sys_q_initialize_calltf; + tf_data.compiletf = sys_q_initialize_compiletf; + tf_data.sizetf = 0; + tf_data.user_data = "$q_initialize"; + vpi_register_systf(&tf_data); + + tf_data.type = vpiSysTask; + tf_data.tfname = "$q_add"; + tf_data.calltf = sys_q_add_calltf; + tf_data.compiletf = sys_q_add_compiletf; + tf_data.sizetf = 0; + tf_data.user_data = "$q_add"; + vpi_register_systf(&tf_data); + + tf_data.type = vpiSysTask; + tf_data.tfname = "$q_remove"; + tf_data.calltf = sys_q_remove_calltf; + tf_data.compiletf = sys_q_remove_compiletf; + tf_data.sizetf = 0; + tf_data.user_data = "$q_remove"; + vpi_register_systf(&tf_data); + + tf_data.type = vpiSysFunc; + tf_data.sysfunctype = vpiSysFuncInt; + tf_data.tfname = "$q_full"; + tf_data.calltf = sys_q_full_calltf; + tf_data.compiletf = sys_q_full_compiletf; + tf_data.sizetf = 0; /* Not needed for a vpiSysFuncInt. */ + tf_data.user_data = "$q_full"; + vpi_register_systf(&tf_data); + + tf_data.type = vpiSysTask; + tf_data.tfname = "$q_exam"; + tf_data.calltf = sys_q_exam_calltf; + tf_data.compiletf = sys_q_exam_compiletf; + tf_data.sizetf = 0; + tf_data.user_data = "$q_exam"; + vpi_register_systf(&tf_data); + + /* Create a callback to clear all the queue memory when the + * simulator finishes. */ + cb.time = NULL; + cb.reason = cbEndOfSimulation; + cb.cb_rtn = cleanup_queue; + cb.user_data = 0x0; + cb.obj = 0x0; + + vpi_register_cb(&cb); +} diff --git a/vpi/sys_table.c b/vpi/sys_table.c index f7b1ad2be..448600384 100644 --- a/vpi/sys_table.c +++ b/vpi/sys_table.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999-2010 Stephen Williams (steve@icarus.com) + * Copyright (c) 1999-2011 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 @@ -29,6 +29,7 @@ extern void sys_finish_register(); extern void sys_deposit_register(); extern void sys_display_register(); extern void sys_plusargs_register(); +extern void sys_queue_register(); extern void sys_random_register(); extern void sys_random_mti_register(); extern void sys_readmem_register(); @@ -198,6 +199,7 @@ void (*vlog_startup_routines[])() = { sys_deposit_register, sys_display_register, sys_plusargs_register, + sys_queue_register, sys_random_register, sys_random_mti_register, sys_readmem_register, diff --git a/vpi/system.sft b/vpi/system.sft index f0eb86f6a..b772dc85c 100644 --- a/vpi/system.sft +++ b/vpi/system.sft @@ -15,6 +15,7 @@ $dist_chi_square vpiSysFuncInt $dist_t vpiSysFuncInt $dist_erlang vpiSysFuncInt $clog2 vpiSysFuncInt +$q_full vpiSysFuncInt $abstime vpiSysFuncReal $simparam vpiSysFuncReal