diff --git a/design_dump.cc b/design_dump.cc index 5a97b5ec0..44a9b8ddc 100644 --- a/design_dump.cc +++ b/design_dump.cc @@ -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 * 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); } +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 { o << setw(ind) << "" << "constant real " << real_ diff --git a/elaborate.cc b/elaborate.cc index 184dfb75a..c81cd978e 100644 --- a/elaborate.cc +++ b/elaborate.cc @@ -1408,7 +1408,8 @@ void PGModule::elaborate_mod_(Design*des, Module*rmod, NetScope*scope) const // that connects to the port. 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 // 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()); } - } else if (prts[0]->port_type() == NetNet::PINOUT) { + } else if (ptype == NetNet::PINOUT) { // For now, do not support unpacked array outputs. ivl_assert(*this, prts[0]->unpacked_dimensions()==0); @@ -1562,7 +1563,7 @@ void PGModule::elaborate_mod_(Design*des, Module*rmod, NetScope*scope) const } else { /* 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 // 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(); 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_vector_width / 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 ) && !prts.empty() && (prts[0]->data_type() != IVL_VT_REAL )) { 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]->port_type(NetNet::POUTPUT); // No support for multiple real drivers. if (instance.size() != 1) { 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 ) && !prts.empty() && (prts[0]->data_type() == IVL_VT_LOGIC )) { 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]->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 if ((! prts.empty()) - && (prts[0]->port_type() != NetNet::PINPUT)) { + && (ptype != NetNet::PINPUT)) { assert(sig->type() != NetNet::REG); } #endif @@ -1718,7 +1713,7 @@ void PGModule::elaborate_mod_(Design*des, Module*rmod, NetScope*scope) const && (prts_vector_width != sig->vector_width())) { bool as_signed = false; - switch (prts[0]->port_type()) { + switch (ptype) { case NetNet::POUTPUT: as_signed = prts[0]->get_signed(); break; @@ -1742,7 +1737,7 @@ void PGModule::elaborate_mod_(Design*des, Module*rmod, NetScope*scope) const " bits, got " << sig->vector_width() << "." << endl; // 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()) { cerr << get_fileline() << ": : Leaving " << (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." << endl; } else { - if (prts[0]->port_type() == NetNet::PINPUT) { + if (ptype == NetNet::PINPUT) { cerr << get_fileline() << ": : Pruning "; } else { 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, - prts[0]->port_type(), as_signed); + ptype, as_signed); } // 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) connect(prts[ldx]->pin(0), sig->pin(0)); - } else switch (prts[0]->port_type()) { + } else switch (ptype) { case NetNet::POUTPUT: ctmp = new NetConcat(scope, scope->local_symbol(), prts_vector_width, prts.size()); diff --git a/emit.cc b/emit.cc index bd46fc5c4..6a08431f8 100644 --- a/emit.cc +++ b/emit.cc @@ -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 * 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; } +bool NetLatch::emit_node(struct target_t*tgt) const +{ + tgt->lpm_latch(this); + return true; +} + bool NetLiteral::emit_node(struct target_t*tgt) const { return tgt->net_literal(this); diff --git a/functor.cc b/functor.cc index 75ed5a606..280a6f9aa 100644 --- a/functor.cc +++ b/functor.cc @@ -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*) { } @@ -219,6 +223,11 @@ void NetFF::functor_node(Design*des, functor_t*fun) 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) { fun->lpm_literal(des, this); diff --git a/functor.h b/functor.h index 63d85a755..716ceda64 100644 --- a/functor.h +++ b/functor.h @@ -75,6 +75,9 @@ struct functor_t { /* This method is called for each FF in the design. */ 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. */ virtual void lpm_logic(class Design*des, class NetLogic*); diff --git a/ivl_target.h b/ivl_target.h index a5d27ed80..42dbe167b 100644 --- a/ivl_target.h +++ b/ivl_target.h @@ -1,7 +1,7 @@ #ifndef 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 * 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_DIVIDE = 12, IVL_LPM_FF = 3, + IVL_LPM_LATCH = 40, IVL_LPM_MOD = 13, IVL_LPM_MULT = 4, 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. * * - D-FlipFlop (IVL_LPM_FF) - * This data is an edge sensitive register. The ivl_lpm_q output and - * single ivl_lpm_data input are the same with, ivl_lpm_width. This + * This device is an edge sensitive register. 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. + * + * - 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. * * - 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); /* IVL_LPM_UFUNC */ 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); /* 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_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); /* 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 */ 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_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_drive_t ivl_lpm_drive0(ivl_lpm_t net); extern ivl_drive_t ivl_lpm_drive1(ivl_lpm_t net); diff --git a/netlist.cc b/netlist.cc index 211083a17..34b3ebf28 100644 --- a/netlist.cc +++ b/netlist.cc @@ -1306,6 +1306,60 @@ const verinum& NetFF::sset_value() const 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) : NetNode(s, n, 2), width_(w) diff --git a/netlist.h b/netlist.h index ad104b30a..2c349aaf3 100644 --- a/netlist.h +++ b/netlist.h @@ -1684,6 +1684,35 @@ class NetFF : public NetNode { 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 * is used as a structural representation of the * operator. The diff --git a/synth2.cc b/synth2.cc index b30b7576c..b42fc440d 100644 --- a/synth2.cc +++ b/synth2.cc @@ -28,6 +28,52 @@ 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, bool active_state, NetLogic::TYPE gate_type, 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) { - if (top_mask.size() == 0) + if (sub_mask.size() == 0) return; - if (sub_mask.size() == 0) { + if (top_mask.size() == 0) { top_mask = sub_mask; return; } @@ -896,10 +942,6 @@ bool NetCase::synth_async(Design*des, NetScope*scope, ena_mux[mdx] = new NetMux(scope, scope->local_symbol(), 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 // we synthesise each clause. full_case[mdx] = true; @@ -1265,7 +1307,6 @@ bool NetCondit::synth_async(Design*des, NetScope*scope, continue; } - bitmasks[idx] = mask_t (nex_map[idx].wid, true); merge_parallel_masks(bitmasks[idx], a_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); if (!flag) return false; - bool latch_flag = false; - for (unsigned idx = 0 ; idx < enables.pin_count() ; idx += 1) { - if (!enables.pin(idx).is_linked(scope()->tie_hi())) { + flag = tie_off_floating_inputs_(des, nex_set, nex_in, bitmasks, false); + if (!flag) return false; + + 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: " << "A latch has been inferred for '" << nex_set[idx].lnk.nexus()->pick_any_net()->name() << "'." << endl; - latch_flag = true; + + if (enables.pin(idx).nexus()->pick_any_net()->local_flag()) { + cerr << get_fileline() << ": warning: The latch " + "enable is connected to a synthesized " + "expression. The latch may be sensitive " + "to glitches." << endl; + } + + if (debug_synth2) { + cerr << get_fileline() << ": debug: " + << "Top level making a " + << nex_set[idx].wid << "-wide " + << "NetLatch device." << endl; + } + + NetLatch*latch = new NetLatch(scope(), scope()->local_symbol(), + 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()); } } - if (latch_flag) { - cerr << get_fileline() << ": sorry: Latches are not " - << "currently supported in synthesis." << endl; - des->errors += 1; - return false; - } - - flag = tie_off_floating_inputs_(des, nex_set, nex_in, bitmasks, false); - if (!flag) return false; - - for (unsigned idx = 0 ; idx < nex_set.size() ; idx += 1) - connect(nex_set[idx].lnk, nex_out.pin(idx)); synthesized_design_ = des; return true; diff --git a/t-dll-api.cc b/t-dll-api.cc index 8cad0a4af..9fe60d365 100644 --- a/t-dll-api.cc +++ b/t-dll-api.cc @@ -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) * * 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) { case IVL_LPM_FF: return net->u_.ff.we; + case IVL_LPM_LATCH: + return net->u_.latch.e; default: assert(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: assert(idx == 0); 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_CONCATZ: @@ -1344,6 +1349,8 @@ extern "C" ivl_nexus_t ivl_lpm_q(ivl_lpm_t net) case IVL_LPM_FF: return net->u_.ff.q.pin; + case IVL_LPM_LATCH: + return net->u_.latch.q.pin; case IVL_LPM_MUX: return net->u_.mux.q; diff --git a/t-dll.cc b/t-dll.cc index 93564e854..626f3a74f 100644 --- a/t-dll.cc +++ b/t-dll.cc @@ -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) * * 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); } +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. */ diff --git a/t-dll.h b/t-dll.h index a94137383..b85bb2d43 100644 --- a/t-dll.h +++ b/t-dll.h @@ -1,7 +1,7 @@ #ifndef 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 * 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_divide(const NetDivide*); void lpm_ff(const NetFF*); + void lpm_latch(const NetLatch*); void lpm_modulo(const NetModulo*); void lpm_mult(const NetMult*); void lpm_mux(const NetMux*); @@ -388,6 +389,17 @@ struct ivl_lpm_s { ivl_expr_t aset_value; ivl_expr_t sset_value; } 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 { unsigned size; diff --git a/target.cc b/target.cc index a4fe8b44a..15cc27c5c 100644 --- a/target.cc +++ b/target.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998-2013 Stephen Williams + * Copyright (c) 1998-2016 Stephen Williams * * This source code is free software; you can redistribute it * 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; } +void target_t::lpm_latch(const NetLatch*) +{ + cerr << "target (" << typeid(*this).name() << "): " + "Unhandled NetLatch." << endl; +} + void target_t::lpm_mult(const NetMult*) { cerr << "target (" << typeid(*this).name() << "): " diff --git a/target.h b/target.h index ec834418d..be39f7ca4 100644 --- a/target.h +++ b/target.h @@ -1,7 +1,7 @@ #ifndef 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 * 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_modulo(const NetModulo*); virtual void lpm_ff(const NetFF*); + virtual void lpm_latch(const NetLatch*); virtual void lpm_mult(const NetMult*); virtual void lpm_mux(const NetMux*); virtual void lpm_pow(const NetPow*); diff --git a/tgt-stub/stub.c b/tgt-stub/stub.c index eeab84e4c..ea71358be 100644 --- a/tgt-stub/stub.c +++ b/tgt-stub/stub.c @@ -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 * 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: \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) { unsigned width = ivl_lpm_width(net); @@ -1017,6 +1050,10 @@ static void show_lpm(ivl_lpm_t net) show_lpm_ff(net); break; + case IVL_LPM_LATCH: + show_lpm_latch(net); + break; + case IVL_LPM_CMP_GE: show_lpm_cmp_ge(net); break; diff --git a/tgt-vlog95/logic_lpm.c b/tgt-vlog95/logic_lpm.c index 6f8e74b84..22490640b 100644 --- a/tgt-vlog95/logic_lpm.c +++ b/tgt-vlog95/logic_lpm.c @@ -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 * 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_DIVIDE: case IVL_LPM_FF: + case IVL_LPM_LATCH: case IVL_LPM_MOD: case IVL_LPM_MULT: 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) { // HERE: should these be allow_UD? 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); } else emit_name_of_nexus(scope, nex, 0); // 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_INT2: 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; case IVL_LPM_CMP_EEQ: fprintf(vlog_out, "("); @@ -1437,8 +1439,27 @@ static void emit_negedge_dff_prim(void) 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_negedge_dff_prim = 0; +static unsigned need_latch_prim = 0; /* * 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 * 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, "\n" "/*\n" " * This is the copyright information for the following primitive(s)\n" " * (library elements).\n" " *\n" -" * Copyright (C) 2011-2015 Cary R. (cygcary@yahoo.com)\n" +" * Copyright (C) 2011-2016 Cary R. (cygcary@yahoo.com)\n" " *\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" @@ -1484,6 +1505,7 @@ void emit_icarus_generated_udps() } if (need_posedge_dff_prim) emit_posedge_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) @@ -1585,7 +1607,7 @@ static void emit_lpm_ff(ivl_scope_t scope, ivl_lpm_t lpm) emitted = 1; } 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 (emitted) fprintf(vlog_out, " | "); 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; } +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, int64_t*array_idx) { @@ -1729,6 +1786,10 @@ void emit_lpm(ivl_scope_t scope, ivl_lpm_t lpm) emit_lpm_ff(scope, lpm); return; } + if (type == IVL_LPM_LATCH) { + emit_lpm_latch(scope, lpm); + return; + } // HERE: Look for a select passed to a pull device (pr2019553). /* Skip assignments to a module instantiation input. */ 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_DIVIDE: fprintf(stderr, "divide"); 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_MULT: fprintf(stderr, "mult"); break; case IVL_LPM_MUX: fprintf(stderr, "mux"); break; diff --git a/tgt-vlog95/scope.c b/tgt-vlog95/scope.c index 1f939d8d2..c4b8278d9 100644 --- a/tgt-vlog95/scope.c +++ b/tgt-vlog95/scope.c @@ -710,13 +710,16 @@ static void emit_named_block_scope(ivl_scope_t scope) * variables. In reality SystemVerilog requires this to be before the * initial/always blocks are processed, but that's not how it is currently * 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)) { ivl_scope_t mod_scope = scope; - /* A task or function can only have initial processes that - * are used to set local variables. */ + /* A task or function or named block can only have initial + * processes that are used to set local variables. */ assert(ivl_process_type(proc) == IVL_PR_INITIAL); /* Find the module scope for this task/function. */ 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); (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. */ - ivl_design_process(design, (ivl_process_f)find_tf_process, scope); + ivl_design_process(design, (ivl_process_f)find_tfb_process, scope); } 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)); } - /* Output any initial blocks for tasks or functions defined - * in this module. Used to initialize local variables. */ - ivl_scope_children(scope, (ivl_scope_f*) emit_tf_process, scope); + /* Output any initial blocks for tasks or functions or named + * blocks defined in this module. Used to initialize local + * variables. */ + ivl_scope_children(scope, (ivl_scope_f*) emit_tfb_process, scope); /* Output the initial/always blocks for this module. */ ivl_design_process(design, (ivl_process_f)find_process, scope); diff --git a/tgt-vvp/draw_net_input.c b/tgt-vvp/draw_net_input.c index e2f94fb20..2b995affa 100644 --- a/tgt-vvp/draw_net_input.c +++ b/tgt-vvp/draw_net_input.c @@ -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 * 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)) { case IVL_LPM_FF: + case IVL_LPM_LATCH: case IVL_LPM_ABS: case IVL_LPM_ADD: case IVL_LPM_ARRAY: @@ -711,6 +712,8 @@ static void draw_net_input_x(ivl_nexus_t nex, tmp += strlen(tmp); switch (res) { case IVL_SIT_TRI: + case IVL_SIT_TRIAND: + case IVL_SIT_TRIOR: case IVL_SIT_UWIRE: for (jdx = 0 ; jdx < wid ; jdx += 1) *tmp++ = 'z'; diff --git a/tgt-vvp/vvp_scope.c b/tgt-vvp/vvp_scope.c index 9bd197df6..53b56dbc6 100644 --- a/tgt-vvp/vvp_scope.c +++ b/tgt-vvp/vvp_scope.c @@ -1830,6 +1830,31 @@ static void draw_lpm_ff(ivl_lpm_t net) fprintf(vvp_out, ";\n"); } +/* + * Emit a LATCH primitive. This uses the following syntax: + * + * .latch , ; + */ +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) { unsigned width = ivl_lpm_width(net); @@ -2160,6 +2185,10 @@ static void draw_lpm_in_scope(ivl_lpm_t net) draw_lpm_ff(net); return; + case IVL_LPM_LATCH: + draw_lpm_latch(net); + return; + case IVL_LPM_CMP_EEQ: case IVL_LPM_CMP_EQ: case IVL_LPM_CMP_EQX: diff --git a/vhdlpp/expression.cc b/vhdlpp/expression.cc index d52fa4d11..9efe60931 100644 --- a/vhdlpp/expression.cc +++ b/vhdlpp/expression.cc @@ -875,8 +875,8 @@ double ExpTime::to_fs() const return val; } -ExpRange::ExpRange(Expression*left, Expression*right, range_dir_t direction) -: left_(left), right_(right), direction_(direction), range_expr_(false), +ExpRange::ExpRange(Expression*left_idx, Expression*right_idx, range_dir_t dir) +: left_(left_idx), right_(right_idx), direction_(dir), range_expr_(false), range_base_(NULL) { } diff --git a/vhdlpp/expression.h b/vhdlpp/expression.h index b1b6ae244..65e1934bd 100644 --- a/vhdlpp/expression.h +++ b/vhdlpp/expression.h @@ -1020,7 +1020,7 @@ class ExpRange : public Expression { typedef enum { DOWNTO, TO, AUTO } range_dir_t; // 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 ExpRange(ExpName*base, bool reverse_range); ~ExpRange(); diff --git a/vhdlpp/sequential.cc b/vhdlpp/sequential.cc index 3e572fce0..9f83c1372 100644 --- a/vhdlpp/sequential.cc +++ b/vhdlpp/sequential.cc @@ -304,7 +304,7 @@ WaitForStmt::WaitForStmt(Expression*delay) { } -WaitStmt::WaitStmt(wait_type_t type, Expression*expr) -: type_(type), expr_(expr) +WaitStmt::WaitStmt(wait_type_t typ, Expression*expr) +: type_(typ), expr_(expr) { } diff --git a/vhdlpp/sequential.h b/vhdlpp/sequential.h index 13a3f5947..b0fa8ac07 100644 --- a/vhdlpp/sequential.h +++ b/vhdlpp/sequential.h @@ -334,7 +334,7 @@ class WaitForStmt : public SequentialStmt { class WaitStmt : public SequentialStmt { public: 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; int elaborate(Entity*ent, ScopeBase*scope); diff --git a/vhdlpp/subprogram.h b/vhdlpp/subprogram.h index 08473239e..82ba804d1 100644 --- a/vhdlpp/subprogram.h +++ b/vhdlpp/subprogram.h @@ -146,9 +146,9 @@ class SubprogramHeader : public LineInfo { class SubprogramStdHeader : public SubprogramHeader { public: - SubprogramStdHeader(perm_string name, std::list*ports, + SubprogramStdHeader(perm_string nam, std::list*ports, const VType*return_type) : - SubprogramHeader(name, ports, return_type) {} + SubprogramHeader(nam, ports, return_type) {} virtual ~SubprogramStdHeader() {}; bool is_std() const { return true; } diff --git a/vvp/Makefile.in b/vvp/Makefile.in index 35750f69d..faae3736a 100644 --- a/vvp/Makefile.in +++ b/vvp/Makefile.in @@ -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 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 \ sfunc.o stop.o \ substitute.o \ diff --git a/vvp/README.txt b/vvp/README.txt index a6da64994..0b8052dc9 100644 --- a/vvp/README.txt +++ b/vvp/README.txt @@ -195,7 +195,7 @@ combining up to four inputs down to one output. B | * * 1 -DFF STATEMENTS: +DFF AND LATCH STATEMENTS: The Verilog language itself does not have a DFF primitive, but post 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 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: + +