/* * check_netlist.cpp - checker for the Qucs netlist * * Copyright (C) 2003-2009 Stefan Jahn * * This 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, or (at your option) * any later version. * * This software 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 package; see the file COPYING. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor, * Boston, MA 02110-1301, USA. * * $Id$ * */ #if HAVE_CONFIG_H # include #endif #include #include #include #include #include #include #include "logging.h" #include "strlist.h" #include "netdefs.h" #include "equation.h" #include "check_netlist.h" #include "constants.h" #include "environment.h" #include "variable.h" #include "module.h" using namespace qucs; using namespace qucs::eqn; /* Global definitions for parser and checker. */ struct definition_t * definition_root = NULL; struct definition_t * subcircuit_root = NULL; environment * env_root = NULL; /* The function counts the nodes in a definition line. */ static int checker_count_nodes (struct definition_t * def) { int count = 0; for (struct node_t * node = def->nodes; node != NULL; node = node->next) count++; return count; } /* The function returns an available definition line for the given type. If there is no such definition type the function returns NULL. */ static struct define_t * checker_find_definition (char * type, int action) { struct define_t * def = module::getModule (type); if (def != NULL && action == def->action) return def; return NULL; } /* The function returns the number of properties in a definition line specified by the given key. */ static int checker_find_property (const char * key, struct pair_t * pp) { int count = 0; while (pp != NULL) { if (!strcmp (pp->key, key)) count++; pp = pp->next; } return count; } /* Checks if the given property key is either optional or required for the given definition type and returns the type of the property. */ static int checker_is_property (struct define_t * available, const char * key) { int i; for (i = 0; PROP_IS_PROP (available->required[i]); i++) { if (!strcmp (available->required[i].key, key)) return available->required[i].type; } for (i = 0; PROP_IS_PROP (available->optional[i]); i++) { if (!strcmp (available->optional[i].key, key)) return available->optional[i].type; } return PROP_NONE; } /* Counts the number of definitions given by the specified type and instance name in the definition list. */ static int checker_count_definition (struct definition_t * root, const char * type, char * instance) { int count = 0; for (struct definition_t * def = root; def != NULL; def = def->next) { if (!strcmp (def->type, type) && !strcmp (def->instance, instance)) { if (++count > 1) def->duplicate = 1; } } return count; } /* Returns the value for a given definition type, key and variable identifier if it is in the list of definitions. Otherwise the function returns NULL. */ static struct value_t * checker_find_variable (struct definition_t * root, const char * type, const char * key, char * ident) { struct pair_t * pair; for (struct definition_t * def = root; def != NULL; def = def->next) { if (!strcmp (def->type, type)) { for (pair = def->pairs; pair != NULL; pair = pair->next) { if (!strcmp (pair->key, key)) if (pair->value->ident != NULL && ident != NULL && strcmp (pair->value->ident, ident) == 0) return pair->value; } } } return NULL; } /* The function returns the appropriate value for a given key within the given netlist definition if the value is a reference (a string). If there is no such key value pair the function returns NULL. */ static struct value_t * checker_find_reference (struct definition_t * def, const char * key) { struct pair_t * pair; for (pair = def->pairs; pair != NULL; pair = pair->next) { if (!strcmp (pair->key, key)) if (pair->value->ident != NULL) return pair->value; } return NULL; } /* The function looks for the given property key within the properties of the given definition line and returns its value if the property is not an identifier. Otherwise the function returns NULL. */ static struct value_t * checker_find_prop_value (struct definition_t * def, const char * key) { struct pair_t * pair; for (pair = def->pairs; pair != NULL; pair = pair->next) { if (!strcmp (pair->key, key)) if (pair->value->ident == NULL) return pair->value; } return NULL; } /* The function returns the number of properties in a definition line specified by the given key. */ static int checker_find_property (struct definition_t * def, const char * key) { return checker_find_property (key, def->pairs); } /* This function looks for the specified property 'key' in the given definition and return its value (a string reference) if it is a reference. Otherwise the function returns NULL and emits an appropriate error message. */ static struct value_t * checker_validate_reference (struct definition_t * def, const char * key) { struct value_t * val; if ((val = checker_find_reference (def, key)) == NULL) { logprint (LOG_ERROR, "line %d: checker error, not a valid `%s' property " "found in `%s:%s'\n", def->line, key, def->type, def->instance); } return val; } /* This function checks whether the given definition is a known microstrip component with a substrate definition. If the given identifier equals this substrate definition then the function returns the appropriate value. Otherwise it returns NULL. */ static struct value_t * checker_find_substrate (struct definition_t * def, char * ident) { struct value_t * val; if (checker_is_property (def->define, "Subst") == PROP_STR) { if ((val = checker_find_reference (def, "Subst")) != NULL) { if (ident != NULL && !strcmp (val->ident, ident)) return val; } } return NULL; } /* Puts the given double value variable into an environment. */ static variable * checker_add_variable (environment * env, char * var, int type, bool pass) { variable * v = new variable (var); eqn::constant * c = new eqn::constant (type); switch (type) { case TAG_DOUBLE: c->d = 0.0; // initialize the variable break; case TAG_VECTOR: c->v = new qucs::vector (); break; } v->setConstant (c); env->addVariable (v, pass); return v; } /* Resolves the variable of a property value. Returns non-zero on success, otherwise zero. */ static int checker_resolve_variable (struct definition_t * root, struct definition_t * def, struct pair_t * pair, int type) { struct value_t * val; struct value_t * value = pair->value; if (value->ident != NULL) { int found = 0; /* 1. find variable in parameter sweeps */ if ((val = checker_find_variable (root, "SW", "Param", value->ident))) { /* add parameter sweep variable to environment */ if (!strcmp (def->type, "SW") && !strcmp (pair->key, "Param")) { checker_add_variable (root->env, value->ident, TAG_DOUBLE, true); } /* mark both the variable identifier and the parameter sweep variable to be actually variables */ val->var = TAG_DOUBLE; value->var = TAG_DOUBLE; found++; } /* 2. find analysis in parameter sweeps */ if ((val = checker_find_variable (root, "SW", "Sim", value->ident))) { found++; } /* 3. find substrate in microstrip components */ if ((val = checker_find_substrate (def, value->ident))) { value->subst = 1; found++; } /* 4. find subcircuit definition in subcircuit components */ if ((val = checker_find_variable (root, "Sub", "Type", value->ident))) { found++; } /* 5. find special identifiers in certain properties */ if (value->range) { found++; } /* 6. find file reference in S-parameter file components */ if ((val = checker_find_variable (root, "SPfile", "File", value->ident))) { found++; } /* 6a. find file reference in S-parameter de-embedding file components */ if ((val = checker_find_variable (root, "SPDfile", "File", value->ident))) { found++; } /* 7. find variable in equation */ if (root->env) { if (root->env->getChecker()->containsVariable (value->ident)) { variable * v; value->var = (type == PROP_LIST) ? TAG_VECTOR : TAG_DOUBLE; if ((v = root->env->getVariable (value->ident)) == NULL) { // put variable into the environment checker_add_variable (root->env, value->ident, value->var, false); } found++; } } /* 8. find file reference in file based sources */ if ((val = checker_find_variable (root, "Vfile", "File", value->ident))) { found++; } if ((val = checker_find_variable (root, "Ifile", "File", value->ident))) { found++; } /* 9. find property reference in the instance */ if (!found && checker_is_property (def->define, value->ident) != PROP_NONE) { if (root->env) { // create reference variable names char * txt = (char *) malloc (strlen (def->instance) + 1 + strlen (value->ident) + 1); sprintf (txt, "%s.%s", def->instance, value->ident); char * ref = (char *) malloc (strlen (def->instance) + 5 + strlen (value->ident) + 1); sprintf (ref, "%s.%s.ref", def->instance, value->ident); // replace property string free (value->ident); value->ident = strdup (ref); value->var = TAG_DOUBLE; // already done previously? variable * v; if ((v = root->env->getVariable (ref)) == NULL) { // put variable into the environment checker_add_variable (root->env, ref, TAG_DOUBLE, false); // also add reference equation into environment root->env->getChecker()->addReference ("#propref", ref, txt); } // done free (txt); free (ref); found++; } } /* not found */ if (!found) { logprint (LOG_ERROR, "line %d: checker error, no such variable `%s' " "used in a `%s:%s' property\n", def->line, value->ident, def->type, def->instance); return 0; } } return 1; } /* Evaluates the unit scale in a property value. It adjusts the actual value and omits the scale. The function returns non-zero on success and zero otherwise. */ static int checker_evaluate_scale (struct value_t * value) { double val = value->value, factor = 1.0; char * scale; if (value->scale != NULL) { scale = value->scale; switch (*scale) { case 'E': scale++; factor = 1e18; break; case 'P': scale++; factor = 1e15; break; case 'T': scale++; factor = 1e12; break; case 'G': scale++; factor = 1e9; break; case 'M': scale++; factor = 1e6; break; case 'k': scale++; factor = 1e3; break; case 'm': scale++; if (*scale == 'i') { scale++; if (*scale == 'l') { scale++; factor = 2.54e-5; } } else factor = 1e-3; break; case 'u': scale++; factor = 1e-6; break; case 'n': scale++; factor = 1e-9; break; case 'p': scale++; factor = 1e-12; break; case 'f': scale++; if (*scale == 't') { scale++; factor = 0.3048; } else factor = 1e-15; break; case 'a': scale++; factor = 1e-18; break; case 'd': scale++; if (*scale == 'B') { scale++; val = std::pow (10.0, val / 10.0); if (*scale == 'm') { scale++; factor = 1e-3; } else if (*scale == 'u') { scale++; factor = 1e-6; } } break; case 'i': scale++; if (*scale == 'n') { scale++; factor = 2.54e-2; } break; case 'y': scale++; if (*scale == 'd') { scale++; factor = 0.9144; } break; } if (*scale != '\0') { value->unit = strdup (scale); } free (value->scale); value->scale = NULL; } value->value = val * factor; return 1; } /* The function returns the number of instances of the given type within the list of definitions. */ static int checker_count_definitions (struct definition_t * root, const char * type, int action) { int count = 0; for (struct definition_t * def = root; def != NULL; def = def->next) { if (def->action == action) { if (type == NULL) count++; else if (!strcmp (def->type, type)) count++; } } return count; } /* This function looks for the specified subcircuit type in the list of available subcircuits and returns its definition. If there is no such subcircuit the function returns NULL: */ static struct definition_t * checker_find_subcircuit (char * n) { struct definition_t * def; for (def = subcircuit_root; def != NULL; def = def->next) if (n != NULL && !strcmp (def->instance, n)) return def; return NULL; } /* The function returns the subcircuit definition for the given subcircuit instance. */ static struct definition_t * checker_get_subcircuit (struct definition_t * def) { struct value_t * val; struct definition_t * sub = NULL; if ((val = checker_find_reference (def, "Type")) != NULL) sub = checker_find_subcircuit (val->ident); return sub; } // Global variable indicating cycles in subcircuit definitions. static int checker_sub_cycles = 0; /* The following function returns the number of circuit instances requiring a DC analysis (being nonlinear) in the list of definitions. */ static int checker_count_nonlinearities (struct definition_t * root) { int count = 0; struct definition_t * sub; for (struct definition_t * def = root; def != NULL; def = def->next) { if (def->nonlinear != 0) count++; // also recurse into subcircuits if possible if (checker_sub_cycles <= 0) { if (!strcmp (def->type, "Sub")) { if ((sub = checker_get_subcircuit (def)) != NULL) { count += checker_count_nonlinearities (sub->sub); } } } } return count; } /* This function returns the number of action definitions with the given instance name. */ static int checker_count_action (struct definition_t * root, char * instance) { int count = 0; for (struct definition_t * def = root; def != NULL; def = def->next) { if (def->action == 1 && !strcmp (def->instance, instance)) count++; } return count; } /* This (recursive) function detects any kind of cyclic definitions of parameter sweeps for the given instance name. The string list argument is used to pass the dependencies. The function returns zero if the parameter sweep in non-cyclic. */ static int checker_validate_para_cycles (struct definition_t * root, char * instance, strlist * deps) { int errors = 0; struct value_t * val; for (struct definition_t * def = root; def != NULL; def = def->next) { /* find the appropriate definition for the given instance */ if (def->action == 1 && !strcmp (def->instance, instance)) { /* emit error message if the instance is already in the dependencies */ if (deps->contains (instance)) { logprint (LOG_ERROR, "checker error, cyclic definition of `%s' " "detected, involves: %s\n", instance, deps->toString ()); return ++errors; } deps->append (instance); /* recurse into parameter sweeps */ if (!strcmp (def->type, "SW")) { if ((val = checker_find_reference (def, "Sim")) != NULL) { return checker_validate_para_cycles (root, val->ident, deps); } } } } return errors; } /* This function validates each parameter sweep within the list of definitions and return non-zero on errors. Emits appropriate error messages. */ static int checker_validate_para (struct definition_t * root) { int errors = 0; struct value_t * val; for (struct definition_t * def = root; def != NULL; def = def->next) { /* find parameter sweep */ if (def->action == 1 && !strcmp (def->type, "SW")) { /* the 'Sim' property must be an identifier */ if ((val = checker_validate_reference (def, "Sim")) == NULL) { errors++; } else { /* check for self-referring sweeps */ if (!strcmp (def->instance, val->ident)) { logprint (LOG_ERROR, "line %d: checker error, definition `%s:%s' " "refers to itself\n", def->line, def->type, def->instance); errors++; } /* look for the referred analysis action definition */ if (checker_count_action (root, val->ident) != 1) { logprint (LOG_ERROR, "line %d: checker error, no such action `%s' " "found as referred in `%s:%s'\n", def->line, val->ident, def->type, def->instance); errors++; } /* finally detect cyclic definitions */ strlist * deps = new strlist (); errors += checker_validate_para_cycles (root, val->ident, deps); delete deps; } } } return errors; } /* This function returns the next port definition in the given list of definitions or NULL if there is no such definition. */ static struct definition_t * checker_find_port (struct definition_t * root) { for (struct definition_t * def = root; def != NULL; def = def->next) { if (def->action == PROP_COMPONENT && !strcmp (def->type, "Pac")) { return def; } } return NULL; } /* This function checks whether the port numbers for the S-parameter analysis are unique or not. It returns zero on success and non-zero if it detected duplicate entries. */ static int checker_validate_ports (struct definition_t * root) { int p, errors = 0; struct value_t * val; struct definition_t * port, * def = root; const char * prop = "Num"; while ((def = checker_find_port (def)) != NULL) { if ((val = checker_find_prop_value (def, prop)) != NULL) { p = (int) val->value; port = root; while ((port = checker_find_port (port)) != NULL) { if (port != def) { if ((val = checker_find_prop_value (port, prop)) != NULL) { if (p == (int) val->value) { logprint (LOG_ERROR, "line %d: checker error, `%s' definitions " "with duplicate `%s=%d' property found: `%s:%s' and " "`%s:%s'\n", def->line, def->type, prop, p, def->type, def->instance, port->type, port->instance); errors++; } } } port = port->next; } } def = def->next; } return errors; } /* The following function checks whether the parametric sweeps in the netlist are valid or not. It returns zero on success and non-zero otherwise. */ static int checker_validate_lists (struct definition_t * root) { int errors = 0; // go through each definition for (struct definition_t * def = root; def != NULL; def = def->next) { /* sweeps possible in parameter sweep, ac-analysis and s-parameter analysis */ if (def->action == 1 && (!strcmp (def->type, "SW") || !strcmp (def->type, "AC") || !strcmp (def->type, "SP"))) { struct value_t * val; if((val = checker_find_reference (def, "Type")) == NULL) { logprint (LOG_ERROR, "line %d: checker error, required property " "`%s' is invalid in `%s:%s'\n", def->line, "Type", def->type, def->instance); errors++; continue; } char * type = val->ident; // list of constant values and constant values if (type && (!strcmp (type, "const") || !strcmp (type, "list"))) { // property 'Values' is required if ((val = checker_find_prop_value (def, "Values")) == NULL) { if (!strcmp (type, "const")) { if ((val = checker_validate_reference (def, "Values")) == NULL) { errors++; } } else { logprint (LOG_ERROR, "line %d: checker error, required property " "`%s' not found in `%s:%s'\n", def->line, "Values", def->type, def->instance); errors++; } } else { if (!strcmp (type, "const")) { // in constant sweeps only one value allowed if (val->next != NULL) { logprint (LOG_ERROR, "line %d: checker error, value of `%s' " "needs to be a single constant value in `%s:%s', no " "lists possible\n", def->line, "Values", def->type, def->instance); errors++; } val->var = TAG_UNKNOWN; } if (!strcmp (type, "list")) { val->var = TAG_VECTOR; } // check and evaluate the unit scale in a value list for (; val != NULL; val = val->next) { if (!checker_evaluate_scale (val)) errors++; } } // property 'Start' is invalid if (checker_find_property (def, "Start") > 0) { logprint (LOG_ERROR, "line %d: checker error, extraneous property " "`%s' is invalid in `%s:%s'\n", def->line, "Start", def->type, def->instance); errors++; } // property 'Stop' is invalid if (checker_find_property (def, "Stop") > 0) { logprint (LOG_ERROR, "line %d: checker error, extraneous property " "`%s' is invalid in `%s:%s'\n", def->line, "Stop", def->type, def->instance); errors++; } // property 'Points' is also invalid if (checker_find_property (def, "Points") > 0) { logprint (LOG_ERROR, "line %d: checker error, extraneous property " "`%s' is invalid in `%s:%s'\n", def->line, "Points", def->type, def->instance); errors++; } } // linearly and logarithmically stepped sweeps else if (type && (!strcmp (type, "lin") || !strcmp (type, "log"))) { // property 'Start' required if (checker_find_property (def, "Start") <= 0) { logprint (LOG_ERROR, "line %d: checker error, required property " "`%s' not found in `%s:%s'\n", def->line, "Start", def->type, def->instance); errors++; } // property 'Stop' required if (checker_find_property (def, "Stop") <= 0) { logprint (LOG_ERROR, "line %d: checker error, required property " "`%s' not found in `%s:%s'\n", def->line, "Stop", def->type, def->instance); errors++; } // property 'Points' is also required if (checker_find_property (def, "Points") <= 0) { logprint (LOG_ERROR, "line %d: checker error, required property " "`%s' not found in `%s:%s'\n", def->line, "Points", def->type, def->instance); errors++; } // property 'Values' is invalid if (checker_find_property (def, "Values") > 0) { logprint (LOG_ERROR, "line %d: checker error, extraneous property " "`%s' is invalid in `%s:%s'\n", def->line, "Values", def->type, def->instance); errors++; } } } } return errors; } /* This function checks the actions to be taken in the netlist. It returns zero on success, non-zero otherwise. */ static int checker_validate_actions (struct definition_t * root) { int a, c, n, errors = 0; if ((n = checker_count_definitions (root, NULL, 1)) < 1) { logprint (LOG_ERROR, "checker error, no actions defined: nothing to do\n"); errors++; } else { // check requirements for s-parameter analysis if ((a = checker_count_definitions (root, "SP", 1)) >= 1) { if ((n = checker_count_definitions (root, "Pac", 0)) < 1) { logprint (LOG_ERROR, "checker error, %d `Pac' definitions found, at " "least 1 required\n", n); errors++; } } // count analyses requiring a DC solution a += checker_count_definitions (root, "AC", 1); // check dc-analysis requirements c = checker_count_nonlinearities (root); n = checker_count_definitions (root, "DC", 1); if (n > 1) { logprint (LOG_ERROR, "checker error, the .DC action is defined %dx, " "single or none required\n", n); errors++; } if (a >= 1 && c >= 1 && n < 1) { logprint (LOG_ERROR, "checker error, a .DC action is required for this " "circuit definition (accounted %d non-linearities)\n", c); errors++; } } errors += checker_validate_para (root); errors += checker_validate_ports (root); errors += checker_validate_lists (root); return errors; } /* This function checks the validity of each microstrip component and its substrate and model references. It returns zero on success, emit error messages if necessary and returns non-zero on errors. */ static int checker_validate_strips (struct definition_t * root) { int errors = 0; struct value_t * val; for (struct definition_t * def = root; def != NULL; def = def->next) { if (!def->action) { /* find components with substrate property */ if (def->define && (checker_is_property (def->define, "Subst") == PROP_STR)) { /* check validity of 'Subst' property */ if ((val = checker_validate_reference (def, "Subst")) == NULL) { errors++; } else { if (checker_count_definition (root, "SUBST", val->ident) != 1) { logprint (LOG_ERROR, "line %d: checker error, no such substrate " "`%s' found as specified in `%s:%s'\n", def->line, val->ident, def->type, def->instance); errors++; } } /* check validity of 'Model' property */ #if DISABLE_FOR_NOW /* ThinkME!!! */ if ((val = checker_validate_reference (def, "Model")) == NULL) { errors++; } #endif } } } return errors; } /* This function counts the number of occurrences of the given node name in the given netlist definition root. */ static int checker_count_nodes (struct definition_t * root, char * n) { int count = 0; struct node_t * node; for (struct definition_t * def = root; def != NULL; def = def->next) { if (!def->action && !def->nodeset) { for (node = def->nodes; node != NULL; node = node->next) if (!strcmp (node->node, n)) count++; } } return count; } /* The function identifies duplicate nodesets for the same node which is not allowed. It returns the number of duplications. */ static int checker_count_nodesets (struct definition_t * root, char * n) { int count = 0; for (struct definition_t * def = root; def != NULL; def = def->next) { if (def->nodeset && !def->duplicate && def->nodes) { char * node = def->nodes->node; if (!strcmp (node, n)) { if (++count > 1) def->duplicate = 1; } } } return count; } /* The following function checks whether the nodes specified by the nodeset functionality is valid in its current scope. It does not check across subcircuit boundaries. */ static int checker_validate_nodesets (struct definition_t * root) { int errors = 0; for (struct definition_t * def = root; def != NULL; def = def->next) { if (def->nodeset && checker_count_nodes (def) == 1) { char * node = def->nodes->node; if (checker_count_nodes (root, node) <= 0) { logprint (LOG_ERROR, "line %d: checker error, no such node `%s' found " "as referenced by `%s:%s'\n", def->line, node, def->type, def->instance); errors++; } if (checker_count_nodesets (root, node) > 1) { logprint (LOG_ERROR, "line %d: checker error, the node `%s' is not " "uniquely defined by `%s:%s'\n", def->line, node, def->type, def->instance); errors++; } } } return errors; } /* This function should be called after the netlist and the equation list have been checked. It verifies that parameter sweep definitions and equation variable identifiers are unique. The function returns zero on success and non-zero otherwise. */ static int netlist_checker_variables_intern (struct definition_t * root, environment * env) { int errors = 0, pos; struct value_t * para, * ref; strlist * eqnvars = env->getChecker()->variables (); strlist * instances = new strlist (); strlist * vars = new strlist (); strlist * refs = new strlist (); // go through list of netlist definitions for (struct definition_t * def = root; def != NULL; def = def->next) { // find parameters sweeps if (def->action == 1 && !strcmp (def->type, "SW")) { para = checker_find_reference (def, "Param"); ref = checker_find_reference (def, "Sim"); if (para != NULL && ref != NULL) { // check whether sweep variable collides with equations if (eqnvars && eqnvars->contains (para->ident)) { logprint (LOG_ERROR, "checker error, equation variable `%s' " "already defined by `%s:%s'\n", para->ident, def->type, def->instance); errors++; } // check for duplicate parameter names in parameter sweeps, but // allow them in same order sweeps if ((pos = vars->index (para->ident)) != -1) { if (strcmp (ref->ident, refs->get (pos))) { logprint (LOG_ERROR, "checker error, variable `%s' in `%s:%s' " "already defined by `%s:%s'\n", para->ident, def->type, def->instance, def->type, instances->get (pos)); errors++; } } // check for duplicate simulations in parameter sweeps (same order // sweep) and allow same parameter name only if ((pos = refs->index (ref->ident)) != -1) { if (strcmp (para->ident, vars->get (pos))) { logprint (LOG_ERROR, "checker error, conflicting variables `%s' " "in `%s:%s' and `%s' in `%s:%s' for `%s'\n", para->ident, def->type, def->instance, vars->get (pos), def->type, instances->get (pos), ref->ident); errors++; } } // collect parameter sweep variables for the above two checks instances->add (def->instance); vars->add (para->ident); refs->add (ref->ident); } } } delete eqnvars; delete refs; delete vars; delete instances; return errors; } /* This is the overall variable checker for the parsed netlist. See the above function for details. */ int netlist_checker_variables (environment * env) { return netlist_checker_variables_intern (definition_root, env); } /* The function checks whether the given key-value combination is inside the allowed range defined by the given property definition and returns the number of error or zero otherwise. */ static int checker_value_in_prop_range (char * instance, struct define_t * def, struct pair_t * pp, struct property_t * prop) { int errors = 0; // check values if (PROP_IS_VAL (*prop)) { if (!PROP_IS_LST (*prop)) { // lists of values possible? if (pp->value->next != NULL) { logprint (LOG_ERROR, "checker error, value of `%s' needs to be " "a single value in `%s:%s', no lists possible\n", pp->key, def->type, instance); errors++; } } else { // a value list struct value_t * val = pp->value; val->var = TAG_VECTOR; // check and evaluate the unit scale in a value list for (; val != NULL; val = val->next) { if (!checker_evaluate_scale (val)) errors++; } } // check range of all values if (PROP_HAS_RANGE (*prop)) { struct value_t * val = pp->value; if (val->ident) { /* no range checking on variable identifier */ logprint (LOG_STATUS, "checker notice, value of `%s' (variable `%s') could be " "out of range `%c%g,%g%c' in `%s:%s'\n", pp->key, val->ident, prop->range.il, prop->range.l, prop->range.h, prop->range.ih, def->type, instance); val = NULL; } for (; val != NULL; val = val->next) { int rerror = 0; if (prop->range.il == '[' && (val->value < prop->range.l)) rerror++; if (prop->range.il == ']' && !(val->value > prop->range.l)) rerror++; if (prop->range.ih == '[' && !(val->value < prop->range.h)) rerror++; if (prop->range.ih == ']' && (val->value > prop->range.h)) rerror++; if (rerror) { logprint (LOG_ERROR, "checker error, value of `%s' (%g) is out of " "range `%c%g,%g%c' in `%s:%s'\n", pp->key, val->value, prop->range.il, prop->range.l, prop->range.h, prop->range.ih, def->type, instance); errors++; } } } // check fraction of integers if (PROP_IS_INT (*prop)) { double integral; if (modf (pp->value->value, &integral) != 0) { logprint (LOG_ERROR, "checker error, value of `%s' (%g) needs to be " "an integer in `%s:%s'\n", pp->key, pp->value->value, def->type, instance); errors++; } } } // check identifiers else { // no identifier given if (pp->value->ident == NULL) { logprint (LOG_ERROR, "checker error, value of `%s' (%g) needs to be " "an identifier in `%s:%s'\n", pp->key, pp->value->value, def->type, instance); errors++; } // check identifier range else { if (PROP_HAS_STR (*prop)) { int i, found = 0; char range[256]; sprintf (range, "["); for (i = 0; prop->range.str[i]; i++) { strcat (range, prop->range.str[i]); strcat (range, ","); if (!strcmp (prop->range.str[i], pp->value->ident)) found++; } if (!found) { range[strlen (range) - 1] = ']'; logprint (LOG_ERROR, "checker error, value of `%s' (%s) needs to be " "in %s in `%s:%s'\n", pp->key, pp->value->ident, range, def->type, instance); errors++; } else pp->value->range = 1; } } } return errors; } /* The function checks whether the given key-value combination being part of the available definition is inside the allowed range and returns zero if not. Otherwise the function returns non-zero. */ static int checker_value_in_range (char * instance, struct define_t * def, struct pair_t * pp) { int i, errors = 0; // go through required properties for (i = 0; PROP_IS_PROP (def->required[i]); i++) { if (!strcmp (def->required[i].key, pp->key)) { errors += checker_value_in_prop_range (instance, def, pp, &def->required[i]); } } // go through optional properties for (i = 0; PROP_IS_PROP (def->optional[i]); i++) { if (!strcmp (def->optional[i].key, pp->key)) { errors += checker_value_in_prop_range (instance, def, pp, &def->optional[i]); } } return errors ? 0 : 1; } /* The function determines the subcircuit definitions defined in the original netlist and builds an appropriate subcircuit definition list. It returns the given definition list with the subcircuits removed. */ static struct definition_t * checker_build_subcircuits (struct definition_t * root) { struct definition_t * def, * next, * prev; for (prev = NULL, def = root; def != NULL; def = next) { next = def->next; if (!strcmp (def->type, "Def")) { if (prev) { prev->next = next; } else { root = next; } def->sub = checker_build_subcircuits (def->sub); def->next = subcircuit_root; subcircuit_root = def; } else prev = def; } return root; } /* The function produces a copy of the given circuit definition and marks it as a copy. The node definition are not included within the copy. */ static struct definition_t * checker_copy_subcircuit (struct definition_t * sub) { struct definition_t * copy; copy = (struct definition_t *) calloc (sizeof (struct definition_t), 1); copy->action = sub->action; copy->nonlinear = sub->nonlinear; copy->substrate = sub->substrate; copy->nodeset = sub->nodeset; copy->define = sub->define; copy->pairs = sub->pairs; copy->ncount = sub->ncount; copy->type = strdup (sub->type); copy->copy = 1; return copy; } /* This function translates the node definitions of the given subcircuit element 'sub'. The translation is based upon the node definitions of the subcircuit 'type' and the instance 'inst' of such a subcircuit. The translated nodes are saved in an extra 'xlate' field of each node of the subcircuit element 'sub'. */ static void checker_xlat_subcircuit_nodes (struct definition_t * type, struct definition_t * inst, struct definition_t * sub) { struct node_t * n, * ninst, * ntype; int i; // go through nodes of the subcircuit 'type' and 'inst' for (i = 1, ntype = type->nodes, ninst = inst->nodes; ntype != NULL; ntype = ntype->next, ninst = ninst->next, i++) { for (n = sub->nodes; n != NULL; n = n->next) { /* check whether a node in the subcircuit element 'sub' corresponds with the 'type', then assign the 'inst's node name */ if (!strcmp (n->node, ntype->node)) { n->xlate = strdup (ninst->node); n->xlatenr = i; } } } } /* The function creates a subcircuit node name consisting of the given arguments. If the given 'instances' is NULL it is left out. The caller is responsible to free() the returned string. */ static char * checker_subcircuit_node (char * type, char * instances, char * instance, char * node) { char * txt = (char *) calloc (1, strlen (type) + strlen (instance) + strlen (node) + (instances ? strlen (instances) : 0) + 4); if (instances) sprintf (txt, "%s.%s.%s.%s", type, instances, instance, node); else sprintf (txt, "%s.%s.%s", type, instance, node); return txt; } /* The function reverses the order of the given node list and returns the reversed list. */ struct node_t * netlist_reverse_nodes (struct node_t * nodes) { struct node_t * root, * next; for (root = NULL; nodes != NULL; nodes = next) { next = nodes->next; nodes->next = root; root = nodes; } return root; } /* This function assigns new node names to the subcircuit element 'copy' based upon the previous node translation between the subcircuit 'type' and the instance 'inst' of this type. The global 'gnd' node is not touched. */ static void checker_copy_subcircuit_nodes (struct definition_t * type, struct definition_t * inst, struct definition_t * sub, struct definition_t * copy, char * instances) { struct node_t * n, * ncopy, * root = NULL; // go through the list of the subcircuit element's 'sub' nodes for (n = sub->nodes; n != NULL; n = n->next) { // create new node based upon the node translation ncopy = (struct node_t *) calloc (sizeof (struct node_t), 1); ncopy->xlatenr = n->xlatenr; if (n->xlate) // translated node { if (instances == NULL) ncopy->node = strdup (n->xlate); else ncopy->node = NULL; // leave it blank yet, indicates translation } else if (!strcmp (n->node, "gnd")) // ground node { ncopy->node = strdup (n->node); } else if (n->node[strlen (n->node) - 1] == '!') // global node { ncopy->node = strdup (n->node); } else // internal subcircuit element node { ncopy->node = checker_subcircuit_node (type->instance, instances, inst->instance, n->node); } // chain the new node list ncopy->next = root; root = ncopy; } /* and finally reverse the created node list and assign it to the subcircuit element's 'copy' */ copy->nodes = netlist_reverse_nodes (root); } // Returns the node at the given position. static struct node_t * checker_get_circuit_node (struct node_t * root, int n) { for (int i = 1; i < n; i++) { root = root->next; assert (root != NULL); } return root; } // The function cleans the translated nodes of a subcircuit template. static void checker_cleanup_xlat_nodes (struct definition_t * sub) { for (struct node_t * n = sub->nodes; n != NULL; n = n->next) { free (n->xlate); n->xlate = NULL; n->xlatenr = 0; } } /* The function is used to assign the nodes of the 'copy' subcircuit element which were left blank intentionally by the element copy in order to indicate that it is an external node. Again, if the current node translation indicates an external node it is blanked again, otherwise the node gets a unique internal name. If the 'instances' list is NULL, then this indicates the root circuit list and node translations are done though they are 'external'. */ static void checker_copy_circuit_nodes (struct definition_t * type, struct definition_t * inst, struct definition_t * sub, struct definition_t * copy, char * instances) { struct node_t * n, * ncopy; // go through the list of the subcircuit element's 'copy' nodes for (ncopy = copy->nodes; ncopy != NULL; ncopy = ncopy->next) { // these NULL nodes have intentionally been blanked if (ncopy->node == NULL) { assert (ncopy->xlatenr != 0); // get translated node n = checker_get_circuit_node (sub->nodes, ncopy->xlatenr); ncopy->xlatenr = n->xlatenr; if (n->xlate) // translated node { if (instances == NULL) // external node indicated by no instances given ncopy->node = strdup (n->xlate); else ncopy->node = NULL; // keep blank } else if (!strcmp (n->node, "gnd")) // global ground node { ncopy->node = strdup (n->node); } else if (n->node[strlen (n->node) - 1] == '!') // other global node { ncopy->node = strdup (n->node); } else // internal subcircuit element node { ncopy->node = checker_subcircuit_node (type->instance, instances, inst->instance, n->node); } } } } /* This function returns the last entry of the given list of definitions or NULL if there is no such element. */ static struct definition_t * checker_find_last_definition (struct definition_t * root) { for (struct definition_t * def = root; def != NULL; def = def->next) if (def->next == NULL) return def; return NULL; } /* Based upon the the given subcircuit instance identifier list the function returns a "." - concatenated string or NULL. */ static char * checker_subcircuit_instance_list (strlist * instances) { if (instances && instances->length () > 0) return instances->toString ("."); return NULL; } /* The function creates a subcircuit instance name consisting of the given arguments. If the given 'instances' is NULL it is left out. The caller is responsible to free() the returned string. */ static char * checker_subcircuit_instance (char * type, char * instances, char * instance, char * base) { char * txt = (char *) calloc (1, strlen (type) + strlen (instance) + strlen (base) + (instances ? strlen (instances) : 0) + 4); if (instances) sprintf (txt, "%s.%s.%s.%s", type, instances, instance, base); else sprintf (txt, "%s.%s.%s", type, instance, base); return txt; } /* This function produces a copy of the given subcircuit 'type' containing the subcircuit elements. Based upon the instance 'inst' definitions (node names and instance name) it assign new element instances and node names. The function returns a NULL terminated circuit element list in reverse order. */ static struct definition_t * checker_copy_subcircuits (struct definition_t * type, struct definition_t * inst, strlist * * instances, environment * parent) { struct definition_t * def, * copy; struct definition_t * root = NULL; strlist * instcopy; char * list; // create environment for subcircuit instance environment * child = new environment (*(type->env)); parent->push_front_Child (child); // put instance properties into subcircuit environment for (struct pair_t * pair = inst->pairs; pair != NULL; pair = pair->next) { // anything else than the 'Type' if (strcmp (pair->key, "Type")) { if (pair->value->ident == NULL) { // simple value child->setDoubleConstant (pair->key, pair->value->value); child->setDouble (pair->key, pair->value->value); } else { // reference to variable in upper environment child->setDoubleReference (pair->key, pair->value->ident); } } } // go through element list of subcircuit for (def = type->sub; def != NULL; def = def->next) { // translate the node list checker_xlat_subcircuit_nodes (type, inst, def); // allow recursive subcircuits if (!strcmp (def->type, "Sub")) { // get subcircuit template definition struct definition_t * sub = checker_get_subcircuit (def); // create a copy of the current subcircuit instance list if ((*instances) == NULL) (*instances) = new strlist (); instcopy = new strlist (*(*instances)); // append instance name to recursive instance list (*instances)->append (inst->instance); copy = checker_copy_subcircuits (sub, def, instances, child); // put the expanded definitions into the sublist if (copy) { list = checker_subcircuit_instance_list (instcopy); // assign blanked node names to each subcircuit for (struct definition_t * c = copy; c != NULL; c = c->next) { checker_copy_circuit_nodes (type, inst, def, c, list); } // append the copies to the subcircuit list struct definition_t * last = checker_find_last_definition (copy); last->next = root; root = copy; } // restore original instance list delete *instances; *instances = instcopy; } else { // element copy copy = checker_copy_subcircuit (def); // assign new instance name to the element list = checker_subcircuit_instance_list (*instances); copy->instance = checker_subcircuit_instance (type->instance, list, inst->instance, def->instance); copy->subcircuit = strdup (type->instance); // assign node list checker_copy_subcircuit_nodes (type, inst, def, copy, list); // apply environment copy->env = child; // chain definition (circuit) list copy->next = root; root = copy; } // cleanup translated nodes checker_cleanup_xlat_nodes (def); } // try giving child environment a unique name strlist * icopy = new strlist (); icopy->append (type->instance); icopy->append (*(instances)); icopy->append (inst->instance); child->setName (std::string(icopy->toString ("."))); delete icopy; return root; } /* The function checks whether the subcircuit 'instance' with the subcircuit 'type' is defined in cycles. It recursively goes through the definitions and emits an appropriate error message if necessary. The function returns zero if there are no cycles detected and non-zero with cycles found. */ static int checker_validate_sub_cycles (struct definition_t * root, char * type, char * instance, strlist * * deps) { int errors = 0, error; struct value_t * val; /* emit an appropriate error message if the subcircuit type is already in the dependencies */ if ((*deps)->contains (type)) { logprint (LOG_ERROR, "checker error, cyclic definition of `%s:%s' " "detected, involves: %s\n", type, instance, (*deps)->toString ()); return ++errors; } (*deps)->append (type); // create temporary list of subcircuit types already tested strlist * checked = new strlist (); // go through the list of circuit elements of the subcircuit for (struct definition_t * def = root->sub; def != NULL; def = def->next) { // find the appropriate definitions if (!strcmp (def->type, "Sub")) { // recurse into subcircuit instances if ((val = checker_find_reference (def, "Type")) != NULL) { if (!checked->contains (val->ident)) // only if not already checked { struct definition_t * sub; checked->append (val->ident); // copy current dependencies strlist * copy = new strlist (*(*deps)); // validate subcircuit sub = checker_find_subcircuit (val->ident); if (sub != NULL) // if possible error = checker_validate_sub_cycles (sub, sub->instance, instance, deps); else error = 1; // on errors: go on if (error) { errors += error; delete copy; } // no errors: restore original dependencies else { delete *deps; *deps = copy; } } } } } delete checked; return errors; } /* This function dynamically creates a circuit definition based on a given subcircuit definition including type, number of nodes and the properties. */ static struct define_t * netlist_create_define (struct definition_t * def) { int o, r; struct pair_t * p; struct define_t * d = (struct define_t *) calloc (sizeof (struct define_t), 1); d->type = strdup (def->instance); d->nodes = checker_count_nodes (def); d->action = PROP_COMPONENT; // determine number of required and optional parameters for (o = r = 0, p = def->pairs; p != NULL; p = p->next) { if (p->value == NULL) r++; else o++; } d->required = (struct property_t *) calloc (sizeof (struct property_t), r + 2); d->optional = (struct property_t *) calloc (sizeof (struct property_t), o + 1); // fill in parameters for (o = r = 0, p = def->pairs; p != NULL; p = p->next) { if (p->value == NULL) { // required d->required[r].key = strdup (p->key); d->required[r].type = PROP_REAL; d->required[r].defaultval.d = PROP_NO_VAL; d->required[r].defaultval.s = PROP_NO_STR; d->required[r].range.il = '.'; d->required[r].range.l = 0; d->required[r].range.h = 0; d->required[r].range.ih = '.'; r++; } else { // optional d->optional[o].key = strdup (p->key); d->optional[o].type = PROP_REAL; d->optional[o].defaultval.d = p->value->value; d->optional[o].defaultval.s = PROP_NO_STR; d->optional[o].range.il = '.'; d->optional[o].range.l = 0; d->optional[o].range.h = 0; d->optional[o].range.ih = '.'; o++; } } // extra required d->required[r].key = strdup ("Type"); d->required[r].type = PROP_STR; d->required[r].defaultval.d = PROP_NO_VAL; d->required[r].defaultval.s = PROP_NO_STR; d->required[r].range.il = '.'; d->required[r].range.l = 0; d->required[r].range.h = 0; d->required[r].range.ih = '.'; return d; } /* The function destroys the given circuit definition which must have been dynamically created before. */ static void netlist_free_define (struct define_t * d) { int i; struct property_t * p; free ((char *) d->type); // free required properties for (i = 0, p = d->required; p[i].key != NULL; i++) { free ((char *) p[i].key); } free (d->required); // free optional properties for (i = 0, p = d->optional; p[i].key != NULL; i++) { free ((char *) p[i].key); } free (d->optional); free (d); } /* The function checks the presence of required and optional properties as well as their content in the given definition. It returns zero on success and non-zero otherwise. */ static int checker_validate_properties (struct definition_t * root, struct definition_t * def, struct define_t * available) { struct pair_t * pair; int i, n, errors = 0; /* check whether the required properties are given */ for (i = 0; PROP_IS_PROP (available->required[i]); i++) { n = checker_find_property (available->required[i].key, def->pairs); if (n != 1) { logprint (LOG_ERROR, "line %d: checker error, required property " "`%s' occurred %dx in `%s:%s'\n", def->line, available->required[i].key, n, def->type, def->instance); errors++; } } /* check whether the optional properties are given zero/once */ for (i = 0; PROP_IS_PROP (available->optional[i]); i++) { n = checker_find_property (available->optional[i].key, def->pairs); if (n >= 2) { logprint (LOG_ERROR, "line %d: checker error, optional property " "`%s' occurred %dx in `%s:%s'\n", def->line, available->optional[i].key, n, def->type, def->instance); errors++; } } /* check the property content */ for (pair = def->pairs; pair != NULL; pair = pair->next) { /* check whether properties are either required or optional */ int type = checker_is_property (available, pair->key); if (type == PROP_NONE) { if (strcmp (def->type, "Def")) { logprint (LOG_ERROR, "line %d: checker error, extraneous property `%s' is " "invalid in `%s:%s'\n", def->line, pair->key, def->type, def->instance); errors++; } } // do not check zero-length lists if (pair->value != NULL) { /* check and evaluate the unit scale in a property */ if (!checker_evaluate_scale (pair->value)) errors++; /* check whether properties are in range */ if (!checker_value_in_range (def->instance, available, pair)) { errors++; } /* check variables in properties */ if (!checker_resolve_variable (root, def, pair, type)) errors++; } } return errors; } /* This function is used by the netlist checker to validate the subcircuits. It returns zero with no errors and non-zero on errors. */ static int checker_validate_subcircuits (struct definition_t * root) { int errors = 0; // go through list of definitions for (struct definition_t * def = root; def != NULL; def = def->next) { // check subcircuit instances if (!strcmp (def->type, "Sub")) { struct value_t * val; // validate the 'Type' reference if ((val = checker_validate_reference (def, "Type")) == NULL) { errors++; } else { // find an appropriate subcircuit type struct definition_t * sub = checker_find_subcircuit (val->ident); if (sub == NULL) { logprint (LOG_ERROR, "line %d: checker error, no such subcircuit " "`%s' found as referred in `%s:%s'\n", def->line, val->ident, def->type, def->instance); errors++; } else { // check the number of nodes of the instance and the type int n1 = checker_count_nodes (def); int n2 = checker_count_nodes (sub); if (n1 != n2) { logprint (LOG_ERROR, "line %d: checker error, subcircuit type " "`%s' requires %d nodes in `%s:%s', found %d\n", def->line, sub->instance, n2, def->type, def->instance, n1); errors++; } // check the subcircuit instance properties struct define_t * available = netlist_create_define (sub); errors += checker_validate_properties (root, def, available); netlist_free_define (available); // and finally check for cyclic definitions strlist * deps = new strlist (); int err = checker_validate_sub_cycles (sub, sub->instance, def->instance, &deps); errors += err; checker_sub_cycles = err; delete deps; } } } } return errors; } /* Deletes node list of a definition. */ static void netlist_free_nodes (struct node_t * node) { struct node_t * n; for (; node != NULL; node = n) { n = node->next; free (node->node); free (node); } } /* The following function free()'s the given value. */ static void netlist_free_value (struct value_t * value) { free (value->ident); if (value->unit) free (value->unit); free (value->scale); free (value); } /* Deletes pair list of a definition. */ static void netlist_free_pairs (struct pair_t * pp) { struct pair_t * np; for (; pp != NULL; pp = np) { np = pp->next; struct value_t * nv, * value; for (value = pp->value; value != NULL; value = nv) { nv = value->next; netlist_free_value (value); } free (pp->key); free (pp); } } /* Deletes the given definition. */ static void netlist_free_definition (struct definition_t * def) { netlist_free_nodes (def->nodes); if (!def->copy) netlist_free_pairs (def->pairs); free (def->subcircuit); free (def->type); free (def->instance); free (def); } /* The function removes the given definition 'cand' from the definition root. It returns the new definition root. */ struct definition_t * netlist_unchain_definition (struct definition_t * root, struct definition_t * cand) { struct definition_t * prev; if (cand == root) { root = cand->next; netlist_free_definition (cand); } else { // find previous to the candidate to be deleted for (prev = root; prev != NULL && prev->next != cand; prev = prev->next) ; if (prev != NULL) { prev->next = cand->next; netlist_free_definition (cand); } } return root; } /* The function expands the subcircuits within the given definition list and returns the expanded list with the subcircuit definitions removed. */ static struct definition_t * checker_expand_subcircuits (struct definition_t * root, environment * parent) { struct definition_t * def, * sub, * copy, * next, * prev; strlist * instances = NULL; // go through the list of definitions for (prev = NULL, def = root; def != NULL; def = next) { next = def->next; // is this a subcircuit instance definition ? if (!strcmp (def->type, "Sub")) { // get the subcircuit type definition sub = checker_get_subcircuit (def); // and make a copy of it copy = checker_copy_subcircuits (sub, def, &instances, parent); if (instances) { delete instances; instances = NULL; } // remove the subcircuit instance from the original list if (prev) { prev->next = next; } else { root = next; } netlist_free_definition (def); // put the expanded definitions into the netlist if (copy) { struct definition_t * last = checker_find_last_definition (copy); last->next = root; if (!prev) prev = last; root = copy; } } // component in the root environment else { prev = def; def->env = parent; } } return root; } /* This function is the checker routine for a parsed netlist. It returns zero on success or non-zero if the parsed netlist contained errors. */ static int netlist_checker_intern (struct definition_t * root) { struct definition_t * def; struct define_t * available; int n, errors = 0; /* go through all definitions */ for (def = root; def != NULL; def = def->next) { /* check whether the definition type is known */ available = checker_find_definition (def->type, def->action); if (available == NULL) { logprint (LOG_ERROR, "line %d: checker error, invalid definition type " "`%s'\n", def->line, def->type); errors++; } else { /* mark nodeset definitions */ def->nodeset = !strcmp (def->type, "NodeSet") ? 1 : 0; /* mark nonlinear circuit definitions */ def->nonlinear = available->nonlinear; /* mark substrate definitions */ def->substrate = available->substrate; /* save available definition */ def->define = available; /* check whether the number of nodes is correct and save the number of given nodes */ n = def->ncount = checker_count_nodes (def); if (available->nodes == PROP_NODES) { if (n < 1) { logprint (LOG_ERROR, "line %d: checker error, at least 1 node required in " "`%s:%s', found %d\n", def->line, def->type, def->instance, n); errors++; } } else if (available->nodes != n) { logprint (LOG_ERROR, "line %d: checker error, %d node(s) required in `%s:%s', " "found %d\n", def->line, available->nodes, def->type, def->instance, n); errors++; } /* check the properties except for subcircuits */ if (strcmp (def->type, "Sub")) { errors += checker_validate_properties (root, def, available); } } /* check the number of definitions */ n = checker_count_definition (root, def->type, def->instance); if (n != 1 && def->duplicate == 0) { logprint (LOG_ERROR, "checker error, found %d definitions of `%s:%s'\n", n, def->type, def->instance); errors++; } } /* check microstrip definitions */ errors += checker_validate_strips (root); /* check subcircuit definitions */ errors += checker_validate_subcircuits (root); /* check nodeset definitions */ errors += checker_validate_nodesets (root); return errors; } #if DEBUG /* Debug function: Prints value representation. */ static void netlist_list_value (struct value_t * value) { if (value == NULL) logprint (LOG_STATUS, "[]"); else if (value->ident) logprint (LOG_STATUS, "%s", value->ident); else if (value->next) { logprint (LOG_STATUS, "["); for (; value != NULL; value = value->next) logprint (LOG_STATUS, "%g%s", value->value, value->next ? ";" : ""); logprint (LOG_STATUS, "]"); } else { logprint (LOG_STATUS, "%g", value->value); if (value->scale) logprint (LOG_STATUS, "%s", value->scale); if (value->unit) logprint (LOG_STATUS, "%s", value->unit); } } /* Debug function: Prints definition list representation. */ static void netlist_lister (struct definition_t * root, const char * prefix) { struct definition_t * def; struct node_t * node; struct pair_t * pair; for (def = root; def != NULL; def = def->next) { logprint (LOG_STATUS, "%s%s:%s", prefix, def->type, def->instance); for (node = def->nodes; node != NULL; node = node->next) { logprint (LOG_STATUS, " %s", node->node); } for (pair = def->pairs; pair != NULL; pair = pair->next) { logprint (LOG_STATUS, " %s=\"", pair->key); netlist_list_value (pair->value); logprint (LOG_STATUS, "\""); } logprint (LOG_STATUS, "\n"); } } /* Debug function: Prints the overall netlist representation. */ void netlist_list (void) { struct definition_t * def; logprint (LOG_STATUS, "subcircuit %s\n", "root"); netlist_lister (definition_root, " "); for (def = subcircuit_root; def != NULL; def = def->next) { logprint (LOG_STATUS, "subcircuit %s\n", def->instance); netlist_lister (def->sub, " "); } } #endif /* DEBUG */ /* The function logs the content of the current netlist by telling how many instances of which kind of components are used in the netlist. */ void netlist_status (void) { struct define_t * def; struct definition_t * cir; int count; logprint (LOG_STATUS, "netlist content\n"); hashiterator it; for (it = hashiterator (module::modules); *it; ++it) { def = it.currentVal()->definition; for (count = 0, cir = definition_root; cir != NULL; cir = cir->next) { if (!strcmp (def->type, cir->type)) count++; } if (count > 0) { logprint (LOG_STATUS, " %5d %s instances\n", count, def->type); } } } /* The function builds the equation list for a given list of definition and removes the definition containing the equations from the list. */ static struct definition_t * checker_build_equations (struct definition_t * root, eqn::node ** eroot) { struct definition_t * def, * next, * prev; eqn::node * eqns, * last; *eroot = NULL; // go through list of definitions for (prev = NULL, def = root; def != NULL; def = next) { next = def->next; if (!strcmp (def->type, "Eqn")) { // rechain definition list if (prev) { prev->next = next; } else { root = next; } // append equations eqns = (eqn::node *) def->eqns; last = eqn::checker::lastEquation (eqns); last->setNext (*eroot); *eroot = eqns; // free this definition netlist_free_definition (def); } else prev = def; } return root; } /* The function creates an environment for the given definition root including equation checker and solver. */ static void checker_setup_env (struct definition_t * root, environment * env, eqn::node * eqns) { // create equation checker eqn::checker * checkee = new eqn::checker (); // pass equations to the checker checkee->setEquations (eqns); // add constants to the list of equations checkee->constants (); // pass checker env->setChecker (checkee); // create equation solver eqn::solver * solvee = new eqn::solver (checkee); // pass solver env->setSolver (solvee); // apply environment to the netlist root if (root) root->env = env; } /* Adds the arguments of a subcircuit into the equation checker of the given environment. */ static void checker_subcircuit_args (struct definition_t * def, environment * env) { for (struct pair_t * pair = def->pairs; pair != NULL; pair = pair->next) { // anything else than the 'Type' if (strcmp (pair->key, "Type")) { // put it into the equation checker env->getChecker()->addDouble ("#subcircuit", pair->key, pair->value->value); // also put it into the environment variable * v = checker_add_variable (env, pair->key, TAG_DOUBLE, true); v->getConstant()->d = pair->value->value; } } } /* This is the global netlist checker. It returns zero on success and non-zero on errors. */ int netlist_checker (environment * env) { int errors = 0; eqn::node * eqns; struct definition_t * def; // create top-level environment env_root = new environment (env->getName ()); // create the subcircuit list definition_root = checker_build_subcircuits (definition_root); // get equation list definition_root = checker_build_equations (definition_root, &eqns); // setup the root environment checker_setup_env (definition_root, env_root, eqns); // check list of subcircuits errors += netlist_checker_intern (subcircuit_root); // check global netlist errors += netlist_checker_intern (definition_root); // check equations in root env_root->setDefinitions (definition_root); errors += env_root->equationChecker (0); env_root->setDefinitions (NULL); // then check each subcircuit list for (def = subcircuit_root; def != NULL; def = def->next) { // get equation list def->sub = checker_build_equations (def->sub, &eqns); // setup the subcircuit environment environment * subenv = new environment (def->instance); env_root->push_front_Child (subenv); checker_setup_env (def, subenv, eqns); if (def->sub) def->sub->env = subenv; // add subcircuit parameters to equations checker_subcircuit_args (def, subenv); // check subcircuit netlist errors += netlist_checker_intern (def->sub); // check equations in subcircuit subenv->setDefinitions (def->sub); errors += subenv->equationChecker (0); subenv->setDefinitions (NULL); } // check actions errors += checker_validate_actions (definition_root); if (!errors) { // create actual root environment env->copy (*env_root); // and finally expand the subcircuits into the global netlist definition_root = checker_expand_subcircuits (definition_root, env); } return errors ? -1 : 0; } /* The function deletes the given definition list. */ static void netlist_destroy_intern (struct definition_t * root) { struct definition_t * def, * next; for (def = root; def != NULL; def = next) { next = def->next; netlist_free_definition (def); } } /* Deletes all available definition lists. */ void netlist_destroy (void) { netlist_destroy_intern (definition_root); for (struct definition_t * def = subcircuit_root; def; def = def->next) { netlist_destroy_intern (def->sub); } netlist_destroy_intern (subcircuit_root); definition_root = subcircuit_root = NULL; netlist_lex_destroy (); } /* Delete root environment(s) if necessary. */ void netlist_destroy_env (void) { if (env_root != NULL) { delete env_root; env_root = NULL; } }