From d16dd26660eddb69e9de232c21d4591ad5fdb152 Mon Sep 17 00:00:00 2001 From: steve Date: Wed, 30 May 2007 17:48:53 +0000 Subject: [PATCH] Support Latch synthesis (Alan Feldstein) --- Makefile.in | 7 +- NetLatch.cc | 31 ++++ NetLatch.h | 30 +++ async.cc | 7 +- netlist.h | 34 ++-- sync.cc | 7 +- synth2.cc | 526 ++++++++++++++++++++++++++++++++-------------------- 7 files changed, 412 insertions(+), 230 deletions(-) create mode 100644 NetLatch.cc create mode 100644 NetLatch.h diff --git a/Makefile.in b/Makefile.in index 6226b3316..de6139b08 100644 --- a/Makefile.in +++ b/Makefile.in @@ -16,7 +16,7 @@ # 59 Temple Place - Suite 330 # Boston, MA 02111-1307, USA # -#ident "$Id: Makefile.in,v 1.169.2.6 2007/03/23 23:26:51 steve Exp $" +#ident "$Id: Makefile.in,v 1.169.2.7 2007/05/30 17:48:53 steve Exp $" # # SHELL = /bin/sh @@ -114,7 +114,7 @@ O = main.o async.o design_dump.o dup_expr.o elaborate.o elab_expr.o \ elab_lval.o elab_net.o elab_anet.o elab_pexpr.o elab_scope.o \ elab_sig.o emit.o eval.o eval_attrib.o \ eval_tree.o expr_synth.o functor.o lexor.o lexor_keyword.o link_const.o \ -load_module.o netlist.o netmisc.o net_assign.o \ +load_module.o netlist.o NetLatch.o netmisc.o net_assign.o \ net_design.o net_event.o net_expr.o net_force.o net_func.o \ net_link.o net_modulo.o net_nex_input.o net_nex_output.o \ net_proc.o net_scope.o net_udp.o net_variable.o pad_to_width.o \ @@ -164,7 +164,7 @@ dep: mkdir dep %.o: %.cc - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -MD -c $< -o $*.o + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -MD -c $< -o $*.o $(DEBUGFLAGS) mv $*.d dep/$*.d @@ -231,7 +231,6 @@ $(bindir)/iverilog-vpi: ./iverilog-vpi $(libdir)/ivl/ivl@EXEEXT@: ./ivl@EXEEXT@ $(INSTALL_PROGRAM) ./ivl@EXEEXT@ $(libdir)/ivl/ivl@EXEEXT@ - $(STRIP) $(strip_dynamic) $(libdir)/ivl/ivl@EXEEXT@ $(libdir)/ivl/xnf-s.conf: $(srcdir)/xnf-s.conf $(INSTALL_DATA) $(srcdir)/xnf-s.conf $(libdir)/ivl/xnf-s.conf diff --git a/NetLatch.cc b/NetLatch.cc new file mode 100644 index 000000000..18d508b9b --- /dev/null +++ b/NetLatch.cc @@ -0,0 +1,31 @@ +// NetLatch.cc +// Author: Alan M. Feldstein +// Class NetLatch member-function definitions + +#include "NetLatch.h" // NetLatch class definition + +// constructor +NetLatch::NetLatch( NetScope *scope, perm_string name, unsigned width ) + // explicitly call base-class constructor + : NetNode( scope, name, 2U * width + 1U ) +{ +} // end NetLatch constructor + +Link &NetLatch::pin_Data( unsigned w ) +{ + unsigned pn = 1 + 2 * w; + assert( pn < pin_count() ); + return pin( pn ); +} // end function pin_Data + +Link &NetLatch::pin_Q( unsigned w ) +{ + unsigned pn = 2 + 2 * w; + assert( pn < pin_count() ); + return pin( pn ); +} // end function pin_Q + +Link &NetLatch::pin_Gate() +{ + return pin( 0 ); +} // end function pin_Q diff --git a/NetLatch.h b/NetLatch.h new file mode 100644 index 000000000..62f8f43bd --- /dev/null +++ b/NetLatch.h @@ -0,0 +1,30 @@ +// NetLatch.h +// Author: Alan M. Feldstein +// This class represents an LPM_LATCH device. In Verilog, storage components of this type can be inferred. +// The pinout is assigned like so: +// 0 -- Gate +// +// 1 -- Data[0] +// 2 -- Q[0] +// ... +#ifndef NETLATCH_H +#define NETLATCH_H + +#include "netlist.h" // NetNode, NetScope, Link class definitions +#include "StringHeap.h" // perm_string class definition + +class NetLatch : public NetNode +{ + public: + NetLatch( NetScope *, perm_string, unsigned ); + + Link &pin_Data( unsigned ); + Link &pin_Q( unsigned ); + Link &pin_Gate(); + + const Link &pin_Data( unsigned ) const; + const Link &pin_Q( unsigned ) const; + const Link &pin_Gate() const; +}; // end class NetLatch + +#endif diff --git a/async.cc b/async.cc index 56dfb20eb..39ad039c2 100644 --- a/async.cc +++ b/async.cc @@ -17,7 +17,7 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #ifdef HAVE_CVS_IDENT -#ident "$Id: async.cc,v 1.7.2.1 2006/05/21 21:58:46 steve Exp $" +#ident "$Id: async.cc,v 1.7.2.2 2007/05/30 17:48:53 steve Exp $" #endif # include "config.h" @@ -89,7 +89,7 @@ bool NetProc::is_asynchronous() return false; } -bool NetProcTop::is_asynchronous() +bool NetProcTop::is_asynchronous() const { if (type_ == NetProcTop::KINITIAL) return false; @@ -99,6 +99,9 @@ bool NetProcTop::is_asynchronous() /* * $Log: async.cc,v $ + * Revision 1.7.2.2 2007/05/30 17:48:53 steve + * Support Latch synthesis (Alan Feldstein) + * * Revision 1.7.2.1 2006/05/21 21:58:46 steve * NetESignal input is only selected bits. * diff --git a/netlist.h b/netlist.h index 9beb17bd9..98e4a93d0 100644 --- a/netlist.h +++ b/netlist.h @@ -19,7 +19,7 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #ifdef HAVE_CVS_IDENT -#ident "$Id: netlist.h,v 1.321.2.24 2006/11/26 01:54:05 steve Exp $" +#ident "$Id: netlist.h,v 1.321.2.25 2007/05/30 17:48:53 steve Exp $" #endif /* @@ -97,7 +97,6 @@ struct sync_accounting_cell { */ class NetObj : public Attrib, public virtual LineInfo { - public: public: // The name of the object must be a permallocated string. A // lex_strings string, for example. @@ -1486,7 +1485,7 @@ class NetProc : public virtual LineInfo, public Attrib { virtual bool synth_async(Design*des, NetScope*scope, bool sync_flag, struct sync_accounting_cell*nex_ff, NetNet*nex_map, NetNet*nex_out, - NetNet*accum_in); + NetNet*accum_in, bool latch_inferred = false, NetNet *gsig = 0); // Synthesize synchronous logic, and return true. The nex_out // is where outputs are actually connected, and the nex_map @@ -1631,10 +1630,10 @@ class NetAssignBase : public NetProc { // accounts for any grouping of NetAssign_ objects that might happen. unsigned lwidth() const; - bool synth_async(Design*des, NetScope*scope, bool sync_flag, + virtual bool synth_async(Design*des, NetScope*scope, bool sync_flag, struct sync_accounting_cell*nex_ff, NetNet*nex_map, NetNet*nex_out, - NetNet*accum_in); + NetNet*accum_in, bool latch_inferred = false, NetNet *gsig = 0); bool synth_sync(Design*des, NetScope*scope, struct sync_accounting_cell*nex_ff, NetNet*nex_map, NetNet*nex_out, @@ -1709,9 +1708,9 @@ class NetBlock : public NetProc { // synthesize as asynchronous logic, and return true. - bool synth_async(Design*des, NetScope*scope, bool sync_flag, + virtual bool synth_async(Design*des, NetScope*scope, bool sync_flag, struct sync_accounting_cell*nex_ff, - NetNet*nex_map, NetNet*nex_out, NetNet*accum_in); + NetNet*nex_map, NetNet*nex_out, NetNet*accum_in, bool latch_inferred = false, NetNet *gsig = 0); bool synth_sync(Design*des, NetScope*scope, struct sync_accounting_cell*nex_ff, @@ -1766,9 +1765,9 @@ class NetCase : public NetProc { virtual NexusSet* nex_input(); virtual void nex_output(NexusSet&out); - bool synth_async(Design*des, NetScope*scope, bool sync_flag, + virtual bool synth_async(Design*des, NetScope*scope, bool sync_flag, struct sync_accounting_cell*nex_ff, - NetNet*nex_map, NetNet*nex_out, NetNet*accum_in); + NetNet*nex_map, NetNet*nex_out, NetNet*accum_in, bool latch_inferred = false, NetNet *gsig = 0); virtual bool emit_proc(struct target_t*) const; virtual void dump(ostream&, unsigned ind) const; @@ -1854,9 +1853,9 @@ class NetCondit : public NetProc { virtual void nex_output(NexusSet&o); bool is_asynchronous(); - bool synth_async(Design*des, NetScope*scope, bool sync_flag, + virtual bool synth_async(Design*des, NetScope*scope, bool sync_flag, struct sync_accounting_cell*nex_ff, - NetNet*nex_map, NetNet*nex_out, NetNet*accum); + NetNet*nex_map, NetNet*nex_out, NetNet*accum, bool latch_inferred = false, NetNet *gsig = 0); bool synth_sync(Design*des, NetScope*scope, struct sync_accounting_cell*nex_ff, @@ -2091,7 +2090,7 @@ class NetEvWait : public NetProc { virtual bool synth_async(Design*des, NetScope*scope, bool sync_flag, struct sync_accounting_cell*nex_ff, NetNet*nex_map, NetNet*nex_out, - NetNet*accum_in); + NetNet*accum_in, bool latch_inferred = false, NetNet *gsig = 0); virtual bool synth_sync(Design*des, NetScope*scope, struct sync_accounting_cell*nex_ff, @@ -2493,9 +2492,9 @@ class NetWhile : public NetProc { virtual bool emit_proc(struct target_t*) const; virtual void dump(ostream&, unsigned ind) const; - bool synth_async(Design*des, NetScope*scope, bool sync_flag, + virtual bool synth_async(Design*des, NetScope*scope, bool sync_flag, struct sync_accounting_cell*nex_ff, - NetNet*nex_map, NetNet*nex_out, NetNet*accum_in); + NetNet*nex_map, NetNet*nex_out, NetNet*accum_in, bool latch_inferred = false, NetNet *gsig = 0); private: NetExpr* cond_; @@ -2524,14 +2523,14 @@ class NetProcTop : public LineInfo, public Attrib { const NetScope*scope() const; /* Return true if this process represents combinational logic. */ - bool is_asynchronous(); + bool is_asynchronous() const; /* Create asynchronous logic from this thread and return true, or return false if that cannot be done. */ bool synth_async(Design*des); /* Return true if this process represents synchronous logic. */ - bool is_synchronous(); + bool is_synchronous() const; /* Create synchronous logic from this thread and return true, or return false if that cannot be done. */ @@ -3546,6 +3545,9 @@ extern ostream& operator << (ostream&, NetNet::Type); /* * $Log: netlist.h,v $ + * Revision 1.321.2.25 2007/05/30 17:48:53 steve + * Support Latch synthesis (Alan Feldstein) + * * Revision 1.321.2.24 2006/11/26 01:54:05 steve * Add synthesis of user defined functions. * diff --git a/sync.cc b/sync.cc index 50454a787..2b373451a 100644 --- a/sync.cc +++ b/sync.cc @@ -17,7 +17,7 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #ifdef HAVE_CVS_IDENT -#ident "$Id: sync.cc,v 1.3 2002/09/24 00:58:35 steve Exp $" +#ident "$Id: sync.cc,v 1.3.2.1 2007/05/30 17:48:54 steve Exp $" #endif # include "config.h" @@ -57,7 +57,7 @@ bool NetEvWait::is_synchronous() return true; //statement_->is_asynchronous(); } -bool NetProcTop::is_synchronous() +bool NetProcTop::is_synchronous() const { if (type_ == NetProcTop::KINITIAL) return false; @@ -67,6 +67,9 @@ bool NetProcTop::is_synchronous() /* * $Log: sync.cc,v $ + * Revision 1.3.2.1 2007/05/30 17:48:54 steve + * Support Latch synthesis (Alan Feldstein) + * * Revision 1.3 2002/09/24 00:58:35 steve * More detailed check of process edge events. * diff --git a/synth2.cc b/synth2.cc index 269c7559a..79040730a 100644 --- a/synth2.cc +++ b/synth2.cc @@ -17,7 +17,7 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #ifdef HAVE_CVS_IDENT -#ident "$Id: synth2.cc,v 1.39.2.47 2006/11/11 23:10:20 steve Exp $" +#ident "$Id: synth2.cc,v 1.39.2.48 2007/05/30 17:48:54 steve Exp $" #endif # include "config.h" @@ -25,7 +25,12 @@ # include "functor.h" # include "netlist.h" # include "compiler.h" -# include +#include +#include "NetLatch.h" +#include + +#include // standard operator new +using std::bad_alloc; static int debug_synth2=0; @@ -59,7 +64,7 @@ bool NetProc::synth_async_noaccum(Design*des, NetScope*scope, bool sync_flag, bool NetProc::synth_async(Design*des, NetScope*scope, bool sync_flag, struct sync_accounting_cell*nex_ff, NetNet*nex_map, NetNet*nex_out, - NetNet*accum_in) + NetNet*accum_in, bool latch_inferred, NetNet *gsig) { return false; } @@ -138,7 +143,7 @@ static int map_nexus_in_index(struct nexus_map_t*table, size_t ntable, bool NetAssignBase::synth_async(Design*des, NetScope*scope, bool sync_flag, struct sync_accounting_cell*nex_ff, NetNet*nex_map, NetNet*nex_out, - NetNet*accum_in) + NetNet*accum_in, bool latch_inferred, NetNet *gsig) { NetNet*rsig = rval_->synthesize(des); if (rsig == 0) { @@ -180,6 +185,12 @@ bool NetAssignBase::synth_async(Design*des, NetScope*scope, bool sync_flag, continue; } + if (cur->bmux() && !sync_flag) { + cerr << get_line() << ": error: Assign to bit select " + << "Not possible in asynchronous logic." << endl; + des->errors += 1; + return false; + } NetNet*lsig = cur->sig(); if (!lsig) { @@ -191,25 +202,64 @@ bool NetAssignBase::synth_async(Design*des, NetScope*scope, bool sync_flag, return false; } - if (cur->bmux() && !sync_flag) { - cerr << get_line() << ": error: Assign to bit select " - << "Not possible in asynchronous logic." << endl; - des->errors += 1; - return false; - } + if ( latch_inferred ) + { + // By this point any bmux() has been dealt with. Panic if that is not so. + assert( !cur->bmux() ); - /* Handle the special case that this is a decoded - enable. generate a demux for the device, with the - WriteData connected to the r-value and the Data - vector connected to the feedback. */ - if (cur->bmux() != 0) { + // Bind the outputs that we do make to the nex_out. Use the nex_map to map the l-value bit position to the nex_out bit position. + + struct nexus_map_t *nex_map_idx = make_nexus_index( nex_map ); + + assert( cur->sig()->msb() - cur->sig()->lsb() >= 0L && + static_cast< unsigned long >( cur->sig()->msb() - cur->sig()->lsb() ) <= static_cast< unsigned long >( UINT_MAX ) - 1UL ); + + try + { + NetLatch *const latchPtr = new NetLatch( scope, cur->sig()->name(), static_cast< unsigned >( cur->sig()->msb() - cur->sig()->lsb() ) + 1U ); + latchPtr->set_line( *this ); + + for ( unsigned idx = 0U; idx < cur->lwidth(); idx += 1U ) + { + unsigned off = cur->get_loff() + idx; + int tmp = map_nexus_in_index( nex_map_idx, nex_map->pin_count(), lsig->pin( off ).nexus() ); + assert( tmp >= 0 ); + unsigned ptr = static_cast< unsigned >( tmp ); + connect( latchPtr->pin_Data( idx ), rsig->pin( roff + idx ) ); + connect( nex_out->pin( ptr ), latchPtr->pin_Q( idx ) ); + connect( latchPtr->pin_Gate(), gsig->pin( 0 ) ); + } + + des->add_node( latchPtr ); + delete latchPtr; + } + + catch ( bad_alloc &memoryAllocationException ) + { + cerr << "Exception occurred: " << memoryAllocationException.what() << endl; + cerr << get_line() << ": error: NetAssignBase::synth_async on failure to create latch at lval "; + dump_lval( cerr ); + cerr << endl; + des->errors += 1; + return false; + } + + } + else + { + + /* Handle the special case that this is a decoded + enable. generate a demux for the device, with the + WriteData connected to the r-value and the Data + vector connected to the feedback. */ + if (cur->bmux() != 0) { assert(sync_flag); NetNet*adr = cur->bmux()->synthesize(des); - /* Create a NetEemux wide enough to connect to all - the bits of the lvalue signal (generally more - then the bits of lwidth). */ + /* Create a NetEemux wide enough to connect to all + the bits of the lvalue signal (generally more + then the bits of lwidth). */ NetDemux*dq = new NetDemux(scope, scope->local_symbol(), lsig->pin_count(), adr->pin_count(), @@ -217,56 +267,56 @@ bool NetAssignBase::synth_async(Design*des, NetScope*scope, bool sync_flag, des->add_node(dq); dq->set_line(*this); - /* The bmux expression connects to the address of - the Demux device. */ + /* The bmux expression connects to the address of + the Demux device. */ for (unsigned idx = 0; idx < adr->pin_count() ; idx += 1) - connect(dq->pin_Address(idx), adr->pin(idx)); + connect(dq->pin_Address(idx), adr->pin(idx)); assert(cur->lwidth() == 1); - /* Cycle the associated FF Data and Q through the - demux to make synchronous "latches" that the - Demux modifies. */ + /* Cycle the associated FF Data and Q through the + demux to make synchronous "latches" that the + Demux modifies. */ assert(nex_ff[0].ff->width() >= lsig->pin_count()); for (unsigned idx = 0; idx < lsig->pin_count(); idx += 1) { - unsigned off = cur->get_loff()+idx; - connect(nex_ff[0].ff->pin_Q(off), dq->pin_Data(idx)); + unsigned off = cur->get_loff()+idx; + connect(nex_ff[0].ff->pin_Q(off), dq->pin_Data(idx)); } struct nexus_map_t*nex_map_idx = make_nexus_index(nex_map); for (unsigned idx = 0; idx < lsig->pin_count(); idx += 1) { - unsigned off = cur->get_loff()+idx; - int tmp = map_nexus_in_index(nex_map_idx, - nex_map->pin_count(), - lsig->pin(off).nexus()); - assert(tmp >= 0); - unsigned ptr = tmp; - connect(nex_out->pin(ptr), dq->pin_Q(idx)); + unsigned off = cur->get_loff()+idx; + int tmp = map_nexus_in_index(nex_map_idx, + nex_map->pin_count(), + lsig->pin(off).nexus()); + assert(tmp >= 0); + unsigned ptr = tmp; + connect(nex_out->pin(ptr), dq->pin_Q(idx)); } delete[]nex_map_idx; - /* The r-value (1 bit) connects to the WriteData - input of the demux. */ + /* The r-value (1 bit) connects to the WriteData + input of the demux. */ connect(dq->pin_WriteData(0), rsig->pin(roff)); roff += cur->lwidth(); cur->turn_sig_to_wire_on_release(); continue; - } + } - /* By this point ant bmux() has been dealt with. Panic - if that is not so. */ - assert(! cur->bmux()); + /* By this point ant bmux() has been dealt with. Panic + if that is not so. */ + assert(! cur->bmux()); - /* Bind the outputs that we do make to the nex_out. Use the - nex_map to map the l-value bit position to the nex_out bit - position. */ + /* Bind the outputs that we do make to the nex_out. Use the + nex_map to map the l-value bit position to the nex_out bit + position. */ - struct nexus_map_t*nex_map_idx = make_nexus_index(nex_map); + struct nexus_map_t*nex_map_idx = make_nexus_index(nex_map); - for (unsigned idx = 0 ; idx < cur->lwidth() ; idx += 1) { + for (unsigned idx = 0 ; idx < cur->lwidth() ; idx += 1) { unsigned off = cur->get_loff()+idx; int tmp = map_nexus_in_index(nex_map_idx, nex_map->pin_count(), @@ -274,15 +324,17 @@ bool NetAssignBase::synth_async(Design*des, NetScope*scope, bool sync_flag, assert(tmp >= 0); unsigned ptr = tmp; connect(nex_out->pin(ptr), rsig->pin(roff+idx)); - } + } + + } roff += cur->lwidth(); - /* This lval_ represents a reg that is a WIRE in the - synthesized results. This function signals the destructor - to change the REG that this l-value refers to into a - WIRE. It is done then, at the last minute, so that pending - synthesis can continue to work with it as a WIRE. */ + /* This lval_ represents a reg that is a WIRE in the + synthesized results. This function signals the destructor + to change the REG that this l-value refers to into a + WIRE. It is done then, at the last minute, so that pending + synthesis can continue to work with it as a WIRE. */ cur->turn_sig_to_wire_on_release(); } @@ -397,7 +449,7 @@ bool NetAssignBase::synth_async_mem_sync_(Design*des, NetScope*scope, */ bool NetBlock::synth_async(Design*des, NetScope*scope, bool sync_flag, struct sync_accounting_cell*nex_ff, - NetNet*nex_map, NetNet*nex_out, NetNet*accum_in) + NetNet*nex_map, NetNet*nex_out, NetNet*accum_in, bool latch_inferred, NetNet *gsig) { if (last_ == 0) { return true; @@ -568,7 +620,7 @@ bool NetBlock::synth_async(Design*des, NetScope*scope, bool sync_flag, bool NetCase::synth_async(Design*des, NetScope*scope, bool sync_flag, struct sync_accounting_cell*nex_ff, - NetNet*nex_map, NetNet*nex_out, NetNet*accum) + NetNet*nex_map, NetNet*nex_out, NetNet*accum, bool latch_inferred, NetNet *gsig) { unsigned cur; @@ -1036,7 +1088,7 @@ bool NetCase::synth_async_1hot_(Design*des, NetScope*scope, bool sync_flag, bool NetCondit::synth_async(Design*des, NetScope*scope, bool sync_flag, struct sync_accounting_cell*nex_ff, NetNet*nex_map, NetNet*nex_out, - NetNet*accum) + NetNet*accum, bool latch_inferred, NetNet *gsig) { /* Detect the special case that this is a nul-effect (for synthesis) statement. This happens, for example, for code @@ -1074,49 +1126,63 @@ bool NetCondit::synth_async(Design*des, NetScope*scope, bool sync_flag, // At least one of the clauses must have contents. */ assert(if_ != 0 || else_ != 0); + // This is the beginning of where it will be decided whether or not a latch is inferred. + // We prefer that latch_inferred be a local variable rather than a function parameter. + // It must be a function parameter to match the signature of the function that we are overriding in the base class, the result of a chain reaction caused by adding that + // parameter to NetAssignBase::synth_async (to give that function awareness of what we are recognizing here). + // Of course, the parameter and the local variable could have been given distinct names. + // Instead, we simply assert that the following code governs. + // In other words, the parameter value coming into this function does not override the value (false) that would be the initial value of a local variable here. + // Probably the incoming parameter got this value from the default argument, but it is not essential to prove that. + assert( latch_inferred == false ); + /* If there is no default_sig, and if this is a fully asynchronous process (nex_map is not a synchronous output) - then both clases *must* be present. + then, if !(both clauses are present), a latch is inferred. If either clause is missing, and the output is synchronous, then the code below can take as the input the output from the DFF without worry for asynchronous cycles. */ if (default_sig == 0 && ! sync_flag) { if (if_ == 0) { - cerr << get_line() << ": error: Asynchronous if statement" - << " is missing the if clause." << endl; - des->errors += 1; - return false; + latch_inferred = true; } if (else_ == 0) { - cerr << get_line() << ": error: Asynchronous if statement" - << " is missing the else clause." << endl; - des->errors += 1; - return false; + latch_inferred = true; } } NetNet*asig = new NetNet(scope, scope->local_symbol(), NetNet::WIRE, nex_map->pin_count()); + bool asigIsLatchOutput = false; asig->local_flag(true); if (if_ == 0) { /* If the if clause is missing, then take the clause to be an assignment from the defaults input. If there is - no defaults input, then take the input to be from the + no defaults input and a latch is not inferred, then take the input to be from the output. */ if (default_sig) { for (unsigned idx = 0 ; idx < asig->pin_count() ; idx += 1) connect(asig->pin(idx), default_sig->pin(idx)); } else { + if ( latch_inferred ) + { + delete asig ; + } + else + { + assert( sync_flag ); + for (unsigned idx = 0 ; idx < asig->pin_count() ; idx += 1) connect(asig->pin(idx), nex_map->pin(idx)); + } } } else { bool flag = if_->synth_async(des, scope, sync_flag, nex_ff, - nex_map, asig, accum); + nex_map, asig, accum, latch_inferred, ssig); if (!flag) { delete asig; cerr << get_line() << ": error: Asynchronous if statement" @@ -1124,10 +1190,12 @@ bool NetCondit::synth_async(Design*des, NetScope*scope, bool sync_flag, des->errors += 1; return false; } + asigIsLatchOutput = latch_inferred; } NetNet*bsig = new NetNet(scope, scope->local_symbol(), NetNet::WIRE, nex_map->pin_count()); + bool bsigIsLatchOutput = false; bsig->local_flag(true); if (else_ == 0) { @@ -1135,12 +1203,21 @@ bool NetCondit::synth_async(Design*des, NetScope*scope, bool sync_flag, for (unsigned idx = 0 ; idx < asig->pin_count() ; idx += 1) connect(bsig->pin(idx), default_sig->pin(idx)); } else { + if ( latch_inferred ) + { + delete bsig; + } + else + { + assert( sync_flag ); + for (unsigned idx = 0 ; idx < asig->pin_count() ; idx += 1) connect(bsig->pin(idx), nex_map->pin(idx)); + } } } else { bool flag = else_->synth_async(des, scope, sync_flag, nex_ff, - nex_map, bsig, accum); + nex_map, bsig, accum, latch_inferred, ssig); if (!flag) { delete asig; delete bsig; @@ -1149,183 +1226,217 @@ bool NetCondit::synth_async(Design*des, NetScope*scope, bool sync_flag, des->errors += 1; return false; } + bsigIsLatchOutput = latch_inferred; } - unsigned mux_width = 0; + if ( latch_inferred ) + { + // The value of a pointer that refers to deallocated storage is indeterminate, so I can't examine asig and bsig to determine which one hasn't been deallocated. + assert ( ( asigIsLatchOutput || bsigIsLatchOutput ) && !( asigIsLatchOutput && bsigIsLatchOutput ) ); // logical exclusive OR + + if ( asigIsLatchOutput ) + { + asig->set_line( *this ); + + for ( unsigned idx = 0U; idx < nex_out->pin_count(); idx += 1U ) + { + connect( nex_out->pin( idx ), asig->pin( idx ) ); + } + + } + else // bsigIsLatchOutput + { + bsig->set_line( *this ); + + for ( unsigned idx = 0U; idx < nex_out->pin_count(); idx += 1U ) + { + connect( nex_out->pin( idx ), bsig->pin( idx ) ); + } - /* Figure out how many mux bits we are going to need. */ - for (unsigned idx = 0 ; idx < nex_out->pin_count(); idx += 1) { - int flag = 0; - if (asig->pin(idx).is_linked()) - flag |= 0100; - if (bsig->pin(idx).is_linked()) - flag |= 0010; - if (accum->pin(idx).is_linked()) - flag |= 0001; - switch (flag) { - case 0111: - case 0110: - case 0101: - mux_width += 1; - break; - case 0100: - if (sync_flag) - mux_width += 1; - break; - case 0011: - mux_width += 1; - break; - case 0010: - if (sync_flag) - mux_width += 1; - break; - case 0001: - mux_width += 1; - break; - case 0000: - break; } - } + } // end if latch_inferred + else + { + unsigned mux_width = 0; - /* Create a mux and hook it up. */ - NetMux*mux = new NetMux(scope, scope->local_symbol(), - mux_width, 2, 1); - mux->set_line(*this); + /* Figure out how many mux bits we are going to need. */ + for (unsigned idx = 0 ; idx < nex_out->pin_count(); idx += 1) { + int flag = 0; + if (asig->pin(idx).is_linked()) + flag |= 0100; + if (bsig->pin(idx).is_linked()) + flag |= 0010; + if (accum->pin(idx).is_linked()) + flag |= 0001; + switch (flag) { + case 0111: + case 0110: + case 0101: + mux_width += 1; + break; + case 0100: + if (sync_flag) + mux_width += 1; + break; + case 0011: + mux_width += 1; + break; + case 0010: + if (sync_flag) + mux_width += 1; + break; + case 0001: + mux_width += 1; + break; + case 0000: + break; + } - NetNet*mux_sig = new NetNet(scope, scope->local_symbol(), - NetNet::WIRE, mux_width); - mux_sig->local_flag(true); - mux_sig->set_line(*this); + } - for (unsigned idx = 0 ; idx < mux_width ; idx += 1) + /* Create a mux and hook it up. */ + NetMux*mux = new NetMux(scope, scope->local_symbol(), + mux_width, 2, 1); + mux->set_line(*this); + NetNet*mux_sig = new NetNet(scope, scope->local_symbol(), + NetNet::WIRE, mux_width); + mux_sig->local_flag(true); + mux_sig->set_line(*this); + + for (unsigned idx = 0 ; idx < mux_width ; idx += 1) connect(mux->pin_Result(idx), mux_sig->pin(idx)); - if (debug_synth) { + if (debug_synth) { cerr << get_line() << ": debug: Condit synth to MUX " << " width=" << mux_width << " sel_width=1" << endl; - } + } - connect(mux->pin_Sel(0), ssig->pin(0)); + connect(mux->pin_Sel(0), ssig->pin(0)); - /* Connected the clauses to the data inputs of the - condition. If there are bits unassigned by the case, then - bind them from the accum bits instead. If the bit is not - represented in the accum list, but this is a synchronous - output, then get the bit from the nex_map, which is the - output held in the DFF. */ - mux_width = 0; - for (unsigned idx = 0 ; idx < nex_out->pin_count() ; idx += 1) { + /* Connected the clauses to the data inputs of the + condition. If there are bits unassigned by the case, then + bind them from the accum bits instead. If the bit is not + represented in the accum list, but this is a synchronous + output, then get the bit from the nex_map, which is the + output held in the DFF. */ + mux_width = 0; + for (unsigned idx = 0 ; idx < nex_out->pin_count() ; idx += 1) { int flag = 0; if (asig->pin(idx).is_linked()) - flag |= 0100; + flag |= 0100; if (bsig->pin(idx).is_linked()) - flag |= 0010; + flag |= 0010; if (accum->pin(idx).is_linked()) - flag |= 0001; + flag |= 0001; switch (flag) { - case 0111: - case 0110: - connect(mux->pin_Data(mux_width, 1), asig->pin(idx)); - connect(mux->pin_Data(mux_width, 0), bsig->pin(idx)); - connect(nex_out->pin(idx), mux->pin_Result(mux_width)); - mux_width += 1; - break; - case 0101: - connect(mux->pin_Data(mux_width, 1), asig->pin(idx)); - connect(mux->pin_Data(mux_width, 0), accum->pin(idx)); - connect(nex_out->pin(idx), mux->pin_Result(mux_width)); - mux_width += 1; - break; - case 0100: - if (sync_flag) { - connect(mux->pin_Data(mux_width, 1), asig->pin(idx)); - connect(mux->pin_Data(mux_width, 0),nex_map->pin(idx)); - connect(nex_out->pin(idx), mux->pin_Result(mux_width)); - mux_width += 1; - } else { + case 0111: + case 0110: + connect(mux->pin_Data(mux_width, 1), asig->pin(idx)); + connect(mux->pin_Data(mux_width, 0), bsig->pin(idx)); + connect(nex_out->pin(idx), mux->pin_Result(mux_width)); + mux_width += 1; + break; + case 0101: + connect(mux->pin_Data(mux_width, 1), asig->pin(idx)); + connect(mux->pin_Data(mux_width, 0), accum->pin(idx)); + connect(nex_out->pin(idx), mux->pin_Result(mux_width)); + mux_width += 1; + break; + case 0100: + if (sync_flag) { + connect(mux->pin_Data(mux_width, 1), asig->pin(idx)); + connect(mux->pin_Data(mux_width, 0),nex_map->pin(idx)); + connect(nex_out->pin(idx), mux->pin_Result(mux_width)); + mux_width += 1; + } else { #if 0 - cerr << get_line() - << ": error: Condition false clause " - << "does not assign expected outputs." << endl; - des->errors += 1; - return_flag = false; + cerr << get_line() + << ": error: Condition false clause " + << "does not assign expected outputs." << endl; + des->errors += 1; + return_flag = false; #else - /* This should check that bsig is latched by - the condition select or is used - internally by the false clause. but since - there is no latch support, assume it is - used internally. */ - connect(nex_out->pin(idx), asig->pin(idx)); + /* This should check that asig is latched by + the condition select or is used + internally by the false clause. but since + there is no latch support, assume it is + used internally. */ + // Now there is latch support, but if a latch is inferred this code will not be reached. + // Note, however, the similarity to the latch inferred code. + connect(nex_out->pin(idx), asig->pin(idx)); #endif - } - break; - case 0011: - connect(mux->pin_Data(mux_width, 1), accum->pin(idx)); - connect(mux->pin_Data(mux_width, 0), bsig->pin(idx)); - connect(nex_out->pin(idx), mux->pin_Result(mux_width)); - mux_width += 1; - break; - case 0010: - if (sync_flag) { - connect(mux->pin_Data(mux_width, 1),nex_map->pin(idx)); - connect(mux->pin_Data(mux_width, 0), bsig->pin(idx)); - connect(nex_out->pin(idx), mux->pin_Result(mux_width)); - mux_width += 1; - } else { + } + break; + case 0011: + connect(mux->pin_Data(mux_width, 1), accum->pin(idx)); + connect(mux->pin_Data(mux_width, 0), bsig->pin(idx)); + connect(nex_out->pin(idx), mux->pin_Result(mux_width)); + mux_width += 1; + break; + case 0010: + if (sync_flag) { + connect(mux->pin_Data(mux_width, 1),nex_map->pin(idx)); + connect(mux->pin_Data(mux_width, 0), bsig->pin(idx)); + connect(nex_out->pin(idx), mux->pin_Result(mux_width)); + mux_width += 1; + } else { #if 0 - cerr << get_line() - << ": error: Condition true clause " - << "does not assign expected outputs." << endl; - des->errors += 1; - return_flag = false; + cerr << get_line() + << ": error: Condition true clause " + << "does not assign expected outputs." << endl; + des->errors += 1; + return_flag = false; #else - /* This should check that bsig is latched by - the condition select or is used - internally by the false clause. but since - there is no latch support, assume it is - used internally. */ - connect(nex_out->pin(idx), bsig->pin(idx)); + /* This should check that bsig is latched by + the condition select or is used + internally by the false clause. but since + there is no latch support, assume it is + used internally. */ + // Now there is latch support, but if a latch is inferred this code will not be reached. + // Note, however, the similarity to the latch inferred code. + connect(nex_out->pin(idx), bsig->pin(idx)); #endif - } - break; - case 0001: - connect(mux->pin_Data(mux_width, 1), accum->pin(idx)); - connect(mux->pin_Data(mux_width, 0), accum->pin(idx)); - connect(nex_out->pin(idx), mux->pin_Result(mux_width)); - mux_width += 1; - break; - case 0000: - if (sync_flag) { - connect(nex_out->pin(idx), nex_map->pin(idx)); - } else { - cerr << get_line() << ": internal error: " - << "Unexplained mode?" << endl; - cerr << get_line() << ": XXXX: " - << "nex_out->pin_count() = " - << nex_out->pin_count() << endl; - assert(0); - } - break; - default: - assert(0); - break; + } + break; + case 0001: + connect(mux->pin_Data(mux_width, 1), accum->pin(idx)); + connect(mux->pin_Data(mux_width, 0), accum->pin(idx)); + connect(nex_out->pin(idx), mux->pin_Result(mux_width)); + mux_width += 1; + break; + case 0000: + if (sync_flag) { + connect(nex_out->pin(idx), nex_map->pin(idx)); + } else { + cerr << get_line() << ": internal error: " + << "Unexplained mode?" << endl; + cerr << get_line() << ": XXXX: " + << "nex_out->pin_count() = " + << nex_out->pin_count() << endl; + assert(0); + } + break; + default: + assert(0); + break; } - } + } - assert(mux_width == mux->width()); + assert(mux_width == mux->width()); - des->add_node(mux); + des->add_node(mux); + } // end if !latch_inferred return true; } bool NetEvWait::synth_async(Design*des, NetScope*scope, bool sync_flag, sync_accounting_cell*nex_ff, - NetNet*nex_map, NetNet*nex_out, NetNet*accum_in) + NetNet*nex_map, NetNet*nex_out, NetNet*accum_in, bool latch_inferred, NetNet *gsig) { bool flag = statement_->synth_async(des, scope, sync_flag, nex_ff, nex_map, nex_out, accum_in); @@ -2404,7 +2515,7 @@ bool NetEvWait::synth_sync(Design*des, NetScope*scope, bool NetWhile::synth_async(Design*des, NetScope*scope, bool sync_flag, struct sync_accounting_cell*nex_ff, - NetNet*nex_map, NetNet*nex_out, NetNet*accum_in) + NetNet*nex_map, NetNet*nex_out, NetNet*accum_in, bool latch_inferred, NetNet *gsig) { cerr << get_line() << ": error: Cannot synthesize for or while loops." @@ -2557,6 +2668,9 @@ void synth2(Design*des) /* * $Log: synth2.cc,v $ + * Revision 1.39.2.48 2007/05/30 17:48:54 steve + * Support Latch synthesis (Alan Feldstein) + * * Revision 1.39.2.47 2006/11/11 23:10:20 steve * Fix async blocks to take accumulated input. *