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