Preliminary support for $table_model.

This patch adds preliminary support for implementing the $table_model()
function for Verilog-A. It is not currently functional, but the VPI
interface routines and data file parsing are mostly complete. The big
things left are building the data structures and adding the interpolation
and extrapolation routines.

We also need to fix vvp to identify constant strings/parameters in a
structural context.
This commit is contained in:
Cary R 2011-04-18 11:29:07 -07:00 committed by Stephen Williams
parent ccf771ec4b
commit c75d30455d
8 changed files with 625 additions and 1 deletions

4
.gitignore vendored
View File

@ -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

View File

@ -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)

View File

@ -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
};

View File

@ -20,3 +20,4 @@ $q_full vpiSysFuncInt
$abstime vpiSysFuncReal
$simparam vpiSysFuncReal
$simparam$str vpiSysFuncSized 1024 unsigned
$table_model vpiSysFuncReal

315
vpi/table_mod.c Normal file
View File

@ -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 <assert.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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);
}

31
vpi/table_mod.h Normal file
View File

@ -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 <stdio.h>
#include <vpi_user.h>
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

161
vpi/table_mod_lexor.lex Normal file
View File

@ -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 <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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
}

99
vpi/table_mod_parse.y Normal file
View File

@ -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 <stdarg.h>
#include <stdio.h>
#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> 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");
}