Logo Search packages:      
Sourcecode: iverilog version File versions  Download package

v2005_math.c

/*
 *  Verilog-2005 math library for Icarus Verilog
 *  http://www.icarus.com/eda/verilog/
 *
 *  Copyright (C) 2007-2009  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 "vpi_config.h"
#include <assert.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <vpi_user.h>

/* Single argument functions. */
typedef struct s_single_data {
    const char *name;
    double (*func)(double);
} t_single_data;

static t_single_data va_single_data[]= {
    {"$sqrt",  sqrt},
    {"$ln",    log},
    {"$log10", log10},
    {"$exp",   exp},
    {"$ceil",  ceil},
    {"$floor", floor},
    {"$sin",   sin},
    {"$cos",   cos},
    {"$tan",   tan},
    {"$asin",  asin},
    {"$acos",  acos},
    {"$atan",  atan},
    {"$sinh",  sinh},
    {"$cosh",  cosh},
    {"$tanh",  tanh},
    {"$asinh", asinh},
    {"$acosh", acosh},
    {"$atanh", atanh},
    {0, 0}  /* Must be NULL terminated! */
};


/* Double argument functions. */
typedef struct s_double_data {
    const char *name;
    double (*func)(double, double);
} t_double_data;

static t_double_data va_double_data[]= {
    {"$pow",   pow},
    {"$atan2", atan2},
    {"$hypot", hypot},
    {0, 0}  /* Must be NULL terminated! */
};


/*
 * This structure holds the single argument information.
 */
typedef struct {
    vpiHandle arg;
    double (*func)(double);
} va_single_t;


/*
 * This structure holds the double argument information.
 */
typedef struct {
    vpiHandle arg1;
    vpiHandle arg2;
    double (*func)(double, double);
} va_double_t;


/*
 * Cleanup the allocated memory at the end of simulation.
 */
static va_single_t** single_funcs = 0;
static unsigned single_funcs_count = 0;
static va_double_t** double_funcs = 0;
static unsigned double_funcs_count = 0;

static PLI_INT32 sys_end_of_simulation(p_cb_data cb_data)
{
    unsigned idx;

    for (idx = 0; idx < single_funcs_count; idx += 1) {
        free(single_funcs[idx]);
    }
    free(single_funcs);
    single_funcs = 0;
    single_funcs_count = 0;

    for (idx = 0; idx < double_funcs_count; idx += 1) {
        free(double_funcs[idx]);
    }
    free(double_funcs);
    double_funcs = 0;
    double_funcs_count = 0;

    return 0;
}


/*
 * Standard error message routine. The format string must take one
 * string argument (the name of the function).
 */
static void va_error_message(vpiHandle callh, const char *format,
                             const char *name) {
    vpi_printf("%s:%d: error: ", vpi_get_str(vpiFile, callh),
               (int)vpi_get(vpiLineNo, callh));
    vpi_printf(format, name);
    vpi_control(vpiFinish, 1);
}


/*
 * Process an argument.
 */
vpiHandle va_process_argument(vpiHandle callh, const char *name,
                              vpiHandle arg, const char *post) {
    PLI_INT32 type;

    if (arg == NULL) return 0;
    type = vpi_get(vpiType, arg);
    /* Math function cannot do anything with a string. */
    if ((type == vpiConstant || type == vpiParameter) &&
        (vpi_get(vpiConstType, arg) == vpiStringConst)) {
        const char* basemsg = "%s cannot process strings";
        char* msg = malloc(strlen(basemsg)+strlen(post)+3);
        strcpy(msg, basemsg);
        strcat(msg, post);
        strcat(msg, ".\n");
        va_error_message(callh, msg, name);
        free(msg);
        return 0;
    }
    return arg;
}


/*
 * Routine to check all the single argument math functions.
 */
static PLI_INT32 va_single_argument_compiletf(PLI_BYTE8 *ud)
{
    vpiHandle callh, argv, arg;
    t_single_data *data;
    const char *name;
    va_single_t* fun_data;

    assert(ud != 0);
    callh = vpi_handle(vpiSysTfCall, 0);
    assert(callh != 0);
    argv = vpi_iterate(vpiArgument, callh);
    data = (t_single_data *) ud;
    name = data->name;

    fun_data = malloc(sizeof(va_single_t));

    /* Check that malloc gave use some memory. */
    if (fun_data == 0) {
        va_error_message(callh, "%s failed to allocate memory.\n", name);
        return 0;
    }

    /* Check that there are arguments. */
    if (argv == 0) {
        va_error_message(callh, "%s requires one argument.\n", name);
        return 0;
    }

    /* In Icarus if we have an argv we have at least one argument. */
    arg = vpi_scan(argv);
    fun_data->arg = va_process_argument(callh, name, arg, "");

    /* These functions only take one argument. */
    arg = vpi_scan(argv);
    if (arg != 0) {
        va_error_message(callh, "%s takes only one argument.\n", name);
    }

    /* Get the function that is to be used by the calltf routine. */
    fun_data->func = data->func;

    vpi_put_userdata(callh, fun_data);
    single_funcs_count += 1;
    single_funcs = (va_single_t **)realloc(single_funcs,
                   single_funcs_count*sizeof(va_single_t **));
    single_funcs[single_funcs_count-1] = fun_data;

    /* vpi_scan() returning 0 (NULL) has already freed argv. */
    return 0;
}


