1458 lines
43 KiB
C
1458 lines
43 KiB
C
/*
|
|
* Copyright (C) 2011-2014 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 <assert.h>
|
|
#include <inttypes.h>
|
|
#include <stdlib.h>
|
|
#include "ivl_alloc.h"
|
|
|
|
/*
|
|
* 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 c_time)
|
|
{
|
|
uint64_t carry = 0U;
|
|
|
|
if ((UINT64_MAX - *low) < c_time) carry = 1U;
|
|
*low += c_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 {
|
|
unsigned carry = 0U;
|
|
/* 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) && !carry) {
|
|
/* If the MSB is set then we will have a carry. */
|
|
if (high > (UINT64_MAX >> 1)) carry = 1U;
|
|
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 c_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);
|
|
c_time = cur_time.high;
|
|
c_time <<= 32;
|
|
c_time |= cur_time.low;
|
|
base[idx].queue[loc].time = c_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 = c_time;
|
|
base[idx].latest_add_time = c_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 c_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);
|
|
c_time = cur_time.high;
|
|
c_time <<= 32;
|
|
c_time |= cur_time.low;
|
|
|
|
/* Set the shortest wait time if needed. */
|
|
assert(c_time >= base[idx].queue[loc].time);
|
|
c_time -= base[idx].queue[loc].time;
|
|
if (c_time < base[idx].shortest_wait_time) {
|
|
base[idx].shortest_wait_time = c_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),
|
|
c_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 c_time;
|
|
|
|
/* Get the current simulation time. */
|
|
cur_time.type = vpiSimTime;
|
|
vpi_get_time(NULL, &cur_time);
|
|
c_time = cur_time.high;
|
|
c_time <<= 32;
|
|
c_time |= cur_time.low;
|
|
|
|
/* Subtract the element with the longest time (the head) from the
|
|
* current time. */
|
|
assert(c_time >= base[idx].queue[base[idx].head].time);
|
|
c_time -= base[idx].queue[base[idx].head].time;
|
|
|
|
return c_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 c_time;
|
|
|
|
/* Get the current simulation time. */
|
|
cur_time.type = vpiSimTime;
|
|
vpi_get_time(NULL, &cur_time);
|
|
c_time = cur_time.high;
|
|
c_time <<= 32;
|
|
c_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) {
|
|
uint64_t add_time = base[idx].queue[loc].time;
|
|
assert(c_time >= add_time);
|
|
add_to_wait_time(&high, &low, c_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 vpiBitVar:
|
|
case vpiByteVar:
|
|
case vpiShortIntVar:
|
|
case vpiIntVar:
|
|
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 vpiBitVar:
|
|
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:
|
|
case vpiIntVar:
|
|
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 vpiBitVar:
|
|
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 vpiIntVar:
|
|
case vpiLongIntVar:
|
|
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) {
|
|
const char *loc = NULL;
|
|
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 c_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, 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 = c_time % scale;
|
|
c_time /= scale;
|
|
if ((scale > 1) && (frac >= scale/2)) c_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 && (c_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 (PLI_INT32 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 = (c_time >> 32) & 0xffffffff;
|
|
case 1:
|
|
val_ptr[0].aval = c_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(ICARUS_VPI_CONST 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(ICARUS_VPI_CONST 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(ICARUS_VPI_CONST 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(ICARUS_VPI_CONST 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(ICARUS_VPI_CONST 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(ICARUS_VPI_CONST 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(ICARUS_VPI_CONST 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(ICARUS_VPI_CONST 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);
|
|
fill_variable_with_x(callh);
|
|
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(ICARUS_VPI_CONST 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(ICARUS_VPI_CONST 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 ia_time = get_mean_interarrival_time(idx);
|
|
rtn = fill_variable_with_scaled_time(value, ia_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 sw_time = get_shortest_wait_time(idx);
|
|
rtn = fill_variable_with_scaled_time(value, sw_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 lq_time = get_longest_queue_time(idx);
|
|
rtn = fill_variable_with_scaled_time(value, lq_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 aw_time = get_average_wait_time(idx);
|
|
rtn = fill_variable_with_scaled_time(value, aw_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(void)
|
|
{
|
|
s_vpi_systf_data tf_data;
|
|
s_cb_data cb;
|
|
vpiHandle res;
|
|
|
|
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";
|
|
res = vpi_register_systf(&tf_data);
|
|
vpip_make_systf_system_defined(res);
|
|
|
|
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";
|
|
res = vpi_register_systf(&tf_data);
|
|
vpip_make_systf_system_defined(res);
|
|
|
|
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";
|
|
res = vpi_register_systf(&tf_data);
|
|
vpip_make_systf_system_defined(res);
|
|
|
|
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";
|
|
res = vpi_register_systf(&tf_data);
|
|
vpip_make_systf_system_defined(res);
|
|
|
|
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";
|
|
res = vpi_register_systf(&tf_data);
|
|
vpip_make_systf_system_defined(res);
|
|
|
|
/* 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);
|
|
}
|