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
* 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_

View File

@ -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());

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
* 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);

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*)
{
}
@ -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);

View File

@ -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*);

View File

@ -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);

View File

@ -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)

View File

@ -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

110
synth2.cc
View File

@ -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;

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)
*
* 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;

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)
*
* 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.
*/

14
t-dll.h
View File

@ -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;

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
* 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() << "): "

View File

@ -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*);

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
* 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)
{
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;

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
* 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;

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
* 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);

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
* 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';

View File

@ -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 <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)
{
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:

View File

@ -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)
{
}

View File

@ -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();

View File

@ -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)
{
}

View File

@ -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);

View File

@ -146,9 +146,9 @@ class SubprogramHeader : public LineInfo {
class SubprogramStdHeader : public SubprogramHeader
{
public:
SubprogramStdHeader(perm_string name, std::list<InterfacePort*>*ports,
SubprogramStdHeader(perm_string nam, std::list<InterfacePort*>*ports,
const VType*return_type) :
SubprogramHeader(name, ports, return_type) {}
SubprogramHeader(nam, ports, return_type) {}
virtual ~SubprogramStdHeader() {};
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
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 \

View File

@ -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:
<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:

View File

@ -1,7 +1,7 @@
#ifndef 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
* 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,
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,
std::list<struct enum_name_s>*names);
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
* 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; }
".import" { return K_IMPORT; }
".island" { return K_ISLAND; }
".latch" { return K_LATCH; }
".modpath" { return K_MODPATH; }
".net" { return K_NET; }
".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
* 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_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_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_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
@ -522,6 +522,11 @@ statement
| 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); }
/* 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. */
| T_LABEL K_REDUCE_AND symbol ';'