/*
 * Routine to implement the single argument math functions.
 */
static PLI_INT32 va_single_argument_calltf(PLI_BYTE8 *ud)
{
    vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
    s_vpi_value val;
    va_single_t* fun_data;

    (void) ud;  /* Not used! */

    /* Retrieve the function and argument data. */
    fun_data = vpi_get_userdata(callh);

    /* Calculate the result */
    val.format = vpiRealVal;
    vpi_get_value(fun_data->arg, &val);
    val.value.real = (fun_data->func)(val.value.real);

    /* Return the result */
    vpi_put_value(callh, &val, 0, vpiNoDelay);

    return 0;
}


/*
 * Routine to check all the double argument math functions.
 */
static PLI_INT32 va_double_argument_compiletf(PLI_BYTE8 *ud)
{
    vpiHandle callh, argv, arg;
    t_double_data *data;
    const char *name;
    va_double_t* fun_data;

    assert(ud != 0);
    callh = vpi_handle(vpiSysTfCall, 0);
    assert(callh != 0);
    argv = vpi_iterate(vpiArgument, callh);
    data = (t_double_data *) ud;
    name = data->name;

    fun_data = malloc(sizeof(va_double_t));

    /* Check that malloc gave use some memory. */
    if (fun_data == 0) {
        va_error_message(callh, "%s failed to allocate memory.\n", name);
        return 0;
    }

    /* Check that there are arguments. */
    if (argv == 0) {
        va_error_message(callh, "%s requires two arguments.\n", name);
        return 0;
    }

    /* In Icarus if we have an argv we have at least one argument. */
    arg = vpi_scan(argv);
    fun_data->arg1 = va_process_argument(callh, name, arg, " (arg1)");

    /* Check that there are at least two arguments. */
    arg = vpi_scan(argv);
    if (arg == 0) {
        va_error_message(callh, "%s requires two arguments.\n", name);
    }
    fun_data->arg2 = va_process_argument(callh, name, arg, " (arg2)");

    /* These functions only take two arguments. */
    arg = vpi_scan(argv);
    if (arg != 0) {
        va_error_message(callh, "%s takes only two arguments.\n", name);
    }

    /* Get the function that is to be used by the calltf routine. */
    fun_data->func = data->func;

    vpi_put_userdata(callh, fun_data);
    double_funcs_count += 1;
    double_funcs = (va_double_t **)realloc(double_funcs,
                   double_funcs_count*sizeof(va_double_t **));
    double_funcs[double_funcs_count-1] = fun_data;

    /* vpi_scan() returning 0 (NULL) has already freed argv. */
    return 0;
}


/*
 * Routine to implement the double argument math functions.
 */
static PLI_INT32 va_double_argument_calltf(PLI_BYTE8 *ud)
{
    vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
    s_vpi_value val;
    double first_arg;
    va_double_t* fun_data;

    (void) ud;  /* Not used! */

    /* Retrieve the function and argument data. */
    fun_data = vpi_get_userdata(callh);

    /* Calculate the result */
    val.format = vpiRealVal;
    vpi_get_value(fun_data->arg1, &val);
    first_arg = val.value.real;
    vpi_get_value(fun_data->arg2, &val);
    val.value.real = (fun_data->func)(first_arg, val.value.real);

    /* Return the result */
    vpi_put_value(callh, &val, 0, vpiNoDelay);

    return 0;
}


/*
 * Register all the functions with Verilog.
 */
static void sys_v2005_math_register(void)
{
    s_cb_data cb_data;
    s_vpi_systf_data tf_data;
    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];
        vpi_register_systf(&tf_data);
    }

    /* 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];
        vpi_register_systf(&tf_data);
    }

    /* 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);
}


/*
 * Hook to get Icarus Verilog to find the registration function.
 */
extern void sys_clog2_register();

void (*vlog_startup_routines[])(void) = {
    sys_v2005_math_register,
    sys_clog2_register,
    0
};

Generated by  Doxygen 1.6.0   Back to index