Added support for simple forms of for() loops in generate blocks.

This is done by treating the loop variable as a temporary parameter
that is valid only inside the loop, and changing the parameter
value on each loop iteration.  The file stream position is used
to iterate the loop with calls to fseek() and ftell(), so that the
input tokenizer continues to work within loops.
This commit is contained in:
Tim Edwards 2023-03-29 16:17:37 -04:00
parent 490f9f7dbc
commit eeb3c0e5c6
3 changed files with 330 additions and 15 deletions

View File

@ -183,7 +183,6 @@ void CloseFile(char *filename)
fclose(outfile);
}
/* STUFF TO READ INPUT FILES */
static char *line = NULL; /* actual line read in */
@ -207,6 +206,21 @@ struct hashdict *definitions = (struct hashdict *)NULL;
#define WHITESPACE_DELIMITER " \t\n\r"
/*----------------------------------------------------------------------*/
/* Seek and Tell on infile stream, for use with handling generate */
/* loops in verilog. */
/*----------------------------------------------------------------------*/
void SeekFile(long offset)
{
fseek(infile, offset, SEEK_SET);
}
long TellFile()
{
return ftell(infile);
}
/*----------------------------------------------------------------------*/
/* TrimQuoted() --- */
/* Remove spaces from inside single- or double-quoted strings. */

View File

@ -47,5 +47,7 @@ extern void InputParseError(FILE *f);
extern int OpenParseFile(char *name, int fnum);
extern int EndParseFile(void);
extern int CloseParseFile(void);
extern void SeekFile(long offset); /* handles verilog 'for' loops */
extern long TellFile(); /* handles verilog 'for' loops */
#endif /* _NETFILE_H */

View File

@ -42,6 +42,7 @@ the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
#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>
@ -63,6 +64,7 @@ the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
#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
@ -83,6 +85,16 @@ struct bus {
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)
@ -311,6 +323,133 @@ int EvalExpr(struct expr_stack **stackptr, int *valptr)
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;
if (*cptr == '`') cptr++;
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.
@ -323,7 +462,6 @@ int EvalExpr(struct expr_stack **stackptr, int *valptr)
int GetBusTok(struct bus *wb)
{
int result, start, end, value;
char oper;
struct property *kl = NULL;
struct expr_stack *stack, *newexp;
@ -338,7 +476,6 @@ int GetBusTok(struct bus *wb)
if (match(nexttok, "[")) {
start = end = -1;
oper = '\0';
while (nexttok) {
SkipTokComments(VLOG_EQUATION_DELIMITERS);
if (match(nexttok, "]")) {
@ -381,12 +518,6 @@ int GetBusTok(struct bus *wb)
// Is name in the parameter list?
kl = (struct property *)HashLookup(nexttok, &verilogparams);
if (kl == NULL) {
// Is name in the definitions list?
kl = (struct property *)HashLookup(nexttok, &verilogdefs);
}
if (kl == NULL) {
Printf("Array value %s is not a number or a parameter.\n",
nexttok);
@ -473,13 +604,16 @@ int GetBusTok(struct bus *wb)
//--------------------------------------------------------------------
// GetBus() is similar to GetBusTok() (see above), but it parses from
// a string instead of the input tokenizer.
// 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 {
@ -882,6 +1016,8 @@ void ReadVerilogFile(char *fname, int filenum, struct cellstack **CellStackPtr,
in_module = (char)0;
in_param = (char)0;
loop.loopvar = NULL;
while (!EndParseFile()) {
SkipTokComments(VLOG_DELIMITERS); /* get the next token */
@ -1422,6 +1558,145 @@ skip_endmodule:
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];
@ -1485,7 +1760,7 @@ skip_endmodule:
}
else { /* "assign" */
SkipTokComments(VLOG_PIN_CHECK_DELIMITERS);
if (GetBus(nexttok, &wb) == 0) {
if (GetBusTok(&wb) == 0) {
char *aptr = strvchr(nexttok, '[');
if (aptr != NULL) {
*aptr = '\0';
@ -1520,9 +1795,11 @@ skip_endmodule:
// "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) {
@ -1531,6 +1808,10 @@ skip_endmodule:
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, "}")) {
@ -1546,7 +1827,7 @@ skip_endmodule:
break;
}
else {
if (GetBus(nexttok, &wb2) == 0) {
if (GetBusTok(&wb2) == 0) {
char *aptr = strvchr(nexttok, '[');
j = wb2.start;
if (aptr != NULL) {
@ -1562,6 +1843,18 @@ skip_endmodule:
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) {
@ -1574,7 +1867,7 @@ skip_endmodule:
Printf("Improper expression is \"%s\".\n", nexttok);
break;
}
if (lhs != NULL) {
if ((lhs != NULL) || (multiplier < 0)) {
Printf("Improper assignment; right-hand side cannot "
"be parsed.\n");
if (i != -1)
@ -1607,8 +1900,14 @@ skip_endmodule:
if (i == wb.end) break;
i += (wb.end > wb.start) ? 1 : -1;
if (j == wb2.end) break;
j += (wb2.end > wb2.start) ? 1 : -1;
if (j == wb2.end) {
if (multiplier <= 1)
break;
else
multiplier--;
}
else
j += (wb2.end > wb2.start) ? 1 : -1;
}
}
}