1 /****************************************************************************
2     Copyright (C) 1987-2015 by Jeffery P. Hansen
3 
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8 
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13 
14     You should have received a copy of the GNU General Public License along
15     with this program; if not, write to the Free Software Foundation, Inc.,
16     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 
18     Last edit by hansen on Fri May  1 20:41:18 2009
19 ****************************************************************************/
20 
21 #include "tkgate.h"
22 #include "print.h"
23 
24 #define MUX_IN 0
25 #define MUX_SEL 1
26 #define MUX_OUT 2
27 
28 static void Mux_WriteCellDef(FILE *f,GCellSpec *gcs);
29 
30 
31 /*
32  * Some of these are shared by the Demux element.  These have a MuxDemux_ prefix.
33  */
34 void Mux_Delete(GCElement *g,GModuleDef *env,int drawp);
35 void Mux_Draw(GCElement *g,int md);
36 void Mux_Rotate(GCElement *g, int centX, int centY,int rdir);
37 GCElement *Mux_Make(EditState **es,GModuleDef *env,int GType,
38 		    int x,int y,int r,const char *Name,int noWires,const char**,int);
39 void Mux_PSWrite(GPrint *P,GModLayout*,GCElement *g);
40 void MuxDemux_VerSave(FILE*,GCElement*);
41 void Mux_SetProp(GCElement*,const char*,const void*);
42 void Mux_AddInput(EditState *es,GCElement *g);
43 int MuxDemux_EditProps(GCElement *g,int isLoadDialog);
44 GCElement *MuxDemux_Copy(GModuleDef *M,GCElement *g,int x,int y,unsigned flags);
45 void Mux_VersionDelta(GCElement*,Version*);
46 GWireNode *Mux_wireSnap(GCElement *g,GWire *w,int *mod,int retry);
47 
48 void Demux_adjustWires(GCElement *g);
49 void Decoder_adjustWires(GCElement *g);
50 
51 static iconDimensions mux_iconDims[] = {
52   {0,  0,  59, 29, 30, 15},
53   {60, 0,  29, 59, 15, 30},
54   {29, 60, 59, 29, 30, 12},
55   {0,  29, 29, 59, 12, 30},
56 };
57 static int mux_iconBoldOffset = 89;
58 
59 GPadLoc mux_in_loc[] = {
60 	{-30,-16,30,-16,D_UP},
61 	{-16,-30,-16,30,D_LEFT},
62 	{-30,16,30,16,D_DOWN},
63 	{16,-30,16,30,D_RIGHT}};
64 
65 GPadLoc mux_out_loc[] = {
66 	{0,13,0,13,D_DOWN},
67 	{13,0,13,0,D_RIGHT},
68 	{0,-13,0,-13,D_UP},
69 	{-13,0,-13,0,D_LEFT}};
70 
71 GPadLoc mux_sel_loc[] = {
72 	{-23,0,-23,0,D_LEFT},
73 	{0,23,0,23,D_DOWN},
74 	{23,0,23,0,D_RIGHT},
75 	{0,-23,0,-23,D_UP},
76 };
77 
78 GPadLoc mux_sel_altloc[] = {
79 	{23,0,23,0,D_RIGHT},
80 	{0,-23,0,-23,D_UP},
81 	{-23,0,-23,0,D_LEFT},
82 	{0,23,0,23,D_DOWN},
83 };
84 
85 static char *psMux[] = {
86   "%",
87   "% x y r mux",
88   "%",
89   "/mux {",
90   "    dup /mrot exch def",
91   "    startgate",
92   "    8 rfont",
93   "    -29.5 15.5 moveto",
94   "    29.5 15.5 lineto",
95   "    16.5 -12.5 lineto",
96   "    -16.5 -12.5 lineto",
97   "    closepath stroke",
98   "    dup					% n n",
99   "    1 add 58 exch div			% n d1",
100   "    2 copy mul				% n d1 dn",
101   "    mrot -90 eq mrot -180 eq or {",
102   "      3 -1 roll 1 sub 50 string cvs exch (0) exch	% d1 (n) (0) dn",
103   "      -29 add 7 rCT				% d1",
104   "      exch -29 add 7 rCT",
105   "    } {",
106   "      3 -1 roll 1 sub 50 string cvs exch	% d1 (n) dn",
107   "      -29 add 7 rCT				% d1",
108   "      (0) exch -29 add 7 rCT",
109   "    } ifelse",
110   "    grestore",
111   "} def",
112   0
113 };
114 
115 GGateInfo gate_mux_info = {
116   0,
117   "MUX",
118   "mux",0x0,
119   "mux",psMux,
120   0,2,
121 
122  { {"m",	{"gm.msi",0},		{"gm.msi.21mux",0,"mux",100},		"gat_make MUX "},
123    {"M 2",	{0,0},			{0,0,0,0},				"gat_make MUX -pins I=2"},
124    {"M 3",	{0,0},			{0,0,0,0},				"gat_make MUX -pins I=3"},
125    {"M 4",	{"gm.msi",0},		{"gm.msi.41mux",0,"mux",200},		"gat_make MUX -pins I=4"},
126    {"M 5",	{0,0},			{0,0,0,0},				"gat_make MUX -pins I=5"},
127    {"M 6",	{0,0},			{0,0,0,0},				"gat_make MUX -pins I=6"},
128    {"M 7",	{0,0},			{0,0,0,0},				"gat_make MUX -pins I=7"},
129    {"M 8",	{"gm.msi",0},		{"gm.msi.81mux",0,"mux",300},		"gat_make MUX -pins I=8"},
130    {"M 9",	{0,0},			{0,0,0,0},				"gat_make MUX -pins I=9"},
131    {0}},
132 
133   mux_iconDims,
134 
135   3,{{"I",IN,1,2,mux_in_loc,1},
136        {"S",IN,1,1,mux_sel_loc,0},
137        {"Z",OUT,1,1,mux_out_loc,0}},
138   {{23,8,LJ},{0,40,CT},{-23,0,RJ},{0,-33,CT}},
139   {1},
140 
141   {"Dsz","Diz",0},
142 
143   Mux_Make,
144   Mux_WriteCellDef,
145   Generic_Init,
146   Mux_Delete,
147   Generic_GetExtents,
148   Generic_HitDistance,
149   Mux_Draw,
150   Generic_Move,
151   MuxDemux_Copy,
152   Mux_AddInput,
153   Err_AddOutput,
154   Err_AddInOut,
155   Mux_Rotate,
156   Err_RemovePort,
157   Err_ChangePin,
158   Nop_SimInitFunc,
159   Nop_SimHitFunc,
160   Mux_PSWrite,
161   MuxDemux_EditProps,
162   MuxDemux_VerSave,
163   Mux_SetProp,
164   Mux_VersionDelta,
165   Mux_wireSnap
166 };
167 
168 
169 /*
170  * Adjust the wires on a mux to conform to the current setting of data_order
171  */
Mux_adjustWiresData(GCElement * g)172 static void Mux_adjustWiresData(GCElement *g)
173 {
174   int N,L,i;
175   double dx,dy,wx,wy;
176   GWire *w;
177   GPadLoc *pd = &g->typeinfo->Pad[MUX_IN].Loc[g->orient];
178 
179 
180   wx = wy = dx = dy = 0;
181   N = wire_numOnPad(g->wires[MUX_IN]);
182   L = iabs(pd->x1-pd->x2)  + iabs(pd->y1-pd->y2); 	/* Length of pad (assumed non-diagonal) */
183 
184   wx = g->xpos + pd->x1;
185   wy = g->ypos + pd->y1;
186 
187   switch (g->orient) {
188   case 0 :
189     dx = (double)L/(double)(N+1);
190     if (g->u.mux.data_order) {
191       wx = g->xpos + pd->x2;
192       dx = -dx;
193     }
194     break;
195   case 1 :
196     dy = (double)L/(double)(N+1);
197     if (!g->u.mux.data_order) {
198       wy = g->ypos + pd->y2;
199       dy = -dy;
200     }
201     break;
202   case 2 :
203     dx = (double)L/(double)(N+1);
204     if (!g->u.mux.data_order) {
205       wx = g->xpos + pd->x2;
206       dx = -dx;
207     }
208     break;
209   case 3 :
210     dy = (double)L/(double)(N+1);
211     if (g->u.mux.data_order) {
212       wy = g->ypos + pd->y2;
213       dy = -dy;
214     }
215     break;
216   }
217 
218   for (i = 0,w = g->wires[MUX_IN];w;i++, w = w->next) {
219     wx += dx;
220     wy += dy;
221     wire_move(w->nodes,(int)wx-w->nodes->x,(int)wy-w->nodes->y,VERTICAL|HORIZONTAL);
222   }
223 }
224 
225 /*
226  * Adjust the wires on a mux to conform to the current setting of select_side
227  */
Mux_adjustWiresSelector(GCElement * g)228 static void Mux_adjustWiresSelector(GCElement *g)
229 {
230   int wx,wy;
231   GPadLoc *pd = &g->typeinfo->Pad[MUX_SEL].Loc[g->orient];
232   GWire *w = g->wires[MUX_SEL];
233   GWire *ow = wire_other(w);
234 
235   if (g->u.mux.select_side)
236     pd = &mux_sel_altloc[g->orient];
237 
238   wx = g->xpos + pd->x2;
239   wy = g->ypos + pd->y2;
240 
241   ob_touch(w);
242   wire_move(w->nodes,wx-w->nodes->x,wy-w->nodes->y,VERTICAL|HORIZONTAL);
243 
244   /*
245    * If wire end is not attached, compute mirror image.  Otherwise use brute force
246    * to fix the wire.
247    */
248   if (w->orient != pd->dir) {
249     w->orient = pd->dir;
250     if (!ow->gate) {
251       int cx = w->nodes->x;
252       int cy = w->nodes->y;
253       GWireNode *n;
254 
255 
256       for (n = w->driver->nodes;n;n = n->out) {
257 	ob_touch(n);
258 	if (g->orient == 0 || g->orient == 2)
259 	  n->x = cx - (n->x - cx);
260 	else
261 	  n->y = cy - (n->y - cy);
262       }
263     } else
264       GWire_snap(w->driver);
265   }
266 }
267 
268 /*
269  * Adjust the wires on a mux.
270  */
Mux_adjustWires(GCElement * g)271 static void Mux_adjustWires(GCElement *g)
272 {
273   Mux_adjustWiresData(g);
274   Mux_adjustWiresSelector(g);
275 }
276 
Mux_Make(EditState ** es,GModuleDef * env,int GType,int x,int y,int r,const char * Name,int noWires,const char ** options,int nOptions)277 GCElement *Mux_Make(EditState **es,GModuleDef *env,int GType,
278 		    int x,int y,int r,const char *Name,int noWires,const char **options,int nOptions)
279 {
280   int nbits;
281   struct celemnt *g;
282   struct wire *w;
283 
284   if (!(g = Generic_Make(es,env,GType,x,y,r,Name,1,options,nOptions)))
285     return NULL;
286 
287   g->u.mux.select_side = 0;
288   g->u.mux.data_order = 0;
289 
290   if (!noWires) {
291     int i,j;
292     const char *Invert,*Pins;
293     int N = GCElement_numPads(g);
294 
295     Invert = seekOption("-invert",options,nOptions);
296     Pins = seekOption("-pins",options,nOptions);
297 
298     for (i = 0;i < N;i++) {
299       int Num = 0;
300 
301       if (Pins) {
302 	char buf[STRMAX];
303 	int N;
304 	if (sscanf(Pins,"%[^=]=%d",buf,&N) == 2 && strcmp(buf,g->typeinfo->Pad[i].name) == 0)
305 	  Num = N;
306       }
307       if (!Num)
308 	Num = GCElement_getPadNum(g,i);
309 
310       for (j = 0;j < Num;j++) {
311 	if (Invert && strcmp(Invert,GCElement_getPadName(g,i)) == 0)
312 	  wire_addToGate(g,i,env,1);
313 	else
314 	  wire_addToGate(g,i,env,0);
315       }
316     }
317 
318     Mux_adjustWires(g);
319 
320     for (i = 0,w = g->wires[MUX_IN];w;w = w->next,i++);
321 
322     nbits = required_bits(i);
323     net_setSize(g->wires[MUX_SEL]->net,nbits);
324   }
325 
326   return g;
327 }
328 
MuxDemux_Copy(GModuleDef * M,GCElement * g,int x,int y,unsigned flags)329 GCElement *MuxDemux_Copy(GModuleDef *M,GCElement *g,int x,int y,unsigned flags)
330 {
331   GCElement *ng = Generic_Copy(M,g,x,y,flags);
332 
333   ob_touch(ng);
334   ng->u.mux.select_side = g->u.mux.select_side;
335   ng->u.mux.data_order = g->u.mux.data_order;
336 
337   return ng;
338 }
339 
340 
Mux_AddInput(EditState * es,GCElement * g)341 void Mux_AddInput(EditState *es,GCElement *g)
342 {
343   int i;
344   int N = GCElement_numPads(g);
345 
346   for (i = 0;i < N;i++)
347     if (GCElement_getPadDir(g,i) == IN && g->typeinfo->Pad[i].CanAdd)
348       break;
349 
350   if (i == N)
351     return;
352 
353   if (es) SetModified(MF_NET|MF_GATE);
354 
355   gate_draw(g,GD_NORMAL);
356   wire_addToGate(g,i,es->env,0);
357   Mux_adjustWires(g);
358   gate_draw(g,GD_NORMAL);
359 }
360 
361 
Mux_Draw_Special(GCElement * g)362 void Mux_Draw_Special(GCElement *g)
363 {
364   GWire *w,*w0;
365   char n[STRMAX];
366   int i;
367 
368   if (!(w0 = g->wires[MUX_IN])) return;
369   for (i = 0,w = w0;w->next;w = w->next,i++);
370   sprintf(n,"%d",i);
371 
372   if (g->selected)
373     XSetFont(TkGate.D,TkGate.instGC,TkGate.stextbXF[TkGate.circuit->zoom_factor]->fid);
374   else
375     XSetFont(TkGate.D,TkGate.instGC,TkGate.stextXF[TkGate.circuit->zoom_factor]->fid);
376 
377   switch (g->orient) {
378   case 0 :
379     dce_DrawString(TkGate.instGC,
380 		   w0->nodes->x,w0->nodes->y + 2,
381 		   AtTop|BetweenLeftAndRight,"0");
382     dce_DrawString(TkGate.instGC,
383 		   w->nodes->x,w->nodes->y + 2,
384 		   AtTop|BetweenLeftAndRight,n);
385     break;
386   case 1 :
387     dce_DrawString(TkGate.instGC,
388 		   w0->nodes->x + 3,w0->nodes->y,
389 		   BetweenTopAndBottom|AtLeft, "0");
390     dce_DrawString(TkGate.instGC,
391 		   w->nodes->x + 3,w->nodes->y,
392 		   BetweenTopAndBottom|AtLeft,n);
393     break;
394   case 2 :
395     dce_DrawString(TkGate.instGC,
396 		   w0->nodes->x,w0->nodes->y - 2,
397 		   AtBottom|BetweenLeftAndRight,"0");
398     dce_DrawString(TkGate.instGC,
399 		   w->nodes->x,w->nodes->y - 2,
400 		   AtBottom|BetweenLeftAndRight,n);
401     break;
402   case 3:
403     dce_DrawString(TkGate.instGC,
404 		   w0->nodes->x - 2,w0->nodes->y,
405 		   BetweenTopAndBottom|AtRight,"0");
406     dce_DrawString(TkGate.instGC,
407 		   w->nodes->x - 2,w->nodes->y,
408 		   BetweenTopAndBottom|AtRight,n);
409     break;
410   }
411 }
412 
Mux_Delete(GCElement * g,GModuleDef * env,int drawp)413 void Mux_Delete(GCElement *g,GModuleDef *env,int drawp)
414 {
415   Generic_Delete(g,env,drawp);
416 }
417 
Mux_Draw(GCElement * g,int md)418 void Mux_Draw(GCElement *g,int md)
419 {
420   Generic_Draw(g,md);
421   Mux_Draw_Special(g);
422 }
423 
Mux_Rotate(GCElement * g,int centX,int centY,int rdir)424 void Mux_Rotate(GCElement *g, int centX, int centY,int rdir)
425 {
426   int x = g->xpos;
427   int y = g->ypos;
428 
429   ob_touch(g);
430   g->xpos = rotateX(x - centX,y - centY,rdir) + centX;
431   g->ypos = rotateY(x - centX,y - centY,rdir) + centY;
432   g->orient = (g->orient + 4 + rdir) % 4;
433 
434   Mux_adjustWires(g);
435 }
436 
Mux_PSWrite(GPrint * P,GModLayout * L,GCElement * g)437 void Mux_PSWrite(GPrint *P,GModLayout *L,GCElement *g)
438 {
439     int NIn;
440     struct wire *w;
441 
442     Generic_PSLabels(P,g);
443 
444     for (NIn = 0, w = g->wires[MUX_IN];w;w = w->next) NIn++;
445 
446     fprintf(P->p_f,"%d %d %d %d %s\n",
447 	NIn,g->xpos,g->ypos,-g->orient*90,g->typeinfo->psprint);
448 }
449 
MuxDemux_VerSave(FILE * f,GCElement * g)450 void MuxDemux_VerSave(FILE *f,GCElement *g)
451 {
452   VerilogBasicGateCall(f,g);
453   VerilogBasicGateParmList(f,g);
454   VerilogBasicGateComment(f,g,1);
455 
456   fprintf(f," /ss:%d /do:%d",g->u.mux.select_side,g->u.mux.data_order);
457 
458   fprintf(f,"\n");
459 }
460 
MuxDemux_EditProps(GCElement * g,int isLoadDialog)461 int MuxDemux_EditProps(GCElement *g,int isLoadDialog)
462 {
463   Tcl_Interp *tcl = TkGate.tcl;
464 
465   Generic_EditProps(g,isLoadDialog);
466   if (isLoadDialog) {
467     DoTcl("set ::edgat_dataOrder %d",g->u.mux.data_order);
468     DoTcl("set ::edgat_selectSide %d",g->u.mux.select_side);
469   } else {
470     const char *p;
471     int new_data_order = g->u.mux.data_order;
472     int new_select_side = g->u.mux.select_side;
473 
474     if ((p = Tcl_GetVar(tcl,"edgat_dataOrder",TCL_GLOBAL_ONLY))) {
475       sscanf(p,"%d",&new_data_order);
476     }
477     if ((p = Tcl_GetVar(tcl,"edgat_selectSide",TCL_GLOBAL_ONLY))) {
478       sscanf(p,"%d",&new_select_side);
479     }
480 
481     if (g->u.mux.data_order != new_data_order || g->u.mux.select_side != new_select_side) {
482       ob_touch(g);
483       gate_draw(g,0);
484       g->u.mux.data_order = new_data_order;
485       g->u.mux.select_side = new_select_side;
486 
487       if (strcmp(g->typeinfo->name,"MUX") == 0)
488 	Mux_adjustWires(g);
489       else if (strcmp(g->typeinfo->name,"DEMUX") == 0)
490 	Demux_adjustWires(g);
491       else if (strcmp(g->typeinfo->name,"DECODER") == 0)
492 	Decoder_adjustWires(g);
493 
494 
495       gate_draw(g,0);
496     }
497   }
498 
499   return 0;
500 }
501 
Mux_SetProp(GCElement * g,const char * prop,const void * value)502 void Mux_SetProp(GCElement *g,const char *prop,const void *value)
503 {
504   if (strcmp(prop,"/ss") == 0) g->u.mux.select_side = *((int*)value);
505   if (strcmp(prop,"/do") == 0) g->u.mux.data_order = *((int*)value);
506 
507   if (g->wires[MUX_SEL])
508     g->wires[MUX_SEL]->orient = (g->orient + 2 + (g->u.mux.select_side ? 2 : 0) ) % 4;
509 }
510 
511 /*
512  * This function is called when reading a save file from a previous version of tkgate.
513  * Default pin placement of muxes changed in version 2.0.  This function sets flags on
514  * muxes to make it compatable with the new vesion, but only if the save file is older
515  * than 2.0.
516  */
Mux_VersionDelta(GCElement * g,Version * V)517 void Mux_VersionDelta(GCElement *g,Version *V)
518 {
519   Version x1 = {"1.8.7", 1, 8, 7};
520 
521   if (VersionCmp(V,&x1) >= 0)
522     return;
523 
524   switch (g->orient) {
525   case D_RIGHT :
526     g->u.mux.select_side = 0;
527     g->u.mux.data_order = 0;
528     g->wires[MUX_SEL]->orient = D_LEFT;
529     break;
530   case D_UP :
531     g->u.mux.select_side = 1;
532     g->u.mux.data_order = 1;
533     g->wires[MUX_SEL]->orient = D_DOWN;
534     break;
535   case D_LEFT :
536     g->u.mux.select_side = 0;
537     g->u.mux.data_order = 1;
538     g->wires[MUX_SEL]->orient = D_RIGHT;
539     break;
540   case D_DOWN :
541     g->u.mux.select_side = 1;
542     g->u.mux.data_order = 0;
543     g->wires[MUX_SEL]->orient = D_UP;
544     break;
545   }
546 }
547 
Mux_wireSnap(GCElement * g,GWire * w,int * mod,int retry)548 GWireNode *Mux_wireSnap(GCElement *g,GWire *w,int *mod,int retry)
549 {
550   int p,n;
551 
552   if (posongate(w,w->gate,&p,&n) == 0) {
553     GPadLoc *pd = &g->typeinfo->Pad[p].Loc[g->orient];
554     if (p == MUX_SEL && g->u.mux.select_side)
555       pd = &mux_sel_altloc[g->orient];
556 
557     *mod = wire_force(w,pd->dir,retry);
558   }
559   return w->nodes;
560 }
561 
562 /*****************************************************************************
563  *
564  * Generate primitive cell definition for muxes
565  *
566  * Parameters:
567  *    f			File to write cell to.
568  *    name		Name of cell to write.
569  *
570  *****************************************************************************/
Mux_WriteCellDef(FILE * f,GCellSpec * gcs)571 static void Mux_WriteCellDef(FILE *f,GCellSpec *gcs)
572 {
573   int multiPad = gcs->gc_multiPad;
574   int numBits = gcs->gc_numBits;
575   int selNumBits = required_bits(gcs->gc_multiPad);
576   int inv = (*gcs->gc_invSpec == 'N');
577   PrimParm primParm;
578 
579   PrimParm_init(&primParm);
580   PrimParm_intSet(&primParm,"NUMIN",multiPad);
581   PrimParm_rangeSet(&primParm,"IZ_RANGE",numBits);
582   PrimParm_intSet(&primParm,"IZ_BITS",numBits);
583   PrimParm_rangeSet(&primParm,"S_RANGE",selNumBits);
584   PrimParm_intSet(&primParm,"S_BITS",selNumBits);
585   PrimParm_invSet(&primParm,"invZ",inv);
586   Primitive_write(f,"mux",gcs,&primParm);
587 }
588 
init_mux()589 void init_mux()
590 {
591   Pixmap P;
592 
593   P = Pixmap_registerFromFile("mux","mux.b");
594   gateinfo_iconInit(&gate_mux_info,P,mux_iconDims,mux_iconBoldOffset);
595   RegisterGate(&gate_mux_info);
596 }
597