/**************************************************************************** Copyright (C) 1987-2005 by Jeffery P. Hansen 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., 675 Mass Ave, Cambridge, MA 02139, USA. ****************************************************************************/ #include #include #include #include "gsim.h" #define CONCAT_Z 0 #define CONCAT_I 1 #define DIR_UNKNOWN 0 #define DIR_BOGUS -1 #define DIR_IZ 1 #define DIR_ZI 2 static void Concat_processEvent(SGate*,EvQueue*,SEvent*); static int Concat_checkGate(SGate*); static void Concat_propFrwdDelay(SPort *P,simTime t); static void Concat_propBackDelay(SPort *P,simTime t); static simTime Concat_delay(SPort *Pfrom,SPort *Pto); static int count_outs(SPort *xP,int *nout); typedef struct concat_data { int dir; /* Direction of merge */ int loopCheck; /* Checking for loop */ SPort *blockP; /* Blocked port */ } ConcatData; static SGateInfo concat_info = { GT_CONCAT, "concat",0x0, 2,{{"Z",GIO_WIRE,0}, {"I",GIO_WIRE,PF_MULTI}}, {{0}}, Generic_copyGate, Concat_processEvent, Concat_checkGate, Nop_initGate, 0, 0, 0, Concat_propFrwdDelay, Concat_propBackDelay, Concat_delay, }; ConcatData *Concat_getData(SGate *g) { if (!g->g_data) { ConcatData *cd = (ConcatData *) malloc(sizeof(ConcatData)); g->g_data = cd; cd->dir = DIR_UNKNOWN; cd->loopCheck = 0; cd->blockP = 0; } return g->g_data; } int Concat_effectivePortType(SPort *P) { SGate *g = P->p_gate; ConcatData *cd = (ConcatData *) Concat_getData(g); switch (cd->dir) { case DIR_IZ : if (P == g->g_ports.port[CONCAT_Z]) return GIO_OUT; else return GIO_IN; break; case DIR_ZI : if (P == g->g_ports.port[CONCAT_Z]) return GIO_IN; else return GIO_OUT; break; default : break; } return GIO_IN; } static void Concat_processEvent(SGate *g,EvQueue *Q,SEvent *E) { int i,b; ConcatData *cd = (ConcatData *) Concat_getData(g); switch (cd->dir) { case DIR_ZI : /* Signals flow from Z to I */ { SState *Z,*C; if (!IsChangeOn(E,g,CONCAT_Z)) break; Z = SGate_allocPortState(g,CONCAT_Z); C = alloc_SState(); b = 0; for (i = CONCAT_I;i < g->g_ports.num;i++) { SPort *I = g->g_ports.port[i]; SState_reinit(C,I->p_state.nbits); SState_copyRange(C,0,Z,I->p_state.nbits-1+b,b); b += I->p_state.nbits; EvQueue_setPort(Q,I,C,0); } free_SState(Z); free_SState(C); } break; case DIR_IZ : /* Signals flow from I to Z */ { SPort *Z = g->g_ports.port[CONCAT_Z]; SState *C; if (IsChangeOn(E,g,CONCAT_Z)) break; C = alloc_SState(); SState_reinit(C,Z->p_state.nbits); b = 0; for (i = CONCAT_I;i < g->g_ports.num;i++) { SState *I = SGate_allocPortState(g,i); SState_copyRange(C,b,I,I->nbits-1,0); b += I->nbits; free_SState(I); } EvQueue_setPort(Q,Z,C,0); free_SState(C); } break; default : error("Event received by uninstantiated concat %s.",g->g_name); break; } } /* * Set directions of all concat elements in the circuit. */ void Concat_setDirections(SModule *M) { HashElem *E; NHash C; SGate **A; int num_left; int i,j; NHash_init(&C); /* * Build table of all concat elements. */ for (E = Hash_first(&M->m_gates);E;E = Hash_next(&M->m_gates,E)) { SGate *g = (SGate*) HashElem_obj(E); if (g->g_type->gi_code == GT_CONCAT) NHash_insert(&C,(nkey_t)g,g); } /* * Copy elements found to an array. */ num_left = Hash_numElems(&C); A = (SGate**)malloc(sizeof(SGate*)*num_left); for (i = 0, E = Hash_first(&C);E;i++, E = Hash_next(&C,E)) A[i] = (SGate*)HashElem_obj(E); NHash_uninit(&C); /* * Set directions of concat elements until we go through a whole pass * without any modifications. */ for (;;) { int num_elim = 0; /* Number of unknown concats eliminated */ for (i = 0;i < num_left;i++) { SGate *g = A[i]; ConcatData *cd = Concat_getData(g); SPort *P; int i_nout = 0,z_nout = 0; /* * Direction is already set for this concat */ if (cd->dir != DIR_UNKNOWN) { A[i] = A[--num_left]; num_elim++; continue; } /* * Scan I ports */ for (j = CONCAT_I;j < g->g_ports.num;j++) { SPort *P = g->g_ports.port[j]; count_outs(P,&i_nout); } P = g->g_ports.port[CONCAT_Z]; count_outs(P,&z_nout); /* * Driving ports found on both sides */ if (i_nout > 0 && z_nout > 0) { A[i] = A[--num_left]; num_elim++; errorGate(g->g_name,"Inconsistent direction on concat element."); continue; } if (i_nout > 0) { cd->dir = DIR_IZ; A[i] = A[--num_left]; num_elim++; continue; } if (z_nout > 0) { cd->dir = DIR_ZI; A[i] = A[--num_left]; num_elim++; continue; } } if (num_elim == 0) break; } free(A); } /* * Count the number of "output" ports connected to the same net as xP. xP * is excluded from the count. The number of outputs found is added to *nout. */ static int count_outs(SPort *xP,int *nout) { SNet *net = xP->p_net; int i; for (i = 0;i < net->n_ports.num;i++) { SPort *P = net->n_ports.port[i]; if (P == xP) continue; switch (P->p_type->io) { case GIO_IN : break; case GIO_OUT : case GIO_TRI : case GIO_INOUT : (*nout)++; break; case GIO_WIRE : { SGate *g = P->p_gate; ConcatData *cd; if (g->g_type != &concat_info) { errorGate(P->p_gate->g_name,"Bogus gate type %s when expecting concat.",g->g_type->gi_name); break; } cd = (ConcatData *) Concat_getData(g); if (cd->blockP == P) return 0; cd = (ConcatData *) Concat_getData(g); if (cd->dir > 0) { if (cd->dir == DIR_IZ && P == g->g_ports.port[CONCAT_Z]) (*nout)++; else if (cd->dir == DIR_ZI && P != g->g_ports.port[CONCAT_Z]) (*nout)++; } } break; default : errorGate(P->p_gate->g_name,"Bogus wire type %d found while checking concat ports.",P->p_type->io); break; } } return 0; } static int Concat_checkGate(SGate *g) { ConcatData *cd; int n = 0; int i; for (i = CONCAT_I;i < g->g_ports.num;i++) n += g->g_ports.port[i]->p_net->n_nbits; if (n != g->g_ports.port[CONCAT_Z]->p_net->n_nbits) { errorGate(g->g_name,"Bit width mismatch (%d != %d).",n,g->g_ports.port[CONCAT_Z]->p_net->n_nbits); return -1; } cd = (ConcatData *) Concat_getData(g); if (cd->dir <= 0) { errorGate(g->g_name,"concat direction could not be determined."); return -1; } return 0; } static void Concat_propFrwdDelay(SPort *P,simTime t) { SGate *g = P->p_gate; SPadInfo *pi = P->p_type; int i; if (SPort_effectiveType(P) != GIO_IN) return; if (t <= P->p_frwdDelay) return; if (pi->idx == 0) { for (i = 1;i < g->g_ports.num;i++) SNet_propFrwdDelay(g->g_ports.port[i]->p_net,t); } else { SNet_propFrwdDelay(g->g_ports.port[0]->p_net,t); } } static void Concat_propBackDelay(SPort *P,simTime t) { SGate *g = P->p_gate; SPadInfo *pi = P->p_type; int i; if (SPort_effectiveType(P) != GIO_OUT) return; if (t <= P->p_backDelay) return; if (pi->idx == 0) { for (i = 1;i < g->g_ports.num;i++) SNet_propBackDelay(g->g_ports.port[i]->p_net,t); } else { SNet_propBackDelay(g->g_ports.port[0]->p_net,t); } } static simTime Concat_delay(SPort *Pfrom,SPort *Pto) { if (!Pto) return 0; if ((Pto->p_type->idx == 0 && Pfrom->p_type->idx == 0) || (Pto->p_type->idx != 0 && Pfrom->p_type->idx != 0)) return NO_TIME; return 0; } void init_concat() { SGateInfo_register(&concat_info,0); }