Merge branch 'master' of github.com:steveicarus/iverilog

This commit is contained in:
Stephen Williams 2016-03-14 12:26:12 -07:00
commit 78a49eacef
31 changed files with 604 additions and 83 deletions

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 1998-2015 Stephen Williams (steve@icarus.com) * Copyright (c) 1998-2016 Stephen Williams (steve@icarus.com)
* *
* This source code is free software; you can redistribute it * This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU * and/or modify it in source code form under the terms of the GNU
@ -688,6 +688,14 @@ void NetFF::dump_node(ostream&o, unsigned ind) const
dump_obj_attr(o, ind+4); dump_obj_attr(o, ind+4);
} }
void NetLatch::dump_node(ostream&o, unsigned ind) const
{
o << setw(ind) << "" << "LPM_LATCH: " << name()
<< " scope=" << scope_path(scope()) << endl;
dump_node_pins(o, ind+4);
dump_obj_attr(o, ind+4);
}
void NetLiteral::dump_node(ostream&o, unsigned ind) const void NetLiteral::dump_node(ostream&o, unsigned ind) const
{ {
o << setw(ind) << "" << "constant real " << real_ o << setw(ind) << "" << "constant real " << real_

View File

@ -1408,7 +1408,8 @@ void PGModule::elaborate_mod_(Design*des, Module*rmod, NetScope*scope) const
// that connects to the port. // that connects to the port.
NetNet*sig = 0; NetNet*sig = 0;
if (prts.empty() || (prts[0]->port_type() == NetNet::PINPUT)) { NetNet::PortType ptype = prts[0]->port_type();
if (prts.empty() || (ptype == NetNet::PINPUT)) {
// Special case: If the input port is an unpacked // Special case: If the input port is an unpacked
// array, then there should be no sub-ports and // array, then there should be no sub-ports and
@ -1501,7 +1502,7 @@ void PGModule::elaborate_mod_(Design*des, Module*rmod, NetScope*scope) const
sig->vector_width()); sig->vector_width());
} }
} else if (prts[0]->port_type() == NetNet::PINOUT) { } else if (ptype == NetNet::PINOUT) {
// For now, do not support unpacked array outputs. // For now, do not support unpacked array outputs.
ivl_assert(*this, prts[0]->unpacked_dimensions()==0); ivl_assert(*this, prts[0]->unpacked_dimensions()==0);
@ -1562,7 +1563,7 @@ void PGModule::elaborate_mod_(Design*des, Module*rmod, NetScope*scope) const
} else { } else {
/* Port type must be OUTPUT here. */ /* Port type must be OUTPUT here. */
ivl_assert(*this, prts[0]->port_type() == NetNet::POUTPUT); ivl_assert(*this, ptype == NetNet::POUTPUT);
// Special case: If the output port is an unpacked // Special case: If the output port is an unpacked
// array, then there should be no sub-ports and // array, then there should be no sub-ports and
@ -1625,11 +1626,9 @@ void PGModule::elaborate_mod_(Design*des, Module*rmod, NetScope*scope) const
} }
prts_vector_width = sig->vector_width(); prts_vector_width = sig->vector_width();
for (unsigned pidx = 0; pidx < prts.size(); pidx += 1) { for (unsigned pidx = 0; pidx < prts.size(); pidx += 1) {
prts[pidx]->port_type(NetNet::NOT_A_PORT);
prts[pidx] = cast_to_int4(des, scope, prts[pidx], prts[pidx] = cast_to_int4(des, scope, prts[pidx],
prts_vector_width / prts_vector_width /
instance.size()); instance.size());
prts[pidx]->port_type(NetNet::POUTPUT);
} }
} }
@ -1638,9 +1637,7 @@ void PGModule::elaborate_mod_(Design*des, Module*rmod, NetScope*scope) const
if ((sig->data_type() == IVL_VT_REAL ) && if ((sig->data_type() == IVL_VT_REAL ) &&
!prts.empty() && (prts[0]->data_type() != IVL_VT_REAL )) { !prts.empty() && (prts[0]->data_type() != IVL_VT_REAL )) {
prts_vector_width -= prts[0]->vector_width() - 1; prts_vector_width -= prts[0]->vector_width() - 1;
prts[0]->port_type(NetNet::NOT_A_PORT);
prts[0] = cast_to_real(des, scope, prts[0]); prts[0] = cast_to_real(des, scope, prts[0]);
prts[0]->port_type(NetNet::POUTPUT);
// No support for multiple real drivers. // No support for multiple real drivers.
if (instance.size() != 1) { if (instance.size() != 1) {
cerr << pins[idx]->get_fileline() << ": error: " cerr << pins[idx]->get_fileline() << ": error: "
@ -1658,10 +1655,8 @@ void PGModule::elaborate_mod_(Design*des, Module*rmod, NetScope*scope) const
if ((sig->data_type() == IVL_VT_BOOL ) && if ((sig->data_type() == IVL_VT_BOOL ) &&
!prts.empty() && (prts[0]->data_type() == IVL_VT_LOGIC )) { !prts.empty() && (prts[0]->data_type() == IVL_VT_LOGIC )) {
for (unsigned pidx = 0; pidx < prts.size(); pidx += 1) { for (unsigned pidx = 0; pidx < prts.size(); pidx += 1) {
prts[pidx]->port_type(NetNet::NOT_A_PORT);
prts[pidx] = cast_to_int2(des, scope, prts[pidx], prts[pidx] = cast_to_int2(des, scope, prts[pidx],
prts[pidx]->vector_width()); prts[pidx]->vector_width());
prts[pidx]->port_type(NetNet::POUTPUT);
} }
} }
@ -1685,7 +1680,7 @@ void PGModule::elaborate_mod_(Design*des, Module*rmod, NetScope*scope) const
#ifndef NDEBUG #ifndef NDEBUG
if ((! prts.empty()) if ((! prts.empty())
&& (prts[0]->port_type() != NetNet::PINPUT)) { && (ptype != NetNet::PINPUT)) {
assert(sig->type() != NetNet::REG); assert(sig->type() != NetNet::REG);
} }
#endif #endif
@ -1718,7 +1713,7 @@ void PGModule::elaborate_mod_(Design*des, Module*rmod, NetScope*scope) const
&& (prts_vector_width != sig->vector_width())) { && (prts_vector_width != sig->vector_width())) {
bool as_signed = false; bool as_signed = false;
switch (prts[0]->port_type()) { switch (ptype) {
case NetNet::POUTPUT: case NetNet::POUTPUT:
as_signed = prts[0]->get_signed(); as_signed = prts[0]->get_signed();
break; break;
@ -1742,7 +1737,7 @@ void PGModule::elaborate_mod_(Design*des, Module*rmod, NetScope*scope) const
" bits, got " << sig->vector_width() << "." << endl; " bits, got " << sig->vector_width() << "." << endl;
// Delete this when inout ports pad correctly. // Delete this when inout ports pad correctly.
if (prts[0]->port_type() == NetNet::PINOUT) { if (ptype == NetNet::PINOUT) {
if (prts_vector_width > sig->vector_width()) { if (prts_vector_width > sig->vector_width()) {
cerr << get_fileline() << ": : Leaving " cerr << get_fileline() << ": : Leaving "
<< (prts_vector_width-sig->vector_width()) << (prts_vector_width-sig->vector_width())
@ -1762,7 +1757,7 @@ void PGModule::elaborate_mod_(Design*des, Module*rmod, NetScope*scope) const
<< " high bits of the port." << " high bits of the port."
<< endl; << endl;
} else { } else {
if (prts[0]->port_type() == NetNet::PINPUT) { if (ptype == NetNet::PINPUT) {
cerr << get_fileline() << ": : Pruning "; cerr << get_fileline() << ": : Pruning ";
} else { } else {
cerr << get_fileline() << ": : Padding "; cerr << get_fileline() << ": : Padding ";
@ -1774,7 +1769,7 @@ void PGModule::elaborate_mod_(Design*des, Module*rmod, NetScope*scope) const
} }
sig = resize_net_to_port_(des, scope, sig, prts_vector_width, sig = resize_net_to_port_(des, scope, sig, prts_vector_width,
prts[0]->port_type(), as_signed); ptype, as_signed);
} }
// Connect the sig expression that is the context of the // Connect the sig expression that is the context of the
@ -1825,7 +1820,7 @@ void PGModule::elaborate_mod_(Design*des, Module*rmod, NetScope*scope) const
for (unsigned ldx = 0 ; ldx < prts.size() ; ldx += 1) for (unsigned ldx = 0 ; ldx < prts.size() ; ldx += 1)
connect(prts[ldx]->pin(0), sig->pin(0)); connect(prts[ldx]->pin(0), sig->pin(0));
} else switch (prts[0]->port_type()) { } else switch (ptype) {
case NetNet::POUTPUT: case NetNet::POUTPUT:
ctmp = new NetConcat(scope, scope->local_symbol(), ctmp = new NetConcat(scope, scope->local_symbol(),
prts_vector_width, prts.size()); prts_vector_width, prts.size());

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 1998-2013 Stephen Williams (steve@icarus.com) * Copyright (c) 1998-2016 Stephen Williams (steve@icarus.com)
* *
* This source code is free software; you can redistribute it * This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU * and/or modify it in source code form under the terms of the GNU
@ -123,6 +123,12 @@ bool NetFF::emit_node(struct target_t*tgt) const
return true; return true;
} }
bool NetLatch::emit_node(struct target_t*tgt) const
{
tgt->lpm_latch(this);
return true;
}
bool NetLiteral::emit_node(struct target_t*tgt) const bool NetLiteral::emit_node(struct target_t*tgt) const
{ {
return tgt->net_literal(this); return tgt->net_literal(this);

View File

@ -78,6 +78,10 @@ void functor_t::lpm_ff(Design*, NetFF*)
{ {
} }
void functor_t::lpm_latch(Design*, NetLatch*)
{
}
void functor_t::lpm_logic(Design*, NetLogic*) void functor_t::lpm_logic(Design*, NetLogic*)
{ {
} }
@ -219,6 +223,11 @@ void NetFF::functor_node(Design*des, functor_t*fun)
fun->lpm_ff(des, this); fun->lpm_ff(des, this);
} }
void NetLatch::functor_node(Design*des, functor_t*fun)
{
fun->lpm_latch(des, this);
}
void NetLiteral::functor_node(Design*des, functor_t*fun) void NetLiteral::functor_node(Design*des, functor_t*fun)
{ {
fun->lpm_literal(des, this); fun->lpm_literal(des, this);

View File

@ -75,6 +75,9 @@ struct functor_t {
/* This method is called for each FF in the design. */ /* This method is called for each FF in the design. */
virtual void lpm_ff(class Design*des, class NetFF*); virtual void lpm_ff(class Design*des, class NetFF*);
/* This method is called for each LATCH in the design. */
virtual void lpm_latch(class Design*des, class NetLatch*);
/* Handle LPM combinational logic devices. */ /* Handle LPM combinational logic devices. */
virtual void lpm_logic(class Design*des, class NetLogic*); virtual void lpm_logic(class Design*des, class NetLogic*);

View File

@ -1,7 +1,7 @@
#ifndef IVL_ivl_target_H #ifndef IVL_ivl_target_H
#define IVL_ivl_target_H #define IVL_ivl_target_H
/* /*
* Copyright (c) 2000-2015 Stephen Williams (steve@icarus.com) * Copyright (c) 2000-2016 Stephen Williams (steve@icarus.com)
* *
* This source code is free software; you can redistribute it * This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU * and/or modify it in source code form under the terms of the GNU
@ -315,6 +315,7 @@ typedef enum ivl_lpm_type_e {
IVL_LPM_CMP_NEE= 19, /* Case NE (!==) */ IVL_LPM_CMP_NEE= 19, /* Case NE (!==) */
IVL_LPM_DIVIDE = 12, IVL_LPM_DIVIDE = 12,
IVL_LPM_FF = 3, IVL_LPM_FF = 3,
IVL_LPM_LATCH = 40,
IVL_LPM_MOD = 13, IVL_LPM_MOD = 13,
IVL_LPM_MULT = 4, IVL_LPM_MULT = 4,
IVL_LPM_MUX = 5, IVL_LPM_MUX = 5,
@ -1309,8 +1310,13 @@ extern unsigned ivl_lpm_lineno(ivl_lpm_t net);
* inputs and the Q. All the types must be exactly the same. * inputs and the Q. All the types must be exactly the same.
* *
* - D-FlipFlop (IVL_LPM_FF) * - D-FlipFlop (IVL_LPM_FF)
* This data is an edge sensitive register. The ivl_lpm_q output and * This device is an edge sensitive register. The ivl_lpm_q output and
* single ivl_lpm_data input are the same with, ivl_lpm_width. This * single ivl_lpm_data input are the same width, ivl_lpm_width. This
* device carries a vector like other LPM devices.
*
* - Latch (IVL_LPM_LATCH)
* This device is an asynchronous latch. The ivl_lpm_q output and
* single ivl_lpm_data input are the same width, ivl_lpm_width. This
* device carries a vector like other LPM devices. * device carries a vector like other LPM devices.
* *
* - Memory port (IVL_LPM_RAM) (deprecated in favor of IVL_LPM_ARRAY) * - Memory port (IVL_LPM_RAM) (deprecated in favor of IVL_LPM_ARRAY)
@ -1428,18 +1434,18 @@ extern unsigned ivl_lpm_negedge(ivl_lpm_t net);
extern ivl_nexus_t ivl_lpm_clk(ivl_lpm_t net); extern ivl_nexus_t ivl_lpm_clk(ivl_lpm_t net);
/* IVL_LPM_UFUNC */ /* IVL_LPM_UFUNC */
extern ivl_scope_t ivl_lpm_define(ivl_lpm_t net); extern ivl_scope_t ivl_lpm_define(ivl_lpm_t net);
/* IVL_LPM_FF */ /* IVL_LPM_FF IVL_LPM_LATCH*/
extern ivl_nexus_t ivl_lpm_enable(ivl_lpm_t net); extern ivl_nexus_t ivl_lpm_enable(ivl_lpm_t net);
/* IVL_LPM_ADD IVL_LPM_CONCAT IVL_LPM_FF IVL_LPM_PART IVL_LPM_MULT /* IVL_LPM_ADD IVL_LPM_CONCAT IVL_LPM_FF IVL_LPM_PART IVL_LPM_MULT
IVL_LPM_MUX IVL_LPM_POW IVL_LPM_SHIFTL IVL_LPM_SHIFTR IVL_LPM_SUB IVL_LPM_MUX IVL_LPM_POW IVL_LPM_SHIFTL IVL_LPM_SHIFTR IVL_LPM_SUB
IVL_LPM_UFUNC IVL_LPM_SUBSTITUTE */ IVL_LPM_UFUNC IVL_LPM_SUBSTITUTE IVL_LPM_LATCH */
extern ivl_nexus_t ivl_lpm_data(ivl_lpm_t net, unsigned idx); extern ivl_nexus_t ivl_lpm_data(ivl_lpm_t net, unsigned idx);
/* IVL_LPM_ADD IVL_LPM_MULT IVL_LPM_POW IVL_LPM_SUB IVL_LPM_CMP_EQ /* IVL_LPM_ADD IVL_LPM_MULT IVL_LPM_POW IVL_LPM_SUB IVL_LPM_CMP_EQ
IVL_LPM_CMP_EEQ IVL_LPM_CMP_EQX IVL_LPM_CMP_EQZ IVL_LPM_CMP_NEE */ IVL_LPM_CMP_EEQ IVL_LPM_CMP_EQX IVL_LPM_CMP_EQZ IVL_LPM_CMP_NEE */
extern ivl_nexus_t ivl_lpm_datab(ivl_lpm_t net, unsigned idx); extern ivl_nexus_t ivl_lpm_datab(ivl_lpm_t net, unsigned idx);
/* IVL_LPM_ADD IVL_LPM_FF IVL_LPM_MULT IVL_LPM_PART IVL_LPM_POW /* IVL_LPM_ADD IVL_LPM_FF IVL_LPM_MULT IVL_LPM_PART IVL_LPM_POW
IVL_LPM_SUB IVL_LPM_UFUNC IVL_LPM_CMP_EEQ IVL_LPM_CMP_EQX IVL_LPM_SUB IVL_LPM_UFUNC IVL_LPM_CMP_EEQ IVL_LPM_CMP_EQX
IVL_LPM_CMP_EQZ IVL_LPM_CMP_NEE IVL_LPM_SUBSTITUTE */ IVL_LPM_CMP_EQZ IVL_LPM_CMP_NEE IVL_LPM_SUBSTITUTE IVL_LPM_LATCH */
extern ivl_nexus_t ivl_lpm_q(ivl_lpm_t net); extern ivl_nexus_t ivl_lpm_q(ivl_lpm_t net);
extern ivl_drive_t ivl_lpm_drive0(ivl_lpm_t net); extern ivl_drive_t ivl_lpm_drive0(ivl_lpm_t net);
extern ivl_drive_t ivl_lpm_drive1(ivl_lpm_t net); extern ivl_drive_t ivl_lpm_drive1(ivl_lpm_t net);

View File

@ -1306,6 +1306,60 @@ const verinum& NetFF::sset_value() const
return sset_value_; return sset_value_;
} }
/*
* The NetLatch class represents an LPM_LATCH device. The pinout is assigned
* like so:
* 0 -- Enable
* 1 -- Data
* 2 -- Q
*/
NetLatch::NetLatch(NetScope*s, perm_string n, unsigned width__)
: NetNode(s, n, 3), width_(width__)
{
pin_Enable().set_dir(Link::INPUT);
pin_Data().set_dir(Link::INPUT);
pin_Q().set_dir(Link::OUTPUT);
}
NetLatch::~NetLatch()
{
}
unsigned NetLatch::width() const
{
return width_;
}
Link& NetLatch::pin_Enable()
{
return pin(0);
}
const Link& NetLatch::pin_Enable() const
{
return pin(0);
}
Link& NetLatch::pin_Data()
{
return pin(1);
}
const Link& NetLatch::pin_Data() const
{
return pin(1);
}
Link& NetLatch::pin_Q()
{
return pin(2);
}
const Link& NetLatch::pin_Q() const
{
return pin(2);
}
NetAbs::NetAbs(NetScope*s, perm_string n, unsigned w) NetAbs::NetAbs(NetScope*s, perm_string n, unsigned w)
: NetNode(s, n, 2), width_(w) : NetNode(s, n, 2), width_(w)

View File

@ -1684,6 +1684,35 @@ class NetFF : public NetNode {
verinum sset_value_; verinum sset_value_;
}; };
/*
* This class represents an LPM_LATCH device. There is no literal gate
* type in Verilog that maps, but gates of this type can be inferred.
*/
class NetLatch : public NetNode {
public:
NetLatch(NetScope*s, perm_string n, unsigned vector_width);
~NetLatch();
unsigned width() const;
Link& pin_Enable();
Link& pin_Data();
Link& pin_Q();
const Link& pin_Enable() const;
const Link& pin_Data() const;
const Link& pin_Q() const;
virtual void dump_node(ostream&, unsigned ind) const;
virtual bool emit_node(struct target_t*) const;
virtual void functor_node(Design*des, functor_t*fun);
private:
unsigned width_;
};
/* /*
* This class implements a basic LPM_MULT combinational multiplier. It * This class implements a basic LPM_MULT combinational multiplier. It
* is used as a structural representation of the * operator. The * is used as a structural representation of the * operator. The

108
synth2.cc
View File

@ -28,6 +28,52 @@
using namespace std; using namespace std;
/* General notes on enables and bitmasks.
*
* When synthesising an asynchronous process that contains conditional
* statements (if/case statements), we need to determine the conditions
* that cause each nexus driven by that process to be updated. If a
* nexus is not updated under all circumstances, we must infer a latch.
* To this end, we generate an enable signal for each output nexus. As
* we walk the statement tree for the process, for each substatement we
* pass the enable signals generated so far into the synth_async method,
* and on return from the synth_async method, the enable signals will be
* updated to reflect any conditions introduced by that substatement.
* Once we have synthesised all the statements for that process, if an
* enable signal is not tied high, we must infer a latch for that nexus.
*
* When synthesising a synchronous process, we use the synth_async method
* to synthesise the combinatorial inputs to the D pins of the flip-flops
* we infer for that process. In this case the enable signal can be used
* as a clock enable for the flip-flop. This saves us explicitly feeding
* back the flip-flop output to undriven inputs of any synthesised muxes.
*
* The strategy described above is not sufficient when not all bits in
* a nexus are treated identically (i.e. different conditional clauses
* drive differing parts of the same vector). To handle this properly,
* we would (potentially) need to generate a separate enable signal for
* each bit in the vector. This would be a lot of work, particularly if
* we wanted to eliminate duplicates. For now, the strategy employed is
* to maintain a bitmask for each output nexus that identifies which bits
* in the nexus are unconditionally driven (driven by every clause). When
* we finish synthesising an asynchronous process, if the bitmask is not
* all ones, we must infer a latch. This currently results in an error,
* because to safely synthesise such a latch we would need the bit-level
* gate enables. When we finish synthesising a synchronous process, if
* the bitmask is not all ones, we explicitly feed the flip-flop outputs
* back to undriven inputs of any synthesised muxes to ensure undriven
* parts of the vector retain their previous state when the flip-flop is
* clocked.
*
* The enable signals are passed as links to the current output nexus
* for each signal. If an enable signal is not linked, this is treated
* as if the signal was tied low.
*
* The bitmasks are passed as bool vectors. 'true' indicates a bit is
* unconditionally driven. An empty vector (size = 0) indicates that
* the current substatement doesn't drive any bits in the nexus.
*/
static void qualify_enable(Design*des, NetScope*scope, NetNet*qualifier, static void qualify_enable(Design*des, NetScope*scope, NetNet*qualifier,
bool active_state, NetLogic::TYPE gate_type, bool active_state, NetLogic::TYPE gate_type,
Link&enable_i, Link&enable_o) Link&enable_i, Link&enable_o)
@ -165,10 +211,10 @@ static void merge_sequential_masks(NetProc::mask_t&top_mask, NetProc::mask_t&sub
static void merge_parallel_masks(NetProc::mask_t&top_mask, NetProc::mask_t&sub_mask) static void merge_parallel_masks(NetProc::mask_t&top_mask, NetProc::mask_t&sub_mask)
{ {
if (top_mask.size() == 0) if (sub_mask.size() == 0)
return; return;
if (sub_mask.size() == 0) { if (top_mask.size() == 0) {
top_mask = sub_mask; top_mask = sub_mask;
return; return;
} }
@ -896,10 +942,6 @@ bool NetCase::synth_async(Design*des, NetScope*scope,
ena_mux[mdx] = new NetMux(scope, scope->local_symbol(), ena_mux[mdx] = new NetMux(scope, scope->local_symbol(),
1, mux_size, sel_need); 1, mux_size, sel_need);
// Initialise the bitmasks appropriately for calculating
// the intersection of the individual clause bitmasks.
bitmasks[mdx] = mask_t (mux_width[mdx], true);
// Assume a full case to start with. We'll check this as // Assume a full case to start with. We'll check this as
// we synthesise each clause. // we synthesise each clause.
full_case[mdx] = true; full_case[mdx] = true;
@ -1265,7 +1307,6 @@ bool NetCondit::synth_async(Design*des, NetScope*scope,
continue; continue;
} }
bitmasks[idx] = mask_t (nex_map[idx].wid, true);
merge_parallel_masks(bitmasks[idx], a_masks[idx]); merge_parallel_masks(bitmasks[idx], a_masks[idx]);
merge_parallel_masks(bitmasks[idx], b_masks[idx]); merge_parallel_masks(bitmasks[idx], b_masks[idx]);
@ -1535,28 +1576,51 @@ bool NetProcTop::synth_async(Design*des)
bool flag = statement_->synth_async(des, scope(), nex_set, nex_out, enables, bitmasks); bool flag = statement_->synth_async(des, scope(), nex_set, nex_out, enables, bitmasks);
if (!flag) return false; if (!flag) return false;
bool latch_flag = false; flag = tie_off_floating_inputs_(des, nex_set, nex_in, bitmasks, false);
for (unsigned idx = 0 ; idx < enables.pin_count() ; idx += 1) { if (!flag) return false;
if (!enables.pin(idx).is_linked(scope()->tie_hi())) {
for (unsigned idx = 0 ; idx < nex_set.size() ; idx += 1) {
if (enables.pin(idx).is_linked(scope()->tie_hi())) {
connect(nex_set[idx].lnk, nex_out.pin(idx));
} else {
cerr << get_fileline() << ": warning: " cerr << get_fileline() << ": warning: "
<< "A latch has been inferred for '" << "A latch has been inferred for '"
<< nex_set[idx].lnk.nexus()->pick_any_net()->name() << nex_set[idx].lnk.nexus()->pick_any_net()->name()
<< "'." << endl; << "'." << endl;
latch_flag = true;
} if (enables.pin(idx).nexus()->pick_any_net()->local_flag()) {
} cerr << get_fileline() << ": warning: The latch "
if (latch_flag) { "enable is connected to a synthesized "
cerr << get_fileline() << ": sorry: Latches are not " "expression. The latch may be sensitive "
<< "currently supported in synthesis." << endl; "to glitches." << endl;
des->errors += 1;
return false;
} }
flag = tie_off_floating_inputs_(des, nex_set, nex_in, bitmasks, false); if (debug_synth2) {
if (!flag) return false; cerr << get_fileline() << ": debug: "
<< "Top level making a "
<< nex_set[idx].wid << "-wide "
<< "NetLatch device." << endl;
}
for (unsigned idx = 0 ; idx < nex_set.size() ; idx += 1) NetLatch*latch = new NetLatch(scope(), scope()->local_symbol(),
connect(nex_set[idx].lnk, nex_out.pin(idx)); nex_set[idx].wid);
des->add_node(latch);
latch->set_line(*this);
NetNet*tmp = nex_out.pin(idx).nexus()->pick_any_net();
tmp->set_line(*this);
assert(tmp);
tmp = crop_to_width(des, tmp, latch->width());
connect(nex_set[idx].lnk, latch->pin_Q());
connect(tmp->pin(0), latch->pin_Data());
assert (enables.pin(idx).is_linked());
connect(enables.pin(idx), latch->pin_Enable());
}
}
synthesized_design_ = des; synthesized_design_ = des;
return true; return true;

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2000-2015 Stephen Williams (steve@icarus.com) * Copyright (c) 2000-2016 Stephen Williams (steve@icarus.com)
* Copyright CERN 2013 / Stephen Williams (steve@icarus.com) * Copyright CERN 2013 / Stephen Williams (steve@icarus.com)
* *
* This source code is free software; you can redistribute it * This source code is free software; you can redistribute it
@ -1148,6 +1148,8 @@ extern "C" ivl_nexus_t ivl_lpm_enable(ivl_lpm_t net)
switch (net->type) { switch (net->type) {
case IVL_LPM_FF: case IVL_LPM_FF:
return net->u_.ff.we; return net->u_.ff.we;
case IVL_LPM_LATCH:
return net->u_.latch.e;
default: default:
assert(0); assert(0);
return 0; return 0;
@ -1220,6 +1222,9 @@ extern "C" ivl_nexus_t ivl_lpm_data(ivl_lpm_t net, unsigned idx)
case IVL_LPM_FF: case IVL_LPM_FF:
assert(idx == 0); assert(idx == 0);
return net->u_.ff.d.pin; return net->u_.ff.d.pin;
case IVL_LPM_LATCH:
assert(idx == 0);
return net->u_.latch.d.pin;
case IVL_LPM_CONCAT: case IVL_LPM_CONCAT:
case IVL_LPM_CONCATZ: case IVL_LPM_CONCATZ:
@ -1344,6 +1349,8 @@ extern "C" ivl_nexus_t ivl_lpm_q(ivl_lpm_t net)
case IVL_LPM_FF: case IVL_LPM_FF:
return net->u_.ff.q.pin; return net->u_.ff.q.pin;
case IVL_LPM_LATCH:
return net->u_.latch.q.pin;
case IVL_LPM_MUX: case IVL_LPM_MUX:
return net->u_.mux.q; return net->u_.mux.q;

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2000-2015 Stephen Williams (steve@icarus.com) * Copyright (c) 2000-2016 Stephen Williams (steve@icarus.com)
* Copyright CERN 2013 / Stephen Williams (steve@icarus.com) * Copyright CERN 2013 / Stephen Williams (steve@icarus.com)
* *
* This source code is free software; you can redistribute it * This source code is free software; you can redistribute it
@ -2051,6 +2051,39 @@ void dll_target::lpm_ff(const NetFF*net)
nexus_lpm_add(obj->u_.ff.d.pin, obj, 0, IVL_DR_HiZ, IVL_DR_HiZ); nexus_lpm_add(obj->u_.ff.d.pin, obj, 0, IVL_DR_HiZ, IVL_DR_HiZ);
} }
void dll_target::lpm_latch(const NetLatch*net)
{
ivl_lpm_t obj = new struct ivl_lpm_s;
obj->type = IVL_LPM_LATCH;
obj->name = net->name();
obj->scope = find_scope(des_, net->scope());
assert(obj->scope);
FILE_NAME(obj, net);
obj->width = net->width();
scope_add_lpm(obj->scope, obj);
const Nexus*nex;
nex = net->pin_Enable().nexus();
assert(nex->t_cookie());
obj->u_.latch.e = nex->t_cookie();
assert(obj->u_.latch.e);
nexus_lpm_add(obj->u_.latch.e, obj, 0, IVL_DR_HiZ, IVL_DR_HiZ);
nex = net->pin_Q().nexus();
assert(nex->t_cookie());
obj->u_.latch.q.pin = nex->t_cookie();
nexus_lpm_add(obj->u_.latch.q.pin, obj, 0,
IVL_DR_STRONG, IVL_DR_STRONG);
nex = net->pin_Data().nexus();
assert(nex->t_cookie());
obj->u_.latch.d.pin = nex->t_cookie();
nexus_lpm_add(obj->u_.latch.d.pin, obj, 0, IVL_DR_HiZ, IVL_DR_HiZ);
}
/* /*
* Make the NetMult object into an IVL_LPM_MULT node. * Make the NetMult object into an IVL_LPM_MULT node.
*/ */

14
t-dll.h
View File

@ -1,7 +1,7 @@
#ifndef IVL_t_dll_H #ifndef IVL_t_dll_H
#define IVL_t_dll_H #define IVL_t_dll_H
/* /*
* Copyright (c) 2000-2015 Stephen Williams (steve@icarus.com) * Copyright (c) 2000-2016 Stephen Williams (steve@icarus.com)
* *
* This source code is free software; you can redistribute it * This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU * and/or modify it in source code form under the terms of the GNU
@ -76,6 +76,7 @@ struct dll_target : public target_t, public expr_scan_t {
void lpm_compare(const NetCompare*); void lpm_compare(const NetCompare*);
void lpm_divide(const NetDivide*); void lpm_divide(const NetDivide*);
void lpm_ff(const NetFF*); void lpm_ff(const NetFF*);
void lpm_latch(const NetLatch*);
void lpm_modulo(const NetModulo*); void lpm_modulo(const NetModulo*);
void lpm_mult(const NetMult*); void lpm_mult(const NetMult*);
void lpm_mux(const NetMux*); void lpm_mux(const NetMux*);
@ -388,6 +389,17 @@ struct ivl_lpm_s {
ivl_expr_t aset_value; ivl_expr_t aset_value;
ivl_expr_t sset_value; ivl_expr_t sset_value;
} ff; } ff;
struct ivl_lpm_latch_s {
ivl_nexus_t e;
union {
ivl_nexus_t*pins;
ivl_nexus_t pin;
} q;
union {
ivl_nexus_t*pins;
ivl_nexus_t pin;
} d;
} latch;
struct ivl_lpm_mux_s { struct ivl_lpm_mux_s {
unsigned size; unsigned size;

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 1998-2013 Stephen Williams <steve@icarus.com> * Copyright (c) 1998-2016 Stephen Williams <steve@icarus.com>
* *
* This source code is free software; you can redistribute it * This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU * and/or modify it in source code form under the terms of the GNU
@ -183,6 +183,12 @@ void target_t::lpm_ff(const NetFF*)
"Unhandled NetFF." << endl; "Unhandled NetFF." << endl;
} }
void target_t::lpm_latch(const NetLatch*)
{
cerr << "target (" << typeid(*this).name() << "): "
"Unhandled NetLatch." << endl;
}
void target_t::lpm_mult(const NetMult*) void target_t::lpm_mult(const NetMult*)
{ {
cerr << "target (" << typeid(*this).name() << "): " cerr << "target (" << typeid(*this).name() << "): "

View File

@ -1,7 +1,7 @@
#ifndef IVL_target_H #ifndef IVL_target_H
#define IVL_target_H #define IVL_target_H
/* /*
* Copyright (c) 1998-2014 Stephen Williams (steve@icarus.com) * Copyright (c) 1998-2016 Stephen Williams (steve@icarus.com)
* *
* This source code is free software; you can redistribute it * This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU * and/or modify it in source code form under the terms of the GNU
@ -95,6 +95,7 @@ struct target_t {
virtual void lpm_divide(const NetDivide*); virtual void lpm_divide(const NetDivide*);
virtual void lpm_modulo(const NetModulo*); virtual void lpm_modulo(const NetModulo*);
virtual void lpm_ff(const NetFF*); virtual void lpm_ff(const NetFF*);
virtual void lpm_latch(const NetLatch*);
virtual void lpm_mult(const NetMult*); virtual void lpm_mult(const NetMult*);
virtual void lpm_mux(const NetMux*); virtual void lpm_mux(const NetMux*);
virtual void lpm_pow(const NetPow*); virtual void lpm_pow(const NetPow*);

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2000-2015 Stephen Williams (steve@icarus.com) * Copyright (c) 2000-2016 Stephen Williams (steve@icarus.com)
* *
* This source code is free software; you can redistribute it * This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU * and/or modify it in source code form under the terms of the GNU
@ -559,6 +559,39 @@ static void show_lpm_ff(ivl_lpm_t net)
} }
static void show_lpm_latch(ivl_lpm_t net)
{
ivl_nexus_t nex;
unsigned width = ivl_lpm_width(net);
fprintf(out, " LPM_LATCH %s: <width=%u>\n",
ivl_lpm_basename(net), width);
nex = ivl_lpm_enable(net);
fprintf(out, " E: %p\n", nex);
if (width_of_nexus(nex) != 1) {
fprintf(out, " E: ERROR: Nexus width is %u\n",
width_of_nexus(nex));
stub_errors += 1;
}
nex = ivl_lpm_data(net,0);
fprintf(out, " D: %p\n", nex);
if (width_of_nexus(nex) != width) {
fprintf(out, " D: ERROR: Nexus width is %u\n",
width_of_nexus(nex));
stub_errors += 1;
}
nex = ivl_lpm_q(net);
fprintf(out, " Q: %p\n", nex);
if (width_of_nexus(nex) != width) {
fprintf(out, " Q: ERROR: Nexus width is %u\n",
width_of_nexus(nex));
stub_errors += 1;
}
}
static void show_lpm_mod(ivl_lpm_t net) static void show_lpm_mod(ivl_lpm_t net)
{ {
unsigned width = ivl_lpm_width(net); unsigned width = ivl_lpm_width(net);
@ -1017,6 +1050,10 @@ static void show_lpm(ivl_lpm_t net)
show_lpm_ff(net); show_lpm_ff(net);
break; break;
case IVL_LPM_LATCH:
show_lpm_latch(net);
break;
case IVL_LPM_CMP_GE: case IVL_LPM_CMP_GE:
show_lpm_cmp_ge(net); show_lpm_cmp_ge(net);
break; break;

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2011-2015 Cary R. (cygcary@yahoo.com) * Copyright (C) 2011-2016 Cary R. (cygcary@yahoo.com)
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -364,6 +364,7 @@ static ivl_nexus_t get_lpm_output(ivl_scope_t scope, ivl_lpm_t lpm)
case IVL_LPM_CONCATZ: case IVL_LPM_CONCATZ:
case IVL_LPM_DIVIDE: case IVL_LPM_DIVIDE:
case IVL_LPM_FF: case IVL_LPM_FF:
case IVL_LPM_LATCH:
case IVL_LPM_MOD: case IVL_LPM_MOD:
case IVL_LPM_MULT: case IVL_LPM_MULT:
case IVL_LPM_MUX: case IVL_LPM_MUX:
@ -607,7 +608,8 @@ void emit_nexus_as_ca(ivl_scope_t scope, ivl_nexus_t nex, unsigned allow_UD,
} else if (sig) { } else if (sig) {
// HERE: should these be allow_UD? // HERE: should these be allow_UD?
if (must_be_sig) { if (must_be_sig) {
emit_nexus_as_ca(scope, ivl_signal_nex(sig, word), emit_nexus_as_ca(ivl_signal_scope(sig),
ivl_signal_nex(sig, word),
0, 0); 0, 0);
} else emit_name_of_nexus(scope, nex, 0); } else emit_name_of_nexus(scope, nex, 0);
// HERE: The assert causes pr1703959 to fail. // HERE: The assert causes pr1703959 to fail.
@ -1137,7 +1139,7 @@ static void emit_lpm_as_ca(ivl_scope_t scope, ivl_lpm_t lpm,
case IVL_LPM_CAST_INT: case IVL_LPM_CAST_INT:
case IVL_LPM_CAST_INT2: case IVL_LPM_CAST_INT2:
case IVL_LPM_CAST_REAL: case IVL_LPM_CAST_REAL:
emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0), 0, 0); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0), 1, 0);
break; break;
case IVL_LPM_CMP_EEQ: case IVL_LPM_CMP_EEQ:
fprintf(vlog_out, "("); fprintf(vlog_out, "(");
@ -1437,8 +1439,27 @@ static void emit_negedge_dff_prim(void)
fprintf(vlog_out, "endprimitive\n"); fprintf(vlog_out, "endprimitive\n");
} }
static void emit_latch_prim(void)
{
fprintf(vlog_out, "\n");
fprintf(vlog_out, "/* Icarus generated UDP to represent a synthesized "
"LATCH. */\n");
fprintf(vlog_out, "primitive IVL_LATCH "
"(q, en, d);\n");
fprintf(vlog_out, "%*coutput q;\n", indent_incr, ' ');
fprintf(vlog_out, "%*cinput en, d;\n", indent_incr, ' ');
fprintf(vlog_out, "%*creg q;\n", indent_incr, ' ');
fprintf(vlog_out, "%*ctable\n", indent_incr, ' ');
fprintf(vlog_out, "%*c 1 0 : ? : 0 ;\n", 2*indent_incr, ' ');
fprintf(vlog_out, "%*c 1 1 : ? : 1 ;\n", 2*indent_incr, ' ');
fprintf(vlog_out, "%*c 0 ? : ? : - ;\n", 2*indent_incr, ' ');
fprintf(vlog_out, "%*cendtable\n", indent_incr, ' ');
fprintf(vlog_out, "endprimitive\n");
}
static unsigned need_posedge_dff_prim = 0; static unsigned need_posedge_dff_prim = 0;
static unsigned need_negedge_dff_prim = 0; static unsigned need_negedge_dff_prim = 0;
static unsigned need_latch_prim = 0;
/* /*
* Synthesis creates a D-FF LPM object. To allow this to be simulated as * Synthesis creates a D-FF LPM object. To allow this to be simulated as
@ -1456,14 +1477,14 @@ void emit_icarus_generated_udps()
{ {
/* Emit the copyright information and LGPL note and then emit any /* Emit the copyright information and LGPL note and then emit any
* needed primitives. */ * needed primitives. */
if (need_posedge_dff_prim || need_negedge_dff_prim) { if (need_posedge_dff_prim || need_negedge_dff_prim || need_latch_prim) {
fprintf(vlog_out, fprintf(vlog_out,
"\n" "\n"
"/*\n" "/*\n"
" * This is the copyright information for the following primitive(s)\n" " * This is the copyright information for the following primitive(s)\n"
" * (library elements).\n" " * (library elements).\n"
" *\n" " *\n"
" * Copyright (C) 2011-2015 Cary R. (cygcary@yahoo.com)\n" " * Copyright (C) 2011-2016 Cary R. (cygcary@yahoo.com)\n"
" *\n" " *\n"
" * This library is free software; you can redistribute it and/or\n" " * This library is free software; you can redistribute it and/or\n"
" * modify it under the terms of the GNU Lesser General Public\n" " * modify it under the terms of the GNU Lesser General Public\n"
@ -1484,6 +1505,7 @@ void emit_icarus_generated_udps()
} }
if (need_posedge_dff_prim) emit_posedge_dff_prim(); if (need_posedge_dff_prim) emit_posedge_dff_prim();
if (need_negedge_dff_prim) emit_negedge_dff_prim(); if (need_negedge_dff_prim) emit_negedge_dff_prim();
if (need_latch_prim) emit_latch_prim();
} }
static void emit_lpm_ff(ivl_scope_t scope, ivl_lpm_t lpm) static void emit_lpm_ff(ivl_scope_t scope, ivl_lpm_t lpm)
@ -1585,7 +1607,7 @@ static void emit_lpm_ff(ivl_scope_t scope, ivl_lpm_t lpm)
emitted = 1; emitted = 1;
} }
nex = ivl_lpm_async_set(lpm); nex = ivl_lpm_async_set(lpm);
if (aset_bits && (aset_bits[0] != '0')) nex = 0; if (!aset_bits || (aset_bits[0] != '0')) nex = 0;
if (nex) { if (nex) {
if (emitted) fprintf(vlog_out, " | "); if (emitted) fprintf(vlog_out, " | ");
emit_nexus_as_ca(scope, nex, 0, 0); emit_nexus_as_ca(scope, nex, 0, 0);
@ -1606,6 +1628,41 @@ static void emit_lpm_ff(ivl_scope_t scope, ivl_lpm_t lpm)
need_posedge_dff_prim = 1; need_posedge_dff_prim = 1;
} }
static void emit_lpm_latch(ivl_scope_t scope, ivl_lpm_t lpm)
{
ivl_nexus_t nex;
unsigned emitted;
fprintf(vlog_out, "%*c", indent, ' ');
fprintf(vlog_out, "IVL_LATCH");
emit_lpm_strength(lpm);
/* The lpm LATCH does not support any delays. */
/* The LATCH name is a temporary so we don't bother to print it unless
* we have a range. Then we need to use a made up name. */
if (ivl_lpm_width(lpm) > 1) {
fprintf(vlog_out, " synth_%p [%u:0]", lpm, ivl_lpm_width(lpm)-1U);
}
fprintf(vlog_out, " (");
/* Emit the q pin. */
emit_name_of_nexus(scope, ivl_lpm_q(lpm), 0);
fprintf(vlog_out, ", ");
/* Emit the enable pin expression(s) if needed. */
emitted = 0;
nex = ivl_lpm_enable(lpm);
if (nex) {
emit_nexus_as_ca(scope, nex, 0, 0);
emitted = 1;
}
if (!emitted) fprintf(vlog_out, "1'b1");
fprintf(vlog_out, ", ");
/* Emit the data pin expression(s). */
nex = ivl_lpm_data(lpm, 0);
assert (nex);
emit_nexus_as_ca(scope, nex, 0, 0);
fprintf(vlog_out, ");\n");
need_latch_prim = 1;
}
static ivl_signal_t get_output_from_nexus(ivl_scope_t scope, ivl_nexus_t nex, static ivl_signal_t get_output_from_nexus(ivl_scope_t scope, ivl_nexus_t nex,
int64_t*array_idx) int64_t*array_idx)
{ {
@ -1729,6 +1786,10 @@ void emit_lpm(ivl_scope_t scope, ivl_lpm_t lpm)
emit_lpm_ff(scope, lpm); emit_lpm_ff(scope, lpm);
return; return;
} }
if (type == IVL_LPM_LATCH) {
emit_lpm_latch(scope, lpm);
return;
}
// HERE: Look for a select passed to a pull device (pr2019553). // HERE: Look for a select passed to a pull device (pr2019553).
/* Skip assignments to a module instantiation input. */ /* Skip assignments to a module instantiation input. */
if (output_is_module_instantiation_input(scope, output)) return; if (output_is_module_instantiation_input(scope, output)) return;
@ -2199,6 +2260,7 @@ void dump_nexus_information(ivl_scope_t scope, ivl_nexus_t nex)
case IVL_LPM_CMP_NEE: fprintf(stderr, "nee"); break; case IVL_LPM_CMP_NEE: fprintf(stderr, "nee"); break;
case IVL_LPM_DIVIDE: fprintf(stderr, "divide"); break; case IVL_LPM_DIVIDE: fprintf(stderr, "divide"); break;
case IVL_LPM_FF: fprintf(stderr, "dff"); break; case IVL_LPM_FF: fprintf(stderr, "dff"); break;
case IVL_LPM_LATCH: fprintf(stderr, "latch"); break;
case IVL_LPM_MOD: fprintf(stderr, "mod"); break; case IVL_LPM_MOD: fprintf(stderr, "mod"); break;
case IVL_LPM_MULT: fprintf(stderr, "mult"); break; case IVL_LPM_MULT: fprintf(stderr, "mult"); break;
case IVL_LPM_MUX: fprintf(stderr, "mux"); break; case IVL_LPM_MUX: fprintf(stderr, "mux"); break;

View File

@ -710,13 +710,16 @@ static void emit_named_block_scope(ivl_scope_t scope)
* variables. In reality SystemVerilog requires this to be before the * variables. In reality SystemVerilog requires this to be before the
* initial/always blocks are processed, but that's not how it is currently * initial/always blocks are processed, but that's not how it is currently
* implemented in Icarus! * implemented in Icarus!
*
* In Verilog-2001 a named block can also have a process to initialize
* variables. This is handled the same way.
*/ */
static int find_tf_process(ivl_process_t proc, ivl_scope_t scope) static int find_tfb_process(ivl_process_t proc, ivl_scope_t scope)
{ {
if (scope == ivl_process_scope(proc)) { if (scope == ivl_process_scope(proc)) {
ivl_scope_t mod_scope = scope; ivl_scope_t mod_scope = scope;
/* A task or function can only have initial processes that /* A task or function or named block can only have initial
* are used to set local variables. */ * processes that are used to set local variables. */
assert(ivl_process_type(proc) == IVL_PR_INITIAL); assert(ivl_process_type(proc) == IVL_PR_INITIAL);
/* Find the module scope for this task/function. */ /* Find the module scope for this task/function. */
while (ivl_scope_type(mod_scope) != IVL_SCT_MODULE) { while (ivl_scope_type(mod_scope) != IVL_SCT_MODULE) {
@ -731,15 +734,16 @@ static int find_tf_process(ivl_process_t proc, ivl_scope_t scope)
} }
/* /*
* Emit any initial blocks for the tasks or functions in a module. * Emit any initial blocks for the tasks/functions/named blocks in a module.
*/ */
static int emit_tf_process(ivl_scope_t scope, ivl_scope_t parent) static int emit_tfb_process(ivl_scope_t scope, ivl_scope_t parent)
{ {
ivl_scope_type_t sc_type = ivl_scope_type(scope); ivl_scope_type_t sc_type = ivl_scope_type(scope);
(void)parent; /* Parameter is not used. */ (void)parent; /* Parameter is not used. */
if ((sc_type == IVL_SCT_FUNCTION) || (sc_type == IVL_SCT_TASK)) { if ((sc_type == IVL_SCT_FUNCTION) || (sc_type == IVL_SCT_TASK) ||
(sc_type == IVL_SCT_BEGIN) || (sc_type == IVL_SCT_FORK)) {
/* Output the initial/always blocks for this module. */ /* Output the initial/always blocks for this module. */
ivl_design_process(design, (ivl_process_f)find_tf_process, scope); ivl_design_process(design, (ivl_process_f)find_tfb_process, scope);
} }
return 0; return 0;
} }
@ -1172,9 +1176,10 @@ int emit_scope(ivl_scope_t scope, ivl_scope_t parent)
emit_tran(scope, ivl_scope_switch(scope, idx)); emit_tran(scope, ivl_scope_switch(scope, idx));
} }
/* Output any initial blocks for tasks or functions defined /* Output any initial blocks for tasks or functions or named
* in this module. Used to initialize local variables. */ * blocks defined in this module. Used to initialize local
ivl_scope_children(scope, (ivl_scope_f*) emit_tf_process, scope); * variables. */
ivl_scope_children(scope, (ivl_scope_f*) emit_tfb_process, scope);
/* Output the initial/always blocks for this module. */ /* Output the initial/always blocks for this module. */
ivl_design_process(design, (ivl_process_f)find_process, scope); ivl_design_process(design, (ivl_process_f)find_process, scope);

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2001-2015 Stephen Williams (steve@icarus.com) * Copyright (c) 2001-2016 Stephen Williams (steve@icarus.com)
* *
* This source code is free software; you can redistribute it * This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU * and/or modify it in source code form under the terms of the GNU
@ -436,6 +436,7 @@ static char* draw_net_input_drive(ivl_nexus_t nex, ivl_nexus_ptr_t nptr)
if (lpm) switch (ivl_lpm_type(lpm)) { if (lpm) switch (ivl_lpm_type(lpm)) {
case IVL_LPM_FF: case IVL_LPM_FF:
case IVL_LPM_LATCH:
case IVL_LPM_ABS: case IVL_LPM_ABS:
case IVL_LPM_ADD: case IVL_LPM_ADD:
case IVL_LPM_ARRAY: case IVL_LPM_ARRAY:
@ -711,6 +712,8 @@ static void draw_net_input_x(ivl_nexus_t nex,
tmp += strlen(tmp); tmp += strlen(tmp);
switch (res) { switch (res) {
case IVL_SIT_TRI: case IVL_SIT_TRI:
case IVL_SIT_TRIAND:
case IVL_SIT_TRIOR:
case IVL_SIT_UWIRE: case IVL_SIT_UWIRE:
for (jdx = 0 ; jdx < wid ; jdx += 1) for (jdx = 0 ; jdx < wid ; jdx += 1)
*tmp++ = 'z'; *tmp++ = 'z';

View File

@ -1830,6 +1830,31 @@ static void draw_lpm_ff(ivl_lpm_t net)
fprintf(vvp_out, ";\n"); fprintf(vvp_out, ";\n");
} }
/*
* Emit a LATCH primitive. This uses the following syntax:
*
* .latch <width> <data>, <enable>;
*/
static void draw_lpm_latch(ivl_lpm_t net)
{
ivl_nexus_t nex;
unsigned width = ivl_lpm_width(net);
fprintf(vvp_out, "L_%p .latch %u ", net, width);
nex = ivl_lpm_data(net,0);
assert(nex);
fprintf(vvp_out, "%s", draw_net_input(nex));
assert(width_of_nexus(nex) == width);
nex = ivl_lpm_enable(net);
assert(nex);
assert(width_of_nexus(nex) == 1);
fprintf(vvp_out, ", %s", draw_net_input(nex));
fprintf(vvp_out, ";\n");
}
static void draw_lpm_shiftl(ivl_lpm_t net) static void draw_lpm_shiftl(ivl_lpm_t net)
{ {
unsigned width = ivl_lpm_width(net); unsigned width = ivl_lpm_width(net);
@ -2160,6 +2185,10 @@ static void draw_lpm_in_scope(ivl_lpm_t net)
draw_lpm_ff(net); draw_lpm_ff(net);
return; return;
case IVL_LPM_LATCH:
draw_lpm_latch(net);
return;
case IVL_LPM_CMP_EEQ: case IVL_LPM_CMP_EEQ:
case IVL_LPM_CMP_EQ: case IVL_LPM_CMP_EQ:
case IVL_LPM_CMP_EQX: case IVL_LPM_CMP_EQX:

View File

@ -875,8 +875,8 @@ double ExpTime::to_fs() const
return val; return val;
} }
ExpRange::ExpRange(Expression*left, Expression*right, range_dir_t direction) ExpRange::ExpRange(Expression*left_idx, Expression*right_idx, range_dir_t dir)
: left_(left), right_(right), direction_(direction), range_expr_(false), : left_(left_idx), right_(right_idx), direction_(dir), range_expr_(false),
range_base_(NULL) range_base_(NULL)
{ {
} }

View File

@ -1020,7 +1020,7 @@ class ExpRange : public Expression {
typedef enum { DOWNTO, TO, AUTO } range_dir_t; typedef enum { DOWNTO, TO, AUTO } range_dir_t;
// Regular range // Regular range
ExpRange(Expression*left, Expression*right, range_dir_t direction); ExpRange(Expression*left_idx, Expression*right_idx, range_dir_t dir);
// 'range/'reverse range attribute // 'range/'reverse range attribute
ExpRange(ExpName*base, bool reverse_range); ExpRange(ExpName*base, bool reverse_range);
~ExpRange(); ~ExpRange();

View File

@ -304,7 +304,7 @@ WaitForStmt::WaitForStmt(Expression*delay)
{ {
} }
WaitStmt::WaitStmt(wait_type_t type, Expression*expr) WaitStmt::WaitStmt(wait_type_t typ, Expression*expr)
: type_(type), expr_(expr) : type_(typ), expr_(expr)
{ {
} }

View File

@ -334,7 +334,7 @@ class WaitForStmt : public SequentialStmt {
class WaitStmt : public SequentialStmt { class WaitStmt : public SequentialStmt {
public: public:
typedef enum { ON, UNTIL, FINAL } wait_type_t; typedef enum { ON, UNTIL, FINAL } wait_type_t;
WaitStmt(wait_type_t type, Expression*expression); WaitStmt(wait_type_t typ, Expression*expression);
void dump(ostream&out, int indent) const; void dump(ostream&out, int indent) const;
int elaborate(Entity*ent, ScopeBase*scope); int elaborate(Entity*ent, ScopeBase*scope);

View File

@ -146,9 +146,9 @@ class SubprogramHeader : public LineInfo {
class SubprogramStdHeader : public SubprogramHeader class SubprogramStdHeader : public SubprogramHeader
{ {
public: public:
SubprogramStdHeader(perm_string name, std::list<InterfacePort*>*ports, SubprogramStdHeader(perm_string nam, std::list<InterfacePort*>*ports,
const VType*return_type) : const VType*return_type) :
SubprogramHeader(name, ports, return_type) {} SubprogramHeader(nam, ports, return_type) {}
virtual ~SubprogramStdHeader() {}; virtual ~SubprogramStdHeader() {};
bool is_std() const { return true; } bool is_std() const { return true; }

View File

@ -72,7 +72,7 @@ V = vpi_modules.o vpi_callback.o vpi_cobject.o vpi_const.o vpi_darray.o \
vpip_to_dec.o vpip_format.o vvp_vpi.o vpip_to_dec.o vpip_format.o vvp_vpi.o
O = main.o parse.o parse_misc.o lexor.o arith.o array_common.o array.o bufif.o compile.o \ O = main.o parse.o parse_misc.o lexor.o arith.o array_common.o array.o bufif.o compile.o \
concat.o dff.o class_type.o enum_type.o extend.o file_line.o npmos.o part.o \ concat.o dff.o class_type.o enum_type.o extend.o file_line.o latch.o npmos.o part.o \
permaheap.o reduce.o resolv.o \ permaheap.o reduce.o resolv.o \
sfunc.o stop.o \ sfunc.o stop.o \
substitute.o \ substitute.o \

View File

@ -195,7 +195,7 @@ combining up to four inputs down to one output.
B | * * 1 B | * * 1
DFF STATEMENTS: DFF AND LATCH STATEMENTS:
The Verilog language itself does not have a DFF primitive, but post The Verilog language itself does not have a DFF primitive, but post
synthesis readily creates DFF devices that are best simulated with a synthesis readily creates DFF devices that are best simulated with a
@ -219,6 +219,15 @@ rising edge causes the device to clear/set, forces the output to
propagate, and disables the clock until the aynchronous input is propagate, and disables the clock until the aynchronous input is
deasserted. Thus, they implement DFF with asynchronous clr or set. deasserted. Thus, they implement DFF with asynchronous clr or set.
Similarly, synthesis creates D-type latches, so there is the LATCH
statement to support this:
<label> .latch <width> <d>, <en>;
The <en> is a single bit vector (or scalar) on port-1. Port-0 is any
type of datum at all. The device will transfer the input to the output
whenever <en> is a logic 1.
UDP STATEMENTS: UDP STATEMENTS:

View File

@ -1,7 +1,7 @@
#ifndef IVL_compile_H #ifndef IVL_compile_H
#define IVL_compile_H #define IVL_compile_H
/* /*
* Copyright (c) 2001-2014 Stephen Williams (steve@icarus.com) * Copyright (c) 2001-2016 Stephen Williams (steve@icarus.com)
* *
* This source code is free software; you can redistribute it * This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU * and/or modify it in source code form under the terms of the GNU
@ -225,6 +225,10 @@ extern void compile_dff_aset(char*label, unsigned width, bool negedge,
struct symb_s arg_a, struct symb_s arg_a,
char*asc_value); char*asc_value);
extern void compile_latch(char*label, unsigned width,
struct symb_s arg_d,
struct symb_s arg_e);
extern void compile_enum2_type(char*label, long width, bool signed_flag, extern void compile_enum2_type(char*label, long width, bool signed_flag,
std::list<struct enum_name_s>*names); std::list<struct enum_name_s>*names);
extern void compile_enum4_type(char*label, long width, bool signed_flag, extern void compile_enum4_type(char*label, long width, bool signed_flag,

88
vvp/latch.cc Normal file
View File

@ -0,0 +1,88 @@
/*
* Copyright (c) 2016 Martin Whitaker (icarus@martin-whitaker.me.uk)
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
# include "compile.h"
# include "schedule.h"
# include "latch.h"
# include <climits>
# include <cstdio>
# include <cassert>
# include <cstdlib>
# include <iostream>
/* We need to ensure an initial output value is propagated. This is
achieved by scheduling an initial value to be sent to port 3. Any
value received on port 3 will propagate an initial value of 'bx. */
vvp_latch::vvp_latch(unsigned width)
: en_(BIT4_X), d_(width, BIT4_X)
{
}
vvp_latch::~vvp_latch()
{
}
void vvp_latch::recv_vec4(vvp_net_ptr_t port, const vvp_vector4_t&bit,
vvp_context_t)
{
vvp_bit4_t tmp;
switch (port.port()) {
case 0: // D
d_ = bit;
if (en_ == BIT4_1)
schedule_propagate_vector(port.ptr(), 0, d_);
break;
case 1: // EN
assert(bit.size() == 1);
tmp = en_;
en_ = bit.value(0);
if (en_ == BIT4_1 && tmp != BIT4_1)
schedule_propagate_vector(port.ptr(), 0, d_);
break;
case 2:
assert(0);
break;
case 3:
port.ptr()->send_vec4(vvp_vector4_t(d_.size(), BIT4_X), 0);
break;
}
}
void compile_latch(char*label, unsigned width,
struct symb_s arg_d,
struct symb_s arg_e)
{
vvp_net_t*ptr = new vvp_net_t;
vvp_latch*fun = new vvp_latch(width);
ptr->fun = fun;
define_functor_symbol(label, ptr);
free(label);
input_connect(ptr, 0, arg_d.text);
input_connect(ptr, 1, arg_e.text);
vvp_vector4_t init_val = vvp_vector4_t(1, BIT4_1);
schedule_init_vector(vvp_net_ptr_t(ptr,3), init_val);
}

45
vvp/latch.h Normal file
View File

@ -0,0 +1,45 @@
#ifndef IVL_latch_H
#define IVL_latch_H
/*
* Copyright (c) 2016 Martin Whitaker (icarus@martin-whitaker.me.uk)
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
# include "vvp_net.h"
/*
* The vvp_latch implements an arbitrary width D-type transparent latch.
* The latch enable is a single bit. Ports are:
*
* port-0: D input
* port-1: EN input
*/
class vvp_latch : public vvp_net_fun_t {
public:
explicit vvp_latch(unsigned width);
~vvp_latch();
void recv_vec4(vvp_net_ptr_t port, const vvp_vector4_t&bit,
vvp_context_t);
private:
vvp_bit4_t en_;
vvp_vector4_t d_;
};
#endif /* IVL_latch_H */

View File

@ -4,7 +4,7 @@
%{ %{
/* /*
* Copyright (c) 2001-2012 Stephen Williams (steve@icarus.com) * Copyright (c) 2001-2016 Stephen Williams (steve@icarus.com)
* *
* This source code is free software; you can redistribute it * This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU * and/or modify it in source code form under the terms of the GNU
@ -168,6 +168,7 @@ static char* strdupnew(char const *str)
".functor" { return K_FUNCTOR; } ".functor" { return K_FUNCTOR; }
".import" { return K_IMPORT; } ".import" { return K_IMPORT; }
".island" { return K_ISLAND; } ".island" { return K_ISLAND; }
".latch" { return K_LATCH; }
".modpath" { return K_MODPATH; } ".modpath" { return K_MODPATH; }
".net" { return K_NET; } ".net" { return K_NET; }
".net/2s" { return K_NET_2S; } ".net/2s" { return K_NET_2S; }

View File

@ -1,7 +1,7 @@
%{ %{
/* /*
* Copyright (c) 2001-2015 Stephen Williams (steve@icarus.com) * Copyright (c) 2001-2016 Stephen Williams (steve@icarus.com)
* *
* This source code is free software; you can redistribute it * This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU * and/or modify it in source code form under the terms of the GNU
@ -86,7 +86,7 @@ static struct __vpiModPath*modpath_dst = 0;
%token K_CONCAT K_CONCAT8 K_DEBUG K_DELAY K_DFF_N K_DFF_N_ACLR %token K_CONCAT K_CONCAT8 K_DEBUG K_DELAY K_DFF_N K_DFF_N_ACLR
%token K_DFF_N_ASET K_DFF_P K_DFF_P_ACLR K_DFF_P_ASET %token K_DFF_N_ASET K_DFF_P K_DFF_P_ACLR K_DFF_P_ASET
%token K_ENUM2 K_ENUM2_S K_ENUM4 K_ENUM4_S K_EVENT K_EVENT_OR %token K_ENUM2 K_ENUM2_S K_ENUM4 K_ENUM4_S K_EVENT K_EVENT_OR
%token K_EXPORT K_EXTEND_S K_FUNCTOR K_IMPORT K_ISLAND K_MODPATH %token K_EXPORT K_EXTEND_S K_FUNCTOR K_IMPORT K_ISLAND K_LATCH K_MODPATH
%token K_NET K_NET_S K_NET_R K_NET_2S K_NET_2U %token K_NET K_NET_S K_NET_R K_NET_2S K_NET_2U
%token K_NET8 K_NET8_2S K_NET8_2U K_NET8_S %token K_NET8 K_NET8_2S K_NET8_2U K_NET8_S
%token K_PARAM_STR K_PARAM_L K_PARAM_REAL K_PART K_PART_PV %token K_PARAM_STR K_PARAM_L K_PARAM_REAL K_PART K_PART_PV
@ -522,6 +522,11 @@ statement
| T_LABEL K_DFF_P_ASET T_NUMBER symbol ',' symbol ',' symbol ',' symbol ',' T_SYMBOL ';' | T_LABEL K_DFF_P_ASET T_NUMBER symbol ',' symbol ',' symbol ',' symbol ',' T_SYMBOL ';'
{ compile_dff_aset($1, $3, false, $4, $6, $8, $10, $12); } { compile_dff_aset($1, $3, false, $4, $6, $8, $10, $12); }
/* LATCH nodes have an output and take 2 inputs. */
| T_LABEL K_LATCH T_NUMBER symbol ',' symbol ';'
{ compile_latch($1, $3, $4, $6); }
/* The various reduction operator nodes take a single input. */ /* The various reduction operator nodes take a single input. */
| T_LABEL K_REDUCE_AND symbol ';' | T_LABEL K_REDUCE_AND symbol ';'