1 //--------------------------------------------------------------
2 // vlog2Spice
3 //
4 // Convert a structural verilog netlist (with power and ground
5 // nets!) into a SPICE netlist.
6 //
7 // Revision 0, 2006-11-11: First release by R. Timothy Edwards.
8 // Revision 1, 2009-07-13: Minor cleanups by Philipp Klaus Krause.
9 // Revision 2, 2013-05-10: Modified to take a library of subcell
10 //		definitions to use for determining port order.
11 // Revision 3, 2013-10-09: Changed from BDnet2BSpice to
12 //		blif2BSpice
13 // Revision 4, 2018-11-28: Changed from blif2BSpice to vlog2Spice
14 //
15 //--------------------------------------------------------------
16 
17 #include <stdio.h>
18 #include <string.h>
19 #include <stdlib.h>
20 #include <unistd.h>
21 #include <math.h>
22 #include <ctype.h>
23 #include <float.h>
24 
25 #include "hash.h"
26 #include "readverilog.h"
27 
28 #define LengthOfLine    	16384
29 
30 #define DO_INCLUDE   0x01
31 #define DO_DELIMITER 0x02
32 
33 /* Linked list of names of SPICE libraries to read */
34 typedef struct _linkedstring *LinkedStringPtr;
35 
36 typedef struct _linkedstring {
37     char *string;
38     LinkedStringPtr next;
39 } LinkedString;
40 
41 /* Function prototypes */
42 int write_output(struct cellrec *, LinkedStringPtr, char *, int);
43 int loc_getline(char s[], int lim, FILE *fp);
44 void helpmessage(FILE *);
45 
46 //--------------------------------------------------------
47 
main(int argc,char * argv[])48 int main (int argc, char *argv[])
49 {
50     int i, result = 0;
51     int flags = 0;
52     char *eptr;
53 
54     char *vloginname = NULL;
55     char *spclibname = NULL;
56     char *spcoutname = NULL;
57 
58     LinkedStringPtr spicelibs = NULL, newspicelib;
59 
60 
61     struct cellrec *topcell = NULL;
62 
63     while ((i = getopt(argc, argv, "hHidD:l:s:o:")) != EOF) {
64 	switch (i) {
65 	    case 'l':
66 		newspicelib = (LinkedStringPtr)malloc(sizeof(LinkedString));
67 		newspicelib->string = strdup(optarg);
68 		newspicelib->next = spicelibs;
69 		spicelibs = newspicelib;
70 		break;
71 	    case 'o':
72 		spcoutname = strdup(optarg);
73 		break;
74 	    case 'i':
75 		flags |= DO_INCLUDE;
76 		break;
77 	    case 'd':
78 		flags |= DO_DELIMITER;
79 		break;
80 	    case 'h':
81 	    case 'H':
82 		helpmessage(stdout);
83 		exit(0);
84 		break;
85 	    case 'D':
86 		eptr = strchr(optarg, '=');
87 		if (eptr != NULL) {
88 		    *eptr = '\0';
89 		    VerilogDefine(optarg, eptr + 1);
90 		    *eptr = '=';
91 		}
92 		else
93 		    VerilogDefine(optarg, "1");
94 		break;
95 	    default:
96 		fprintf(stderr, "Unknown switch %c\n", (char)i);
97 		helpmessage(stderr);
98 		exit(1);
99 		break;
100 	}
101     }
102 
103     if (optind < argc) {
104 	vloginname = strdup(argv[optind]);
105 	optind++;
106     }
107     else {
108 	fprintf(stderr, "Couldn't find a filename as input\n");
109 	helpmessage(stderr);
110 	exit(1);
111     }
112     optind++;
113 
114     topcell = ReadVerilog(vloginname);
115     if (topcell != NULL)
116 	result = write_output(topcell, spicelibs, spcoutname, flags);
117     else
118 	result = 1;	/* Return error code */
119 
120     return result;
121 }
122 
123 /*--------------------------------------------------------------*/
124 /* Verilog backslash notation, which is the most absurd syntax	*/
125 /* in the known universe, is fundamentally incompatible with	*/
126 /* SPICE.  The ad-hoc solution used by qflow is to replace	*/
127 /* the trailing space with another backslash such that the	*/
128 /* name is SPICE-compatible and the original syntax can be	*/
129 /* recovered when needed.					*/
130 /*--------------------------------------------------------------*/
131 
backslash_fix(char * netname)132 void backslash_fix(char *netname)
133 {
134     char *sptr;
135 
136     if (*netname == '\\')
137 	if ((sptr = strchr(netname, ' ')) != NULL)
138 	    *sptr = '\\';
139 }
140 
141 /*--------------------------------------------------------------*/
142 /* write_output ---  Write the SPICE netlist output		*/
143 /*								*/
144 /* ARGS: 							*/
145 /* RETURNS: 0 on success, 1 on error.				*/
146 /* SIDE EFFECTS: 						*/
147 /*--------------------------------------------------------------*/
148 
write_output(struct cellrec * topcell,LinkedStringPtr spicelibs,char * outname,int flags)149 int write_output(struct cellrec *topcell, LinkedStringPtr spicelibs,
150 		char *outname, int flags)
151 {
152     FILE *libfile;
153     FILE *outfile;
154     char *libname;
155     LinkedStringPtr curspicelib;
156 
157     struct netrec *net;
158     struct instance *inst;
159     struct portrec *port;
160     struct portrec *newport, *portlist, *lastport;
161 
162     int i, j, k, start, end, pcount = 1;
163     int result = 0;
164     int instidx, insti;
165 
166     char *lptr;
167     char *sp, *sp2;
168     char line[LengthOfLine];
169 
170     struct hashtable Libhash;
171 
172     if (outname != NULL) {
173 	outfile = fopen(outname, "w");
174 	if (outfile == NULL) {
175 	    fprintf(stderr, "Error:  Couldn't open file %s for writing\n", outname);
176 	    return 1;
177 	}
178     }
179     else
180 	outfile = stdout;
181 
182     /* Initialize SPICE library hash table */
183     InitializeHashTable(&Libhash, SMALLHASHSIZE);
184 
185     // Read one or more SPICE libraries of subcircuits and use them to define
186     // the order of pins that were read from LEF (which is not necessarily in
187     // SPICE pin order).
188 
189     for (curspicelib = spicelibs; curspicelib; curspicelib = curspicelib->next) {
190 	libname = curspicelib->string;
191 
192 	libfile = fopen(libname, "r");
193 	if (libfile == NULL) {
194 	    fprintf(stderr, "Couldn't open %s for reading\n", libname);
195 	    continue;
196 	}
197 
198 	/* Read SPICE library of subcircuits, if one is specified.	*/
199 	/* Retain the name and order of ports passed to each		*/
200 	/* subcircuit.							*/
201 
202 	j = 0;
203 	while (loc_getline(line, sizeof(line), libfile) > 0) {
204 	    if (!strncasecmp(line, ".subckt", 7)) {
205 		char *cellname;
206 		lastport = NULL;
207 		portlist = NULL;
208 
209 		/* Read cellname */
210 		sp = line + 7;
211 		while (isspace(*sp) && (*sp != '\n')) sp++;
212 		sp2 = sp;
213 		while (!isspace(*sp2) && (*sp2 != '\n')) sp2++;
214 		*sp2 = '\0';
215 
216 		/* Keep a record of the cellname until we generate the	*/
217 		/* hash entry for it					*/
218 		cellname = strdup(sp);
219 
220 		/* Now fill out the ordered port list */
221 		sp = sp2 + 1;
222 		while (isspace(*sp) && (*sp != '\n') && (*sp != '\0')) sp++;
223 		while (sp) {
224 
225 		    /* Move string pointer to next port name */
226 
227 		    if (*sp == '\n' || *sp == '\0') {
228 			loc_getline(line, sizeof(line), libfile);
229 			if (*line == '+')
230 			    sp = line + 1;
231 			else
232 			    break;
233 		    }
234 		    while (isspace(*sp) && (*sp != '\n')) sp++;
235 
236 		    /* Terminate port name and advance pointer */
237 		    sp2 = sp;
238 		    while (!isspace(*sp2) && (*sp2 != '\n') && (*sp2 != '\0')) sp2++;
239 		    *sp2 = '\0';
240 
241 		    /* Add port to list (in forward order) */
242 
243 		    newport = (struct portrec *)malloc(sizeof(struct portrec));
244 		    if (portlist == NULL)
245 			portlist = newport;
246 		    else
247 			lastport->next = newport;
248 		    lastport = newport;
249 		    newport->name = strdup(sp);
250 		    newport->net = NULL;
251 		    newport->direction = 0;
252 		    newport->next = NULL;
253 
254 		    sp = sp2 + 1;
255 		}
256 
257 		/* Read input to end of subcircuit */
258 
259 		if (strncasecmp(line, ".ends", 4)) {
260 		    while (loc_getline(line, sizeof(line), libfile) > 0)
261 			if (!strncasecmp(line, ".ends", 4))
262 			    break;
263 		}
264 
265 		/* Hash the new port record by cellname */
266 		HashPtrInstall(cellname, portlist, &Libhash);
267 		free(cellname);
268 	    }
269 	}
270 	fclose(libfile);
271     }
272 
273     /* Write output header */
274     fprintf(outfile, "*SPICE netlist created from verilog structural netlist module "
275 			"%s by vlog2Spice (qflow)\n", topcell->name);
276     fprintf(outfile, "*This file may contain array delimiters, not for use in simulation.\n");
277     fprintf(outfile, "\n");
278 
279     /* If flags has DO_INCLUDE then dump the contents of the	*/
280     /* libraries.  If 0, then just write a .include line.	*/
281 
282     for (curspicelib = spicelibs; curspicelib; curspicelib = curspicelib->next) {
283 	libname = curspicelib->string;
284 
285 	if (flags & DO_INCLUDE) {
286 	    libfile = fopen(libname, "r");
287 	    if (libfile != NULL) {
288 		fprintf(outfile, "** Start of included library %s\n", libname);
289 		/* Write out the subcircuit library file verbatim */
290 		while (loc_getline(line, sizeof(line), libfile) > 0)
291 		    fputs(line, outfile);
292 		fprintf(outfile, "** End of included library %s\n", libname);
293 		fclose(libfile);
294 	    }
295 	}
296 	else {
297 	    fprintf(outfile, ".include %s\n", libname);
298 	}
299     }
300     fprintf(outfile, "\n");
301 
302     /* Generate the subcircuit definition, adding power and ground nets */
303 
304     fprintf(outfile, ".subckt %s ", topcell->name);
305 
306     for (port = topcell->portlist; port; port = port->next) {
307 	if ((net = BusHashLookup(port->name, &topcell->nets)) != NULL) {
308 	    start = net->start;
309 	    end = net->end;
310 	}
311 	else start = end = -1;
312 
313 	if (start > end) {
314 	    int tmp;
315 	    tmp = start;
316 	    start = end;
317 	    end = tmp;
318 	}
319 	if (start == end) {
320 	    fprintf(outfile, "%s", port->name);
321 	    if (pcount++ % 8 == 7) {
322 		pcount = 0;
323 		fprintf(outfile, "\n+");
324 	    }
325 	    fprintf(outfile, " ");
326 	}
327 	else {
328 	    for (i = start; i <= end; i++) {
329 		/* Note that use of brackets is not legal SPICE syntax	*/
330 		/* but suffices for LVS and such.  Output should be	*/
331 		/* post-processed before using in simulation.		*/
332 		if (flags & DO_DELIMITER)
333 		    fprintf(outfile, "%s<%d>", port->name, i);
334 		else
335 		    fprintf(outfile, "%s[%d]", port->name, i);
336 
337 		if (pcount++ % 8 == 7) {
338 		    pcount = 0;
339 		    fprintf(outfile, "\n+");
340 		}
341 		fprintf(outfile, " ");
342 	    }
343 	}
344     }
345     fprintf(outfile, "\n\n");
346 
347     /* Output instances */
348 
349     instidx = -1;
350     for (inst = topcell->instlist; inst; ) {
351 	int argcnt;
352 	struct portrec *libport;
353 
354 	/* Check if the instance is an array */
355 	if (inst->arraystart != -1) {
356 	    if (instidx == -1) {
357 		instidx = inst->arraystart;
358 		insti = 0;
359 	    }
360 	    else if (inst->arraystart > inst->arrayend) {
361 		instidx--;
362 		insti++;
363 	    }
364 	    else if (inst->arraystart < inst->arrayend) {
365 		instidx++;
366 		insti++;
367 	    }
368 	}
369 
370 	if (inst->arraystart == -1)
371 	    fprintf(outfile, "X%s ", inst->instname);
372 	else
373 	    fprintf(outfile, "X%s[%d] ", inst->instname, instidx);
374         pcount = 1;
375 
376 	/* Search library records for subcircuit */
377 
378 	portlist = (struct portrec *)HashLookup(inst->cellname, &Libhash);
379 
380 	/* If no library entry exists, complain about arbitrary port	*/
381 	/* order, then use the instance's port names to create a port	*/
382 	/* record entry.						*/
383 
384 	if (portlist == NULL) {
385 	    fprintf(stderr, "Warning:  No SPICE subcircuit for %s.  Pin"
386 			" order will be arbitrary.\n", inst->cellname);
387 	    lastport = portlist = NULL;
388 	    for (libport = inst->portlist; libport; libport = libport->next) {
389 	    	newport = (struct portrec *)malloc(sizeof(struct portrec));
390 		if (portlist == NULL)
391 			portlist = newport;
392 		else
393 		    lastport->next = newport;
394 		lastport = newport;
395 		newport->name = strdup(libport->name);
396 		newport->net = NULL;
397 		newport->direction = libport->direction;
398 		newport->next = NULL;
399 	    }
400 	    HashPtrInstall(inst->cellname, portlist, &Libhash);
401 	}
402 
403 	/* Output pin connections in the order of the LEF record, which	*/
404 	/* has been forced to match the port order of the SPICE library	*/
405 	/* If there is no SPICE library record, output in the order of	*/
406 	/* the instance, which may or may not be correct.  In such a	*/
407 	/* case, flag a warning.					*/
408 
409 	argcnt = 0;
410 	for (libport = portlist; ; libport = libport->next) {
411 	    char *dptr, dsave;
412 	    int idx = 0, is_array = FALSE, match = FALSE;
413 
414 	    if (portlist != NULL && libport == NULL) break;
415 
416 	    argcnt++;
417 	    argcnt %= 8;
418 	    if (argcnt == 7)
419 		fprintf(outfile, "\n+ ");
420 
421 	    dptr = NULL;
422 	    if (libport) {
423 		for (dptr = libport->name; *dptr != '\0'; dptr++) {
424 		    if (*dptr == '[') {
425 			is_array = TRUE;
426 			dsave = *dptr;
427 			*dptr = '\0';
428 			sscanf(dptr + 1, "%d", &idx);
429 			break;
430 		    }
431 		}
432 	    }
433 
434 	    /* Treat arrayed instances like a bit-blasted port */
435 	    if ((is_array == FALSE) && (inst->arraystart != -1)) {
436 		is_array = TRUE;
437 		idx = insti;
438 	    }
439 
440 	    for (port = inst->portlist; port; port = port->next) {
441 		/* Find the port name in the instance which matches */
442 		/* the port name in the macro definition.	    */
443 
444 		if (libport) {
445 		    if (!strcasecmp(libport->name, port->name)) {
446 			match = TRUE;
447 			break;
448 		    }
449 		}
450 		else {
451 		    match = TRUE;
452 		    break;
453 		}
454 	    }
455 	    if (!match) {
456 		char *gptr;
457 
458 		/* Deal with annoying use of "!" as a global indicator,	*/
459 		/* which is sometimes used. . .				*/
460 		gptr = libport->name + strlen(libport->name) - 1;
461 		if (*gptr == '!') {
462 		    *gptr = '\0';
463 		    for (port = inst->portlist; port; port = port->next) {
464 			if (!strcasecmp(libport->name, port->name)) {
465 			    match = TRUE;
466 			    break;
467 			}
468 		    }
469 		    *gptr = '!';
470 		}
471 		else {
472 		    for (port = inst->portlist; port; port = port->next) {
473 			gptr = port->name + strlen(port->name) - 1;
474 			if (*gptr == '!') {
475 			    *gptr = '\0';
476 			    if (!strcasecmp(libport->name, port->name)) match = TRUE;
477 			    *gptr = '!';
478 			    if (match == TRUE) break;
479 			}
480 		    }
481 		}
482 	    }
483 	    if (!match) {
484 		fprintf(stderr, "Error:  Instance %s has no port %s!\n",
485 			inst->instname, libport->name);
486 	    }
487 	    else {
488 		if (flags & DO_DELIMITER) {
489 		    char *d1ptr, *d2ptr;
490 		    if ((d1ptr = strchr(port->net, '[')) != NULL) {
491 			if ((d2ptr = strchr(d1ptr + 1, ']')) != NULL) {
492 			    *d1ptr = '<';
493 			    *d2ptr = '>';
494 			}
495 		    }
496 		}
497 		if (is_array) {
498 		    char *portname = port->net;
499 		    if (*portname == '{') {
500 			char *epos, ssave;
501 			int k;
502 
503 			/* Bus notation "{a, b, c, ... }"	    */
504 			/* Go to the end and count bits backwards.  */
505 			/* until reaching the idx'th position.	    */
506 
507 			/* To be done:  Move GetIndexedNet() from   */
508 			/* vlog2Verilog.c to readverilog.c and call */
509 			/* it from here.  It is more complete than  */
510 			/* this implementation.			    */
511 
512 			while (*portname != '}' && *portname != '\0') portname++;
513 			for (k = 0; k <= idx; k++) {
514 			    epos = portname;
515 			    portname--;
516 			    while (*portname != ',' && portname > port->net)
517 				portname--;
518 			}
519 			if (*portname == ',') portname++;
520 			ssave = *epos;
521 			*epos = '\0';
522 			backslash_fix(portname);
523 			fprintf(outfile, "%s", portname);
524 			*epos = ssave;
525 		    }
526 		    else {
527 			struct netrec wb;
528 
529 			GetBus(portname, &wb, &topcell->nets);
530 
531 			if (wb.start < 0) {
532 			    /* portname is not a bus */
533 			    backslash_fix(portname);
534 			    fprintf(outfile, "%s", portname);
535 			}
536 			else {
537 			    int lidx;
538 			    if (wb.start < wb.end)
539 				lidx =  wb.start + idx;
540 			    else
541 				lidx = wb.start - idx;
542 			    /* portname is a partial or full bus */
543 			    dptr = strrchr(portname, '[');
544 			    if (dptr) *dptr = '\0';
545 			    backslash_fix(portname);
546 			    if (flags & DO_DELIMITER)
547 				fprintf(outfile, "%s<%d>", portname, lidx);
548 			    else
549 				fprintf(outfile, "%s[%d]", portname, lidx);
550 			    if (dptr) *dptr = '[';
551 			}
552 		    }
553 		}
554 		else {
555 		    backslash_fix(port->net);
556 		    fprintf(outfile, "%s", port->net);
557 		}
558 
559 		if (pcount++ % 8 == 7) {
560 		    pcount = 0;
561 		    fprintf(outfile, "\n+");
562 		}
563 		fprintf(outfile, " ");
564 	    }
565 
566 	    if (portlist == NULL) {
567 		fprintf(stdout, "Warning:  No defined subcircuit %s for "
568 				"instance %s!\n", inst->cellname, inst->instname);
569 		fprintf(stdout, "Pins will be output in arbitrary order.\n");
570 		break;
571 	    }
572 	    if (dptr != NULL) *dptr = '[';
573 	}
574 	fprintf(outfile, "%s\n", inst->cellname);
575 
576 	if ((inst->arraystart != -1) && (instidx != inst->arrayend)) continue;
577 	instidx = -1;
578 	inst = inst->next;
579     }
580     fprintf(outfile, "\n.ends\n");
581     fprintf(outfile, ".end\n");
582 
583     if (outname != NULL) fclose(outfile);
584 
585     return result;
586 }
587 
588 /*--------------------------------------------------------------*/
589 /*C loc_getline: read a line, return length			*/
590 /*								*/
591 /* ARGS: 							*/
592 /* RETURNS: 1 to OS						*/
593 /* SIDE EFFECTS: 						*/
594 /*--------------------------------------------------------------*/
595 
loc_getline(char * s,int lim,FILE * fp)596 int loc_getline(char *s, int lim, FILE *fp)
597 {
598     int c, i;
599 
600     i = 0;
601     while(--lim > 0 && (c = getc(fp)) != EOF && c != '\n')
602 	s[i++] = c;
603     if (c == '\n')
604 	s[i++] = c;
605     s[i] = '\0';
606     if (c == EOF) i = 0;
607     return i;
608 }
609 
610 /*--------------------------------------------------------------*/
611 /*C helpmessage - tell user how to use the program		*/
612 /*								*/
613 /*  ARGS: error code (0 = success, 1 = error)			*/
614 /*  RETURNS: 1 to OS						*/
615 /*  SIDE EFFECTS: 						*/
616 /*--------------------------------------------------------------*/
617 
helpmessage(FILE * fout)618 void helpmessage(FILE *fout)
619 {
620     fprintf(fout, "vlog2Spice [-options] netlist \n");
621     fprintf(fout, "\n");
622     fprintf(fout, "vlog2Spice converts a netlist in verilog format \n");
623     fprintf(fout, "to Spice subcircuit format. Output on stdout unless -o option used.\n");
624     fprintf(fout, "Input file must be a structural verilog netlist with power and ground.\n");
625     fprintf(fout, "\n");
626     fprintf(fout, "Options:\n");
627     fprintf(fout, "   -h          Print this message\n");
628     fprintf(fout, "   -i          Generate include statement for library, not a dump.\n");
629     fprintf(fout, "   -d          Convert array delimiter brackets to angle brackets.\n");
630     fprintf(fout, "   -D <key>=<value>  Preregister a verilog definition.\n");
631     fprintf(fout, "   -l <path>   Specify path to SPICE library of standard cells.\n");
632     fprintf(fout, "   -o <path>   Specify path to output SPICE file.\n");
633     fprintf(fout, "\n");
634 
635 } /* helpmessage() */
636 
637