2985 lines
86 KiB
C
2985 lines
86 KiB
C
/* "NETGEN", a netlist-specification tool for VLSI
|
|
Copyright (C) 1989, 1990 Massimo A. Sivilotti
|
|
Author's address: mass@csvax.cs.caltech.edu;
|
|
Caltech 256-80, Pasadena CA 91125.
|
|
|
|
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 (any 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; see the file copying. If not, write to
|
|
the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
|
|
|
|
/* verilog.c -- Input for Verilog format (structural verilog only) */
|
|
|
|
/* The verilog input is limited to "structural verilog", that is, */
|
|
/* verilog code that only defines inputs, outputs, local nodes (via the */
|
|
/* "wire" statement), and instanced modules. All modules are read down */
|
|
/* to the point where either a module (1) does not conform to the */
|
|
/* requirements above, or (2) has no module definition, in which case */
|
|
/* it is treated as a "black box" subcircuit and therefore becomes a */
|
|
/* low-level device. Because in verilog syntax all instances of a */
|
|
/* module repeat both the module pin names and the local connections, */
|
|
/* placeholders can be built without the need to redefine pins later, */
|
|
/* as must be done for formats like SPICE that don't declare pin names */
|
|
/* in an instance call. */
|
|
|
|
/* Note that use of 1'b0 or 1'b1 and similar variants is prohibited; */
|
|
/* the structural netlist should either declare VSS and/or VDD as */
|
|
/* inputs, or use tie-high and tie-low standard cells. */
|
|
|
|
/* Most verilog syntax has been captured below. Still to do: Handle */
|
|
/* vectors that are created on the fly using {...} notation, including */
|
|
/* the {n{...}} concatenation method. */
|
|
|
|
#include "config.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h> /* for calloc(), free(), getenv() */
|
|
#include <ctype.h> /* for isalnum() */
|
|
#ifndef IBMPC
|
|
#include <sys/types.h> /* for getpwnam() tilde expansion */
|
|
#include <pwd.h>
|
|
#endif
|
|
|
|
#ifdef TCL_NETGEN
|
|
#include <tcl.h>
|
|
#endif
|
|
|
|
#include "netgen.h"
|
|
#include "objlist.h"
|
|
#include "netfile.h"
|
|
#include "print.h"
|
|
#include "hash.h"
|
|
#include "netcmp.h"
|
|
|
|
// See netfile.c for explanation of delimiters. 'X'
|
|
// separates single-character delimiters from two-character delimiters.
|
|
#define VLOG_DELIMITERS "X///**/#((**)X,;:(){}[]="
|
|
#define VLOG_EQUATION_DELIMITERS "X///**/#((**)X,;:(){}[]=+-*/"
|
|
#define VLOG_PIN_NAME_DELIMITERS "X///**/(**)X()"
|
|
#define VLOG_PIN_CHECK_DELIMITERS "X///**/(**)X,;(){}"
|
|
#define VLOG_INTEGER_DELIMITERS "X///**/X;[]"
|
|
|
|
// Used by portelement structure "flags" record.
|
|
#define PORT_NOT_FOUND 0
|
|
#define PORT_FOUND 1
|
|
|
|
// Global storage for verilog parameters
|
|
struct hashdict verilogparams;
|
|
// Global storage for verilog definitions
|
|
struct hashdict verilogdefs;
|
|
// Record file pointer that is associated with the hash tables
|
|
int hashfile = -1;
|
|
|
|
// Global storage for wire buses
|
|
struct hashdict buses;
|
|
|
|
struct bus {
|
|
int start;
|
|
int end;
|
|
};
|
|
|
|
// Global storage for a 'for' loop
|
|
struct _loop {
|
|
char *loopvar;
|
|
int start;
|
|
int end;
|
|
long filepos;
|
|
};
|
|
|
|
struct _loop loop;
|
|
|
|
// Free a bus structure in the hash table during cleanup
|
|
|
|
int freebus (struct hashlist *p)
|
|
{
|
|
struct bus *wb;
|
|
|
|
wb = (struct bus *)(p->ptr);
|
|
FREE(wb);
|
|
return 1;
|
|
}
|
|
|
|
// Create a new bus structure
|
|
|
|
struct bus *NewBus()
|
|
{
|
|
struct bus *wb;
|
|
|
|
wb = (struct bus *)CALLOC(1, sizeof(struct bus));
|
|
if (wb == NULL) Fprintf(stderr, "NewBus: Core allocation error\n");
|
|
return (wb);
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Find a character c in a string, assuming that string may contain
|
|
// verilog names, where anything, including char c, may appear in the
|
|
// string if it is a backslash-escaped name. Only the position of
|
|
// character c outside of a verilog name is reported.
|
|
//-------------------------------------------------------------------------
|
|
|
|
char *strvchr(char *string, char c)
|
|
{
|
|
char *s;
|
|
|
|
for (s = string; *s != '\0'; s++) {
|
|
if (*s == '\\') {
|
|
while (*s != '\0' && *s != ' ') s++;
|
|
if (*s == '\0') {
|
|
Fprintf(stderr, "Error: Verilog backslash-escaped name"
|
|
" does not end with a space.\n");
|
|
break;
|
|
}
|
|
}
|
|
if (*s == c) return s;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/* Linked list, much like tokstack in netgen.c, but much simpler. */
|
|
/* contents are either a value, if oper = 0, or an operator. */
|
|
|
|
struct expr_stack {
|
|
int value;
|
|
char oper;
|
|
struct expr_stack *next;
|
|
struct expr_stack *last;
|
|
};
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Evaluate an expression for an array bound. This is much like
|
|
// ReduceOneExpression() in netgen.c, but only handles basic integer
|
|
// arithmetic (+,-,*,/) and grouping by parentheses.
|
|
//
|
|
// Returns 1 if successful, 0 on error.
|
|
// Evaluated result is placed in the integer pointed to by "valptr".
|
|
//-------------------------------------------------------------------------
|
|
|
|
int EvalExpr(struct expr_stack **stackptr, int *valptr)
|
|
{
|
|
struct expr_stack *texp, *start, *tmp, *stack;
|
|
int modified, value;
|
|
|
|
stack = *stackptr;
|
|
|
|
/* Most expressions are going to be just one number, so treat
|
|
* that as a special case.
|
|
*/
|
|
if ((stack->oper == '\0') && (stack->last == NULL)) {
|
|
*valptr = stack->value;
|
|
FREE(stack);
|
|
*stackptr = NULL;
|
|
return 1;
|
|
}
|
|
|
|
/* Move to the end of the stack, which is the beginning of the
|
|
* expression.
|
|
*/
|
|
for (start = stack; start->last; start = start->last);
|
|
|
|
/* Run passes until no more modifications can be made */
|
|
modified = TRUE;
|
|
|
|
while (modified == TRUE) {
|
|
modified = FALSE;
|
|
|
|
/* Find any + or - that is used as a sign of a value */
|
|
|
|
for (texp = start; texp; texp = texp->next) {
|
|
if ((texp->oper == '+') || (texp->oper == '-') || (texp->oper == '*') ||
|
|
(texp->oper == '/') || (texp->oper == '(')) {
|
|
|
|
if ((texp->next != NULL) && (texp->next->next != NULL) &&
|
|
(texp->next->next->oper == '\0')) {
|
|
|
|
if (texp->next->oper == '+') {
|
|
/* Remove the unnecessary sign */
|
|
tmp = texp->next;
|
|
texp->next = tmp->next;
|
|
tmp->next->last = texp;
|
|
FREE(tmp);
|
|
modified = TRUE;
|
|
}
|
|
else if (texp->next->oper == '-') {
|
|
/* Remove the sign and negate the value */
|
|
tmp = texp->next;
|
|
texp->next->next->value = -texp->next->next->value;
|
|
texp->next = tmp->next;
|
|
tmp->next->last = texp;
|
|
FREE(tmp);
|
|
modified = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Reduce (a * b) and (a / b) */
|
|
|
|
for (texp = start; texp; texp = texp->next) {
|
|
if ((texp->last != NULL) && (texp->next != NULL) && (texp->oper != '\0')) {
|
|
if ((texp->last->oper == '\0') && (texp->next->oper == '\0')) {
|
|
if (texp->oper == '*') {
|
|
/* Multiply */
|
|
texp->last->value *= texp->next->value;
|
|
/* Remove two items from the stack */
|
|
tmp = texp;
|
|
texp = texp->last;
|
|
texp->next = tmp->next->next;
|
|
if (tmp->next->next) tmp->next->next->last = texp;
|
|
FREE(tmp->next);
|
|
FREE(tmp);
|
|
modified = TRUE;
|
|
}
|
|
if (texp->oper == '/') {
|
|
/* Divide */
|
|
texp->last->value /= texp->next->value;
|
|
/* Remove two items from the stack */
|
|
tmp = texp;
|
|
texp = texp->last;
|
|
texp->next = tmp->next->next;
|
|
if (tmp->next->next) tmp->next->next->last = texp;
|
|
FREE(tmp->next);
|
|
FREE(tmp);
|
|
modified = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Reduce (a + b) and (a - b) */
|
|
|
|
for (texp = start; texp; texp = texp->next) {
|
|
if ((texp->last != NULL) && (texp->next != NULL) && (texp->oper != '\0')) {
|
|
/* Watch for (a)*b+c or a+b*(c); multiplies must be solved first */
|
|
if ((texp->last->last != NULL) && ((texp->last->last->oper == '*')
|
|
|| (texp->last->last->oper == '/'))) {
|
|
/* Do nothing */
|
|
}
|
|
else if ((texp->next->next != NULL) && ((texp->next->next->oper == '*')
|
|
|| (texp->next->next->oper == '/'))) {
|
|
/* Do nothing */
|
|
}
|
|
else if ((texp->last->oper == '\0') && (texp->next->oper == '\0')) {
|
|
if (texp->oper == '-') {
|
|
/* Subtract */
|
|
texp->last->value -= texp->next->value;
|
|
/* Remove two items from the stack */
|
|
tmp = texp;
|
|
texp = texp->last;
|
|
texp->next = tmp->next->next;
|
|
if (tmp->next->next) tmp->next->next->last = texp;
|
|
FREE(tmp->next);
|
|
FREE(tmp);
|
|
modified = TRUE;
|
|
}
|
|
if (texp->oper == '+') {
|
|
/* Add */
|
|
texp->last->value += texp->next->value;
|
|
/* Remove two items from the stack */
|
|
tmp = texp;
|
|
texp = texp->last;
|
|
texp->next = tmp->next->next;
|
|
if (tmp->next->next) tmp->next->next->last = texp;
|
|
FREE(tmp->next);
|
|
FREE(tmp);
|
|
modified = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Reduce (a) */
|
|
|
|
for (texp = start; texp; texp = texp->next) {
|
|
if ((texp->last != NULL) && (texp->next != NULL) && (texp->oper == '\0')) {
|
|
if ((texp->last->oper == '(') && (texp->next->oper == ')')) {
|
|
tmp = texp->last;
|
|
texp->last->oper = '\0';
|
|
texp->last->value = texp->value;
|
|
texp->last->next = texp->next->next;
|
|
if (texp->next->next) texp->next->next->last = texp->last;
|
|
FREE(texp->next);
|
|
FREE(texp);
|
|
texp = tmp;
|
|
modified = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
/* If only one numerical item remains, then place it in valptr and return 1 */
|
|
texp = start;
|
|
if ((texp->oper == '\0') && (texp->next == NULL)) {
|
|
*valptr = texp->value;
|
|
FREE(texp);
|
|
*stackptr = NULL;
|
|
return 1;
|
|
}
|
|
|
|
/* Clean up the stack before returning error status */
|
|
while (texp) {
|
|
tmp = texp;
|
|
texp = texp->next;
|
|
FREE(tmp);
|
|
}
|
|
*stackptr = NULL;
|
|
return 0;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Parse an expression which must resolve to a single integer.
|
|
// If it does, return "1" and put the integer in the return
|
|
// pointer "iptr". If not, return "0" and iptr is undefined.
|
|
// The expression may use parameters, standard arithmetic,
|
|
// and parenthetic grouping. This routine does not tokenize input
|
|
// but assumes that the entire expression is in the string "expr".
|
|
//-------------------------------------------------------------------------
|
|
|
|
int ParseIntegerExpression(char *expr, int *iptr)
|
|
{
|
|
int result, value;
|
|
char *sptr, *cptr, savec;
|
|
struct property *kl = NULL;
|
|
struct expr_stack *stack, *newexp;
|
|
|
|
stack = NULL;
|
|
savec = '\0';
|
|
sptr = expr;
|
|
result = 1;
|
|
|
|
while (sptr && (*sptr != '\0')) {
|
|
// Move sptr to first non-space character
|
|
while (isspace(*sptr)) sptr++;
|
|
|
|
// Tokenize. Look ahead to next delimeter and truncate string there.
|
|
cptr = sptr + 1;
|
|
if (isalnum(*sptr) || (*sptr == '_') || (*sptr == '$')) {
|
|
while (*cptr != '\0') {
|
|
if (isalnum(*cptr)) cptr++;
|
|
else if ((*cptr == '_') || (*cptr == '$')) cptr++;
|
|
else break;
|
|
}
|
|
}
|
|
savec = *cptr;
|
|
*cptr = '\0';
|
|
|
|
if (match(sptr, "+") || match(sptr, "-")
|
|
|| match(sptr, "*") || match(sptr, "/")
|
|
|| match(sptr, "(") || match(sptr, ")")) {
|
|
newexp = (struct expr_stack *)MALLOC(sizeof(struct expr_stack));
|
|
newexp->oper = *sptr;
|
|
newexp->value = 0;
|
|
newexp->next = NULL;
|
|
newexp->last = stack;
|
|
if (stack) stack->next = newexp;
|
|
stack = newexp;
|
|
|
|
sptr = cptr;
|
|
*cptr = savec;
|
|
continue;
|
|
}
|
|
if ((result = sscanf(sptr, "%d", &value)) != 1) {
|
|
|
|
// Is name in the parameter list?
|
|
kl = (struct property *)HashLookup(nexttok, &verilogparams);
|
|
if (kl == NULL) {
|
|
Printf("Value %s in expression is not a number or a parameter.\n",
|
|
sptr);
|
|
value = 0;
|
|
break;
|
|
}
|
|
else {
|
|
if (kl->type == PROP_STRING) {
|
|
result = sscanf(kl->pdefault.string, "%d", &value);
|
|
if (result != 1) {
|
|
Printf("Parameter %s has value %s that cannot be parsed"
|
|
" as an integer.\n",
|
|
nexttok, kl->pdefault.string);
|
|
value = 0;
|
|
break;
|
|
}
|
|
}
|
|
else if (kl->type == PROP_INTEGER) {
|
|
value = kl->pdefault.ival;
|
|
}
|
|
else if (kl->type == PROP_DOUBLE) {
|
|
value = (int)kl->pdefault.dval;
|
|
if ((double)value != kl->pdefault.dval) {
|
|
Printf("Parameter %s has value %g that cannot be parsed"
|
|
" as an integer.\n",
|
|
nexttok, kl->pdefault.dval);
|
|
value = 0;
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
Printf("Parameter %s has unknown type; don't know how"
|
|
" to parse.\n", nexttok);
|
|
value = 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
newexp = (struct expr_stack *)MALLOC(sizeof(struct expr_stack));
|
|
newexp->oper = '\0';
|
|
newexp->value = value;
|
|
newexp->next = NULL;
|
|
newexp->last = stack;
|
|
if (stack) stack->next = newexp;
|
|
stack = newexp;
|
|
|
|
/* Move to next token */
|
|
sptr = cptr;
|
|
*cptr = savec;
|
|
}
|
|
|
|
if (result != 0) {
|
|
if (stack == NULL) {
|
|
Printf("Empty array found.\n");
|
|
result = 0;
|
|
}
|
|
else if (EvalExpr(&stack, iptr) != 1) {
|
|
Printf("Bad expression found in array.\n");
|
|
result = 0;
|
|
}
|
|
}
|
|
|
|
/* In case of error, stack may need cleaning up */
|
|
while (stack != NULL) {
|
|
newexp = stack;
|
|
stack = stack->last;
|
|
FREE(newexp);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
// Get bus indexes from the notation name[a:b]. If there is only "name"
|
|
// then look up the name in the bus hash list and return the index bounds.
|
|
// Return 0 on success, 1 on syntax error, and -1 if signal is not a bus.
|
|
//
|
|
// Note that this routine relies on the delimiter characters including
|
|
// "[", ":", and "]" when calling NextTok.
|
|
//-------------------------------------------------------------------------
|
|
|
|
int GetBusTok(struct bus *wb)
|
|
{
|
|
int result, start, end, value;
|
|
struct property *kl = NULL;
|
|
struct expr_stack *stack, *newexp;
|
|
|
|
if (wb == NULL) return 0;
|
|
|
|
wb->start = -1;
|
|
wb->end = -1;
|
|
stack = NULL;
|
|
|
|
/* Parse a value for array bounds [a : b], including possible use */
|
|
/* of parameters, definitions, and basic arithmetic. */
|
|
|
|
if (match(nexttok, "[")) {
|
|
start = end = -1;
|
|
while (nexttok) {
|
|
SkipTokComments(VLOG_EQUATION_DELIMITERS);
|
|
if (match(nexttok, "]")) {
|
|
result = 1;
|
|
if (stack == NULL) {
|
|
Printf("Empty array found.\n");
|
|
return 1;
|
|
}
|
|
if (EvalExpr(&stack, &end) != 1) {
|
|
Printf("Bad expression found in array.\n");
|
|
return 1;
|
|
}
|
|
if (start == -1) start = end; // Single bit
|
|
break;
|
|
}
|
|
if (match(nexttok, ":")) {
|
|
if (stack == NULL) {
|
|
Printf("Empty array start found.\n");
|
|
return 1;
|
|
}
|
|
if (EvalExpr(&stack, &start) != 1) {
|
|
Printf("Bad expression found in array.\n");
|
|
return 1;
|
|
}
|
|
continue;
|
|
}
|
|
else if (match(nexttok, "+") || match(nexttok, "-")
|
|
|| match(nexttok, "*") || match(nexttok, "/")
|
|
|| match(nexttok, "(") || match(nexttok, ")")) {
|
|
newexp = (struct expr_stack *)MALLOC(sizeof(struct expr_stack));
|
|
newexp->oper = *nexttok;
|
|
newexp->value = 0;
|
|
newexp->next = NULL;
|
|
newexp->last = stack;
|
|
if (stack) stack->next = newexp;
|
|
stack = newexp;
|
|
continue;
|
|
}
|
|
if ((result = sscanf(nexttok, "%d", &value)) != 1) {
|
|
|
|
// Is name in the parameter list?
|
|
kl = (struct property *)HashLookup(nexttok, &verilogparams);
|
|
if (kl == NULL) {
|
|
Printf("Array value %s is not a number or a parameter.\n",
|
|
nexttok);
|
|
value = 0;
|
|
break;
|
|
}
|
|
else {
|
|
if (kl->type == PROP_STRING) {
|
|
result = sscanf(kl->pdefault.string, "%d", &value);
|
|
if (result != 1) {
|
|
Printf("Parameter %s has value %s that cannot be parsed"
|
|
" as an integer.\n",
|
|
nexttok, kl->pdefault.string);
|
|
value = 0;
|
|
break;
|
|
}
|
|
}
|
|
else if (kl->type == PROP_INTEGER) {
|
|
value = kl->pdefault.ival;
|
|
}
|
|
else if (kl->type == PROP_DOUBLE) {
|
|
value = (int)kl->pdefault.dval;
|
|
if ((double)value != kl->pdefault.dval) {
|
|
Printf("Parameter %s has value %g that cannot be parsed"
|
|
" as an integer.\n",
|
|
nexttok, kl->pdefault.dval);
|
|
value = 0;
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
Printf("Parameter %s has unknown type; don't know how"
|
|
" to parse.\n", nexttok);
|
|
value = 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
newexp = (struct expr_stack *)MALLOC(sizeof(struct expr_stack));
|
|
newexp->oper = '\0';
|
|
newexp->value = value;
|
|
newexp->next = NULL;
|
|
newexp->last = stack;
|
|
if (stack) stack->next = newexp;
|
|
stack = newexp;
|
|
}
|
|
|
|
wb->start = start;
|
|
wb->end = end;
|
|
|
|
/* In case of error, stack may need cleaning up */
|
|
while (stack != NULL) {
|
|
newexp = stack;
|
|
stack = stack->last;
|
|
FREE(newexp);
|
|
}
|
|
|
|
while (!match(nexttok, "]")) {
|
|
SkipTokComments(VLOG_DELIMITERS);
|
|
if (nexttok == NULL) {
|
|
Printf("End of file reached while reading array bounds.\n");
|
|
return 1;
|
|
}
|
|
else if (match(nexttok, ";")) {
|
|
// Better than reading to end-of-file, give up on end-of-statement
|
|
Printf("End of statement reached while reading array bounds.\n");
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
struct bus *hbus;
|
|
hbus = (struct bus *)HashLookup(nexttok, &buses);
|
|
if (hbus != NULL) {
|
|
wb->start = hbus->start;
|
|
wb->end = hbus->end;
|
|
}
|
|
else
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
// GetBus() is similar to GetBusTok() (see above), but it parses from
|
|
// a string instead of the input tokenizer, and expressions are not
|
|
// allowed.
|
|
//--------------------------------------------------------------------
|
|
|
|
int GetBus(char *astr, struct bus *wb)
|
|
{
|
|
char *colonptr, *brackstart, *brackend, *sigend, sdelim, *aastr;
|
|
int result, start, end;
|
|
struct property *kl = NULL;
|
|
struct expr_stack *stack, *newexp;
|
|
|
|
if (wb == NULL) return 0;
|
|
else {
|
|
wb->start = -1;
|
|
wb->end = -1;
|
|
}
|
|
|
|
/* Check for wire bundles. If there are bundles, process each */
|
|
/* section separately and concatenate the sizes. */
|
|
/* To be done: Handle nested bundles, including N-times concatenation */
|
|
|
|
if (*astr == '{') {
|
|
struct bus wbb;
|
|
|
|
astr++;
|
|
wb->end = 0;
|
|
while((*astr != '\0') && (*astr != '}')) {
|
|
sigend = strvchr(astr, ',');
|
|
if (sigend == NULL) sigend = strvchr(astr, '}');
|
|
if (sigend == NULL) {
|
|
Printf("Badly formed wire bundle \"%s\"\n", astr - 1);
|
|
return 1;
|
|
}
|
|
sdelim = *sigend;
|
|
*sigend = '\0';
|
|
if (GetBus(astr, &wbb) == 0) {
|
|
if (wbb.start > wbb.end)
|
|
wb->start += (wbb.start - wbb.end + 1);
|
|
else
|
|
wb->start += (wbb.end - wbb.start + 1);
|
|
}
|
|
else {
|
|
wb->start++;
|
|
}
|
|
*sigend = sdelim;
|
|
astr = sigend + 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// Delimiters may appear in backslash-escaped names. . . ignore these.
|
|
aastr = astr;
|
|
if (*aastr == '\\') {
|
|
aastr++;
|
|
while (*aastr != ' ' && *aastr != '\\' && *aastr != '\0') aastr++;
|
|
}
|
|
|
|
brackstart = strvchr(aastr, '[');
|
|
if (brackstart != NULL) {
|
|
brackend = strvchr(aastr, ']');
|
|
if (brackend == NULL) {
|
|
Printf("Badly formed array notation \"%s\"\n", astr);
|
|
return 1;
|
|
}
|
|
*brackend = '\0';
|
|
colonptr = strvchr(aastr, ':');
|
|
if (colonptr) *colonptr = '\0';
|
|
result = ParseIntegerExpression(brackstart + 1, &start);
|
|
if (colonptr) *colonptr = ':';
|
|
if (result != 1) {
|
|
Printf("Badly formed array notation \"%s\"\n", astr);
|
|
*brackend = ']';
|
|
return 1;
|
|
}
|
|
if (colonptr)
|
|
result = ParseIntegerExpression(colonptr + 1, &end);
|
|
else {
|
|
result = 1;
|
|
end = start; // Single bit
|
|
}
|
|
*brackend = ']';
|
|
if (result != 1) {
|
|
Printf("Badly formed array notation \"%s\"\n", astr);
|
|
return 1;
|
|
}
|
|
wb->start = start;
|
|
wb->end = end;
|
|
}
|
|
else {
|
|
struct bus *hbus;
|
|
hbus = (struct bus *)HashLookup(astr, &buses);
|
|
if (hbus != NULL) {
|
|
wb->start = hbus->start;
|
|
wb->end = hbus->end;
|
|
}
|
|
else
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
// Output a Verilog Module. Note that since Verilog does not describe
|
|
// low-level devices like transistors, capacitors, etc., then this
|
|
// format is limited to black-box subcircuits. Cells containing any
|
|
// such low-level devices are ignored.
|
|
//--------------------------------------------------------------------
|
|
|
|
void VerilogModule(struct nlist *tp)
|
|
{
|
|
struct objlist *ob, *mob;
|
|
int node, maxnode;
|
|
char *model;
|
|
struct tokstack *stackptr;
|
|
|
|
/* 1st pass: traverse list of objects for low-level device checks */
|
|
|
|
for (ob = tp->cell; ob != NULL; ob = ob->next) {
|
|
if (ob->type == FIRSTPIN) {
|
|
struct nlist *tp2;
|
|
|
|
tp2 = LookupCellFile(ob->model.class, tp->file);
|
|
|
|
/* Check the class. Low-level devices cause the */
|
|
/* routine to return without generating output. */
|
|
|
|
switch (tp2->class) {
|
|
case CLASS_NMOS4: case CLASS_PMOS4: case CLASS_FET4:
|
|
case CLASS_NMOS: case CLASS_PMOS: case CLASS_FET3:
|
|
case CLASS_FET: case CLASS_ECAP:
|
|
case CLASS_NPN: case CLASS_PNP: case CLASS_BJT:
|
|
case CLASS_RES: case CLASS_RES3:
|
|
case CLASS_DIODE: case CLASS_INDUCTOR:
|
|
case CLASS_CAP: case CLASS_CAP3:
|
|
case CLASS_XLINE:
|
|
return;
|
|
case CLASS_SUBCKT: case CLASS_MODULE:
|
|
break;
|
|
default:
|
|
Printf ("Bad device class \"%s\" found.\n", tp2->class);
|
|
break; /* ignore it. . . */
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Check to see that all children have been dumped first */
|
|
|
|
for (ob = tp->cell; ob != NULL; ob = ob->next) {
|
|
if (ob->type == FIRSTPIN) {
|
|
struct nlist *tp2;
|
|
|
|
tp2 = LookupCellFile(ob->model.class, tp->file);
|
|
if ((tp2 != NULL) && !(tp2->dumped) && (tp2->class == CLASS_SUBCKT))
|
|
VerilogModule(tp2);
|
|
}
|
|
}
|
|
|
|
/* Print module pin list */
|
|
|
|
FlushString("module %s (\n",tp->name);
|
|
for (ob = tp->cell; ob != NULL; ob = ob->next)
|
|
if (IsPortInPortlist(ob, tp)) FlushString("input %s,\n", ob->name);
|
|
FlushString(");\n");
|
|
|
|
/* Print names of all nodes as 'wire' statements */
|
|
|
|
maxnode = 0;
|
|
for (ob = tp->cell; ob != NULL; ob = ob->next)
|
|
if (ob->node > maxnode) maxnode = ob->node;
|
|
|
|
/* was: for (node = 0; node <= maxnode; node++) */
|
|
for (node = 1; node <= maxnode; node++)
|
|
FlushString(" wire %s;\n", NodeName(tp, node));
|
|
|
|
/* 2nd pass: traverse list of objects for output */
|
|
|
|
for (ob = tp->cell; ob != NULL; ob = ob->next) {
|
|
if (ob->type == FIRSTPIN) {
|
|
int drain_node, gate_node, source_node;
|
|
struct nlist *tp2;
|
|
|
|
tp2 = LookupCellFile(ob->model.class, tp->file);
|
|
model = tp2->name;
|
|
|
|
/* Check the class. Low-level devices cause the routine to */
|
|
/* return value 1 (don't output). */
|
|
|
|
switch (tp2->class) {
|
|
case CLASS_SUBCKT: case CLASS_MODULE:
|
|
break;
|
|
default:
|
|
Printf ("Bad device class found.\n");
|
|
continue; /* ignore it. . . */
|
|
}
|
|
|
|
FlushString("%s %s (\n", model, ob->instance.name);
|
|
|
|
/* Print out nodes. */
|
|
|
|
mob = tp2->cell;
|
|
while (ob) {
|
|
if (ob->type >= FIRSTPIN)
|
|
FlushString(".%s(%s),\n", mob->name, ob->name);
|
|
ob = ob->next;
|
|
mob = mob->next;
|
|
if (ob->next && ob->next->type <= FIRSTPIN) break;
|
|
}
|
|
FlushString(");\n", model, ob->instance.name);
|
|
}
|
|
}
|
|
|
|
FlushString("endmodule\n");
|
|
tp->dumped = 1;
|
|
}
|
|
|
|
/* Write a Verilog module (top-level routine) */
|
|
|
|
void VerilogTop(char *name, int fnum, char *filename)
|
|
{
|
|
struct nlist *tp;
|
|
char FileName[500];
|
|
|
|
tp = LookupCellFile(name, fnum);
|
|
|
|
if (tp == NULL) {
|
|
Printf ("No cell '%s' found.\n", name);
|
|
return;
|
|
}
|
|
|
|
if (filename == NULL || strlen(filename) == 0)
|
|
SetExtension(FileName, name, VERILOG_EXTENSION);
|
|
else
|
|
SetExtension(FileName, filename, VERILOG_EXTENSION);
|
|
|
|
if (!OpenFile(FileName, 80)) {
|
|
perror("write verilog: Unable to open output file.");
|
|
return;
|
|
}
|
|
ClearDumpedList();
|
|
/* Start with general information in comment lines at the top */
|
|
FlushString("/*\n");
|
|
FlushString(" * Verilog structural netlist for cell %s\n", name);
|
|
FlushString(" * Written by Netgen %s.%s\n\n", NETGEN_VERSION, NETGEN_REVISION);
|
|
FlushString(" */\n");
|
|
VerilogModule(tp);
|
|
CloseFile(FileName);
|
|
}
|
|
|
|
/* If any pins are marked unconnected, see if there are */
|
|
/* other pins of the same name that have connections. */
|
|
|
|
void CleanupModule() {
|
|
int maxnode = 0;
|
|
int has_submodules = FALSE;
|
|
struct objlist *sobj, *nobj, *lobj, *pobj;
|
|
struct objlist *myLastPort, *object_it, *myNextObject;
|
|
|
|
if (CurrentCell == NULL) return;
|
|
|
|
myLastPort = NULL;
|
|
|
|
/* Reorder objects so that all ports come first, before nodes, because
|
|
* parts of the code depend on it.
|
|
*/
|
|
|
|
for (object_it = CurrentCell->cell; object_it && object_it->type <= 0;
|
|
object_it = myNextObject ) {
|
|
|
|
myNextObject = object_it->next;
|
|
if (!myNextObject) // end of list
|
|
continue;
|
|
|
|
if (myLastPort == NULL) {
|
|
if (object_it->type == PORT) {
|
|
myLastPort = object_it; // port at begining of list
|
|
myNextObject = object_it; // otherwise skips one
|
|
}
|
|
else if (myNextObject->type == PORT) {
|
|
object_it->next = myNextObject->next;
|
|
myNextObject->next = CurrentCell->cell;
|
|
CurrentCell->cell = myNextObject;
|
|
myLastPort = myNextObject;
|
|
}
|
|
}
|
|
else if (myNextObject->type == PORT) {
|
|
object_it->next = myNextObject->next;
|
|
myNextObject->next = myLastPort->next;
|
|
myLastPort->next = myNextObject;
|
|
myLastPort = myNextObject;
|
|
}
|
|
}
|
|
|
|
for (sobj = CurrentCell->cell; sobj; sobj = sobj->next)
|
|
if (sobj->node > maxnode)
|
|
maxnode = sobj->node + 1;
|
|
|
|
lobj = NULL;
|
|
for (sobj = CurrentCell->cell; sobj != NULL;) {
|
|
nobj = sobj->next;
|
|
if (sobj->type == FIRSTPIN)
|
|
has_submodules = TRUE;
|
|
if (sobj->node < 0) {
|
|
if (IsPort(sobj) && sobj->model.port == PROXY)
|
|
sobj->node = maxnode++;
|
|
else if (IsPort(sobj)) {
|
|
for (pobj = CurrentCell->cell; pobj && (pobj->type == PORT);
|
|
pobj = pobj->next) {
|
|
if (pobj == sobj) continue;
|
|
if (match(pobj->name, sobj->name) && pobj->node >= 0) {
|
|
sobj->node = pobj->node;
|
|
break;
|
|
}
|
|
}
|
|
lobj = sobj;
|
|
}
|
|
else
|
|
lobj = sobj;
|
|
}
|
|
else
|
|
lobj = sobj;
|
|
sobj = nobj;
|
|
}
|
|
if (has_submodules == FALSE) SetClass(CLASS_MODULE);
|
|
|
|
if (buses.hashtab != NULL) {
|
|
RecurseHashTable(&buses, freebus);
|
|
HashKill(&buses);
|
|
}
|
|
}
|
|
|
|
/*------------------------------------------------------*/
|
|
/* Structure for stacking nested module definitions */
|
|
/*------------------------------------------------------*/
|
|
|
|
/* Forward declarations */
|
|
extern void IncludeVerilog(char *, int, struct cellstack **, int);
|
|
|
|
/* External declarations (from spice.c) */
|
|
extern void PushStack(char *cellname, struct cellstack **top);
|
|
extern void PopStack(struct cellstack **top);
|
|
|
|
/*------------------------------------------------------*/
|
|
/* Callback routine for FindInstanceOf() */
|
|
/* NOTE: This casts a (struct objlist) pointer to a */
|
|
/* (struct nlist) pointer for the purpose of using */
|
|
/* RecurseCellHashTable2(). FindInstanceOf() casts it */
|
|
/* back into a (struct objlist) pointer. */
|
|
/*------------------------------------------------------*/
|
|
|
|
struct nlist *findInstance(struct hashlist *p, void *clientdata)
|
|
{
|
|
struct nlist *ptr;
|
|
struct objlist *ob;
|
|
struct nlist *tref = (struct nlist *)clientdata;
|
|
|
|
ptr = (struct nlist *)(p->ptr);
|
|
if (ptr->file != tref->file) return NULL;
|
|
|
|
ob = LookupInstance(tref->name, ptr);
|
|
return (struct nlist *)ob;
|
|
}
|
|
|
|
/*------------------------------------------------------*/
|
|
/* Routine to find the first instance of a cell */
|
|
/*------------------------------------------------------*/
|
|
|
|
struct objlist *FindInstanceOf(struct nlist *tc)
|
|
{
|
|
return (struct objlist *)RecurseCellHashTable2(findInstance, (void *)tc);
|
|
}
|
|
|
|
/*------------------------------------------------------*/
|
|
/* Given a reference cell pointer tref and a port name */
|
|
/* portname, check if portname is a port of tref. If */
|
|
/* not, then call Port() to add one. If tref is NULL, */
|
|
/* then always add the port. */
|
|
/*------------------------------------------------------*/
|
|
|
|
void CheckPort(struct objlist *tref, char *portname)
|
|
{
|
|
struct objlist *ob;
|
|
|
|
if (tref != NULL) {
|
|
for (ob = CurrentCell->cell; ob && (ob->type == PORT); ob = ob->next) {
|
|
if ((*matchfunc)(ob->name, portname))
|
|
return;
|
|
}
|
|
}
|
|
Port(portname);
|
|
}
|
|
|
|
/*------------------------------------------------------*/
|
|
/* Read a verilog structural netlist */
|
|
/*------------------------------------------------------*/
|
|
|
|
void ReadVerilogFile(char *fname, int filenum, struct cellstack **CellStackPtr,
|
|
int blackbox)
|
|
{
|
|
int cdnum = 1, rdnum = 1, i, ival;
|
|
int warnings = 0, hasports, inlined_decls = 0, localcount = 1;
|
|
double dval;
|
|
char devtype, in_module, in_param;
|
|
char *eqptr, *matchptr;
|
|
struct keyvalue *kvlist = NULL;
|
|
char inst[MAX_STR_LEN], model[MAX_STR_LEN], portname[MAX_STR_LEN], pkey[MAX_STR_LEN];
|
|
struct nlist *tp, *tpsave;
|
|
struct objlist *parent, *sobj, *nobj, *lobj, *pobj, *cref;
|
|
|
|
inst[MAX_STR_LEN-1] = '\0';
|
|
model[MAX_STR_LEN-1] = '\0';
|
|
in_module = (char)0;
|
|
in_param = (char)0;
|
|
|
|
loop.loopvar = NULL;
|
|
|
|
while (!EndParseFile()) {
|
|
|
|
SkipTokComments(VLOG_DELIMITERS); /* get the next token */
|
|
if ((EndParseFile()) && (nexttok == NULL)) break;
|
|
else if (nexttok == NULL)
|
|
break;
|
|
|
|
/* Ignore end-of-statement markers */
|
|
else if (match(nexttok, ";"))
|
|
continue;
|
|
|
|
/* Ignore primitive definitions */
|
|
else if (match(nexttok, "primitive")) {
|
|
while (1) {
|
|
SkipNewLine(VLOG_DELIMITERS);
|
|
SkipTokComments(VLOG_DELIMITERS);
|
|
if (EndParseFile()) break;
|
|
if (match(nexttok, "endprimitive")) {
|
|
in_module = 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Handle parameters by treating as a localparam or definition. */
|
|
/* Currently anything other than a constant value is not handled */
|
|
/* and so will flag a warning. */
|
|
|
|
else if (match(nexttok, "parameter") || match(nexttok, "localparam")) {
|
|
char *paramkey = NULL;
|
|
char *paramval = NULL;
|
|
|
|
// Pick up key = value pairs and store in current cell. Look only
|
|
// at the keyword before "=". Then set the definition as everything
|
|
// remaining in the line, excluding comments, until the end-of-statement
|
|
|
|
while (nexttok != NULL)
|
|
{
|
|
struct property *kl = NULL;
|
|
|
|
/* Parse for parameters used in expressions. Save */
|
|
/* parameters in the "verilogparams" hash table. */
|
|
|
|
SkipTok(VLOG_DELIMITERS);
|
|
if ((nexttok == NULL) || (nexttok[0] == '\0')) break;
|
|
if (match(nexttok, "=")) {
|
|
/* Pick up remainder of statement */
|
|
while (nexttok != NULL) {
|
|
SkipTokNoNewline("X///**/X;,");
|
|
if (nexttok == NULL) break;
|
|
if (match(nexttok, ";") || match(nexttok, ",")) break;
|
|
if (paramval == NULL) paramval = strsave(nexttok);
|
|
else {
|
|
char *paramlast;
|
|
/* Append nexttok to paramval */
|
|
paramlast = paramval;
|
|
paramval = (char *)MALLOC(strlen(paramlast) + strlen(nexttok)
|
|
+ 2);
|
|
sprintf(paramval, "%s %s", paramlast, nexttok);
|
|
FREE(paramlast);
|
|
}
|
|
}
|
|
|
|
kl = NewProperty();
|
|
kl->key = strsave(paramkey);
|
|
kl->idx = 0;
|
|
kl->merge = MERGE_NONE;
|
|
|
|
if (ConvertStringToInteger(paramval, &ival) == 1) {
|
|
kl->type = PROP_INTEGER;
|
|
kl->slop.ival = 0;
|
|
kl->pdefault.ival = ival;
|
|
}
|
|
else if (ConvertStringToFloat(paramval, &dval) == 1) {
|
|
kl->type = PROP_DOUBLE;
|
|
kl->slop.dval = 0.01;
|
|
kl->pdefault.dval = dval;
|
|
}
|
|
else {
|
|
kl->type = PROP_STRING;
|
|
kl->slop.dval = 0.0;
|
|
kl->pdefault.string = strsave(paramval);
|
|
}
|
|
|
|
HashPtrInstall(paramkey, kl, &verilogparams);
|
|
FREE(paramval);
|
|
paramval = NULL;
|
|
|
|
if ((nexttok == NULL) || match(nexttok, ";")) break;
|
|
}
|
|
else {
|
|
if (paramkey != NULL) FREE(paramkey);
|
|
paramkey = strsave(nexttok);
|
|
}
|
|
}
|
|
if (paramval != NULL) FREE(paramval);
|
|
if (paramkey != NULL) FREE(paramkey);
|
|
}
|
|
|
|
else if (match(nexttok, "module")) {
|
|
InitializeHashTable(&buses, OBJHASHSIZE);
|
|
SkipTokNoNewline(VLOG_DELIMITERS);
|
|
if (nexttok == NULL) {
|
|
Fprintf(stderr, "Badly formed \"module\" line\n");
|
|
goto skip_endmodule;
|
|
}
|
|
|
|
if (in_module == (char)1) {
|
|
Fprintf(stderr, "Missing \"endmodule\" statement on subcircuit.\n");
|
|
InputParseError(stderr);
|
|
}
|
|
in_module = (char)1;
|
|
cref = NULL;
|
|
|
|
/* Save pointer to current cell */
|
|
if (CurrentCell != NULL)
|
|
parent = CurrentCell->cell;
|
|
else
|
|
parent = NULL;
|
|
|
|
/* Check for existence of the cell. We may need to rename it. */
|
|
|
|
snprintf(model, MAX_STR_LEN-1, "%s", nexttok);
|
|
tp = LookupCellFile(nexttok, filenum);
|
|
tpsave = NULL;
|
|
hasports = (char)0;
|
|
|
|
/* Check for name conflict with duplicate cell names */
|
|
/* This may mean that the cell was used before it was */
|
|
/* defined, but CDL files sometimes just redefine the */
|
|
/* same cell over and over. So check if it's empty. */
|
|
|
|
if ((tp != NULL) && (tp->class != CLASS_MODULE)) {
|
|
int n;
|
|
char *ds;
|
|
|
|
// NOTE: Use this to ignore the new definition---should be
|
|
// an option to netgen.
|
|
/* goto skip_endmodule; */
|
|
|
|
ds = strrchr(model, '[');
|
|
if ((ds != NULL) && (*(ds + 1) == '['))
|
|
sscanf(ds + 2, "%d", &n);
|
|
else {
|
|
ds = model + strlen(model);
|
|
sprintf(ds, "[[0]]");
|
|
n = -1;
|
|
}
|
|
|
|
Printf("Duplicate cell %s in file\n", nexttok);
|
|
tp->flags |= CELL_DUPLICATE;
|
|
while (tp != NULL) {
|
|
n++;
|
|
/* Append "[[n]]" to the preexisting model name to force uniqueness */
|
|
sprintf(ds, "[[%d]]", n);
|
|
tp = LookupCellFile(model, filenum);
|
|
}
|
|
Printf("Renaming original cell to %s\n", model);
|
|
InstanceRename(nexttok, model, filenum);
|
|
CellRehash(nexttok, model, filenum);
|
|
CellDef(nexttok, filenum);
|
|
tp = LookupCellFile(nexttok, filenum);
|
|
}
|
|
else if (tp != NULL) { /* Cell exists, but as a placeholder */
|
|
tpsave = tp;
|
|
CellDef("_PLACEHOLDER_", filenum);
|
|
tp = LookupCellFile("_PLACEHOLDER_", filenum);
|
|
}
|
|
else if (tp == NULL) { /* Completely new cell, no name conflict */
|
|
CellDef(model, filenum);
|
|
tp = LookupCellFile(model, filenum);
|
|
}
|
|
|
|
inlined_decls = (char)0;
|
|
|
|
if (tp != NULL) {
|
|
struct bus wb, *nb;
|
|
|
|
tp->flags |= CELL_VERILOG;
|
|
PushStack(tp->name, CellStackPtr);
|
|
|
|
/* Need to support both types of I/O lists: Those */
|
|
/* that declare names only in the module list and */
|
|
/* follow with input/output and vector size */
|
|
/* declarations as individual statements in the module */
|
|
/* definition, and those which declare everything */
|
|
/* inside the pin list. */
|
|
|
|
SkipTokComments(VLOG_DELIMITERS);
|
|
|
|
// Check for parameters within #( ... )
|
|
|
|
if (match(nexttok, "#(")) {
|
|
SkipTokComments(VLOG_DELIMITERS);
|
|
in_param = (char)1;
|
|
}
|
|
else if (match(nexttok, "(")) {
|
|
SkipTokComments(VLOG_DELIMITERS);
|
|
}
|
|
|
|
wb.start = wb.end = -1;
|
|
while ((nexttok != NULL) && !match(nexttok, ";")) {
|
|
if (in_param) {
|
|
if (match(nexttok, ")")) {
|
|
in_param = (char)0;
|
|
SkipTokComments(VLOG_DELIMITERS);
|
|
if (!match(nexttok, "(")) {
|
|
Fprintf(stderr, "Badly formed module block parameter list.\n");
|
|
goto skip_endmodule;
|
|
}
|
|
}
|
|
else if (match(nexttok, "=")) {
|
|
|
|
// The parameter value is the next token.
|
|
SkipTokComments(VLOG_DELIMITERS); /* get the next token */
|
|
eqptr = nexttok;
|
|
|
|
// Try first as a double, otherwise it's a string
|
|
// Double value's slop defaults to 1%.
|
|
if (ConvertStringToFloat(eqptr, &dval) == 1)
|
|
PropertyDouble(tp->name, filenum, pkey, 0.01, dval);
|
|
else
|
|
PropertyString(tp->name, filenum, pkey, 0, eqptr);
|
|
}
|
|
else {
|
|
/* Assume this is a keyword and save it */
|
|
strcpy(pkey, nexttok);
|
|
}
|
|
}
|
|
else if (!match(nexttok, ",")) {
|
|
if (match(nexttok, ")")) break;
|
|
// Ignore input, output, and inout keywords, and handle buses.
|
|
|
|
if (inlined_decls == (char)0) {
|
|
if (match(nexttok, "input") || match(nexttok, "output") ||
|
|
match(nexttok, "inout"))
|
|
inlined_decls = (char)1;
|
|
}
|
|
else {
|
|
if (!match(nexttok, "input") && !match(nexttok, "output") &&
|
|
!match(nexttok, "inout") && !match(nexttok, "real") &&
|
|
!match(nexttok, "wire") && !match(nexttok, "logic") &&
|
|
!match(nexttok, "integer")) {
|
|
if (match(nexttok, "[")) {
|
|
if (GetBusTok(&wb) != 0) {
|
|
// Didn't parse as a bus, so wing it
|
|
wb.start = wb.end = -1;
|
|
CheckPort(cref, nexttok);
|
|
}
|
|
}
|
|
else {
|
|
if (wb.start != -1) {
|
|
if (wb.start > wb.end) {
|
|
for (i = wb.start; i >= wb.end; i--) {
|
|
sprintf(portname, "%s[%d]", nexttok, i);
|
|
CheckPort(cref, portname);
|
|
}
|
|
}
|
|
else {
|
|
for (i = wb.start; i <= wb.end; i++) {
|
|
sprintf(portname, "%s[%d]", nexttok, i);
|
|
CheckPort(cref, portname);
|
|
}
|
|
}
|
|
/* Also register this port as a bus */
|
|
nb = NewBus();
|
|
nb->start = wb.start;
|
|
nb->end = wb.end;
|
|
HashPtrInstall(nexttok, nb, &buses);
|
|
|
|
wb.start = wb.end = -1;
|
|
}
|
|
else {
|
|
CheckPort(cref, nexttok);
|
|
}
|
|
}
|
|
hasports = 1;
|
|
}
|
|
}
|
|
}
|
|
SkipTokComments(VLOG_DELIMITERS);
|
|
if (nexttok == NULL) break;
|
|
}
|
|
SetClass((blackbox) ? CLASS_MODULE : CLASS_SUBCKT);
|
|
|
|
if (inlined_decls == 1) {
|
|
if (hasports == 0)
|
|
// If the cell defines no ports, then create a proxy
|
|
Port((char *)NULL);
|
|
|
|
/* In the blackbox case, don't read the cell contents */
|
|
if (blackbox) goto skip_endmodule;
|
|
}
|
|
}
|
|
else {
|
|
|
|
skip_endmodule:
|
|
/* There was an error, so skip to the end of the */
|
|
/* subcircuit definition */
|
|
|
|
while (1) {
|
|
SkipNewLine(VLOG_DELIMITERS);
|
|
SkipTokComments(VLOG_DELIMITERS);
|
|
if (EndParseFile()) break;
|
|
if (match(nexttok, "endmodule")) {
|
|
in_module = 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (match(nexttok, "input") || match(nexttok, "output")
|
|
|| match(nexttok, "inout")) {
|
|
struct bus wb, *nb;
|
|
|
|
// Parsing of ports as statements not in the module pin list.
|
|
wb.start = wb.end = -1;
|
|
while (1) {
|
|
SkipTokComments(VLOG_DELIMITERS);
|
|
if (EndParseFile()) break;
|
|
|
|
if (match(nexttok, ";")) {
|
|
// End of statement
|
|
break;
|
|
}
|
|
else if (match(nexttok, "[")) {
|
|
if (GetBusTok(&wb) != 0) {
|
|
// Didn't parse as a bus, so wing it
|
|
wb.start = wb.end = -1;
|
|
CheckPort(cref, nexttok);
|
|
}
|
|
}
|
|
else if (!match(nexttok, ",")) {
|
|
if (wb.start != -1) {
|
|
if (wb.start > wb.end) {
|
|
for (i = wb.start; i >= wb.end; i--) {
|
|
sprintf(portname, "%s[%d]", nexttok, i);
|
|
CheckPort(cref, portname);
|
|
}
|
|
}
|
|
else {
|
|
for (i = wb.start; i <= wb.end; i++) {
|
|
sprintf(portname, "%s[%d]", nexttok, i);
|
|
CheckPort(cref, portname);
|
|
}
|
|
}
|
|
/* Also register this port as a bus */
|
|
nb = NewBus();
|
|
nb->start = wb.start;
|
|
nb->end = wb.end;
|
|
HashPtrInstall(nexttok, nb, &buses);
|
|
wb.start = wb.end = -1;
|
|
}
|
|
else {
|
|
CheckPort(cref, nexttok);
|
|
}
|
|
}
|
|
hasports = 1;
|
|
}
|
|
}
|
|
else if (match(nexttok, "endmodule")) {
|
|
|
|
CleanupModule();
|
|
EndCell();
|
|
|
|
if (in_module == (char)0) {
|
|
Fprintf(stderr, "\"endmodule\" occurred outside of a module!\n");
|
|
InputParseError(stderr);
|
|
}
|
|
in_module = (char)0;
|
|
cref = NULL;
|
|
|
|
if (*CellStackPtr) PopStack(CellStackPtr);
|
|
if (*CellStackPtr) ReopenCellDef((*CellStackPtr)->cellname, filenum);
|
|
SkipNewLine(VLOG_DELIMITERS);
|
|
|
|
if (tpsave != NULL) {
|
|
struct nlist *tpplace;
|
|
char *savename;
|
|
int lnum, pnum, ltest;
|
|
unsigned char valid;
|
|
struct objlist *lobj, *pobj;
|
|
|
|
/* Handle a placeholder from a verilog file that has been replaced
|
|
* by a netlist with pins in a different order. The pins need to
|
|
* be matched, corrected in the original cell and all instances,
|
|
* and the new cell deleted.
|
|
*/
|
|
|
|
Printf("Verilog placeholder module %s replaced by module definition\n",
|
|
tpsave->name);
|
|
tpplace = LookupCellFile("_PLACEHOLDER_", filenum);
|
|
|
|
/* If tpsave was generated from an instance in a SPICE netlist that
|
|
* did not have a black-box subcircuit definition, then the pins
|
|
* will all be labeled 1, 2, 3, etc. If so, then assume that the
|
|
* verilog pins are in order, and rename the placeholder pins.
|
|
* If the number of pins does not match, or if the pins are not
|
|
* labeled as ascending integers, then leave the cell alone.
|
|
* In either case, output a warning message.
|
|
*/
|
|
|
|
/* Get the number of ports in the placeholder */
|
|
pnum = 0;
|
|
for (pobj = tpplace->cell; pobj; pobj = pobj->next) {
|
|
if (pobj->type != PORT) break;
|
|
pnum++;
|
|
}
|
|
|
|
/* Get the number of ports in the saved cell and make */
|
|
/* sure that it equals the number of ports in the */
|
|
/* placeholder, and that all of the ports in the saved */
|
|
/* cell are integers in ascending order. */
|
|
|
|
valid = TRUE;
|
|
lnum = 0;
|
|
for (lobj = tpsave->cell; lobj; lobj = lobj->next) {
|
|
if (lobj->type != PORT) break;
|
|
lnum++;
|
|
if (sscanf(lobj->name, "%d", <est) != 1) break;
|
|
if (ltest != lnum) break;
|
|
}
|
|
if ((lobj != NULL) && (lobj->type == PORT))
|
|
valid = FALSE; /* Pins are not integers in ascending order */
|
|
if (pnum != lnum) valid = FALSE; /* Different number of pins */
|
|
|
|
if (valid == TRUE) {
|
|
Printf("Replacing pins of placeholder cell %s from cell definition.\n",
|
|
tpsave->name);
|
|
pobj = tpplace->cell;
|
|
for (lobj = tpsave->cell; lobj; lobj = lobj->next) {
|
|
if (lobj->type != PORT) break;
|
|
if (pobj == NULL) break; /* should not happen */
|
|
FREE(lobj->name);
|
|
lobj->name = (char *)MALLOC(strlen(pobj->name) + 1);
|
|
strcpy(lobj->name, pobj->name);
|
|
pobj = pobj->next;
|
|
}
|
|
}
|
|
else {
|
|
Printf("Placeholder pins of cell %s are not compatible and"
|
|
" will be left unchanged\n", tpsave->name);
|
|
}
|
|
|
|
/* MatchPins is part of netcmp and normally Circuit2 is the
|
|
* circuit being matched, so set Circuit2 to the original
|
|
* verilog black-box cell, and MatchPins() will force its
|
|
* pins to be rearranged to match the module definition just
|
|
* read.
|
|
*/
|
|
Circuit2 = tpsave;
|
|
MatchPins(tpplace, tpsave, 0);
|
|
savename = strsave(tpsave->name);
|
|
/* Now the original verilog black-box cell can be removed */
|
|
FreePorts(savename);
|
|
CellDelete(savename, filenum);
|
|
/* And _PLACEHOLDER_ is renamed to the original name of the cell. */
|
|
CellRehash("_PLACEHOLDER_", savename, filenum);
|
|
tpsave = NULL;
|
|
Circuit2 = NULL;
|
|
FREE(savename);
|
|
}
|
|
}
|
|
|
|
else if (match(nexttok, "`include")) {
|
|
char *iname, *iptr, *quotptr, *pathend, *userpath = NULL;
|
|
|
|
SkipTokNoNewline(VLOG_DELIMITERS);
|
|
if (nexttok == NULL) continue; /* Ignore if no filename */
|
|
|
|
// Any file included in another Verilog file needs to be
|
|
// interpreted relative to the path of the parent Verilog file,
|
|
// unless it's an absolute pathname.
|
|
|
|
pathend = strrchr(fname, '/');
|
|
iptr = nexttok;
|
|
while (*iptr == '\'' || *iptr == '\"') iptr++;
|
|
if ((pathend != NULL) && (*iptr != '/') && (*iptr != '~')) {
|
|
*pathend = '\0';
|
|
iname = (char *)MALLOC(strlen(fname) + strlen(iptr) + 2);
|
|
sprintf(iname, "%s/%s", fname, iptr);
|
|
*pathend = '/';
|
|
}
|
|
#ifndef IBMPC
|
|
else if ((*iptr == '~') && (*(iptr + 1) == '/')) {
|
|
/* For ~/<path>, substitute tilde from $HOME */
|
|
userpath = getenv("HOME");
|
|
iname = (char *)MALLOC(strlen(userpath) + strlen(iptr));
|
|
sprintf(iname, "%s%s", userpath, iptr + 1);
|
|
}
|
|
else if (*iptr == '~') {
|
|
/* For ~<user>/<path>, substitute tilde from getpwnam() */
|
|
struct passwd *passwd;
|
|
char *pathstart;
|
|
pathstart = strchr(iptr, '/');
|
|
if (pathstart) *pathstart = '\0';
|
|
passwd = getpwnam(iptr + 1);
|
|
if (passwd != NULL) {
|
|
userpath = passwd->pw_dir;
|
|
if (pathstart) {
|
|
*pathstart = '/';
|
|
iname = (char *)MALLOC(strlen(userpath) + strlen(pathstart) + 1);
|
|
sprintf(iname, "%s%s", userpath, pathstart);
|
|
}
|
|
else {
|
|
/* Almost certainly an error, but make the substitution anyway */
|
|
iname = STRDUP(userpath);
|
|
}
|
|
}
|
|
else {
|
|
/* Probably an error, but copy the filename verbatim */
|
|
iname = STRDUP(iptr);
|
|
}
|
|
}
|
|
#endif
|
|
else
|
|
iname = STRDUP(iptr);
|
|
|
|
// Eliminate any single or double quotes around the filename
|
|
iptr = iname;
|
|
quotptr = iptr;
|
|
while (*quotptr != '\'' && *quotptr != '\"' &&
|
|
*quotptr != '\0' && *quotptr != '\n') quotptr++;
|
|
if (*quotptr == '\'' || *quotptr == '\"') *quotptr = '\0';
|
|
|
|
IncludeVerilog(iptr, filenum, CellStackPtr, blackbox);
|
|
FREE(iname);
|
|
SkipNewLine(VLOG_DELIMITERS);
|
|
}
|
|
else if (match(nexttok, "`define")) {
|
|
struct property *kl = NULL;
|
|
|
|
// Pick up key-value pair and store in current cell
|
|
|
|
/* Parse for definitions used in expressions. Save */
|
|
/* definitions in the "verilogdefs" hash table. */
|
|
|
|
SkipTokNoNewline(VLOG_DELIMITERS);
|
|
if ((nexttok == NULL) || (nexttok[0] == '\0')) break;
|
|
|
|
kl = NewProperty();
|
|
kl->key = strsave(nexttok);
|
|
kl->idx = 0;
|
|
kl->merge = MERGE_NONE;
|
|
|
|
SkipTokNoNewline(VLOG_DELIMITERS);
|
|
if ((nexttok == NULL) || (nexttok[0] == '\0')) {
|
|
// Let "`define X" be equivalent to "`define X 1". Use integer value.
|
|
kl->type = PROP_INTEGER;
|
|
kl->pdefault.ival = 1;
|
|
kl->slop.ival = 0;
|
|
}
|
|
else if (ConvertStringToInteger(nexttok, &ival) == 1) {
|
|
/* Parameter parses as an integer */
|
|
kl->type = PROP_INTEGER;
|
|
kl->pdefault.ival = ival;
|
|
kl->slop.ival = 0; // Exact match default
|
|
}
|
|
else if (ConvertStringToFloat(nexttok, &dval) == 1) {
|
|
/* Parameter parses as a floating-point number */
|
|
kl->type = PROP_DOUBLE;
|
|
kl->pdefault.dval = dval;
|
|
kl->slop.dval = 0.01; // One percent default
|
|
}
|
|
else {
|
|
char *toks;
|
|
|
|
/* Treat the parameter as a string; BUT pull everything to */
|
|
/* EOL, not just the current token. */
|
|
toks = GetLineAtTok();
|
|
|
|
kl->type = PROP_STRING;
|
|
kl->pdefault.string = strsave(toks);
|
|
kl->slop.dval = 0.0;
|
|
|
|
SkipNewLine(VLOG_DELIMITERS);
|
|
}
|
|
if (kl) HashPtrInstall(kl->key, kl, &verilogdefs);
|
|
}
|
|
else if (match(nexttok, "`undef")) {
|
|
struct property *kl = NULL;
|
|
|
|
SkipTokNoNewline(VLOG_DELIMITERS);
|
|
if ((nexttok == NULL) || (nexttok[0] == '\0')) break;
|
|
|
|
kl = HashLookup(nexttok, &verilogdefs);
|
|
if (kl != NULL) {
|
|
HashDelete(nexttok, &verilogdefs);
|
|
if (kl->type == PROP_STRING)
|
|
if (kl->pdefault.string != NULL)
|
|
FREE(kl->pdefault.string);
|
|
FREE(kl->key);
|
|
}
|
|
/* Presumably it is not an error to undefine an undefined keyword */
|
|
}
|
|
else if (match(nexttok, "real") || match(nexttok, "integer")) {
|
|
Printf("Ignoring '%s' in module '%s'\n", nexttok, model);
|
|
/* Do not skip to end of module, as these can be in the middle of */
|
|
/* I/O assignments, which need to be parsed. */
|
|
while (!match(nexttok, ";")) SkipTok("X///**/X,;");
|
|
continue;
|
|
}
|
|
else if (match(nexttok, "end")) {
|
|
/* Handle a 'for' loop */
|
|
if (loop.loopvar != NULL) {
|
|
int loopval;
|
|
struct property *klr;
|
|
|
|
klr = (struct property *)HashLookup(loop.loopvar, &verilogparams);
|
|
loopval = klr->pdefault.ival;
|
|
|
|
if (loopval < loop.end) loopval++;
|
|
else if (loopval > loop.end) loopval--;
|
|
SeekFile(loop.filepos);
|
|
if (loopval == loop.end)
|
|
{
|
|
HashDelete(loop.loopvar, &verilogparams);
|
|
FREE(klr);
|
|
FREE(loop.loopvar);
|
|
loop.loopvar = NULL;
|
|
}
|
|
else
|
|
klr->pdefault.ival = loopval;
|
|
}
|
|
continue;
|
|
}
|
|
else if (match(nexttok, "begin") || match(nexttok, "generate")) {
|
|
/* 'generate' section or 'for' loop start is ignored */
|
|
continue;
|
|
}
|
|
else if (match(nexttok, "generate") || match(nexttok, "endgenerate")) {
|
|
/* 'generate' section is ignored */
|
|
continue;
|
|
}
|
|
else if (match(nexttok, "genvar")) {
|
|
while (!match(nexttok, ";")) SkipTok("X///**/X,;");
|
|
continue;
|
|
}
|
|
else if (match(nexttok, "for")) {
|
|
char limittype = '\0';
|
|
char *paramkey = NULL;
|
|
struct property *kl = NULL, *klr;
|
|
/* Parse out the loop variable and count. Set the loop
|
|
* variable as a parameter. Find the beginning of the
|
|
* loop block and record the file position so that the
|
|
* block can be re-parsed for each loop iteration.
|
|
*/
|
|
SkipTokNoNewline(VLOG_DELIMITERS);
|
|
if ((nexttok == NULL) || !match(nexttok, "(")) {
|
|
Printf("Badly formed 'for' loop.\n");
|
|
FREE(paramkey);
|
|
break;
|
|
}
|
|
SkipTokNoNewline(VLOG_DELIMITERS);
|
|
/* Next token must be the loop variable */
|
|
if (nexttok == NULL) break;
|
|
paramkey = strsave(nexttok);
|
|
SkipTokNoNewline(VLOG_DELIMITERS);
|
|
if (!match(nexttok, "=")) {
|
|
Printf("Badly formed 'for' loop.\n");
|
|
FREE(paramkey);
|
|
break;
|
|
}
|
|
SkipTokNoNewline(VLOG_INTEGER_DELIMITERS);
|
|
if (ParseIntegerExpression(nexttok, &ival) == 0) {
|
|
FREE(paramkey);
|
|
break;
|
|
}
|
|
|
|
kl = NewProperty();
|
|
kl->type = PROP_INTEGER;
|
|
kl->slop.ival = 0;
|
|
kl->pdefault.ival = ival;
|
|
kl->key = paramkey;
|
|
kl->idx = 0;
|
|
kl->merge = MERGE_NONE;
|
|
|
|
SkipTokNoNewline(VLOG_DELIMITERS);
|
|
if (!match(nexttok, ";")) {
|
|
Printf("Badly formed 'for' loop.\n");
|
|
FREE(paramkey);
|
|
FREE(kl);
|
|
break;
|
|
}
|
|
|
|
/* Assuming a standard 'for' loop here */
|
|
SkipTokNoNewline(VLOG_DELIMITERS);
|
|
if (!match(nexttok, paramkey)) {
|
|
Printf("Don't know how to parse this 'for' loop!\n");
|
|
FREE(paramkey);
|
|
FREE(kl);
|
|
break;
|
|
}
|
|
SkipTokNoNewline(VLOG_DELIMITERS);
|
|
if (strlen(nexttok) == 1) limittype = *nexttok;
|
|
SkipTokNoNewline(VLOG_INTEGER_DELIMITERS);
|
|
if (ParseIntegerExpression(nexttok, &ival) == 0) {
|
|
FREE(kl);
|
|
FREE(paramkey);
|
|
break;
|
|
}
|
|
|
|
/* Loops will stop after the last value, so if value is > or <,
|
|
* then adjust the end value accordingly.
|
|
*/
|
|
if (limittype == '<')
|
|
ival--;
|
|
else if (limittype == '>')
|
|
ival++;
|
|
|
|
SkipTokNoNewline(VLOG_DELIMITERS);
|
|
if (!match(nexttok, ";")) {
|
|
Printf("Badly formed 'for' loop.\n");
|
|
FREE(paramkey);
|
|
FREE(kl);
|
|
break;
|
|
}
|
|
|
|
/* Assume a standard 'for' loop and skip to the block begin */
|
|
/* To do: Parse out the loop increment value. */
|
|
while (1) {
|
|
SkipTokNoNewline(VLOG_DELIMITERS);
|
|
if (EndParseFile()) break;
|
|
else if (match(nexttok, "begin")) break;
|
|
}
|
|
if (EndParseFile()) {
|
|
Printf("Badly formed 'for' loop: No begin/end block.\n");
|
|
FREE(paramkey);
|
|
FREE(kl);
|
|
break;
|
|
}
|
|
|
|
/* Save the loop variable, ending value, and file position */
|
|
loop.loopvar = paramkey;
|
|
loop.start = kl->pdefault.ival;
|
|
loop.end = ival;
|
|
loop.filepos = TellFile();
|
|
|
|
/* 'for' loop has been completely parsed, so save the loop variable */
|
|
HashPtrInstall(paramkey, kl, &verilogparams);
|
|
}
|
|
else if (match(nexttok, "wire") || match(nexttok, "assign")) { /* wire = node */
|
|
struct bus wb, wb2, *nb;
|
|
char nodename[MAX_STR_LEN], noderoot[MAX_STR_LEN];
|
|
int is_wire = match(nexttok, "wire");
|
|
int j;
|
|
struct objlist *lhs, *rhs;
|
|
|
|
/* Get left-hand side expression. If this is a wire statement, */
|
|
/* then define the wire. If is_wire is false, then the wire */
|
|
/* should already be defined. */
|
|
|
|
if (is_wire) {
|
|
SkipTokNoNewline(VLOG_DELIMITERS);
|
|
if (match(nexttok, "real"))
|
|
SkipTokNoNewline(VLOG_DELIMITERS);
|
|
else if (match(nexttok, "logic"))
|
|
SkipTokNoNewline(VLOG_DELIMITERS);
|
|
|
|
if (GetBusTok(&wb) == 0) {
|
|
/* Handle bus notation */
|
|
SkipTokNoNewline(VLOG_DELIMITERS);
|
|
strcpy(noderoot, nexttok);
|
|
if (wb.start > wb.end) {
|
|
for (i = wb.end; i <= wb.start; i++) {
|
|
sprintf(nodename, "%s[%d]", nexttok, i);
|
|
if (LookupObject(nodename, CurrentCell) == NULL)
|
|
Node(nodename);
|
|
if (i == wb.start) lhs = LookupObject(nodename, CurrentCell);
|
|
}
|
|
}
|
|
else {
|
|
for (i = wb.start; i <= wb.end; i++) {
|
|
sprintf(nodename, "%s[%d]", nexttok, i);
|
|
if (LookupObject(nodename, CurrentCell) == NULL)
|
|
Node(nodename);
|
|
if (i == wb.start) lhs = LookupObject(nodename, CurrentCell);
|
|
}
|
|
}
|
|
nb = NewBus();
|
|
nb->start = wb.start;
|
|
nb->end = wb.end;
|
|
HashPtrInstall(nexttok, nb, &buses);
|
|
}
|
|
else {
|
|
if (LookupObject(nexttok, CurrentCell) == NULL) {
|
|
Node(nexttok);
|
|
lhs = LookupObject(nexttok, CurrentCell);
|
|
}
|
|
}
|
|
while (1) {
|
|
SkipTokNoNewline(VLOG_DELIMITERS);
|
|
if (match(nexttok, ",")) {
|
|
SkipTokComments(VLOG_DELIMITERS);
|
|
if (LookupObject(nexttok, CurrentCell) == NULL) {
|
|
Node(nexttok);
|
|
lhs = LookupObject(nexttok, CurrentCell);
|
|
}
|
|
}
|
|
else break;
|
|
}
|
|
}
|
|
else { /* "assign" */
|
|
SkipTokComments(VLOG_PIN_CHECK_DELIMITERS);
|
|
char *aptr = strvchr(nexttok, '[');
|
|
if (((aptr == NULL) && (GetBusTok(&wb) == 0)) ||
|
|
((aptr != NULL) && (GetBus(aptr, &wb) == 0))) {
|
|
if (aptr != NULL) {
|
|
*aptr = '\0';
|
|
/* Find object of first net in bus */
|
|
strcpy(noderoot, nexttok);
|
|
sprintf(nodename, "%s[%d]", nexttok, wb.start);
|
|
lhs = LookupObject(nodename, CurrentCell);
|
|
*aptr = '[';
|
|
}
|
|
else {
|
|
strcpy(noderoot, nexttok);
|
|
/* Set LHS to the start of the vector */
|
|
sprintf(nodename, "%s[%d]", nexttok, wb.start);
|
|
lhs = LookupObject(nodename, CurrentCell);
|
|
}
|
|
}
|
|
else {
|
|
lhs = LookupObject(nexttok, CurrentCell);
|
|
/* Handle the case in which an assignment is made
|
|
* without first declaring a wire for the signal,
|
|
* which is considered valid syntax (patch by
|
|
* Sylvain Munaut).
|
|
*/
|
|
if (lhs == NULL) {
|
|
Node(nexttok);
|
|
lhs = LookupObject(nexttok, CurrentCell);
|
|
}
|
|
strcpy(noderoot, nexttok);
|
|
}
|
|
SkipTokComments(VLOG_DELIMITERS);
|
|
if (lhs && ((!nexttok) || (!match(nexttok, "=")))) {
|
|
fprintf(stderr, "Empty assignment for net %s\n", lhs->name);
|
|
}
|
|
}
|
|
|
|
/* Check for assignment statement, and handle any allowed uses. */
|
|
/* Any uses other than those mentioned below will cause the entire */
|
|
/* module to be treated as a black box. */
|
|
|
|
// Allowed uses of "assign" for netlists:
|
|
// "assign a = b" joins two nets.
|
|
// "assign a = {b, c, ...}" creates a bus from components.
|
|
// "assign" using any boolean arithmetic is not structural verilog.
|
|
// "assign a = {x{b}}" creates a bus by repeating a component.
|
|
|
|
if (nexttok && match(nexttok, "=")) {
|
|
char assignname[MAX_STR_LEN], assignroot[MAX_STR_LEN];
|
|
int multiplier = 1;
|
|
|
|
i = wb.start;
|
|
while (1) {
|
|
SkipTokComments(VLOG_PIN_CHECK_DELIMITERS);
|
|
if (!nexttok) break;
|
|
|
|
if (match(nexttok, "{")) {
|
|
/* RHS is a bundle */
|
|
/* Make provisional multiplier active. NOTE: */
|
|
/* this is not going to properly handle complex */
|
|
/* multipliers like {2{a,b}}. Needs rework. */
|
|
if (multiplier < 0) multiplier = -multiplier;
|
|
continue;
|
|
}
|
|
else if (match(nexttok, "}")) {
|
|
/* End of bundle */
|
|
continue;
|
|
}
|
|
else if (match(nexttok, ",")) {
|
|
/* Additional signals in bundle */
|
|
continue;
|
|
}
|
|
else if (match(nexttok, ";")) {
|
|
/* End of assignment */
|
|
break;
|
|
}
|
|
else {
|
|
char *aptr = strvchr(nexttok, '[');
|
|
if (((aptr == NULL) && (GetBusTok(&wb2) == 0)) ||
|
|
((aptr != NULL) && (GetBus(aptr, &wb2) == 0))) {
|
|
j = wb2.start;
|
|
if (aptr != NULL) {
|
|
*aptr = '\0';
|
|
strcpy(assignroot, nexttok);
|
|
sprintf(assignname, "%s[%d]", nexttok, j);
|
|
rhs = LookupObject(assignname, CurrentCell);
|
|
*aptr = '[';
|
|
}
|
|
else
|
|
strcpy(assignroot, nexttok);
|
|
}
|
|
else {
|
|
j = -1;
|
|
rhs = LookupObject(nexttok, CurrentCell);
|
|
|
|
/* Check if rhs starts with a signal multiplier */
|
|
if (rhs == NULL) {
|
|
if (ConvertStringToInteger(nexttok, &multiplier) == 1) {
|
|
/* Set multiplier to a negative value to
|
|
* indicate that it is provisional, waiting on
|
|
* signal specification to follow.
|
|
*/
|
|
multiplier = -multiplier;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
if ((lhs == NULL) || (rhs == NULL)) {
|
|
if (rhs != NULL) {
|
|
Printf("Improper assignment; left-hand side cannot "
|
|
"be parsed.\n");
|
|
if (j != -1)
|
|
Printf("Right-hand side is \"%s\".\n", assignroot);
|
|
else
|
|
Printf("Right-hand side is \"%s\".\n", rhs->name);
|
|
Printf("Improper expression is \"%s\".\n", nexttok);
|
|
break;
|
|
}
|
|
if ((lhs != NULL) || (multiplier < 0)) {
|
|
Printf("Improper assignment; right-hand side cannot "
|
|
"be parsed.\n");
|
|
if (i != -1)
|
|
Printf("Left-hand side is \"%s\".\n", noderoot);
|
|
else
|
|
Printf("Left-hand side is \"%s\".\n", lhs->name);
|
|
Printf("Improper expression is \"%s\".\n", nexttok);
|
|
/* Not parsable, probably behavioral verilog? */
|
|
Printf("Module '%s' is not structural verilog, "
|
|
"making black-box.\n", model);
|
|
SetClass(CLASS_MODULE);
|
|
goto skip_endmodule;
|
|
}
|
|
}
|
|
while (1) {
|
|
/* Assign bits in turn from bundle in RHS to bits of LHS */
|
|
/* until bits in signal are exhausted or LHS is full. */
|
|
|
|
if (i != -1)
|
|
snprintf(nodename, MAX_STR_LEN, "%s[%d]", noderoot, i);
|
|
else if (lhs != NULL)
|
|
strncpy(nodename, lhs->name, MAX_STR_LEN - 1);
|
|
else {
|
|
Printf("Error: Improper node name \"%s\".\n", noderoot);
|
|
goto skip_endmodule;
|
|
}
|
|
if (j != -1)
|
|
snprintf(assignname, MAX_STR_LEN, "%s[%d]", assignroot, j);
|
|
else if (rhs != NULL)
|
|
strncpy(assignname, rhs->name, MAX_STR_LEN - 1);
|
|
else {
|
|
Printf("Error: Improper assignment name \"%s\".\n",
|
|
assignroot);
|
|
goto skip_endmodule;
|
|
}
|
|
|
|
join(nodename, assignname);
|
|
|
|
if (i == wb.end) break;
|
|
i += (wb.end > wb.start) ? 1 : -1;
|
|
|
|
if (j == wb2.end) {
|
|
if (multiplier <= 1)
|
|
break;
|
|
else
|
|
multiplier--;
|
|
}
|
|
else
|
|
j += (wb2.end > wb2.start) ? 1 : -1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
while (nexttok && !match(nexttok, ";"))
|
|
SkipTokComments(VLOG_DELIMITERS);
|
|
}
|
|
else if (match(nexttok, "endmodule")) {
|
|
// No action---new module is started with next 'module' statement,
|
|
// if any.
|
|
SkipNewLine(VLOG_DELIMITERS);
|
|
in_module = (char)0; /* Should have been done already */
|
|
}
|
|
else if (nexttok[0] == '`') {
|
|
// Ignore any other directive starting with a backtick (e.g., `timescale)
|
|
SkipNewLine(VLOG_DELIMITERS);
|
|
}
|
|
else if (match(nexttok, "reg") || match(nexttok, "always") ||
|
|
match(nexttok, "specify") || match(nexttok, "initial")) {
|
|
Printf("Behavioral keyword '%s' found in source.\n", nexttok);
|
|
Printf("Module '%s' is not structural verilog, making black-box.\n", model);
|
|
// To be done: Remove any contents (but may not be necessary)
|
|
// Recast as module
|
|
SetClass(CLASS_MODULE);
|
|
goto skip_endmodule;
|
|
}
|
|
else { /* module instances */
|
|
char instancename[MAX_STR_LEN], modulename[MAX_STR_LEN];
|
|
int itype, arraystart, arrayend, arraymax, arraymin;
|
|
char ignore;
|
|
|
|
instancename[MAX_STR_LEN-1] = '\0';
|
|
modulename[MAX_STR_LEN-1] = '\0';
|
|
|
|
struct portelement {
|
|
char *name; // Name of port in subcell
|
|
char *net; // Name of net connecting to port in the parent
|
|
int width; // Width of port, if port is a bus
|
|
char flags; // Used for marking if port was added into netlist
|
|
struct portelement *next;
|
|
};
|
|
|
|
struct portelement *head, *tail, *scan, *last, *scannext;
|
|
struct objlist *obptr;
|
|
|
|
strncpy(modulename, nexttok, MAX_STR_LEN-1);
|
|
|
|
/* If module name is a verilog primitive, then treat the module as a */
|
|
/* black box (this is not a complete list. Preferable to use hash */
|
|
/* function instead of lots of strcmp() calls). */
|
|
|
|
if (!strcmp(modulename, "buf") || !strcmp(modulename, "notif1") ||
|
|
!strcmp(modulename, "not") || !strcmp(modulename, "and") ||
|
|
!strcmp(modulename, "or") || !strcmp(modulename, "bufif0") ||
|
|
!strcmp(modulename, "bufif1") || !strcmp(modulename, "notif0")) {
|
|
|
|
Printf("Module contains verilog primitive '%s'.\n", nexttok);
|
|
Printf("Module '%s' is not structural verilog, making black-box.\n", model);
|
|
SetClass(CLASS_MODULE);
|
|
goto skip_endmodule;
|
|
}
|
|
|
|
if (!(*CellStackPtr)) {
|
|
CellDef(fname, filenum);
|
|
PushStack(fname, CellStackPtr);
|
|
}
|
|
|
|
SkipTokComments(VLOG_DELIMITERS);
|
|
|
|
nextinst:
|
|
ignore = FALSE;
|
|
head = NULL;
|
|
tail = NULL;
|
|
|
|
// Next token must be '#(' (parameters) or an instance name
|
|
|
|
if (match(nexttok, "#(")) {
|
|
|
|
// Read the parameter list
|
|
SkipTokComments(VLOG_DELIMITERS);
|
|
|
|
while (nexttok != NULL) {
|
|
char *paramname;
|
|
|
|
if (match(nexttok, ")")) {
|
|
SkipTokComments(VLOG_DELIMITERS);
|
|
break;
|
|
}
|
|
else if (match(nexttok, ",")) {
|
|
SkipTokComments(VLOG_DELIMITERS);
|
|
continue;
|
|
}
|
|
|
|
// We need to look for parameters of the type ".name(value)"
|
|
|
|
else if (nexttok[0] == '.') {
|
|
paramname = strsave(nexttok + 1);
|
|
SkipTokComments(VLOG_DELIMITERS);
|
|
if (!match(nexttok, "(")) {
|
|
Printf("Error: Expecting parameter value, got %s.\n", nexttok);
|
|
}
|
|
SkipTokComments(VLOG_DELIMITERS);
|
|
if (match(nexttok, ")")) {
|
|
Printf("Error: Parameter with no value found.\n");
|
|
}
|
|
else {
|
|
AddProperty(&kvlist, paramname, nexttok);
|
|
SkipTokComments(VLOG_DELIMITERS);
|
|
if (!match(nexttok, ")")) {
|
|
Printf("Error: Expecting end of parameter value, "
|
|
"got %s.\n", nexttok);
|
|
}
|
|
}
|
|
FREE(paramname);
|
|
}
|
|
SkipTokComments(VLOG_DELIMITERS);
|
|
}
|
|
if (!nexttok) {
|
|
Printf("Error: Still reading module, but got end-of-file.\n");
|
|
goto skip_endmodule;
|
|
}
|
|
}
|
|
|
|
strncpy(instancename, nexttok, MAX_STR_LEN-1);
|
|
/* Printf("Diagnostic: new instance is %s\n", instancename); */
|
|
SkipTokComments(VLOG_DELIMITERS);
|
|
|
|
arraystart = arrayend = -1;
|
|
if (match(nexttok, "[")) {
|
|
// Handle instance array notation.
|
|
struct bus wb;
|
|
if (GetBusTok(&wb) == 0) {
|
|
arraystart = wb.start;
|
|
arrayend = wb.end;
|
|
}
|
|
SkipTokComments(VLOG_DELIMITERS);
|
|
}
|
|
|
|
if (match(nexttok, "(")) {
|
|
char savetok = (char)0;
|
|
struct portelement *new_port;
|
|
|
|
// Read the pin list
|
|
while (nexttok != NULL) {
|
|
SkipTokComments(VLOG_PIN_CHECK_DELIMITERS);
|
|
if (match(nexttok, ")")) break;
|
|
else if (match(nexttok, ",")) continue;
|
|
|
|
// We need to look for pins of the type ".name(value)"
|
|
|
|
if (nexttok[0] != '.') {
|
|
Printf("Warning: Ignoring subcircuit with no pin names "
|
|
"at \"%s\"\n", nexttok);
|
|
InputParseError(stderr);
|
|
while (nexttok != NULL) {
|
|
SkipTokComments(VLOG_DELIMITERS);
|
|
if (match(nexttok, ";")) break;
|
|
}
|
|
ignore = TRUE;
|
|
break;
|
|
}
|
|
else {
|
|
new_port = (struct portelement *)CALLOC(1, sizeof(struct portelement));
|
|
new_port->name = strsave(nexttok + 1);
|
|
new_port->width = -1;
|
|
new_port->flags = PORT_NOT_FOUND;
|
|
SkipTokComments(VLOG_DELIMITERS);
|
|
if (!match(nexttok, "(")) {
|
|
Printf("Badly formed subcircuit pin line at \"%s\"\n", nexttok);
|
|
SkipNewLine(VLOG_DELIMITERS);
|
|
}
|
|
SkipTokComments(VLOG_PIN_CHECK_DELIMITERS);
|
|
if (match(nexttok, ")")) {
|
|
char localnet[MAX_STR_LEN];
|
|
// Empty parens, so create a new local node
|
|
savetok = (char)1;
|
|
if (arraystart != -1) {
|
|
/* No-connect on an instance array must also be an array */
|
|
sprintf(localnet, "_noconnect_%d_[%d:%d]", localcount++,
|
|
arraystart, arrayend);
|
|
}
|
|
else
|
|
sprintf(localnet, "_noconnect_%d_", localcount++);
|
|
new_port->net = strsave(localnet);
|
|
}
|
|
else {
|
|
if (!strcmp(nexttok, "{")) {
|
|
char *wire_bundle = (char *)MALLOC(1);
|
|
char *new_wire_bundle = NULL;
|
|
*wire_bundle = '\0';
|
|
/* Wire bundle---read to "}" */
|
|
while (nexttok) {
|
|
new_wire_bundle = (char *)MALLOC(strlen(wire_bundle) +
|
|
strlen(nexttok) + 1);
|
|
/* Roundabout way to do realloc() becase there is no REALLOC() */
|
|
strcpy(new_wire_bundle, wire_bundle);
|
|
strcat(new_wire_bundle, nexttok);
|
|
FREE(wire_bundle);
|
|
wire_bundle = new_wire_bundle;
|
|
if (!strcmp(nexttok, "}")) break;
|
|
SkipTokComments(VLOG_PIN_CHECK_DELIMITERS);
|
|
}
|
|
if (!nexttok) {
|
|
Printf("Unterminated net in pin %s\n", wire_bundle);
|
|
}
|
|
new_port->net = wire_bundle;
|
|
}
|
|
else if ((strchr(nexttok, '[') != NULL) &&
|
|
(strchr(nexttok, ']') == NULL)) {
|
|
/* If a bus expressions has whitespace, then treat it like
|
|
* a bundle, above, concatenating to the closing ']'.
|
|
*/
|
|
char *array_expr = (char *)MALLOC(1);
|
|
char *new_array_expr = NULL;
|
|
*array_expr = '\0';
|
|
/* Read to "]" */
|
|
while (nexttok) {
|
|
new_array_expr = (char *)MALLOC(strlen(array_expr) +
|
|
strlen(nexttok) + 1);
|
|
/* Roundabout way to do realloc() becase there is no REALLOC() */
|
|
strcpy(new_array_expr, array_expr);
|
|
strcat(new_array_expr, nexttok);
|
|
FREE(array_expr);
|
|
array_expr = new_array_expr;
|
|
if (strchr(nexttok, ']')) break;
|
|
SkipTokComments(VLOG_PIN_CHECK_DELIMITERS);
|
|
}
|
|
if (!nexttok) {
|
|
Printf("Unterminated net in pin %s\n", array_expr);
|
|
}
|
|
new_port->net = array_expr;
|
|
}
|
|
else if (nexttok[0] == '~' || nexttok[0] == '!' || nexttok[0] == '-') {
|
|
/* All of these imply that the signal is logically manipulated */
|
|
/* in turn implying behavioral code. */
|
|
Printf("Module '%s' is not structural verilog, "
|
|
"making black-box.\n", model);
|
|
SetClass(CLASS_MODULE);
|
|
goto skip_endmodule;
|
|
}
|
|
else
|
|
new_port->net = strsave(nexttok);
|
|
|
|
/* Read array information along with name; will be parsed later */
|
|
SkipTokComments(VLOG_PIN_CHECK_DELIMITERS);
|
|
if (match(nexttok, "[")) {
|
|
/* Check for space between name and array identifier */
|
|
SkipTokComments(VLOG_PIN_NAME_DELIMITERS);
|
|
if (!match(nexttok, ")")) {
|
|
char *expnet;
|
|
expnet = (char *)MALLOC(strlen(new_port->net)
|
|
+ strlen(nexttok) + 2);
|
|
sprintf(expnet, "%s[%s", new_port->net, nexttok);
|
|
FREE(new_port->net);
|
|
new_port->net = expnet;
|
|
}
|
|
SkipTokComments(VLOG_DELIMITERS);
|
|
}
|
|
|
|
if (!match(nexttok, ")")) {
|
|
Printf("Badly formed subcircuit pin line at \"%s\"\n", nexttok);
|
|
SkipNewLine(VLOG_DELIMITERS);
|
|
}
|
|
}
|
|
|
|
if (head == NULL) head = new_port;
|
|
else tail->next = new_port;
|
|
new_port->next = NULL;
|
|
tail = new_port;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
Printf("Expected to find instance pin block but got \"%s\"\n", nexttok);
|
|
}
|
|
if (ignore == TRUE) continue; /* moving along. . . */
|
|
|
|
/* Verilog allows multiple instances of a single cell type to be chained */
|
|
/* together with commas. */
|
|
|
|
SkipTokComments(VLOG_DELIMITERS);
|
|
if (match(nexttok, ",")) {
|
|
goto nextinst;
|
|
}
|
|
|
|
/* Otherwise, instance must end with a semicolon */
|
|
|
|
else if (!match(nexttok, ";")) {
|
|
Printf("Expected to find end of instance but got \"%s\"\n", nexttok);
|
|
InputParseError(stderr);
|
|
}
|
|
|
|
/* Check for ignored class */
|
|
|
|
if ((itype = IsIgnored(modulename, filenum)) == IGNORE_CLASS) {
|
|
Printf("Class '%s' instanced in input but is being ignored.\n", model);
|
|
return;
|
|
}
|
|
|
|
/* Check for shorted pins */
|
|
|
|
if ((itype == IGNORE_SHORTED) && (head != NULL)) {
|
|
unsigned char shorted = (unsigned char)1;
|
|
struct portelement *p;
|
|
for (p = head->next; p; p = p->next) {
|
|
if (strcasecmp(head->name, p->name))
|
|
shorted = (unsigned char)0;
|
|
break;
|
|
}
|
|
if (shorted == (unsigned char)1) {
|
|
Printf("Instance of '%s' is shorted, ignoring.\n", modulename);
|
|
while (head) {
|
|
p = head->next;
|
|
FREE(head);
|
|
head = p;
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (head == NULL) {
|
|
Fprintf(stderr, "Warning: Cell %s has no pins\n", modulename);
|
|
}
|
|
|
|
/* Check that the module exists. If not, generate an empty */
|
|
/* module entry matching the call. */
|
|
|
|
tp = LookupCellFile(modulename, filenum);
|
|
if (tp == NULL) {
|
|
struct bus wb, pb;
|
|
char defport[MAX_STR_LEN];
|
|
|
|
Fprintf(stdout, "Creating placeholder cell definition for "
|
|
"module %s.\n", modulename);
|
|
CellDef(modulename, filenum);
|
|
CurrentCell->flags |= CELL_PLACEHOLDER;
|
|
for (scan = head; scan != NULL; scan = scan->next) {
|
|
// Check if net name is a wire bus or portion of a bus
|
|
if (GetBus(scan->net, &wb) == 0) {
|
|
int range;
|
|
|
|
// This takes care of three situations:
|
|
// (1) The signal bus length matches the number of instances:
|
|
// apply one signal per instance.
|
|
// (2) The signal bus length is a multiple of the number of instances:
|
|
// apply a signal sub-bus to each instance.
|
|
// (3) The number of instances is a multiple of the signal bus length:
|
|
// apply the same signal to each instance.
|
|
|
|
if ((arrayend - arraystart) == (wb.end - wb.start)) {
|
|
// Net is a bus, but net is split over arrayed instances
|
|
Port(scan->name);
|
|
}
|
|
else if (wb.start > wb.end) {
|
|
if ((arraystart - arrayend) > (wb.start - wb.end))
|
|
range = (((arraystart - arrayend) + 1) /
|
|
((wb.start - wb.end) + 1)) - 1;
|
|
else
|
|
range = (((wb.start - wb.end) + 1) /
|
|
((arraystart - arrayend) + 1)) - 1;
|
|
|
|
for (i = range; i >= 0; i--) {
|
|
sprintf(defport, "%s[%d]", scan->name, i);
|
|
Port(defport);
|
|
}
|
|
}
|
|
else {
|
|
if ((arrayend - arraystart) > (wb.end - wb.start))
|
|
range = (((arrayend - arraystart) + 1) /
|
|
((wb.end - wb.start) + 1)) - 1;
|
|
else
|
|
range = (((wb.end - wb.start) + 1) /
|
|
((arrayend - arraystart) + 1)) - 1;
|
|
|
|
for (i = 0; i <= range; i++) {
|
|
sprintf(defport, "%s[%d]", scan->name, i);
|
|
Port(defport);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
Port(scan->name);
|
|
}
|
|
}
|
|
if (head == NULL) {
|
|
Port((char *)NULL); // Must have something for pin 1
|
|
}
|
|
SetClass(CLASS_MODULE);
|
|
tp = CurrentCell;
|
|
EndCell();
|
|
ReopenCellDef((*CellStackPtr)->cellname, filenum); /* Reopen */
|
|
}
|
|
|
|
/* Work through scan list and expand ports/nets that are arrays */
|
|
|
|
last = (struct portelement *)NULL;
|
|
scan = head;
|
|
while (scan != NULL) {
|
|
int portstart, portend, portnum;
|
|
|
|
scannext = scan->next;
|
|
portstart = -1;
|
|
|
|
for (obptr = tp->cell; obptr && obptr->type == PORT; obptr = obptr->next) {
|
|
char *delimiter;
|
|
if ((delimiter = strrchr(obptr->name, '[')) != NULL) {
|
|
*delimiter = '\0';
|
|
if ((*matchfunc)(obptr->name, scan->name)) {
|
|
if (sscanf(delimiter + 1, "%d", &portnum) == 1) {
|
|
if (portstart == -1)
|
|
portstart = portnum;
|
|
else
|
|
portend = portnum;
|
|
}
|
|
}
|
|
*delimiter = '[';
|
|
}
|
|
}
|
|
if (portstart != -1) {
|
|
struct bus wb;
|
|
struct portelement *new_port;
|
|
char vname[MAX_STR_LEN];
|
|
int j, result;
|
|
struct objlist *bobj;
|
|
char *bptr;
|
|
int minnet, maxnet, testidx, width;
|
|
|
|
width = portstart - portend;
|
|
if (width < 0) width = -width;
|
|
width++;
|
|
scan->width = width;
|
|
|
|
result = GetBus(scan->net, &wb);
|
|
if (result == 0) {
|
|
int match = 0;
|
|
int wblen, arraylen;
|
|
|
|
arraylen = arraystart - arrayend;
|
|
wblen = wb.start - wb.end;
|
|
|
|
if (arraylen < 0) arraylen = -arraylen;
|
|
if (wblen < 0) wblen = -wblen;
|
|
|
|
arraylen++;
|
|
wblen++;
|
|
|
|
if ((scan->width * arraylen) == wblen) match = 1;
|
|
else if (wblen == scan->width) match = 1;
|
|
else if (wblen == arraylen) match = 1;
|
|
else {
|
|
Fprintf(stderr, "Warning: Net %s bus width (%d) does not match "
|
|
"port %s bus width (%d) or array width (%d).\n",
|
|
scan->net, wblen, scan->name, scan->width, arraylen);
|
|
}
|
|
|
|
// Net is bit-sliced across array of instances.
|
|
|
|
if (wb.start > wb.end) {
|
|
char *bptr = NULL, *cptr = NULL, cchar, *netname;
|
|
unsigned char is_bundle = 0;
|
|
struct bus wbb;
|
|
|
|
i = wb.start;
|
|
j = portstart;
|
|
|
|
netname = scan->net;
|
|
if (*netname == '{') {
|
|
is_bundle = 1;
|
|
netname++;
|
|
cptr = strvchr(netname, ',');
|
|
if (cptr == NULL) cptr = strvchr(netname, '}');
|
|
if (cptr == NULL) cptr = netname + strlen(netname) - 1;
|
|
cchar = *cptr;
|
|
*cptr = '\0';
|
|
}
|
|
|
|
// Remove indexed part of scan->net
|
|
if (GetBus(netname, &wbb) == 0) {
|
|
i = wbb.start;
|
|
if ((bptr = strvchr(netname, '[')) != NULL)
|
|
*bptr = '\0';
|
|
}
|
|
else
|
|
i = -1;
|
|
|
|
while (1) {
|
|
new_port = (struct portelement *)CALLOC(1,
|
|
sizeof(struct portelement));
|
|
sprintf(vname, "%s[%d]", scan->name, j);
|
|
new_port->name = strsave(vname);
|
|
if (i == -1)
|
|
sprintf(vname, "%s", netname);
|
|
else
|
|
sprintf(vname, "%s[%d]", netname, i);
|
|
new_port->net = strsave(vname);
|
|
new_port->width = scan->width;
|
|
|
|
if (last == NULL)
|
|
head = new_port;
|
|
else
|
|
last->next = new_port;
|
|
|
|
new_port->next = scannext;
|
|
last = new_port;
|
|
|
|
if (j == portend) break;
|
|
|
|
if (portstart > portend) j--;
|
|
else j++;
|
|
if (i != -1) {
|
|
if (wbb.start > wbb.end) i--;
|
|
else i++;
|
|
}
|
|
|
|
if (is_bundle &&
|
|
((i == -1) ||
|
|
((wbb.start > wbb.end) && (i < wbb.end)) ||
|
|
((wbb.start <= wbb.end) && (i > wbb.end)))) {
|
|
if (bptr) *bptr = '[';
|
|
|
|
netname = cptr + 1;
|
|
if (cptr) *cptr = cchar; /* Restore previous bundle delimiter */
|
|
cptr = strvchr(netname, ',');
|
|
if (cptr == NULL) cptr = strvchr(netname, '}');
|
|
if (cptr == NULL) cptr = netname + strlen(netname) - 1;
|
|
cchar = *cptr;
|
|
*cptr = '\0';
|
|
|
|
if (GetBus(netname, &wbb) == 0) {
|
|
i = wbb.start;
|
|
if ((bptr = strvchr(netname, '[')) != NULL)
|
|
*bptr = '\0';
|
|
}
|
|
else i = -1;
|
|
}
|
|
}
|
|
FREE(scan);
|
|
scan = last;
|
|
if (cptr) *cptr = cchar; /* Restore bundle delimiter */
|
|
}
|
|
}
|
|
else if (portstart != portend) {
|
|
/* If the name starts with "_noconnect_", then it was an
|
|
* auto-generated name that incorrectly assumed a single-bit
|
|
* wide signal because it did not have a module prototype.
|
|
* Rewrite the noconnect name as an array. This just means
|
|
* that the verilog had an empty list "()" and the initial
|
|
* assumption was wrong. If not all instances agree on the
|
|
* size, then this will eventually generate an error.
|
|
*/
|
|
if (!strncmp(scan->net, "_noconnect_", 11) &&
|
|
(strchr(scan->net, '\[') == NULL) && (width > 1)) {
|
|
char *savenet = scan->net;
|
|
char *newnet = MALLOC(strlen(scan->net) + 12);
|
|
sprintf(newnet, "%s[%d:0]", scan->net, scan->width - 1);
|
|
FREE(savenet);
|
|
scan->net = newnet;
|
|
continue; /* Re-process this entry */
|
|
}
|
|
else
|
|
Fprintf(stderr, "Error: Single net %s is connected to bus port %s\n",
|
|
scan->net, scan->name);
|
|
}
|
|
}
|
|
last = scan;
|
|
scan = scannext;
|
|
}
|
|
|
|
arraymax = (arraystart > arrayend) ? arraystart : arrayend;
|
|
arraymin = (arraystart > arrayend) ? arrayend : arraystart;
|
|
|
|
for (i = arraymin; i <= arraymax; i++) {
|
|
char *brackptr;
|
|
int j;
|
|
char locinst[MAX_STR_LEN];
|
|
|
|
if (i != -1)
|
|
sprintf(locinst, "%s[%d]", instancename, i);
|
|
else
|
|
strcpy(locinst, instancename);
|
|
Instance(modulename, locinst);
|
|
LinkProperties(modulename, kvlist);
|
|
|
|
obptr = LookupInstance(locinst, CurrentCell);
|
|
if (obptr != NULL) {
|
|
do {
|
|
struct bus wb, wb2;
|
|
char *obpinname;
|
|
int obpinidx;
|
|
|
|
// NOTE: Verilog allows any order of pins, since both the
|
|
// instance and cell pin names are given. So for each pin
|
|
// in obptr (which defines the pin order) , we have to find
|
|
// the corresponding pin in the scan list.
|
|
|
|
obpinname = strrchr(obptr->name, '/');
|
|
if (!obpinname) break;
|
|
obpinname++;
|
|
|
|
scan = head;
|
|
obpinidx = -1;
|
|
while (scan != NULL) {
|
|
if (match(obpinname, scan->name)) {
|
|
scan->flags |= PORT_FOUND;
|
|
break;
|
|
}
|
|
scan = scan->next;
|
|
}
|
|
if (scan == NULL) {
|
|
char localnet[MAX_STR_LEN];
|
|
|
|
/* Assume an implicit unconnected pin */
|
|
sprintf(localnet, "_noconnect_%d_", localcount++);
|
|
Node(localnet);
|
|
join(localnet, obptr->name);
|
|
Fprintf(stdout,
|
|
"Note: Implicit pin %s in instance %s of %s in cell %s\n",
|
|
obpinname, locinst, modulename, CurrentCell->name);
|
|
}
|
|
else if (GetBus(scan->net, &wb) == 0) {
|
|
char *bptr2;
|
|
char *scanroot;
|
|
scanroot = strsave(scan->net);
|
|
brackptr = strvchr(scanroot, '[');
|
|
if (brackptr) *brackptr = '\0';
|
|
|
|
if (arraystart == -1) {
|
|
// Port may be an array
|
|
int range;
|
|
char pinname[MAX_STR_LEN];
|
|
|
|
// Check if port is an array
|
|
if (obpinidx == -1) {
|
|
if (wb.start != wb.end) {
|
|
Printf("Error: Bus connected to single port\n");
|
|
}
|
|
// Use only the first bit of the bus
|
|
sprintf(pinname, "%s[%d]", scanroot, wb.start);
|
|
if (LookupObject(pinname, CurrentCell) == NULL)
|
|
Node(pinname);
|
|
join(pinname, obptr->name);
|
|
if (brackptr) *brackptr = '[';
|
|
}
|
|
else {
|
|
// NOTE: Making unsupportable assumption that
|
|
// pin and port indexes match---need to fix this!
|
|
sprintf(pinname, "%s[%d]", scanroot, obpinidx);
|
|
if (LookupObject(pinname, CurrentCell) == NULL)
|
|
Node(pinname);
|
|
join(pinname, obptr->name);
|
|
}
|
|
}
|
|
else {
|
|
// Instance must be an array
|
|
char netname[MAX_STR_LEN];
|
|
int slice, portlen, siglen;
|
|
|
|
/* Get the array size of the port for bit slicing */
|
|
portlen = (scan->width < 0) ? 1 : scan->width;
|
|
|
|
/* Get the full array size of the connecting bus */
|
|
GetBus(scanroot, &wb2);
|
|
siglen = wb2.start - wb2.end;
|
|
if (siglen < 0) siglen = -siglen;
|
|
siglen++;
|
|
|
|
// If signal array is smaller than the portlength *
|
|
// length of instance array, then the signal wraps.
|
|
|
|
if (wb2.start >= wb2.end && arraystart >= arrayend) {
|
|
slice = wb.start - (arraystart - i) * portlen;
|
|
while (slice < wb2.end) slice += siglen;
|
|
}
|
|
else if (wb2.start < wb2.end && arraystart > arrayend) {
|
|
slice = wb.start + (arraystart - i) * portlen;
|
|
while (slice > wb2.end) slice -= siglen;
|
|
}
|
|
else if (wb2.start > wb2.end && arraystart < arrayend) {
|
|
slice = wb.start - (arraystart + i) * portlen;
|
|
while (slice < wb2.end) slice += siglen;
|
|
}
|
|
else { // (wb2.start < wb2.end && arraystart < arrayend)
|
|
slice = wb.start + (arraystart + i) * portlen;
|
|
while (slice > wb2.end) slice -= siglen;
|
|
}
|
|
|
|
sprintf(netname, "%s[%d]", scanroot, slice);
|
|
if (LookupObject(netname, CurrentCell) == NULL) Node(netname);
|
|
join(netname, obptr->name);
|
|
}
|
|
FREE(scanroot);
|
|
}
|
|
else {
|
|
if (LookupObject(scan->net, CurrentCell) == NULL) Node(scan->net);
|
|
join(scan->net, obptr->name);
|
|
}
|
|
|
|
/* Before exiting the loop, check if all ports in the */
|
|
/* scan list were handled. */
|
|
|
|
if ((obptr->next == NULL) || (obptr->next->type <= FIRSTPIN)) {
|
|
for (scan = head; scan; scan = scan->next) {
|
|
if (!(scan->flags & PORT_FOUND)) {
|
|
if (tp->flags & CELL_PLACEHOLDER) {
|
|
char tempname[MAX_STR_LEN];
|
|
int maxnode;
|
|
|
|
/* This pin was probably implicit in the first call */
|
|
/* and so it needs to be added to the definition. */
|
|
|
|
ReopenCellDef(modulename, filenum);
|
|
Port(scan->name);
|
|
ReopenCellDef((*CellStackPtr)->cellname, filenum);
|
|
|
|
/* obptr->next now gets the new port. Update the */
|
|
/* port number, and copy class and instance name. */
|
|
nobj = GetObject();
|
|
sprintf(tempname, "%s%s%s", obptr->instance.name,
|
|
SEPARATOR, scan->name);
|
|
nobj->name = strsave(tempname);
|
|
nobj->model.class = strsave(obptr->model.class);
|
|
nobj->instance.name = strsave(obptr->instance.name);
|
|
nobj->type = obptr->type + 1;
|
|
nobj->next = obptr->next;
|
|
nobj->node = -1;
|
|
obptr->next = nobj;
|
|
HashPtrInstall(nobj->name, nobj, &(CurrentCell->objdict));
|
|
|
|
/* Ensure that CurrentTail is correct */
|
|
if (obptr == CurrentTail) CurrentTail = nobj;
|
|
|
|
if (LookupObject(scan->net, CurrentCell) == NULL)
|
|
Node(scan->net);
|
|
join(scan->net, nobj->name);
|
|
scan->flags |= PORT_FOUND;
|
|
|
|
/* Now any previous instance of the same cell must */
|
|
/* insert the same additional pin as a no-connect. */
|
|
/* NOTE: This should be running a callback on all */
|
|
/* cells in the file, not just CurrentCell. */
|
|
|
|
for (sobj = CurrentCell->cell; sobj && (sobj != obptr);
|
|
sobj = sobj->next) {
|
|
if (sobj->type == FIRSTPIN) {
|
|
if (match(sobj->model.class, obptr->model.class)) {
|
|
while (sobj->next && (sobj->next->type > FIRSTPIN))
|
|
sobj = sobj->next;
|
|
/* Stop when reaching the current instance */
|
|
if (sobj->type == obptr->type + 1) break;
|
|
nobj = GetObject();
|
|
sprintf(tempname, "%s%s%s", sobj->instance.name,
|
|
SEPARATOR, scan->name);
|
|
nobj->name = strsave(tempname);
|
|
nobj->model.class = strsave(sobj->model.class);
|
|
nobj->instance.name = strsave(sobj->instance.name);
|
|
nobj->type = obptr->type + 1;
|
|
nobj->node = -1;
|
|
nobj->next = sobj->next;
|
|
sobj->next = nobj;
|
|
HashPtrInstall(nobj->name, nobj,
|
|
&(CurrentCell->objdict));
|
|
|
|
sprintf(tempname, "_noconnect_%d_", localcount++);
|
|
Node(tempname);
|
|
join(tempname, nobj->name);
|
|
Fprintf(stdout, "Note: Implicit pin %s in instance "
|
|
"%s of %s in cell %s\n",
|
|
scan->name, sobj->instance.name,
|
|
modulename, CurrentCell->name);
|
|
}
|
|
}
|
|
}
|
|
obptr = obptr->next;
|
|
}
|
|
else {
|
|
Fprintf(stderr, "Error: Instance %s has pin %s which is "
|
|
"not in the %s cell definition.\n",
|
|
locinst, scan->name, modulename);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
obptr = obptr->next;
|
|
} while (obptr != NULL && obptr->type > FIRSTPIN);
|
|
}
|
|
if (i == -1) break; /* No array */
|
|
}
|
|
DeleteProperties(&kvlist);
|
|
|
|
/* free up the allocated list */
|
|
scan = head;
|
|
while (scan != NULL) {
|
|
scannext = scan->next;
|
|
FREE(scan->name);
|
|
FREE(scan->net);
|
|
FREE(scan);
|
|
scan = scannext;
|
|
}
|
|
}
|
|
continue;
|
|
|
|
baddevice:
|
|
Fprintf(stderr, "Badly formed line in input.\n");
|
|
}
|
|
|
|
/* Watch for bad ending syntax */
|
|
|
|
if (in_module == (char)1) {
|
|
Fprintf(stderr, "Missing \"endmodule\" statement in module.\n");
|
|
InputParseError(stderr);
|
|
}
|
|
|
|
if (*(CellStackPtr)) {
|
|
CleanupModule();
|
|
EndCell();
|
|
if (*CellStackPtr) PopStack(CellStackPtr);
|
|
if (*CellStackPtr) ReopenCellDef((*CellStackPtr)->cellname, filenum);
|
|
}
|
|
|
|
if (warnings)
|
|
Fprintf(stderr, "File %s read with %d warning%s.\n", fname,
|
|
warnings, (warnings == 1) ? "" : "s");
|
|
}
|
|
|
|
/*----------------------------------------------*/
|
|
/* Top-level verilog module file read routine */
|
|
/*----------------------------------------------*/
|
|
|
|
char *ReadVerilogTop(char *fname, int *fnum, int blackbox)
|
|
{
|
|
struct property *kl = NULL;
|
|
struct cellstack *CellStack = NULL;
|
|
struct nlist *tp;
|
|
int filenum;
|
|
|
|
// Make sure CurrentCell is clear
|
|
CurrentCell = NULL;
|
|
|
|
if ((filenum = OpenParseFile(fname, *fnum)) < 0) {
|
|
|
|
if (strchr(fname, '.') == NULL) {
|
|
char name[1024];
|
|
SetExtension(name, fname, VERILOG_EXTENSION);
|
|
if ((filenum = OpenParseFile(name, *fnum)) < 0) {
|
|
Fprintf(stderr, "Error in Verilog file read: No file %s\n", name);
|
|
*fnum = filenum;
|
|
return NULL;
|
|
}
|
|
}
|
|
else {
|
|
Fprintf(stderr, "Error in Verilog file read: No file %s\n", fname);
|
|
*fnum = filenum;
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/* All Verilog file reading is case sensitive. However: if */
|
|
/* a SPICE file was read before it, then it will be forced to */
|
|
/* be case insensitive, with a stern warning. */
|
|
|
|
if (matchfunc == matchnocase) {
|
|
Printf("Warning: A case-insensitive file has been read and so the "
|
|
"verilog file must be treated case-insensitive to match.\n");
|
|
}
|
|
else {
|
|
matchfunc = match;
|
|
matchintfunc = matchfile;
|
|
hashfunc = hashcase;
|
|
}
|
|
|
|
if ((hashfile != -1) && (hashfile != *fnum)) {
|
|
/* Started a new file, so remove all the parameters and definitions */
|
|
RecurseHashTable(&verilogparams, freeprop);
|
|
HashKill(&verilogparams);
|
|
RecurseHashTable(&verilogdefs, freeprop);
|
|
HashKill(&verilogdefs);
|
|
hashfile = -1;
|
|
}
|
|
if (hashfile == -1) {
|
|
InitializeHashTable(&verilogparams, OBJHASHSIZE);
|
|
InitializeHashTable(&verilogdefs, OBJHASHSIZE);
|
|
hashfile = *fnum;
|
|
}
|
|
definitions = &verilogdefs;
|
|
|
|
/* Add the pre-defined key "LVS" to verilogdefs */
|
|
|
|
kl = NewProperty();
|
|
kl->merge = MERGE_NONE;
|
|
kl->key = strsave("LVS");
|
|
kl->idx = 0;
|
|
kl->type = PROP_INTEGER;
|
|
kl->slop.ival = 0;
|
|
kl->pdefault.ival = 1;
|
|
HashPtrInstall(kl->key, kl, &verilogdefs);
|
|
|
|
ReadVerilogFile(fname, filenum, &CellStack, blackbox);
|
|
CloseParseFile();
|
|
|
|
// Cleanup
|
|
while (CellStack != NULL) PopStack(&CellStack);
|
|
|
|
definitions = (struct hashdict *)NULL;
|
|
|
|
// Record the top level file.
|
|
if (LookupCellFile(fname, filenum) == NULL) CellDef(fname, filenum);
|
|
|
|
tp = LookupCellFile(fname, filenum);
|
|
if (tp) tp->flags |= CELL_TOP;
|
|
|
|
*fnum = filenum;
|
|
return fname;
|
|
}
|
|
|
|
/*--------------------------------------*/
|
|
/* Wrappers for ReadVerilogTop() */
|
|
/*--------------------------------------*/
|
|
|
|
char *ReadVerilog(char *fname, int *fnum)
|
|
{
|
|
return ReadVerilogTop(fname, fnum, 0);
|
|
}
|
|
|
|
/*--------------------------------------*/
|
|
/* Verilog file include routine */
|
|
/*--------------------------------------*/
|
|
|
|
void IncludeVerilog(char *fname, int parent, struct cellstack **CellStackPtr,
|
|
int blackbox)
|
|
{
|
|
int filenum = -1;
|
|
char name[MAX_STR_LEN];
|
|
|
|
/* If fname does not begin with "/", then assume that it is */
|
|
/* in the same relative path as its parent. */
|
|
|
|
if (fname[0] != '/') {
|
|
char *ppath;
|
|
if (*CellStackPtr && ((*CellStackPtr)->cellname != NULL)) {
|
|
strcpy(name, (*CellStackPtr)->cellname);
|
|
ppath = strrchr(name, '/');
|
|
if (ppath != NULL)
|
|
strcpy(ppath + 1, fname);
|
|
else
|
|
strcpy(name, fname);
|
|
filenum = OpenParseFile(name, parent);
|
|
}
|
|
}
|
|
|
|
/* If we failed the path relative to the parent, then try the */
|
|
/* filename alone (relative to the path where netgen was */
|
|
/* executed). */
|
|
|
|
if (filenum < 0) {
|
|
if ((filenum = OpenParseFile(fname, parent)) < 0) {
|
|
|
|
/* If that fails, see if a standard Verilog extension */
|
|
/* helps, if the file didn't have an extension. But */
|
|
/* really, we're getting desperate at this point. */
|
|
|
|
if (strchr(fname, '.') == NULL) {
|
|
SetExtension(name, fname, VERILOG_EXTENSION);
|
|
filenum = OpenParseFile(name, parent);
|
|
if (filenum < 0) {
|
|
fprintf(stderr,"Error in Verilog file include: No file %s\n", name);
|
|
return;
|
|
}
|
|
}
|
|
else {
|
|
fprintf(stderr,"Error in Verilog file include: No file %s\n", fname);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
ReadVerilogFile(fname, parent, CellStackPtr, blackbox);
|
|
CloseParseFile();
|
|
}
|
|
|