diff --git a/.cvsignore b/.cvsignore index aa1d8069f..09d9b8b0d 100644 --- a/.cvsignore +++ b/.cvsignore @@ -2,6 +2,7 @@ lexor_keyword.cc parse.h parse.cc parse.cc.output +syn-rules.cc lexor.cc ivl dep diff --git a/Makefile.in b/Makefile.in index ef690c370..8c05c3d7b 100644 --- a/Makefile.in +++ b/Makefile.in @@ -18,7 +18,7 @@ # 59 Temple Place - Suite 330 # Boston, MA 02111-1307, USA # -#ident "$Id: Makefile.in,v 1.54 2000/05/12 05:23:15 steve Exp $" +#ident "$Id: Makefile.in,v 1.55 2000/05/13 20:55:47 steve Exp $" # # SHELL = /bin/sh @@ -67,7 +67,7 @@ distclean: clean rm -f Makefile TT = t-null.o t-verilog.o t-vvm.o t-xnf.o -FF = nodangle.o propinit.o synth.o xnfio.o +FF = nodangle.o propinit.o synth.o syn-rules.o xnfio.o O = main.o cprop.o design_dump.o dup_expr.o elaborate.o elab_expr.o \ elab_net.o elab_pexpr.o elab_scope.o elab_sig.o emit.o eval.o eval_tree.o \ @@ -119,6 +119,9 @@ parse.h parse.cc: $(srcdir)/parse.y bison --verbose -t -p VL -d $(srcdir)/parse.y -o parse.cc mv parse.cc.h parse.h +syn-rules.cc: $(srcdir)/syn-rules.y + bison --verbose -p syn_ -o syn-rules.cc $(srcdir)/syn-rules.y + lexor.cc: $(srcdir)/lexor.lex flex -PVL -s -olexor.cc $(srcdir)/lexor.lex diff --git a/iverilog.c b/iverilog.c index e33b29c8b..873732b8d 100644 --- a/iverilog.c +++ b/iverilog.c @@ -17,7 +17,7 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #if !defined(WINNT) -#ident "$Id: iverilog.c,v 1.12 2000/05/09 00:02:13 steve Exp $" +#ident "$Id: iverilog.c,v 1.13 2000/05/13 20:55:47 steve Exp $" #endif #include @@ -96,7 +96,7 @@ static int t_vvm(char*cmd, unsigned ncmd) sprintf(tmp, " | %s/ivl %s -o %s.cc -tvvm -Fcprop %s -Fnodangle" " -fVPI_MODULE_PATH=%s", base, warning_flags, opath, - synth_flag?"-Fsynth":"", base); + synth_flag?"-Fsynth -Fsyn-rules":"", base); rc = strlen(tmp); cmd = realloc(cmd, ncmd+rc+1); @@ -155,7 +155,7 @@ static int t_xnf(char*cmd, unsigned ncmd) { int rc; - sprintf(tmp, " | %s/ivl %s -o %s -txnf -Fcprop -Fsynth " + sprintf(tmp, " | %s/ivl %s -o %s -txnf -Fcprop -Fsynth -Fsyn-rules " "-Fnodangle -Fxnfio", base, warning_flags, opath); rc = strlen(tmp); @@ -355,6 +355,9 @@ int main(int argc, char **argv) /* * $Log: iverilog.c,v $ + * Revision 1.13 2000/05/13 20:55:47 steve + * Use yacc based synthesizer. + * * Revision 1.12 2000/05/09 00:02:13 steve * Parameterize LD lib in C++ command line. * diff --git a/main.cc b/main.cc index 124852967..e506a11a9 100644 --- a/main.cc +++ b/main.cc @@ -19,7 +19,7 @@ const char COPYRIGHT[] = * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #if !defined(WINNT) && !defined(macintosh) -#ident "$Id: main.cc,v 1.33 2000/05/08 05:29:43 steve Exp $" +#ident "$Id: main.cc,v 1.34 2000/05/13 20:55:47 steve Exp $" #endif const char NOTICE[] = @@ -89,6 +89,7 @@ extern Design* elaborate(const map&modules, extern void cprop(Design*des); extern void propinit(Design*des); extern void synth(Design*des); +extern void syn_rules(Design*des); extern void nodangle(Design*des); extern void xnfio(Design*des); @@ -101,6 +102,7 @@ static struct net_func_map { { "nodangle",&nodangle }, { "propinit",&propinit }, { "synth", &synth }, + { "syn-rules", &syn_rules }, { "xnfio", &xnfio }, { 0, 0 } }; @@ -303,6 +305,9 @@ int main(int argc, char*argv[]) /* * $Log: main.cc,v $ + * Revision 1.34 2000/05/13 20:55:47 steve + * Use yacc based synthesizer. + * * Revision 1.33 2000/05/08 05:29:43 steve * no need for nobufz functor. * diff --git a/syn-rules.y b/syn-rules.y new file mode 100644 index 000000000..e91bcb158 --- /dev/null +++ b/syn-rules.y @@ -0,0 +1,350 @@ + +%{ +/* + * Copyright (c) 2000 Stephen Williams (steve@icarus.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) && !defined(macintosh) +#ident "$Id: syn-rules.y,v 1.1 2000/05/13 20:55:47 steve Exp $" +#endif + +/* + * This file implements synthesys based on matching threads and + * converting them to equivilent devices. The trick here is that the + * proc_match_t functor can be used to scan a process and generate a + * string of tokens. That string of tokens can then be matched by the + * rules to determin what kind of device is to be made. + */ + +# include "netlist.h" +# include "functor.h" + +struct syn_token_t { + int token; + + NetAssign*assign; + NetAssignMem*assign_mem; + NetProcTop*top; + NetEvWait*evwait; + NetEvent*event; + NetExpr*expr; + + syn_token_t*next_; +}; +#define YYSTYPE syn_token_t* + +static int yylex(); +static void yyerror(const char*); +static Design*des_; + +static void make_DFF_CE(Design*des, NetProcTop*top, NetEvWait*wclk, + NetEvent*eclk, NetExpr*cexp, NetAssign*asn); +static void make_RAM_CE(Design*des, NetProcTop*top, NetEvWait*wclk, + NetEvent*eclk, NetExpr*cexp, NetAssignMem*asn); +%} + +%token S_ALWAYS S_ASSIGN S_ASSIGN_MEM S_ELSE S_EVENT S_EXPR S_IF S_INITIAL + +%% + +start + + /* These rules match simple DFF devices. Clocked assignments are + simply implemented as DFF, and a CE is easily expressed with a + conditional statement. The typical Verilog that get these are: + + always @(posedge CLK) Q = D + always @(negedge CLK) Q = D + + always @(posedge CLK) if (CE) Q = D; + always @(negedge CLK) if (CE) Q = D; + + The width of Q and D cause a wide register to be created. The + code generators generally implement that as an array of + flip-flops. */ + + : S_ALWAYS '@' '(' S_EVENT ')' S_ASSIGN + { make_DFF_CE(des_, $1->top, $2->evwait, $4->event, + 0, $6->assign); + des_->delete_process($1->top); + } + + | S_ALWAYS '@' '(' S_EVENT ')' S_IF S_EXPR S_ASSIGN ';' + { make_DFF_CE(des_, $1->top, $2->evwait, $4->event, + $7->expr, $8->assign); + des_->delete_process($1->top); + } + + + /* These rules match RAM devices. They are similar to DFF, except + that there is an index for the word. The typical Verilog that get + these are: + + always @(posedge CLK) M[a] = D + always @(negedge CLK) M[a] = D + + always @(posedge CLK) if (CE) M[a] = D; + always @(negedge CLK) if (CE) M[a] = D; + + The width of Q and D cause a wide register to be created. The + code generators generally implement that as an array of + flip-flops. */ + + | S_ALWAYS '@' '(' S_EVENT ')' S_ASSIGN_MEM ';' + { make_RAM_CE(des_, $1->top, $2->evwait, $4->event, + 0, $6->assign_mem); + des_->delete_process($1->top); + } + + | S_ALWAYS '@' '(' S_EVENT ')' S_IF S_EXPR S_ASSIGN_MEM ';' ';' + { make_RAM_CE(des_, $1->top, $2->evwait, $4->event, + $7->expr, $8->assign_mem); + des_->delete_process($1->top); + } + ; + +%% + + + /* Various actions. */ +static void make_DFF_CE(Design*des, NetProcTop*top, NetEvWait*wclk, + NetEvent*eclk, NetExpr*cexp, NetAssign*asn) +{ + NetEvProbe*pclk = eclk->probe(0); + NetNet*d = asn->rval()->synthesize(des); + NetNet*ce = cexp? cexp->synthesize(des) : 0; + + NetFF*ff = new NetFF(asn->name(), asn->pin_count()); + + for (unsigned idx = 0 ; idx < ff->width() ; idx += 1) { + connect(ff->pin_Data(idx), d->pin(idx)); + connect(ff->pin_Q(idx), asn->pin(idx)); + } + + connect(ff->pin_Clock(), pclk->pin(0)); + if (ce) connect(ff->pin_Enable(), ce->pin(0)); + + ff->attribute("LPM_FFType", "DFF"); + if (pclk->edge() == NetEvProbe::NEGEDGE) + ff->attribute("Clock:LPM_Polarity", "INVERT"); + + des->add_node(ff); +} + +static void make_RAM_CE(Design*des, NetProcTop*top, NetEvWait*wclk, + NetEvent*eclk, NetExpr*cexp, NetAssignMem*asn) +{ + NetMemory*mem = asn->memory(); + NetNet*adr = asn->index(); + + NetEvProbe*pclk = eclk->probe(0); + NetNet*d = asn->rval()->synthesize(des); + NetNet*ce = cexp? cexp->synthesize(des) : 0; + + NetRamDq*ram = new NetRamDq(des_->local_symbol(mem->name()), mem, + adr->pin_count()); + + for (unsigned idx = 0 ; idx < adr->pin_count() ; idx += 1) + connect(adr->pin(idx), ram->pin_Address(idx)); + + for (unsigned idx = 0 ; idx < ram->width() ; idx += 1) + connect(ram->pin_Data(idx), d->pin(idx)); + + if (ce) connect(ram->pin_WE(), ce->pin(0)); + + assert(pclk->edge() == NetEvProbe::POSEDGE); + connect(ram->pin_InClock(), pclk->pin(0)); + + ram->absorb_partners(); + des_->add_node(ram); + + des->add_node(ram); +} + +static syn_token_t*first_ = 0; +static syn_token_t*last_ = 0; +static syn_token_t*ptr_ = 0; + +/* + * The match class is used to take a process and turn it into a stream + * of tokens. This stream is used by the yylex function to feed tokens + * to the parser. + */ +struct tokenize : public proc_match_t { + tokenize() { } + ~tokenize() { } + + int assign(NetAssign*dev) + { + syn_token_t*cur; + cur = new syn_token_t; + cur->token = S_ASSIGN; + cur->assign = dev; + cur->next_ = 0; + last_->next_ = cur; + last_ = cur; + return 0; + } + + int assign_mem(NetAssignMem*dev) + { + syn_token_t*cur; + cur = new syn_token_t; + cur->token = S_ASSIGN_MEM; + cur->assign_mem = dev; + cur->next_ = 0; + last_->next_ = cur; + last_ = cur; + return 0; + } + + int condit(NetCondit*dev) + { + syn_token_t*cur; + + cur = new syn_token_t; + cur->token = S_IF; + cur->next_ = 0; + last_->next_ = cur; + last_ = cur; + + cur = new syn_token_t; + cur->token = S_EXPR; + cur->expr = dev->expr(); + cur->next_ = 0; + last_->next_ = cur; + last_ = cur; + + dev -> if_clause() -> match_proc(this); + + if (dev->else_clause()) { + cur = new syn_token_t; + cur->token = S_ELSE; + cur->next_ = 0; + last_->next_ = cur; + last_ = cur; + + dev -> else_clause() -> match_proc(this); + } + + cur = new syn_token_t; + cur->token = ';'; + cur->next_ = 0; + last_->next_ = cur; + last_ = cur; + return 0; + } + + int event_wait(NetEvWait*dev) + { + syn_token_t*cur; + + cur = new syn_token_t; + cur->token = '@'; + cur->evwait = dev; + cur->next_ = 0; + last_->next_ = cur; + last_ = cur; + + cur = new syn_token_t; + cur->token = '('; + cur->next_ = 0; + last_->next_ = cur; + last_ = cur; + + for (unsigned idx = 0; idx < dev->nevents(); idx += 1) { + cur = new syn_token_t; + cur->token = S_EVENT; + cur->event = dev->event(idx); + cur->next_ = 0; + last_->next_ = cur; + last_ = cur; + } + + cur = new syn_token_t; + cur->token = ')'; + cur->next_ = 0; + last_->next_ = cur; + last_ = cur; + + dev -> statement() -> match_proc(this); + + cur = new syn_token_t; + cur->token = ';'; + cur->next_ = 0; + last_->next_ = cur; + last_ = cur; + return 0; + } +}; + +static void syn_start_process(NetProcTop*t) +{ + first_ = new syn_token_t; + last_ = first_; + ptr_ = first_; + + first_->token = (t->type() == NetProcTop::KALWAYS)? S_ALWAYS : S_INITIAL; + first_->top = t; + first_->next_ = 0; + + tokenize go; + t -> statement() -> match_proc(&go); +} + +static void syn_done_process() +{ + while (first_) { + syn_token_t*cur = first_; + first_ = cur->next_; + delete cur; + } +} + +static int yylex() +{ + if (ptr_ == 0) { + yylval = 0; + return EOF; + } + + yylval = ptr_; + ptr_ = ptr_->next_; + return yylval->token; +} + +struct syn_rules_f : public functor_t { + ~syn_rules_f() { } + + void process(class Design*des, class NetProcTop*top) + { + syn_start_process(top); + yyparse(); + syn_done_process(); + } +}; + +void syn_rules(Design*d) +{ + des_ = d; + syn_rules_f obj; + des_->functor(&obj); +} + +static void yyerror(const char*) +{ +} diff --git a/synth.cc b/synth.cc index a802fd0d6..d28c95fe7 100644 --- a/synth.cc +++ b/synth.cc @@ -17,16 +17,9 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #if !defined(WINNT) && !defined(macintosh) -#ident "$Id: synth.cc,v 1.9 2000/04/16 22:57:34 steve Exp $" +#ident "$Id: synth.cc,v 1.10 2000/05/13 20:55:47 steve Exp $" #endif -/* - * The synth function searches the behavioral description for - * patterns that are known to represent LPM library components. This - * is especially interesting for the sequential components such as - * flip flops and latches. As threads are transformed into components, - * the design is rewritten. - */ # include "functor.h" # include "netlist.h" @@ -102,194 +95,6 @@ int do_expr::event_wait(NetEvWait*stmt) return 0; } -/* - * This transform recognizes the following patterns: - * - * always @(posedge CLK) Q = D - * always @(negedge CLK) Q = D - * - * always @(posedge CLK) if (CE) Q = D; - * always @(negedge CLK) if (CE) Q = D; - * - * These are memory assignments: - * - * always @(posedge CLK) M[a] = D - * always @(negedge CLK) M[a] = D - * - * always @(posedge CLK) if (CE) M[a] = D; - * always @(negedge CLK) if (CE) M[a] = D; - * - * The r-value of the assignments must be identifiers (i.e. wires or - * registers) and the CE must be single-bit identifiers. The generated - * device will be wide enough to accomodate Q and D. - */ -class match_dff : public proc_match_t { - - public: - match_dff(Design*d, NetProcTop*t) - : des_(d), top_(t), wclk_(0), eclk_(0), pclk_(0), con_(0), ce_(0), - asn_(0), asm_(0), d_(0) - { } - - ~match_dff() { } - - void make_it(); - - private: - void make_dff_(); - void make_ram_(); - - virtual int assign(NetAssign*); - virtual int assign_mem(NetAssignMem*); - virtual int condit(NetCondit*); - virtual int event_wait(NetEvWait*); - - Design*des_; - NetProcTop*top_; - - NetEvWait*wclk_; - NetEvent *eclk_; - NetEvProbe*pclk_; - - NetCondit *con_; - NetNet*ce_; - - NetAssign *asn_; - NetAssignMem*asm_; - - NetNet*d_; -}; - -int match_dff::assign(NetAssign*as) -{ - if (!pclk_) - return 0; - if (asn_ || asm_) - return 0; - - asn_ = as; - d_ = asn_->rval()->synthesize(des_); - if (d_ == 0) - return 0; - - return 1; -} - -int match_dff::assign_mem(NetAssignMem*as) -{ - if (!pclk_) - return 0; - if (asn_ || asm_) - return 0; - - asm_ = as; - d_ = asm_->rval()->synthesize(des_); - if (d_ == 0) - return 0; - - return 1; -} - -int match_dff::condit(NetCondit*co) -{ - if (!pclk_) - return 0; - if (con_ || asn_ || asm_) - return 0; - - con_ = co; - if (con_->else_clause()) - return 0; - ce_ = con_->expr()->synthesize(des_); - if (ce_ == 0) - return 0; - - return con_->if_clause()->match_proc(this); -} - -int match_dff::event_wait(NetEvWait*evw) -{ - if (evw->nevents() != 1) - return 0; - - wclk_ = evw; - eclk_ = evw->event(0); - - if (eclk_->nprobe() != 1) - return 0; - - pclk_ = eclk_->probe(0); - - return wclk_->statement()->match_proc(this); -} - -void match_dff::make_it() -{ - if (asn_) { - assert(asm_ == 0); - make_dff_(); - } else { - assert(asm_); - make_ram_(); - } -} - -void match_dff::make_dff_() -{ - NetFF*ff = new NetFF(asn_->name(), asn_->pin_count()); - - for (unsigned idx = 0 ; idx < ff->width() ; idx += 1) { - connect(ff->pin_Data(idx), d_->pin(idx)); - connect(ff->pin_Q(idx), asn_->pin(idx)); - } - - connect(ff->pin_Clock(), pclk_->pin(0)); - if (ce_) connect(ff->pin_Enable(), ce_->pin(0)); - - ff->attribute("LPM_FFType", "DFF"); - if (pclk_->edge() == NetEvProbe::NEGEDGE) - ff->attribute("Clock:LPM_Polarity", "INVERT"); - - des_->add_node(ff); -} - -void match_dff::make_ram_() -{ - NetMemory*mem = asm_->memory(); - NetNet*adr = asm_->index(); - NetRamDq*ram = new NetRamDq(des_->local_symbol(mem->name()), mem, - adr->pin_count()); - for (unsigned idx = 0 ; idx < adr->pin_count() ; idx += 1) - connect(adr->pin(idx), ram->pin_Address(idx)); - - for (unsigned idx = 0 ; idx < ram->width() ; idx += 1) - connect(ram->pin_Data(idx), d_->pin(idx)); - - if (ce_) - connect(ram->pin_WE(), ce_->pin(0)); - - assert(pclk_->edge() == NetEvProbe::POSEDGE); - connect(ram->pin_InClock(), pclk_->pin(0)); - - ram->absorb_partners(); - des_->add_node(ram); -} - -class match_block : public proc_match_t { - - public: - match_block(Design*d, NetProcTop*t) - : des_(d), top_(t) - { } - - ~match_block() { } - - private: - - Design*des_; - NetProcTop*top_; -}; - class synth_f : public functor_t { public: @@ -297,6 +102,7 @@ class synth_f : public functor_t { private: void proc_always_(class Design*); + void proc_initial_(class Design*); NetProcTop*top_; }; @@ -308,35 +114,28 @@ class synth_f : public functor_t { */ void synth_f::process(class Design*des, class NetProcTop*top) { + top_ = top; switch (top->type()) { case NetProcTop::KALWAYS: - top_ = top; proc_always_(des); break; + case NetProcTop::KINITIAL: + proc_initial_(des); + break; } } -/* - * An "always ..." statement has been found. - */ void synth_f::proc_always_(class Design*des) { do_expr expr_pat(des); top_->statement()->match_proc(&expr_pat); - - match_dff dff_pat(des, top_); - if (top_->statement()->match_proc(&dff_pat)) { - dff_pat.make_it(); - des->delete_process(top_); - return; - } - - match_block block_pat(des, top_); - if (top_->statement()->match_proc(&block_pat)) { - cerr << "XXXX: recurse and return here" << endl; - } } +void synth_f::proc_initial_(class Design*des) +{ + do_expr expr_pat(des); + top_->statement()->match_proc(&expr_pat); +} void synth(Design*des) { @@ -346,41 +145,10 @@ void synth(Design*des) /* * $Log: synth.cc,v $ + * Revision 1.10 2000/05/13 20:55:47 steve + * Use yacc based synthesizer. + * * Revision 1.9 2000/04/16 22:57:34 steve * Catch expressions that are part of conditionals. - * - * Revision 1.8 2000/04/12 20:02:53 steve - * Finally remove the NetNEvent and NetPEvent classes, - * Get synthesis working with the NetEvWait class, - * and get started supporting multiple events in a - * wait in vvm. - * - * Revision 1.7 2000/04/02 04:26:07 steve - * Remove the useless sref template. - * - * Revision 1.6 2000/02/23 02:56:55 steve - * Macintosh compilers do not support ident. - * - * Revision 1.5 2000/02/13 04:35:43 steve - * Include some block matching from Larry. - * - * Revision 1.4 1999/12/05 02:24:09 steve - * Synthesize LPM_RAM_DQ for writes into memories. - * - * Revision 1.3 1999/12/01 06:06:16 steve - * Redo synth to use match_proc_t scanner. - * - * Revision 1.2 1999/11/02 04:55:34 steve - * Add the synthesize method to NetExpr to handle - * synthesis of expressions, and use that method - * to improve r-value handling of LPM_FF synthesis. - * - * Modify the XNF target to handle LPM_FF objects. - * - * Revision 1.1 1999/11/01 02:07:41 steve - * Add the synth functor to do generic synthesis - * and add the LPM_FF device to handle rows of - * flip-flops. - * */