Support use of vector defaults for XSPICE vector parameters.

This answers Feature Request 60 - "Array as default value for parameter".
This commit is contained in:
Giles Atkinson 2023-09-10 16:13:14 +01:00 committed by Holger Vogt
parent f37a292c82
commit 7ff4ad0b32
7 changed files with 204 additions and 84 deletions

View File

@ -101,8 +101,8 @@ struct Mif_Param_Info {
char *name; /* Name of this parameter */
char *description; /* Description of this parameter */
Mif_Data_Type_t type; /* Is this a real, boolean, string, ... */
Mif_Boolean_t has_default; /* True if there is a default value */
Mif_Parse_Value_t default_value; /* The default value */
int default_value_siz;/* Size of default_values array. */
Mif_Parse_Value_t *default_values; /* The default values (array). */
Mif_Boolean_t has_lower_limit; /* True if there is a lower limit */
Mif_Parse_Value_t lower_limit; /* The lower limit for this parameter */
Mif_Boolean_t has_upper_limit; /* True if there is a upper limit */

View File

@ -208,8 +208,8 @@ typedef struct {
char *name; /* Name of this parameter */
char *description; /* Description of this parameter */
Data_Type_t type; /* Data type, e.g. REAL, INTEGER, ... */
bool has_default; /* True if there is a default value */
Value_t default_value; /* The default value */
int default_value_idx;/* Start of default_values.. */
int default_value_cnt;/* Number of default_values.. */
bool has_lower_limit; /* True if there is a lower limit */
Value_t lower_limit; /* The lower limit for this parameter */
bool has_upper_limit; /* True if there is a upper limit */
@ -239,7 +239,14 @@ typedef struct {
} Inst_Var_Info_t;
/* Structure used when parsing default values from the IFS file, used below. */
typedef struct {
bool has_value;
bool advance; // Used for vector default values.
Data_Type_t kind;
Value_t u; // Union holding the value.
} My_Value_t;
/*
* The all encompassing structure for the ifs table information
@ -254,7 +261,8 @@ typedef struct {
Param_Info_t *param; /* Array of parameter info structs */
int num_inst_var; /* Number of entries in the instance var table(s) */
Inst_Var_Info_t *inst_var; /* Array of instance variable info structs */
int num_default_values;
My_Value_t *defaults_var; /* Array of annotated values. */
} Ifs_Table_t;

View File

@ -115,16 +115,14 @@ Ifs_Table_t *parser_ifs_table;
int ifs_num_errors;
static size_t alloced_size [4];
/* Allocated and initial sizes of tables for parse results. */
/*
* !!!!! Make sure these are large enough so that they never get realloced
* !!!!! since that will cause garbage uninitialized data...
* !!!!! (FIX THIS!)
*/
#define DEFAULT_SIZE_CONN 100
#define DEFAULT_SIZE_PARAM 100
#define DEFAULT_SIZE_INST_VAR 100
static size_t alloced_size [5];
#define DEFAULT_SIZE_CONN 10
#define DEFAULT_SIZE_PARAM 10
#define DEFAULT_SIZE_INST_VAR 10
#define DEFAULT_SIZE_DEFAULTS 100
#define GROW_SIZE 10
typedef enum {
@ -132,6 +130,7 @@ typedef enum {
TBL_PORT,
TBL_PARAMETER,
TBL_STATIC_VAR,
TBL_DEFAULTS,
} Table_t;
typedef struct {
@ -368,15 +367,19 @@ assign_limits (Data_Type_t type, Param_Info_t *param, Range_t range)
}
/*---------------------------------------------------------------------------*/
/* Advance the item variable, checking for table overflow. */
static void
check_item_num (void)
{
item++;
if (item-item_offset >= ITEM_BUFFER_SIZE) {
fatal ("Too many items in table - split into sub-tables");
fatal("Too many items in table - split into sub-tables");
}
if (item > (int) alloced_size [context.table] ) {
if (item >= (int) alloced_size [context.table] ) {
switch (context.table) {
case TBL_NAME:
case TBL_NAME: // Fixed size.
case TBL_DEFAULTS: // Handled in store_default_value().
break;
case TBL_PORT:
alloced_size[context.table] += GROW_SIZE;
@ -407,7 +410,6 @@ check_item_num (void)
break;
}
}
item++;
}
/*---------------------------------------------------------------------------*/
@ -429,6 +431,7 @@ check_end_item_num (void)
num_items_fixed = true;
switch (context.table) {
case TBL_NAME:
case TBL_DEFAULTS:
break;
case TBL_PORT:
TBL->num_conn = num_items;
@ -445,11 +448,38 @@ check_end_item_num (void)
}
#define INIT(n) item = (n); item_offset = (n); num_items = (n); num_items_fixed = false
#define ITEM check_item_num()
#define END check_end_item_num()
#define ITEM check_item_num() // Advances the item_buffer index.
#define END check_end_item_num() // Check for excessive values in later row.
/* Store a default value for a parameter. */
static void store_default_value(My_Value_t *vp)
{
if (TBL->num_default_values >= alloced_size[TBL_DEFAULTS]) {
/* Expand table. */
alloced_size [TBL_DEFAULTS] += GROW_SIZE * 5;
TBL->defaults_var = (My_Value_t *) realloc(TBL->defaults_var,
alloced_size[TBL_DEFAULTS] * sizeof (My_Value_t));
if (!TBL->defaults_var) {
fatal ("Error allocating memory for default parameter values");
}
}
TBL->defaults_var[TBL->num_default_values++] = *vp;
if (vp->advance)
ITEM; // Count another parameter sub-table.
}
%}
/* The parser perfoms a double scan for each row (labelled line), with the
* row spanning multiple sub-tables (individual connection definitions etc.).
* Values and other information are copied into the item_buffer[]
* array as they are recognised and then moved to their destination stucture
* TBL->xxxx[index] when the whole row has been parsed. Default parameter
* values are hndled differently and copied directly to TBL->defaults_var.
*/
%token TOK_ALLOWED_TYPES
%token TOK_ARRAY
%token TOK_ARRAY_BOUNDS
@ -501,6 +531,10 @@ check_end_item_num (void)
%token TOK_SPICE_MODEL_NAME
%token TOK_STRING_LITERAL
/* This declares the type of Bison's "semantic values" (the value associated
* with an instance of a symbol of the grammer, $$), defining YYSTYPE.
*/
%union {
Ctype_List_t *ctype_list;
Dir_t dir;
@ -549,6 +583,7 @@ YYSTYPE item_buffer [ITEM_BUFFER_SIZE];
ifs_file : {TBL->num_conn = 0;
TBL->num_param = 0;
TBL->num_inst_var = 0;
TBL->num_default_values = 0;
saw_function_name = false;
saw_model_name = false;
@ -556,14 +591,18 @@ ifs_file : {TBL->num_conn = 0;
alloced_size [TBL_PORT] = DEFAULT_SIZE_CONN;
alloced_size [TBL_PARAMETER] = DEFAULT_SIZE_PARAM;
alloced_size [TBL_STATIC_VAR] = DEFAULT_SIZE_INST_VAR;
alloced_size [TBL_DEFAULTS] = DEFAULT_SIZE_DEFAULTS;
TBL->conn = (Conn_Info_t*)
calloc(DEFAULT_SIZE_CONN, sizeof (Conn_Info_t));
TBL->param = (Param_Info_t*)
calloc (DEFAULT_SIZE_PARAM, sizeof (Param_Info_t));
calloc (DEFAULT_SIZE_PARAM, sizeof (Param_Info_t));
TBL->inst_var = (Inst_Var_Info_t*)
calloc (DEFAULT_SIZE_INST_VAR, sizeof (Inst_Var_Info_t));
if (! (TBL->conn && TBL->param && TBL->inst_var) ) {
calloc (DEFAULT_SIZE_INST_VAR, sizeof (Inst_Var_Info_t));
TBL->defaults_var = (My_Value_t *)
calloc(DEFAULT_SIZE_DEFAULTS, sizeof (My_Value_t));
if (! (TBL->conn && TBL->param &&
TBL->inst_var && TBL->defaults_var) ) {
fatal ("Could not allocate enough memory");
}
}
@ -701,18 +740,8 @@ parameter_table_item : TOK_PARAMETER_NAME list_of_ids
check_dtype_not_pointer (ITEM_BUF(i).dtype);
TBL->param[i].type = ITEM_BUF(i).dtype;
}}
| TOK_DEFAULT_VALUE list_of_values
{int i;
END;
FOR_ITEM (i) {
TBL->param[i].has_default =
ITEM_BUF(i).value.has_value;
if (TBL->param[i].has_default) {
assign_value (TBL->param[i].type,
&TBL->param[i].default_value,
ITEM_BUF(i).value);
}
}}
| TOK_DEFAULT_VALUE list_of_default_values
{END; } // Check against current sub-table count.
| TOK_LIMITS list_of_ranges
{int i;
END;
@ -870,15 +899,22 @@ number_or_dash : TOK_DASH {$$.has_bound = false;}
$$.bound = $1;}
;
list_of_values : /* empty */
| list_of_values value_or_dash {ITEM; BUF.value = $2;}
list_of_pure_values : value { $1.advance = 1; store_default_value(&$1); }
| list_of_pure_values value
{ $2.advance = 0; store_default_value(&$2); }
;
list_of_default_values : /* empty */
| list_of_default_values value_or_dash
{ $2.advance = 1; store_default_value(&$2); }
| list_of_default_values
TOK_LBRACKET list_of_pure_values TOK_RBRACKET
value_or_dash : TOK_DASH {$$.has_value = false;}
| value
;
value : string {$$.has_value = true;
value : string {$$.has_value = true;
$$.kind = CMPP_STRING;
$$.u.svalue = $1;}
| btype {$$.has_value = true;

View File

@ -39,18 +39,6 @@ NON-STANDARD FEATURES
#include "cmpp.h"
typedef struct {
bool has_value;
Data_Type_t kind;
union {
bool bvalue;
int ivalue;
double rvalue;
Complex_t cvalue;
char *svalue;
} u;
} My_Value_t;
typedef struct {
bool has_bound;
My_Value_t bound;

View File

@ -42,6 +42,10 @@ NON-STANDARD FEATURES
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#ifndef _MSC_VER
#include <unistd.h>
#endif
#include "cmpp.h"
/* Local function prototypes */
@ -183,6 +187,8 @@ EXITPOINT:
filename, strerror(errno));
xrc = -1;
}
if (xrc < 0 && filename)
unlink(filename); // So "make" will not see it.
}
if (filename != (char *) NULL) {
@ -680,29 +686,91 @@ static int write_param_info(
FILE *fp, /* File to write to */
Ifs_Table_t *ifs_table) /* Table of Interface Specification data */
{
int xrc = 0;
int i;
const char *str;
Param_Info_t * const param = ifs_table->param;
const int num_param = ifs_table->num_param;
int rc, xrc = 0;
int i, dv_idx;
const char *str;
/* Only write the paramTable if there is something to put in it. */
/* Otherwise, we will put NULL in the SPICEdev structure in its slot */
if (ifs_table->num_param == 0) {
if (num_param <= 0) {
return 0;
}
/* Write the default values for each parameter. */
/* Write the structure beginning */
int rc = 0;
for (i = 0, dv_idx = 0, rc = 0; i < num_param; i++) {
Param_Info_t * p_param_cur = param + i;
My_Value_t * p_val = ifs_table->defaults_var + dv_idx;
int start_index = dv_idx;
if (dv_idx >= ifs_table->num_default_values) {
fprintf(stderr, "Not enough default values for parameter: %s.\n",
p_param_cur->name);
return 1;
}
if (!p_val->has_value) {
/* Dummy entry, no default values. */
++dv_idx;
p_param_cur->default_value_cnt = 0;
continue;
}
rc |= fprintf(fp, "static struct Mif_Parse_Value %s_default[] = {\n",
p_param_cur->name);
do { // Write the default values for this parameter.
if (!p_val->advance && !p_param_cur->is_array) {
fprintf(stderr,
"Vector initialisation of default values "
"for non-vector parameter: %s.\n",
p_param_cur->name);
return 1;
}
if (p_val->kind != p_param_cur->type) {
if (p_val->kind == CMPP_INTEGER &&
p_param_cur->type == CMPP_REAL) {
/* Promote it. */
p_val->u.rvalue = p_val->u.ivalue;
} else {
fprintf(stderr,
"Data type of default value does not match for "
"parameter: %s.\n",
p_param_cur->name);
return 1;
}
}
str = value_to_str(p_param_cur->type, p_val->u);
rc |= fprintf(fp, " %s,\n", str);
++dv_idx; ++p_val;
} while (dv_idx < ifs_table->num_default_values && !p_val->advance);
rc |= fprintf(fp, "};\n\n");
p_param_cur->default_value_cnt = dv_idx - start_index;
}
/* Check outputs */
if (rc < 0) {
print_error("Writing of default param values failed.");
return -1;
}
/* Write the main parameter structure beginning. */
rc = 0;
rc |= fprintf(fp,
"\n"
"static Mif_Param_Info_t MIFparamTable[] = {\n");
/* Write out an entry for each parameter in the table */
const Param_Info_t * const param = ifs_table->param;
const int num_param = ifs_table->num_param;
for (i = 0; i < num_param; i++) {
const Param_Info_t * const p_param_cur = param + i;
@ -713,14 +781,11 @@ static int write_param_info(
rc |= fprintf(fp, " %s,\n",
data_type_to_str(p_param_cur->type));
str = boolean_to_str(p_param_cur->has_default);
rc |= fprintf(fp, " %s,\n", str);
if (p_param_cur->has_default == true)
str = value_to_str(p_param_cur->type, p_param_cur->default_value);
rc |= fprintf(fp, " %d,\n", p_param_cur->default_value_cnt);
if (p_param_cur->default_value_cnt)
rc |= fprintf(fp, " %s_default,\n", p_param_cur->name);
else
str = no_value_to_str();
rc |= fprintf(fp, " %s,\n", str);
rc |= fprintf(fp, " NULL,\n");
str = boolean_to_str(p_param_cur->has_lower_limit);
rc |= fprintf(fp, " %s,\n", str);

View File

@ -570,7 +570,7 @@ MIF_INP2A (
if(mdfast->param[i]->is_null) {
char* emessage;
if(! param_info->has_default) {
if(param_info->default_value_siz == 0) {
if (param_info->type == MIF_STRING)
continue; // Allow NULL
emessage = tprintf("Parameter %s on model %s has no default",

View File

@ -135,7 +135,6 @@ MIFsetup(
for( ; model != NULL; model = MIFnextModel(model)) {
/* For each parameter not given explicitly on the .model */
/* card, default it */
@ -151,42 +150,66 @@ MIFsetup(
model->param[i]->size = 1;
model->param[i]->element = TMALLOC(Mif_Value_t, 1);
} else { /* parameter is an array */
/* MIF_INP2A() parser assures that there is an associated array connection */
/* Since several instances may share this model, we have to create an array */
/* big enough for the instance with the biggest connection array */
max_size = 0;
for(here = MIFinstances(model); here != NULL; here = MIFnextInstance(here)) {
size = here->conn[param_info->conn_ref]->size;
if(size > max_size)
max_size = size;
/* If there is an associated array connection, take the
* size from there. Since several instances may share
* this model, create an array big enough for the
* instance with the biggest connection array.
* Otherwise, use the size of the default.
*/
if (param_info->has_conn_ref) {
for (here = MIFinstances(model), max_size = 0;
here != NULL;
here = MIFnextInstance(here)) {
size = here->conn[param_info->conn_ref]->size;
if (size > max_size)
max_size = size;
}
} else {
max_size = param_info->default_value_siz;
if (param_info->has_lower_bound &&
param_info->lower_bound > max_size) {
max_size = param_info->lower_bound;
}
}
model->param[i]->size = max_size;
model->param[i]->element = TMALLOC(Mif_Value_t, max_size);
} /* end if parameter is an array */
/* set the parameter element(s) to default value */
for(j = 0; j < model->param[i]->size; j++) {
if (param_info->default_value_siz == 0)
continue;
/* Set the parameter element(s) to default value(s). */
for(j = 0; j < model->param[i]->size; j++) {
Mif_Parse_Value_t *dvp;
if (j >= param_info->default_value_siz) {
dvp = param_info->default_values +
param_info->default_value_siz - 1;
} else {
dvp = param_info->default_values + j;
}
switch(param_info->type) {
case MIF_BOOLEAN:
model->param[i]->element[j].bvalue = param_info->default_value.bvalue;
model->param[i]->element[j].bvalue = dvp->bvalue;
break;
case MIF_INTEGER:
model->param[i]->element[j].ivalue = param_info->default_value.ivalue;
model->param[i]->element[j].ivalue = dvp->ivalue;
break;
case MIF_REAL:
model->param[i]->element[j].rvalue = param_info->default_value.rvalue;
model->param[i]->element[j].rvalue = dvp->rvalue;
break;
case MIF_COMPLEX:
model->param[i]->element[j].cvalue = param_info->default_value.cvalue;
model->param[i]->element[j].cvalue = dvp->cvalue;
break;
case MIF_STRING:
model->param[i]->element[j].svalue = param_info->default_value.svalue;
model->param[i]->element[j].svalue = dvp->svalue;
break;
default: