/* * Copyright (c) 2002 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 */ #ifdef HAVE_CVS_IDENT #ident "$Id: synth2.cc,v 1.16 2002/10/21 01:42:09 steve Exp $" #endif # include "config.h" # include "functor.h" # include "netlist.h" # include "compiler.h" # include bool NetProc::synth_async(Design*des, NetScope*scope, const NetNet*nex_map, NetNet*nex_out) { return false; } bool NetProc::synth_sync(Design*des, NetScope*scope, NetFF*ff, const NetNet*nex_map, NetNet*nex_out, const svector&events) { if (events.count() > 0) { cerr << get_line() << ": error: Events are unaccounted" << " for in process synthesis." << endl; des->errors += 1; } /* Synthesize the input to the DFF. */ return synth_async(des, scope, nex_map, nex_out); } static unsigned find_nexus_in_set(const NetNet*nset, const Nexus*nex) { unsigned idx = 0; for (idx = 0 ; idx < nset->pin_count() ; idx += 1) if (nset->pin(idx).nexus() == nex) return idx; return idx; } /* * Async synthesis of assignments is done by synthesizing the rvalue * expression, then connecting the l-value directly to the output of * the r-value. * * The nex_map is the O-set for the statement, and lists the positions * of the outputs as the caller wants results linked up. The nex_out, * however, is the set of nexa that are to actually get linked to the * r-value. */ bool NetAssignBase::synth_async(Design*des, NetScope*scope, const NetNet*nex_map, NetNet*nex_out) { NetNet*rsig = rval_->synthesize(des); assert(rsig); NetNet*lsig = lval_->sig(); assert(lsig); assert(lval_->more == 0); assert(lval_->lwidth() == nex_map->pin_count()); assert(nex_map->pin_count() <= rsig->pin_count()); for (unsigned idx = 0 ; idx < lval_->lwidth() ; idx += 1) { unsigned off = lval_->get_loff()+idx; unsigned ptr = find_nexus_in_set(nex_map, lsig->pin(off).nexus()); connect(nex_out->pin(ptr), rsig->pin(idx)); } return true; } /* * Sequential blocks are translated to asynchronous logic by * translating each statement of the block, in order, into gates. The * nex_out for the block is the union of the nex_out for all the * substatements. */ bool NetBlock::synth_async(Design*des, NetScope*scope, const NetNet*nex_map, NetNet*nex_out) { if (last_ == 0) return true; bool flag = true; NetProc*cur = last_; do { cur = cur->next_; /* Create a temporary nex_map for the substatement. */ NexusSet tmp_set; cur->nex_output(tmp_set); NetNet*tmp_map = new NetNet(scope, "tmp1", NetNet::WIRE, tmp_set.count()); for (unsigned idx = 0 ; idx < tmp_map->pin_count() ; idx += 1) connect(tmp_set[idx], tmp_map->pin(idx)); /* Create also a temporary net_out to collect the output. */ NetNet*tmp_out = new NetNet(scope, "tmp2", NetNet::WIRE, tmp_set.count()); bool ok_flag = cur->synth_async(des, scope, tmp_map, tmp_out); flag = flag && ok_flag; if (ok_flag == false) continue; /* Use the nex_map to link up the output from the substatement to the output of the block as a whole. */ for (unsigned idx = 0 ; idx < tmp_out->pin_count() ; idx += 1) { unsigned ptr = find_nexus_in_set(nex_map, tmp_set[idx]); connect(nex_out->pin(ptr), tmp_out->pin(idx)); } delete tmp_map; delete tmp_out; } while (cur != last_); return flag; } bool NetCase::synth_async(Design*des, NetScope*scope, const NetNet*nex_map, NetNet*nex_out) { unsigned cur; NetNet*esig = expr_->synthesize(des); /* Scan the select vector looking for constant bits. The constant bits will be elided from the select input connect, but we still need to keep track of them. */ unsigned sel_pins = 0; unsigned long sel_mask = 0; unsigned long sel_ref = 0; for (unsigned idx = 0 ; idx < esig->pin_count() ; idx += 1) { if (esig->pin(idx).nexus()->drivers_constant()) { verinum::V bit = esig->pin(idx).nexus()->driven_value(); if (bit == verinum::V1) sel_ref |= 1 << idx; } else { sel_pins += 1; sel_mask |= 1 << idx; } } /* Build a map of guard values to mux select values. This helps account for constant select bits that are being elided. */ mapguard2sel; cur = 0; for (unsigned idx = 0 ; idx < (1<pin_count()) ; idx += 1) { if ((idx & ~sel_mask) == sel_ref) { guard2sel[idx] = cur; cur += 1; } } assert(cur == (1 << sel_pins)); NetMux*mux = new NetMux(scope, scope->local_hsymbol(), nex_out->pin_count(), 1 << sel_pins, sel_pins); /* Connect the non-constant select bits to the select input of the mux device. */ cur = 0; for (unsigned idx = 0 ; idx < esig->pin_count() ; idx += 1) { /* skip bits that are known to be constant. */ if ((sel_mask & (1 << idx)) == 0) continue; connect(mux->pin_Sel(cur), esig->pin(idx)); cur += 1; } assert(cur == sel_pins); for (unsigned idx = 0 ; idx < mux->width() ; idx += 1) connect(nex_out->pin(idx), mux->pin_Result(idx)); for (unsigned item = 0 ; item < nitems_ ; item += 1) { assert(items_[item].guard); assert(items_[item].statement); NetEConst*ge = dynamic_cast(items_[item].guard); assert(ge); verinum gval = ge->value(); unsigned sel_idx = guard2sel[gval.as_ulong()]; NetNet*sig = new NetNet(scope, scope->local_hsymbol(), NetNet::WIRE, nex_map->pin_count()); sig->local_flag(true); items_[item].statement->synth_async(des, scope, nex_map, sig); for (unsigned idx = 0 ; idx < mux->width() ; idx += 1) connect(mux->pin_Data(idx, sel_idx), sig->pin(idx)); } des->add_node(mux); return true; } bool NetCondit::synth_async(Design*des, NetScope*scope, const NetNet*nex_map, NetNet*nex_out) { NetNet*ssig = expr_->synthesize(des); assert(ssig); if (if_ == 0) return false; if (else_ == 0) { cerr << get_line() << ": error: Asynchronous if statement" << " is missing the else clause." << endl; return false; } assert(if_ != 0); assert(else_ != 0); NetNet*asig = new NetNet(scope, scope->local_hsymbol(), NetNet::WIRE, nex_map->pin_count()); asig->local_flag(true); if_->synth_async(des, scope, nex_map, asig); NetNet*bsig = new NetNet(scope, scope->local_hsymbol(), NetNet::WIRE, nex_map->pin_count()); bsig->local_flag(true); else_->synth_async(des, scope, nex_map, bsig); NetMux*mux = new NetMux(scope, scope->local_hsymbol(), nex_out->pin_count(), 2, 1); connect(mux->pin_Sel(0), ssig->pin(0)); for (unsigned idx = 0 ; idx < asig->pin_count() ; idx += 1) connect(mux->pin_Data(idx, 1), asig->pin(idx)); for (unsigned idx = 0 ; idx < bsig->pin_count() ; idx += 1) connect(mux->pin_Data(idx, 0), bsig->pin(idx)); for (unsigned idx = 0 ; idx < mux->width() ; idx += 1) connect(nex_out->pin(idx), mux->pin_Result(idx)); des->add_node(mux); return true; } bool NetEvWait::synth_async(Design*des, NetScope*scope, const NetNet*nex_map, NetNet*nex_out) { return statement_->synth_async(des, scope, nex_map, nex_out); } bool NetProcTop::synth_async(Design*des) { NexusSet nex_set; statement_->nex_output(nex_set); NetNet*nex_out = new NetNet(scope(), "tmp", NetNet::WIRE, nex_set.count()); for (unsigned idx = 0 ; idx < nex_out->pin_count() ; idx += 1) connect(nex_set[idx], nex_out->pin(idx)); bool flag = statement_->synth_async(des, scope(), nex_out, nex_out); delete nex_out; return flag; } /* * This method is called when a block is encountered near the surface * of a synchronous always statement. For example, this code will be * invoked for input like this: * * always @(posedge clk...) begin * * * ... * end * * This needs to be split into a DFF bank for each statement, because * the statements may each infer different reset and enable signals. */ bool NetBlock::synth_sync(Design*des, NetScope*scope, NetFF*ff, const NetNet*nex_map, NetNet*nex_out, const svector&events_in) { if (last_ == 0) return true; bool flag = true; NetProc*cur = last_; unsigned offset = 0; do { cur = cur->next_; /* Create a temporary nex_map for the substatement. */ NexusSet tmp_set; cur->nex_output(tmp_set); NetNet*tmp_map = new NetNet(scope, "tmp1", NetNet::WIRE, tmp_set.count()); for (unsigned idx = 0 ; idx < tmp_map->pin_count() ; idx += 1) connect(tmp_set[idx], tmp_map->pin(idx)); /* Create also a temporary net_out to collect the output. */ NetNet*tmp_out = new NetNet(scope, "tmp2", NetNet::WIRE, tmp_set.count()); /* Create a new DFF to handle this part of the begin-end block. Connect this NetFF to the associated pins of the existing wide NetFF device. */ NetFF*ff2 = new NetFF(scope, scope->local_hsymbol().c_str(), tmp_out->pin_count()); des->add_node(ff2); for (unsigned idx = 0 ; idx < ff2->width() ; idx += 1) { connect(ff->pin_Data(idx+offset), ff2->pin_Data(idx)); connect(ff->pin_Q(idx+offset), ff2->pin_Q(idx)); } if (ff->pin_Aclr().is_linked()) connect(ff->pin_Aclr(), ff2->pin_Aclr()); if (ff->pin_Aset().is_linked()) connect(ff->pin_Aset(), ff2->pin_Aset()); if (ff->pin_Clock().is_linked()) connect(ff->pin_Clock(), ff2->pin_Clock()); if (ff->pin_Enable().is_linked()) connect(ff->pin_Enable(),ff2->pin_Enable()); /* Now go on with the synchronous synthesis for this subset of the statement. */ bool ok_flag = cur->synth_sync(des, scope, ff2, tmp_map, tmp_out, events_in); flag = flag && ok_flag; if (ok_flag == false) continue; /* Use the nex_map to link up the output from the substatement to the output of the block as a whole. */ for (unsigned idx = 0 ; idx < tmp_out->pin_count() ; idx += 1) { unsigned ptr = find_nexus_in_set(nex_map, tmp_set[idx]); connect(nex_out->pin(ptr), tmp_out->pin(idx)); } delete tmp_map; delete tmp_out; offset += ff2->width(); } while (cur != last_); /* Done. The large NetFF is no longer needed, as it has been taken up by the smaller NetFF devices. */ assert(offset == ff->width()); delete ff; return flag; } /* * This method handles the case where I find a conditional near the * surface of a synchronous thread. This conditional can be a CE or an * asynchronous set/reset, depending on whether the pin of the * expression is connected to an event, or not. */ bool NetCondit::synth_sync(Design*des, NetScope*scope, NetFF*ff, const NetNet*nex_map, NetNet*nex_out, const svector&events_in) { /* Synthesize the enable expression. */ NetNet*ce = expr_->synthesize(des); assert(ce->pin_count() == 1); /* Try first to turn the ce into an asynchronous set/reset input. If the ce is linked to a probe, then that probe is a set/reset input. */ for (unsigned idx = 0 ; idx < events_in.count() ; idx += 1) { NetEvProbe*ev = events_in[idx]; if (connected(ce->pin(0), ev->pin(0))) { assert(ev->edge() == NetEvProbe::POSEDGE); /* Synthesize the true clause to figure out what kind of set/reset we have. */ NetNet*asig = new NetNet(scope, scope->local_hsymbol(), NetNet::WIRE, nex_map->pin_count()); asig->local_flag(true); if_->synth_async(des, scope, nex_map, asig); assert(asig->pin_count() == ff->width()); assert(asig->pin(0).nexus()->drivers_constant()); switch (asig->pin(0).nexus()->driven_value()) { case verinum::V0: connect(ff->pin_Aclr(), ce->pin(0)); break; case verinum::V1: connect(ff->pin_Aset(), ce->pin(0)); break; default: assert(0); } delete asig; assert(events_in.count() == 1); return else_->synth_sync(des, scope, ff, nex_map, nex_out, svector(0)); } } /* Failed to find an asynchronous set/reset, so any events input are probably in error. */ if (events_in.count() > 0) { cerr << get_line() << ": error: Events are unaccounted" << " for in process synthesis." << endl; des->errors += 1; } /* If this is an if/then/else, then it is likely a combinational if, and I should synthesize it that way. */ if (if_ && else_) { return synth_async(des, scope, nex_map, nex_out); } assert(if_); assert(!else_); /* Synthesize the input to the DFF. */ bool flag = if_->synth_async(des, scope, nex_map, nex_out); if (flag == false) return flag; assert(expr_); connect(ff->pin_Enable(), ce->pin(0)); return true; } bool NetEvWait::synth_sync(Design*des, NetScope*scope, NetFF*ff, const NetNet*nex_map, NetNet*nex_out, const svector&events_in) { if (events_in.count() > 0) { cerr << get_line() << ": error: Events are unaccounted" << " for in process synthesis." << endl; des->errors += 1; } assert(events_in.count() == 0); /* This can't be other then one unless there are named events, which I cannot synthesize. */ assert(nevents_ == 1); NetEvent*ev = events_[0]; assert(ev->nprobe() >= 1); svectorevents (ev->nprobe() - 1); /* Get the input set from the substatement. This will be used to figure out which of the probes in the clock. */ NexusSet*statement_input = statement_ -> nex_input(); /* Search for a clock input. The clock input is the edge event that is not also an input to the substatement. */ NetEvProbe*pclk = 0; unsigned event_idx = 0; for (unsigned idx = 0 ; idx < ev->nprobe() ; idx += 1) { NetEvProbe*tmp = ev->probe(idx); assert(tmp->pin_count() == 1); NexusSet tmp_nex; tmp_nex .add( tmp->pin(0).nexus() ); if (! statement_input ->contains(tmp_nex)) { if (pclk != 0) { cerr << get_line() << ": error: Too many " << "clocks for synchronous logic." << endl; cerr << get_line() << ": : Perhaps an" << " asynchronous set/reset is misused?" << endl; des->errors += 1; } pclk = tmp; } else { events[event_idx++] = tmp; } } if (pclk == 0) { cerr << get_line() << ": error: None of the edges" << " are valid clock inputs." << endl; cerr << get_line() << ": : Perhaps the clock" << " is read by a statement or expression?" << endl; return false; } connect(ff->pin_Clock(), pclk->pin(0)); if (pclk->edge() == NetEvProbe::NEGEDGE) ff->attribute("Clock:LPM_Polarity", verinum("INVERT")); #if 0 if (ev->nprobe() > 1) { cerr << get_line() << ": sorry: I don't know how " << "to synthesize asynchronous DFF controls." << endl; return false; } #endif /* Synthesize the input to the DFF. */ bool flag = statement_->synth_sync(des, scope, ff, nex_map, nex_out, events); return flag; } bool NetProcTop::synth_sync(Design*des) { NexusSet nex_set; statement_->nex_output(nex_set); NetFF*ff = new NetFF(scope(), scope()->local_hsymbol().c_str(), nex_set.count()); des->add_node(ff); ff->attribute("LPM_FFType", verinum("DFF")); /* The D inputs to the DFF device will receive the output from the statments of the process. */ NetNet*nex_d = new NetNet(scope(), scope()->local_hsymbol().c_str(), NetNet::WIRE, nex_set.count()); nex_d->local_flag(true); for (unsigned idx = 0 ; idx < nex_set.count() ; idx += 1) { connect(nex_d->pin(idx), ff->pin_Data(idx)); } /* The Q outputs of the DFF will connect to the actual outputs of the process. Thus, the DFF will be between the outputs of the process and the outputs of the substatement. */ NetNet*nex_q = new NetNet(scope(), "tmpq", NetNet::WIRE, nex_set.count()); for (unsigned idx = 0 ; idx < nex_set.count() ; idx += 1) { connect(nex_set[idx], nex_q->pin(idx)); connect(nex_q->pin(idx), ff->pin_Q(idx)); } /* Synthesize the input to the DFF. */ bool flag = statement_->synth_sync(des, scope(), ff, nex_q, nex_d, svector()); delete nex_q; return flag; } class synth2_f : public functor_t { public: void process(class Design*, class NetProcTop*); private: }; /* * Look at a process. If it is asynchronous, then synthesize it as an * asynchronous process and delete the process itself for its gates. */ void synth2_f::process(class Design*des, class NetProcTop*top) { if (top->attribute("ivl_synthesis_off").as_ulong() != 0) return; if (top->is_synchronous()) do { bool flag = top->synth_sync(des); if (! flag) { cerr << top->get_line() << ": error: " << "Unable to synthesize synchronous process." << endl; des->errors += 1; return; } des->delete_process(top); return; } while (0); if (! top->is_asynchronous()) { bool synth_error_flag = false; if (top->attribute("ivl_combinational").as_ulong() != 0) { cerr << top->get_line() << ": error: " << "Process is marked combinational," << " but isn't really." << endl; des->errors += 1; synth_error_flag = true; } if (top->attribute("ivl_synthesis_on").as_ulong() != 0) { cerr << top->get_line() << ": error: " << "Process is marked for synthesis," << " but I can't do it." << endl; des->errors += 1; synth_error_flag = true; } if (! synth_error_flag) cerr << top->get_line() << ": warning: " << "Process not synthesized." << endl; return; } if (! top->synth_async(des)) { cerr << top->get_line() << ": internal error: " << "is_asynchronous does not match " << "sync_async results." << endl; return; } des->delete_process(top); } void synth2(Design*des) { synth2_f synth_obj; des->functor(&synth_obj); } /* * $Log: synth2.cc,v $ * Revision 1.16 2002/10/21 01:42:09 steve * Synthesizer support for synchronous begin-end blocks. * * Revision 1.15 2002/10/20 19:19:37 steve * Handle conditional error cases better. * * Revision 1.14 2002/09/26 03:42:10 steve * Remove excess debug messages. * * Revision 1.13 2002/09/26 03:18:04 steve * Generate vvp code for asynch set/reset of NetFF. * * Revision 1.12 2002/09/26 01:13:14 steve * Synthesize async set/reset is certain cases. * * Revision 1.11 2002/09/24 00:58:35 steve * More detailed check of process edge events. * * Revision 1.10 2002/09/17 04:40:28 steve * Connect output of block to net_out, instead of statement outputs. * * Revision 1.9 2002/09/16 00:30:33 steve * Add to synth2 support for synthesis of * synchronous logic. This includes DFF enables * modeled by if/then/else. * * Revision 1.8 2002/08/18 22:07:16 steve * Detect temporaries in sequential block synthesis. * * Revision 1.7 2002/08/12 01:35:00 steve * conditional ident string using autoconfig. * * Revision 1.6 2002/08/10 22:07:08 steve * Observe attributes to control synthesis. * * Revision 1.5 2002/07/29 00:00:28 steve * Asynchronous synthesis of sequential blocks. * * Revision 1.4 2002/07/16 04:40:48 steve * Allow wide rvalues assigned to narrow nex_out. * * Revision 1.3 2002/07/07 22:32:15 steve * Asynchronous synthesis of case statements. * * Revision 1.2 2002/07/01 00:54:21 steve * synth_asych of if/else requires redirecting the target * if sub-statements. Use NetNet objects to manage the * situation. * * Revision 1.1 2002/06/30 02:21:32 steve * Add structure for asynchronous logic synthesis. * */