704 lines
15 KiB
C
704 lines
15 KiB
C
/* Functions to generate C or Verilog code from HCL */
|
|
/* This file maintains a parse tree representation of expressions */
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <ctype.h>
|
|
|
|
#include "node.h"
|
|
#include "outgen.h"
|
|
|
|
#define MAXBUF 1024
|
|
|
|
void yyerror(const char *str);
|
|
void yyserror(const char *str, char *other);
|
|
|
|
/* For error reporting */
|
|
static char* show_expr(node_ptr expr);
|
|
|
|
/* The symbol table */
|
|
#define SYM_LIM 100
|
|
static node_ptr sym_tab[2][SYM_LIM];
|
|
static int sym_count = 0;
|
|
|
|
/* Optional simulator name */
|
|
char simname[MAXBUF] = "";
|
|
|
|
#ifdef UCLID
|
|
int annotate = 0;
|
|
/* Keep list of argument names encountered in node definition */
|
|
char *arg_names[SYM_LIM];
|
|
int arg_cnt = 0;
|
|
#endif
|
|
|
|
|
|
extern FILE *outfile;
|
|
|
|
/*
|
|
* usage - print helpful diagnostic information
|
|
*/
|
|
static void usage(char *name)
|
|
{
|
|
#ifdef VLOG
|
|
fprintf(stderr, "Usage: %s [-h] < HCL_file > verilog_file\n", name);
|
|
#else
|
|
#ifdef UCLID
|
|
fprintf(stderr, "Usage: %s [-ah] < HCL_file > uclid_file\n", name);
|
|
fprintf(stderr, " -a Add define/use annotations\n");
|
|
#else /* !UCLID */
|
|
fprintf(stderr, "Usage: %s [-h][-n NAM] < HCL_file > C_file\n", name);
|
|
#endif /* UCLID */
|
|
#endif /* VLOG */
|
|
fprintf(stderr, " -h Print this message\n");
|
|
fprintf(stderr, " -n NAM Specify processor name\n");
|
|
exit(0);
|
|
}
|
|
|
|
|
|
/* Initialization */
|
|
void init_node(int argc, char **argv)
|
|
{
|
|
int c;
|
|
int max_column = 75;
|
|
int first_indent = 4;
|
|
int other_indents = 2;
|
|
|
|
/* Parse the command line arguments */
|
|
while ((c = getopt(argc, argv, "hna")) != -1) {
|
|
switch(c) {
|
|
case 'h':
|
|
usage(argv[0]);
|
|
break;
|
|
case 'n': /* Optional simulator name */
|
|
strcpy(simname, argv[optind]);
|
|
break;
|
|
#ifdef UCLID
|
|
case 'a':
|
|
annotate = 1;
|
|
break;
|
|
#endif
|
|
default:
|
|
printf("Invalid option '%c'\n", c);
|
|
usage(argv[0]);
|
|
break;
|
|
}
|
|
}
|
|
|
|
#if !defined(VLOG) && !defined(UCLID)
|
|
/* Define and initialize the simulator name */
|
|
if (!strcmp(simname, ""))
|
|
printf("char simname[] = \"Y86-64 Processor\";\n");
|
|
else
|
|
printf("char simname[] = \"Y86-64 Processor: %s\";\n", simname);
|
|
#endif
|
|
outgen_init(outfile, max_column, first_indent, other_indents);
|
|
}
|
|
|
|
static void add_symbol(node_ptr name, node_ptr val)
|
|
{
|
|
if (sym_count >= SYM_LIM) {
|
|
yyerror("Symbol table limit exceeded");
|
|
return;
|
|
}
|
|
sym_tab[0][sym_count] = name;
|
|
sym_tab[1][sym_count] = val;
|
|
sym_count++;
|
|
}
|
|
|
|
|
|
static char *node_names[] =
|
|
{"quote", "var", "num", "and", "or", "not", "comp", "ele", "case"};
|
|
|
|
static void show_node(node_ptr node)
|
|
{
|
|
printf("Node type: %s, Boolean ? %c, String value: %s\n",
|
|
node_names[node->type], node->isbool ? 'Y':'N', node->sval);
|
|
}
|
|
|
|
|
|
void finish_node(int check_ref)
|
|
{
|
|
if (check_ref) {
|
|
int i;
|
|
for (i = 0; i < sym_count; i++)
|
|
if (!sym_tab[0][i]->ref) {
|
|
fprintf(stderr, "Warning, argument '%s' not referenced\n",
|
|
sym_tab[0][i]->sval);
|
|
}
|
|
}
|
|
}
|
|
|
|
static node_ptr find_symbol(char *name)
|
|
{
|
|
int i;
|
|
for (i = 0; i < sym_count; i++) {
|
|
if (strcmp(name, sym_tab[0][i]->sval) == 0) {
|
|
node_ptr result = sym_tab[1][i];
|
|
sym_tab[0][i]->ref++;
|
|
return result;
|
|
}
|
|
}
|
|
yyserror("Symbol %s not found", name);
|
|
return NULL;
|
|
}
|
|
|
|
#ifdef UCLID
|
|
/* See if string should be considered argument.
|
|
Currently, omit strings that are all upper case */
|
|
static int is_arg(char *name)
|
|
{
|
|
int upper = 1;
|
|
int c;
|
|
while ((c=*name++) != '\0')
|
|
upper = upper && isupper(c);
|
|
return !upper;
|
|
}
|
|
|
|
/* See if string is part of current argument list */
|
|
static void check_for_arg(char *name)
|
|
{
|
|
int i;
|
|
if (!is_arg(name))
|
|
return;
|
|
for (i = 0; i < arg_cnt; i++)
|
|
if (strcmp(arg_names[i], name) == 0)
|
|
return;
|
|
arg_names[arg_cnt++] = name;
|
|
}
|
|
#endif
|
|
|
|
static node_ptr new_node(node_type_t t, int isbool,
|
|
char *s, node_ptr a1, node_ptr a2)
|
|
{
|
|
node_ptr result = malloc(sizeof(node_rec));
|
|
result->type = t;
|
|
result->isbool = isbool;
|
|
result->sval = s;
|
|
result->arg1 = a1;
|
|
result->arg2 = a2;
|
|
result->ref = 0;
|
|
result->next = NULL;
|
|
return result;
|
|
}
|
|
|
|
/* Concatenate two lists */
|
|
node_ptr concat(node_ptr n1, node_ptr n2)
|
|
{
|
|
node_ptr tail = n1;
|
|
if (!n1)
|
|
return n2;
|
|
while (tail->next)
|
|
tail = tail->next;
|
|
tail->next = n2;
|
|
return n1;
|
|
}
|
|
|
|
static void free_node(node_ptr n)
|
|
{
|
|
free(n->sval);
|
|
free(n);
|
|
}
|
|
|
|
node_ptr make_quote(char *qstring)
|
|
{
|
|
|
|
/* Quoted string still has quotes around it */
|
|
int len = strlen(qstring)-2;
|
|
char *sname = malloc(len+1);
|
|
strncpy(sname, qstring+1, len);
|
|
sname[len] = '\0';
|
|
return new_node(N_QUOTE, 0, sname, NULL, NULL);
|
|
}
|
|
|
|
node_ptr make_var(char *name)
|
|
{
|
|
char *sname = malloc(strlen(name)+1);
|
|
strcpy(sname, name);
|
|
/* Initially assume var is not Boolean */
|
|
return new_node(N_VAR, 0, sname, NULL, NULL);
|
|
}
|
|
|
|
node_ptr make_num(char *name)
|
|
{
|
|
char *sname = malloc(strlen(name)+1);
|
|
strcpy(sname, name);
|
|
return new_node(N_NUM, 0, sname, NULL, NULL);
|
|
}
|
|
|
|
void set_bool(node_ptr varnode)
|
|
{
|
|
if (!varnode)
|
|
yyerror("Null node encountered");
|
|
varnode->isbool = 1;
|
|
}
|
|
|
|
/* Make sure argument is OK */
|
|
static int check_arg(node_ptr arg, int wantbool)
|
|
{
|
|
if (!arg) {
|
|
yyerror("Null node encountered");
|
|
return 0;
|
|
}
|
|
if (arg->type == N_VAR) {
|
|
node_ptr qval = find_symbol(arg->sval);
|
|
if (!qval) {
|
|
yyserror("Variable '%s' not found", arg->sval);
|
|
return 0;
|
|
}
|
|
if (wantbool != qval->isbool) {
|
|
if (wantbool)
|
|
yyserror("Variable '%s' not Boolean", arg->sval);
|
|
else
|
|
yyserror("Variable '%s' not integer", arg->sval);
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
if (arg->type == N_NUM) {
|
|
if (wantbool && strcmp(arg->sval,"0") != 0 &&
|
|
strcmp(arg->sval,"1") != 0) {
|
|
yyserror("Value '%s' not Boolean", arg->sval);
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
if (wantbool && !arg->isbool)
|
|
yyserror("Non Boolean argument '%s'", show_expr(arg));
|
|
if (!wantbool && arg->isbool)
|
|
yyserror("Non integer argument '%s'", show_expr(arg));
|
|
return (wantbool == arg->isbool);
|
|
}
|
|
|
|
node_ptr make_not(node_ptr arg)
|
|
{
|
|
check_arg(arg, 1);
|
|
return new_node(N_NOT, 1, "!", arg, NULL);
|
|
}
|
|
|
|
node_ptr make_and(node_ptr arg1, node_ptr arg2)
|
|
{
|
|
check_arg(arg1, 1);
|
|
check_arg(arg2, 1);
|
|
return new_node(N_AND, 1, "&", arg1, arg2);
|
|
}
|
|
|
|
node_ptr make_or(node_ptr arg1, node_ptr arg2)
|
|
{
|
|
check_arg(arg1, 1);
|
|
check_arg(arg2, 1);
|
|
return new_node(N_OR, 1, "|", arg1, arg2);
|
|
}
|
|
|
|
node_ptr make_comp(node_ptr op, node_ptr arg1, node_ptr arg2)
|
|
{
|
|
check_arg(arg1, 0);
|
|
check_arg(arg2, 0);
|
|
return new_node(N_COMP, 1, op->sval, arg1, arg2);
|
|
}
|
|
|
|
node_ptr make_ele(node_ptr arg1, node_ptr arg2)
|
|
{
|
|
node_ptr ele;
|
|
check_arg(arg1, 0);
|
|
for (ele = arg1; ele; ele = ele->next)
|
|
check_arg(ele, 0);
|
|
return new_node(N_ELE, 1, "in", arg1, arg2);
|
|
}
|
|
|
|
node_ptr make_case(node_ptr arg1, node_ptr arg2)
|
|
{
|
|
check_arg(arg1, 1);
|
|
check_arg(arg2, 0);
|
|
return new_node(N_CASE, 0, ":", arg1, arg2);
|
|
}
|
|
|
|
void insert_code(node_ptr qstring)
|
|
{
|
|
if (!qstring)
|
|
yyerror("Null node");
|
|
else {
|
|
#if !defined(VLOG) && !defined(UCLID)
|
|
fputs(qstring->sval, outfile);
|
|
fputs("\n", outfile);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void add_arg(node_ptr var, node_ptr qstring, int isbool)
|
|
{
|
|
if (!var || !qstring) {
|
|
yyerror("Null node");
|
|
return;
|
|
}
|
|
add_symbol(var, qstring);
|
|
if (isbool) {
|
|
set_bool(var);
|
|
set_bool(qstring);
|
|
}
|
|
}
|
|
|
|
static char expr_buf[1024];
|
|
static int errlen = 0;
|
|
#define MAXERRLEN 80
|
|
|
|
/* Recursively display expression for error reporting */
|
|
static void show_expr_helper(node_ptr expr)
|
|
{
|
|
switch(expr->type) {
|
|
int len;
|
|
node_ptr ele;
|
|
case N_QUOTE:
|
|
len = strlen(expr->sval) + 2;
|
|
if (len + errlen < MAXERRLEN) {
|
|
sprintf(expr_buf+errlen, "'%s'", expr->sval);
|
|
errlen += len;
|
|
}
|
|
break;
|
|
case N_VAR:
|
|
len = strlen(expr->sval);
|
|
if (len + errlen < MAXERRLEN) {
|
|
sprintf(expr_buf+errlen, "%s", expr->sval);
|
|
errlen += len;
|
|
}
|
|
break;
|
|
case N_NUM:
|
|
len = strlen(expr->sval);
|
|
if (len + errlen < MAXERRLEN) {
|
|
sprintf(expr_buf+errlen, "%s", expr->sval);
|
|
errlen += len;
|
|
}
|
|
break;
|
|
case N_AND:
|
|
if (errlen < MAXERRLEN) {
|
|
sprintf(expr_buf+errlen, "(");
|
|
errlen+=1;
|
|
show_expr_helper(expr->arg1);
|
|
sprintf(expr_buf+errlen, " & ");
|
|
errlen+=3;
|
|
}
|
|
if (errlen < MAXERRLEN) {
|
|
show_expr_helper(expr->arg2);
|
|
sprintf(expr_buf+errlen, ")");
|
|
errlen+=1;
|
|
}
|
|
break;
|
|
case N_OR:
|
|
if (errlen < MAXERRLEN) {
|
|
sprintf(expr_buf+errlen, "(");
|
|
errlen+=1;
|
|
show_expr_helper(expr->arg1);
|
|
sprintf(expr_buf+errlen, " | ");
|
|
errlen+=3;
|
|
}
|
|
if (errlen < MAXERRLEN) {
|
|
show_expr_helper(expr->arg2);
|
|
sprintf(expr_buf+errlen, ")");
|
|
errlen+=1;
|
|
}
|
|
break;
|
|
case N_NOT:
|
|
if (errlen < MAXERRLEN) {
|
|
sprintf(expr_buf+errlen, "!");
|
|
errlen+=1;
|
|
show_expr_helper(expr->arg1);
|
|
}
|
|
break;
|
|
case N_COMP:
|
|
if (errlen < MAXERRLEN) {
|
|
sprintf(expr_buf+errlen, "(");
|
|
errlen+=1;
|
|
show_expr_helper(expr->arg1);
|
|
sprintf(expr_buf+errlen, " %s ", expr->sval);
|
|
errlen+=4;
|
|
}
|
|
if (errlen < MAXERRLEN) {
|
|
show_expr_helper(expr->arg2);
|
|
sprintf(expr_buf+errlen, ")");
|
|
errlen+=1;
|
|
}
|
|
break;
|
|
case N_ELE:
|
|
if (errlen < MAXERRLEN) {
|
|
sprintf(expr_buf+errlen, "(");
|
|
errlen+=1;
|
|
show_expr_helper(expr->arg1);
|
|
sprintf(expr_buf+errlen, " in {");
|
|
errlen+=5;
|
|
}
|
|
for (ele = expr->arg2; ele; ele=ele->next) {
|
|
if (errlen < MAXERRLEN) {
|
|
show_expr_helper(ele);
|
|
if (ele->next) {
|
|
sprintf(expr_buf+errlen, ", ");
|
|
errlen+=2;
|
|
}
|
|
}
|
|
}
|
|
if (errlen < MAXERRLEN) {
|
|
sprintf(expr_buf+errlen, "})");
|
|
errlen+=2;
|
|
}
|
|
break;
|
|
case N_CASE:
|
|
if (errlen < MAXERRLEN) {
|
|
sprintf(expr_buf+errlen, "[ ");
|
|
errlen+=2;
|
|
}
|
|
for (ele = expr; errlen < MAXERRLEN && ele; ele=ele->next) {
|
|
show_expr_helper(ele->arg1);
|
|
sprintf(expr_buf+errlen, " : ");
|
|
errlen += 3;
|
|
show_expr_helper(ele->arg2);
|
|
}
|
|
if (errlen < MAXERRLEN) {
|
|
sprintf(expr_buf+errlen, " ]");
|
|
errlen+=2;
|
|
}
|
|
break;
|
|
default:
|
|
if (errlen < MAXERRLEN) {
|
|
sprintf(expr_buf+errlen, "??");
|
|
errlen+=2;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
static char *show_expr(node_ptr expr)
|
|
{
|
|
errlen = 0;
|
|
show_expr_helper(expr);
|
|
if (errlen >= MAXERRLEN)
|
|
sprintf(expr_buf+errlen, "...");
|
|
return expr_buf;
|
|
}
|
|
|
|
/* Recursively generate code for function */
|
|
static void gen_expr(node_ptr expr)
|
|
{
|
|
node_ptr ele;
|
|
switch(expr->type) {
|
|
case N_QUOTE:
|
|
yyserror("Unexpected quoted string", expr->sval);
|
|
break;
|
|
case N_VAR:
|
|
{
|
|
node_ptr qstring = find_symbol(expr->sval);
|
|
if (qstring)
|
|
#if defined(VLOG) || defined(UCLID)
|
|
outgen_print("%s", expr->sval);
|
|
#else
|
|
outgen_print("(%s)", qstring->sval);
|
|
#endif
|
|
else
|
|
yyserror("Invalid variable '%s'", expr->sval);
|
|
#ifdef UCLID
|
|
check_for_arg(expr->sval);
|
|
#endif
|
|
|
|
}
|
|
break;
|
|
case N_NUM:
|
|
#ifdef UCLID
|
|
{
|
|
long long int val = atoll(expr->sval);
|
|
if (val < -1)
|
|
outgen_print("pred^%d(CZERO)", -val);
|
|
else if (val == -1)
|
|
outgen_print("pred(CZERO)");
|
|
else if (val == 0)
|
|
outgen_print("CZERO");
|
|
else if (val == 1)
|
|
outgen_print("succ(CZERO)");
|
|
else
|
|
outgen_print("succ^%d(CZERO)", val);
|
|
}
|
|
#else /* !UCLID */
|
|
fputs(expr->sval, outfile);
|
|
#endif /* UCLID */
|
|
break;
|
|
case N_AND:
|
|
outgen_print("(");
|
|
outgen_upindent();
|
|
gen_expr(expr->arg1);
|
|
outgen_print(" & ");
|
|
gen_expr(expr->arg2);
|
|
outgen_print(")");
|
|
outgen_downindent();
|
|
break;
|
|
case N_OR:
|
|
outgen_print("(");
|
|
outgen_upindent();
|
|
gen_expr(expr->arg1);
|
|
outgen_print(" | ");
|
|
gen_expr(expr->arg2);
|
|
outgen_print(")");
|
|
outgen_downindent();
|
|
break;
|
|
case N_NOT:
|
|
#if defined(VLOG) || defined(UCLID)
|
|
outgen_print("~");
|
|
#else
|
|
outgen_print("!");
|
|
#endif
|
|
gen_expr(expr->arg1);
|
|
break;
|
|
case N_COMP:
|
|
outgen_print("(");
|
|
outgen_upindent();
|
|
gen_expr(expr->arg1);
|
|
#ifdef UCLID
|
|
{
|
|
char *cval = expr->sval;
|
|
if (strcmp(cval, "==") == 0)
|
|
cval = "=";
|
|
outgen_print(" %s ", cval);
|
|
}
|
|
#else /* !UCLID */
|
|
outgen_print(" %s ", expr->sval);
|
|
#endif /* UCLID */
|
|
gen_expr(expr->arg2);
|
|
outgen_print(")");
|
|
outgen_downindent();
|
|
break;
|
|
case N_ELE:
|
|
outgen_print("(");
|
|
outgen_upindent();
|
|
for (ele = expr->arg2; ele; ele=ele->next) {
|
|
gen_expr(expr->arg1);
|
|
#ifdef UCLID
|
|
outgen_print(" = ");
|
|
#else
|
|
outgen_print(" == ");
|
|
#endif
|
|
gen_expr(ele);
|
|
if (ele->next)
|
|
#if defined(VLOG) || defined(UCLID)
|
|
outgen_print(" | ");
|
|
#else
|
|
outgen_print(" || ");
|
|
#endif
|
|
}
|
|
outgen_print(")");
|
|
outgen_downindent();
|
|
break;
|
|
case N_CASE:
|
|
#ifdef UCLID
|
|
outgen_print("case");
|
|
outgen_terminate();
|
|
{
|
|
/* Use this to keep track of last case when no default is given */
|
|
node_ptr last_arg2 = NULL;
|
|
for (ele = expr; ele; ele=ele->next) {
|
|
outgen_print(" ");
|
|
if (ele->arg1->type == N_NUM && atoll(ele->arg1->sval) == 1) {
|
|
outgen_print("default");
|
|
last_arg2 = NULL;
|
|
}
|
|
else {
|
|
gen_expr(ele->arg1);
|
|
last_arg2 = ele->arg2;
|
|
}
|
|
outgen_print(" : ");
|
|
gen_expr(ele->arg2);
|
|
outgen_print(";");
|
|
outgen_terminate();
|
|
}
|
|
if (last_arg2) {
|
|
/* Use final case as default */
|
|
outgen_print(" default : ");
|
|
gen_expr(last_arg2);
|
|
outgen_print(";");
|
|
outgen_terminate();
|
|
}
|
|
}
|
|
outgen_print(" esac");
|
|
#else /* !UCLID */
|
|
outgen_print("(");
|
|
outgen_upindent();
|
|
int done = 0;
|
|
for (ele = expr; ele && !done; ele=ele->next) {
|
|
if (ele->arg1->type == N_NUM && atoll(ele->arg1->sval) == 1) {
|
|
gen_expr(ele->arg2);
|
|
done = 1;
|
|
} else {
|
|
gen_expr(ele->arg1);
|
|
outgen_print(" ? ");
|
|
gen_expr(ele->arg2);
|
|
outgen_print(" : ");
|
|
}
|
|
}
|
|
if (!done)
|
|
outgen_print("0");
|
|
outgen_print(")");
|
|
outgen_downindent();
|
|
#endif
|
|
break;
|
|
default:
|
|
yyerror("Unknown node type");
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/* Generate code defining function for var */
|
|
void gen_funct(node_ptr var, node_ptr expr, int isbool)
|
|
{
|
|
if (!var || !expr) {
|
|
yyerror("Null node");
|
|
return;
|
|
}
|
|
check_arg(expr, isbool);
|
|
#ifdef VLOG
|
|
outgen_print("assign %s = ", var->sval);
|
|
outgen_terminate();
|
|
outgen_print(" ");
|
|
gen_expr(expr);
|
|
outgen_print(";");
|
|
outgen_terminate();
|
|
outgen_terminate();
|
|
#else /* !VLOG */
|
|
#ifdef UCLID
|
|
if (annotate) {
|
|
/* Print annotation information*/
|
|
outgen_print("(* $define %s *)", var->sval);
|
|
outgen_terminate();
|
|
}
|
|
outgen_print("%s := ", var->sval);
|
|
outgen_terminate();
|
|
outgen_print(" ");
|
|
if (isbool && expr->type == N_NUM) {
|
|
outgen_print("%d", atoll(var->sval));
|
|
} else
|
|
gen_expr(expr);
|
|
outgen_print(";");
|
|
outgen_terminate();
|
|
if (annotate) {
|
|
int i;
|
|
outgen_print("(* $args");
|
|
for (i = 0; i < arg_cnt; i++)
|
|
outgen_print("%c%s", i == 0 ? ' ' : ':', arg_names[i]);
|
|
outgen_print(" *)");
|
|
outgen_terminate();
|
|
arg_cnt = 0;
|
|
}
|
|
outgen_terminate();
|
|
#else /* !UCLID */
|
|
/* Print function header */
|
|
outgen_print("long long gen_%s()", var->sval);
|
|
outgen_terminate();
|
|
outgen_print("{");
|
|
outgen_terminate();
|
|
outgen_print(" return ");
|
|
gen_expr(expr);
|
|
outgen_print(";");
|
|
outgen_terminate();
|
|
outgen_print("}");
|
|
outgen_terminate();
|
|
outgen_terminate();
|
|
#endif /* UCLID */
|
|
#endif /* VLOG */
|
|
}
|