diff options
Diffstat (limited to 'src/engines/netlist.c')
-rw-r--r-- | src/engines/netlist.c | 689 |
1 files changed, 689 insertions, 0 deletions
diff --git a/src/engines/netlist.c b/src/engines/netlist.c new file mode 100644 index 0000000..05c370f --- /dev/null +++ b/src/engines/netlist.c @@ -0,0 +1,689 @@ +/* + * netlist.c + * + * + * Author: + * Richard Hult <rhult@hem.passagen.se> + * Ricardo Markiewicz <rmarkie@fi.uba.ar> + * Andres de Barbara <adebarbara@fi.uba.ar> + * + * Web page: http://arrakis.lug.fi.uba.ar/ + * + * Copyright (C) 1999-2001 Richard Hult + * Copyright (C) 2003,2006 Ricardo Markiewicz + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <gnome.h> +#include <stdio.h> +#include <string.h> +#include <string.h> +#include "main.h" +#include "schematic.h" +#include "node-store.h" +#include "node.h" +#include "wire.h" +#include "part-private.h" +#include "part-property.h" +#include "netlist.h" +#include "errors.h" + +static void netlist_helper_node_foreach_reset (gpointer key, gpointer value, gpointer user_data); +static void netlist_helper_wire_traverse (Wire *wire, NetlistData *data); +static void netlist_helper_node_traverse (Node *node, NetlistData *data); +static void netlist_helper_node_foreach_traverse (gpointer key, gpointer value, NetlistData *data); +static gboolean foreach_model_free (gpointer key, gpointer model, gpointer user_data); +static gboolean foreach_model_save (gpointer key, gpointer model, gpointer user_data); +static char *linebreak (char *str); +static void nl_node_traverse (Node *node, GSList **lst); + +static void +nl_wire_traverse (Wire *wire, GSList **lst) +{ + GSList *nodes; + + g_return_if_fail (wire != NULL); + g_return_if_fail (IS_WIRE (wire)); + + if (wire_is_visited (wire)) + return; + + wire_set_visited (wire, TRUE); + + for (nodes = wire_get_nodes (wire); nodes; nodes = nodes->next) { + GSList *pins; + Part *part; + Node *node = nodes->data; + + for(pins=node->pins; pins; pins=pins->next) { + char *template, *tmp; + char **template_split; + + part = PART (((Pin *)pins->data)->part); + + tmp = part_get_property (part, "template"); + + if (!tmp) continue; + + template = part_property_expand_macros (part, tmp); + + template_split = g_strsplit (template, " ", 0); + + (*lst) = g_slist_prepend (*lst, g_strdup (template_split[0])); + + g_strfreev (template_split); + g_free (tmp); + g_free (template); + } + + nl_node_traverse (node, lst); + } +} + +static void +nl_node_traverse (Node *node, GSList **lst) +{ + GSList *wires; + + g_return_if_fail (node != NULL); + g_return_if_fail (IS_NODE (node)); + + if (node_is_visited (node)) + return; + + node_set_visited (node, TRUE); + + for (wires = node->wires; wires; wires = wires->next) { + Wire *wire = wires->data; + nl_wire_traverse (wire, lst); + } +} + +static GSList * +netlist_helper_get_clamp_parts (NodeStore *store, Node *node) +{ + GList *wires; + GSList *lst; + Wire *wire; + GSList *ret=NULL; + + if (!node) return NULL; + + node_store_node_foreach (store, (GHFunc *)netlist_helper_node_foreach_reset, NULL); + for (wires = store->wires; wires; wires = wires->next) { + wire = wires->data; + wire_set_visited (wire, FALSE); + } + + lst = node->wires; + while (lst) { + nl_wire_traverse (lst->data, &ret); + lst = lst->next; + } + + return ret; +} + +void +netlist_helper_init_data (NetlistData *data) +{ + data->pins = g_hash_table_new (g_direct_hash, g_direct_equal); + data->models = g_hash_table_new (g_str_hash, g_str_equal); + data->node_nr = 1; + data->gnd_list = NULL; + data->clamp_list = NULL; + data->mark_list = NULL; + data->node_and_number_list = NULL; +} + +void +netlist_helper_node_foreach_reset (gpointer key, gpointer value, gpointer user_data) +{ + Node *node = value; + + node_set_visited (node, FALSE); +} + +void +netlist_helper_wire_traverse (Wire *wire, NetlistData *data) +{ + GSList *nodes; + + g_return_if_fail (wire != NULL); + g_return_if_fail (IS_WIRE (wire)); + + if (wire_is_visited (wire)) + return; + + wire_set_visited (wire, TRUE); + + for (nodes = wire_get_nodes (wire); nodes; nodes = nodes->next) { + Node *node = nodes->data; + + netlist_helper_node_traverse (node, data); + } +} + +void +netlist_helper_node_traverse (Node *node, NetlistData *data) +{ + GSList *wires, *pins; + gchar *prop; + NodeAndNumber *nan; + + g_return_if_fail (node != NULL); + g_return_if_fail (IS_NODE (node)); + + if (node_is_visited (node)) + return; + + node_set_visited (node, TRUE); + + /* Keep track of netlist nr <---> Node. */ + nan = g_new0 (NodeAndNumber, 1); + nan->node_nr = data->node_nr; + nan->node = node; + data->node_and_number_list = g_list_prepend (data->node_and_number_list, + nan); + + /* Traverse the pins at this node. */ + for (pins = node->pins; pins; pins = pins->next) { + Pin *pin = pins->data; + + /* First see if the pin belongs to an "internal", special part. */ + prop = part_get_property (pin->part, "internal"); + if (prop) { + if (!g_strcasecmp (prop, "marker")) { + Marker *marker; + gchar *name, *value; + + name = part_get_property (pin->part, "name"); + + if (!name) { + g_free (prop); + continue; + } + + value = part_property_expand_macros (pin->part, name); + g_free (name); + + if (!value) + continue; + + marker = g_new0 (Marker, 1); + marker->node_nr = data->node_nr; + marker->name = value; + data->mark_list = g_slist_prepend (data->mark_list, marker); + } else if (!g_strcasecmp (prop, "ground")) { + data->gnd_list = g_slist_prepend (data->gnd_list, GINT_TO_POINTER (data->node_nr)); + } else if (!g_strcasecmp (prop, "point")) { + data->clamp_list = g_slist_prepend (data->clamp_list, GINT_TO_POINTER (data->node_nr)); + } else if (!g_strncasecmp (prop, "jumper", 5)) { + /* Either jumper2 or jumper4. */ + Node *opposite_node; + Pin opposite_pin; + gint pin_nr, opposite_pin_nr; + SheetPos pos; + Pin *jumper_pins; + gint num_pins; + + opposite_pin_nr = -1; + num_pins = part_get_num_pins (pin->part); + jumper_pins = part_get_pins (pin->part); + for (pin_nr = 0; pin_nr < num_pins; pin_nr++) { + if (&jumper_pins[pin_nr] == pin) { + opposite_pin_nr = pin_nr; + break; + } + } + + switch (opposite_pin_nr) { + case 0: + opposite_pin_nr = 1; + break; + case 1: + opposite_pin_nr = 0; + break; + case 2: + opposite_pin_nr = 3; + break; + case 3: + opposite_pin_nr = 2; + break; + default: + g_assert (TRUE); + break; + } + + opposite_pin = jumper_pins[opposite_pin_nr]; + + item_data_get_pos (ITEM_DATA (pin->part), &pos); + pos.x += opposite_pin.offset.x; + pos.y += opposite_pin.offset.y; + + opposite_node = node_store_get_node (data->store, pos); + +#if 0 + if (node_is_visited (opposite_node)) { + GList *list; + + /* Set the node name on the current node to the same as the + already visited node. */ + for (list = data->node_and_number_list; list; list = list->next) { + NodeAndNumber *opposite_nan = list->data; + + if (opposite_nan->node == opposite_node) + nan->node_nr = opposite_nan->node_nr; + } + } +#endif + netlist_helper_node_traverse (opposite_node, data); + } + + if (g_strcasecmp (prop, "point")) { + g_free (prop); + continue; + } + g_free(prop); + } + + /* Keep track of models to include. Needs to be freed when the + * hash table is no longer needed. + */ + prop = part_get_property (pin->part, "model"); + if (prop) { + if (!g_hash_table_lookup (data->models, prop)) + g_hash_table_insert (data->models, prop, NULL); + } + + g_hash_table_insert (data->pins, pin, GINT_TO_POINTER (data->node_nr)); + } + + /* Traverse the wires at this node. */ + for (wires = node->wires; wires; wires = wires->next) { + Wire *wire = wires->data; + netlist_helper_wire_traverse (wire, data); + } +} + +void +netlist_helper_node_foreach_traverse (gpointer key, gpointer value, NetlistData *data) +{ + Node *node = value; + + /* Only visit nodes that are not already visited. */ + if (node_is_visited (node)) + return; + + netlist_helper_node_traverse (node, data); + + data->node_nr++; +} + +gint +compare_marker (gconstpointer a, gconstpointer b) +{ + const Marker *ma; + gint node_nr; + + ma = a; + node_nr = GPOINTER_TO_INT (b); + + if (ma->node_nr == node_nr) + return 0; + else + return 1; +} + +gboolean +foreach_model_save (gpointer key, gpointer model, gpointer user_data) +{ + GList **l = (GList **)user_data; + + (*l) = g_list_prepend (*l, g_strdup ((gchar *)key)); + + return TRUE; +} + +gboolean +foreach_model_free (gpointer key, gpointer model, gpointer user_data) +{ + g_free (key); + + return FALSE; +} + +char * +linebreak (char *str) +{ + char **split, *tmp; + GString *out; + int i; + + split = g_strsplit (str, "\\", 0); + + out = g_string_new (""); + + i = 0; + while (split[i] != NULL) { + if (split[i][0] == 'n') { + if (strlen (split[i]) > 1) { + out = g_string_append_c (out, '\n'); + out = g_string_append (out, split[i] + 1); + } + } else { + out = g_string_append (out, split[i]); + } + + i++; + } + + g_strfreev (split); + tmp = out->str; + g_string_free (out, FALSE); /* Don't free the string data. */ + return tmp; +} + +void +netlist_helper_create (Schematic *sm, Netlist *out, GError **error) +{ + NetlistData data; + GList *parts, *wires, *list; + Part *part; + gint pin_nr, num_pins, num_nodes, num_gnd_nodes, i, j, num_clamps; + Pin *pins; + gchar *template, **template_split; + NodeStore *store; + gchar **node2real; + + schematic_log_clear (sm); + + out->models = NULL; + out->title = schematic_get_filename (sm); + out->settings = schematic_get_sim_settings (sm); + store = schematic_get_store (sm); + out->store = store; + node_store_node_foreach (store, (GHFunc *)netlist_helper_node_foreach_reset, NULL); + for (wires = store->wires; wires; wires = wires->next) { + Wire *wire = wires->data; + wire_set_visited (wire, FALSE); + } + + netlist_helper_init_data (&data); + data.store = store; + + node_store_node_foreach (store, (GHFunc *)netlist_helper_node_foreach_traverse, &data); + + num_gnd_nodes = g_slist_length (data.gnd_list); + num_clamps = g_slist_length (data.clamp_list); + + /* Check if there is a Ground node */ + if (num_gnd_nodes == 0) { + schematic_log_append (sm, _("No ground node. Aborting.\n")); + schematic_log_show (sm); + g_set_error (error, + OREGANO_ERROR, + OREGANO_SIMULATE_ERROR_NO_GND, + _("Possibly due to a faulty circuit schematic. Please check that\n" + "you have a ground node and try again.") + ); + + /* FIXME!!! */ + goto bail_out; + } + + if (num_clamps == 0) { + schematic_log_append (sm, _("No test clamps found. Aborting.\n")); + schematic_log_show (sm); + g_set_error (error, + OREGANO_ERROR, + OREGANO_SIMULATE_ERROR_NO_CLAMP, + _("Possibly due to a faulty circuit schematic. Please check that\n" + "you have one o more test clamps and try again.") + ); + + goto bail_out; + } + + num_nodes = data.node_nr - 1; + + /* + * Build an array for going from node nr to "real node nr", + * where gnd nodes are nr 0 and the rest of the nodes are + * 1, 2, 3, ... + */ + node2real = g_new0 (gchar*, num_nodes + 1); + + for (i = 1, j = 1; i <= num_nodes; i++) { + GSList *mlist; + + if (g_slist_find (data.gnd_list, GINT_TO_POINTER (i))) + node2real[i] = g_strdup ("0"); + else if ((mlist = g_slist_find_custom (data.mark_list, + GINT_TO_POINTER (i), + compare_marker))) { + Marker *marker = mlist->data; + node2real[i] = g_strdup (marker->name); + } + else node2real[i] = g_strdup_printf ("%d", j++); + } + + + /* + * Fill in the netlist node names for all the used nodes. + */ + for (list = data.node_and_number_list; list; list = list->next) { + NodeAndNumber *nan = list->data; + if (nan->node->netlist_node_name != NULL) + g_free (nan->node->netlist_node_name); + if (nan->node_nr != 0) + nan->node->netlist_node_name = g_strdup (node2real[nan->node_nr]); + } + + /* Initialize out->template */ + out->template = g_string_new(""); + for (parts = store->parts; parts; parts = parts->next) { + gchar *tmp, *internal; + GString *str; + + part = parts->data; + internal = part_get_property (part, "internal"); + if (internal != NULL) { + gint node_nr; + Pin *pins; + if (g_strcasecmp (internal, "point") != 0) { + g_free (internal); + continue; + } + + /* Got a clamp!, set node number */ + pins = part_get_pins (part); + node_nr = GPOINTER_TO_INT (g_hash_table_lookup (data.pins, &pins[0])); + if (!node_nr) { + g_warning ("Couln't find part, pin_nr %d.", 0); + } else { + gchar *tmp; + tmp = node2real[node_nr]; + + /* need to substrac 1, netlist starts in 0, and node_nr in 1 */ + pins[0].node_nr = atoi(node2real[node_nr]); + } + g_free (internal); + continue; + } + + tmp = part_get_property (part, "template"); + if (!tmp) { + continue; + } + + template = part_property_expand_macros (part, tmp); + //g_print ("Template: '%s'\n" "macro : '%s'\n", tmp, template); + if (tmp != NULL) g_free (tmp); + + tmp = linebreak (template); + + if (template != NULL) g_free (template); + template = tmp; + + num_pins = part_get_num_pins (part); + pins = part_get_pins (part); + + template_split = g_strsplit (template, " ", 0); + if (template != NULL) g_free (template); + template = NULL; + + str = g_string_new (""); + + i = 0; + while (template_split[i] != NULL && template_split[i][0] != '%') { + g_string_append (str, template_split[i++]); + g_string_append_c (str , ' '); + //g_print ("str: %s\n", str->str); + } + + //g_print ("Reading %d pins.\n)", num_pins); + + for (pin_nr = 0; pin_nr < num_pins; pin_nr++) { + gint node_nr; + node_nr = GPOINTER_TO_INT (g_hash_table_lookup (data.pins, &pins[pin_nr])); + if (!node_nr) { + g_warning ("Couldn't find part, pin_nr %d.", pin_nr); + } else { + gchar *tmp; + tmp = node2real[node_nr]; + + /* need to substrac 1, netlist starts in 0, and node_nr in 1 */ + pins[pin_nr].node_nr = atoi(node2real[node_nr]); + g_string_append (str, tmp); + g_string_append_c (str, ' '); + /*g_print ("str: %s\n", str->str);*/ + i++; + } + + while (template_split[i] != NULL) { + if (template_split[i][0] == '%') + break; + + g_string_append (str, template_split[i]); + g_string_append_c (str, ' '); + //g_print ("str: %s\n", str->str); + i++; + } + } + + //g_print ("Done with pins, i = %d\n", i); + + while (template_split[i] != NULL) { + g_string_append (str, template_split[i]); + g_string_append_c (str, ' '); + //g_print ("str: %s\n", str->str); + i++; + } + + g_strfreev (template_split); + //g_print ("str: %s\n", str->str); + out->template = g_string_append(out->template, str->str); + out->template = g_string_append_c(out->template, '\n'); + g_string_free (str, TRUE); + } + + for (i = 0; i < num_nodes + 1; i++) { + g_free (node2real[i]); + } + g_free (node2real); + + g_hash_table_foreach (data.models, (GHFunc)foreach_model_save, &out->models); + + return; + +bail_out: + g_hash_table_foreach (data.models, (GHFunc)foreach_model_free, NULL); + g_hash_table_destroy (data.models); + g_hash_table_destroy (data.pins); + for (list = data.node_and_number_list; list; list = list->next) { + NodeAndNumber *nan = list->data; + if (nan != NULL) g_free (nan); + } + g_list_free (data.node_and_number_list); +} + +char * +netlist_helper_create_analisys_string (NodeStore *store, gboolean do_ac) +{ + GList *parts; + GList *p; + gchar *prop, *type, *ac; + parts = node_store_get_parts (store); + GString *out; + gchar *ret; + + out = g_string_new (""); + + for(p=parts; p; p = p->next) { + prop = part_get_property (p->data, "internal"); + if (prop) { + if (!g_strcasecmp (prop, "point")) { + Pin *pins = part_get_pins (p->data); + g_free (prop); + + prop = part_get_property (p->data, "type"); + + if (!g_strcasecmp (prop, "v")) { + if (!do_ac) { + g_string_append_printf (out, " %s(%d)", prop, pins[0].node_nr); + } else { + gchar *ac_type, *ac_db; + ac_type = part_get_property (p->data, "ac_type"); + ac_db = part_get_property (p->data, "ac_db"); + + if (!g_strcasecmp (ac_db, "true")) + g_string_append_printf (out, " %s%sdb(%d)", prop, ac_type, pins[0].node_nr); + else + g_string_append_printf (out, " %s%s(%d)", prop, ac_type, pins[0].node_nr); + } + } else { + Node *node; + SheetPos lookup_key; + SheetPos part_pos; + + item_data_get_pos (ITEM_DATA (p->data), &part_pos); + + lookup_key.x = part_pos.x + pins[0].offset.x; + lookup_key.y = part_pos.y + pins[0].offset.y; + + node = node_store_get_or_create_node (store, lookup_key); + + if (node) { + GSList *lst, *it; + it = lst = netlist_helper_get_clamp_parts (store, node); + while (it) { + g_string_append_printf (out, " i(%s)", (char *)it->data); + it = it->next; + } + g_slist_free (lst); + } + } + } + } + } + + ret = out->str; + g_string_free (out, FALSE); + return ret; +} + |