/* * COPYRIGHT * * cschem - modular/flexible schematics editor - libcschem (core library) * Copyright (C) 2018,2019,2022..2024 Tibor 'Igor2' Palinkas * * (Supported by NLnet NGI0 PET Fund in 2022, Entrust in 2023) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version.* * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 31 Milk Street, # 960789 Boston, MA 02196 USA * * Contact: * Project page: http://repo.hu/projects/sch-rnd * contact lead developer: http://www.repo.hu/projects/sch-rnd/contact.html * mailing list: http://www.repo.hu/projects/sch-rnd/contact.html */ #include "config.h" #include #include #include "abstract.h" #include "actions_csch.h" #include "concrete.h" #include "event.h" #include "cnc_line.h" #include "cnc_arc.h" #include "cnc_poly.h" #include "cnc_text.h" #include "cnc_bitmap.h" #include "cnc_conn.h" #include "cnc_grp.h" #include "cnc_pen.h" #include "cnc_obj.h" #include "engine.h" #include "project.h" #include "util_compile.h" #include "util_project.h" #include "libcschem.h" #include "non_graphical.h" #include "hierarchy.h" #include "compile.h" int csch_compile_attribute(csch_ahdr_t *dst, const char *dstkey, const csch_attrib_t *srca, csch_source_arg_t *src, const char *err1, const char *err2, const char *err3, int referee, int append, csch_attrib_t **dsta_out) { csch_attrib_t *dsta = NULL; const char *key = (dstkey == NULL ? srca->key : dstkey); const char *sval, *dval; if (err1 == NULL) err1 = ""; if (err2 == NULL) err2 = ""; if (err3 == NULL) err3 = ""; if (key[0] == '-') { if (dsta_out != NULL) *dsta_out = NULL; csch_attr_src_free(src); return 0; } if (key[0] == '+') { key++; append = 1; } dsta = csch_attrib_get(&dst->attr, key); if (dsta_out != NULL) *dsta_out = dsta; sval = srca == NULL ? NULL : srca->val; dval = dsta == NULL ? NULL : dsta->val; /* Hierarchic special casing: remove hierarchic prefix from the name attribute so it doesn't cause conflicts, e.g. when a v/foo is merged with a matching ^/foo */ if ((key[0] == 'n') || (key[1] == 'a') || (key[2] == 'm') || (key[3] == 'e') || (key[4] == '\0')) { if (sval != NULL) csch_split_name_scope(&sval); if (dval != NULL) csch_split_name_scope(&dval); } /* attribute merging logic */ if (sval != NULL) { if (append) return csch_attrib_append_val(&dst->attr, srca->prio, key, srca, src); if (dsta != NULL) { if (dval != NULL) { if (strcmp(sval, dval) == 0) { /* both set, same value, remember lowest prio */ if (srca->prio >= dsta->prio) { csch_attrib_append_src(dsta, srca->prio, src, 1); csch_attr_src_free(src); return 0; /* ignore new */ } } else { /* different values, look at prios */ if (srca->prio > dsta->prio) { csch_attrib_append_src(dsta, srca->prio, src, 1); csch_attr_src_free(src); return 0; /* lower prio, ignore */ } if (srca->prio == dsta->prio) { if (!referee) /* it's okay if a group ref's referee had it - we are overwriting it from the group ref */ rnd_message(RND_MSG_ERROR, "compile_attributes(): %s %s %s: attribute collision for '%s'\n", err1, err2, err3, srca->key); csch_attr_src_free(src); return -1; } /* else set */ } } } csch_attrib_set(&dst->attr, srca->prio, key, sval, src, dsta_out); } else { if (append) { if ((dsta != NULL) && (dval != NULL)) { csch_attr_src_free(src); rnd_message(RND_MSG_ERROR, "compile_attributes(): %s %s %s: can not append to destination attribute '%s', it is a string not an array\n", err1, err2, err3, key); return -1; } return csch_attrib_append_val(&dst->attr, srca->prio, key, srca, src); } else csch_attrib_set_arr(&dst->attr, srca->prio, key, &srca->arr, src, dsta_out); } return 0; } /* Copy all sources from srca in front of the sources in dst */ static void compile_attribute_copy_srcs_front(csch_attrib_t *dst, const csch_attrib_t *srca) { long n; if (srca->source.used <= 0) return; vts0_alloc_insert(&dst->source, 0, srca->source.used); for(n = 0; n < srca->source.used; n++) dst->source.array[n] = rnd_strdup(srca->source.array[n]); } static int compile_attributes(csch_ahdr_t *dst, const csch_attribs_t *src, csch_cgrp_t *cgrp, const char *err1, const char *err2, const char *err3, int referee) { htsp_entry_t *e; int res = 0, r1; for(e = htsp_first(src); e; e = htsp_next(src, e)) { const csch_attrib_t *srca = e->value; csch_source_arg_t *src = csch_attrib_src_ac(&cgrp->hdr, srca->key, "compile_attributes()"); csch_attrib_t *dsta; r1 = csch_compile_attribute(dst, NULL, srca, src, err1, err2, err3, referee, 0, &dsta); if ((r1 == 0) && (dsta != NULL)) compile_attribute_copy_srcs_front(dsta, srca); res |= r1; } TODO("this should return res"); return 0; } static int compile_wire_net(csch_abstract_t *dst, int viewid, csch_hier_path_t *hpath, const csch_sheet_t *sheet, csch_cgrp_t *src) { char tmpname[128]; const char *orig_name; char *name_glob = NULL, *name_loc = NULL; const csch_attrib_t *aname; csch_anet_t *net; int no_uname = 0; csch_ascope_t scope; aname = csch_attrib_get(&src->attr, "name"); dst->ucnt.wirenet++; if ((aname == NULL) || (aname->key == NULL) || (*aname->key == '\0')) { sprintf(tmpname, "anon_net_%ld", dst->ucnt.wirenet); orig_name = tmpname; no_uname = 1; } else orig_name = aname->val; scope = csch_split_name_scope(&orig_name); /* Translate name, including hierarchical address prefixes */ csch_eng_call_namemod((csch_project_t *)sheet->hidlib.project, viewid, hpath, CSCH_ENGHK_WIRENET_NAME_TO_NET_NAME, &name_glob, &name_loc, orig_name, FGW_COBJ, src, FGW_HPATH, hpath, FGW_INVALID); if (name_glob == NULL) { rnd_message(RND_MSG_ERROR, "Internal error: failed to perform wire_net name translation\n"); return -1; } net = csch_anet_get_at(dst, hpath->hlev, scope, name_loc); if (net == NULL) { net = csch_anet_new(dst, hpath->hlev, scope, name_glob, name_loc, no_uname); if (net == NULL) { csch_eng_free_namemod(&name_glob, &name_loc); return -1; } } csch_eng_free_namemod(&name_glob, &name_loc); csch_compile_add_source(src, &net->hdr); return compile_attributes(&net->hdr, &src->attr, src, "net", orig_name, NULL, 0); } static csch_aport_t *compile_port(csch_abstract_t *dst, int viewid, csch_hier_path_t *hpath, const csch_sheet_t *sheet, csch_acomp_t *comp, csch_cgrp_t *t, int hier_down) { char tmpname[128]; const csch_attrib_t *trole, *tname; const char *orig_name; char *name_glob = NULL, *name_loc = NULL; csch_aport_t *port = NULL; if ((t->hdr.type != CSCH_CTYPE_GRP) && (t->hdr.type != CSCH_CTYPE_GRP_REF)) return NULL; if (t->hdr.type == CSCH_CTYPE_GRP_REF) trole = csch_cgrp_ref_get_attr(t, "role"); else trole = csch_attrib_get(&t->attr, "role"); if ((trole == NULL) || (trole->val == NULL) || (strcmp(trole->val, "terminal") != 0)) return NULL; if (t->hdr.type == CSCH_CTYPE_GRP_REF) tname = csch_cgrp_ref_get_attr(t, "name"); else tname = csch_attrib_get(&t->attr, "name"); dst->ucnt.port++; if ((tname == NULL) || (tname->key == NULL) || (*tname->key == '\0')) { sprintf(tmpname, "anon_%ld", dst->ucnt.port); orig_name = tmpname; } else orig_name = tname->val; csch_eng_call_namemod((csch_project_t *)sheet->hidlib.project, viewid, hpath, CSCH_ENGHK_TERMINAL_NAME_TO_PORT_NAME, &name_glob, &name_loc, orig_name, FGW_AOBJ, comp, FGW_COBJ, t, FGW_HPATH, hpath, FGW_INVALID); if (name_glob == NULL) { rnd_message(RND_MSG_ERROR, "Internal error: failed to perform port name translation\n"); return NULL; } port = csch_aport_get(dst, comp, name_glob, 1); if (port == NULL) return NULL; port->parent = comp; csch_compile_add_source(t, &port->hdr); compile_attributes(&port->hdr, &t->attr, t, "port", (comp == NULL ? "" : comp->name), orig_name, 0); csch_eng_free_namemod(&name_glob, &name_loc); return port; } /* Compile a normal (non-sheet-ref) symbol into a component; */ static int compile_symbol_to_component(csch_abstract_t *dst, int viewid, csch_hier_path_t *hpath, csch_ascope_t scope, const csch_sheet_t *sheet, csch_cgrp_t *src, char *name_glob, char *name_loc, const char *orig_name) { htip_entry_t *e; csch_acomp_t *comp; int res = 0; /* don't compile if it had no name - note: at the moment this can't happen (because of anon_comp_%ld) */ if (orig_name == NULL) return 0; comp = csch_acomp_get_at(dst, hpath->hlev, scope, name_loc); if (comp == NULL) comp = csch_acomp_new(dst, hpath->hlev, scope, name_glob, name_loc); if (comp == NULL) return -1; csch_compile_add_source(src, &comp->hdr); res |= compile_attributes(&comp->hdr, &src->attr, src, "component", orig_name, NULL, 0); if ((src->hdr.type == CSCH_CTYPE_GRP_REF) && (src->data.ref.grp != NULL)) res |= compile_attributes(&comp->hdr, &src->data.ref.grp->attr, src->data.ref.grp, "component", orig_name, NULL, 1); csch_eng_call((csch_project_t *)sheet->hidlib.project, viewid, hpath, CSCH_ENGHK_SYMBOL_JOINED_COMPONENT, FGW_COBJ, src, FGW_AOBJ, comp, FGW_HPATH, hpath, FGW_INVALID); /* compile ports */ for(e = htip_first(&src->id2obj); e != NULL; e = htip_next(&src->id2obj, e)) compile_port(dst, viewid, hpath, sheet, comp, e->value, 0); return res; } /* Return is grp is directly on the sheet and not within a symbol on any level */ static int is_grp_a_sheet_port(const csch_sheet_t *sheet, csch_cgrp_t *grp) { csch_cgrp_t *g; for(g = grp->hdr.parent; g != NULL; g = g->hdr.parent) { if (g == &sheet->direct) return 1; if (g->role == CSCH_ROLE_SYMBOL) return 0; } return 1; } /* Hierarchic: search child sheet terminals and return the abstract port that has a matching port name (or NULL if not found or error). Also sets *error_indicated_out to 1 if an error message was printed. */ static csch_aport_t *find_subsheet_port(csch_abstract_t *dst, csch_sheet_t *child, const char *port_name, int *error_indicated_out) { csch_chdr_t *h; csch_aport_t *res = NULL; for(h = gdl_first(&child->active); h != NULL; h = gdl_next(&child->active, h)) { csch_cgrp_t *grp = (csch_cgrp_t *)h; if (h->indirect) continue; if ((h->type != CSCH_CTYPE_GRP) && (h->type != CSCH_CTYPE_GRP_REF)) continue; /* can use enum role here because role attribute overwrite is handled in csch_cgrp_ref_render() */ if (grp->role == CSCH_ROLE_TERMINAL) { if (is_grp_a_sheet_port(child, grp)) { long n; for(n = 0; n < grp->aid.used; n++) { csch_aport_t *aport = htip_get(&dst->aid2obj, grp->aid.array[n]); if ((aport != NULL) && (aport->hdr.type == CSCH_ATYPE_PORT)) { if (strcmp(aport->name, port_name) == 0) { if (res != NULL) { rnd_msg_error("Compilation error: sheet '%s': multiple sheet terminals are called '%s'\n", child->hidlib.loadname, port_name); *error_indicated_out = 1; return NULL; } res = aport; /*go on searching for more so the above error can trigger */ } } } } } } return res; } /* Returns whether a sheet ref sym has parameter pass-down attributes */ static int srs_has_params(csch_cgrp_t *srs) { htsp_entry_t *e; for(e = htsp_first(&srs->attr); e != NULL; e = htsp_next(&srs->attr, e)) if (strncmp(e->key, "cschem/param/", 13) == 0) return 1; return 0; } int csch_compile_descend(csch_abstract_t *dst, int viewid, csch_hier_path_t *hpath, csch_cgrp_t *srs, char *name_loc, csch_sheet_t *child) { int res, restore_attrs = 0; csch_hier_temp_t htmp; csch_attribs_t aorig; /* pass down parameters by temporary switching child to a new attribute hash */ if (srs_has_params(srs)) { htsp_entry_t *e, *old; /* clone orig attrs */ memcpy(&aorig, &child->direct.attr, sizeof(csch_attribs_t)); csch_attrib_init(&child->direct.attr); csch_attrib_copy_all(&child->direct.attr, &aorig); restore_attrs = 1; /* copy the new ones */ for(e = htsp_first(&srs->attr); e != NULL; e = htsp_next(&srs->attr, e)) { if (strncmp(e->key, "cschem/param/", 13) == 0) { const char *new_key = e->key+13; csch_attrib_t *a = csch_attrib_dup_rename(e->value, new_key); old = htsp_popentry(&child->direct.attr, new_key); if (old != NULL) csch_attr_free(e->value); htsp_set(&child->direct.attr, a->key, a); } } } res = csch_hier_enter(hpath, &htmp, srs, name_loc); if (res != 0) return -1; res = csch_compile_sheet_(dst, viewid, hpath, child); csch_hier_leave(hpath, &htmp); /* restore child's old attribute hash (undo parameter passing) */ if (restore_attrs) { csch_attrib_uninit(&child->direct.attr); memcpy(&child->direct.attr, &aorig, sizeof(csch_attribs_t)); } return res; } /* Compile a normal (non-sheet-ref) symbol into a component; use name_tmp if available, else use name_ */ static int compile_symbol_child_sheet(csch_abstract_t *dst, int viewid, csch_hier_path_t *hpath, csch_ascope_t scope, const csch_sheet_t *sheet, csch_cgrp_t *src, char *name_glob, char *name_loc, const char *orig_name, csch_sheet_t *child) { int res; csch_acomp_t *comp; htip_entry_t *e; if (orig_name == NULL) { rnd_message(RND_MSG_ERROR, "Sheet reference symbol in hierarchic design must have a name\n"); return -1; /* comp has no name -> error - can't happen because of anon_comp_%ld */ } comp = csch_acomp_get(dst, name_glob); if (comp == NULL) { comp = csch_acomp_new(dst, hpath->hlev, CSCH_ASCOPE_SHEET_LOCAL, name_glob, name_loc); res = csch_compile_descend(dst, viewid, hpath, src, name_loc, child); comp->child_sheet = child; comp->hdr.omit = 1; } else res = 0; /* Bind sheet ref sym terminals to child sheet terminals */ for(e = htip_first(&src->id2obj); e != NULL; e = htip_next(&src->id2obj, e)) { csch_aport_t *sym_port; const char *sym_port_api_name; sym_port = compile_port(dst, viewid, hpath, sheet, comp, e->value, 1); if (sym_port != NULL) { int error_indicated = 0; sym_port_api_name = sym_port->name; sym_port->subsheet_port = find_subsheet_port(dst, child, sym_port_api_name, &error_indicated); if (sym_port->subsheet_port == NULL) { if (!error_indicated) rnd_message(RND_MSG_ERROR, "Hierarchy error: sheet %s doesn't have terminal %s for symbol port %s:%s\n", child->hidlib.loadname, sym_port_api_name, comp->name, sym_port_api_name); res = -1; } else sym_port->subsheet_port->referer = comp; sym_port->hdr.omit = 1; } } return res; } static int compile_symbol(csch_abstract_t *dst, int viewid, csch_hier_path_t *hpath, const csch_sheet_t *sheet, csch_cgrp_t *src, rnd_bool do_sheet_ref_syms) { char tmpname[128]; const char *orig_name; char *name_glob = NULL, *name_loc = NULL; const csch_attrib_t *aname = NULL; csch_sheet_t *child = NULL; int hres, cres; csch_ascope_t scope = CSCH_ASCOPE_unknown; hres = csch_hier_find_sym_child(sheet, src, &child, 0); if (hres < 0) return hres; /* we are called in two passes, first doing non-sheet-refs then sheet refs */ if ((hres == 0) && do_sheet_ref_syms) return 0; if ((hres == 1) && !do_sheet_ref_syms) return 0; if (src->hdr.type == CSCH_CTYPE_GRP_REF) aname = csch_cgrp_ref_get_attr(src, "name"); else aname = csch_attrib_get(&src->attr, "name"); dst->ucnt.comp++; if ((aname == NULL) || (aname->val == NULL) || (*aname->val == '\0') || (aname->key == NULL) || (*aname->key == '\0')) { sprintf(tmpname, "anon_comp_%ld", dst->ucnt.comp); orig_name = tmpname; } else { orig_name = aname->val; scope = csch_split_name_scope(&orig_name); } csch_eng_call_namemod((csch_project_t *)sheet->hidlib.project, viewid, hpath, CSCH_ENGHK_SYMBOL_NAME_TO_COMPONENT_NAME, &name_glob, &name_loc, orig_name, FGW_COBJ, src, FGW_HPATH, hpath, FGW_INVALID); if (name_glob == NULL) { csch_eng_free_namemod(&name_glob, &name_loc); rnd_message(RND_MSG_ERROR, "Internal error: failed to perform symbol name translation\n"); return -1; } if (hres == 1) cres = compile_symbol_child_sheet(dst, viewid, hpath, scope, sheet, src, name_glob, name_loc, orig_name, child); else cres = compile_symbol_to_component(dst, viewid, hpath, scope, sheet, src, name_glob, name_loc, orig_name); csch_eng_free_namemod(&name_glob, &name_loc); return cres; } int csch_compile_connect_net_to(csch_anet_t **net, csch_ahdr_t *a, int allow_reconn) { if (a->type == CSCH_ATYPE_PORT) { csch_aport_t *ap = (csch_aport_t *)a; csch_anet_t *new_net; if (ap->sheet_port) { /* rnd_trace(" conn sheet port '%s' to net '%s'!\n", ap->name, (*net)->name);*/ ap->hdr.omit = 1; /* go on so that sheet local connections are made */ } if ((ap->parent != NULL) && (ap->parent->child_sheet != NULL)) { /* rnd_trace(" conn ref sym port '%s' to net '%s'!\n", ap->name, (*net)->name);*/ if ((ap->subsheet_port != NULL) && (ap->subsheet_port->conn.net)) { /* rnd_trace(" connect to subsheet port's net %s\n", ap->subsheet_port->conn.net->name);*/ new_net = csch_cmp_merge_nets(*net, ap->subsheet_port->conn.net); if (new_net == NULL) { rnd_message(RND_MSG_ERROR, "csch_compile_connect_net_to(): failed to merge nets %s and %s at port %s:%s for hierarchic connection down\n", (*net)->name, ap->subsheet_port->conn.net->name, APORT_PARENT_NAME(ap), ap->name); return -1; } *net = new_net; } /* go on so that sheet local connections are made */ } if (ap->conn.net != NULL) { int allowed = 0; /* special case: resistor term overlap with vcc term and a wire is also connected; this means we have a term-term net and a wirenet and this shouldn't be considered as a reconn because the term-term connection is really invisible */ if (ap->conn.net->term_term || (*net)->term_term) allowed = 1; if (!allow_reconn && !allowed) { rnd_message(RND_MSG_ERROR, "csch_compile_connect_net_to(): port %s:%s already connected\n(This error is generated because multiport_net_merge is disabled in the config)\n", APORT_PARENT_NAME(ap), ap->name); return -1; } new_net = csch_cmp_merge_nets(*net, ap->conn.net); if (new_net == NULL) { rnd_message(RND_MSG_ERROR, "csch_compile_connect_net_to(): failed to merge nets %s and %s at port %s:%s\n", (*net)->name, ap->conn.net->name, APORT_PARENT_NAME(ap), ap->name); return -1; } *net = new_net; } ap->conn.net = *net; TODO("bus: do the same for buses and bus ports? Or do we put nets in buses?"); vtp0_append(&(*net)->conns, a); return 0; } rnd_message(RND_MSG_ERROR, "csch_compile_connect_net_to(): unsupported object type\n"); return -1; } int csch_compile_disconnect(csch_ahdr_t *a) { if (a->type == CSCH_ATYPE_PORT) { csch_aport_t *ap = (csch_aport_t *)a; csch_anet_t *net = ap->conn.net; long n; if (net == NULL) return 0; /* already disconnected */ ap->conn.net = NULL; for(n = 0; n < net->conns.used; n++) { if (net->conns.array[n] == ap) { vtp0_remove(&net->conns, n, 1); break; } } return 0; } rnd_message(RND_MSG_ERROR, "csch_compile_disconnect(): unsupported object type\n"); return -1; } static int compile_conn(csch_abstract_t *dst, const csch_sheet_t *sheet, csch_hier_path_t *hpath, const csch_conn_t *src) { long n; csch_anet_t *net = NULL; if (src->conn.used < 2) { rnd_msg_error("Ignoring conn object with %d connection(s) - needs at least 2\n", src->conn.used); return -1; } /* first pass: find the net */ for(n = 0; n < src->conn.used; n++) { const csch_chdr_t *obj = src->conn.array[n]; const csch_cgrp_t *grp = obj->parent; csch_ahdr_t *a; TODO("remove this:"); if (grp == NULL) continue; if ((grp->hdr.type != CSCH_CTYPE_GRP) && (grp->hdr.type != CSCH_CTYPE_GRP_REF)) continue; if (grp->aid.used < 1) { rnd_msg_error("Invalid object on the conn list (empty); conn id: #%ld\n", src->hdr.oid); return -1; } a = htip_get(&dst->aid2obj, grp->aid.array[0]); if (a == NULL) { rnd_msg_error("Invalid object on the conn list (NULL); conn id: #%ld\n", src->hdr.oid); return -1; } switch(a->type) { case CSCH_ATYPE_NET: if ((net != NULL) && (&net->hdr != a)) { TODO("figure how to handle network collisions"); rnd_msg_error("Network collision\n"); return -1; } net = (csch_anet_t *)a; break; case CSCH_ATYPE_PORT: break; default: rnd_msg_error("Invalid object on the conn list (%s)\n", csch_atype_name(a->type)); return -1; } } /* create an anon net if there was no wirenet in the conn list; this happens in a term-term connection */ if (net == NULL) { char tmpname[128]; sprintf(tmpname, "anon_net_%ld", ++dst->ucnt.wirenet); net = csch_anet_new(dst, NULL, CSCH_ASCOPE_GLOBAL, tmpname, tmpname, 1); if (net == NULL) { rnd_msg_error("failed to create anon net for terminal-terminal connection\n"); return -1; } net->term_term = 1; net->hdepth = hpath->hdepth; net->hlev = hpath->hlev; /* even tho it won't be referenced because it's a globally unique name... */ } /* second pass: make the connections */ for(n = 0; n < src->conn.used; n++) { const csch_chdr_t *obj = src->conn.array[n]; const csch_cgrp_t *grp = obj->parent; csch_ahdr_t *a; TODO("remove this:"); if (grp == NULL) continue; if ((grp->hdr.type != CSCH_CTYPE_GRP) && (grp->hdr.type != CSCH_CTYPE_GRP_REF)) continue; if (grp->aid.used < 1) continue; a = htip_get(&dst->aid2obj, grp->aid.array[0]); if (a == &net->hdr) continue; if (csch_compile_connect_net_to(&net, a, CSCH_CFG(multiport_net_merge, 0)) != 0) return -1; } return 0; } static void compile_reset_abstract(csch_abstract_t *dst) { /* reset uniq name counters */ dst->ucnt.wirenet = 0; dst->ucnt.comp = 0; dst->ucnt.port = 0; dst->ucnt.target_tmp = 0; } static void csch_reset_sheet_aids(const csch_sheet_t *src) { const csch_chdr_t *h; for(h = gdl_first(&src->active); h != NULL; h = gdl_next(&src->active, h)) { if (csch_obj_is_grp(h)) { csch_cgrp_t *grp = (csch_cgrp_t *)h; grp->aid.used = 0; } } } int csch_compile_sheet_(csch_abstract_t *dst, int viewid, csch_hier_path_t *hpath, const csch_sheet_t *src) { int res = 0; const csch_chdr_t *h; if (src->non_graphical) { if ((src->non_graphical_impl == NULL) || (src->non_graphical_impl->compile_sheet == NULL)) { rnd_message(RND_MSG_ERROR, "Don't know how to compile non-graphical sheet %s\n", src->hidlib.fullpath); return -1; } return src->non_graphical_impl->compile_sheet(dst, viewid, hpath, src); } csch_reset_sheet_aids(src); /* compile nets and non-sheet-ref components */ for(h = gdl_first(&src->active); h != NULL; h = gdl_next(&src->active, h)) { csch_cgrp_t *grp = (csch_cgrp_t *)h; if (h->indirect) continue; if ((h->type != CSCH_CTYPE_GRP) && (h->type != CSCH_CTYPE_GRP_REF)) continue; /* can use enum role here because role attribute overwrite is handled in csch_cgrp_ref_render() */ switch(grp->role) { case CSCH_ROLE_WIRE_NET: res |= compile_wire_net(dst, viewid, hpath, src, grp); break; case CSCH_ROLE_SYMBOL: res |= compile_symbol(dst, viewid, hpath, src, grp, 0); break; case CSCH_ROLE_TERMINAL: if (is_grp_a_sheet_port(src, grp)) { csch_aport_t *aport = compile_port(dst, viewid, hpath, src, NULL, grp, 0); csch_eng_call((csch_project_t *)src->hidlib.project, viewid, hpath, CSCH_ENGHK_COMPILE_PORT, FGW_AOBJ, aport, FGW_HPATH, hpath, FGW_INVALID); aport->sheet_port = 1; /* rnd_trace("Sheet port: prefix='%s' name='%s'\n", hpath->prefix.array, aport->name);*/ } break; case CSCH_ROLE_invalid: case CSCH_ROLE_empty: case CSCH_ROLE_BUS_NET: case CSCH_ROLE_BUS_TERMINAL: case CSCH_ROLE_HUB_POINT: case CSCH_ROLE_JUNCTION: case CSCH_ROLE_EXTOBJ_GFX: break; } } /* compile sheet-ref components */ for(h = gdl_first(&src->active); h != NULL; h = gdl_next(&src->active, h)) { csch_cgrp_t *grp = (csch_cgrp_t *)h; if (h->indirect) continue; if ((h->type != CSCH_CTYPE_GRP) && (h->type != CSCH_CTYPE_GRP_REF)) continue; if (grp->role == CSCH_ROLE_SYMBOL) res |= compile_symbol(dst, viewid, hpath, src, grp, 1); } csch_eng_call((csch_project_t *)src->hidlib.project, viewid, hpath, CSCH_ENGHK_CONNS_BEFORE, FGW_PTR, dst, FGW_HPATH, hpath, FGW_INVALID); /* compile connections between nets and components (the "netlist" part) */ for(h = gdl_first(&src->active); h != NULL; h = gdl_next(&src->active, h)) { const csch_conn_t *conn = (const csch_conn_t *)h; if (h->indirect) continue; if (h->type != CSCH_CTYPE_CONN) continue; res |= compile_conn(dst, src, hpath, conn); } csch_eng_call((csch_project_t *)src->hidlib.project, viewid, hpath, CSCH_ENGHK_CONNS_AFTER, FGW_PTR, dst, FGW_HPATH, hpath, FGW_INVALID); return res; } int csch_compile_sheet(csch_abstract_t *dst, int viewid, const csch_sheet_t *src) { int res; csch_hier_path_t hpath; csch_hier_path_init(&hpath, dst); compile_reset_abstract(dst); res = csch_compile_sheet_(dst, viewid, &hpath, src); csch_hier_path_free(&hpath); return res; } /* Returns whether the loop should be restarted because of new components added */ RND_INLINE int csch_compile_post_port(csch_project_t *proj, int viewid, csch_hier_path_t *hpath, csch_abstract_t *dst, csch_aport_t *port) { if (port->hdr.compiled) return 0; port->hdr.compiled = 1; csch_eng_call(proj, viewid, hpath, CSCH_ENGHK_COMPILE_PORT, FGW_AOBJ, port, FGW_INVALID); return dst->new_ports; } /* Returns whether the loop should be restarted because of new components added */ RND_INLINE int csch_compile_post_comp(csch_project_t *proj, int viewid, csch_hier_path_t *hpath, csch_abstract_t *dst, csch_acomp_t *comp) { htsp_entry_t *p; if (comp->hdr.compiled) return 0; comp->hdr.compiled = 1; csch_eng_call(proj, viewid, hpath, CSCH_ENGHK_COMPILE_COMPONENT0, FGW_AOBJ, comp, FGW_INVALID); csch_compile_update_obj_cache(&comp->hdr); csch_eng_call(proj, viewid, hpath, CSCH_ENGHK_COMPILE_COMPONENT1, FGW_AOBJ, comp, FGW_INVALID); csch_compile_update_obj_cache(&comp->hdr); restart_port:; dst->new_ports = 0; for(p = htsp_first(&comp->ports); p != NULL; p = htsp_next(&comp->ports, p)) if (csch_compile_post_port(proj, viewid, hpath, dst, p->value)) goto restart_port; /* if new components are created restart the loop because genht gets confused */ csch_eng_call(proj, viewid, hpath, CSCH_ENGHK_COMPILE_COMPONENT2, FGW_AOBJ, comp, FGW_INVALID); return dst->new_comps; } /* Returns whether the loop should be restarted because of new components added */ RND_INLINE int csch_compile_post_net(csch_project_t *proj, int viewid, csch_hier_path_t *hpath, csch_abstract_t *dst, csch_anet_t *net) { if (net->hdr.compiled) return 0; net->hdr.compiled = 1; csch_eng_call(proj, viewid, hpath, CSCH_ENGHK_COMPILE_NET, FGW_AOBJ, net, FGW_INVALID); return dst->new_nets; } RND_INLINE void csch_compile_post_update_cache_(csch_ahdr_t *obj) { const char *val; val = csch_attrib_get_str(&obj->attr, "display/dnp"); if ((val != NULL) && (*val != '\0')) obj->dnp = 1; val = csch_attrib_get_str(&obj->attr, "display/omit"); if ((val != NULL) && (*val != '\0')) obj->omit = 1; } void csch_compile_update_obj_cache(csch_ahdr_t *obj) { csch_compile_post_update_cache_(obj); } /* Calculate all caches in the abstract model after the compilation has finished */ RND_INLINE void csch_compile_post_update_cache(csch_project_t *proj, int viewid, csch_abstract_t *abst) { htsp_entry_t *n, *c, *p; for(n = htsp_first(&abst->nets); n != NULL; n = htsp_next(&abst->nets, n)) { csch_anet_t *net = n->value; csch_compile_post_update_cache_(&net->hdr); } for(c = htsp_first(&abst->comps); c != NULL; c = htsp_next(&abst->comps, c)) { csch_acomp_t *comp = c->value; csch_compile_post_update_cache_(&comp->hdr); for(p = htsp_first(&comp->ports); p != NULL; p = htsp_next(&comp->ports, p)) { csch_aport_t *port = p->value; csch_compile_post_update_cache_(&port->hdr); } } } static int cmp_comp_hdepth(const void *C1, const void *C2) { const csch_acomp_t **c1 = (const csch_acomp_t **)C1; const csch_acomp_t **c2 = (const csch_acomp_t **)C2; /* secondary key: if depths are the same, sort by aid for reproducible output (different libc implementations will behave differently if primary key is equal) */ if ((*c1)->hdepth == (*c2)->hdepth) return ((*c1)->hdr.aid > (*c2)->hdr.aid) ? +1 : -1; /* primary key: hdepth */ if ((*c1)->hdepth > (*c2)->hdepth) return 1; return -1; } int csch_compile_post(csch_project_t *proj, int viewid, csch_abstract_t *dst) { htsp_entry_t *e; csch_sheet_t *sheet; csch_hier_path_t *hpath = NULL; /* we have no path after compilation */ vtp0_t lst = {0}; long n; /* NOTE: have to sort objects and go from top to bottom in hierarchy to make sure the top end of a v/foo reference is created before the bottom end ^/foo (or just foo) is found */ restart_comp:; lst.used = 0; dst->new_comps = 0; for(e = htsp_first(&dst->comps); e != NULL; e = htsp_next(&dst->comps, e)) { csch_acomp_t *acomp = e->value; if (!acomp->postprocessed) vtp0_append(&lst, acomp); } if (lst.used > 0) { qsort(lst.array, lst.used, sizeof(void *), cmp_comp_hdepth); for(n = 0; n < lst.used; n++) { csch_acomp_t *acomp = lst.array[n]; acomp->postprocessed = 1; csch_compile_post_comp(proj, viewid, hpath, dst, acomp); } if (dst->new_comps) goto restart_comp; } lst.used = 0; restart_net:; dst->new_nets = 0; for(e = htsp_first(&dst->nets); e != NULL; e = htsp_next(&dst->nets, e)) if (csch_compile_post_net(proj, viewid, hpath, dst, e->value)) goto restart_net; /* if new components are created restart the loop because genht gets confused */ vtp0_uninit(&lst); csch_text_invalidate_all_project(proj, 1); csch_eng_call(proj, viewid, NULL, CSCH_ENGHK_PROJECT_AFTER, FGW_PTR, dst, FGW_PTR, proj, FGW_INVALID); csch_compile_post_update_cache(proj, viewid, dst); sheet = *vtp0_get(&proj->hdr.designs, 0, 0); rnd_event(&sheet->hidlib, CSCH_EVENT_PRJ_COMPILED, NULL); return 0; } int csch_compile_project(csch_project_t *prj, int viewid, csch_abstract_t *dst, int quiet) { int res = 0, r; long n; htpp_entry_t *e; compile_reset_abstract(dst); htpp_init(&dst->eng_transient, ptrhash, ptrkeyeq); dst->view_id = viewid; dst->prj = prj; dst->comp_cnt++; if (csch_project_is_partial(prj)) rnd_message(RND_MSG_ERROR, "*** PARTIAL PROJECT ***\nNot all root sheets are loaded, the resulting abstract model\nwill be partial too.\n"); csch_eng_call(prj, viewid, NULL, CSCH_ENGHK_PROJECT_BEFORE, FGW_PTR, dst, FGW_PTR, prj, FGW_INVALID); for(n = 0; n < vtp0_len(&prj->hdr.designs); n++) { csch_sheet_t *sheet = *vtp0_get(&prj->hdr.designs, n, 0); /* hierarchy: start from root sheets only, skip aux sheets; unlisted/unknown are rather compiled so no-project-file works */ if ((sheet->stype == CSCH_SHTY_AUX) || (sheet->stype == CSCH_SHTY_EXTERNAL)) continue; if ((sheet->stype == CSCH_SHTY_UNLISTED) || (sheet->stype == CSCH_SHTY_unknown)) { if (prj->num_root_sheets != 0) { rnd_message(RND_MSG_WARNING, "Compile: not compiling sheet %s: unlisted file in a project with explicit sheet list in project file\n", sheet->hidlib.loadname); if (!sheet->non_graphical) csch_reset_sheet_aids(sheet); continue; } else { /* implicit project file, accept and compile unlisted sheets */ } } { csch_hier_path_t hpath; csch_hier_path_init(&hpath, dst); r = csch_compile_sheet_(dst, viewid, &hpath, sheet); csch_hier_path_free(&hpath); } if (r != 0) rnd_message(RND_MSG_ERROR, "sheet #%d (%s) compilation failed: %d\n", n, sheet->hidlib.fullpath, r); res |= r; } csch_compile_post(prj, viewid, dst); for(e = htpp_first(&dst->eng_transient); e != NULL; e = htpp_next(&dst->eng_transient, e)) rnd_message(RND_MSG_ERROR, "csch_compile_project(): engine transient leftover: %s (memory leak)\n", e->key); htpp_uninit(&dst->eng_transient); dst->prj = NULL; return res; }