summaryrefslogtreecommitdiff
path: root/src/vlog2Cel.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/vlog2Cel.c')
-rw-r--r--src/vlog2Cel.c571
1 files changed, 571 insertions, 0 deletions
diff --git a/src/vlog2Cel.c b/src/vlog2Cel.c
new file mode 100644
index 0000000..3829b88
--- /dev/null
+++ b/src/vlog2Cel.c
@@ -0,0 +1,571 @@
+//----------------------------------------------------------------
+// vlog2Cel
+//----------------------------------------------------------------
+// Generate a .cel file for the GrayWolf placement tool from a
+// verilog source, plus LEF files for the technology, standard
+// cells, and (if present) hard macros.
+//
+// Revision 0, 2018-12-1: First release by R. Timothy Edwards.
+//
+// This program is written in ISO C99.
+//----------------------------------------------------------------
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h> /* for getopt() */
+#include <math.h>
+#include <ctype.h>
+#include <float.h>
+
+#include "hash.h"
+#include "readverilog.h"
+#include "readlef.h"
+
+int write_output(struct cellrec *, int, char *);
+void helpmessage(FILE *outf);
+
+char *VddNet = NULL;
+char *GndNet = NULL;
+
+struct hashtable LEFhash;
+
+/*--------------------------------------------------------------*/
+
+int main (int argc, char *argv[])
+{
+ int i, units, result;
+
+ char *outfile = NULL;
+ char *vlogname = NULL;
+ struct cellrec *topcell;
+
+ VddNet = strdup("VDD");
+ GndNet = strdup("VSS");
+
+ InitializeHashTable(&LEFhash, SMALLHASHSIZE);
+ units = 100; /* Default value is centimicrons */
+
+ while ((i = getopt(argc, argv, "hHu:l:o:")) != EOF) {
+ switch (i) {
+ case 'h':
+ case 'H':
+ helpmessage(stdout);
+ return 0;
+ case 'u':
+ if (sscanf(optarg, "%d", &units) != 1) {
+ fprintf(stderr, "Cannot read integer units from \"%s\"\n", optarg);
+ }
+ break;
+ case 'l':
+ LefRead(optarg); /* Can be called multiple times */
+ break;
+ case 'o':
+ outfile = strdup(optarg);
+ break;
+ default:
+ fprintf(stderr, "Bad switch \"%c\"\n", (char)i);
+ helpmessage(stderr);
+ return 1;
+ }
+ }
+
+ if (optind < argc) {
+ vlogname = strdup(argv[optind]);
+ optind++;
+ }
+ else {
+ fprintf(stderr, "Couldn't find a filename as input\n");
+ helpmessage(stderr);
+ return 1;
+ }
+ optind++;
+
+ /* If any LEF files were read, hash the GateInfo list */
+ if (GateInfo != NULL) {
+ GATE gate;
+ for (gate = GateInfo; gate; gate = gate->next) {
+ HashPtrInstall(gate->gatename, gate, &LEFhash);
+ }
+ }
+
+ topcell = ReadVerilog(vlogname);
+ result = write_output(topcell, units, outfile);
+ return result;
+}
+
+/*--------------------------------------------------------------*/
+/* write_output: Generate the .cel file output. */
+/* */
+/* ARGS: */
+/* RETURNS: 0 on success, 1 on error */
+/* SIDE EFFECTS: */
+/*--------------------------------------------------------------*/
+
+int write_output(struct cellrec *topcell, int units, char *outfile)
+{
+ FILE *outfptr = stdout;
+ int result = 0;
+
+ struct netrec *net;
+ struct portrec *port;
+ struct instance *inst;
+
+ GATE gateginfo;
+
+ int i, j, layers, feedx, cellidx, kidx;
+ int llx, lly, cllx, clly, curx, cury;
+ int urx, ury, width, height, px, py;
+ int *pitchx, *pitchy;
+ int lvert = -1;
+ int arrayidx;
+ char *netsptr;
+
+ if (outfile != NULL) {
+ outfptr = fopen(outfile, "w");
+ if (outfptr == NULL) {
+ fprintf(stderr, "Error: Failed to open file %s for output\n",
+ outfile);
+ return 1;
+ }
+ }
+
+ /* Count route layers (just need a maximum for memory allocation) */
+ layers = LefGetMaxRouteLayer();
+
+ pitchx = (int *)calloc((layers + 1), sizeof(int));
+ pitchy = (int *)calloc((layers + 1), sizeof(int));
+
+ /* Pull pitch information from LEF database */
+
+ for (i = 0; i <= layers; i++) {
+ pitchx[i] = (int)(LefGetRoutePitchX(i) * (double)units + 0.5);
+ pitchy[i] = (int)(LefGetRoutePitchY(i) * (double)units + 0.5);
+ }
+
+ /* Find first vertical route that is not route 0 */
+ for (i = 1; i <= layers; i++) {
+ if (LefGetRouteOrientation(i) == 0) {
+ lvert = i;
+ break;
+ }
+ }
+
+ /* If X pitch on layer lvert is zero, then infinite loops happen */
+ if (lvert < 0) {
+ fprintf(stderr, "Error: Failed to get layer information; cannot continue.\n");
+ return 1;
+ }
+ else if (pitchx[lvert] <= 0) {
+ fprintf(stderr, "Error: Bad value %d for X pitch on vertical route"
+ " layer %d; cannot continue.\n",
+ pitchx[lvert], lvert);
+ return 1;
+ }
+
+ /* Write instances in the order of the input file */
+
+ cellidx = 1;
+ kidx = 1;
+ for (inst = topcell->instlist; inst; inst = inst->next) {
+ if (inst->cellname)
+ gateginfo = HashLookup(inst->cellname, &LEFhash);
+ else
+ gateginfo = NULL;
+
+ if (gateginfo == NULL) {
+ fprintf(stderr, "Error: Cell \"%s\" of instance \"%s\" not found"
+ " in LEF databases!\n", inst->cellname, inst->instname);
+ result = 1; // Set error result but continue output.
+ continue;
+ }
+
+ width = (int)(gateginfo->width * (double)units + 0.5);
+ height = (int)(gateginfo->height * (double)units + 0.5);
+
+ cllx = -(width >> 1);
+ clly = -(height >> 1);
+ curx = width + cllx;
+ cury = height + clly;
+
+ arrayidx = inst->arraystart;
+ while (1) {
+
+ if (arrayidx != -1)
+ fprintf(outfptr, "cell %d %s:%s[%d]\n", cellidx, inst->cellname,
+ inst->instname, arrayidx);
+ else
+ fprintf(outfptr, "cell %d %s:%s\n", cellidx, inst->cellname,
+ inst->instname);
+ fprintf(outfptr, "left %d right %d bottom %d top %d\n",
+ cllx, curx, clly, cury);
+ cellidx++;
+
+ /* Generate implicit feedthroughs to satisfy global routing, as */
+ /* many as will fit on the vertical track pitch. */
+
+ feedx = cllx + pitchx[lvert] / 2 + pitchx[lvert];
+ kidx = 1;
+ while (feedx < curx) {
+ fprintf(outfptr, "pin name twfeed%d signal TW_PASS_THRU layer %d %d %d\n",
+ kidx, lvert, feedx, clly);
+ fprintf(outfptr, " equiv name twfeed%d layer %d %d %d\n",
+ kidx, lvert, feedx, cury);
+ feedx += pitchx[lvert];
+ kidx++;
+ }
+
+ /* Write each port and net connection */
+ for (port = inst->portlist; port; port = port->next) {
+
+ /* Any one of these can be a bus: the port, the net, */
+ /* or the instance. */
+
+ int is_port_bus = FALSE;
+ int is_net_bus = FALSE;
+ int is_inst_bus = (arrayidx == -1) ? FALSE : TRUE;
+
+ /* Bus (array) cases:
+ * instance port net
+ * 1) single single single : simple case
+ * 2) single single array : not legal unless bus is 1-bit
+ * 3) single array single : copy net to each port bit
+ * 4) single array array : bit-wise matching net to port
+ * 5) array single single : copy net to each instance
+ * 6) array single array : bit-wise matching net to instance
+ * 7) array array single : copy net in both dimensions
+ * 8) array array array : bit-wise matching net to port, then
+ * copy to each instance.
+ */
+
+ /* Verilog backslash-escaped names have spaces that */
+ /* break pretty much every other format, so replace */
+ /* the space with the (much more sensible) second */
+ /* backslash. This can be detected and changed */
+ /* back by programs converting the syntax back into */
+ /* verilog. */
+
+ netsptr = port->net;
+ if (*port->net == '\\') {
+ netsptr = strchr(port->net, ' ');
+ if (netsptr != NULL) *netsptr = '\\';
+ }
+
+ /* Find the port name in the gate pin list */
+ for (j = 0; j < gateginfo->nodes; j++) {
+ if (!strcmp(port->name, gateginfo->node[j])) break;
+ }
+ if (j == gateginfo->nodes) {
+ /* Is this a bus? */
+ for (j = 0; j < gateginfo->nodes; j++) {
+ char *delim = strrchr(gateginfo->node[j], '[');
+ if (delim != NULL) {
+ *delim = '\0';
+ if (!strcmp(port->name, gateginfo->node[j]))
+ is_port_bus = TRUE;
+ *delim = '[';
+ if (is_port_bus) break;
+ }
+ }
+ }
+
+ /* Check if the net itself is an array */
+ net = HashLookup(port->net, &topcell->nets);
+ if (net && (net->start != -1)) {
+ char *sptr, *dptr, *cptr;
+
+ is_net_bus = TRUE;
+
+ /* However, if net name is a 1-bit bus subnet, then */
+ /* it is not considered to be a bus. Note that */
+ /* brackets inside a verilog backslash-escaped name */
+ /* are not array indicators. */
+
+ dptr = strchr(netsptr, '[');
+ if (dptr) {
+ cptr = strchr(dptr + 1, ':');
+ if (!cptr) {
+ is_net_bus = FALSE;
+ }
+ }
+ }
+
+ if (j == gateginfo->nodes) {
+ fprintf(stderr, "Error: Pin \"%s\" not found in LEF macro \"%s\"!\n",
+ port->name, gateginfo->gatename);
+ result = 1; // Set error result but continue output
+ }
+ else if (is_net_bus == FALSE) {
+ /* Pull pin position from first rectangle in taps list. This */
+ /* does not have to be accurate; just representative. */
+ int bufidx;
+ char *sigptr;
+ DSEG tap = gateginfo->taps[j];
+
+ /* If LEF file failed to specify pin geometry, then use cell center */
+ if (tap == NULL) {
+ px = cllx;
+ py = clly;
+ }
+ else {
+ llx = (int)(tap->x1 * (double)units + 0.5);
+ lly = (int)(tap->y1 * (double)units + 0.5);
+ urx = (int)(tap->x2 * (double)units + 0.5);
+ ury = (int)(tap->y2 * (double)units + 0.5);
+ px = cllx + ((llx + urx) / 2);
+ py = clly + ((lly + ury) / 2);
+ }
+
+ if (((sigptr = strstr(port->net, "_bF$buf")) != NULL) &&
+ ((sscanf(sigptr + 7, "%d", &bufidx)) == 1) &&
+ (gateginfo->direction[j] == PORT_CLASS_INPUT)) {
+ fprintf(outfptr, "pin_group\n");
+ *sigptr = '\0';
+ fprintf(outfptr, "pin name %s_bF$pin/%s ",
+ port->net, port->name);
+ *sigptr = '_';
+ fprintf(outfptr, "signal %s layer %d %d %d\n",
+ port->net, lvert, px, py);
+ fprintf(outfptr, "end_pin_group\n");
+ }
+ else {
+ fprintf(outfptr, "pin name %s signal %s layer %d %d %d\n",
+ port->name, port->net, lvert, px, py);
+ }
+ }
+ else { /* Handle arrays */
+ char *apin, *anet, *dptr, *cptr;
+ int a, pidx, armax, armin;
+
+ if ((is_inst_bus == TRUE) && (is_port_bus == FALSE) &&
+ (is_net_bus == TRUE)) {
+ armax = armin = arrayidx;
+ }
+ else {
+ armax = armin = 0;
+ for (j = 0; j < gateginfo->nodes; j++) {
+ char *delim, *sptr;
+
+ sptr = gateginfo->node[j];
+ if (*sptr == '\\') sptr = strchr(sptr, ' ');
+ if (sptr == NULL) sptr = gateginfo->node[j];
+ delim = strrchr(sptr, '[');
+ if (delim != NULL) {
+ *delim = '\0';
+ if (!strcmp(port->name, gateginfo->node[j])) {
+ if (sscanf(delim + 1, "%d", &pidx) == 1) {
+ if (pidx > armax) armax = pidx;
+ if (pidx < armin) armin = pidx;
+ }
+ }
+ *delim = '[';
+ }
+ }
+ }
+
+ /* To do: Need to check if array is high-to-low or low-to-high */
+ /* Presently assuming arrays are always defined high-to-low */
+
+ apin = (char *)malloc(strlen(port->name) + 15);
+ for (a = armax; a >= armin; a--) {
+ if (is_port_bus)
+ sprintf(apin, "%s[%d]", port->name, a);
+ else
+ sprintf(apin, "%s", port->name);
+
+ /* If net is not delimited by {...} then it is also */
+ /* an array. Otherwise, find the nth element in */
+ /* the brace-enclosed set. */
+
+ /* To do: if any component of the array is a vector */
+ /* then we need to count bits in that vector. */
+
+ if (*port->net == '{') {
+ int aidx;
+ char *sptr, ssave;
+ char *pptr = port->net + 1;
+ for (aidx = 0; aidx < (armax - a); aidx++) {
+ sptr = pptr;
+ while (*sptr != ',' && *sptr != '}') sptr++;
+ pptr = sptr + 1;
+ }
+ sptr = pptr;
+ if (*sptr != '\0') {
+ while (*sptr != ',' && *sptr != '}') sptr++;
+ ssave = *sptr;
+ *sptr = '\0';
+ anet = (char *)malloc(strlen(pptr) + 1);
+ sprintf(anet, "%s", pptr);
+ *sptr = ssave;
+ }
+ else {
+ anet = NULL; /* Must handle this error! */
+ }
+ }
+ else if (((dptr = strrchr(netsptr, '[')) != NULL) &&
+ ((cptr = strrchr(netsptr, ':')) != NULL)) {
+ int fhigh, flow, fidx;
+ sscanf(dptr + 1, "%d", &fhigh);
+ sscanf(cptr + 1, "%d", &flow);
+ if (fhigh > flow) fidx = fhigh - (armax - a);
+ else fidx = flow + (armax - a);
+ anet = (char *)malloc(strlen(port->net) + 15);
+ *dptr = '\0';
+ sprintf(anet, "%s[%d]", port->net, fidx);
+ *dptr = '[';
+ }
+ else {
+ anet = (char *)malloc(strlen(port->net) + 15);
+ sprintf(anet, "%s[%d]", port->net, a);
+ }
+
+ /* Find the corresponding port bit */
+ for (j = 0; j < gateginfo->nodes; j++) {
+ if (anet == NULL) break;
+ if (!strcmp(apin, gateginfo->node[j])) {
+
+ /* Pull pin position from first rectangle in taps */
+ /* list. This does not have to be accurate; just */
+ /* representative. */
+ int bufidx;
+ char *sigptr;
+ DSEG tap = gateginfo->taps[j];
+
+ /* If LEF file failed to specify pin geometry, then */
+ /* use cell center */
+ if (tap == NULL) {
+ px = cllx;
+ py = clly;
+ }
+ else {
+ llx = (int)(tap->x1 * (double)units + 0.5);
+ lly = (int)(tap->y1 * (double)units + 0.5);
+ urx = (int)(tap->x2 * (double)units + 0.5);
+ ury = (int)(tap->y2 * (double)units + 0.5);
+ px = cllx + ((llx + urx) / 2);
+ py = clly + ((lly + ury) / 2);
+ }
+
+ if (((sigptr = strstr(port->net, "_bF$buf")) != NULL) &&
+ ((sscanf(sigptr + 7, "%d", &bufidx)) == 1) &&
+ (gateginfo->direction[j] == PORT_CLASS_INPUT)) {
+ fprintf(outfptr, "pin_group\n");
+ *sigptr = '\0';
+ fprintf(outfptr, "pin name %s_bF$pin/%s ",
+ anet, apin);
+ *sigptr = '_';
+ fprintf(outfptr, "signal %s layer %d %d %d\n",
+ anet, lvert, px, py);
+ fprintf(outfptr, "end_pin_group\n");
+ }
+ else {
+ fprintf(outfptr, "pin name %s signal %s layer %d %d %d\n",
+ apin, anet, lvert, px, py);
+ }
+ break;
+ }
+ }
+ free(anet);
+ if (j == gateginfo->nodes) {
+ fprintf(stderr, "Error: Failed to find port %s in cell %s"
+ " port list!\n", port->name, inst->cellname);
+ }
+ }
+ free(apin);
+ }
+ }
+ if (inst->arraystart < inst->arrayend) {
+ if (++arrayidx > inst->arrayend) break;
+ }
+ else {
+ if (--arrayidx < inst->arrayend) break;
+ }
+ }
+ }
+
+ /* Compute size of a pin as the route pitch (px and py are half sizes). */
+ /* This prevents pins from being spaced tighter than the route pitches. */
+
+ px = pitchx[lvert] >> 1;
+ py = pitchy[lvert] >> 1;
+
+ /* Various attempts to get a valid value for the Y pitch. */
+ if (py == 0) py = pitchy[lvert - 1];
+ if (py == 0) py = pitchy[lvert + 1];
+ if (py == 0) py = pitchx[lvert];
+
+ /* Output pins from the verilog input/output list */
+
+ kidx = 1;
+ for (port = topcell->portlist; port; port = port->next) {
+ if (port->name == NULL) continue;
+ net = HashLookup(port->name, &topcell->nets);
+ if (net && net->start >= 0 && net->end >= 0) {
+ if (net->start > net->end) {
+ for (i = net->start; i >= net->end; i--) {
+ fprintf(outfptr, "pad %d name twpin_%s[%d]\n", kidx,
+ port->name, i);
+ fprintf(outfptr, "corners 4 %d %d %d %d %d %d %d %d\n",
+ -px, -py, -px, py, px, py, px, -py);
+ fprintf(outfptr, "pin name %s[%d] signal %s[%d] layer %d 0 0\n",
+ port->name, i, port->name, i, lvert);
+ kidx++;
+ }
+ }
+ else {
+ for (i = net->start; i <= net->end; i++) {
+ fprintf(outfptr, "pad %d name twpin_%s[%d]\n", kidx,
+ port->name, i);
+ fprintf(outfptr, "corners 4 %d %d %d %d %d %d %d %d\n",
+ -px, -py, -px, py, px, py, px, -py);
+ fprintf(outfptr, "pin name %s[%d] signal %s[%d] layer %d 0 0\n",
+ port->name, i, port->name, i, lvert);
+ kidx++;
+ }
+ }
+ }
+ else {
+ fprintf(outfptr, "pad %d name twpin_%s\n", kidx, port->name);
+ fprintf(outfptr, "corners 4 %d %d %d %d %d %d %d %d\n",
+ -px, -py, -px, py, px, py, px, -py);
+ fprintf(outfptr, "pin name %s signal %s layer %d 0 0\n",
+ port->name, port->name, lvert);
+ kidx++;
+ }
+ fprintf(outfptr, "\n");
+
+ }
+ if (outfile != NULL) fclose(outfptr);
+
+ fflush(stdout);
+ return result;
+}
+
+/*--------------------------------------------------------------*/
+/* C helpmessage - tell user how to use the program */
+/* */
+/* ARGS: */
+/* RETURNS: 1 to OS */
+/* SIDE EFFECTS: */
+/*--------------------------------------------------------------*/
+
+void helpmessage(FILE *outf)
+{
+ fprintf(outf, "vlog2Cel [-options] <netlist>\n");
+ fprintf(outf, "\n");
+ fprintf(outf, "vlog2Cel converts a netlist in verilog to a .cel file\n");
+ fprintf(outf, "for the GrayWolf placement tool. Output on stdout. LEF\n");
+ fprintf(outf, "files are required for determining cell dimensions.\n");
+ fprintf(outf, "\n");
+ fprintf(outf, "options:\n");
+ fprintf(outf, "\n");
+ fprintf(outf, " -h Print this message\n");
+ fprintf(outf, " -o <path> Output filename is <path>, otherwise output"
+ " on stdout.\n");
+ fprintf(outf, " -u <value> Use scale <value> for units (default "
+ "100 = centimicrons)\n");
+ fprintf(outf, " -l <path> Read LEF file from <path> (may be called multiple"
+ " times)\n");
+
+} /* helpmessage() */
+