diff --git a/PUdp.h b/PUdp.h new file mode 100644 index 000000000..35ba4c1e9 --- /dev/null +++ b/PUdp.h @@ -0,0 +1,80 @@ +#ifndef __PUdp_H +#define __PUdp_H +/* + * Copyright (c) 1998 Stephen Williams (steve@picturel.com) + * + * This source code is free software; you can redistribute it + * and/or modify it in source code form under the terms of the GNU + * General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) + * any later 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; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ +#if !defined(WINNT) +#ident "$Id: PUdp.h,v 1.1 1998/11/25 02:35:53 steve Exp $" +#endif + +# include +# include "verinum.h" + +/* + * This class represents a parsed UDP. This is a much simpler object + * then a module or macromodule. + * + * - all ports are scaler, + * - pin 0 (the first port) is always output, + * and the remaining pins are input. + * + * Thus, the ports can be represented as an ordered list of pin names. + * If the output port is declared as a register in the Verilog source, + * then this is a sequential UDP and the sequential flag is set to true. + * + * STATE TABLE + * Each entry in the state table is given as a string with the same + * number of characters as inputs. If the UDP is sequential, a + * character is also included at the end of the string to represent + * the current output. + * + * If the UDP is sequential, the "initial" member is taken to be the + * intial value assigned in the source, or 'x' if none is given. + */ +class PUdp { + + public: + explicit PUdp(const string&n, unsigned nports) + : ports(nports), sequential(false), initial(verinum::Vx), name_(n) { } + + vectorports; + bool sequential; + + vectortinput; + vector tcurrent; + vector toutput; + + verinum::V initial; + + void dump(ostream&out) const; + + private: + const string name_; + + private: // Not implemented + PUdp(const PUdp&); + PUdp& operator= (const PUdp&); +}; + +/* + * $Log: PUdp.h,v $ + * Revision 1.1 1998/11/25 02:35:53 steve + * Parse UDP primitives all the way to pform. + * + */ +#endif diff --git a/lexor.lex b/lexor.lex index ad202a0b8..70db0c98a 100644 --- a/lexor.lex +++ b/lexor.lex @@ -19,7 +19,7 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #if !defined(WINNT) -#ident "$Id: lexor.lex,v 1.4 1998/11/23 00:20:23 steve Exp $" +#ident "$Id: lexor.lex,v 1.5 1998/11/25 02:35:53 steve Exp $" #endif //# define YYSTYPE lexval @@ -55,7 +55,7 @@ static verinum*make_sized_hex(const char*txt); [ \t\b\f\r] { ; } \n { yylloc.first_line += 1; } -"//".* { ; } +<*>"//".* { ; } "/*" { BEGIN(CCOMMENT); } . { yymore(); } @@ -82,7 +82,14 @@ static verinum*make_sized_hex(const char*txt); return STRING; } . { yymore(); } -[xXbB01\?] { return yytext[0]; } +\(\?\?\) { return '*'; } +\(01\) { return 'r'; } +\(10\) { return 'f'; } +[bB] { return 'b'; } +[fF] { return 'f'; } +[rR] { return 'r'; } +[xX] { return 'x'; } +[pPnN01\?\*\-] { return yytext[0]; } [a-zA-Z_][a-zA-Z0-9$_]* { int rc = check_identifier(yytext); @@ -103,13 +110,13 @@ static verinum*make_sized_hex(const char*txt); yylval.text = new string(yytext); return SYSTEM_IDENTIFIER; } -([0-9][0-9_])?\'d[0-9][0-9_]* { yylval.number = 0; +[0-9][0-9_]*\'d[0-9][0-9_]* { yylval.number = 0; return NUMBER; } -([0-9][0-9_])?\'[bB][0-1xz_]+ { yylval.number = make_sized_binary(yytext); +[0-9][0-9_]*\'[bB][0-1xz_]+ { yylval.number = make_sized_binary(yytext); return NUMBER; } -([0-9][0-9_])?\'[oO][0-7xz_]+ { yylval.number = make_sized_octal(yytext); +[0-9][0-9_]*\'[oO][0-7xz_]+ { yylval.number = make_sized_octal(yytext); return NUMBER; } -([0-9][0-9_])?\'[hH][0-9a-fA-Fxz_]+ { yylval.number = make_sized_hex(yytext); +[0-9][0-9_]*\'[hH][0-9a-fA-Fxz_]+ { yylval.number = make_sized_hex(yytext); return NUMBER; } [0-9][0-9_]* { @@ -275,7 +282,7 @@ static verinum*make_sized_binary(const char*txt) verinum::V*bits = new verinum::V[size]; - unsigned idx = size; + unsigned idx = 0; char*eptr = ptr + strlen(ptr) - 1; while ((eptr > ptr) && (idx < size)) { @@ -306,7 +313,7 @@ static verinum*make_sized_binary(const char*txt) // Zero-extend binary number, except that z or x is extended // if it is the highest supplied digit. - while (idx > 0) { + while (idx < size) { switch (ptr[1]) { case '0': case '1': @@ -336,7 +343,7 @@ static verinum*make_sized_octal(const char*txt) verinum::V*bits = new verinum::V[size]; - unsigned idx = size; + unsigned idx = 0; char*eptr = ptr + strlen(ptr); while ((eptr > ptr) && (idx < (size-3))) { @@ -363,7 +370,7 @@ static verinum*make_sized_octal(const char*txt) } // zero extend octal numbers - while (idx > 0) switch (ptr[1]) { + while (idx < size) switch (ptr[1]) { case 'x': bits[idx++] = verinum::Vx; break; @@ -387,7 +394,7 @@ static verinum*make_sized_hex(const char*txt) verinum::V*bits = new verinum::V[size]; - unsigned idx = size; + unsigned idx = 0; char*eptr = ptr + strlen(ptr); while ((eptr > ptr) && (idx < (size-4))) { @@ -428,7 +435,7 @@ static verinum*make_sized_hex(const char*txt) } // zero extend octal numbers - while (idx > 0) switch (ptr[1]) { + while (idx < size) switch (ptr[1]) { case 'x': bits[idx++] = verinum::Vx; break; diff --git a/main.cc b/main.cc index 8512cc982..ae8d4bf71 100644 --- a/main.cc +++ b/main.cc @@ -17,7 +17,7 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #if !defined(WINNT) -#ident "$Id: main.cc,v 1.5 1998/11/18 04:25:22 steve Exp $" +#ident "$Id: main.cc,v 1.6 1998/11/25 02:35:53 steve Exp $" #endif # include @@ -155,7 +155,8 @@ int main(int argc, char*argv[]) /* Parse the input. Make the pform. */ listmodules; - int rc = pform_parse(input, modules); + mapprimitives; + int rc = pform_parse(input, modules, primitives); if (rc) { return rc; @@ -163,12 +164,18 @@ int main(int argc, char*argv[]) if (dump_flag) { ofstream out ("a.pf"); - out << "PFORM DUMP:" << endl; + out << "PFORM DUMP MODULES:" << endl; for (list::iterator mod = modules.begin() ; mod != modules.end() ; mod ++ ) { pform_dump(out, *mod); } + out << "PFORM DUMP PRIMITIVES:" << endl; + for (map::iterator idx = primitives.begin() + ; idx != primitives.end() + ; idx ++ ) { + (*idx).second->dump(out); + } } @@ -219,6 +226,9 @@ int main(int argc, char*argv[]) /* * $Log: main.cc,v $ + * Revision 1.6 1998/11/25 02:35:53 steve + * Parse UDP primitives all the way to pform. + * * Revision 1.5 1998/11/18 04:25:22 steve * Add -f flags for generic flag key/values. * diff --git a/parse.y b/parse.y index 51ef92aa3..395ae5979 100644 --- a/parse.y +++ b/parse.y @@ -19,7 +19,7 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #if !defined(WINNT) -#ident "$Id: parse.y,v 1.6 1998/11/23 00:20:23 steve Exp $" +#ident "$Id: parse.y,v 1.7 1998/11/25 02:35:53 steve Exp $" #endif # include "parse_misc.h" @@ -30,6 +30,7 @@ extern void lex_end_table(); %} %union { + char letter; string*text; list*strings; @@ -74,6 +75,13 @@ extern void lex_end_table(); %token KK_attribute +%type udp_input_sym udp_output_sym +%type udp_input_list udp_sequ_entry udp_comb_entry +%type udp_entry_list udp_comb_entry_list udp_sequ_entry_list +%type udp_body udp_port_list +%type udp_port_decl udp_port_decls +%type udp_initial udp_init_opt + %type identifier lvalue register_variable %type list_of_register_variables %type list_of_variables @@ -622,46 +630,162 @@ statement_opt udp_body : K_table { lex_start_table(); } - udp_comb_entry_list - K_endtable { lex_end_table(); } + udp_entry_list + K_endtable { lex_end_table(); $$ = $3; } + ; + +udp_entry_list + : udp_comb_entry_list + | udp_sequ_entry_list ; udp_comb_entry : udp_input_list ':' udp_output_sym ';' + { string*tmp = $1; + *tmp += ':'; + *tmp += $3; + $$ = tmp; + } ; udp_comb_entry_list : udp_comb_entry + { list*tmp = new list; + tmp->push_back(*$1); + delete $1; + $$ = tmp; + } | udp_comb_entry_list udp_comb_entry + { list*tmp = $1; + tmp->push_back(*$2); + delete $2; + $$ = tmp; + } + ; + +udp_sequ_entry_list + : udp_sequ_entry + { list*tmp = new list; + tmp->push_back(*$1); + delete $1; + $$ = tmp; + } + | udp_sequ_entry_list udp_sequ_entry + { list*tmp = $1; + tmp->push_back(*$2); + delete $2; + $$ = tmp; + } + ; + +udp_sequ_entry + : udp_input_list ':' udp_input_sym ':' udp_output_sym ';' + { string*tmp = $1; + *tmp += ':'; + *tmp += $3; + *tmp += ':'; + *tmp += $5; + $$ = tmp; + } + ; + +udp_initial + : K_initial IDENTIFIER '=' NUMBER ';' + { PExpr*etmp = new PENumber($4); + PAssign*atmp = new PAssign(*$2, etmp); + delete $2; + $$ = atmp; + } + ; + +udp_init_opt + : udp_initial { $$ = $1; } + | { $$ = 0; } ; udp_input_list : udp_input_sym + { string*tmp = new string; + *tmp += $1; + $$ = tmp; + } | udp_input_list udp_input_sym + { string*tmp = $1; + *tmp += $2; + $$ = tmp; + } ; -udp_input_sym : '0' | '1' | 'x' | 'X' | '?' | 'b' | 'B' ; -udp_output_sym : '0' | '1' | 'x' | 'X' ; +udp_input_sym + : '0' { $$ = '0'; } + | '1' { $$ = '1'; } + | 'x' { $$ = 'x'; } + | '?' { $$ = '?'; } + | 'b' { $$ = 'b'; } + | '*' { $$ = '*'; } + | 'f' { $$ = 'f'; } + | 'r' { $$ = 'f'; } + ; + +udp_output_sym + : '0' { $$ = '0'; } + | '1' { $$ = '1'; } + | 'x' { $$ = 'x'; } + | '-' { $$ = '-'; } + ; udp_port_decl : K_input list_of_variables ';' + { $$ = pform_make_udp_input_ports($2); } | K_output IDENTIFIER ';' + { PWire*pp = new PWire(*$2); + pp->port_type = NetNet::POUTPUT; + list*tmp = new list; + tmp->push_back(pp); + delete $2; + $$ = tmp; + } + | K_reg IDENTIFIER ';' + { PWire*pp = new PWire(*$2, NetNet::REG); + pp->port_type = NetNet::PIMPLICIT; + list*tmp = new list; + tmp->push_back(pp); + delete $2; + $$ = tmp; + } ; udp_port_decls : udp_port_decl + { $$ = $1; } | udp_port_decls udp_port_decl + { list*tmp = $1; + tmp->merge(*$2); + delete $2; + $$ = tmp; + } ; udp_port_list - : IDENTIFIER { ; } - | udp_port_list ',' IDENTIFIER { ; } + : IDENTIFIER + { list*tmp = new list; + tmp->push_back(*$1); + delete $1; + $$ = tmp; + } + | udp_port_list ',' IDENTIFIER + { list*tmp = $1; + tmp->push_back(*$3); + delete $3; + $$ = tmp; + } ; udp_primitive : K_primitive IDENTIFIER '(' udp_port_list ')' ';' udp_port_decls + udp_init_opt udp_body K_endprimitive - { yyerror(@1, "Sorry, UDP primitives not supported."); } + { pform_make_udp($2, $4, $7, $9, $8); } ; diff --git a/pform.cc b/pform.cc index af130cc57..edaac902b 100644 --- a/pform.cc +++ b/pform.cc @@ -17,12 +17,14 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #if !defined(WINNT) -#ident "$Id: pform.cc,v 1.4 1998/11/23 00:20:23 steve Exp $" +#ident "$Id: pform.cc,v 1.5 1998/11/25 02:35:53 steve Exp $" #endif # include "pform.h" # include "parse_misc.h" +# include "PUdp.h" # include +# include # include # include @@ -31,6 +33,7 @@ extern int VLparse(); static Module*cur_module = 0; static list*vl_modules = 0; +static map vl_primitives; /* * This function evaluates delay expressions. The result should be a @@ -74,6 +77,151 @@ void pform_endmodule(const string&name) cur_module = 0; } +void pform_make_udp(string*name, list*parms, + list*decl, list*table, + Statement*init_expr) +{ + assert(parms->size() > 0); + + /* Put the declarations into a map, so that I can check them + off with the parameters in the list. I will rebuild a list + of parameters for the PUdp object. */ + map defs; + for (list::iterator cur = decl->begin() + ; cur != decl->end() + ; cur ++ ) + + if (defs[(*cur)->name] == 0) { + defs[(*cur)->name] = *cur; + + } else switch ((*cur)->port_type) { + + case NetNet::PIMPLICIT: + case NetNet::POUTPUT: + assert(defs[(*cur)->name]->port_type != NetNet::PINPUT); + // OK, merge the output definitions. + defs[(*cur)->name]->port_type = NetNet::POUTPUT; + if ((*cur)->type == NetNet::REG) + defs[(*cur)->name]->type = NetNet::REG; + break; + + case NetNet::PINPUT: + // Allow duplicate input declarations. + assert(defs[(*cur)->name]->port_type == NetNet::PINPUT); + delete *cur; + break; + + default: + assert(0); + } + + + /* Put the parameters into a vector of wire descriptions. Look + in the map for the definitions of the name. */ + vector pins (parms->size()); + { list::iterator cur; + unsigned idx; + for (cur = parms->begin(), idx = 0 + ; cur != parms->end() + ; idx++, cur++) { + pins[idx] = defs[*cur]; + } + } + + /* Check that the output is an output and the inputs are + inputs. I can also make sure that only the single output is + declared a register, if anything. */ + assert(pins.size() > 0); + assert(pins[0]); + assert(pins[0]->port_type == NetNet::POUTPUT); + for (unsigned idx = 1 ; idx < pins.size() ; idx += 1) { + assert(pins[idx]); + assert(pins[idx]->port_type == NetNet::PINPUT); + assert(pins[idx]->type != NetNet::REG); + } + + /* Interpret and check the table entry strings, to make sure + they correspond to the inputs, output and output type. Make + up vectors for the fully interpreted result that can be + placed in the PUdp object. */ + vector input (table->size()); + vector current (table->size()); + vector output (table->size()); + { unsigned idx = 0; + for (list::iterator cur = table->begin() + ; cur != table->end() + ; cur ++, idx += 1) { + string tmp = *cur; + assert(tmp.find(':') == (pins.size() - 1)); + + input[idx] = tmp.substr(0, pins.size()-1); + tmp = tmp.substr(pins.size()-1); + + if (pins[0]->type == NetNet::REG) { + assert(tmp[0] == ':'); + assert(tmp.size() == 4); + current[idx] = tmp[1]; + tmp = tmp.substr(2); + } + + assert(tmp[0] == ':'); + assert(tmp.size() == 2); + output[idx] = tmp[1]; + } + } + + /* Verify the "initial" statement, if present, to be sure that + it only assignes to the output and the output is + registered. Then save the initial value that I get. */ + verinum::V init = verinum::Vx; + if (init_expr) { + // XXXX + assert(pins[0]->type == NetNet::REG); + + PAssign*pa = dynamic_cast(init_expr); + assert(pa); + + // XXXX + assert(pa->lval() == pins[0]->name); + + const PENumber*np = dynamic_cast(pa->get_expr()); + assert(np); + + init = np->value()[0]; + } + + // Put the primitive into the primitives table + if (vl_primitives[*name]) { + VLerror("UDP primitive already exists."); + + } else { + PUdp*udp = new PUdp(*name, parms->size()); + + // Detect sequential udp. + if (pins[0]->type == NetNet::REG) + udp->sequential = true; + + // Make the port list for the UDP + for (unsigned idx = 0 ; idx < pins.size() ; idx += 1) + udp->ports[idx] = pins[idx]->name; + + udp->tinput = input; + udp->tcurrent = current; + udp->toutput = output; + udp->initial = init; + + vl_primitives[*name] = udp; + } + + /* Delete the excess tables and lists from the parser. */ + delete name; + delete parms; + delete decl; + delete table; + delete init_expr; +} + + void pform_makegate(PGBuiltin::Type type, const string&name, const vector&wires, @@ -249,6 +397,22 @@ void pform_set_net_range(list*names, list*range) } } +list* pform_make_udp_input_ports(list*names) +{ + list*out = new list; + + for (list::const_iterator cur = names->begin() + ; cur != names->end() + ; cur ++ ) { + PWire*pp = new PWire(*cur); + pp->port_type = NetNet::PINPUT; + out->push_back(pp); + } + + delete names; + return out; +} + void pform_make_behavior(PProcess::Type type, Statement*st) { PProcess*pp = new PProcess(type, st); @@ -284,7 +448,7 @@ Statement* pform_make_calltask(string*name, list*parms) } FILE*vl_input = 0; -int pform_parse(FILE*input, list&modules) +int pform_parse(FILE*input, list&modules, map&prim) { vl_input = input; vl_modules = &modules; @@ -294,12 +458,17 @@ int pform_parse(FILE*input, list&modules) if (rc) { cerr << "I give up." << endl; } + + prim = vl_primitives; return error_count; } /* * $Log: pform.cc,v $ + * Revision 1.5 1998/11/25 02:35:53 steve + * Parse UDP primitives all the way to pform. + * * Revision 1.4 1998/11/23 00:20:23 steve * NetAssign handles lvalues as pin links * instead of a signal pointer, diff --git a/pform.h b/pform.h index 74391b798..429259989 100644 --- a/pform.h +++ b/pform.h @@ -19,7 +19,7 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #if !defined(WINNT) -#ident "$Id: pform.h,v 1.2 1998/11/23 00:20:23 steve Exp $" +#ident "$Id: pform.h,v 1.3 1998/11/25 02:35:53 steve Exp $" #endif # include "netlist.h" @@ -27,6 +27,7 @@ # include "Statement.h" # include "PGate.h" # include "PExpr.h" +# include "PUdp.h" # include "PWire.h" # include "verinum.h" # include @@ -77,6 +78,10 @@ struct lgate { extern void pform_startmodule(const string&, list*ports); extern void pform_endmodule(const string&); +extern void pform_make_udp(string*name, list*parms, + list*decl, list*table, + Statement*init); + /* * The makewire functions announce to the pform code new wires. These * go into a module that is currently opened. @@ -92,6 +97,8 @@ extern Statement* pform_make_block(PBlock::BL_TYPE, list*); extern Statement* pform_make_assignment(string*t, PExpr*e); extern Statement* pform_make_calltask(string*t, list* =0); +extern list* pform_make_udp_input_ports(list*); + /* * The makegate function creates a new gate (which need not have a * name) and connects it to the specified wires. @@ -117,11 +124,14 @@ extern void pform_make_pgassign(const string&lval, PExpr*sel, PExpr*rval); * parses the source file and places all the modules it finds into the * mod list. The dump function dumps a module to the output stream. */ -extern int pform_parse(FILE*, list&mod); +extern int pform_parse(FILE*, list&mod, map&prim); extern void pform_dump(ostream&out, Module*mod); /* * $Log: pform.h,v $ + * Revision 1.3 1998/11/25 02:35:53 steve + * Parse UDP primitives all the way to pform. + * * Revision 1.2 1998/11/23 00:20:23 steve * NetAssign handles lvalues as pin links * instead of a signal pointer, diff --git a/pform_dump.cc b/pform_dump.cc index 53578fe20..0f5939d9f 100644 --- a/pform_dump.cc +++ b/pform_dump.cc @@ -17,7 +17,7 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #if !defined(WINNT) -#ident "$Id: pform_dump.cc,v 1.5 1998/11/23 00:20:23 steve Exp $" +#ident "$Id: pform_dump.cc,v 1.6 1998/11/25 02:35:54 steve Exp $" #endif /* @@ -332,9 +332,42 @@ void pform_dump(ostream&out, Module*mod) out << "endmodule" << endl; } +void PUdp::dump(ostream&out) const +{ + out << "primitive " << name_ << "(" << ports[0]; + for (unsigned idx = 1 ; idx < ports.size() ; idx += 1) + out << ", " << ports[idx]; + out << ");" << endl; + + if (sequential) + out << " reg " << ports[0] << ";" << endl; + + out << " table" << endl; + for (unsigned idx = 0 ; idx < tinput.size() ; idx += 1) { + out << " "; + for (unsigned chr = 0 ; chr < tinput[idx].length() ; chr += 1) + out << " " << tinput[idx][chr]; + + if (sequential) + out << " : " << tcurrent[idx]; + + out << " : " << toutput[idx] << " ;" << endl; + } + out << " endtable" << endl; + + if (sequential) + out << " initial " << ports[0] << " = 1'b" << initial + << ";" << endl; + + out << "endprimitive" << endl; +} + /* * $Log: pform_dump.cc,v $ + * Revision 1.6 1998/11/25 02:35:54 steve + * Parse UDP primitives all the way to pform. + * * Revision 1.5 1998/11/23 00:20:23 steve * NetAssign handles lvalues as pin links * instead of a signal pointer,