diff --git a/.gitignore b/.gitignore index 5ab32ccfe..abbb0c195 100644 --- a/.gitignore +++ b/.gitignore @@ -64,6 +64,10 @@ dep /vpi/sdf_parse.h /vpi/sdf_parse.output /vpi/sys_readmem_lex.c +/vpi/table_mod_lexor.c +/vpi/table_mod_parse.c +/vpi/table_mod_parse.h +/vpi/table_mod_parse.output /vvp/dump.* /vvp/lexor.cc diff --git a/vpi/Makefile.in b/vpi/Makefile.in index d2d5631c0..f7d9ad8ed 100644 --- a/vpi/Makefile.in +++ b/vpi/Makefile.in @@ -56,7 +56,8 @@ 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_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 + sdf_lexor.o sdf_parse.o stringheap.o vams_simparam.o \ + table_mod.o table_mod_lexor.o table_mod_parse.o OPP = vcd_priv2.o ifeq (@HAVE_LIBZ@,yes) @@ -82,6 +83,8 @@ check: all clean: rm -rf *.o sys_readmem_lex.c dep system.vpi rm -f sdf_lexor.c sdf_parse.c sdf_parse.output sdf_parse.h + rm -f table_mod_parse.c table_mod_parse.h table_mod_parse.output + rm -f table_mod_lexor.c rm -f va_math.vpi v2005_math.vpi v2009.vpi distclean: clean @@ -128,6 +131,14 @@ sdf_lexor.c: sdf_lexor.lex sdf_parse.c sdf_parse.h: $(srcdir)/sdf_parse.y $(YACC) --verbose -d -p sdf -o sdf_parse.c $(srcdir)/sdf_parse.y +table_mod_lexor.o: table_mod_lexor.c table_mod_parse.h + +table_mod_lexor.c: $(srcdir)/table_mod_lexor.lex + $(LEX) -t -Ptblmod $< > $@ + +table_mod_parse.c table_mod_parse.h: $(srcdir)/table_mod_parse.y + $(YACC) --verbose -d -p tblmod -o table_mod_parse.c $< + v2005_math.vpi: $M ../vvp/libvpi.a $(CC) @shared@ -o $@ $M -L../vvp $(LDFLAGS) -lvpi $(VA_MATH_VPI_LDFLAGS) diff --git a/vpi/sys_table.c b/vpi/sys_table.c index 9116d34c6..8a7dabffe 100644 --- a/vpi/sys_table.c +++ b/vpi/sys_table.c @@ -39,6 +39,7 @@ extern void sys_time_register(); extern void sys_vcd_register(); extern void sys_vcdoff_register(); extern void sys_special_register(); +extern void table_model_register(); extern void vams_simparam_register(); #ifdef HAVE_LIBZ @@ -208,6 +209,7 @@ void (*vlog_startup_routines[])() = { sys_lxt_or_vcd_register, sys_sdf_register, sys_special_register, + table_model_register, vams_simparam_register, 0 }; diff --git a/vpi/system.sft b/vpi/system.sft index b772dc85c..0b43b1c65 100644 --- a/vpi/system.sft +++ b/vpi/system.sft @@ -20,3 +20,4 @@ $q_full vpiSysFuncInt $abstime vpiSysFuncReal $simparam vpiSysFuncReal $simparam$str vpiSysFuncSized 1024 unsigned +$table_model vpiSysFuncReal diff --git a/vpi/table_mod.c b/vpi/table_mod.c new file mode 100644 index 000000000..6e86f232f --- /dev/null +++ b/vpi/table_mod.c @@ -0,0 +1,315 @@ +/* + * 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 +#include +#include +#include +#include +#include "sys_priv.h" +#include "table_mod.h" +#include "ivl_alloc.h" + +/* + * This structure is saved for each table model instance. + */ +typedef struct t_table_mod { + vpiHandle *indep; /* Independent variable arguments. */ + double *indep_val; /* Current independent variable values. */ + union { /* Data file or argument to get the data file. */ + char *name; + vpiHandle arg; + } file; + unsigned have_fname; /* Has the file name been allocated? */ + vpiHandle control; /* Control string argument. */ +// HERE need a pointer to the dependent data and the final table. + unsigned dims; /* The number of independent variables. */ +} s_table_mod, *p_table_mod; + +static p_table_mod *tables = 0; +static unsigned table_count = 0U; + +/* + * Routine to cleanup the table model data at the end of simulation. + */ +static PLI_INT32 cleanup_table_mod(p_cb_data cause) +{ + unsigned idx; + (void) cause; /* Unused argument. */ + + for (idx = 0; idx < table_count; idx += 1) { + free(tables[idx]->indep); + free(tables[idx]->indep_val); + if (tables[idx]->have_fname) free(tables[idx]->file.name); + free(tables[idx]); + } + free(tables); + tables = 0; + table_count = 0U; + + return 0; +} + +/* + * Create an empty table model object and add it to the list of table + * model objects. + */ +static p_table_mod create_table() +{ + /* Create an empty table model object. */ + p_table_mod obj = (p_table_mod) malloc(sizeof(s_table_mod)); + assert(obj); + + /* Add it to the list of tables. */ + table_count += 1; + tables = (p_table_mod *) realloc(tables, sizeof(p_table_mod)*table_count); + tables[table_count-1] = obj; + + /* Initialize and return the table object. */ + obj->indep = 0; + obj->indep_val = 0; + obj->have_fname = 0; + obj->control = 0; + obj->dims = 0; + return obj; +} + +/* + * Check to see if this is a constant string. It returns 1 if the argument + * is a constant string otherwise it returns 0. + */ +unsigned is_const_string_obj(vpiHandle arg) +{ + unsigned rtn = 0; + + assert(arg); + + switch (vpi_get(vpiType, arg)) { + case vpiConstant: + case vpiParameter: + /* This must be a constant string. */ + if (vpi_get(vpiConstType, arg) == vpiStringConst) rtn = 1; + } + + return rtn; +} + +/* + * Check that the given $table_model() call has valid arguments. + */ +static PLI_INT32 sys_table_model_compiletf(ICARUS_VPI_CONST PLI_BYTE8 *name) +{ + vpiHandle callh = vpi_handle(vpiSysTfCall, 0); + vpiHandle argv = vpi_iterate(vpiArgument, callh); + vpiHandle arg; + p_table_mod table = create_table(); + + /* 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 at least two arguments.\n", name); + vpi_control(vpiFinish, 1); + return 0; + } + + /* The first N (dimensions) arguments must be numeric. */ + for (arg = vpi_scan(argv); + arg && is_numeric_obj(arg); + arg = vpi_scan(argv)) { + table->dims += 1; + table->indep = (vpiHandle *)realloc(table->indep, + sizeof(vpiHandle)*table->dims); + table->indep[table->dims-1] = arg; + } + + /* We must have a data file. */ + if (arg == 0) { + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s requires a file name argument.\n", name); + vpi_control(vpiFinish, 1); + return 0; + } + + /* For now we only allow a constant string (file name). */ + if (! is_const_string_obj(arg)) { + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s file name argument must be a constant string.\n", + name); + vpi_control(vpiFinish, 1); + return 0; + } + table->file.arg = arg; + + /* There may be an optional constant string (control string). */ + arg = vpi_scan(argv); + if (arg) { + if (! is_const_string_obj(arg)) { + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s control string argument must be a constant " + "string.\n", name); + vpi_control(vpiFinish, 1); + return 0; + } + check_for_extra_args(argv, callh, name, "N numeric and 1 or 2 " + " constant string arguments", 0); + table->control = arg; + } + + /* Save the table data information. */ + vpi_put_userdata(callh, table); + + return 0; +} + +/* + * Initialize the table model data structure. + * + * The first time $table_model() is called we need to evaluate the file name + * and control strings. Then we must load the data from the file. + */ +static unsigned initialize_table_model(vpiHandle callh, const char *name, + p_table_mod table) +{ + FILE *fp; + + /* Get the table file name. */ + table->file.name = get_filename(callh, name, table->file.arg); + table->have_fname = 1; + if (table->file.name == 0) return 1U; + fprintf(stderr, "Building table \"%s\" with %u variables\n", + table->file.name, table->dims); + + /* Open the file. */ + fp = fopen(table->file.name, "r"); + if (fp == 0) { + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s() cannot open file \"%s\" for reading (%s).\n", + name, table->file.name, strerror(errno)); + return 1U; + } +// HERE: Need to process the control string. + + /* Parse the given file and produce the basic data structure. */ +// HERE: This is going to return a pointer to the data. +// We need to update the minimum cols when the control string has +// been processed. + if (! parse_table_model(fp, table->file.name, callh, table->dims+1)) { + return 1U; + } + + /* Close the file now that we have loaded all the data. */ + if (fclose(fp)) { + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s() cannot close file \"%s\" (%s).\n", + name, table->file.name, strerror(errno)); + return 1U; + } + + /* Allocate space for the current argument values. */ + table->indep_val = (double*) malloc(sizeof(double)*table->dims); + assert(table->indep_val); + + return 0U; +} + +static double eval_table_model(vpiHandle callh, p_table_mod table) +{ + unsigned idx; + fprintf(stderr, "Evaluating table \"%s\" with %u variables\n", + table->file.name, table->dims); + for (idx = 0; idx < table->dims; idx += 1) { + fprintf(stderr, " Arg %u: %#g\n", idx, table->indep_val[idx]); + } + + return 0.0; +} + +/* + * Runtime routine for the $table_model(). + */ +static PLI_INT32 sys_table_model_calltf(ICARUS_VPI_CONST PLI_BYTE8 *name) +{ + vpiHandle callh = vpi_handle(vpiSysTfCall, 0); + s_vpi_value val; + p_table_mod table; + unsigned idx; + double result; + + /* Retrieve the table data. */ + table = vpi_get_userdata(callh); + + /* If this is the first call then build the data structure. */ + if ((table->have_fname == 0) && + initialize_table_model(callh, name, table)) { + vpi_control(vpiFinish, 1); + return 0; + } + + /* Load the current argument values into the table structure. */ + for (idx = 0; idx < table->dims; idx += 1) { + val.format = vpiRealVal; + vpi_get_value(table->indep[idx], &val); + table->indep_val[idx] = val.value.real; + } + + /* Interpolate/extrapolate the data structure to find the value. */ + result = eval_table_model(callh, table); + + /* Return the calculated value. */ + val.format = vpiRealVal; + val.value.real = result; + vpi_put_value(callh, &val, 0, vpiNoDelay); + + return 0; +} + +/* + * Routine to register the system function provided in this file. + */ +void table_model_register() +{ + s_vpi_systf_data tf_data; + s_cb_data cb; + vpiHandle res; + + tf_data.type = vpiSysFunc; + tf_data.sysfunctype = vpiSysFuncReal; + tf_data.tfname = "$table_model"; + tf_data.calltf = sys_table_model_calltf; + tf_data.compiletf = sys_table_model_compiletf; + tf_data.sizetf = 0; /* Not needed for a vpiSysFuncReal. */ + tf_data.user_data = "$table_model"; + res = vpi_register_systf(&tf_data); + vpip_make_systf_system_defined(res); + + /* Create a callback to clear all the table model memory when the + * simulator finishes. */ + cb.time = NULL; + cb.reason = cbEndOfSimulation; + cb.cb_rtn = cleanup_table_mod; + cb.user_data = 0x0; + cb.obj = 0x0; + + vpi_register_cb(&cb); +} diff --git a/vpi/table_mod.h b/vpi/table_mod.h new file mode 100644 index 000000000..e56f8705c --- /dev/null +++ b/vpi/table_mod.h @@ -0,0 +1,31 @@ +#ifndef __table_mod_H +#define __table_mod_H +/* + * 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 +#include + +extern int parse_table_model(FILE *fp, const char *file_name, vpiHandle callh, + unsigned min_cols); + +extern int tblmodlex(); +extern void destroy_tblmod_lexor(); +extern void init_tblmod_lexor(FILE *fp); + +#endif diff --git a/vpi/table_mod_lexor.lex b/vpi/table_mod_lexor.lex new file mode 100644 index 000000000..6823c9aed --- /dev/null +++ b/vpi/table_mod_lexor.lex @@ -0,0 +1,161 @@ + +%option never-interactive +%option noinput +%option nounput +%option noyywrap + +%{ +/* + * 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 +#include +#include +#include +#include "table_mod.h" +#include "table_mod_parse.h" +#include "ivl_alloc.h" + +static double get_scaled_real(const char *yytext); +static double get_real(const char *yytext); + +#define yylval tblmodlval +%} + +SPACE [ \t] +EOL [\r\n]+ +SCALE [afpnumkKMGT] + +%% + + /* Skip comment lines. */ +"#".*{EOL} { + tblmodlloc.first_line += 1; + } + + /* Skip white space. */ +{SPACE} { ; } + +{EOL} { + tblmodlloc.first_line += 1; + return EOL; + } + + /* Recognize a real value with a Verilog-AMS scale. */ +[+-]?[0-9][0-9_]*(\.[0-9][0-9_]*)?{SCALE} { + yylval.real = get_scaled_real(yytext); + return REAL; + } + + /* Recognize and other numeric value. */ +[+-]?[0-9][0-9_]*(\.[0-9][0-9_]*)?([eE][+-]?[0-9][0-9_]*)? { + yylval.real = get_real(yytext); + return REAL; + } + +%% + +/* + * Convert a Verilog-AMS scaled real number to a real value. + */ +double get_scaled_real(const char *text) +{ + double result; + size_t len = strlen(text); + char *buf = malloc(len + 5); + const char *scale; + + /* Copy the number to the buffer. */ + strcpy(buf, text); + + /* Convert the scale character to exponential form. */ + switch (text[len-1]) { + case 'a': scale = "e-18"; break; /* atto */ + case 'f': scale = "e-15"; break; /* femto */ + case 'p': scale = "e-12"; break; /* pico */ + case 'n': scale = "e-9"; break; /* nano */ + case 'u': scale = "e-6"; break; /* micro */ + case 'm': scale = "e-3"; break; /* milli */ + case 'k': + case 'K': scale = "e3"; break; /* kilo */ + case 'M': scale = "e6"; break; /* mega */ + case 'G': scale = "e9"; break; /* giga */ + case 'T': scale = "e12"; break; /* tera */ + default: + assert(0); + break; + } + buf[len-1] = 0; + strcat(buf, scale); + + /* Convert the exponential string to a real value, */ + result = get_real(buf); + + free(buf); + return result; +} + +/* + * Convert a normal number to a real value. + */ +double get_real(const char *text) +{ + double result; + char *buf = malloc(strlen(text) + 1); + char *cptr = buf; + char *end; + unsigned idx; + + /* We must have a buffer to remove any '_' characters. */ + assert(buf); + + /* Remove any '_' characters from the string. */ + for (idx = 0; text[idx]; idx += 1) { + if (text[idx] == '_') continue; + *cptr = text[idx]; + cptr += 1; + } + *cptr = 0; + + /* Convert the buffer to a double value and check that the + * conversion was done successfully. */ + result = strtod(buf, &end); + assert(*end == 0); + + free(buf); + return result; +} + +void init_tblmod_lexor(FILE *fp) +{ + yyrestart(fp); +} + +/* + * Modern version of flex (>=2.5.9) can clean up the scanner data. + */ +void destroy_tblmod_lexor() +{ +# ifdef FLEX_SCANNER +# if YY_FLEX_MAJOR_VERSION >= 2 && YY_FLEX_MINOR_VERSION >= 5 +# if defined(YY_FLEX_SUBMINOR_VERSION) && YY_FLEX_SUBMINOR_VERSION >= 9 + yylex_destroy(); +# endif +# endif +# endif +} diff --git a/vpi/table_mod_parse.y b/vpi/table_mod_parse.y new file mode 100644 index 000000000..bfce73e3e --- /dev/null +++ b/vpi/table_mod_parse.y @@ -0,0 +1,99 @@ + +%{ +/* + * 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 +#include +#include "table_mod.h" + +static unsigned minimum_columns; +static unsigned number_of_columns = 0U; +static unsigned cur_columns; +static const char *in_file_name; + +extern int tblmodlex(void); +static void yyerror(const char *fmt, ...); + +%} + +%locations + +%union { + double real; +}; + +%token EOL +%token REAL + +%% + +table : point + | table point + +point : columns EOL + { + fprintf(stderr, "\n"); + if (number_of_columns) { + if (cur_columns != number_of_columns) { + yyerror("Found %u columns, expected %u.", + cur_columns, number_of_columns); + } + } else { + if (cur_columns < minimum_columns) { + yyerror("Found %u columns, need at least %u.", + cur_columns, minimum_columns); + } + number_of_columns = cur_columns; + } + } + +columns : REAL + { + fprintf(stderr, "%#g", $1); + cur_columns = 1U; + } + | columns REAL + { + fprintf(stderr, ", %#g", $2); + cur_columns += 1U; + } + +%% + +int parse_table_model(FILE *fp, const char *file_name, vpiHandle callh, + unsigned min_cols) +{ + minimum_columns = min_cols; + in_file_name = file_name; + init_tblmod_lexor(fp); + yyparse(); + destroy_tblmod_lexor(); +// HERE: Need to handle errors. + return 1; +} + +void yyerror(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + fprintf(stderr, "%s:%u: TABLE ERROR: ", in_file_name, yylloc.first_line); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + +}