Merge branch 'master' of github.com:steveicarus/iverilog
This commit is contained in:
commit
78a49eacef
|
|
@ -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_
|
||||
|
|
|
|||
25
elaborate.cc
25
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());
|
||||
|
|
|
|||
8
emit.cc
8
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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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*);
|
||||
|
||||
|
|
|
|||
18
ivl_target.h
18
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);
|
||||
|
|
|
|||
54
netlist.cc
54
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)
|
||||
|
|
|
|||
29
netlist.h
29
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
|
||||
|
|
|
|||
110
synth2.cc
110
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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
35
t-dll.cc
35
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.
|
||||
*/
|
||||
|
|
|
|||
14
t-dll.h
14
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;
|
||||
|
|
|
|||
|
|
@ -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() << "): "
|
||||
|
|
|
|||
3
target.h
3
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*);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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';
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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; }
|
||||
|
|
|
|||
|
|
@ -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 \
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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 */
|
||||
|
|
@ -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; }
|
||||
|
|
|
|||
|
|
@ -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 ';'
|
||||
|
|
|
|||
Loading…
Reference in New Issue