V0.8: rework latch synthesis and warn that async. set/clear are missing.
This patch reworks the latch synthesis that Alan submitted to be more in line with the rest of the code and simplified it in a few places. It also adds a warning that we do not currently support synthesizing asynchronous set/clear signals for a latch.
This commit is contained in:
parent
14926e7ce0
commit
11fa67ebbe
45
netlist.h
45
netlist.h
|
|
@ -1512,9 +1512,9 @@ class NetProc : public virtual LineInfo, public Attrib {
|
||||||
NetNet*nex_map, NetNet*nex_out);
|
NetNet*nex_map, NetNet*nex_out);
|
||||||
|
|
||||||
virtual bool synth_async(Design*des, NetScope*scope, bool sync_flag,
|
virtual bool synth_async(Design*des, NetScope*scope, bool sync_flag,
|
||||||
struct sync_accounting_cell*nex_ff,
|
struct sync_accounting_cell*nex_ff,
|
||||||
NetNet*nex_map, NetNet*nex_out,
|
NetNet*nex_map, NetNet*nex_out, NetNet*accum_in,
|
||||||
NetNet*accum_in, bool latch_inferred = false, NetNet *gsig = 0);
|
bool&latch_inferred, NetNet *gsig = 0);
|
||||||
|
|
||||||
// Synthesize synchronous logic, and return true. The nex_out
|
// Synthesize synchronous logic, and return true. The nex_out
|
||||||
// is where outputs are actually connected, and the nex_map
|
// is where outputs are actually connected, and the nex_map
|
||||||
|
|
@ -1660,9 +1660,9 @@ class NetAssignBase : public NetProc {
|
||||||
unsigned lwidth() const;
|
unsigned lwidth() const;
|
||||||
|
|
||||||
virtual bool synth_async(Design*des, NetScope*scope, bool sync_flag,
|
virtual bool synth_async(Design*des, NetScope*scope, bool sync_flag,
|
||||||
struct sync_accounting_cell*nex_ff,
|
struct sync_accounting_cell*nex_ff,
|
||||||
NetNet*nex_map, NetNet*nex_out,
|
NetNet*nex_map, NetNet*nex_out, NetNet*accum_in,
|
||||||
NetNet*accum_in, bool latch_inferred = false, NetNet *gsig = 0);
|
bool&latch_inferred, NetNet *gsig = 0);
|
||||||
bool synth_sync(Design*des, NetScope*scope,
|
bool synth_sync(Design*des, NetScope*scope,
|
||||||
struct sync_accounting_cell*nex_ff,
|
struct sync_accounting_cell*nex_ff,
|
||||||
NetNet*nex_map, NetNet*nex_out,
|
NetNet*nex_map, NetNet*nex_out,
|
||||||
|
|
@ -1738,8 +1738,9 @@ class NetBlock : public NetProc {
|
||||||
|
|
||||||
// synthesize as asynchronous logic, and return true.
|
// synthesize as asynchronous logic, and return true.
|
||||||
virtual bool synth_async(Design*des, NetScope*scope, bool sync_flag,
|
virtual bool synth_async(Design*des, NetScope*scope, bool sync_flag,
|
||||||
struct sync_accounting_cell*nex_ff,
|
struct sync_accounting_cell*nex_ff,
|
||||||
NetNet*nex_map, NetNet*nex_out, NetNet*accum_in, bool latch_inferred = false, NetNet *gsig = 0);
|
NetNet*nex_map, NetNet*nex_out, NetNet*accum_in,
|
||||||
|
bool&latch_inferred, NetNet *gsig = 0);
|
||||||
|
|
||||||
bool synth_sync(Design*des, NetScope*scope,
|
bool synth_sync(Design*des, NetScope*scope,
|
||||||
struct sync_accounting_cell*nex_ff,
|
struct sync_accounting_cell*nex_ff,
|
||||||
|
|
@ -1795,17 +1796,19 @@ class NetCase : public NetProc {
|
||||||
virtual void nex_output(NexusSet&out);
|
virtual void nex_output(NexusSet&out);
|
||||||
|
|
||||||
virtual bool synth_async(Design*des, NetScope*scope, bool sync_flag,
|
virtual bool synth_async(Design*des, NetScope*scope, bool sync_flag,
|
||||||
struct sync_accounting_cell*nex_ff,
|
struct sync_accounting_cell*nex_ff,
|
||||||
NetNet*nex_map, NetNet*nex_out, NetNet*accum_in, bool latch_inferred = false, NetNet *gsig = 0);
|
NetNet*nex_map, NetNet*nex_out, NetNet*accum_in,
|
||||||
|
bool&latch_inferred, NetNet *gsig = 0);
|
||||||
|
|
||||||
virtual bool emit_proc(struct target_t*) const;
|
virtual bool emit_proc(struct target_t*) const;
|
||||||
virtual void dump(ostream&, unsigned ind) const;
|
virtual void dump(ostream&, unsigned ind) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool synth_async_1hot_(Design*des, NetScope*scope, bool sync_flag,
|
bool synth_async_1hot_(Design*des, NetScope*scope, bool sync_flag,
|
||||||
struct sync_accounting_cell*nex_ff,
|
struct sync_accounting_cell*nex_ff,
|
||||||
NetNet*nex_map, NetNet*nex_out, NetNet*accum_in,
|
NetNet*nex_map, NetNet*nex_out, NetNet*accum_in,
|
||||||
NetNet*esig, unsigned hot_items);
|
NetNet*esig, unsigned hot_items,
|
||||||
|
bool&latch_inferred, NetNet *gsig = 0);
|
||||||
private:
|
private:
|
||||||
|
|
||||||
TYPE type_;
|
TYPE type_;
|
||||||
|
|
@ -1883,8 +1886,9 @@ class NetCondit : public NetProc {
|
||||||
|
|
||||||
bool is_asynchronous();
|
bool is_asynchronous();
|
||||||
virtual bool synth_async(Design*des, NetScope*scope, bool sync_flag,
|
virtual bool synth_async(Design*des, NetScope*scope, bool sync_flag,
|
||||||
struct sync_accounting_cell*nex_ff,
|
struct sync_accounting_cell*nex_ff,
|
||||||
NetNet*nex_map, NetNet*nex_out, NetNet*accum, bool latch_inferred = false, NetNet *gsig = 0);
|
NetNet*nex_map, NetNet*nex_out, NetNet*accum,
|
||||||
|
bool&latch_inferred, NetNet *gsig = 0);
|
||||||
|
|
||||||
bool synth_sync(Design*des, NetScope*scope,
|
bool synth_sync(Design*des, NetScope*scope,
|
||||||
struct sync_accounting_cell*nex_ff,
|
struct sync_accounting_cell*nex_ff,
|
||||||
|
|
@ -2117,9 +2121,9 @@ class NetEvWait : public NetProc {
|
||||||
virtual void nex_output(NexusSet&out);
|
virtual void nex_output(NexusSet&out);
|
||||||
|
|
||||||
virtual bool synth_async(Design*des, NetScope*scope, bool sync_flag,
|
virtual bool synth_async(Design*des, NetScope*scope, bool sync_flag,
|
||||||
struct sync_accounting_cell*nex_ff,
|
struct sync_accounting_cell*nex_ff,
|
||||||
NetNet*nex_map, NetNet*nex_out,
|
NetNet*nex_map, NetNet*nex_out, NetNet*accum_in,
|
||||||
NetNet*accum_in, bool latch_inferred = false, NetNet *gsig = 0);
|
bool&latch_inferred, NetNet *gsig = 0);
|
||||||
|
|
||||||
virtual bool synth_sync(Design*des, NetScope*scope,
|
virtual bool synth_sync(Design*des, NetScope*scope,
|
||||||
struct sync_accounting_cell*nex_ff,
|
struct sync_accounting_cell*nex_ff,
|
||||||
|
|
@ -2523,8 +2527,9 @@ class NetWhile : public NetProc {
|
||||||
virtual void dump(ostream&, unsigned ind) const;
|
virtual void dump(ostream&, unsigned ind) const;
|
||||||
|
|
||||||
virtual bool synth_async(Design*des, NetScope*scope, bool sync_flag,
|
virtual bool synth_async(Design*des, NetScope*scope, bool sync_flag,
|
||||||
struct sync_accounting_cell*nex_ff,
|
struct sync_accounting_cell*nex_ff,
|
||||||
NetNet*nex_map, NetNet*nex_out, NetNet*accum_in, bool latch_inferred = false, NetNet *gsig = 0);
|
NetNet*nex_map, NetNet*nex_out, NetNet*accum_in,
|
||||||
|
bool&latch_inferred, NetNet *gsig = 0);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
NetExpr* cond_;
|
NetExpr* cond_;
|
||||||
|
|
|
||||||
448
synth2.cc
448
synth2.cc
|
|
@ -51,28 +51,30 @@ bool NetProc::synth_async_noaccum(Design*des, NetScope*scope, bool sync_flag,
|
||||||
const perm_string tmp = perm_string::literal("tmp");
|
const perm_string tmp = perm_string::literal("tmp");
|
||||||
NetNet*stub = new NetNet(scope, tmp, NetNet::WIRE, nex_out->pin_count());
|
NetNet*stub = new NetNet(scope, tmp, NetNet::WIRE, nex_out->pin_count());
|
||||||
|
|
||||||
|
bool latch_inferred = false;
|
||||||
bool flag = synth_async(des, scope, sync_flag, nex_ff,
|
bool flag = synth_async(des, scope, sync_flag, nex_ff,
|
||||||
nex_map, nex_out, stub);
|
nex_map, nex_out, stub, latch_inferred);
|
||||||
|
|
||||||
delete stub;
|
delete stub;
|
||||||
return flag;
|
return flag;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NetProc::synth_async(Design*des, NetScope*scope, bool sync_flag,
|
bool NetProc::synth_async(Design*des, NetScope*scope, bool sync_flag,
|
||||||
struct sync_accounting_cell*nex_ff,
|
struct sync_accounting_cell*nex_ff,
|
||||||
NetNet*nex_map, NetNet*nex_out,
|
NetNet*nex_map, NetNet*nex_out,
|
||||||
NetNet*accum_in, bool latch_inferred, NetNet *gsig)
|
NetNet*accum_in, bool&latch_inferred, NetNet*gsig)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NetProc::synth_sync(Design*des, NetScope*scope,
|
bool NetProc::synth_sync(Design*des, NetScope*scope,
|
||||||
struct sync_accounting_cell*nex_ff,
|
struct sync_accounting_cell*nex_ff,
|
||||||
NetNet*nex_map, NetNet*nex_out,
|
NetNet*nex_map, NetNet*nex_out,
|
||||||
const svector<NetEvProbe*>&events)
|
const svector<NetEvProbe*>&events)
|
||||||
{
|
{
|
||||||
return synth_async_noaccum(des, scope, true, nex_ff, nex_map, nex_out);
|
return synth_async_noaccum(des, scope, true, nex_ff, nex_map, nex_out);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
static unsigned find_nexus_in_set(const NetNet*nset, const Nexus*nex)
|
static unsigned find_nexus_in_set(const NetNet*nset, const Nexus*nex)
|
||||||
{
|
{
|
||||||
|
|
@ -84,10 +86,12 @@ static unsigned find_nexus_in_set(const NetNet*nset, const Nexus*nex)
|
||||||
return idx;
|
return idx;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
struct nexus_map_t {
|
struct nexus_map_t {
|
||||||
const Nexus*nex;
|
const Nexus*nex;
|
||||||
int idx;
|
int idx;
|
||||||
};
|
};
|
||||||
|
|
||||||
static int ncp_compare(const void*p1, const void*p2)
|
static int ncp_compare(const void*p1, const void*p2)
|
||||||
{
|
{
|
||||||
const Nexus*a1 = ((const struct nexus_map_t*)p1) -> nex;
|
const Nexus*a1 = ((const struct nexus_map_t*)p1) -> nex;
|
||||||
|
|
@ -138,9 +142,10 @@ static int map_nexus_in_index(struct nexus_map_t*table, size_t ntable,
|
||||||
*/
|
*/
|
||||||
|
|
||||||
bool NetAssignBase::synth_async(Design*des, NetScope*scope, bool sync_flag,
|
bool NetAssignBase::synth_async(Design*des, NetScope*scope, bool sync_flag,
|
||||||
struct sync_accounting_cell*nex_ff,
|
struct sync_accounting_cell*nex_ff,
|
||||||
NetNet*nex_map, NetNet*nex_out,
|
NetNet*nex_map, NetNet*nex_out,
|
||||||
NetNet*accum_in, bool latch_inferred, NetNet *gsig)
|
NetNet*accum_in, bool&latch_inferred,
|
||||||
|
NetNet*gsig)
|
||||||
{
|
{
|
||||||
NetNet*rsig = rval_->synthesize(des);
|
NetNet*rsig = rval_->synthesize(des);
|
||||||
if (rsig == 0) {
|
if (rsig == 0) {
|
||||||
|
|
@ -199,138 +204,126 @@ bool NetAssignBase::synth_async(Design*des, NetScope*scope, bool sync_flag,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( latch_inferred )
|
if (latch_inferred) {
|
||||||
{
|
/* We do not support a bmux with a latch. */
|
||||||
// By this point any bmux() has been dealt with. Panic if that is not so.
|
assert(!cur->bmux());
|
||||||
assert( !cur->bmux() );
|
|
||||||
|
|
||||||
// Bind the outputs that we do make to the nex_out. Use the nex_map to map the l-value bit position to the nex_out bit position.
|
/* 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);
|
||||||
|
|
||||||
struct nexus_map_t *nex_map_idx = make_nexus_index( nex_map );
|
/* 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);
|
||||||
|
|
||||||
assert( cur->sig()->msb() - cur->sig()->lsb() >= 0L &&
|
for (unsigned idx = 0U; idx < cur->lwidth(); idx += 1U) {
|
||||||
static_cast< unsigned long >( cur->sig()->msb() - cur->sig()->lsb() ) <= static_cast< unsigned long >( UINT_MAX ) - 1UL );
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
NetLatch *const latchPtr = new NetLatch( scope, cur->sig()->name(), static_cast< unsigned >( cur->sig()->msb() - cur->sig()->lsb() ) + 1U );
|
|
||||||
latchPtr->set_line( *this );
|
|
||||||
|
|
||||||
for ( unsigned idx = 0U; idx < cur->lwidth(); idx += 1U )
|
|
||||||
{
|
|
||||||
unsigned off = cur->get_loff() + idx;
|
unsigned off = cur->get_loff() + idx;
|
||||||
int tmp = map_nexus_in_index( nex_map_idx, nex_map->pin_count(), lsig->pin( off ).nexus() );
|
int tmp = map_nexus_in_index(nex_map_idx,
|
||||||
assert( tmp >= 0 );
|
nex_map->pin_count(),
|
||||||
unsigned ptr = static_cast< unsigned >( tmp );
|
lsig->pin(off).nexus());
|
||||||
connect( latchPtr->pin_Data( idx ), rsig->pin( roff + idx ) );
|
assert(tmp >= 0);
|
||||||
connect( nex_out->pin( ptr ), latchPtr->pin_Q( idx ) );
|
unsigned ptr = tmp;
|
||||||
connect( latchPtr->pin_Clock(), gsig->pin( 0 ) );
|
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));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
|
||||||
des->add_node( latchPtr );
|
/* 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));
|
||||||
|
}
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
delete[] nex_map_idx;
|
||||||
|
|
||||||
|
/* The r-value (1 bit) connects to the WriteData
|
||||||
|
input of the demux. */
|
||||||
|
connect(dq->pin_WriteData(0), rsig->pin(roff));
|
||||||
|
|
||||||
|
roff += cur->lwidth();
|
||||||
|
cur->turn_sig_to_wire_on_release();
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
catch ( bad_alloc &memoryAllocationException )
|
/* By this point any bmux() has been dealt with. Panic
|
||||||
{
|
if that is not so. */
|
||||||
cerr << "Exception occurred: " << memoryAllocationException.what() << endl;
|
assert(! cur->bmux());
|
||||||
cerr << get_line() << ": error: NetAssignBase::synth_async on failure to create latch at lval ";
|
|
||||||
dump_lval( cerr );
|
|
||||||
cerr << endl;
|
|
||||||
des->errors += 1;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
|
|
||||||
/* Handle the special case that this is a decoded
|
|
||||||
enable. generate a demux for the device, with the
|
|
||||||
WriteData connected to the r-value and the Data
|
|
||||||
vector connected to the feedback. */
|
|
||||||
if (cur->bmux() != 0) {
|
|
||||||
assert(sync_flag);
|
|
||||||
|
|
||||||
NetNet*adr = cur->bmux()->synthesize(des);
|
|
||||||
|
|
||||||
/* Create a NetEemux wide enough to connect to all
|
|
||||||
the bits of the lvalue signal (generally more
|
|
||||||
then the bits of lwidth). */
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/* Bind the outputs that we do make to the nex_out. Use
|
||||||
|
the nex_map to map the l-value bit position to the
|
||||||
|
nex_out bit position. */
|
||||||
struct nexus_map_t*nex_map_idx = make_nexus_index(nex_map);
|
struct nexus_map_t*nex_map_idx = make_nexus_index(nex_map);
|
||||||
|
|
||||||
for (unsigned idx = 0; idx < lsig->pin_count(); idx += 1) {
|
for (unsigned idx = 0 ; idx < cur->lwidth() ; idx += 1) {
|
||||||
unsigned off = cur->get_loff()+idx;
|
unsigned off = cur->get_loff()+idx;
|
||||||
int tmp = map_nexus_in_index(nex_map_idx,
|
int tmp = map_nexus_in_index(nex_map_idx,
|
||||||
nex_map->pin_count(),
|
nex_map->pin_count(),
|
||||||
lsig->pin(off).nexus());
|
lsig->pin(off).nexus());
|
||||||
assert(tmp >= 0);
|
assert(tmp >= 0);
|
||||||
unsigned ptr = tmp;
|
unsigned ptr = tmp;
|
||||||
connect(nex_out->pin(ptr), dq->pin_Q(idx));
|
connect(nex_out->pin(ptr), rsig->pin(roff+idx));
|
||||||
}
|
}
|
||||||
|
|
||||||
delete[]nex_map_idx;
|
}
|
||||||
|
|
||||||
/* The r-value (1 bit) connects to the WriteData
|
|
||||||
input of the demux. */
|
|
||||||
connect(dq->pin_WriteData(0), rsig->pin(roff));
|
|
||||||
|
|
||||||
roff += cur->lwidth();
|
|
||||||
cur->turn_sig_to_wire_on_release();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* By this point ant bmux() has been dealt with. Panic
|
|
||||||
if that is not so. */
|
|
||||||
assert(! cur->bmux());
|
|
||||||
|
|
||||||
/* 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);
|
|
||||||
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
roff += cur->lwidth();
|
roff += cur->lwidth();
|
||||||
|
|
||||||
/* This lval_ represents a reg that is a WIRE in the
|
/* This lval_ represents a reg that is a WIRE in the
|
||||||
synthesized results. This function signals the destructor
|
synthesized results. This function signals the destructor
|
||||||
to change the REG that this l-value refers to into a
|
to change the REG that this l-value refers to into a
|
||||||
WIRE. It is done then, at the last minute, so that pending
|
WIRE. It is done then, at the last minute, so that pending
|
||||||
synthesis can continue to work with it as a WIRE. */
|
synthesis can continue to work with it as a WIRE. */
|
||||||
cur->turn_sig_to_wire_on_release();
|
cur->turn_sig_to_wire_on_release();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -444,8 +437,9 @@ bool NetAssignBase::synth_async_mem_sync_(Design*des, NetScope*scope,
|
||||||
* substatements.
|
* substatements.
|
||||||
*/
|
*/
|
||||||
bool NetBlock::synth_async(Design*des, NetScope*scope, bool sync_flag,
|
bool NetBlock::synth_async(Design*des, NetScope*scope, bool sync_flag,
|
||||||
struct sync_accounting_cell*nex_ff,
|
struct sync_accounting_cell*nex_ff,
|
||||||
NetNet*nex_map, NetNet*nex_out, NetNet*accum_in, bool latch_inferred, NetNet *gsig)
|
NetNet*nex_map, NetNet*nex_out, NetNet*accum_in,
|
||||||
|
bool&latch_inferred, NetNet*gsig)
|
||||||
{
|
{
|
||||||
if (last_ == 0) {
|
if (last_ == 0) {
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -516,7 +510,8 @@ bool NetBlock::synth_async(Design*des, NetScope*scope, bool sync_flag,
|
||||||
delete [] nex_map_idx;
|
delete [] nex_map_idx;
|
||||||
|
|
||||||
bool ok_flag = cur->synth_async(des, scope, sync_flag, nex_ff,
|
bool ok_flag = cur->synth_async(des, scope, sync_flag, nex_ff,
|
||||||
tmp_map, tmp_out, new_accum);
|
tmp_map, tmp_out, new_accum,
|
||||||
|
latch_inferred, gsig);
|
||||||
flag = flag && ok_flag;
|
flag = flag && ok_flag;
|
||||||
|
|
||||||
delete new_accum;
|
delete new_accum;
|
||||||
|
|
@ -615,8 +610,9 @@ bool NetBlock::synth_async(Design*des, NetScope*scope, bool sync_flag,
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NetCase::synth_async(Design*des, NetScope*scope, bool sync_flag,
|
bool NetCase::synth_async(Design*des, NetScope*scope, bool sync_flag,
|
||||||
struct sync_accounting_cell*nex_ff,
|
struct sync_accounting_cell*nex_ff,
|
||||||
NetNet*nex_map, NetNet*nex_out, NetNet*accum, bool latch_inferred, NetNet *gsig)
|
NetNet*nex_map, NetNet*nex_out, NetNet*accum,
|
||||||
|
bool&latch_inferred, NetNet*gsig)
|
||||||
{
|
{
|
||||||
unsigned cur;
|
unsigned cur;
|
||||||
|
|
||||||
|
|
@ -671,16 +667,16 @@ bool NetCase::synth_async(Design*des, NetScope*scope, bool sync_flag,
|
||||||
nondefault_items += 1;
|
nondefault_items += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Handle the special case that this can be done it a smaller
|
/* Handle the special case that this can be done in a smaller
|
||||||
1-hot MUX. If there are fewer active cases then there are
|
1-hot MUX. If there are fewer active cases then there are
|
||||||
select pins, then a 1-hot encoding should be better. */
|
select pins, then a 1-hot encoding should be better. */
|
||||||
if (nondefault_items < sel_pins) {
|
if (nondefault_items < sel_pins) {
|
||||||
if (debug_synth)
|
if (debug_synth)
|
||||||
cerr << get_line() << ": debug: "
|
cerr << get_line() << ": debug: "
|
||||||
<< "Implement case statement as 1-hot MUX." << endl;
|
<< "Implement case statement as 1-hot MUX." << endl;
|
||||||
return synth_async_1hot_(des, scope, sync_flag, nex_ff,
|
return synth_async_1hot_(des, scope, sync_flag, nex_ff, nex_map,
|
||||||
nex_map, nex_out, accum,
|
nex_out, accum, esig, nondefault_items,
|
||||||
esig, nondefault_items);
|
latch_inferred, gsig);
|
||||||
}
|
}
|
||||||
|
|
||||||
NetMux*mux = new NetMux(scope, scope->local_symbol(),
|
NetMux*mux = new NetMux(scope, scope->local_symbol(),
|
||||||
|
|
@ -889,8 +885,8 @@ bool NetCase::synth_async(Design*des, NetScope*scope, bool sync_flag,
|
||||||
/* Synthesize this specified case. The synth_async will
|
/* Synthesize this specified case. The synth_async will
|
||||||
connect all the output bits it knows how to the sig net. */
|
connect all the output bits it knows how to the sig net. */
|
||||||
statement_map[item]->synth_async(des, scope, sync_flag,
|
statement_map[item]->synth_async(des, scope, sync_flag,
|
||||||
nex_ff,
|
nex_ff, nex_map, sig, accum,
|
||||||
nex_map, sig, accum);
|
latch_inferred, gsig);
|
||||||
|
|
||||||
for (unsigned idx = 0 ; idx < mux->width() ; idx += 1) {
|
for (unsigned idx = 0 ; idx < mux->width() ; idx += 1) {
|
||||||
if (sig->pin(idx).is_linked())
|
if (sig->pin(idx).is_linked())
|
||||||
|
|
@ -960,9 +956,10 @@ bool NetCase::synth_async(Design*des, NetScope*scope, bool sync_flag,
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NetCase::synth_async_1hot_(Design*des, NetScope*scope, bool sync_flag,
|
bool NetCase::synth_async_1hot_(Design*des, NetScope*scope, bool sync_flag,
|
||||||
struct sync_accounting_cell*nex_ff,
|
struct sync_accounting_cell*nex_ff,
|
||||||
NetNet*nex_map, NetNet*nex_out, NetNet*accum,
|
NetNet*nex_map, NetNet*nex_out, NetNet*accum,
|
||||||
NetNet*esig, unsigned hot_items)
|
NetNet*esig, unsigned hot_items,
|
||||||
|
bool&latch_inferred, NetNet*gsig)
|
||||||
{
|
{
|
||||||
unsigned sel_pins = hot_items;
|
unsigned sel_pins = hot_items;
|
||||||
|
|
||||||
|
|
@ -1013,10 +1010,11 @@ bool NetCase::synth_async_1hot_(Design*des, NetScope*scope, bool sync_flag,
|
||||||
connect(mux->pin_Sel(use_item), reduc->pin(0));
|
connect(mux->pin_Sel(use_item), reduc->pin(0));
|
||||||
|
|
||||||
NetNet*item_sig = new NetNet(scope, scope->local_symbol(),
|
NetNet*item_sig = new NetNet(scope, scope->local_symbol(),
|
||||||
NetNet::WIRE, nex_map->pin_count());
|
NetNet::WIRE, nex_map->pin_count());
|
||||||
assert(items_[item].statement);
|
assert(items_[item].statement);
|
||||||
items_[item].statement->synth_async(des, scope, sync_flag, nex_ff,
|
items_[item].statement->synth_async(des, scope, sync_flag, nex_ff,
|
||||||
nex_map, item_sig, accum);
|
nex_map, item_sig, accum,
|
||||||
|
latch_inferred, gsig);
|
||||||
for (unsigned idx = 0 ; idx < item_sig->pin_count() ; idx += 1)
|
for (unsigned idx = 0 ; idx < item_sig->pin_count() ; idx += 1)
|
||||||
connect(mux->pin_Data(idx, 1<<use_item), item_sig->pin(idx));
|
connect(mux->pin_Data(idx, 1<<use_item), item_sig->pin(idx));
|
||||||
|
|
||||||
|
|
@ -1031,9 +1029,10 @@ bool NetCase::synth_async_1hot_(Design*des, NetScope*scope, bool sync_flag,
|
||||||
NetNet*default_sig = 0;
|
NetNet*default_sig = 0;
|
||||||
if (default_statement) {
|
if (default_statement) {
|
||||||
default_sig = new NetNet(scope, scope->local_symbol(),
|
default_sig = new NetNet(scope, scope->local_symbol(),
|
||||||
NetNet::WIRE, nex_map->pin_count());
|
NetNet::WIRE, nex_map->pin_count());
|
||||||
default_statement->synth_async(des, scope, sync_flag, nex_ff,
|
default_statement->synth_async(des, scope, sync_flag, nex_ff,
|
||||||
nex_map, default_sig, accum);
|
nex_map, default_sig, accum,
|
||||||
|
latch_inferred, gsig);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1079,12 +1078,13 @@ bool NetCase::synth_async_1hot_(Design*des, NetScope*scope, bool sync_flag,
|
||||||
/*
|
/*
|
||||||
* Handle synthesis for an asynchronous condition statement. If we get
|
* 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
|
* here, we know that the CE of a DFF has already been filled, so the
|
||||||
* 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.
|
* 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.
|
||||||
*/
|
*/
|
||||||
bool NetCondit::synth_async(Design*des, NetScope*scope, bool sync_flag,
|
bool NetCondit::synth_async(Design*des, NetScope*scope, bool sync_flag,
|
||||||
struct sync_accounting_cell*nex_ff,
|
struct sync_accounting_cell*nex_ff,
|
||||||
NetNet*nex_map, NetNet*nex_out,
|
NetNet*nex_map, NetNet*nex_out,
|
||||||
NetNet*accum, bool latch_inferred, NetNet *gsig)
|
NetNet*accum, bool&latch_inferred, NetNet*gsig)
|
||||||
{
|
{
|
||||||
/* Detect the special case that this is a nul-effect (for
|
/* Detect the special case that this is a nul-effect (for
|
||||||
synthesis) statement. This happens, for example, for code
|
synthesis) statement. This happens, for example, for code
|
||||||
|
|
@ -1119,66 +1119,50 @@ bool NetCondit::synth_async(Design*des, NetScope*scope, bool sync_flag,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// At least one of the clauses must have contents. */
|
/* At least one of the clauses must have content. */
|
||||||
assert(if_ != 0 || else_ != 0);
|
assert(if_ != 0 || else_ != 0);
|
||||||
|
|
||||||
// This is the beginning of where it will be decided whether or not a latch is inferred.
|
/* Latches cannot nest! */
|
||||||
// We prefer that latch_inferred be a local variable rather than a function parameter.
|
assert(!latch_inferred);
|
||||||
// It must be a function parameter to match the signature of the function that we are overriding in the base class, the result of a chain reaction caused by adding that
|
|
||||||
// parameter to NetAssignBase::synth_async (to give that function awareness of what we are recognizing here).
|
|
||||||
// Of course, the parameter and the local variable could have been given distinct names.
|
|
||||||
// Instead, we simply assert that the following code governs.
|
|
||||||
// In other words, the parameter value coming into this function does not override the value (false) that would be the initial value of a local variable here.
|
|
||||||
// Probably the incoming parameter got this value from the default argument, but it is not essential to prove that.
|
|
||||||
assert( latch_inferred == false );
|
|
||||||
|
|
||||||
/* If there is no default_sig, and if this is a fully
|
/* If there is no default_sig, and if this is a fully
|
||||||
asynchronous process (nex_map is not a synchronous output)
|
asynchronous process (nex_map is not a synchronous output)
|
||||||
then, if !(both clauses are present), a latch is inferred.
|
then, if a clause is missing, a latch will be inferred.
|
||||||
|
|
||||||
If either clause is missing, and the output is synchronous,
|
If either clause is missing, and the output is synchronous,
|
||||||
then the code below can take as the input the output from
|
then the code below can take as the input the output from
|
||||||
the DFF without worry for asynchronous cycles. */
|
the DFF without worry for asynchronous cycles. */
|
||||||
if (default_sig == 0 && ! sync_flag) {
|
if (default_sig == 0 && ! sync_flag && (if_ == 0 || else_ == 0)) {
|
||||||
if (if_ == 0) {
|
latch_inferred = true;
|
||||||
latch_inferred = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (else_ == 0) {
|
|
||||||
latch_inferred = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
bool my_latch_inferred = latch_inferred;
|
||||||
|
|
||||||
NetNet*asig = new NetNet(scope, scope->local_symbol(),
|
NetNet*asig = new NetNet(scope, scope->local_symbol(),
|
||||||
NetNet::WIRE, nex_map->pin_count());
|
NetNet::WIRE, nex_map->pin_count());
|
||||||
bool asigIsLatchOutput = false;
|
|
||||||
asig->local_flag(true);
|
asig->local_flag(true);
|
||||||
|
|
||||||
if (if_ == 0) {
|
if (if_ == 0) {
|
||||||
/* If the if clause is missing, then take the clause to
|
/* If the if clause is missing, then take the clause to
|
||||||
be an assignment from the defaults input. If there is
|
be an assignment from the default input. If there is
|
||||||
no defaults input and a latch is not inferred, then take the input to be from the
|
no default input and a latch is not inferred, then
|
||||||
output. */
|
take the input to be from the output (sync). */
|
||||||
if (default_sig) {
|
if (default_sig) {
|
||||||
for (unsigned idx = 0 ; idx < asig->pin_count() ; idx += 1)
|
for (unsigned idx = 0 ; idx < asig->pin_count() ; idx += 1)
|
||||||
connect(asig->pin(idx), default_sig->pin(idx));
|
connect(asig->pin(idx), default_sig->pin(idx));
|
||||||
} else {
|
} else if (latch_inferred) {
|
||||||
if ( latch_inferred )
|
|
||||||
{
|
|
||||||
delete asig ;
|
delete asig ;
|
||||||
}
|
asig = 0;
|
||||||
else
|
} else {
|
||||||
{
|
assert(sync_flag);
|
||||||
assert( sync_flag );
|
|
||||||
|
|
||||||
for (unsigned idx = 0 ; idx < asig->pin_count() ; idx += 1)
|
for (unsigned idx = 0 ; idx < asig->pin_count() ; idx += 1)
|
||||||
connect(asig->pin(idx), nex_map->pin(idx));
|
connect(asig->pin(idx), nex_map->pin(idx));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
bool flag = if_->synth_async(des, scope, sync_flag, nex_ff,
|
bool flag = if_->synth_async(des, scope, sync_flag, nex_ff,
|
||||||
nex_map, asig, accum, latch_inferred, ssig);
|
nex_map, asig, accum, latch_inferred,
|
||||||
|
ssig);
|
||||||
if (!flag) {
|
if (!flag) {
|
||||||
delete asig;
|
delete asig;
|
||||||
cerr << get_line() << ": error: Asynchronous if statement"
|
cerr << get_line() << ": error: Asynchronous if statement"
|
||||||
|
|
@ -1186,34 +1170,33 @@ bool NetCondit::synth_async(Design*des, NetScope*scope, bool sync_flag,
|
||||||
des->errors += 1;
|
des->errors += 1;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
asigIsLatchOutput = latch_inferred;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NetNet*bsig = new NetNet(scope, scope->local_symbol(),
|
NetNet*bsig = new NetNet(scope, scope->local_symbol(),
|
||||||
NetNet::WIRE, nex_map->pin_count());
|
NetNet::WIRE, nex_map->pin_count());
|
||||||
bool bsigIsLatchOutput = false;
|
|
||||||
bsig->local_flag(true);
|
bsig->local_flag(true);
|
||||||
|
|
||||||
if (else_ == 0) {
|
if (else_ == 0) {
|
||||||
|
/* 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). */
|
||||||
if (default_sig) {
|
if (default_sig) {
|
||||||
for (unsigned idx = 0 ; idx < asig->pin_count() ; idx += 1)
|
for (unsigned idx = 0 ; idx < asig->pin_count() ; idx += 1)
|
||||||
connect(bsig->pin(idx), default_sig->pin(idx));
|
connect(bsig->pin(idx), default_sig->pin(idx));
|
||||||
} else {
|
} else if (latch_inferred) {
|
||||||
if ( latch_inferred )
|
|
||||||
{
|
|
||||||
delete bsig;
|
delete bsig;
|
||||||
}
|
bsig = 0;
|
||||||
else
|
} else {
|
||||||
{
|
assert(sync_flag);
|
||||||
assert( sync_flag );
|
|
||||||
|
|
||||||
for (unsigned idx = 0 ; idx < asig->pin_count() ; idx += 1)
|
for (unsigned idx = 0 ; idx < asig->pin_count() ; idx += 1)
|
||||||
connect(bsig->pin(idx), nex_map->pin(idx));
|
connect(bsig->pin(idx), nex_map->pin(idx));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
bool flag = else_->synth_async(des, scope, sync_flag, nex_ff,
|
bool flag = else_->synth_async(des, scope, sync_flag, nex_ff,
|
||||||
nex_map, bsig, accum, latch_inferred, ssig);
|
nex_map, bsig, accum,
|
||||||
|
latch_inferred, ssig);
|
||||||
if (!flag) {
|
if (!flag) {
|
||||||
delete asig;
|
delete asig;
|
||||||
delete bsig;
|
delete bsig;
|
||||||
|
|
@ -1222,38 +1205,30 @@ bool NetCondit::synth_async(Design*des, NetScope*scope, bool sync_flag,
|
||||||
des->errors += 1;
|
des->errors += 1;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
bsigIsLatchOutput = latch_inferred;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( latch_inferred )
|
/* If we have inferred a latch then connect the appropriate signal to
|
||||||
{
|
* the output. */
|
||||||
// The value of a pointer that refers to deallocated storage is indeterminate, so I can't examine asig and bsig to determine which one hasn't been deallocated.
|
if (latch_inferred) {
|
||||||
assert ( ( asigIsLatchOutput || bsigIsLatchOutput ) && !( asigIsLatchOutput && bsigIsLatchOutput ) ); // logical exclusive OR
|
if (!my_latch_inferred) {
|
||||||
|
cerr << get_line() << ": sorry: D-latch asynchronous "
|
||||||
if ( asigIsLatchOutput )
|
<< "set/clear synthesis is not currently supported."
|
||||||
{
|
<< endl;
|
||||||
asig->set_line( *this );
|
des->errors += 1;
|
||||||
|
return false;
|
||||||
for ( unsigned idx = 0U; idx < nex_out->pin_count(); idx += 1U )
|
|
||||||
{
|
|
||||||
connect( nex_out->pin( idx ), asig->pin( idx ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
else // bsigIsLatchOutput
|
if (asig) {
|
||||||
{
|
assert(bsig == 0);
|
||||||
bsig->set_line( *this );
|
asig->set_line( *this );
|
||||||
|
for (unsigned idx = 0; idx < nex_out->pin_count(); idx += 1)
|
||||||
for ( unsigned idx = 0U; idx < nex_out->pin_count(); idx += 1U )
|
connect(nex_out->pin(idx), asig->pin(idx));
|
||||||
{
|
} else {
|
||||||
connect( nex_out->pin( idx ), bsig->pin( idx ) );
|
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));
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
} // end if latch_inferred
|
|
||||||
else
|
|
||||||
{
|
|
||||||
unsigned mux_width = 0;
|
unsigned mux_width = 0;
|
||||||
|
|
||||||
/* Figure out how many mux bits we are going to need. */
|
/* Figure out how many mux bits we are going to need. */
|
||||||
|
|
@ -1355,13 +1330,8 @@ bool NetCondit::synth_async(Design*des, NetScope*scope, bool sync_flag,
|
||||||
des->errors += 1;
|
des->errors += 1;
|
||||||
return_flag = false;
|
return_flag = false;
|
||||||
#else
|
#else
|
||||||
/* This should check that asig is latched by
|
/* Assume that asig is used internally. Note the
|
||||||
the condition select or is used
|
* similarity to the latch inferred code. */
|
||||||
internally by the false clause. but since
|
|
||||||
there is no latch support, assume it is
|
|
||||||
used internally. */
|
|
||||||
// Now there is latch support, but if a latch is inferred this code will not be reached.
|
|
||||||
// Note, however, the similarity to the latch inferred code.
|
|
||||||
connect(nex_out->pin(idx), asig->pin(idx));
|
connect(nex_out->pin(idx), asig->pin(idx));
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
@ -1386,13 +1356,8 @@ bool NetCondit::synth_async(Design*des, NetScope*scope, bool sync_flag,
|
||||||
des->errors += 1;
|
des->errors += 1;
|
||||||
return_flag = false;
|
return_flag = false;
|
||||||
#else
|
#else
|
||||||
/* This should check that bsig is latched by
|
/* Assume that asig is used internally. Note the
|
||||||
the condition select or is used
|
* similarity to the latch inferred code. */
|
||||||
internally by the false clause. but since
|
|
||||||
there is no latch support, assume it is
|
|
||||||
used internally. */
|
|
||||||
// Now there is latch support, but if a latch is inferred this code will not be reached.
|
|
||||||
// Note, however, the similarity to the latch inferred code.
|
|
||||||
connect(nex_out->pin(idx), bsig->pin(idx));
|
connect(nex_out->pin(idx), bsig->pin(idx));
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
@ -1425,17 +1390,19 @@ bool NetCondit::synth_async(Design*des, NetScope*scope, bool sync_flag,
|
||||||
|
|
||||||
|
|
||||||
des->add_node(mux);
|
des->add_node(mux);
|
||||||
} // end if !latch_inferred
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NetEvWait::synth_async(Design*des, NetScope*scope, bool sync_flag,
|
bool NetEvWait::synth_async(Design*des, NetScope*scope, bool sync_flag,
|
||||||
sync_accounting_cell*nex_ff,
|
sync_accounting_cell*nex_ff,
|
||||||
NetNet*nex_map, NetNet*nex_out, NetNet*accum_in, bool latch_inferred, NetNet *gsig)
|
NetNet*nex_map, NetNet*nex_out, NetNet*accum_in,
|
||||||
|
bool&latch_inferred, NetNet*gsig)
|
||||||
{
|
{
|
||||||
bool flag = statement_->synth_async(des, scope, sync_flag, nex_ff,
|
bool flag = statement_->synth_async(des, scope, sync_flag, nex_ff,
|
||||||
nex_map, nex_out, accum_in);
|
nex_map, nex_out, accum_in,
|
||||||
|
latch_inferred, gsig);
|
||||||
return flag;
|
return flag;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2510,8 +2477,9 @@ bool NetEvWait::synth_sync(Design*des, NetScope*scope,
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NetWhile::synth_async(Design*des, NetScope*scope, bool sync_flag,
|
bool NetWhile::synth_async(Design*des, NetScope*scope, bool sync_flag,
|
||||||
struct sync_accounting_cell*nex_ff,
|
struct sync_accounting_cell*nex_ff,
|
||||||
NetNet*nex_map, NetNet*nex_out, NetNet*accum_in, bool latch_inferred, NetNet *gsig)
|
NetNet*nex_map, NetNet*nex_out, NetNet*accum_in,
|
||||||
|
bool&latch_inferred, NetNet*gsig)
|
||||||
{
|
{
|
||||||
cerr << get_line()
|
cerr << get_line()
|
||||||
<< ": error: Cannot synthesize for or while loops."
|
<< ": error: Cannot synthesize for or while loops."
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue