From 0597b56ba23001f244d4d78c6ae7af7ac92bd8f5 Mon Sep 17 00:00:00 2001 From: Cary R Date: Mon, 2 May 2011 09:17:34 -0700 Subject: [PATCH] More $table_model code. This patch adds the ability to process the control string and emit some debug information. --- vpi/table_mod.c | 476 ++++++++++++++++++++++++++++++++++++++++-- vpi/table_mod.h | 2 +- vpi/table_mod_parse.y | 42 +++- 3 files changed, 486 insertions(+), 34 deletions(-) diff --git a/vpi/table_mod.c b/vpi/table_mod.c index 0d86db6d1..1a1d68a5f 100644 --- a/vpi/table_mod.c +++ b/vpi/table_mod.c @@ -19,12 +19,39 @@ #include "sys_priv.h" #include #include +#include #include #include #include #include "table_mod.h" #include "ivl_alloc.h" +/* + * Flag used to enable/disable debug output. + */ +static unsigned table_model_debug = 0; + +// HERE: Should these just be defines? +/* + * Define the allowed interpolation control values. + */ +typedef enum interp_type_e { + IVL_CLOSEST_POINT = 0, /* D */ + IVL_LINEAR_INTERP = 1, /* 1 - Default */ + IVL_QUADRATIC_INTERP = 2, /* 2 */ + IVL_CUBIC_INTERP = 3, /* 3 */ + IVL_IGNORE_COLUMN = 4 /* I */ +} interp_type_t; + +/* + * Define the allowed extrapolation control values. + */ +typedef enum extrap_type_e { + IVL_CONSTANT_EXTRAP = 0, /* C */ + IVL_LINEAR_EXTRAP = 1, /* L - Default */ + IVL_ERROR_EXTRAP = 2 /* E */ +} extrap_type_t; + /* * This structure is saved for each table model instance. */ @@ -35,14 +62,24 @@ typedef struct t_table_mod { char *name; vpiHandle arg; } file; - unsigned have_fname; /* Has the file name been allocated? */ - vpiHandle control; /* Control string argument. */ + union { /* Control string argument. */ + struct { + char *interp; + char *extrap_low; + char *extrap_high; + } info; + vpiHandle arg; + } control; // HERE need a pointer to the dependent data and the final table. unsigned dims; /* The number of independent variables. */ + unsigned fields; /* The number of control fields. */ + unsigned depend; /* Where the dependent column is located. */ + char have_fname; /* Has the file name been allocated? */ + char have_ctl; /* Has the file name been allocated? */ } s_table_mod, *p_table_mod; static p_table_mod *tables = 0; -static unsigned table_count = 0U; +static unsigned table_count = 0; /* * Routine to cleanup the table model data at the end of simulation. @@ -56,11 +93,16 @@ static PLI_INT32 cleanup_table_mod(p_cb_data cause) free(tables[idx]->indep); free(tables[idx]->indep_val); if (tables[idx]->have_fname) free(tables[idx]->file.name); + if (tables[idx]->have_ctl) { + free(tables[idx]->control.info.interp); + free(tables[idx]->control.info.extrap_low); + free(tables[idx]->control.info.extrap_high); + } free(tables[idx]); } free(tables); tables = 0; - table_count = 0U; + table_count = 0; return 0; } @@ -84,8 +126,11 @@ static p_table_mod create_table() obj->indep = 0; obj->indep_val = 0; obj->have_fname = 0; - obj->control = 0; + obj->have_ctl = 0; + obj->control.arg = 0; + obj->depend = 0; obj->dims = 0; + obj->fields = 0; return obj; } @@ -109,6 +154,29 @@ unsigned is_const_string_obj(vpiHandle arg) return rtn; } +/* + * Get any command line flags. For now we only have a debug flag. + */ +static void check_command_line_flags() +{ + struct t_vpi_vlog_info vlog_info; + unsigned idx; + static unsigned command_line_processed = 0; + + /* If we have already processed the arguments then just return. */ + if (command_line_processed) return; + + vpi_get_vlog_info(&vlog_info); + + for (idx = 0; idx < vlog_info.argc; idx += 1) { + if (strcmp(vlog_info.argv[idx], "-table-model-debug") == 0) { + table_model_debug = 1; + } + } + + command_line_processed = 1; +} + /* * Check that the given $table_model() call has valid arguments. */ @@ -119,6 +187,9 @@ static PLI_INT32 sys_table_model_compiletf(ICARUS_VPI_CONST PLI_BYTE8 *name) vpiHandle arg; p_table_mod table = create_table(); + /* See if there are any table model arguments. */ + check_command_line_flags(); + /* Check that there are arguments. */ if (argv == 0) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), @@ -171,7 +242,7 @@ static PLI_INT32 sys_table_model_compiletf(ICARUS_VPI_CONST PLI_BYTE8 *name) } check_for_extra_args(argv, callh, name, "N numeric and 1 or 2 " " constant string arguments", 0); - table->control = arg; + table->control.arg = arg; } /* Save the table data information. */ @@ -180,6 +251,339 @@ static PLI_INT32 sys_table_model_compiletf(ICARUS_VPI_CONST PLI_BYTE8 *name) return 0; } +/* + * Initialize the control string fields with the default value. + */ +static void initialize_control_fields_def(p_table_mod table) +{ + char *val; + unsigned width = table->dims; + + /* Set the default interpolation for each dimension. */ + val = (char *) malloc(width); + assert(val); + table->control.info.interp = memset(val, IVL_LINEAR_INTERP, width); + + /* Set the default low extrapolation for each dimension. */ + val = (char *) malloc(width); + assert(val); + table->control.info.extrap_low = memset(val, IVL_LINEAR_EXTRAP, width); + + /* Set the default low extrapolation for each dimension. */ + val = (char *) malloc(width); + assert(val); + table->control.info.extrap_high = memset(val, IVL_LINEAR_EXTRAP, width); + + /* By default the dependent data column is the first one after the + * independent data column(s). */ + table->depend = 1; + + /* Set the number of control fields. */ + table->fields = width; + + /* We have defined the interpolation/extrapolation fields. */ + table->have_ctl = 1; +} + +/* + * Parse the extrapolation control codes. + */ +static unsigned parse_extrap(vpiHandle callh, p_table_mod table, unsigned idx, + char **extrap, char *control) +{ + /* Advance to the first extrapolation code. */ + *extrap += 1; + + /* By default both the low and high extrapolation is set by the + * first extrapolation character. We'll override the high value + * later if there is a second character. */ + switch (**extrap) { + /* Clamp (no) extrapolation. */ + case 'C': + table->control.info.extrap_low[idx] = IVL_CONSTANT_EXTRAP; + table->control.info.extrap_high[idx] = IVL_CONSTANT_EXTRAP; + break; + + /* Linear extrapolation. */ + case 'L': + table->control.info.extrap_low[idx] = IVL_LINEAR_EXTRAP; + table->control.info.extrap_high[idx] = IVL_LINEAR_EXTRAP; + break; + + /* Error on extrapolation. */ + case 'E': + table->control.info.extrap_low[idx] = IVL_ERROR_EXTRAP; + table->control.info.extrap_high[idx] = IVL_ERROR_EXTRAP; + break; + + /* There are no extrapolation characters so use the default + * (linear extrapolation). */ + case 0: + case ',': + table->control.info.extrap_low[idx] = IVL_LINEAR_EXTRAP; + table->control.info.extrap_high[idx] = IVL_LINEAR_EXTRAP; + return 0; + + /* Anything else is an error. */ + default: + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("Unknown extrapolation code '%c' for dimension " + "%u: %s\n", **extrap, idx+1, control); + vpi_control(vpiFinish, 1); + return 1; + } + + /* Advance to the next (high) extrapolation code. */ + *extrap += 1; + + /* Override the high extrapolation value if needed. */ + switch (**extrap) { + /* Clamp (no) extrapolation. */ + case 'C': + table->control.info.extrap_high[idx] = IVL_CONSTANT_EXTRAP; + break; + + /* Linear extrapolation. */ + case 'L': + table->control.info.extrap_high[idx] = IVL_LINEAR_EXTRAP; + break; + + /* Error on extrapolation. */ + case 'E': + table->control.info.extrap_high[idx] = IVL_ERROR_EXTRAP; + break; + + /* There is no high extrapolation character so just return. */ + case 0: + case ',': + return 0; + + /* Anything else is an error. */ + default: + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("Unknown high extrapolation code '%c' for dimension " + "%u: %s\n", **extrap, idx+1, control); + vpi_control(vpiFinish, 1); + return 1; + } + + /* Advance to the next field. */ + *extrap += 1; + + return 0; +} + +/* + * Load the control string fields for the given string. Return 0 if there + * is a problem parsing the control string. + */ +static unsigned initialize_control_fields(vpiHandle callh, p_table_mod table, + char *control) +{ + unsigned width, idx; + char *cp; + + /* If we need to fail these must be defined to zero until memory + * has actually been allocated. */ + table->control.info.interp = 0; + table->control.info.extrap_low = 0; + table->control.info.extrap_high = 0; + + /* We have defined the interpolation/extrapolation fields. */ + table->have_ctl = 1; + + /* Look for the dependent data field. */ + cp = index(control, ';'); + if (cp) { + size_t len; + unsigned long val; + char *end; + /* NULL terminate the independent part of the string. */ + *cp = 0; + cp += 1; + len = strlen(cp); + /* If the length is zero or not all digits then the string + * is invalid. */ +// HERE: Do we need to support/ignore an underscore? What about in the file? + if ((! len) || (len != strspn(cp, "0123456789"))) { + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("Control string dependent selector is " + "invalid: %s.\n", cp); + vpi_control(vpiFinish, 1); + return 0; + } + val = strtoul(cp, &end, 10); + assert(*end == 0); + /* If the value is too big also report an error. */ + if (val > UINT_MAX) { + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("Control string dependent value is " + "to large: %lu.\n", val); + vpi_control(vpiFinish, 1); + return 0; + } + table->depend = (unsigned) val; + } else table->depend = 1; + + /* Find the number of interpolation/extrapolation control fields. */ + width = 1; + cp = control; + while ((cp = index(cp, ','))) { + cp += 1; + width += 1; + } + /* We must have at least as many control fields as dimensions. */ + if (width < table->dims) { + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("Not enough control field(s) (%u) for the dimension(s) " + "(%u).", width, table->dims); + vpi_control(vpiFinish, 1); + return 0; + } + + /* Allocate space for the interpolation/extrapolation values. */ + table->control.info.interp = (char *) malloc(width); + assert(table->control.info.interp); + table->control.info.extrap_low = (char *) malloc(width); + assert(table->control.info.extrap_low); + table->control.info.extrap_high = (char *) malloc(width); + assert(table->control.info.extrap_high); + + /* Parse the individual dimension control string. */ + cp = control; + for (idx = 0; idx < width; idx += 1) { + switch (*cp) { + /* Closest point interpolation. */ + case 'D': + table->control.info.interp[idx] = IVL_CLOSEST_POINT; + if (parse_extrap(callh, table, idx, &cp, control)) return 0; + break; + + /* Linear interpolation. */ + case '1': + table->control.info.interp[idx] = IVL_LINEAR_INTERP; + if (parse_extrap(callh, table, idx, &cp, control)) return 0; + break; + + /* Quadratic interpolation. */ + case '2': + table->control.info.interp[idx] = IVL_QUADRATIC_INTERP; + if (parse_extrap(callh, table, idx, &cp, control)) return 0; + break; + + /* Cubic interpolation. */ + case '3': + table->control.info.interp[idx] = IVL_CUBIC_INTERP; + if (parse_extrap(callh, table, idx, &cp, control)) return 0; + break; + + /* Ignore column. No extrapolation codes are allowed. */ + case 'I': + table->control.info.interp[idx] = IVL_IGNORE_COLUMN; + break; + + /* This field is not specified so use the default values. + * Linear interpolation and extrapolation. */ + case 0: + assert(idx == width-1); + case ',': + table->control.info.interp[idx] = IVL_LINEAR_INTERP; + table->control.info.extrap_low[idx] = IVL_LINEAR_EXTRAP; + table->control.info.extrap_high[idx] = IVL_LINEAR_EXTRAP; + break; + + /* Anything else is an error. */ + default: + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("Unknown interpolation code '%c' for dimension " + "%u: %s\n", *cp, idx+1, control); + vpi_control(vpiFinish, 1); + return 0; + } + + /* Verify that there is no extra junk at the end of the field. */ + if (*cp && (*cp != ',')) { + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("Extra control characters found for dimension " + "%u: %s\n", idx+1, control); + vpi_control(vpiFinish, 1); + return 0; + } + + /* Advance to the next field if we are not at the end of the + * control string. */ + if (*cp == ',') cp += 1; + } + + /* Return the number of fields plus the dependent offset as the + * minimum number of fields. */ + return (width + table->depend); +} + +/* + * Print out the interpolation code. + */ +static void dump_interp(char interp) +{ + switch (interp) { + case IVL_CLOSEST_POINT: fprintf(stderr, "D"); break; + case IVL_LINEAR_INTERP: fprintf(stderr, "1"); break; + case IVL_QUADRATIC_INTERP: fprintf(stderr, "2"); break; + case IVL_CUBIC_INTERP: fprintf(stderr, "3"); break; + case IVL_IGNORE_COLUMN: fprintf(stderr, "I"); break; + default: fprintf(stderr, "<%d>", interp); break; + } +} + +/* + * Print out the extrapolation code. + */ +static void dump_extrap(char extrap) +{ + switch (extrap) { + case IVL_CONSTANT_EXTRAP: fprintf(stderr, "C"); break; + case IVL_LINEAR_EXTRAP: fprintf(stderr, "L"); break; + case IVL_ERROR_EXTRAP: fprintf(stderr, "E"); break; + default: fprintf(stderr, "<%d>", extrap); break; + } +} + +/* + * Print some diagnostic information about the $table_model() call. + */ +static void dump_diag_info(vpiHandle callh, p_table_mod table) +{ + unsigned idx; + char msg[64]; + + snprintf(msg, sizeof(msg), "%s:%d: DEBUG:", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + msg[sizeof(msg)-1] = 0; + fprintf(stderr, "%s Building %u variable table using \"%s\" and\n", + msg, table->dims, table->file.name); + fprintf(stderr, "%*s the following control string: ", + (int) strlen(msg), " "); + dump_interp(table->control.info.interp[0]); + dump_extrap(table->control.info.extrap_low[0]); + dump_extrap(table->control.info.extrap_high[0]); + for (idx = 1; idx < table->fields; idx += 1) { + fprintf(stderr, ","); + dump_interp(table->control.info.interp[idx]); + dump_extrap(table->control.info.extrap_low[idx]); + dump_extrap(table->control.info.extrap_high[idx]); + } + fprintf(stderr, ";%d\n", table->depend); + fprintf(stderr, "%*s The data file must have at least %u columns.\n", + (int) strlen(msg), " ", table->fields+table->depend); +} + /* * Initialize the table model data structure. * @@ -189,14 +593,13 @@ static PLI_INT32 sys_table_model_compiletf(ICARUS_VPI_CONST PLI_BYTE8 *name) static unsigned initialize_table_model(vpiHandle callh, const char *name, p_table_mod table) { + s_vpi_value val; 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); + if (table->file.name == 0) return 1; /* Open the file. */ fp = fopen(table->file.name, "r"); @@ -205,41 +608,70 @@ static unsigned initialize_table_model(vpiHandle callh, const char *name, (int)vpi_get(vpiLineNo, callh)); vpi_printf("%s() cannot open file \"%s\" for reading (%s).\n", name, table->file.name, strerror(errno)); - return 1U; + return 1; } -// 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; + /* Check to see if there is a control string. */ + if (table->control.arg) { + val.format = vpiStringVal; + vpi_get_value(table->control.arg, &val); + if (*(val.value.str) == 0) { + /* If there is an empty control string field then we just + * use the default values for interpolation/extrapolation. + * We also assume that there only dimensions + 1 fields. */ + initialize_control_fields_def(table); + } else { + if (initialize_control_fields(callh, table, val.value.str)) { + return 1; + } + } + } else { + /* If there is no control string field then we just use the + * default values for interpolation/extrapolation. We also + * assume that there only dimensions + 1 fields. */ + initialize_control_fields_def(table); } + /* If requested dump some diagnostic information. */ + if (table_model_debug) dump_diag_info(callh, table); + + /* Parse the given file and produce the basic data structure. We + * need to have columns for each control string field and for the + * dependent data. */ +// HERE: This is going to return a pointer to the data. + if (! parse_table_model(fp, table->file.name, callh, + table->fields, table->depend)) { + return 1; + } +// HERE: once we are done with the control string should we build a table +// of function pointer for the interp/extrap? + /* 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; + return 1; } /* Allocate space for the current argument values. */ table->indep_val = (double*) malloc(sizeof(double)*table->dims); assert(table->indep_val); - return 0U; + return 0; } +/* + * Routine to evalute the table model using the current input values. + */ 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); +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]); +fprintf(stderr, " Arg %u: %#g\n", idx, table->indep_val[idx]); } return 0.0; diff --git a/vpi/table_mod.h b/vpi/table_mod.h index e56f8705c..3a92f6088 100644 --- a/vpi/table_mod.h +++ b/vpi/table_mod.h @@ -22,7 +22,7 @@ #include extern int parse_table_model(FILE *fp, const char *file_name, vpiHandle callh, - unsigned min_cols); + unsigned indep__cols, unsigned dep_col); extern int tblmodlex(); extern void destroy_tblmod_lexor(); diff --git a/vpi/table_mod_parse.y b/vpi/table_mod_parse.y index bfce73e3e..ecd5d3deb 100644 --- a/vpi/table_mod_parse.y +++ b/vpi/table_mod_parse.y @@ -18,13 +18,18 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include #include #include #include "table_mod.h" +#include "ivl_alloc.h" static unsigned minimum_columns; -static unsigned number_of_columns = 0U; +static unsigned indep_columns; +static unsigned dep_column; +static unsigned number_of_columns = 0; static unsigned cur_columns; +static double *values; static const char *in_file_name; extern int tblmodlex(void); @@ -48,9 +53,17 @@ table : point point : columns EOL { - fprintf(stderr, "\n"); + unsigned idx; + fprintf(stderr, "%#g", values[0]); + for (idx = 1; idx < indep_columns; idx += 1) { + fprintf(stderr, ", %#g", values[idx]); + } + fprintf(stderr, " => %#g\n", values[indep_columns]); if (number_of_columns) { - if (cur_columns != number_of_columns) { + if (cur_columns < minimum_columns) { + yyerror("Found %u columns, need at least %u.", + cur_columns, minimum_columns); + } else if (cur_columns != number_of_columns) { yyerror("Found %u columns, expected %u.", cur_columns, number_of_columns); } @@ -58,32 +71,39 @@ point : columns EOL if (cur_columns < minimum_columns) { yyerror("Found %u columns, need at least %u.", cur_columns, minimum_columns); - } - number_of_columns = cur_columns; + } else number_of_columns = cur_columns; } } columns : REAL { - fprintf(stderr, "%#g", $1); - cur_columns = 1U; + values[0] = $1; + cur_columns = 1; } | columns REAL { - fprintf(stderr, ", %#g", $2); - cur_columns += 1U; + if (cur_columns < indep_columns) values[cur_columns] = $2; + else if (cur_columns == dep_column) values[indep_columns] = $2; + cur_columns += 1; } %% int parse_table_model(FILE *fp, const char *file_name, vpiHandle callh, - unsigned min_cols) + unsigned indep_cols, unsigned dep_col) { - minimum_columns = min_cols; + assert(indep_cols > 1); + assert(dep_col > 0); + indep_columns = indep_cols; + minimum_columns = indep_cols + dep_col; + dep_column = minimum_columns - 1; + values = malloc(sizeof(double)*(indep_cols+1)); + assert(values); in_file_name = file_name; init_tblmod_lexor(fp); yyparse(); destroy_tblmod_lexor(); + free(values); // HERE: Need to handle errors. return 1; }