2002-06-30 04:21:31 +02:00
|
|
|
/*
|
2010-12-14 02:42:48 +01:00
|
|
|
* Copyright (c) 2002-2010 Stephen Williams (steve@icarus.com)
|
2002-06-30 04:21:31 +02:00
|
|
|
*
|
|
|
|
|
* 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
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
# include "config.h"
|
|
|
|
|
|
|
|
|
|
# include "functor.h"
|
|
|
|
|
# include "netlist.h"
|
2002-07-01 02:54:21 +02:00
|
|
|
# include "compiler.h"
|
2007-05-30 19:48:53 +02:00
|
|
|
#include <cassert>
|
|
|
|
|
#include <climits>
|
2008-01-22 03:04:30 +01:00
|
|
|
#include <cstdlib>
|
2007-05-30 19:48:53 +02:00
|
|
|
|
|
|
|
|
#include <new> // standard operator new
|
|
|
|
|
using std::bad_alloc;
|
2002-06-30 04:21:31 +02:00
|
|
|
|
2003-12-17 17:52:39 +01:00
|
|
|
static int debug_synth2=0;
|
2004-03-15 19:40:12 +01:00
|
|
|
|
|
|
|
|
#ifdef __FUNCTION__
|
|
|
|
|
|
2003-12-17 17:52:39 +01:00
|
|
|
#define DEBUG_SYNTH2_ENTRY(class) if (debug_synth2) { cerr << "Enter " << class << "::" \
|
2011-03-07 19:52:29 +01:00
|
|
|
<< __FUNCTION__ << endl; dump(cerr, 4); }
|
2003-12-17 17:52:39 +01:00
|
|
|
#define DEBUG_SYNTH2_EXIT(class,val) if (debug_synth2) { cerr << "Exit " << class << "::" \
|
2011-03-07 19:52:29 +01:00
|
|
|
<< __FUNCTION__ << ", result " << val << endl; }
|
2003-12-17 17:52:39 +01:00
|
|
|
|
2004-03-15 19:40:12 +01:00
|
|
|
#else
|
|
|
|
|
#define DEBUG_SYNTH2_ENTRY(class)
|
|
|
|
|
#define DEBUG_SYNTH2_EXIT(class,val)
|
|
|
|
|
#endif
|
|
|
|
|
|
2006-03-27 01:09:21 +02:00
|
|
|
bool NetProc::synth_async_noaccum(Design*des, NetScope*scope, bool sync_flag,
|
|
|
|
|
struct sync_accounting_cell*nex_ff,
|
|
|
|
|
NetNet*nex_map, NetNet*nex_out)
|
2002-09-16 02:30:33 +02:00
|
|
|
{
|
2006-03-27 01:09:21 +02:00
|
|
|
/* Make an unconnected stub for the accum_in net. */
|
|
|
|
|
const perm_string tmp = perm_string::literal("tmp");
|
|
|
|
|
NetNet*stub = new NetNet(scope, tmp, NetNet::WIRE, nex_out->pin_count());
|
|
|
|
|
|
2010-12-29 21:33:08 +01:00
|
|
|
bool latch_inferred = false;
|
2006-03-27 01:09:21 +02:00
|
|
|
bool flag = synth_async(des, scope, sync_flag, nex_ff,
|
2010-12-29 21:33:08 +01:00
|
|
|
nex_map, nex_out, stub, latch_inferred);
|
2006-03-27 01:09:21 +02:00
|
|
|
|
|
|
|
|
delete stub;
|
|
|
|
|
return flag;
|
2002-09-16 02:30:33 +02:00
|
|
|
}
|
|
|
|
|
|
2005-12-14 01:54:29 +01:00
|
|
|
bool NetProc::synth_async(Design*des, NetScope*scope, bool sync_flag,
|
2010-12-29 21:33:08 +01:00
|
|
|
struct sync_accounting_cell*nex_ff,
|
|
|
|
|
NetNet*nex_map, NetNet*nex_out,
|
|
|
|
|
NetNet*accum_in, bool&latch_inferred, NetNet*gsig)
|
2005-08-22 03:00:41 +02:00
|
|
|
{
|
2006-03-27 01:09:21 +02:00
|
|
|
return false;
|
2005-08-22 03:00:41 +02:00
|
|
|
}
|
|
|
|
|
|
2006-01-18 02:23:23 +01:00
|
|
|
bool NetProc::synth_sync(Design*des, NetScope*scope,
|
2010-12-29 21:33:08 +01:00
|
|
|
struct sync_accounting_cell*nex_ff,
|
|
|
|
|
NetNet*nex_map, NetNet*nex_out,
|
|
|
|
|
const svector<NetEvProbe*>&events)
|
2002-09-16 02:30:33 +02:00
|
|
|
{
|
2006-03-27 01:09:21 +02:00
|
|
|
return synth_async_noaccum(des, scope, true, nex_ff, nex_map, nex_out);
|
2002-09-16 02:30:33 +02:00
|
|
|
}
|
2010-12-29 21:33:08 +01:00
|
|
|
|
2006-08-08 04:17:48 +02:00
|
|
|
#if 0
|
2002-07-01 02:54:21 +02:00
|
|
|
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;
|
|
|
|
|
}
|
2006-08-08 04:17:48 +02:00
|
|
|
#endif
|
2010-12-29 21:33:08 +01:00
|
|
|
|
2006-08-08 04:17:48 +02:00
|
|
|
struct nexus_map_t {
|
|
|
|
|
const Nexus*nex;
|
|
|
|
|
int idx;
|
|
|
|
|
};
|
2010-12-29 21:33:08 +01:00
|
|
|
|
2006-08-08 04:17:48 +02:00
|
|
|
static int ncp_compare(const void*p1, const void*p2)
|
|
|
|
|
{
|
|
|
|
|
const Nexus*a1 = ((const struct nexus_map_t*)p1) -> nex;
|
|
|
|
|
const Nexus*a2 = ((const struct nexus_map_t*)p2) -> nex;
|
|
|
|
|
if (a1 < a2)
|
|
|
|
|
return -1;
|
|
|
|
|
if (a1 > a2)
|
|
|
|
|
return 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct nexus_map_t*make_nexus_index(const NetNet*nset)
|
|
|
|
|
{
|
|
|
|
|
struct nexus_map_t*table = new struct nexus_map_t[nset->pin_count()];
|
|
|
|
|
for (unsigned idx = 0 ; idx < nset->pin_count() ; idx += 1) {
|
|
|
|
|
table[idx].nex = nset->pin(idx).nexus();
|
|
|
|
|
table[idx].idx = idx;
|
|
|
|
|
}
|
|
|
|
|
qsort(table, nset->pin_count(), sizeof(struct nexus_map_t), ncp_compare);
|
|
|
|
|
return table;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int map_nexus_in_index(struct nexus_map_t*table, size_t ntable,
|
|
|
|
|
const Nexus*nex)
|
|
|
|
|
{
|
|
|
|
|
struct nexus_map_t key;
|
|
|
|
|
key.nex = nex;
|
|
|
|
|
struct nexus_map_t*res = (struct nexus_map_t*)
|
|
|
|
|
bsearch(&key, table, ntable,
|
|
|
|
|
sizeof(struct nexus_map_t), ncp_compare);
|
|
|
|
|
|
|
|
|
|
if (res == 0)
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
assert(res->nex == nex);
|
|
|
|
|
return res->idx;
|
|
|
|
|
}
|
2002-06-30 04:21:31 +02:00
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Async synthesis of assignments is done by synthesizing the rvalue
|
|
|
|
|
* expression, then connecting the l-value directly to the output of
|
|
|
|
|
* the r-value.
|
2002-07-01 02:54:21 +02:00
|
|
|
*
|
|
|
|
|
* 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.
|
2002-06-30 04:21:31 +02:00
|
|
|
*/
|
2005-11-13 23:28:48 +01:00
|
|
|
|
2005-12-14 01:54:29 +01:00
|
|
|
bool NetAssignBase::synth_async(Design*des, NetScope*scope, bool sync_flag,
|
2010-12-29 21:33:08 +01:00
|
|
|
struct sync_accounting_cell*nex_ff,
|
|
|
|
|
NetNet*nex_map, NetNet*nex_out,
|
|
|
|
|
NetNet*accum_in, bool&latch_inferred,
|
|
|
|
|
NetNet*gsig)
|
2002-06-30 04:21:31 +02:00
|
|
|
{
|
|
|
|
|
NetNet*rsig = rval_->synthesize(des);
|
2006-05-20 18:06:48 +02:00
|
|
|
if (rsig == 0) {
|
|
|
|
|
cerr << get_line() << ": error: Cannot synthesize r-value "
|
|
|
|
|
<< "expression of assignment." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2002-07-01 02:54:21 +02:00
|
|
|
assert(rsig);
|
2005-12-14 01:54:29 +01:00
|
|
|
|
2005-12-10 05:26:32 +01:00
|
|
|
unsigned roff = 0;
|
2002-06-30 04:21:31 +02:00
|
|
|
|
2005-12-10 05:26:32 +01:00
|
|
|
for (NetAssign_*cur = lval_ ; cur ; cur = cur->more) {
|
|
|
|
|
|
2006-03-12 08:34:16 +01:00
|
|
|
NetMemory*lmem = cur->mem();
|
|
|
|
|
if (lmem && !sync_flag) {
|
|
|
|
|
cerr << get_line() << ": error: Cannot synthesize memory "
|
2006-03-16 06:39:43 +01:00
|
|
|
<< "assignment in asynchronous logic." << endl;
|
2006-03-12 08:34:16 +01:00
|
|
|
des->errors += 1;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Is this an assignment to a memory? If so, then
|
|
|
|
|
explode the memory to an array of reg bits. The
|
|
|
|
|
context that is calling this will attach a decoder
|
|
|
|
|
between the ff and the r-val. In fact, the memory at
|
|
|
|
|
this point has already been scanned and exploded, so
|
|
|
|
|
the explode_to_reg method below will return a
|
|
|
|
|
pre-existing vector.
|
|
|
|
|
|
|
|
|
|
Note that this is only workable if we are in the
|
|
|
|
|
asynchronous path of a synchronous thread. The
|
|
|
|
|
sync_flag must be true in this case. */
|
|
|
|
|
if (lmem) {
|
|
|
|
|
assert(sync_flag);
|
2006-04-16 21:26:37 +02:00
|
|
|
synth_async_mem_sync_(des, scope, cur, rsig, roff,
|
|
|
|
|
nex_map, nex_out);
|
2006-03-12 08:34:16 +01:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2007-05-30 19:48:53 +02:00
|
|
|
if (cur->bmux() && !sync_flag) {
|
|
|
|
|
cerr << get_line() << ": error: Assign to bit select "
|
|
|
|
|
<< "Not possible in asynchronous logic." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2006-03-12 08:34:16 +01:00
|
|
|
|
2005-12-10 05:26:32 +01:00
|
|
|
NetNet*lsig = cur->sig();
|
|
|
|
|
if (!lsig) {
|
|
|
|
|
cerr << get_line() << ": error: NetAssignBase::synth_async "
|
|
|
|
|
"on unsupported lval ";
|
|
|
|
|
dump_lval(cerr);
|
|
|
|
|
cerr << endl;
|
2006-03-18 19:43:21 +01:00
|
|
|
des->errors += 1;
|
2005-12-10 05:26:32 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2010-12-29 21:33:08 +01:00
|
|
|
if (latch_inferred) {
|
|
|
|
|
/* We do not support a bmux with a latch. */
|
|
|
|
|
assert(!cur->bmux());
|
2007-05-30 19:48:53 +02:00
|
|
|
|
2010-12-29 21:33:08 +01:00
|
|
|
/* Build a latch wide enough to hold the signal. */
|
|
|
|
|
NetLatch *latch = new NetLatch(scope, lsig->name(),
|
|
|
|
|
lsig->pin_count());
|
|
|
|
|
des->add_node(latch);
|
|
|
|
|
latch->set_line(*this);
|
2007-05-30 19:48:53 +02:00
|
|
|
|
2010-12-29 21:33:08 +01:00
|
|
|
/* Connect the output to the nex_output. Use the nex_map
|
|
|
|
|
to map the l-value bit position to the nex_out bit
|
|
|
|
|
position. Also connect the data and clock (gate) pins. */
|
|
|
|
|
struct nexus_map_t *nex_map_idx = make_nexus_index(nex_map);
|
2007-05-30 19:48:53 +02:00
|
|
|
|
2010-12-29 21:33:08 +01:00
|
|
|
for (unsigned idx = 0U; idx < cur->lwidth(); idx += 1U) {
|
2007-05-30 19:48:53 +02:00
|
|
|
unsigned off = cur->get_loff() + idx;
|
2010-12-29 21:33:08 +01:00
|
|
|
int tmp = map_nexus_in_index(nex_map_idx,
|
|
|
|
|
nex_map->pin_count(),
|
|
|
|
|
lsig->pin(off).nexus());
|
|
|
|
|
assert(tmp >= 0);
|
|
|
|
|
unsigned ptr = tmp;
|
|
|
|
|
connect(latch->pin_Data(idx), rsig->pin(roff + idx));
|
|
|
|
|
connect(nex_out->pin(ptr), latch->pin_Q(idx) );
|
|
|
|
|
connect(latch->pin_Clock(), gsig->pin(0));
|
2007-05-30 19:48:53 +02:00
|
|
|
}
|
2010-12-29 21:33:08 +01:00
|
|
|
} else {
|
2007-05-30 19:48:53 +02:00
|
|
|
|
2010-12-29 21:33:08 +01:00
|
|
|
/* 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). */
|
|
|
|
|
NetDemux*dq = new NetDemux(scope,
|
|
|
|
|
scope->local_symbol(),
|
|
|
|
|
lsig->pin_count(),
|
|
|
|
|
adr->pin_count(),
|
|
|
|
|
lsig->pin_count());
|
|
|
|
|
des->add_node(dq);
|
|
|
|
|
dq->set_line(*this);
|
|
|
|
|
|
|
|
|
|
/* 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));
|
|
|
|
|
|
|
|
|
|
assert(cur->lwidth() == 1);
|
|
|
|
|
|
|
|
|
|
/* 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));
|
|
|
|
|
}
|
2007-05-30 19:48:53 +02:00
|
|
|
|
2010-12-29 21:33:08 +01:00
|
|
|
struct nexus_map_t*nex_map_idx;
|
|
|
|
|
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;
|
|
|
|
|
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));
|
|
|
|
|
}
|
2006-03-27 01:09:21 +02:00
|
|
|
|
2010-12-29 21:33:08 +01:00
|
|
|
delete[] nex_map_idx;
|
2006-05-18 03:47:12 +02:00
|
|
|
|
2010-12-29 21:33:08 +01:00
|
|
|
/* The r-value (1 bit) connects to the WriteData
|
|
|
|
|
input of the demux. */
|
|
|
|
|
connect(dq->pin_WriteData(0), rsig->pin(roff));
|
2006-08-08 04:17:48 +02:00
|
|
|
|
2010-12-29 21:33:08 +01:00
|
|
|
roff += cur->lwidth();
|
|
|
|
|
cur->turn_sig_to_wire_on_release();
|
|
|
|
|
continue;
|
2006-03-27 01:09:21 +02:00
|
|
|
}
|
|
|
|
|
|
2010-12-29 21:33:08 +01:00
|
|
|
/* By this point any bmux() has been dealt with. Panic
|
|
|
|
|
if that is not so. */
|
|
|
|
|
assert(! cur->bmux());
|
2006-08-08 04:17:48 +02:00
|
|
|
|
2010-12-29 21:33:08 +01:00
|
|
|
/* 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);
|
2006-08-08 04:17:48 +02:00
|
|
|
|
2010-12-29 21:33:08 +01:00
|
|
|
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(),
|
|
|
|
|
lsig->pin(off).nexus());
|
|
|
|
|
assert(tmp >= 0);
|
|
|
|
|
unsigned ptr = tmp;
|
|
|
|
|
connect(nex_out->pin(ptr), rsig->pin(roff+idx));
|
|
|
|
|
}
|
2007-05-30 19:48:53 +02:00
|
|
|
|
2010-12-29 21:33:08 +01:00
|
|
|
}
|
2002-06-30 04:21:31 +02:00
|
|
|
|
2005-12-10 05:26:32 +01:00
|
|
|
roff += cur->lwidth();
|
|
|
|
|
|
2010-12-29 21:33:08 +01:00
|
|
|
/* 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. */
|
2005-12-10 05:26:32 +01:00
|
|
|
cur->turn_sig_to_wire_on_release();
|
|
|
|
|
}
|
2004-08-28 17:08:31 +02:00
|
|
|
|
2002-06-30 04:21:31 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2006-04-16 21:26:37 +02:00
|
|
|
/*
|
|
|
|
|
* Handle the special case of assignment to memory. Explode the memory
|
|
|
|
|
* to an array of reg bits. The context that is calling this will
|
|
|
|
|
* attach a decoder between the ff and the r-val. In fact, the memory
|
|
|
|
|
* at this point has already been scanned and exploded, so the
|
|
|
|
|
* explode_to_reg method below will return a pre-existing vector.
|
|
|
|
|
*/
|
|
|
|
|
bool NetAssignBase::synth_async_mem_sync_(Design*des, NetScope*scope,
|
|
|
|
|
NetAssign_*cur,
|
|
|
|
|
NetNet*rsig, unsigned&roff,
|
|
|
|
|
NetNet*nex_map, NetNet*nex_out)
|
|
|
|
|
{
|
|
|
|
|
NetMemory*lmem = cur->mem();
|
|
|
|
|
assert(lmem);
|
|
|
|
|
|
2006-08-08 04:17:48 +02:00
|
|
|
if (debug_synth) {
|
|
|
|
|
cerr << get_line() << ": debug: Start synthesis of assign "
|
|
|
|
|
"to memory " << lmem->name() << "." << endl;
|
|
|
|
|
}
|
|
|
|
|
|
2006-04-16 21:26:37 +02:00
|
|
|
NetNet*msig = lmem->explode_to_reg();
|
|
|
|
|
cur->incr_mem_lref();
|
|
|
|
|
|
|
|
|
|
/* Handle the special case that the mux expression is
|
|
|
|
|
constant. In this case, just hook up the pertinent bits. */
|
|
|
|
|
if (NetEConst*ae = dynamic_cast<NetEConst*>(cur->bmux())) {
|
2006-06-03 01:42:48 +02:00
|
|
|
long adr_s = ae->value().as_long();
|
|
|
|
|
if (adr_s >= (long)lmem->count()) {
|
2006-05-05 03:56:36 +02:00
|
|
|
cerr << get_line() << ": error: "
|
2006-06-03 01:42:48 +02:00
|
|
|
<< "Address " << adr_s
|
2006-05-05 03:56:36 +02:00
|
|
|
<< " is outside range of memory."
|
|
|
|
|
<< " Skipping assignment." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2006-08-08 04:17:48 +02:00
|
|
|
struct nexus_map_t*nex_map_idx = make_nexus_index(nex_map);
|
|
|
|
|
|
2006-06-03 01:42:48 +02:00
|
|
|
unsigned adr = lmem->index_to_address(adr_s) * lmem->width();
|
2006-04-16 21:26:37 +02:00
|
|
|
for (unsigned idx = 0 ; idx < cur->lwidth() ; idx += 1) {
|
|
|
|
|
unsigned off = adr+idx;
|
2006-08-08 04:17:48 +02:00
|
|
|
int tmp = map_nexus_in_index(nex_map_idx,
|
|
|
|
|
nex_map->pin_count(),
|
|
|
|
|
msig->pin(off).nexus());
|
|
|
|
|
assert(tmp >= 0);
|
|
|
|
|
unsigned ptr = tmp;
|
2006-04-16 21:26:37 +02:00
|
|
|
connect(nex_out->pin(ptr), rsig->pin(roff+idx));
|
|
|
|
|
}
|
|
|
|
|
|
2006-08-08 04:17:48 +02:00
|
|
|
delete[]nex_map_idx;
|
|
|
|
|
|
2006-04-16 21:26:37 +02:00
|
|
|
cur->turn_sig_to_wire_on_release();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
assert(cur->bmux() != 0);
|
|
|
|
|
|
|
|
|
|
NetNet*adr = cur->bmux()->synthesize(des);
|
|
|
|
|
|
|
|
|
|
NetDemux*dq = new NetDemux(scope, scope->local_symbol(),
|
|
|
|
|
msig->pin_count(),
|
|
|
|
|
adr->pin_count(),
|
|
|
|
|
msig->pin_count() / cur->lwidth());
|
|
|
|
|
des->add_node(dq);
|
|
|
|
|
dq->set_line(*this);
|
|
|
|
|
|
|
|
|
|
for (unsigned idx = 0; idx < adr->pin_count() ; idx += 1)
|
|
|
|
|
connect(dq->pin_Address(idx), adr->pin(idx));
|
|
|
|
|
|
2006-08-08 04:17:48 +02:00
|
|
|
struct nexus_map_t*nex_map_idx = make_nexus_index(nex_map);
|
|
|
|
|
|
2006-04-16 21:26:37 +02:00
|
|
|
for (unsigned idx = 0; idx < msig->pin_count(); idx += 1) {
|
|
|
|
|
unsigned off = idx;
|
2006-08-08 04:17:48 +02:00
|
|
|
int tmp = map_nexus_in_index(nex_map_idx,
|
|
|
|
|
nex_map->pin_count(),
|
|
|
|
|
msig->pin(off).nexus());
|
|
|
|
|
assert(tmp >= 0);
|
|
|
|
|
unsigned ptr = tmp;
|
2006-04-16 21:26:37 +02:00
|
|
|
connect(nex_out->pin(ptr), dq->pin_Q(idx));
|
|
|
|
|
}
|
|
|
|
|
|
2006-08-08 04:17:48 +02:00
|
|
|
delete[]nex_map_idx;
|
|
|
|
|
|
2006-04-16 21:26:37 +02:00
|
|
|
for (unsigned idx = 0 ; idx < msig->pin_count(); idx += 1)
|
|
|
|
|
connect(dq->pin_Data(idx), nex_map->pin(roff+idx));
|
|
|
|
|
|
|
|
|
|
for (unsigned idx = 0 ; idx < cur->lwidth() ; idx += 1)
|
|
|
|
|
connect(dq->pin_WriteData(idx), rsig->pin(roff+idx));
|
|
|
|
|
|
|
|
|
|
roff += cur->lwidth();
|
|
|
|
|
cur->turn_sig_to_wire_on_release();
|
|
|
|
|
|
2006-08-08 04:17:48 +02:00
|
|
|
if (debug_synth) {
|
|
|
|
|
cerr << get_line() << ": debug: Finish synthesis of assign "
|
|
|
|
|
"to memory " << lmem->name() << "." << endl;
|
|
|
|
|
}
|
|
|
|
|
|
2006-04-16 21:26:37 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2002-07-29 02:00:28 +02:00
|
|
|
/*
|
|
|
|
|
* 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.
|
|
|
|
|
*/
|
2005-12-14 01:54:29 +01:00
|
|
|
bool NetBlock::synth_async(Design*des, NetScope*scope, bool sync_flag,
|
2010-12-29 21:33:08 +01:00
|
|
|
struct sync_accounting_cell*nex_ff,
|
|
|
|
|
NetNet*nex_map, NetNet*nex_out, NetNet*accum_in,
|
|
|
|
|
bool&latch_inferred, NetNet*gsig)
|
2002-07-29 02:00:28 +02:00
|
|
|
{
|
2003-12-17 17:52:39 +01:00
|
|
|
if (last_ == 0) {
|
2002-07-29 02:00:28 +02:00
|
|
|
return true;
|
2003-12-17 17:52:39 +01:00
|
|
|
}
|
2002-07-29 02:00:28 +02:00
|
|
|
|
2006-05-18 03:47:12 +02:00
|
|
|
if (debug_synth) {
|
|
|
|
|
cerr << get_line() << ": debug: "
|
|
|
|
|
<< (sync_flag?"sync":"async")
|
2006-07-23 21:42:33 +02:00
|
|
|
<< " synthesis of statement block. "
|
|
|
|
|
<< "pin_count=" << nex_out->pin_count() << endl;
|
2006-05-18 03:47:12 +02:00
|
|
|
}
|
|
|
|
|
|
2004-02-18 18:11:54 +01:00
|
|
|
const perm_string tmp1 = perm_string::literal("tmp1");
|
|
|
|
|
const perm_string tmp2 = perm_string::literal("tmp2");
|
2005-08-22 00:49:54 +02:00
|
|
|
const perm_string tmp3 = perm_string::literal("tmp3");
|
|
|
|
|
|
|
|
|
|
NetNet*accum_out = new NetNet(scope, tmp3, NetNet::WIRE,
|
|
|
|
|
nex_out->pin_count());
|
|
|
|
|
accum_out->local_flag(true);
|
2004-02-18 18:11:54 +01:00
|
|
|
|
2006-11-12 00:10:20 +01:00
|
|
|
/* Output that ultimately have not been driven should collect
|
|
|
|
|
their value from the accumulated input. */
|
|
|
|
|
assert(accum_out->pin_count() == accum_in->pin_count());
|
|
|
|
|
for (unsigned idx = 0 ; idx < accum_out->pin_count() ; idx += 1) {
|
|
|
|
|
if (accum_in->pin(idx).nexus()->is_driven())
|
|
|
|
|
connect(accum_out->pin(idx), accum_in->pin(idx));
|
|
|
|
|
}
|
|
|
|
|
|
2002-07-29 02:00:28 +02:00
|
|
|
bool flag = true;
|
|
|
|
|
NetProc*cur = last_;
|
|
|
|
|
do {
|
2005-08-22 03:00:41 +02:00
|
|
|
NetNet*new_accum;
|
|
|
|
|
|
2002-07-29 02:00:28 +02:00
|
|
|
cur = cur->next_;
|
|
|
|
|
|
2002-09-17 06:40:28 +02:00
|
|
|
/* Create a temporary nex_map for the substatement. */
|
2002-07-29 02:00:28 +02:00
|
|
|
NexusSet tmp_set;
|
|
|
|
|
cur->nex_output(tmp_set);
|
2004-02-18 18:11:54 +01:00
|
|
|
NetNet*tmp_map = new NetNet(scope, tmp1, NetNet::WIRE,
|
2002-07-29 02:00:28 +02:00
|
|
|
tmp_set.count());
|
2002-09-17 06:40:28 +02:00
|
|
|
for (unsigned idx = 0 ; idx < tmp_map->pin_count() ; idx += 1)
|
|
|
|
|
connect(tmp_set[idx], tmp_map->pin(idx));
|
2002-07-29 02:00:28 +02:00
|
|
|
|
2002-09-17 06:40:28 +02:00
|
|
|
/* Create also a temporary net_out to collect the
|
|
|
|
|
output. */
|
2004-02-18 18:11:54 +01:00
|
|
|
NetNet*tmp_out = new NetNet(scope, tmp2, NetNet::WIRE,
|
2002-09-17 06:40:28 +02:00
|
|
|
tmp_set.count());
|
2005-12-10 04:30:50 +01:00
|
|
|
tmp_out->set_line(*this);
|
2002-09-17 06:40:28 +02:00
|
|
|
|
2005-08-22 03:00:41 +02:00
|
|
|
/* Make a temporary set of currently accumulated outputs
|
|
|
|
|
that we can pass to the synth_async of the
|
|
|
|
|
sub-statement. Some sub-statements will use this to
|
|
|
|
|
handle default cases specially. We will delete this
|
|
|
|
|
temporary map as soon as the synth_async is done. */
|
|
|
|
|
new_accum = new NetNet(scope, tmp3, NetNet::WIRE, tmp_set.count());
|
2006-08-08 04:17:48 +02:00
|
|
|
struct nexus_map_t*nex_map_idx = make_nexus_index(nex_map);
|
|
|
|
|
|
2005-08-22 03:00:41 +02:00
|
|
|
for (unsigned idx = 0 ; idx < tmp_set.count() ; idx += 1) {
|
2006-08-08 04:17:48 +02:00
|
|
|
int tmp = map_nexus_in_index(nex_map_idx,
|
|
|
|
|
nex_map->pin_count(),
|
|
|
|
|
tmp_set[idx]);
|
|
|
|
|
assert(tmp >= 0);
|
|
|
|
|
unsigned ptr = tmp;
|
2006-11-12 00:10:20 +01:00
|
|
|
if (accum_out->pin(ptr).nexus()->is_driven())
|
2005-08-22 03:00:41 +02:00
|
|
|
connect(new_accum->pin(idx), accum_out->pin(ptr));
|
|
|
|
|
}
|
|
|
|
|
|
2006-08-08 04:17:48 +02:00
|
|
|
delete [] nex_map_idx;
|
|
|
|
|
|
2006-03-27 01:09:21 +02:00
|
|
|
bool ok_flag = cur->synth_async(des, scope, sync_flag, nex_ff,
|
2010-12-29 21:33:08 +01:00
|
|
|
tmp_map, tmp_out, new_accum,
|
|
|
|
|
latch_inferred, gsig);
|
2002-07-29 02:00:28 +02:00
|
|
|
flag = flag && ok_flag;
|
|
|
|
|
|
2005-08-22 03:00:41 +02:00
|
|
|
delete new_accum;
|
|
|
|
|
|
2005-12-10 04:30:50 +01:00
|
|
|
/* NOTE: tmp_set is not valid after this point, since
|
|
|
|
|
the cur->synth_async method may change nexa that it
|
|
|
|
|
refers to. */
|
|
|
|
|
|
2002-07-29 02:00:28 +02:00
|
|
|
if (ok_flag == false)
|
|
|
|
|
continue;
|
|
|
|
|
|
2005-08-22 03:00:41 +02:00
|
|
|
/* Now start building a new set of accumulated outputs
|
|
|
|
|
that we will pass to the next statement of the block,
|
|
|
|
|
or that will be the output of the block. */
|
|
|
|
|
new_accum = new NetNet(scope, tmp3, NetNet::WIRE,
|
|
|
|
|
nex_out->pin_count());
|
2005-12-10 04:30:50 +01:00
|
|
|
new_accum->set_line(*this);
|
2006-07-23 21:42:33 +02:00
|
|
|
new_accum->local_flag(true);
|
2005-08-22 00:49:54 +02:00
|
|
|
|
2002-09-17 06:40:28 +02:00
|
|
|
/* Use the nex_map to link up the output from the
|
2006-08-08 04:17:48 +02:00
|
|
|
substatement to the output of the block as a
|
|
|
|
|
whole. */
|
|
|
|
|
|
|
|
|
|
nex_map_idx = make_nexus_index(nex_map);
|
|
|
|
|
|
2002-07-29 02:00:28 +02:00
|
|
|
for (unsigned idx = 0 ; idx < tmp_out->pin_count() ; idx += 1) {
|
2006-08-08 04:17:48 +02:00
|
|
|
int tmp = map_nexus_in_index(nex_map_idx,
|
|
|
|
|
nex_map->pin_count(),
|
|
|
|
|
tmp_map->pin(idx).nexus());
|
|
|
|
|
if (tmp < 0) {
|
2005-12-10 04:30:50 +01:00
|
|
|
cerr << cur->get_line() << ": internal error: "
|
|
|
|
|
<< "Nexus isn't in nex_map?! idx=" << idx
|
|
|
|
|
<< " map width = " << nex_map->pin_count()
|
|
|
|
|
<< " tmp_map count = " << tmp_map->pin_count()
|
|
|
|
|
<< endl;
|
|
|
|
|
}
|
2006-08-08 04:17:48 +02:00
|
|
|
assert(tmp >= 0);
|
|
|
|
|
unsigned ptr = tmp;
|
2005-08-22 00:49:54 +02:00
|
|
|
connect(new_accum->pin(ptr), tmp_out->pin(idx));
|
2002-07-29 02:00:28 +02:00
|
|
|
}
|
|
|
|
|
|
2006-08-08 04:17:48 +02:00
|
|
|
delete[]nex_map_idx;
|
|
|
|
|
|
2002-09-17 06:40:28 +02:00
|
|
|
delete tmp_map;
|
2002-07-29 02:00:28 +02:00
|
|
|
delete tmp_out;
|
|
|
|
|
|
2006-07-23 21:42:33 +02:00
|
|
|
assert(new_accum->pin_count() == accum_out->pin_count());
|
|
|
|
|
|
2005-08-22 00:49:54 +02:00
|
|
|
/* Anything that is not redriven by the current
|
|
|
|
|
statement inherits the value that was driven from any
|
2006-07-23 21:42:33 +02:00
|
|
|
previous statements. Thus, the current statement can
|
|
|
|
|
*override* the outputs of any previous statements. */
|
2005-08-22 00:49:54 +02:00
|
|
|
for (unsigned idx = 0; idx < new_accum->pin_count(); idx += 1) {
|
2006-11-12 00:10:20 +01:00
|
|
|
|
2006-07-23 21:42:33 +02:00
|
|
|
if (new_accum->pin(idx).nexus()->is_driven())
|
2005-08-22 00:49:54 +02:00
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
connect(new_accum->pin(idx), accum_out->pin(idx));
|
|
|
|
|
}
|
|
|
|
|
delete accum_out;
|
|
|
|
|
accum_out = new_accum;
|
|
|
|
|
|
2002-07-29 02:00:28 +02:00
|
|
|
} while (cur != last_);
|
|
|
|
|
|
2006-11-12 00:10:20 +01:00
|
|
|
|
|
|
|
|
/* Output that ultimately have not been driven should collect
|
|
|
|
|
their value from the accumulated input. Do this only for
|
|
|
|
|
asynchronous blocks, because synchronous blocks are handled
|
|
|
|
|
else where (from the context) where unaccounted inputs are
|
|
|
|
|
connected to the output to feedback. */
|
|
|
|
|
if (!sync_flag) {
|
|
|
|
|
assert(accum_out->pin_count() == accum_in->pin_count());
|
|
|
|
|
for (unsigned idx = 0; idx < accum_out->pin_count(); idx += 1) {
|
|
|
|
|
if (! accum_out->pin(idx).is_linked())
|
|
|
|
|
connect(accum_out->pin(idx), accum_in->pin(idx));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2006-07-23 21:42:33 +02:00
|
|
|
/* Now bind the accumulated output values to the nex_out
|
2005-08-22 00:49:54 +02:00
|
|
|
passed in. Note that each previous step has already did the
|
|
|
|
|
pin mapping, so just connect. */
|
2006-07-23 21:42:33 +02:00
|
|
|
assert(nex_out->pin_count() == accum_out->pin_count());
|
2005-08-22 00:49:54 +02:00
|
|
|
for (unsigned idx = 0 ; idx < accum_out->pin_count() ; idx += 1) {
|
|
|
|
|
connect(nex_out->pin(idx), accum_out->pin(idx));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
delete accum_out;
|
|
|
|
|
|
2006-07-23 21:42:33 +02:00
|
|
|
if (debug_synth) {
|
|
|
|
|
cerr << get_line() << ": debug: "
|
|
|
|
|
<< (sync_flag?"sync":"async")
|
|
|
|
|
<< " synthesis of statement block complete. " << endl;
|
2006-11-12 00:10:20 +01:00
|
|
|
|
2006-07-23 21:42:33 +02:00
|
|
|
}
|
2002-07-29 02:00:28 +02:00
|
|
|
return flag;
|
|
|
|
|
}
|
|
|
|
|
|
2005-12-14 01:54:29 +01:00
|
|
|
bool NetCase::synth_async(Design*des, NetScope*scope, bool sync_flag,
|
2010-12-29 21:33:08 +01:00
|
|
|
struct sync_accounting_cell*nex_ff,
|
|
|
|
|
NetNet*nex_map, NetNet*nex_out, NetNet*accum,
|
|
|
|
|
bool&latch_inferred, NetNet*gsig)
|
2002-07-08 00:32:15 +02:00
|
|
|
{
|
|
|
|
|
unsigned cur;
|
|
|
|
|
|
|
|
|
|
NetNet*esig = expr_->synthesize(des);
|
|
|
|
|
|
2006-07-10 02:21:49 +02:00
|
|
|
bool full_case_flag = false;
|
|
|
|
|
if (attribute(perm_string::literal("ivl_full_case")).len() > 0)
|
|
|
|
|
full_case_flag = true;
|
|
|
|
|
|
2002-07-08 00:32:15 +02:00
|
|
|
/* 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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2006-01-18 02:23:23 +01:00
|
|
|
/* At this point, sel_ref represents the constant part of the
|
|
|
|
|
select input. That is, (select&sel_mask) == sel_ref for all
|
|
|
|
|
guard values that are reachable. We can use this to skip
|
|
|
|
|
unreachable guards. */
|
|
|
|
|
|
|
|
|
|
|
2002-07-08 00:32:15 +02:00
|
|
|
/* Build a map of guard values to mux select values. This
|
|
|
|
|
helps account for constant select bits that are being
|
2006-01-18 02:23:23 +01:00
|
|
|
elided. The guard2sel mapping will only be valid for
|
|
|
|
|
reachable guards. */
|
2002-07-08 00:32:15 +02:00
|
|
|
map<unsigned long,unsigned long>guard2sel;
|
|
|
|
|
cur = 0;
|
2003-06-21 03:21:42 +02:00
|
|
|
for (unsigned idx = 0 ; idx < (1U<<esig->pin_count()) ; idx += 1) {
|
2002-07-08 00:32:15 +02:00
|
|
|
if ((idx & ~sel_mask) == sel_ref) {
|
|
|
|
|
guard2sel[idx] = cur;
|
|
|
|
|
cur += 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
2003-06-21 03:21:42 +02:00
|
|
|
assert(cur == (1U << sel_pins));
|
2002-07-08 00:32:15 +02:00
|
|
|
|
2006-01-21 22:42:31 +01:00
|
|
|
unsigned nondefault_items = 0;
|
|
|
|
|
for (unsigned item = 0 ; item < nitems_ ; item += 1) {
|
|
|
|
|
if (items_[item].guard != 0)
|
|
|
|
|
nondefault_items += 1;
|
|
|
|
|
}
|
|
|
|
|
|
2010-12-29 21:33:08 +01:00
|
|
|
/* Handle the special case that this can be done in a smaller
|
2006-11-12 00:10:20 +01:00
|
|
|
1-hot MUX. If there are fewer active cases then there are
|
|
|
|
|
select pins, then a 1-hot encoding should be better. */
|
|
|
|
|
if (nondefault_items < sel_pins) {
|
|
|
|
|
if (debug_synth)
|
|
|
|
|
cerr << get_line() << ": debug: "
|
|
|
|
|
<< "Implement case statement as 1-hot MUX." << endl;
|
2010-12-29 21:33:08 +01:00
|
|
|
return synth_async_1hot_(des, scope, sync_flag, nex_ff, nex_map,
|
|
|
|
|
nex_out, accum, esig, nondefault_items,
|
|
|
|
|
latch_inferred, gsig);
|
2006-11-12 00:10:20 +01:00
|
|
|
}
|
2006-01-21 22:42:31 +01:00
|
|
|
|
2003-02-26 02:29:24 +01:00
|
|
|
NetMux*mux = new NetMux(scope, scope->local_symbol(),
|
2002-07-08 00:32:15 +02:00
|
|
|
nex_out->pin_count(),
|
2003-06-21 03:21:42 +02:00
|
|
|
1U << sel_pins, sel_pins);
|
2005-09-11 04:56:37 +02:00
|
|
|
mux->set_line(*this);
|
2002-07-08 00:32:15 +02:00
|
|
|
|
|
|
|
|
/* 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. */
|
2003-06-21 03:21:42 +02:00
|
|
|
if ((sel_mask & (1U << idx)) == 0)
|
2002-07-08 00:32:15 +02:00
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
connect(mux->pin_Sel(cur), esig->pin(idx));
|
|
|
|
|
cur += 1;
|
|
|
|
|
}
|
|
|
|
|
assert(cur == sel_pins);
|
|
|
|
|
|
2003-08-28 06:11:17 +02:00
|
|
|
/* Hook up the output of the mux to the mapped output pins. */
|
2002-07-08 00:32:15 +02:00
|
|
|
for (unsigned idx = 0 ; idx < mux->width() ; idx += 1)
|
|
|
|
|
connect(nex_out->pin(idx), mux->pin_Result(idx));
|
|
|
|
|
|
2003-03-25 05:04:29 +01:00
|
|
|
NetProc**statement_map = new NetProc*[1 << sel_pins];
|
2003-06-21 03:21:42 +02:00
|
|
|
for (unsigned item = 0 ; item < (1U<<sel_pins) ; item += 1)
|
2003-03-25 05:04:29 +01:00
|
|
|
statement_map[item] = 0;
|
|
|
|
|
|
|
|
|
|
/* Assign the input statements to MUX inputs. This involves
|
|
|
|
|
calculating the guard value, passing that through the
|
|
|
|
|
guard2sel map, then saving the statement in the
|
|
|
|
|
statement_map. If I find a default case, then save that for
|
|
|
|
|
use later. */
|
|
|
|
|
NetProc*default_statement = 0;
|
2002-07-08 00:32:15 +02:00
|
|
|
for (unsigned item = 0 ; item < nitems_ ; item += 1) {
|
2003-03-25 05:04:29 +01:00
|
|
|
/* Skip the default case this pass. */
|
|
|
|
|
if (items_[item].guard == 0) {
|
|
|
|
|
default_statement = items_[item].statement;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2002-07-08 00:32:15 +02:00
|
|
|
|
|
|
|
|
NetEConst*ge = dynamic_cast<NetEConst*>(items_[item].guard);
|
2006-10-30 03:03:30 +01:00
|
|
|
if (ge == 0) {
|
|
|
|
|
cerr << items_[item].guard->get_line() << ": error: "
|
|
|
|
|
<< "Guard expression is not constant for synthesis."
|
|
|
|
|
<< endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2002-07-08 00:32:15 +02:00
|
|
|
assert(ge);
|
|
|
|
|
verinum gval = ge->value();
|
2006-01-18 02:23:23 +01:00
|
|
|
|
2006-07-02 02:50:15 +02:00
|
|
|
list<verinum>gstack;
|
|
|
|
|
gstack.push_front(gval);
|
2006-01-18 02:23:23 +01:00
|
|
|
|
2006-07-02 02:50:15 +02:00
|
|
|
/* A guard may have X/Z values, if this is a casex
|
|
|
|
|
statement. In this case, replace a number with an x/z
|
|
|
|
|
values with two numbers, one with a 0 substituted,
|
|
|
|
|
another with a 1 substituted. Only process as a guard
|
|
|
|
|
numbers that are well defined. The gstack allows us
|
|
|
|
|
to build a list of numbers that match the pattern. */
|
|
|
|
|
while (! gstack.empty()) {
|
|
|
|
|
verinum tmp = gstack.front();
|
|
|
|
|
gstack.pop_front();
|
2002-07-08 00:32:15 +02:00
|
|
|
|
2006-07-02 02:50:15 +02:00
|
|
|
if (tmp.is_defined()
|
|
|
|
|
|| type() == NetCase::EQ) {
|
|
|
|
|
|
|
|
|
|
/* Skip guards that are unreachable. */
|
|
|
|
|
if ((sel_ref&~sel_mask) != (tmp.as_ulong()&~sel_mask))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
unsigned sel_idx = guard2sel[tmp.as_ulong()];
|
|
|
|
|
assert(items_[item].statement);
|
|
|
|
|
statement_map[sel_idx] = items_[item].statement;
|
|
|
|
|
|
2006-08-22 06:22:45 +02:00
|
|
|
} else {
|
|
|
|
|
/* Process casex and casez patterns. */
|
2006-07-02 02:50:15 +02:00
|
|
|
verinum tmp0 = tmp;
|
|
|
|
|
verinum tmp1 = tmp;
|
|
|
|
|
unsigned idx = 0;
|
2006-08-22 06:22:45 +02:00
|
|
|
verinum::V tv = verinum::Vz;
|
2006-07-02 02:50:15 +02:00
|
|
|
while (idx < tmp.len()) {
|
2006-08-22 06:22:45 +02:00
|
|
|
tv = tmp.get(idx);
|
2006-07-02 02:50:15 +02:00
|
|
|
if (tv == verinum::Vx)
|
|
|
|
|
break;
|
|
|
|
|
if (tv == verinum::Vz)
|
|
|
|
|
break;
|
|
|
|
|
idx += 1;
|
|
|
|
|
}
|
2006-08-22 06:22:45 +02:00
|
|
|
// Can't handle an X in a casez statement.
|
|
|
|
|
assert(tv==verinum::Vx? type()==NetCase::EQX : true);
|
2006-07-02 02:50:15 +02:00
|
|
|
assert(idx < tmp.len());
|
2006-08-22 06:22:45 +02:00
|
|
|
|
2006-07-02 02:50:15 +02:00
|
|
|
tmp0.set(idx, verinum::V0);
|
|
|
|
|
tmp1.set(idx, verinum::V1);
|
|
|
|
|
gstack.push_front(tmp1);
|
|
|
|
|
gstack.push_front(tmp0);
|
|
|
|
|
}
|
|
|
|
|
}
|
2003-03-25 05:04:29 +01:00
|
|
|
}
|
|
|
|
|
|
2005-08-22 03:00:41 +02:00
|
|
|
/* Set up a default default_sig that uses the accumulated
|
|
|
|
|
input pins. This binding is suppressed by an actual default
|
|
|
|
|
statement if one exists. */
|
|
|
|
|
NetNet*default_sig = 0;
|
|
|
|
|
if (default_statement == 0) {
|
|
|
|
|
default_sig = accum;
|
|
|
|
|
for (unsigned idx = 0 ; idx < accum->pin_count() ; idx += 1) {
|
|
|
|
|
if (! accum->pin(idx).is_linked()) {
|
|
|
|
|
default_sig = 0;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2005-11-13 23:28:48 +01:00
|
|
|
bool return_flag = true;
|
|
|
|
|
|
2003-12-20 01:59:31 +01:00
|
|
|
/* Now that statements match with mux inputs, synthesize the
|
2003-03-25 05:04:29 +01:00
|
|
|
sub-statements. If I get to an input that has no statement,
|
|
|
|
|
then use the default statement there. */
|
2003-06-21 03:21:42 +02:00
|
|
|
for (unsigned item = 0 ; item < (1U<<sel_pins) ; item += 1) {
|
2003-03-25 05:04:29 +01:00
|
|
|
|
|
|
|
|
/* Detect the case that this is a default input, and I
|
|
|
|
|
have a precalculated default_sig. */
|
|
|
|
|
if ((statement_map[item] == 0) && (default_sig != 0)) {
|
|
|
|
|
for (unsigned idx = 0 ; idx < mux->width() ; idx += 1)
|
|
|
|
|
connect(mux->pin_Data(idx, item), default_sig->pin(idx));
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2003-03-06 01:28:41 +01:00
|
|
|
NetNet*sig = new NetNet(scope, scope->local_symbol(),
|
2002-07-08 00:32:15 +02:00
|
|
|
NetNet::WIRE, nex_map->pin_count());
|
|
|
|
|
sig->local_flag(true);
|
2003-03-25 05:04:29 +01:00
|
|
|
|
2006-01-27 02:58:53 +01:00
|
|
|
/* If this statement is missing, arrange for the default
|
|
|
|
|
statement to be processed here. Also, make the sig be
|
|
|
|
|
the default sig so that the next time we run into a
|
|
|
|
|
reference to the default, we just hook up to the
|
|
|
|
|
default again. */
|
2003-03-25 05:04:29 +01:00
|
|
|
if (statement_map[item] == 0) {
|
|
|
|
|
statement_map[item] = default_statement;
|
|
|
|
|
default_statement = 0;
|
|
|
|
|
default_sig = sig;
|
|
|
|
|
}
|
|
|
|
|
|
2006-07-10 02:21:49 +02:00
|
|
|
if (statement_map[item] == 0 && !sync_flag && !full_case_flag) {
|
2003-12-20 01:59:31 +01:00
|
|
|
/* Missing case and no default; this could still be
|
|
|
|
|
* synthesizable with synchronous logic, but not here. */
|
2005-08-22 03:00:41 +02:00
|
|
|
cerr << get_line()
|
2006-07-02 02:50:15 +02:00
|
|
|
<< ": error: Case item " << item << " is missing"
|
|
|
|
|
<< " in combinational process." << endl;
|
2006-03-18 19:43:21 +01:00
|
|
|
cerr << get_line()
|
2006-07-02 02:50:15 +02:00
|
|
|
<< ": : Do you need a default case?" << endl;
|
2006-03-18 19:43:21 +01:00
|
|
|
des->errors += 1;
|
2005-11-13 23:28:48 +01:00
|
|
|
return_flag = false;
|
|
|
|
|
continue;
|
2003-12-20 01:59:31 +01:00
|
|
|
}
|
2002-07-08 00:32:15 +02:00
|
|
|
|
2006-07-10 02:21:49 +02:00
|
|
|
if (statement_map[item] == 0 && !sync_flag) {
|
|
|
|
|
assert(full_case_flag);
|
|
|
|
|
|
|
|
|
|
/* Cases that should never happen, we connect to
|
|
|
|
|
0 bits. Hopefully, the target (or constant
|
|
|
|
|
propagation) will know how to optimize this
|
|
|
|
|
away. */
|
|
|
|
|
NetConst*zero = new NetConst(scope, scope->local_symbol(),
|
|
|
|
|
verinum::V0);
|
|
|
|
|
zero->set_line(*this);
|
|
|
|
|
des->add_node(zero);
|
|
|
|
|
|
|
|
|
|
NetNet*zsig = new NetNet(scope, scope->local_symbol(),
|
|
|
|
|
NetNet::WIRE, 1);
|
|
|
|
|
zsig->local_flag(true);
|
|
|
|
|
zsig->set_line(*this);
|
|
|
|
|
|
|
|
|
|
connect(zsig->pin(0), zero->pin(0));
|
|
|
|
|
|
|
|
|
|
for (unsigned idx=0; idx < mux->width(); idx += 1)
|
|
|
|
|
connect(mux->pin_Data(idx,item), zsig->pin(0));
|
|
|
|
|
|
|
|
|
|
if (debug_synth) {
|
|
|
|
|
cerr << get_line()
|
|
|
|
|
<< ": debug: Case item " << item << " is set to"
|
|
|
|
|
<< " zero in combinational process." << endl;
|
|
|
|
|
}
|
|
|
|
|
|
2006-11-12 00:10:20 +01:00
|
|
|
continue;
|
|
|
|
|
}
|
2006-01-01 02:30:39 +01:00
|
|
|
|
2006-11-12 00:10:20 +01:00
|
|
|
|
|
|
|
|
/* If after all this is an unspecified case, then get the
|
|
|
|
|
input from the synchronous output. Note that we know
|
2011-03-04 22:52:08 +01:00
|
|
|
by design that there is no relevant default or accum
|
2006-11-12 00:10:20 +01:00
|
|
|
input to use here, as those cases are handled above. */
|
|
|
|
|
if (statement_map[item] == 0) {
|
2006-01-01 02:30:39 +01:00
|
|
|
|
|
|
|
|
for (unsigned idx=0; idx < mux->width(); idx += 1)
|
|
|
|
|
connect(mux->pin_Data(idx,item), nex_map->pin(idx));
|
|
|
|
|
|
2006-11-12 00:10:20 +01:00
|
|
|
continue;
|
|
|
|
|
}
|
2006-06-26 02:05:46 +02:00
|
|
|
|
2006-11-12 00:10:20 +01:00
|
|
|
|
|
|
|
|
/* Synthesize this specified case. The synth_async will
|
|
|
|
|
connect all the output bits it knows how to the sig net. */
|
|
|
|
|
statement_map[item]->synth_async(des, scope, sync_flag,
|
2010-12-29 21:33:08 +01:00
|
|
|
nex_ff, nex_map, sig, accum,
|
|
|
|
|
latch_inferred, gsig);
|
2006-11-12 00:10:20 +01:00
|
|
|
|
|
|
|
|
for (unsigned idx = 0 ; idx < mux->width() ; idx += 1) {
|
|
|
|
|
if (sig->pin(idx).is_linked())
|
|
|
|
|
connect(mux->pin_Data(idx,item), sig->pin(idx));
|
|
|
|
|
else if (accum->pin(idx).is_linked())
|
|
|
|
|
connect(mux->pin_Data(idx,item), accum->pin(idx));
|
|
|
|
|
else if (sync_flag)
|
|
|
|
|
connect(mux->pin_Data(idx,item), nex_map->pin(idx));
|
|
|
|
|
else {
|
|
|
|
|
/* No likely input for this bit. So
|
|
|
|
|
leave it. The connectivity test
|
|
|
|
|
below will determine if this is an
|
|
|
|
|
error or not. */
|
2005-11-13 23:28:48 +01:00
|
|
|
}
|
|
|
|
|
}
|
2002-07-08 00:32:15 +02:00
|
|
|
}
|
2004-10-04 03:10:51 +02:00
|
|
|
|
2006-06-26 02:05:46 +02:00
|
|
|
/* Input connectivity check. */
|
|
|
|
|
|
|
|
|
|
for (unsigned wdx = 0 ; wdx < mux->width() ; wdx += 1) {
|
|
|
|
|
unsigned linked_count = 0;
|
|
|
|
|
unsigned last_linked = 0;
|
|
|
|
|
for (unsigned item = 0 ; item < (1U<<sel_pins) ; item += 1) {
|
|
|
|
|
if (mux->pin_Data(wdx,item).is_linked()) {
|
|
|
|
|
linked_count += 1;
|
|
|
|
|
last_linked = item;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (linked_count == (1U<<sel_pins))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
/* If we find a single input is connected to the mux,
|
|
|
|
|
then we can guess that this is probably a case of an
|
|
|
|
|
internal value that is not really an output. In that
|
|
|
|
|
case, repeat the connection to all the inputs so that
|
|
|
|
|
it consistently follows the expression that feeds it,
|
|
|
|
|
no matter what the select.
|
|
|
|
|
|
|
|
|
|
NOTE: Perhaps it would be better th reduce the mux
|
|
|
|
|
width by one and connect this through? */
|
|
|
|
|
if (linked_count==1) {
|
|
|
|
|
for (unsigned item = 0; item < (1U<<sel_pins); item += 1) {
|
|
|
|
|
if (item == last_linked)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
connect(mux->pin_Data(wdx,item),
|
|
|
|
|
mux->pin_Data(wdx,last_linked));
|
|
|
|
|
}
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Strange connection pattern. Error message. */
|
2006-07-02 02:50:15 +02:00
|
|
|
if (return_flag != false) {
|
|
|
|
|
cerr << get_line()
|
|
|
|
|
<< ": error: case " << last_linked << " statement"
|
|
|
|
|
<< " does not assign expected outputs." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return_flag = false;
|
|
|
|
|
}
|
2006-06-26 02:05:46 +02:00
|
|
|
}
|
|
|
|
|
|
2003-03-25 05:04:29 +01:00
|
|
|
delete[]statement_map;
|
2002-07-08 00:32:15 +02:00
|
|
|
des->add_node(mux);
|
|
|
|
|
|
2005-11-13 23:28:48 +01:00
|
|
|
return return_flag;
|
2002-07-08 00:32:15 +02:00
|
|
|
}
|
|
|
|
|
|
2006-01-21 22:42:31 +01:00
|
|
|
bool NetCase::synth_async_1hot_(Design*des, NetScope*scope, bool sync_flag,
|
2010-12-29 21:33:08 +01:00
|
|
|
struct sync_accounting_cell*nex_ff,
|
|
|
|
|
NetNet*nex_map, NetNet*nex_out, NetNet*accum,
|
|
|
|
|
NetNet*esig, unsigned hot_items,
|
|
|
|
|
bool&latch_inferred, NetNet*gsig)
|
2006-01-21 22:42:31 +01:00
|
|
|
{
|
|
|
|
|
unsigned sel_pins = hot_items;
|
|
|
|
|
|
|
|
|
|
NetMux*mux = new NetMux(scope, scope->local_symbol(),
|
|
|
|
|
nex_out->pin_count(),
|
|
|
|
|
1U << sel_pins, sel_pins);
|
|
|
|
|
mux->set_line(*this);
|
|
|
|
|
|
|
|
|
|
/* Hook up the output of the mux to the mapped output pins. */
|
|
|
|
|
for (unsigned idx = 0 ; idx < mux->width() ; idx += 1)
|
|
|
|
|
connect(nex_out->pin(idx), mux->pin_Result(idx));
|
|
|
|
|
|
|
|
|
|
NetNet*tmps = new NetNet(scope, scope->local_symbol(),
|
|
|
|
|
NetNet::WIRE, sel_pins);
|
|
|
|
|
for (unsigned idx = 0 ; idx < sel_pins ; idx += 1)
|
|
|
|
|
connect(tmps->pin(idx), mux->pin_Sel(idx));
|
|
|
|
|
|
|
|
|
|
NetProc*default_statement = 0;
|
|
|
|
|
unsigned use_item = 0;
|
|
|
|
|
for (unsigned item = 0 ; item < nitems_ ; item += 1) {
|
|
|
|
|
if (items_[item].guard == 0) {
|
|
|
|
|
default_statement = items_[item].statement;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NetNet*tmp1 = items_[item].guard->synthesize(des);
|
|
|
|
|
assert(tmp1);
|
|
|
|
|
|
|
|
|
|
NetLogic*reduc = new NetLogic(scope, scope->local_symbol(),
|
|
|
|
|
1 + esig->pin_count(),
|
|
|
|
|
NetLogic::AND);
|
|
|
|
|
des->add_node(reduc);
|
|
|
|
|
|
|
|
|
|
tmps = new NetNet(scope, scope->local_symbol(),
|
|
|
|
|
NetNet::WIRE, esig->pin_count());
|
|
|
|
|
for (unsigned idx = 0 ; idx < tmps->pin_count() ; idx += 1)
|
|
|
|
|
connect(tmps->pin(idx), reduc->pin(idx+1));
|
|
|
|
|
|
|
|
|
|
assert(tmp1->pin_count() == esig->pin_count());
|
|
|
|
|
for (unsigned idx = 0 ; idx < tmp1->pin_count() ; idx += 1) {
|
|
|
|
|
NetCaseCmp*cmp = new NetCaseCmp(scope,scope->local_symbol());
|
|
|
|
|
des->add_node(cmp);
|
|
|
|
|
connect(cmp->pin(0), reduc->pin(1+idx));
|
|
|
|
|
connect(cmp->pin(1), esig->pin(idx));
|
|
|
|
|
connect(cmp->pin(2), tmp1->pin(idx));
|
|
|
|
|
}
|
|
|
|
|
|
2006-01-22 01:13:59 +01:00
|
|
|
connect(mux->pin_Sel(use_item), reduc->pin(0));
|
2006-01-21 22:42:31 +01:00
|
|
|
|
|
|
|
|
NetNet*item_sig = new NetNet(scope, scope->local_symbol(),
|
2010-12-29 21:33:08 +01:00
|
|
|
NetNet::WIRE, nex_map->pin_count());
|
2006-01-21 22:42:31 +01:00
|
|
|
assert(items_[item].statement);
|
2006-03-27 01:09:21 +02:00
|
|
|
items_[item].statement->synth_async(des, scope, sync_flag, nex_ff,
|
2010-12-29 21:33:08 +01:00
|
|
|
nex_map, item_sig, accum,
|
|
|
|
|
latch_inferred, gsig);
|
2006-01-21 22:42:31 +01:00
|
|
|
for (unsigned idx = 0 ; idx < item_sig->pin_count() ; idx += 1)
|
|
|
|
|
connect(mux->pin_Data(idx, 1<<use_item), item_sig->pin(idx));
|
|
|
|
|
|
|
|
|
|
use_item += 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
assert(use_item == hot_items);
|
|
|
|
|
|
|
|
|
|
/* Set up a default default_sig that uses the accumulated
|
|
|
|
|
input pins. This binding is suppressed by an actual default
|
|
|
|
|
statement if one exists. */
|
|
|
|
|
NetNet*default_sig = 0;
|
|
|
|
|
if (default_statement) {
|
|
|
|
|
default_sig = new NetNet(scope, scope->local_symbol(),
|
2010-12-29 21:33:08 +01:00
|
|
|
NetNet::WIRE, nex_map->pin_count());
|
2006-03-27 01:09:21 +02:00
|
|
|
default_statement->synth_async(des, scope, sync_flag, nex_ff,
|
2010-12-29 21:33:08 +01:00
|
|
|
nex_map, default_sig, accum,
|
|
|
|
|
latch_inferred, gsig);
|
2006-01-21 22:42:31 +01:00
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (default_sig == 0 && default_statement == 0) {
|
|
|
|
|
default_sig = accum;
|
|
|
|
|
for (unsigned idx = 0 ; idx < accum->pin_count() ; idx += 1) {
|
|
|
|
|
if (! accum->pin(idx).is_linked()) {
|
|
|
|
|
default_sig = 0;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* No explicit sig, so if this is a synchronous process,
|
|
|
|
|
connect the input to the output. */
|
|
|
|
|
if (default_sig == 0 && sync_flag) {
|
|
|
|
|
default_sig = new NetNet(scope, scope->local_symbol(),
|
|
|
|
|
NetNet::WIRE, nex_map->pin_count());
|
|
|
|
|
for (unsigned idx = 0; idx < default_sig->pin_count() ; idx += 1)
|
|
|
|
|
connect(default_sig->pin(idx), nex_map->pin(idx));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (unsigned item = 0 ; item < (1UL<<sel_pins) ; item += 1) {
|
|
|
|
|
unsigned count_bits = 0;
|
|
|
|
|
for (unsigned idx = 0 ; idx < sel_pins ; idx += 1) {
|
|
|
|
|
if (item & (1<<idx))
|
|
|
|
|
count_bits += 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (count_bits == 1)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
for (unsigned idx = 0 ; idx < mux->width() ; idx += 1)
|
|
|
|
|
connect(mux->pin_Data(idx,item), default_sig->pin(idx));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
des->add_node(mux);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2006-06-01 05:01:48 +02:00
|
|
|
|
2005-12-14 01:54:29 +01:00
|
|
|
/*
|
|
|
|
|
* Handle synthesis for an asynchronous condition statement. If we get
|
|
|
|
|
* here, we know that the CE of a DFF has already been filled, so the
|
2010-12-29 21:33:08 +01:00
|
|
|
* condition expression goes to the select of an asynchronous mux, unless
|
|
|
|
|
* a latch is inferred in which case it goes to the latch's gate input.
|
2005-12-14 01:54:29 +01:00
|
|
|
*/
|
|
|
|
|
bool NetCondit::synth_async(Design*des, NetScope*scope, bool sync_flag,
|
2010-12-29 21:33:08 +01:00
|
|
|
struct sync_accounting_cell*nex_ff,
|
|
|
|
|
NetNet*nex_map, NetNet*nex_out,
|
|
|
|
|
NetNet*accum, bool&latch_inferred, NetNet*gsig)
|
2002-06-30 04:21:31 +02:00
|
|
|
{
|
2006-08-23 06:08:19 +02:00
|
|
|
/* Detect the special case that this is a nul-effect (for
|
|
|
|
|
synthesis) statement. This happens, for example, for code
|
|
|
|
|
like this: if (foo) $display(...); */
|
|
|
|
|
if (nex_out->pin_count() == 0) {
|
|
|
|
|
if (debug_synth) {
|
|
|
|
|
cerr << get_line() << ": debug: Skip synthesis of "
|
|
|
|
|
<< "Condit statement with null effect." << endl;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2002-07-01 02:54:21 +02:00
|
|
|
NetNet*ssig = expr_->synthesize(des);
|
2006-11-02 03:13:15 +01:00
|
|
|
if (ssig == 0) {
|
|
|
|
|
if (debug_synth) {
|
|
|
|
|
cerr << get_line() << ": debug: Unable to synthesize "
|
|
|
|
|
<< "condition expression." << endl;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2002-07-01 02:54:21 +02:00
|
|
|
assert(ssig);
|
|
|
|
|
|
2005-08-22 03:00:41 +02:00
|
|
|
/* Use the accumulated input net as a default input for
|
|
|
|
|
covering a missing clause, except that if I find portions
|
|
|
|
|
are unconnected, then give up on that idea. */
|
|
|
|
|
NetNet*default_sig = accum;
|
|
|
|
|
for (unsigned idx = 0 ; idx < default_sig->pin_count() ; idx += 1) {
|
|
|
|
|
if (! default_sig->pin(idx).is_linked()) {
|
|
|
|
|
default_sig = 0;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2003-12-17 17:52:39 +01:00
|
|
|
}
|
2005-08-22 03:00:41 +02:00
|
|
|
|
2010-12-29 21:33:08 +01:00
|
|
|
/* At least one of the clauses must have content. */
|
2005-12-14 01:54:29 +01:00
|
|
|
assert(if_ != 0 || else_ != 0);
|
|
|
|
|
|
2010-12-29 21:33:08 +01:00
|
|
|
/* Latches cannot nest! */
|
|
|
|
|
assert(!latch_inferred);
|
2007-05-30 19:48:53 +02:00
|
|
|
|
2005-12-14 01:54:29 +01:00
|
|
|
/* If there is no default_sig, and if this is a fully
|
|
|
|
|
asynchronous process (nex_map is not a synchronous output)
|
2010-12-29 21:33:08 +01:00
|
|
|
then, if a clause is missing, a latch will be inferred.
|
2005-12-14 01:54:29 +01:00
|
|
|
|
|
|
|
|
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. */
|
2010-12-29 21:33:08 +01:00
|
|
|
if (default_sig == 0 && ! sync_flag && (if_ == 0 || else_ == 0)) {
|
|
|
|
|
latch_inferred = true;
|
2002-10-20 21:19:37 +02:00
|
|
|
}
|
2010-12-29 21:33:08 +01:00
|
|
|
bool my_latch_inferred = latch_inferred;
|
2002-10-20 21:19:37 +02:00
|
|
|
|
2003-03-06 01:28:41 +01:00
|
|
|
NetNet*asig = new NetNet(scope, scope->local_symbol(),
|
2002-07-01 02:54:21 +02:00
|
|
|
NetNet::WIRE, nex_map->pin_count());
|
|
|
|
|
asig->local_flag(true);
|
|
|
|
|
|
2005-08-22 03:00:41 +02:00
|
|
|
if (if_ == 0) {
|
2005-12-14 01:54:29 +01:00
|
|
|
/* If the if clause is missing, then take the clause to
|
2010-12-29 21:33:08 +01:00
|
|
|
be an assignment from the default input. If there is
|
|
|
|
|
no default input and a latch is not inferred, then
|
|
|
|
|
take the input to be from the output (sync). */
|
2005-12-14 01:54:29 +01:00
|
|
|
if (default_sig) {
|
|
|
|
|
for (unsigned idx = 0 ; idx < asig->pin_count() ; idx += 1)
|
|
|
|
|
connect(asig->pin(idx), default_sig->pin(idx));
|
2010-12-29 21:33:08 +01:00
|
|
|
} else if (latch_inferred) {
|
2007-05-30 19:48:53 +02:00
|
|
|
delete asig ;
|
2010-12-29 21:33:08 +01:00
|
|
|
asig = 0;
|
|
|
|
|
} else {
|
|
|
|
|
assert(sync_flag);
|
2007-05-30 19:48:53 +02:00
|
|
|
|
2005-12-14 01:54:29 +01:00
|
|
|
for (unsigned idx = 0 ; idx < asig->pin_count() ; idx += 1)
|
|
|
|
|
connect(asig->pin(idx), nex_map->pin(idx));
|
|
|
|
|
}
|
2005-08-22 03:00:41 +02:00
|
|
|
|
|
|
|
|
} else {
|
2006-03-27 01:09:21 +02:00
|
|
|
bool flag = if_->synth_async(des, scope, sync_flag, nex_ff,
|
2010-12-29 21:33:08 +01:00
|
|
|
nex_map, asig, accum, latch_inferred,
|
|
|
|
|
ssig);
|
2005-08-22 03:00:41 +02:00
|
|
|
if (!flag) {
|
|
|
|
|
delete asig;
|
|
|
|
|
cerr << get_line() << ": error: Asynchronous if statement"
|
|
|
|
|
<< " true clause failed to synthesize." << endl;
|
2006-03-18 19:43:21 +01:00
|
|
|
des->errors += 1;
|
2005-08-22 03:00:41 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
2003-12-20 01:59:31 +01:00
|
|
|
}
|
2004-10-04 03:10:51 +02:00
|
|
|
|
2003-03-06 01:28:41 +01:00
|
|
|
NetNet*bsig = new NetNet(scope, scope->local_symbol(),
|
2002-07-01 02:54:21 +02:00
|
|
|
NetNet::WIRE, nex_map->pin_count());
|
|
|
|
|
bsig->local_flag(true);
|
|
|
|
|
|
2005-08-22 03:00:41 +02:00
|
|
|
if (else_ == 0) {
|
2010-12-29 21:33:08 +01:00
|
|
|
/* If the else clause is missing, then take the clause to
|
|
|
|
|
be an assignment from the default input. If there is
|
|
|
|
|
no default input and a latch is not inferred, then
|
|
|
|
|
take the input to be from the output (sync). */
|
2005-12-14 01:54:29 +01:00
|
|
|
if (default_sig) {
|
|
|
|
|
for (unsigned idx = 0 ; idx < asig->pin_count() ; idx += 1)
|
|
|
|
|
connect(bsig->pin(idx), default_sig->pin(idx));
|
2010-12-29 21:33:08 +01:00
|
|
|
} else if (latch_inferred) {
|
2007-05-30 19:48:53 +02:00
|
|
|
delete bsig;
|
2010-12-29 21:33:08 +01:00
|
|
|
bsig = 0;
|
|
|
|
|
} else {
|
|
|
|
|
assert(sync_flag);
|
2007-05-30 19:48:53 +02:00
|
|
|
|
2005-12-14 01:54:29 +01:00
|
|
|
for (unsigned idx = 0 ; idx < asig->pin_count() ; idx += 1)
|
|
|
|
|
connect(bsig->pin(idx), nex_map->pin(idx));
|
|
|
|
|
}
|
2005-08-22 03:00:41 +02:00
|
|
|
} else {
|
2006-03-27 01:09:21 +02:00
|
|
|
bool flag = else_->synth_async(des, scope, sync_flag, nex_ff,
|
2010-12-29 21:33:08 +01:00
|
|
|
nex_map, bsig, accum,
|
|
|
|
|
latch_inferred, ssig);
|
2005-08-22 03:00:41 +02:00
|
|
|
if (!flag) {
|
|
|
|
|
delete asig;
|
|
|
|
|
delete bsig;
|
|
|
|
|
cerr << get_line() << ": error: Asynchronous if statement"
|
|
|
|
|
<< " else clause failed to synthesize." << endl;
|
2006-03-18 19:43:21 +01:00
|
|
|
des->errors += 1;
|
2005-08-22 03:00:41 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
2003-12-20 01:59:31 +01:00
|
|
|
}
|
2002-07-01 02:54:21 +02:00
|
|
|
|
2010-12-29 21:33:08 +01:00
|
|
|
/* If we have inferred a latch then connect the appropriate signal to
|
|
|
|
|
* the output. */
|
|
|
|
|
if (latch_inferred) {
|
|
|
|
|
if (!my_latch_inferred) {
|
|
|
|
|
cerr << get_line() << ": sorry: D-latch asynchronous "
|
|
|
|
|
<< "set/clear synthesis is not currently supported."
|
|
|
|
|
<< endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return false;
|
2006-06-01 05:01:48 +02:00
|
|
|
}
|
2010-12-29 21:33:08 +01:00
|
|
|
if (asig) {
|
|
|
|
|
assert(bsig == 0);
|
|
|
|
|
asig->set_line( *this );
|
|
|
|
|
for (unsigned idx = 0; idx < nex_out->pin_count(); idx += 1)
|
|
|
|
|
connect(nex_out->pin(idx), asig->pin(idx));
|
|
|
|
|
} else {
|
|
|
|
|
assert(bsig);
|
|
|
|
|
bsig->set_line( *this );
|
|
|
|
|
for (unsigned idx = 0; idx < nex_out->pin_count(); idx += 1)
|
|
|
|
|
connect(nex_out->pin(idx), bsig->pin(idx));
|
2007-05-30 19:48:53 +02:00
|
|
|
}
|
2010-12-29 21:33:08 +01:00
|
|
|
} else {
|
2007-05-30 19:48:53 +02:00
|
|
|
unsigned mux_width = 0;
|
2006-08-23 06:08:19 +02:00
|
|
|
|
2007-05-30 19:48:53 +02:00
|
|
|
/* 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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 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)
|
2006-08-23 06:08:19 +02:00
|
|
|
connect(mux->pin_Result(idx), mux_sig->pin(idx));
|
|
|
|
|
|
2007-05-30 19:48:53 +02:00
|
|
|
if (debug_synth) {
|
2006-07-23 21:42:33 +02:00
|
|
|
cerr << get_line() << ": debug: Condit synth to MUX "
|
|
|
|
|
<< " width=" << mux_width
|
|
|
|
|
<< " sel_width=1" << endl;
|
2007-05-30 19:48:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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) {
|
2006-06-01 05:01:48 +02:00
|
|
|
int flag = 0;
|
2005-11-16 01:38:26 +01:00
|
|
|
if (asig->pin(idx).is_linked())
|
2007-05-30 19:48:53 +02:00
|
|
|
flag |= 0100;
|
2005-11-16 01:38:26 +01:00
|
|
|
if (bsig->pin(idx).is_linked())
|
2007-05-30 19:48:53 +02:00
|
|
|
flag |= 0010;
|
2006-06-01 05:01:48 +02:00
|
|
|
if (accum->pin(idx).is_linked())
|
2007-05-30 19:48:53 +02:00
|
|
|
flag |= 0001;
|
2006-07-23 21:42:33 +02:00
|
|
|
|
2006-06-01 05:01:48 +02:00
|
|
|
switch (flag) {
|
2007-05-30 19:48:53 +02:00
|
|
|
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 {
|
2006-06-01 05:01:48 +02:00
|
|
|
#if 0
|
2007-05-30 19:48:53 +02:00
|
|
|
cerr << get_line()
|
|
|
|
|
<< ": error: Condition false clause "
|
|
|
|
|
<< "does not assign expected outputs." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return_flag = false;
|
2006-06-01 05:01:48 +02:00
|
|
|
#else
|
2010-12-29 21:33:08 +01:00
|
|
|
/* Assume that asig is used internally. Note the
|
|
|
|
|
* similarity to the latch inferred code. */
|
2007-05-30 19:48:53 +02:00
|
|
|
connect(nex_out->pin(idx), asig->pin(idx));
|
2006-06-01 05:01:48 +02:00
|
|
|
#endif
|
2007-05-30 19:48:53 +02:00
|
|
|
}
|
|
|
|
|
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 {
|
2006-06-01 05:01:48 +02:00
|
|
|
#if 0
|
2007-05-30 19:48:53 +02:00
|
|
|
cerr << get_line()
|
|
|
|
|
<< ": error: Condition true clause "
|
|
|
|
|
<< "does not assign expected outputs." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return_flag = false;
|
2006-06-01 05:01:48 +02:00
|
|
|
#else
|
2010-12-29 21:33:08 +01:00
|
|
|
/* Assume that asig is used internally. Note the
|
|
|
|
|
* similarity to the latch inferred code. */
|
2007-05-30 19:48:53 +02:00
|
|
|
connect(nex_out->pin(idx), bsig->pin(idx));
|
2006-06-01 05:01:48 +02:00
|
|
|
#endif
|
2007-05-30 19:48:53 +02:00
|
|
|
}
|
|
|
|
|
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());
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
des->add_node(mux);
|
2010-12-29 21:33:08 +01:00
|
|
|
}
|
2002-07-01 02:54:21 +02:00
|
|
|
|
|
|
|
|
return true;
|
2002-06-30 04:21:31 +02:00
|
|
|
}
|
|
|
|
|
|
2005-12-14 01:54:29 +01:00
|
|
|
bool NetEvWait::synth_async(Design*des, NetScope*scope, bool sync_flag,
|
2010-12-29 21:33:08 +01:00
|
|
|
sync_accounting_cell*nex_ff,
|
|
|
|
|
NetNet*nex_map, NetNet*nex_out, NetNet*accum_in,
|
|
|
|
|
bool&latch_inferred, NetNet*gsig)
|
2002-06-30 04:21:31 +02:00
|
|
|
{
|
2006-03-27 01:09:21 +02:00
|
|
|
bool flag = statement_->synth_async(des, scope, sync_flag, nex_ff,
|
2010-12-29 21:33:08 +01:00
|
|
|
nex_map, nex_out, accum_in,
|
|
|
|
|
latch_inferred, gsig);
|
2003-12-17 17:52:39 +01:00
|
|
|
return flag;
|
2002-06-30 04:21:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool NetProcTop::synth_async(Design*des)
|
|
|
|
|
{
|
2002-07-01 02:54:21 +02:00
|
|
|
NexusSet nex_set;
|
|
|
|
|
statement_->nex_output(nex_set);
|
|
|
|
|
|
2004-02-18 18:11:54 +01:00
|
|
|
const perm_string tmp1 = perm_string::literal("tmp");
|
|
|
|
|
NetNet*nex_out = new NetNet(scope(), tmp1, NetNet::WIRE,
|
2002-07-01 02:54:21 +02:00
|
|
|
nex_set.count());
|
|
|
|
|
for (unsigned idx = 0 ; idx < nex_out->pin_count() ; idx += 1)
|
|
|
|
|
connect(nex_set[idx], nex_out->pin(idx));
|
|
|
|
|
|
2006-03-27 01:09:21 +02:00
|
|
|
bool flag = statement_->synth_async_noaccum(des, scope(), false, 0,
|
|
|
|
|
nex_out, nex_out);
|
2002-07-01 02:54:21 +02:00
|
|
|
|
|
|
|
|
delete nex_out;
|
|
|
|
|
return flag;
|
2002-06-30 04:21:31 +02:00
|
|
|
}
|
|
|
|
|
|
2005-12-19 02:13:47 +01:00
|
|
|
static bool merge_ff_slices(NetFF*ff1, unsigned idx1,
|
|
|
|
|
NetFF*ff2, unsigned idx2)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
/* If the Aset inputs are connected, and not to each other
|
|
|
|
|
(possible since pre-existing Asets are carried forwards)
|
|
|
|
|
then there is a conflict. */
|
|
|
|
|
if (ff1->pin_Aset().is_linked()
|
|
|
|
|
&& ff2->pin_Aset().is_linked()
|
|
|
|
|
&& ! ff1->pin_Aset().is_linked(ff2->pin_Aset())) {
|
|
|
|
|
cerr << ff2->get_line() << ": error: "
|
|
|
|
|
<< "DFF Aset conflicts with "
|
|
|
|
|
<< ff1->get_line() << "." << endl;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ff1->pin_Aclr().is_linked()
|
|
|
|
|
&& ff2->pin_Aclr().is_linked()
|
|
|
|
|
&& ! ff1->pin_Aclr().is_linked(ff2->pin_Aclr())) {
|
|
|
|
|
cerr << ff2->get_line() << ": error: "
|
|
|
|
|
<< "DFF Aclr conflicts with "
|
|
|
|
|
<< ff1->get_line() << "." << endl;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2006-01-18 02:23:23 +01:00
|
|
|
#if XXXX
|
2005-12-19 02:13:47 +01:00
|
|
|
if (ff2->pin_Data(idx2).is_linked())
|
|
|
|
|
connect(ff1->pin_Data(idx1), ff2->pin_Data(idx2));
|
|
|
|
|
if (ff2->pin_Aset().is_linked())
|
|
|
|
|
connect(ff1->pin_Aset(), ff2->pin_Aset());
|
|
|
|
|
if (ff2->pin_Aclr().is_linked())
|
|
|
|
|
connect(ff1->pin_Aclr(), ff2->pin_Aclr());
|
2006-01-18 02:23:23 +01:00
|
|
|
if (ff2->pin_Sclr().is_linked())
|
|
|
|
|
connect(ff1->pin_Sclr(), ff2->pin_Sclr());
|
|
|
|
|
if (ff2->pin_Sset().is_linked())
|
|
|
|
|
connect(ff1->pin_Sset(), ff2->pin_Sset());
|
|
|
|
|
if (ff2->pin_Clock().is_linked())
|
|
|
|
|
connect(ff1->pin_Clock(), ff2->pin_Clock());
|
|
|
|
|
#endif
|
|
|
|
|
if (ff2->pin_Enable().is_linked())
|
|
|
|
|
connect(ff1->pin_Enable(),ff2->pin_Enable());
|
2005-12-19 02:13:47 +01:00
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2006-02-19 01:11:31 +01:00
|
|
|
bool NetAssignBase::synth_sync(Design*des, NetScope*scope,
|
|
|
|
|
struct sync_accounting_cell*nex_ff,
|
|
|
|
|
NetNet*nex_map, NetNet*nex_out,
|
|
|
|
|
const svector<NetEvProbe*>&events)
|
|
|
|
|
{
|
|
|
|
|
unsigned count_lval = 0;
|
|
|
|
|
NetAssign_*demux = 0;
|
|
|
|
|
|
|
|
|
|
for (NetAssign_*cur = lval_ ; cur ; cur = cur->more) {
|
|
|
|
|
if (cur->bmux())
|
|
|
|
|
demux = cur;
|
2006-03-12 08:34:16 +01:00
|
|
|
if (cur->mem())
|
|
|
|
|
demux = cur;
|
2006-02-19 01:11:31 +01:00
|
|
|
|
|
|
|
|
count_lval += 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (demux != 0 && count_lval != 1) {
|
|
|
|
|
cerr << get_line() << ": error: Cannot synthesize assignmnents"
|
|
|
|
|
<< "that mix memory and vector assignments." << endl;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* There is no memory address, so resort to async
|
|
|
|
|
assignments. */
|
|
|
|
|
if (demux == 0) {
|
2006-08-08 04:17:48 +02:00
|
|
|
if (debug_synth) {
|
|
|
|
|
cerr << get_line() << ": debug: Looks like simple assign "
|
|
|
|
|
<< "to a synchronous vector." << endl;
|
|
|
|
|
}
|
|
|
|
|
|
2006-02-19 01:11:31 +01:00
|
|
|
/* Synthesize the input to the DFF. */
|
2006-03-27 01:09:21 +02:00
|
|
|
return synth_async_noaccum(des, scope, true, nex_ff,
|
|
|
|
|
nex_map, nex_out);
|
2006-02-19 01:11:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
assert(demux->bmux() != 0);
|
|
|
|
|
|
2006-04-10 05:43:39 +02:00
|
|
|
/* Obviously, we need the r-value synthesized to connect it up. */
|
|
|
|
|
NetNet*rsig = rval_->synthesize(des);
|
|
|
|
|
assert(rsig->pin_count() == lval_->lwidth());
|
|
|
|
|
|
|
|
|
|
/* Detect and handle the special case that the l-value is an
|
|
|
|
|
assign to a constant bit. We don't need a demux in that
|
|
|
|
|
case. */
|
|
|
|
|
if (demux->mem() && dynamic_cast<NetEConst*>(demux->bmux())) {
|
2006-08-08 04:17:48 +02:00
|
|
|
if (debug_synth) {
|
|
|
|
|
cerr << get_line() << ": debug: Looks like an assign "
|
|
|
|
|
<< "to a fixed memory word." << endl;
|
|
|
|
|
}
|
|
|
|
|
|
2006-04-10 05:43:39 +02:00
|
|
|
NetMemory*lmem = demux->mem();
|
|
|
|
|
NetNet*msig = lmem->explode_to_reg();
|
2006-04-16 21:26:37 +02:00
|
|
|
demux->incr_mem_lref();
|
2006-04-10 05:43:39 +02:00
|
|
|
|
|
|
|
|
NetEConst*ae = dynamic_cast<NetEConst*>(demux->bmux());
|
|
|
|
|
long adr = ae->value().as_long();
|
|
|
|
|
adr = lmem->index_to_address(adr) * lmem->width();
|
|
|
|
|
|
2006-08-08 04:17:48 +02:00
|
|
|
struct nexus_map_t*nex_map_idx = make_nexus_index(nex_map);
|
|
|
|
|
|
2006-04-10 05:43:39 +02:00
|
|
|
for (unsigned idx = 0 ; idx < demux->lwidth() ; idx += 1) {
|
|
|
|
|
unsigned off = adr+idx;
|
2006-08-08 04:17:48 +02:00
|
|
|
int tmp = map_nexus_in_index(nex_map_idx,
|
|
|
|
|
nex_map->pin_count(),
|
|
|
|
|
msig->pin(off).nexus());
|
|
|
|
|
assert(tmp >= 0);
|
|
|
|
|
unsigned ptr = tmp;
|
2006-04-10 05:43:39 +02:00
|
|
|
connect(nex_out->pin(ptr), rsig->pin(idx));
|
|
|
|
|
}
|
2006-08-08 04:17:48 +02:00
|
|
|
|
|
|
|
|
delete[]nex_map_idx;
|
|
|
|
|
|
2006-04-10 05:43:39 +02:00
|
|
|
lval_->turn_sig_to_wire_on_release();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2006-08-08 04:17:48 +02:00
|
|
|
if (debug_synth) {
|
|
|
|
|
cerr << get_line() << ": debug: Looks like an assign "
|
|
|
|
|
<< "to an addressed memory word." << endl;
|
|
|
|
|
}
|
|
|
|
|
|
2006-04-10 05:43:39 +02:00
|
|
|
/* We also need the address (now known to be non-constant)
|
|
|
|
|
synthesized and connected to a decoder. */
|
2006-02-19 01:11:31 +01:00
|
|
|
NetNet*adr = demux->bmux()->synthesize(des);
|
|
|
|
|
NetDecode*dq = new NetDecode(scope, scope->local_symbol(),
|
2006-03-12 08:34:16 +01:00
|
|
|
nex_ff[0].ff, adr->pin_count(),
|
|
|
|
|
lval_->lwidth());
|
2006-02-19 01:11:31 +01:00
|
|
|
des->add_node(dq);
|
|
|
|
|
dq->set_line(*this);
|
|
|
|
|
|
|
|
|
|
for (unsigned idx = 0 ; idx < adr->pin_count() ; idx += 1)
|
|
|
|
|
connect(dq->pin_Address(idx), adr->pin(idx));
|
|
|
|
|
|
|
|
|
|
for (unsigned idx = 0 ; idx < nex_ff[0].ff->width() ; idx += 1)
|
2006-03-12 08:34:16 +01:00
|
|
|
connect(nex_ff[0].ff->pin_Data(idx), rsig->pin(idx%lval_->lwidth()));
|
2006-02-19 01:11:31 +01:00
|
|
|
|
2006-03-12 08:34:16 +01:00
|
|
|
if (lval_->mem()) {
|
|
|
|
|
NetNet*exp = lval_->mem()->reg_from_explode();
|
|
|
|
|
assert(exp);
|
2006-04-16 21:26:37 +02:00
|
|
|
lval_->incr_mem_lref();
|
2006-03-12 08:34:16 +01:00
|
|
|
}
|
2006-02-19 01:11:31 +01:00
|
|
|
lval_->turn_sig_to_wire_on_release();
|
2006-08-08 04:17:48 +02:00
|
|
|
|
|
|
|
|
if (debug_synth) {
|
|
|
|
|
cerr << get_line() << ": debug: Synchronous assign done." << endl;
|
|
|
|
|
}
|
|
|
|
|
|
2006-02-19 01:11:31 +01:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2002-10-21 03:42:08 +02:00
|
|
|
/*
|
|
|
|
|
* 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
|
|
|
|
|
* <statement1>
|
|
|
|
|
* <statement2>
|
|
|
|
|
* ...
|
|
|
|
|
* end
|
|
|
|
|
*
|
|
|
|
|
* This needs to be split into a DFF bank for each statement, because
|
|
|
|
|
* the statements may each infer different reset and enable signals.
|
|
|
|
|
*/
|
2006-01-18 02:23:23 +01:00
|
|
|
bool NetBlock::synth_sync(Design*des, NetScope*scope,
|
|
|
|
|
struct sync_accounting_cell*nex_ff,
|
2005-12-14 01:54:29 +01:00
|
|
|
NetNet*nex_map, NetNet*nex_out,
|
|
|
|
|
const svector<NetEvProbe*>&events_in)
|
2002-10-21 03:42:08 +02:00
|
|
|
{
|
2005-12-19 02:13:47 +01:00
|
|
|
/* Do nothing for empty blocks. */
|
|
|
|
|
if (last_ == 0)
|
2002-10-21 03:42:08 +02:00
|
|
|
return true;
|
|
|
|
|
|
2006-08-08 04:17:48 +02:00
|
|
|
if (debug_synth) {
|
|
|
|
|
cerr << get_line() << ": debug: Start synthesis of block" << endl;
|
|
|
|
|
}
|
|
|
|
|
|
2006-01-18 02:23:23 +01:00
|
|
|
/* Assert that this region still represents a single DFF. */
|
|
|
|
|
for (unsigned idx = 1 ; idx < nex_out->pin_count() ; idx += 1) {
|
|
|
|
|
assert(nex_ff[0].ff == nex_ff[idx].ff);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NetFF*ff = nex_ff[0].ff;
|
|
|
|
|
assert(ff->width() == nex_out->pin_count());
|
2006-07-23 21:42:33 +02:00
|
|
|
|
2006-01-18 02:23:23 +01:00
|
|
|
unsigned block_width = nex_out->pin_count();
|
|
|
|
|
|
2006-07-23 21:42:33 +02:00
|
|
|
verinum tmp_aset = ff->aset_value();
|
|
|
|
|
verinum tmp_sset = ff->sset_value();
|
|
|
|
|
|
2002-10-21 03:42:08 +02:00
|
|
|
bool flag = true;
|
|
|
|
|
|
2004-02-18 18:11:54 +01:00
|
|
|
const perm_string tmp1 = perm_string::literal("tmp1");
|
|
|
|
|
const perm_string tmp2 = perm_string::literal("tmp2");
|
|
|
|
|
|
2002-10-21 03:42:08 +02:00
|
|
|
NetProc*cur = last_;
|
|
|
|
|
do {
|
|
|
|
|
cur = cur->next_;
|
|
|
|
|
|
2006-08-08 04:17:48 +02:00
|
|
|
if (debug_synth) {
|
|
|
|
|
cerr << get_line() << ": debug: "
|
|
|
|
|
<< "Collect information for statement at "
|
|
|
|
|
<< cur->get_line() << endl;
|
|
|
|
|
}
|
|
|
|
|
|
2002-10-21 03:42:08 +02:00
|
|
|
/* Create a temporary nex_map for the substatement. */
|
|
|
|
|
NexusSet tmp_set;
|
|
|
|
|
cur->nex_output(tmp_set);
|
2004-02-18 18:11:54 +01:00
|
|
|
NetNet*tmp_map = new NetNet(scope, tmp1, NetNet::WIRE,
|
2002-10-21 03:42:08 +02:00
|
|
|
tmp_set.count());
|
|
|
|
|
for (unsigned idx = 0 ; idx < tmp_map->pin_count() ; idx += 1)
|
|
|
|
|
connect(tmp_set[idx], tmp_map->pin(idx));
|
|
|
|
|
|
2003-08-14 04:41:05 +02:00
|
|
|
/* NOTE: After this point, tmp_set should not be used as
|
|
|
|
|
the various functions I call do a lot of connecting,
|
|
|
|
|
and the nexa in the tmp_set may get realloced. Use
|
|
|
|
|
the tmp_map instead. */
|
|
|
|
|
|
2002-10-21 03:42:08 +02:00
|
|
|
/* Create also a temporary net_out to collect the
|
2002-11-09 21:22:57 +01:00
|
|
|
output. The tmp1 and tmp2 map and out sets together
|
|
|
|
|
are used to collect the outputs from the substatement
|
|
|
|
|
for the inputs of the FF bank. */
|
2004-02-18 18:11:54 +01:00
|
|
|
NetNet*tmp_out = new NetNet(scope, tmp2, NetNet::WIRE,
|
2003-08-14 04:41:05 +02:00
|
|
|
tmp_map->pin_count());
|
2002-10-21 03:42:08 +02:00
|
|
|
|
|
|
|
|
/* Create a new DFF to handle this part of the begin-end
|
|
|
|
|
block. Connect this NetFF to the associated pins of
|
2002-10-23 03:45:24 +02:00
|
|
|
the existing wide NetFF device. While I'm at it, also
|
|
|
|
|
copy the aset_value bits for the new ff device. */
|
2004-02-18 18:11:54 +01:00
|
|
|
NetFF*ff2 = new NetFF(scope, scope->local_symbol(),
|
2002-10-21 03:42:08 +02:00
|
|
|
tmp_out->pin_count());
|
2005-12-19 02:13:47 +01:00
|
|
|
ff2->set_line(*cur);
|
2002-10-21 03:42:08 +02:00
|
|
|
des->add_node(ff2);
|
|
|
|
|
|
2006-01-18 02:23:23 +01:00
|
|
|
struct sync_accounting_cell*tmp_ff
|
|
|
|
|
= new struct sync_accounting_cell[ff2->width()];
|
|
|
|
|
|
2006-08-08 04:17:48 +02:00
|
|
|
if (debug_synth) {
|
|
|
|
|
cerr << get_line() << ": debug: "
|
|
|
|
|
<< "Map pins for statement at "
|
|
|
|
|
<< cur->get_line() << endl;
|
|
|
|
|
}
|
|
|
|
|
|
2002-10-23 03:45:24 +02:00
|
|
|
verinum aset_value2 (verinum::V1, ff2->width());
|
2003-08-15 04:23:52 +02:00
|
|
|
verinum sset_value2 (verinum::V1, ff2->width());
|
2006-08-08 04:17:48 +02:00
|
|
|
struct nexus_map_t*nex_map_idx = make_nexus_index(nex_map);
|
|
|
|
|
|
2002-10-21 03:42:08 +02:00
|
|
|
for (unsigned idx = 0 ; idx < ff2->width() ; idx += 1) {
|
2006-08-08 04:17:48 +02:00
|
|
|
int tmp = map_nexus_in_index(nex_map_idx,
|
|
|
|
|
nex_map->pin_count(),
|
|
|
|
|
tmp_map->pin(idx).nexus());
|
|
|
|
|
assert(tmp >= 0);
|
|
|
|
|
unsigned ptr = tmp;
|
2003-08-15 04:23:52 +02:00
|
|
|
|
|
|
|
|
/* Copy the asynch set bit to the new device. */
|
2002-10-23 03:45:24 +02:00
|
|
|
if (ptr < tmp_aset.len())
|
|
|
|
|
aset_value2.set(idx, tmp_aset[ptr]);
|
2002-11-09 21:22:57 +01:00
|
|
|
|
2003-08-15 04:23:52 +02:00
|
|
|
/* Copy the synch set bit to the new device. */
|
|
|
|
|
if (ptr < tmp_sset.len())
|
|
|
|
|
sset_value2.set(idx, tmp_sset[ptr]);
|
|
|
|
|
|
2006-01-18 02:23:23 +01:00
|
|
|
// Connect the tmp_out vector, what the
|
|
|
|
|
// sub-synthesis is linking, to the inputs of this
|
|
|
|
|
// new FF.
|
|
|
|
|
connect(tmp_out->pin(idx), ff2->pin_Data(idx));
|
|
|
|
|
tmp_ff[idx].ff = ff2;
|
|
|
|
|
tmp_ff[idx].pin = idx;
|
|
|
|
|
tmp_ff[idx].proc = cur;
|
2002-10-21 03:42:08 +02:00
|
|
|
}
|
2003-08-15 04:23:52 +02:00
|
|
|
|
2006-08-08 04:17:48 +02:00
|
|
|
delete[]nex_map_idx;
|
|
|
|
|
|
|
|
|
|
if (debug_synth) {
|
|
|
|
|
cerr << get_line() << ": debug: "
|
|
|
|
|
<< "Propagate FF controls for statement at "
|
|
|
|
|
<< cur->get_line() << endl;
|
|
|
|
|
}
|
|
|
|
|
|
2005-12-19 02:13:47 +01:00
|
|
|
/* PUll the non-sliced inputs (clock, set, reset, etc)
|
|
|
|
|
forward to the new FF we are building. */
|
2002-10-21 03:42:08 +02:00
|
|
|
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());
|
2003-08-15 04:23:52 +02:00
|
|
|
if (ff->pin_Sclr().is_linked())
|
|
|
|
|
connect(ff->pin_Sclr(), ff2->pin_Sclr());
|
|
|
|
|
if (ff->pin_Sset().is_linked())
|
|
|
|
|
connect(ff->pin_Sset(), ff2->pin_Sset());
|
2002-10-21 03:42:08 +02:00
|
|
|
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());
|
|
|
|
|
|
2002-10-23 03:45:24 +02:00
|
|
|
/* Remember to store the aset value into the new FF. If
|
|
|
|
|
this leads to an Aset value of 0 (and Aclr is not
|
|
|
|
|
otherwise used) then move the Aset input to Aclr. */
|
|
|
|
|
if (tmp_aset.len() == ff->width()) {
|
|
|
|
|
|
2003-04-03 06:30:00 +02:00
|
|
|
if (aset_value2.is_zero()
|
2002-10-23 03:45:24 +02:00
|
|
|
&& ff2->pin_Aset().is_linked()
|
|
|
|
|
&& !ff2->pin_Aclr().is_linked()) {
|
|
|
|
|
|
|
|
|
|
ff2->pin_Aset().unlink();
|
2006-01-18 02:23:23 +01:00
|
|
|
connect(ff2->pin_Aclr(), ff->pin_Aset());
|
2002-10-23 03:45:24 +02:00
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
ff2->aset_value(aset_value2);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2006-04-10 05:43:39 +02:00
|
|
|
if (tmp_sset.len() == ff->width()) {
|
|
|
|
|
|
|
|
|
|
if (sset_value2.is_zero()
|
|
|
|
|
&& ff2->pin_Sset().is_linked()
|
|
|
|
|
&& !ff2->pin_Sclr().is_linked()) {
|
|
|
|
|
|
|
|
|
|
ff2->pin_Sset().unlink();
|
|
|
|
|
connect(ff2->pin_Sclr(), ff->pin_Sset());
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
ff2->sset_value(sset_value2);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2006-08-08 04:17:48 +02:00
|
|
|
if (debug_synth) {
|
|
|
|
|
cerr << get_line() << ": debug: "
|
|
|
|
|
<< "Start substatement synthesis at "
|
|
|
|
|
<< cur->get_line() << endl;
|
|
|
|
|
}
|
|
|
|
|
|
2002-10-21 03:42:08 +02:00
|
|
|
/* Now go on with the synchronous synthesis for this
|
2005-12-31 05:28:14 +01:00
|
|
|
statement of the block. The tmp_map is the output
|
2003-08-14 04:41:05 +02:00
|
|
|
nexa that we expect, and the tmp_out is where we want
|
|
|
|
|
those outputs connected. */
|
2006-01-18 02:23:23 +01:00
|
|
|
bool ok_flag = cur->synth_sync(des, scope,
|
|
|
|
|
tmp_ff, tmp_map, tmp_out,
|
|
|
|
|
events_in);
|
2011-03-04 23:11:28 +01:00
|
|
|
ff2 = 0; // NOTE: The synth_sync may delete ff2.
|
2002-10-21 03:42:08 +02:00
|
|
|
flag = flag && ok_flag;
|
|
|
|
|
|
|
|
|
|
if (ok_flag == false)
|
|
|
|
|
continue;
|
|
|
|
|
|
2006-08-08 04:17:48 +02:00
|
|
|
if (debug_synth) {
|
|
|
|
|
cerr << get_line() << ": debug: "
|
|
|
|
|
<< "Bind block substatement to ff bits." << endl;
|
|
|
|
|
}
|
2002-10-21 03:42:08 +02:00
|
|
|
/* Use the nex_map to link up the output from the
|
2002-11-06 04:22:28 +01:00
|
|
|
substatement to the output of the block as a
|
|
|
|
|
whole. It is occasionally possible to have outputs
|
|
|
|
|
beyond the input set, for example when the l-value of
|
|
|
|
|
an assignment is smaller then the r-value. */
|
2006-08-08 04:17:48 +02:00
|
|
|
|
|
|
|
|
nex_map_idx = make_nexus_index(nex_map);
|
|
|
|
|
|
2002-10-21 03:42:08 +02:00
|
|
|
for (unsigned idx = 0 ; idx < tmp_out->pin_count() ; idx += 1) {
|
2006-01-18 02:23:23 +01:00
|
|
|
ff2 = tmp_ff[idx].ff;
|
|
|
|
|
unsigned ff2_pin = tmp_ff[idx].pin;
|
2006-08-08 04:17:48 +02:00
|
|
|
|
|
|
|
|
int tmp = map_nexus_in_index(nex_map_idx,
|
|
|
|
|
nex_map->pin_count(),
|
|
|
|
|
tmp_map->pin(idx).nexus());
|
|
|
|
|
assert(tmp >= 0);
|
|
|
|
|
unsigned ptr = tmp;
|
2003-08-14 04:41:05 +02:00
|
|
|
|
2006-01-18 02:23:23 +01:00
|
|
|
if (ptr >= nex_out->pin_count())
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
// This is the current DFF for this bit slice...
|
|
|
|
|
NetFF*ff1 = nex_ff[ptr].ff;
|
|
|
|
|
unsigned ff1_pin = nex_ff[ptr].pin;
|
|
|
|
|
|
|
|
|
|
// Connect the new NetFF to the baseline
|
|
|
|
|
// NetFF Q and D pins.
|
|
|
|
|
connect(ff1->pin_Data(ff1_pin), ff2->pin_Data(ff2_pin));
|
|
|
|
|
connect(ff1->pin_Q(ff1_pin), ff2->pin_Q(ff2_pin));
|
|
|
|
|
// Merge the new ff2 with the current ff1. This
|
|
|
|
|
// brings all the non-sliced bits forward from
|
|
|
|
|
// ff1, as well as any other merging needed.
|
|
|
|
|
merge_ff_slices(ff2, ff2_pin, ff1, ff1_pin);
|
|
|
|
|
|
|
|
|
|
// Save the bit slice map as the new referece.
|
|
|
|
|
nex_ff[ptr] = tmp_ff[idx];
|
|
|
|
|
|
|
|
|
|
// If the (old) current DFF is not the same as the
|
|
|
|
|
// baseline DFF, then it is possible that the
|
|
|
|
|
// slice update rendered ff1 obsolete. If so, it
|
|
|
|
|
// will no longer appear in the nex_ff map, so
|
|
|
|
|
// remove it.
|
|
|
|
|
if (ff1 != ff) {
|
|
|
|
|
bool found_flag = false;
|
|
|
|
|
for (unsigned scan=0
|
|
|
|
|
; scan < ff->width() && !found_flag
|
|
|
|
|
; scan += 1) {
|
|
|
|
|
if (nex_ff[scan].ff == ff1)
|
|
|
|
|
found_flag = true;
|
|
|
|
|
}
|
|
|
|
|
if (! found_flag) {
|
|
|
|
|
delete ff1;
|
|
|
|
|
}
|
|
|
|
|
}
|
2002-10-21 03:42:08 +02:00
|
|
|
}
|
2006-08-08 04:17:48 +02:00
|
|
|
delete[]nex_map_idx;
|
2002-10-21 03:42:08 +02:00
|
|
|
|
2006-08-15 05:41:24 +02:00
|
|
|
if (debug_synth) {
|
|
|
|
|
cerr << get_line() << ": debug: "
|
|
|
|
|
<< "Bind block substatement done." << endl;
|
|
|
|
|
}
|
|
|
|
|
|
2002-10-21 03:42:08 +02:00
|
|
|
delete tmp_map;
|
|
|
|
|
delete tmp_out;
|
2006-01-18 02:23:23 +01:00
|
|
|
delete tmp_ff;
|
2002-10-21 03:42:08 +02:00
|
|
|
|
|
|
|
|
} while (cur != last_);
|
|
|
|
|
|
2006-05-20 18:06:48 +02:00
|
|
|
|
|
|
|
|
if (flag == false)
|
|
|
|
|
return false;
|
|
|
|
|
|
2006-01-18 02:23:23 +01:00
|
|
|
/* Done. The large NetFF is no longer needed, as it has been
|
|
|
|
|
taken up by the smaller NetFF devices. */
|
|
|
|
|
delete ff;
|
|
|
|
|
ff = 0;
|
|
|
|
|
|
2006-08-08 04:17:48 +02:00
|
|
|
if (debug_synth) {
|
|
|
|
|
cerr << get_line() << ": debug: "
|
|
|
|
|
<< "Check block synthesis for completelness. " << endl;
|
|
|
|
|
}
|
|
|
|
|
|
2005-12-19 02:13:47 +01:00
|
|
|
/* Run through the pin accounting one more time to make sure
|
|
|
|
|
the data inputs are all connected. */
|
2006-01-18 02:23:23 +01:00
|
|
|
for (unsigned idx = 0 ; idx < block_width ; idx += 1) {
|
|
|
|
|
if (nex_ff[idx].proc == 0)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
NetFF*ff2 = nex_ff[idx].ff;
|
|
|
|
|
unsigned pin = nex_ff[idx].pin;
|
2005-12-19 02:13:47 +01:00
|
|
|
/* Skip this output if it is not handled in this block. */
|
|
|
|
|
if (ff2 == 0)
|
|
|
|
|
continue;
|
|
|
|
|
|
2005-12-31 05:28:14 +01:00
|
|
|
if (pin >= ff2->width()) {
|
|
|
|
|
cerr << ff2->get_line() << ": internal error: "
|
|
|
|
|
<< "pin " << pin << " out of range of "
|
|
|
|
|
<< ff2->width() << " bit DFF." << endl;
|
|
|
|
|
flag = false;
|
2006-03-18 19:43:21 +01:00
|
|
|
des->errors += 1;
|
2005-12-31 05:28:14 +01:00
|
|
|
|
2005-12-19 02:13:47 +01:00
|
|
|
/* If this block mentioned it, then the data must have
|
|
|
|
|
been set here. */
|
2005-12-31 05:28:14 +01:00
|
|
|
} else if (!ff2->pin_Data(pin).is_linked()) {
|
2005-12-19 02:13:47 +01:00
|
|
|
cerr << ff2->get_line() << ": error: "
|
2006-01-18 02:23:23 +01:00
|
|
|
<< "DFF introduced here is missing Data "
|
|
|
|
|
<< pin << " input." << endl;
|
2005-12-19 02:13:47 +01:00
|
|
|
flag = false;
|
2006-03-18 19:43:21 +01:00
|
|
|
des->errors += 1;
|
2005-12-19 02:13:47 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2006-08-08 04:17:48 +02:00
|
|
|
if (debug_synth) {
|
|
|
|
|
cerr << get_line() << ": debug: Finish synthesis of block" << endl;
|
|
|
|
|
}
|
|
|
|
|
|
2002-10-21 03:42:08 +02:00
|
|
|
return flag;
|
|
|
|
|
}
|
|
|
|
|
|
2006-06-23 05:49:46 +02:00
|
|
|
static bool test_ff_set_clr(struct sync_accounting_cell*nex_ff, unsigned bits)
|
|
|
|
|
{
|
|
|
|
|
for (unsigned idx = 0 ; idx < bits ; idx += 1) {
|
|
|
|
|
NetFF*ff = nex_ff[idx].ff;
|
|
|
|
|
if (ff->pin_Sset().is_linked())
|
|
|
|
|
return true;
|
|
|
|
|
if (ff->pin_Sclr().is_linked())
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int NetCondit::connect_set_clr_range_(struct sync_accounting_cell*nex_ff,
|
|
|
|
|
unsigned bits, NetNet*rst,
|
|
|
|
|
const verinum&val)
|
|
|
|
|
{
|
|
|
|
|
/* Oops, this is not a constant, presumably because the case
|
|
|
|
|
is not fully connected. In this case, we need to fall back
|
|
|
|
|
on more general synthesis. */
|
|
|
|
|
if (! val.is_defined()) {
|
|
|
|
|
if (debug_synth)
|
|
|
|
|
cerr << get_line() << ": debug: "
|
|
|
|
|
<< "Give up on set/clr synthesis, since "
|
|
|
|
|
<< "r-value = " << val << endl;
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
assert(val.is_defined());
|
|
|
|
|
|
|
|
|
|
unsigned base = 0;
|
|
|
|
|
unsigned wid = nex_ff[0].ff->width();
|
|
|
|
|
while (base < bits) {
|
|
|
|
|
NetFF*ff = nex_ff[base].ff;
|
|
|
|
|
assert(base+wid <= bits);
|
|
|
|
|
|
|
|
|
|
verinum tmp(0UL, wid);
|
|
|
|
|
for (unsigned idx = 0 ; idx < wid ; idx += 1) {
|
|
|
|
|
assert(nex_ff[base+idx].ff == ff);
|
|
|
|
|
tmp.set(idx, val.get(base+idx));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (tmp.is_zero()) {
|
|
|
|
|
connect(ff->pin_Sclr(), rst->pin(0));
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
connect(ff->pin_Sset(), rst->pin(0));
|
|
|
|
|
ff->sset_value(tmp);
|
|
|
|
|
}
|
|
|
|
|
if (debug_synth)
|
|
|
|
|
cerr << get_line() << ": debug: "
|
|
|
|
|
<< "Create a synchronous set for "
|
|
|
|
|
<< ff->width() << " bit ff." << endl;
|
|
|
|
|
|
|
|
|
|
base += wid;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int NetCondit::connect_enable_range_(Design*des, NetScope*scope,
|
|
|
|
|
struct sync_accounting_cell*nex_ff,
|
|
|
|
|
unsigned bits, NetNet*ce)
|
|
|
|
|
{
|
|
|
|
|
unsigned base = 0;
|
|
|
|
|
unsigned wid = nex_ff[0].ff->width();
|
|
|
|
|
while (base < bits) {
|
|
|
|
|
NetFF*ff = nex_ff[base].ff;
|
|
|
|
|
assert(base+wid <= bits);
|
|
|
|
|
|
|
|
|
|
for (unsigned idx = 0 ; idx < wid ; idx += 1) {
|
|
|
|
|
assert(nex_ff[base+idx].ff == ff);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Watch out for the special case that there is already
|
|
|
|
|
a CE connected to this FF. This can be caused by code
|
2011-03-04 23:11:28 +01:00
|
|
|
like this:
|
2006-06-23 05:49:46 +02:00
|
|
|
|
|
|
|
|
if (a) if (b) <statement>;
|
|
|
|
|
|
|
|
|
|
In this case, we are working on the inner IF, so we
|
|
|
|
|
AND the a and b expressions to make a new CE. */
|
|
|
|
|
|
|
|
|
|
if (ff->pin_Enable().is_linked()) {
|
|
|
|
|
NetLogic*ce_and = new NetLogic(scope,
|
|
|
|
|
scope->local_symbol(), 3,
|
|
|
|
|
NetLogic::AND);
|
|
|
|
|
des->add_node(ce_and);
|
|
|
|
|
connect(ff->pin_Enable(), ce_and->pin(1));
|
|
|
|
|
connect(ce->pin(0), ce_and->pin(2));
|
|
|
|
|
|
|
|
|
|
ff->pin_Enable().unlink();
|
|
|
|
|
connect(ff->pin_Enable(), ce_and->pin(0));
|
|
|
|
|
|
|
|
|
|
NetNet*tmp = new NetNet(scope, scope->local_symbol(),
|
|
|
|
|
NetNet::IMPLICIT, 1);
|
|
|
|
|
tmp->local_flag(true);
|
|
|
|
|
connect(ff->pin_Enable(), tmp->pin(0));
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
|
|
connect(ff->pin_Enable(), ce->pin(0));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
base += wid;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2002-10-21 03:42:08 +02:00
|
|
|
/*
|
|
|
|
|
* 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.
|
|
|
|
|
*/
|
2006-01-18 02:23:23 +01:00
|
|
|
bool NetCondit::synth_sync(Design*des, NetScope*scope,
|
|
|
|
|
struct sync_accounting_cell*nex_ff,
|
2005-12-14 01:54:29 +01:00
|
|
|
NetNet*nex_map, NetNet*nex_out,
|
2002-09-26 03:13:14 +02:00
|
|
|
const svector<NetEvProbe*>&events_in)
|
2002-09-16 02:30:33 +02:00
|
|
|
{
|
2006-08-08 04:17:48 +02:00
|
|
|
if (debug_synth) {
|
|
|
|
|
cerr << get_line() << ": debug: "
|
|
|
|
|
<< "Start sync synthesis of conditional" << endl;
|
|
|
|
|
}
|
|
|
|
|
|
2003-08-10 19:04:23 +02:00
|
|
|
/* First try to turn the condition expression into an
|
|
|
|
|
asynchronous set/reset. If the condition expression has
|
|
|
|
|
inputs that are included in the sensitivity list, then it
|
|
|
|
|
is likely intended as an asynchronous input. */
|
2002-11-10 00:29:29 +01:00
|
|
|
|
2003-08-10 19:04:23 +02:00
|
|
|
NexusSet*expr_input = expr_->nex_input();
|
|
|
|
|
assert(expr_input);
|
2002-09-26 03:13:14 +02:00
|
|
|
for (unsigned idx = 0 ; idx < events_in.count() ; idx += 1) {
|
|
|
|
|
|
2006-06-23 05:49:46 +02:00
|
|
|
|
2003-08-10 19:04:23 +02:00
|
|
|
NetEvProbe*ev = events_in[idx];
|
|
|
|
|
NexusSet pin_set;
|
|
|
|
|
pin_set.add(ev->pin(0).nexus());
|
2002-10-23 03:45:24 +02:00
|
|
|
|
2003-08-10 19:04:23 +02:00
|
|
|
if (! expr_input->contains(pin_set))
|
|
|
|
|
continue;
|
2002-10-23 03:45:24 +02:00
|
|
|
|
2005-12-07 03:14:37 +01:00
|
|
|
/* If we are taking this to be an asynchronous
|
|
|
|
|
set/clear, then *all* the condition expression inputs
|
|
|
|
|
must be asynchronous. Check that here. */
|
|
|
|
|
if (! pin_set.contains(*expr_input)) {
|
|
|
|
|
|
|
|
|
|
NexusSet tmp_set;
|
|
|
|
|
tmp_set.add(ev->pin(0).nexus());
|
|
|
|
|
for (unsigned tmp = idx+1; tmp<events_in.count(); tmp += 1) {
|
|
|
|
|
NetEvProbe*ev_tmp = events_in[tmp];
|
|
|
|
|
tmp_set.add(ev_tmp->pin(0).nexus());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (! tmp_set.contains(*expr_input)) {
|
|
|
|
|
cerr << get_line() << ": error: Condition expression "
|
|
|
|
|
<< "mixes synchronous and asynchronous "
|
|
|
|
|
<< "inputs." << endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2003-08-10 19:04:23 +02:00
|
|
|
/* Ah, this edge is in the sensitivity list for the
|
|
|
|
|
expression, so we have an asynchronous
|
|
|
|
|
input. Synthesize the set/reset input expression. */
|
|
|
|
|
|
|
|
|
|
NetNet*rst = expr_->synthesize(des);
|
|
|
|
|
assert(rst->pin_count() == 1);
|
|
|
|
|
|
2006-06-23 05:49:46 +02:00
|
|
|
/* WARNING: This case relies on the ff vector being a
|
|
|
|
|
single NetFF. */
|
|
|
|
|
for (unsigned bit = 1 ; bit < nex_out->pin_count() ; bit += 1) {
|
|
|
|
|
assert(nex_ff[0].ff == nex_ff[bit].ff);
|
|
|
|
|
}
|
|
|
|
|
NetFF*ff = nex_ff[0].ff;
|
|
|
|
|
|
2003-08-10 19:04:23 +02:00
|
|
|
/* XXXX I really should find a way to check that the
|
|
|
|
|
edge used on the reset input is correct. This would
|
2011-03-04 22:52:08 +01:00
|
|
|
involve interpreting the expression that is fed by the
|
2003-08-10 19:04:23 +02:00
|
|
|
reset expression. */
|
|
|
|
|
//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_symbol(),
|
|
|
|
|
NetNet::WIRE, nex_map->pin_count());
|
|
|
|
|
asig->local_flag(true);
|
2003-10-27 03:18:04 +01:00
|
|
|
|
|
|
|
|
assert(if_ != 0);
|
2006-03-27 01:09:21 +02:00
|
|
|
bool flag = if_->synth_async_noaccum(des, scope, true, nex_ff,
|
|
|
|
|
nex_map, asig);
|
2003-08-10 19:04:23 +02:00
|
|
|
|
2006-06-23 05:49:46 +02:00
|
|
|
assert(asig->pin_count() == nex_map->pin_count());
|
2003-08-10 19:04:23 +02:00
|
|
|
|
|
|
|
|
/* Collect the set/reset value into a verinum. If
|
|
|
|
|
this turns out to be entirely 0 values, then
|
|
|
|
|
use the Aclr input. Otherwise, use the Aset
|
|
|
|
|
input and save the set value. */
|
|
|
|
|
verinum tmp (verinum::V0, ff->width());
|
2006-06-23 05:49:46 +02:00
|
|
|
unsigned count_z = 0;
|
|
|
|
|
unsigned count_x = 0;
|
2003-08-10 19:04:23 +02:00
|
|
|
for (unsigned bit = 0 ; bit < ff->width() ; bit += 1) {
|
|
|
|
|
|
|
|
|
|
assert(asig->pin(bit).nexus()->drivers_constant());
|
2006-06-23 05:49:46 +02:00
|
|
|
verinum::V val = asig->pin(bit).nexus()->driven_value();
|
|
|
|
|
tmp.set(bit, val);
|
|
|
|
|
|
|
|
|
|
switch (val) {
|
|
|
|
|
case verinum::V0:
|
|
|
|
|
case verinum::V1:
|
|
|
|
|
break;
|
|
|
|
|
case verinum::Vz:
|
|
|
|
|
count_z += 1;
|
|
|
|
|
break;
|
|
|
|
|
case verinum::Vx:
|
|
|
|
|
count_x += 1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2003-08-10 19:04:23 +02:00
|
|
|
}
|
2002-09-26 03:13:14 +02:00
|
|
|
|
2006-06-23 05:49:46 +02:00
|
|
|
if (count_x > 0) {
|
2006-05-20 18:06:48 +02:00
|
|
|
cerr << get_line() << ": internal error: "
|
2006-06-23 05:49:46 +02:00
|
|
|
<< "True clause returns constant 'bx values"
|
2006-05-20 18:06:48 +02:00
|
|
|
<< " which are not plausible for set/reset." << endl;
|
2006-06-23 05:49:46 +02:00
|
|
|
cerr << get_line() << ": : "
|
|
|
|
|
<< "XXXX val = " << tmp << endl;
|
2006-05-20 18:06:48 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2006-06-23 05:49:46 +02:00
|
|
|
if (count_z > 0) {
|
|
|
|
|
#if 0
|
|
|
|
|
cerr << get_line() << ": internal error: "
|
|
|
|
|
<< "True clause returns constant 'bz values"
|
|
|
|
|
<< " which are not plausible for set/reset." << endl;
|
|
|
|
|
cerr << get_line() << ": : "
|
|
|
|
|
<< "XXXX val = " << tmp << endl;
|
|
|
|
|
return false;
|
|
|
|
|
#endif
|
|
|
|
|
assert(count_z < ff->width());
|
|
|
|
|
/* Some bits are not reset by this input. To make
|
|
|
|
|
this work, split the current ff into a pair of
|
|
|
|
|
FF, one connected to the reset, and the other
|
|
|
|
|
not. */
|
|
|
|
|
NetFF*ff1 = new NetFF(scope, ff->name(),
|
|
|
|
|
ff->width() - count_z);
|
|
|
|
|
NetFF*ffz = new NetFF(scope, scope->local_symbol(),
|
|
|
|
|
count_z);
|
|
|
|
|
des->add_node(ff1);
|
|
|
|
|
des->add_node(ffz);
|
|
|
|
|
connect(ff->pin_Clock(), ff1->pin_Clock());
|
|
|
|
|
connect(ff->pin_Clock(), ffz->pin_Clock());
|
|
|
|
|
verinum tmp1(0UL, ff1->width());
|
|
|
|
|
unsigned bit1 = 0;
|
|
|
|
|
unsigned bitz = 0;
|
|
|
|
|
|
|
|
|
|
for (unsigned bit = 0 ; bit < ff->width() ; bit += 1) {
|
|
|
|
|
|
|
|
|
|
if (tmp.get(bit) == verinum::Vz) {
|
|
|
|
|
connect(ffz->pin_Q(bitz), ff->pin_Q(bit));
|
|
|
|
|
connect(ffz->pin_Data(bitz), ff->pin_Data(bit));
|
|
|
|
|
nex_ff[bit].ff = ffz;
|
|
|
|
|
nex_ff[bit].pin = bitz;
|
|
|
|
|
bitz += 1;
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
connect(ff1->pin_Q(bit1), ff->pin_Q(bit));
|
|
|
|
|
connect(ff1->pin_Data(bit1), ff->pin_Data(bit));
|
|
|
|
|
nex_ff[bit].ff = ff1;
|
|
|
|
|
nex_ff[bit].pin = bit1;
|
|
|
|
|
tmp1.set(bit1, tmp.get(bit));
|
|
|
|
|
bit1 += 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
delete ff;
|
|
|
|
|
ff = ff1;
|
|
|
|
|
tmp = tmp1;
|
|
|
|
|
}
|
|
|
|
|
|
2003-08-10 19:04:23 +02:00
|
|
|
assert(tmp.is_defined());
|
|
|
|
|
if (tmp.is_zero()) {
|
|
|
|
|
connect(ff->pin_Aclr(), rst->pin(0));
|
2002-09-26 03:13:14 +02:00
|
|
|
|
2003-08-10 19:04:23 +02:00
|
|
|
} else {
|
|
|
|
|
connect(ff->pin_Aset(), rst->pin(0));
|
|
|
|
|
ff->aset_value(tmp);
|
2002-09-26 03:13:14 +02:00
|
|
|
}
|
|
|
|
|
|
2003-08-10 19:04:23 +02:00
|
|
|
delete asig;
|
|
|
|
|
delete expr_input;
|
|
|
|
|
|
2005-12-07 03:14:37 +01:00
|
|
|
if (else_ == 0) {
|
2005-12-19 02:13:47 +01:00
|
|
|
|
|
|
|
|
/* The lack of an else_ clause here means that
|
|
|
|
|
there is no data input to the DFF yet
|
|
|
|
|
defined. This is bad, but the data input may be
|
|
|
|
|
given later in an enclosing block, so don't
|
|
|
|
|
report an error here quite yet. */
|
|
|
|
|
return true;
|
2005-12-07 03:14:37 +01:00
|
|
|
}
|
|
|
|
|
|
2003-10-27 03:18:04 +01:00
|
|
|
assert(else_ != 0);
|
2005-12-14 02:53:39 +01:00
|
|
|
|
|
|
|
|
/* Create a new NetEvProbe list that does not include
|
|
|
|
|
the current probe that we've absorbed into this
|
|
|
|
|
input. */
|
|
|
|
|
assert(events_in.count() >= 1);
|
|
|
|
|
svector<NetEvProbe*> events_tmp (events_in.count() - 1);
|
|
|
|
|
unsigned count_events = 0;
|
|
|
|
|
for (unsigned tmp = 0 ; tmp < events_in.count() ; tmp += 1) {
|
|
|
|
|
if (tmp == idx) continue;
|
|
|
|
|
events_tmp[count_events++] = events_in[tmp];
|
|
|
|
|
}
|
|
|
|
|
|
2006-01-18 02:23:23 +01:00
|
|
|
flag = flag && else_->synth_sync(des, scope,
|
|
|
|
|
nex_ff, nex_map,
|
2005-12-14 02:53:39 +01:00
|
|
|
nex_out, events_tmp);
|
|
|
|
|
|
2006-08-08 04:17:48 +02:00
|
|
|
if (debug_synth)
|
|
|
|
|
cerr << get_line() << ": debug: "
|
|
|
|
|
<< "End synthesis of conditional" << endl;
|
2003-12-17 17:52:39 +01:00
|
|
|
return flag;
|
2002-09-26 03:13:14 +02:00
|
|
|
}
|
|
|
|
|
|
2003-08-10 19:04:23 +02:00
|
|
|
delete expr_input;
|
|
|
|
|
|
2006-08-08 04:17:48 +02:00
|
|
|
if (debug_synth) {
|
|
|
|
|
cerr << get_line() << ": debug: "
|
|
|
|
|
<< "Condit expression input not sensitive, "
|
|
|
|
|
<< "so must be synchronous. " << endl;
|
|
|
|
|
}
|
|
|
|
|
|
2003-08-15 04:23:52 +02:00
|
|
|
/* Detect the case that this is a *synchronous* set/reset. It
|
2011-03-04 22:52:08 +01:00
|
|
|
is not asynchronous because we know the condition is not
|
2003-08-15 04:23:52 +02:00
|
|
|
included in the sensitivity list, but if the if_ case is
|
|
|
|
|
constant (has no inputs) then we can model this as a
|
2003-10-27 03:18:04 +01:00
|
|
|
synchronous set/reset.
|
|
|
|
|
|
|
|
|
|
This is only synchronous set/reset if there is a true and a
|
|
|
|
|
false clause, and no inputs. The "no inputs" requirement is
|
2006-01-18 07:16:11 +01:00
|
|
|
met if the assignments are of all constant values.
|
|
|
|
|
|
|
|
|
|
Also, we will not allow both Sset and Sclr to be used on a
|
|
|
|
|
single LPM_FF (due to unclear priority issues) so don't try
|
2006-03-12 08:34:16 +01:00
|
|
|
if either are already connected.
|
|
|
|
|
|
|
|
|
|
XXXX This should be disabled if there is a memory involved
|
|
|
|
|
in any sub-statements? */
|
2003-08-15 04:23:52 +02:00
|
|
|
|
2006-10-30 03:03:30 +01:00
|
|
|
NexusSet*a_set = if_? if_->nex_input() : 0;
|
|
|
|
|
|
|
|
|
|
if (a_set && (a_set->count() == 0)
|
2006-01-18 07:16:11 +01:00
|
|
|
&& if_ && else_
|
2006-06-23 05:49:46 +02:00
|
|
|
&& !test_ff_set_clr(nex_ff, nex_map->pin_count())) {
|
2003-08-15 04:23:52 +02:00
|
|
|
|
|
|
|
|
NetNet*rst = expr_->synthesize(des);
|
|
|
|
|
assert(rst->pin_count() == 1);
|
|
|
|
|
|
|
|
|
|
/* Synthesize the true clause to figure out what
|
|
|
|
|
kind of set/reset we have. */
|
|
|
|
|
NetNet*asig = new NetNet(scope, scope->local_symbol(),
|
|
|
|
|
NetNet::WIRE, nex_map->pin_count());
|
|
|
|
|
asig->local_flag(true);
|
2006-03-27 01:09:21 +02:00
|
|
|
bool flag = if_->synth_async_noaccum(des, scope, true, nex_ff,
|
|
|
|
|
nex_map, asig);
|
2003-08-15 04:23:52 +02:00
|
|
|
|
2003-12-20 01:59:31 +01:00
|
|
|
if (!flag) {
|
|
|
|
|
/* This path leads nowhere */
|
|
|
|
|
delete asig;
|
2006-04-01 03:37:58 +02:00
|
|
|
} else do {
|
2006-06-23 05:49:46 +02:00
|
|
|
assert(asig->pin_count() == nex_map->pin_count());
|
|
|
|
|
|
|
|
|
|
unsigned nbits = nex_map->pin_count();
|
2003-08-15 04:23:52 +02:00
|
|
|
|
2003-12-20 01:59:31 +01:00
|
|
|
/* Collect the set/reset value into a verinum. If
|
|
|
|
|
this turns out to be entirely 0 values, then
|
|
|
|
|
use the Sclr input. Otherwise, use the Aset
|
|
|
|
|
input and save the set value. */
|
2006-06-23 05:49:46 +02:00
|
|
|
verinum tmp (verinum::V0, nbits);
|
|
|
|
|
for (unsigned bit = 0; bit< nbits; bit += 1) {
|
2003-08-15 04:23:52 +02:00
|
|
|
|
2003-12-20 01:59:31 +01:00
|
|
|
assert(asig->pin(bit).nexus()->drivers_constant());
|
|
|
|
|
tmp.set(bit, asig->pin(bit).nexus()->driven_value());
|
|
|
|
|
}
|
2003-08-15 04:23:52 +02:00
|
|
|
|
2006-06-23 05:49:46 +02:00
|
|
|
if (connect_set_clr_range_(nex_ff, nbits, rst, tmp) < 0) {
|
2006-04-01 03:37:58 +02:00
|
|
|
delete asig;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2003-08-15 04:23:52 +02:00
|
|
|
|
2003-12-20 01:59:31 +01:00
|
|
|
delete a_set;
|
2003-08-15 04:23:52 +02:00
|
|
|
|
2003-12-20 01:59:31 +01:00
|
|
|
assert(else_ != 0);
|
2006-01-18 02:23:23 +01:00
|
|
|
flag = else_->synth_sync(des, scope,
|
|
|
|
|
nex_ff, nex_map, nex_out,
|
|
|
|
|
svector<NetEvProbe*>(0))
|
2003-12-20 01:59:31 +01:00
|
|
|
&& flag;
|
2006-08-08 04:17:48 +02:00
|
|
|
|
|
|
|
|
if (debug_synth)
|
|
|
|
|
cerr << get_line() << ": debug: "
|
|
|
|
|
<< "End synthesis of conditional" << endl;
|
2003-12-20 01:59:31 +01:00
|
|
|
return flag;
|
2006-04-01 03:37:58 +02:00
|
|
|
} while (0);
|
2003-08-15 04:23:52 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
delete a_set;
|
|
|
|
|
|
2002-09-26 03:13:14 +02:00
|
|
|
/* Failed to find an asynchronous set/reset, so any events
|
2005-12-19 02:13:47 +01:00
|
|
|
input are probably in error, or simply not in use. */
|
2002-09-26 03:13:14 +02:00
|
|
|
|
2003-08-10 19:04:23 +02:00
|
|
|
|
2002-09-16 02:30:33 +02:00
|
|
|
/* If this is an if/then/else, then it is likely a
|
|
|
|
|
combinational if, and I should synthesize it that way. */
|
|
|
|
|
if (if_ && else_) {
|
2006-08-08 04:17:48 +02:00
|
|
|
if (debug_synth) {
|
|
|
|
|
cerr << get_line() << ": debug: "
|
|
|
|
|
<< "Condit expression looks like a synchronous mux."
|
|
|
|
|
<< endl;
|
|
|
|
|
}
|
|
|
|
|
|
2006-03-27 01:09:21 +02:00
|
|
|
bool flag =synth_async_noaccum(des, scope, true, nex_ff,
|
|
|
|
|
nex_map, nex_out);
|
2006-08-08 04:17:48 +02:00
|
|
|
|
|
|
|
|
if (debug_synth)
|
|
|
|
|
cerr << get_line() << ": debug: "
|
|
|
|
|
<< "End synthesis of conditional" << endl;
|
2003-12-17 17:52:39 +01:00
|
|
|
return flag;
|
2002-09-16 02:30:33 +02:00
|
|
|
}
|
|
|
|
|
|
2006-10-30 03:03:30 +01:00
|
|
|
/* At this point, all that's left are synchronous enables and
|
|
|
|
|
synchronous disables. These are cases where only one of the
|
|
|
|
|
if_ and else_ clauses is given. */
|
|
|
|
|
assert( (if_ && !else_) || (else_ && !if_) );
|
2002-09-16 02:30:33 +02:00
|
|
|
|
2006-08-08 04:17:48 +02:00
|
|
|
if (debug_synth) {
|
|
|
|
|
cerr << get_line() << ": debug: "
|
2006-10-30 03:03:30 +01:00
|
|
|
<< "Condit expression looks like a synchronous "
|
|
|
|
|
<< (if_? "enable" : "disable") << ". " << endl;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NetProc*clause = if_? if_ : else_;
|
|
|
|
|
assert(clause);
|
|
|
|
|
|
|
|
|
|
/* If this is a conditional disable, then turn it to an enable
|
|
|
|
|
by putting a NOT in front of the disable expression. */
|
|
|
|
|
NetExpr*condit = expr_;
|
|
|
|
|
if (else_ != 0) {
|
|
|
|
|
assert(if_ == 0);
|
|
|
|
|
condit = new NetEUReduce('!', condit);
|
|
|
|
|
condit->set_line(*expr_);
|
2006-08-08 04:17:48 +02:00
|
|
|
}
|
|
|
|
|
|
2003-08-14 04:41:05 +02:00
|
|
|
/* Synthesize the enable expression. */
|
2006-10-30 03:03:30 +01:00
|
|
|
NetNet*ce = condit->synthesize(des);
|
2003-08-14 04:41:05 +02:00
|
|
|
assert(ce->pin_count() == 1);
|
|
|
|
|
|
2002-11-10 00:29:29 +01:00
|
|
|
/* What's left, is a synchronous CE statement like this:
|
|
|
|
|
|
|
|
|
|
if (expr_) <true statement>;
|
|
|
|
|
|
2003-01-27 06:09:17 +01:00
|
|
|
The expr_ expression has already been synthesized to the ce
|
2002-11-10 00:29:29 +01:00
|
|
|
net, so we connect it here to the FF. What's left is to
|
|
|
|
|
synthesize the substatement as a combinational
|
2006-06-23 05:49:46 +02:00
|
|
|
statement. */
|
2002-09-16 02:30:33 +02:00
|
|
|
|
2006-06-23 05:49:46 +02:00
|
|
|
unsigned nbits = nex_map->pin_count();
|
|
|
|
|
connect_enable_range_(des, scope, nex_ff, nbits, ce);
|
2002-11-10 00:29:29 +01:00
|
|
|
|
2006-08-08 04:17:48 +02:00
|
|
|
if (debug_synth) {
|
|
|
|
|
cerr << get_line() << ": debug: "
|
|
|
|
|
<< "Condit expression make input that is sync enabled."
|
|
|
|
|
<< endl;
|
|
|
|
|
}
|
|
|
|
|
|
2006-10-30 03:03:30 +01:00
|
|
|
bool flag = clause->synth_sync(des, scope,
|
2006-01-18 02:23:23 +01:00
|
|
|
nex_ff, nex_map, nex_out,
|
|
|
|
|
events_in);
|
2002-09-16 02:30:33 +02:00
|
|
|
|
2006-08-08 04:17:48 +02:00
|
|
|
|
|
|
|
|
if (debug_synth)
|
|
|
|
|
cerr << get_line() << ": debug: "
|
|
|
|
|
<< "End synthesis of conditional" << endl;
|
2003-12-17 17:52:39 +01:00
|
|
|
return flag;
|
2002-09-16 02:30:33 +02:00
|
|
|
}
|
|
|
|
|
|
2006-01-18 02:23:23 +01:00
|
|
|
bool NetEvWait::synth_sync(Design*des, NetScope*scope,
|
|
|
|
|
struct sync_accounting_cell*nex_ff,
|
2005-12-14 01:54:29 +01:00
|
|
|
NetNet*nex_map, NetNet*nex_out,
|
2002-09-26 03:13:14 +02:00
|
|
|
const svector<NetEvProbe*>&events_in)
|
2002-09-16 02:30:33 +02:00
|
|
|
{
|
2002-09-26 03:13:14 +02:00
|
|
|
if (events_in.count() > 0) {
|
|
|
|
|
cerr << get_line() << ": error: Events are unaccounted"
|
2005-12-19 02:13:47 +01:00
|
|
|
<< " for in process synthesis. (evw)" << endl;
|
2002-09-26 03:13:14 +02:00
|
|
|
des->errors += 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
assert(events_in.count() == 0);
|
|
|
|
|
|
2006-08-08 04:17:48 +02:00
|
|
|
if (debug_synth) {
|
|
|
|
|
cerr << get_line() << ": debug: Start synthesis of event wait statement."
|
|
|
|
|
<< endl;
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-04 22:52:08 +01:00
|
|
|
/* This can't be other than one unless there are named events,
|
2002-09-24 02:58:35 +02:00
|
|
|
which I cannot synthesize. */
|
2002-09-16 02:30:33 +02:00
|
|
|
assert(nevents_ == 1);
|
|
|
|
|
NetEvent*ev = events_[0];
|
|
|
|
|
|
2002-09-24 02:58:35 +02:00
|
|
|
assert(ev->nprobe() >= 1);
|
2002-09-26 03:13:14 +02:00
|
|
|
svector<NetEvProbe*>events (ev->nprobe() - 1);
|
2002-09-24 02:58:35 +02:00
|
|
|
|
|
|
|
|
/* 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;
|
2002-09-26 03:13:14 +02:00
|
|
|
unsigned event_idx = 0;
|
2002-09-24 02:58:35 +02:00
|
|
|
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;
|
2002-09-26 03:13:14 +02:00
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
events[event_idx++] = tmp;
|
2002-09-24 02:58:35 +02:00
|
|
|
}
|
|
|
|
|
}
|
2002-09-16 02:30:33 +02:00
|
|
|
|
2002-09-24 02:58:35 +02:00
|
|
|
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;
|
2006-03-18 19:43:21 +01:00
|
|
|
des->errors += 1;
|
2002-09-24 02:58:35 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
2002-09-16 02:30:33 +02:00
|
|
|
|
2006-06-23 05:49:46 +02:00
|
|
|
unsigned base = 0;
|
|
|
|
|
while (base < nex_map->pin_count()) {
|
|
|
|
|
unsigned wid = nex_ff[base].ff->width();
|
|
|
|
|
assert((base + wid) <= nex_map->pin_count());
|
|
|
|
|
|
|
|
|
|
NetFF*ff = nex_ff[base].ff;
|
|
|
|
|
connect(ff->pin_Clock(), pclk->pin(0));
|
|
|
|
|
if (pclk->edge() == NetEvProbe::NEGEDGE)
|
|
|
|
|
ff->attribute(perm_string::literal("ivl:clock_polarity"),
|
|
|
|
|
verinum("INVERT"));
|
|
|
|
|
|
|
|
|
|
base += wid;
|
|
|
|
|
}
|
|
|
|
|
assert(base == nex_map->pin_count());
|
2002-09-16 02:30:33 +02:00
|
|
|
|
2002-09-24 02:58:35 +02:00
|
|
|
/* Synthesize the input to the DFF. */
|
2006-01-18 02:23:23 +01:00
|
|
|
bool flag = statement_->synth_sync(des, scope, nex_ff,
|
2002-09-26 03:13:14 +02:00
|
|
|
nex_map, nex_out, events);
|
2002-09-24 02:58:35 +02:00
|
|
|
|
2006-08-08 04:17:48 +02:00
|
|
|
if (debug_synth) {
|
|
|
|
|
cerr << get_line() << ": debug: Finished synthesis of event wait statement."
|
|
|
|
|
<< endl;
|
|
|
|
|
}
|
|
|
|
|
|
2002-09-16 02:30:33 +02:00
|
|
|
return flag;
|
|
|
|
|
}
|
|
|
|
|
|
2006-03-18 19:43:21 +01:00
|
|
|
bool NetWhile::synth_async(Design*des, NetScope*scope, bool sync_flag,
|
2010-12-29 21:33:08 +01:00
|
|
|
struct sync_accounting_cell*nex_ff,
|
|
|
|
|
NetNet*nex_map, NetNet*nex_out, NetNet*accum_in,
|
|
|
|
|
bool&latch_inferred, NetNet*gsig)
|
2006-03-18 19:43:21 +01:00
|
|
|
{
|
|
|
|
|
cerr << get_line()
|
|
|
|
|
<< ": error: Cannot synthesize for or while loops."
|
|
|
|
|
<< endl;
|
|
|
|
|
des->errors += 1;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2002-09-16 02:30:33 +02:00
|
|
|
bool NetProcTop::synth_sync(Design*des)
|
|
|
|
|
{
|
2006-08-08 04:17:48 +02:00
|
|
|
if (debug_synth) {
|
|
|
|
|
cerr << get_line() << ": debug: Start synthesis of process." << endl;
|
|
|
|
|
}
|
|
|
|
|
|
2002-09-16 02:30:33 +02:00
|
|
|
NexusSet nex_set;
|
|
|
|
|
statement_->nex_output(nex_set);
|
|
|
|
|
|
2006-08-08 04:17:48 +02:00
|
|
|
if (debug_synth) {
|
|
|
|
|
cerr << get_line() << ": debug: Process seems to have "
|
|
|
|
|
<< nex_set.count() << " output bits." << endl;
|
|
|
|
|
}
|
|
|
|
|
|
2004-02-18 18:11:54 +01:00
|
|
|
NetFF*ff = new NetFF(scope(), scope()->local_symbol(),
|
2002-09-16 02:30:33 +02:00
|
|
|
nex_set.count());
|
|
|
|
|
des->add_node(ff);
|
2004-02-20 19:53:33 +01:00
|
|
|
ff->attribute(perm_string::literal("LPM_FFType"), verinum("DFF"));
|
2006-02-19 01:11:31 +01:00
|
|
|
unsigned process_pin_count = ff->width();
|
2002-09-16 02:30:33 +02:00
|
|
|
|
2006-01-18 02:23:23 +01:00
|
|
|
struct sync_accounting_cell*nex_ff
|
|
|
|
|
= new struct sync_accounting_cell[ff->pin_count()];
|
2006-02-19 01:11:31 +01:00
|
|
|
for (unsigned idx = 0 ; idx < process_pin_count ; idx += 1) {
|
2006-01-18 02:23:23 +01:00
|
|
|
nex_ff[idx].ff = ff;
|
|
|
|
|
nex_ff[idx].pin = idx;
|
|
|
|
|
nex_ff[idx].proc = statement_;
|
|
|
|
|
}
|
|
|
|
|
|
2002-09-16 02:30:33 +02:00
|
|
|
/* The D inputs to the DFF device will receive the output from
|
2003-01-27 06:09:17 +01:00
|
|
|
the statements of the process. */
|
2003-03-06 01:28:41 +01:00
|
|
|
NetNet*nex_d = new NetNet(scope(), scope()->local_symbol(),
|
2002-09-16 02:30:33 +02:00
|
|
|
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. */
|
2004-02-18 18:11:54 +01:00
|
|
|
const perm_string tmpq = perm_string::literal("tmpq");
|
|
|
|
|
NetNet*nex_q = new NetNet(scope(), tmpq, NetNet::WIRE,
|
2002-09-16 02:30:33 +02:00
|
|
|
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. */
|
2006-01-18 02:23:23 +01:00
|
|
|
bool flag = statement_->synth_sync(des, scope(),
|
|
|
|
|
nex_ff,
|
2002-09-26 03:13:14 +02:00
|
|
|
nex_q, nex_d,
|
|
|
|
|
svector<NetEvProbe*>());
|
2002-09-16 02:30:33 +02:00
|
|
|
|
2006-02-19 01:11:31 +01:00
|
|
|
|
2002-09-16 02:30:33 +02:00
|
|
|
delete nex_q;
|
2006-01-18 02:23:23 +01:00
|
|
|
delete[]nex_ff;
|
2002-09-16 02:30:33 +02:00
|
|
|
|
2006-08-08 04:17:48 +02:00
|
|
|
if (debug_synth) {
|
|
|
|
|
cerr << get_line() << ": debug: Finished synthesis of process." << endl;
|
|
|
|
|
}
|
|
|
|
|
|
2002-09-16 02:30:33 +02:00
|
|
|
return flag;
|
|
|
|
|
}
|
|
|
|
|
|
2002-06-30 04:21:31 +02:00
|
|
|
class synth2_f : public functor_t {
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
void process(class Design*, class NetProcTop*);
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
2002-07-01 02:54:21 +02:00
|
|
|
* Look at a process. If it is asynchronous, then synthesize it as an
|
|
|
|
|
* asynchronous process and delete the process itself for its gates.
|
2002-06-30 04:21:31 +02:00
|
|
|
*/
|
|
|
|
|
void synth2_f::process(class Design*des, class NetProcTop*top)
|
|
|
|
|
{
|
2004-02-20 19:53:33 +01:00
|
|
|
if (top->attribute(perm_string::literal("ivl_synthesis_off")).as_ulong() != 0)
|
2002-08-11 00:07:08 +02:00
|
|
|
return;
|
|
|
|
|
|
2003-06-23 02:14:44 +02:00
|
|
|
/* If the scope that contains this process as a cell attribute
|
|
|
|
|
attached to it, then skip synthesis. */
|
2004-02-20 19:53:33 +01:00
|
|
|
if (top->scope()->attribute(perm_string::literal("ivl_synthesis_cell")).len() > 0)
|
2003-06-23 02:14:44 +02:00
|
|
|
return;
|
|
|
|
|
|
2002-09-16 02:30:33 +02:00
|
|
|
if (top->is_synchronous()) do {
|
|
|
|
|
bool flag = top->synth_sync(des);
|
2002-09-24 02:58:35 +02:00
|
|
|
if (! flag) {
|
2002-10-20 21:19:37 +02:00
|
|
|
cerr << top->get_line() << ": error: "
|
|
|
|
|
<< "Unable to synthesize synchronous process." << endl;
|
2002-09-24 02:58:35 +02:00
|
|
|
des->errors += 1;
|
|
|
|
|
return;
|
|
|
|
|
}
|
2002-09-16 02:30:33 +02:00
|
|
|
des->delete_process(top);
|
|
|
|
|
return;
|
|
|
|
|
} while (0);
|
|
|
|
|
|
2002-07-01 02:54:21 +02:00
|
|
|
if (! top->is_asynchronous()) {
|
2002-08-19 00:07:16 +02:00
|
|
|
bool synth_error_flag = false;
|
2004-02-20 19:53:33 +01:00
|
|
|
if (top->attribute(perm_string::literal("ivl_combinational")).as_ulong() != 0) {
|
2002-08-11 00:07:08 +02:00
|
|
|
cerr << top->get_line() << ": error: "
|
|
|
|
|
<< "Process is marked combinational,"
|
2002-07-01 02:54:21 +02:00
|
|
|
<< " but isn't really." << endl;
|
2002-08-11 00:07:08 +02:00
|
|
|
des->errors += 1;
|
2002-08-19 00:07:16 +02:00
|
|
|
synth_error_flag = true;
|
2002-08-11 00:07:08 +02:00
|
|
|
}
|
|
|
|
|
|
2004-02-20 19:53:33 +01:00
|
|
|
if (top->attribute(perm_string::literal("ivl_synthesis_on")).as_ulong() != 0) {
|
2002-08-11 00:07:08 +02:00
|
|
|
cerr << top->get_line() << ": error: "
|
|
|
|
|
<< "Process is marked for synthesis,"
|
|
|
|
|
<< " but I can't do it." << endl;
|
|
|
|
|
des->errors += 1;
|
2002-08-19 00:07:16 +02:00
|
|
|
synth_error_flag = true;
|
2002-08-11 00:07:08 +02:00
|
|
|
}
|
|
|
|
|
|
2006-03-18 19:43:21 +01:00
|
|
|
if (! synth_error_flag) {
|
2002-08-19 00:07:16 +02:00
|
|
|
cerr << top->get_line() << ": warning: "
|
|
|
|
|
<< "Process not synthesized." << endl;
|
2006-03-18 19:43:21 +01:00
|
|
|
}
|
2002-06-30 04:21:31 +02:00
|
|
|
return;
|
2002-07-01 02:54:21 +02:00
|
|
|
}
|
2002-06-30 04:21:31 +02:00
|
|
|
|
2002-07-01 02:54:21 +02:00
|
|
|
if (! top->synth_async(des)) {
|
2006-03-18 19:43:21 +01:00
|
|
|
cerr << top->get_line() << ": error: "
|
|
|
|
|
<< "Asynchronous process cannot be synthesized." << endl;
|
|
|
|
|
des->errors += 1;
|
2002-06-30 04:21:31 +02:00
|
|
|
return;
|
2002-07-01 02:54:21 +02:00
|
|
|
}
|
2002-06-30 04:21:31 +02:00
|
|
|
|
|
|
|
|
des->delete_process(top);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void synth2(Design*des)
|
|
|
|
|
{
|
2003-12-17 17:52:39 +01:00
|
|
|
debug_synth2 = atoi(des->get_flag("ivl-synth2-debug"));
|
2002-06-30 04:21:31 +02:00
|
|
|
synth2_f synth_obj;
|
|
|
|
|
des->functor(&synth_obj);
|
|
|
|
|
}
|