Add synthesis support for casez statements.
This generates an EQZ LPM device that carries the case-z-ness to the code generator. Also add to the vvp code generator support for the EQZ device so that the synthesis results can be simulated. Account for the wildcard devices in the sizer.
This commit is contained in:
parent
26f71f2d94
commit
f8dc430fe5
|
|
@ -174,6 +174,25 @@ ostream& operator << (ostream&fd, PortType::Enum val)
|
|||
return fd;
|
||||
}
|
||||
|
||||
ostream& operator << (ostream&fd, NetCaseCmp::kind_t that)
|
||||
{
|
||||
switch (that) {
|
||||
case NetCaseCmp::EEQ:
|
||||
fd << "===";
|
||||
break;
|
||||
case NetCaseCmp::NEQ:
|
||||
fd << "!==";
|
||||
break;
|
||||
case NetCaseCmp::XEQ:
|
||||
fd << "==?";
|
||||
break;
|
||||
case NetCaseCmp::ZEQ:
|
||||
fd << "==z?";
|
||||
break;
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
ostream& ivl_type_s::debug_dump(ostream&o) const
|
||||
{
|
||||
o << typeid(*this).name();
|
||||
|
|
@ -550,10 +569,7 @@ void NetBUFZ::dump_node(ostream&o, unsigned ind) const
|
|||
|
||||
void NetCaseCmp::dump_node(ostream&o, unsigned ind) const
|
||||
{
|
||||
if (eeq_)
|
||||
o << setw(ind) << "" << "case compare === : " << name() << endl;
|
||||
else
|
||||
o << setw(ind) << "" << "case compare !== : " << name() << endl;
|
||||
o << setw(ind) << "" << "case compare " << kind_ << ": " << name() << endl;
|
||||
|
||||
dump_node_pins(o, ind+4);
|
||||
}
|
||||
|
|
@ -1004,7 +1020,7 @@ void NetCase::dump(ostream&o, unsigned ind) const
|
|||
break;
|
||||
}
|
||||
|
||||
for (unsigned idx = 0 ; idx < nitems_ ; idx += 1) {
|
||||
for (unsigned idx = 0 ; idx < items_.size() ; idx += 1) {
|
||||
o << setw(ind+2) << "";
|
||||
if (items_[idx].guard)
|
||||
o << *items_[idx].guard << ":";
|
||||
|
|
|
|||
|
|
@ -274,7 +274,7 @@ NetNet* NetEBComp::synthesize(Design*des, NetScope*scope, NetExpr*root)
|
|||
|
||||
if (op_ == 'E' || op_ == 'N') {
|
||||
NetCaseCmp*gate = new NetCaseCmp(scope, scope->local_symbol(),
|
||||
width, op_=='E'?true:false);
|
||||
width, op_=='E'?NetCaseCmp::EEQ:NetCaseCmp::NEQ);
|
||||
gate->set_line(*this);
|
||||
connect(gate->pin(0), osig->pin(0));
|
||||
connect(gate->pin(1), lsig->pin(0));
|
||||
|
|
|
|||
15
ivl_target.h
15
ivl_target.h
|
|
@ -300,6 +300,8 @@ typedef enum ivl_lpm_type_e {
|
|||
IVL_LPM_CONCAT = 16,
|
||||
IVL_LPM_CONCATZ = 36, /* Transparent concat */
|
||||
IVL_LPM_CMP_EEQ= 18, /* Case EQ (===) */
|
||||
IVL_LPM_CMP_EQX= 37, /* Windcard EQ (==?) */
|
||||
IVL_LPM_CMP_EQZ= 38, /* casez EQ */
|
||||
IVL_LPM_CMP_EQ = 10,
|
||||
IVL_LPM_CMP_GE = 1,
|
||||
IVL_LPM_CMP_GT = 2,
|
||||
|
|
@ -1265,7 +1267,7 @@ extern unsigned ivl_lpm_lineno(ivl_lpm_t net);
|
|||
* width of the part. The ivl_lpm_q is the part end, and the
|
||||
* ivl_lpm_data(0) is the non-part end.
|
||||
*
|
||||
* - Comparisons (IVL_LPM_CMP_GT/GE/EQ/NE/EEQ/NEE)
|
||||
* - Comparisons (IVL_LPM_CMP_GT/GE/EQ/NE/EEQ/NEE/EQX/EQZ)
|
||||
* These devices have two inputs, available by the ivl_lpm_data()
|
||||
* function, and one output available by the ivl_lpm_q function. The
|
||||
* output width is always 1, but the ivl_lpm_width() returns the width
|
||||
|
|
@ -1277,6 +1279,11 @@ extern unsigned ivl_lpm_lineno(ivl_lpm_t net);
|
|||
* magnitude compare, the signedness does matter. In any case, the
|
||||
* result of the compare is always unsigned.
|
||||
*
|
||||
* The EQX and EQZ nodes are windcard compares, where xz bits (EQX) or
|
||||
* z bits (EQZ) in the data(1) operand are treated as windcards. no
|
||||
* bits in the data(0) operand are wild. This matches the
|
||||
* SystemVerilog convention for the ==? operator.
|
||||
*
|
||||
* - Mux Device (IVL_LPM_MUX)
|
||||
* The MUX device has a q output, a select input, and a number of data
|
||||
* inputs. The ivl_lpm_q output and the ivl_lpm_data inputs all have
|
||||
|
|
@ -1414,10 +1421,12 @@ extern ivl_nexus_t ivl_lpm_enable(ivl_lpm_t net);
|
|||
IVL_LPM_MUX IVL_LPM_POW IVL_LPM_SHIFTL IVL_LPM_SHIFTR IVL_LPM_SUB
|
||||
IVL_LPM_UFUNC */
|
||||
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_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_SUB IVL_LPM_UFUNC IVL_LPM_CMP_EEQ IVL_LPM_CMP_EQX
|
||||
IVL_LPM_CMP_EQZ IVL_LPM_CMP_NEE */
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -416,8 +416,8 @@ bool NetCase::evaluate_function_vect_(const LineInfo&loc,
|
|||
|
||||
NetProc*default_statement = 0;
|
||||
|
||||
for (unsigned cnt = 0 ; cnt < nitems_ ; cnt += 1) {
|
||||
Item*item = &items_[cnt];
|
||||
for (unsigned cnt = 0 ; cnt < items_.size() ; cnt += 1) {
|
||||
const Item*item = &items_[cnt];
|
||||
|
||||
if (item->guard == 0) {
|
||||
default_statement = item->statement;
|
||||
|
|
@ -478,8 +478,8 @@ bool NetCase::evaluate_function_real_(const LineInfo&loc,
|
|||
|
||||
NetProc*default_statement = 0;
|
||||
|
||||
for (unsigned cnt = 0 ; cnt < nitems_ ; cnt += 1) {
|
||||
Item*item = &items_[cnt];
|
||||
for (unsigned cnt = 0 ; cnt < items_.size() ; cnt += 1) {
|
||||
const Item*item = &items_[cnt];
|
||||
|
||||
if (item->guard == 0) {
|
||||
default_statement = item->statement;
|
||||
|
|
|
|||
|
|
@ -340,7 +340,7 @@ NexusSet* NetCase::nex_input(bool rem_out)
|
|||
if (result == 0)
|
||||
return 0;
|
||||
|
||||
for (unsigned idx = 0 ; idx < nitems_ ; idx += 1) {
|
||||
for (size_t idx = 0 ; idx < items_.size() ; idx += 1) {
|
||||
|
||||
/* Skip cases that have empty statements. */
|
||||
if (items_[idx].statement == 0)
|
||||
|
|
|
|||
|
|
@ -102,7 +102,7 @@ void NetBlock::nex_output(NexusSet&out)
|
|||
|
||||
void NetCase::nex_output(NexusSet&out)
|
||||
{
|
||||
for (unsigned idx = 0 ; idx < nitems_ ; idx += 1) {
|
||||
for (size_t idx = 0 ; idx < items_.size() ; idx += 1) {
|
||||
|
||||
// Empty statements clearly have no output.
|
||||
if (items_[idx].statement == 0)
|
||||
|
|
|
|||
11
net_proc.cc
11
net_proc.cc
|
|
@ -71,23 +71,18 @@ const NetProc* NetBlock::proc_next(const NetProc*cur) const
|
|||
}
|
||||
|
||||
NetCase::NetCase(NetCase::TYPE c, NetExpr*ex, unsigned cnt)
|
||||
: type_(c), expr_(ex), nitems_(cnt)
|
||||
: type_(c), expr_(ex), items_(cnt)
|
||||
{
|
||||
assert(expr_);
|
||||
items_ = new Item[nitems_];
|
||||
for (unsigned idx = 0 ; idx < nitems_ ; idx += 1) {
|
||||
items_[idx].statement = 0;
|
||||
}
|
||||
}
|
||||
|
||||
NetCase::~NetCase()
|
||||
{
|
||||
delete expr_;
|
||||
for (unsigned idx = 0 ; idx < nitems_ ; idx += 1) {
|
||||
for (size_t idx = 0 ; idx < items_.size() ; idx += 1) {
|
||||
delete items_[idx].guard;
|
||||
if (items_[idx].statement) delete items_[idx].statement;
|
||||
}
|
||||
delete[]items_;
|
||||
}
|
||||
|
||||
NetCase::TYPE NetCase::type() const
|
||||
|
|
@ -97,7 +92,7 @@ NetCase::TYPE NetCase::type() const
|
|||
|
||||
void NetCase::set_case(unsigned idx, NetExpr*e, NetProc*p)
|
||||
{
|
||||
assert(idx < nitems_);
|
||||
assert(idx < items_.size());
|
||||
items_[idx].guard = e;
|
||||
items_[idx].statement = p;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1903,8 +1903,8 @@ unsigned NetBUFZ::width() const
|
|||
return width_;
|
||||
}
|
||||
|
||||
NetCaseCmp::NetCaseCmp(NetScope*s, perm_string n, unsigned wid, bool eeq__)
|
||||
: NetNode(s, n, 3), width_(wid), eeq_(eeq__)
|
||||
NetCaseCmp::NetCaseCmp(NetScope*s, perm_string n, unsigned wid, kind_t k)
|
||||
: NetNode(s, n, 3), width_(wid), kind_(k)
|
||||
{
|
||||
pin(0).set_dir(Link::OUTPUT);
|
||||
pin(1).set_dir(Link::INPUT);
|
||||
|
|
@ -1920,11 +1920,6 @@ unsigned NetCaseCmp::width() const
|
|||
return width_;
|
||||
}
|
||||
|
||||
bool NetCaseCmp::eeq() const
|
||||
{
|
||||
return eeq_;
|
||||
}
|
||||
|
||||
NetCondit::NetCondit(NetExpr*ex, NetProc*i, NetProc*e)
|
||||
: expr_(ex), if_(i), else_(e)
|
||||
{
|
||||
|
|
|
|||
37
netlist.h
37
netlist.h
|
|
@ -2218,30 +2218,43 @@ class NetBUFZ : public NetNode {
|
|||
* input. The elaboration, btw, needs to make sure the input widths
|
||||
* match.
|
||||
*
|
||||
* The case compare can be generated to handle ===/!==, or also
|
||||
* to test guards in the case/casez/casex statements.
|
||||
*
|
||||
* This pins are assigned as:
|
||||
*
|
||||
* 0 -- Output (always returns 0 or 1)
|
||||
* 1 -- Input
|
||||
* 2 -- Input
|
||||
* 2 -- Input (windcard input for EQX and EQZ variants)
|
||||
*/
|
||||
class NetCaseCmp : public NetNode {
|
||||
|
||||
public:
|
||||
explicit NetCaseCmp(NetScope*s, perm_string n, unsigned wid, bool eeq);
|
||||
enum kind_t {
|
||||
EEQ, // ===
|
||||
NEQ, // !==
|
||||
XEQ, // casex guard tests
|
||||
ZEQ // casez guard tests
|
||||
};
|
||||
|
||||
public:
|
||||
explicit NetCaseCmp(NetScope*s, perm_string n, unsigned wid, kind_t eeq);
|
||||
~NetCaseCmp();
|
||||
|
||||
unsigned width() const;
|
||||
// true if this is ===, false if this is !==
|
||||
bool eeq() const;
|
||||
// What kind of case compare?
|
||||
inline kind_t kind() const { return kind_; }
|
||||
|
||||
virtual void dump_node(ostream&, unsigned ind) const;
|
||||
virtual bool emit_node(struct target_t*) const;
|
||||
|
||||
private:
|
||||
unsigned width_;
|
||||
bool eeq_;
|
||||
const kind_t kind_;
|
||||
};
|
||||
|
||||
extern ostream& operator << (ostream&fd, NetCaseCmp::kind_t that);
|
||||
|
||||
/* NOTE: This class should be replaced with the NetLiteral class
|
||||
* below, that is more general in that it supports different types of
|
||||
* values.
|
||||
|
|
@ -2868,10 +2881,10 @@ class NetCase : public NetProc {
|
|||
|
||||
TYPE type() const;
|
||||
const NetExpr*expr() const { return expr_; }
|
||||
unsigned nitems() const { return nitems_; }
|
||||
inline unsigned nitems() const { return items_.size(); }
|
||||
|
||||
const NetExpr*expr(unsigned idx) const { return items_[idx].guard;}
|
||||
const NetProc*stat(unsigned idx) const { return items_[idx].statement; }
|
||||
inline const NetExpr*expr(unsigned idx) const { return items_[idx].guard;}
|
||||
inline const NetProc*stat(unsigned idx) const { return items_[idx].statement; }
|
||||
|
||||
virtual NexusSet* nex_input(bool rem_out = true);
|
||||
virtual void nex_output(NexusSet&out);
|
||||
|
|
@ -2892,16 +2905,20 @@ class NetCase : public NetProc {
|
|||
bool evaluate_function_real_(const LineInfo&loc,
|
||||
map<perm_string,LocalVar>&ctx) const;
|
||||
|
||||
bool synth_async_casez_(Design*des, NetScope*scope,
|
||||
NexusSet&nex_map, NetBus&nex_out,
|
||||
NetBus&accumulated_nex_out);
|
||||
|
||||
TYPE type_;
|
||||
|
||||
struct Item {
|
||||
inline Item() : guard(0), statement(0) { }
|
||||
NetExpr*guard;
|
||||
NetProc*statement;
|
||||
};
|
||||
|
||||
NetExpr* expr_;
|
||||
unsigned nitems_;
|
||||
Item*items_;
|
||||
std::vector<Item>items_;
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
|||
169
synth2.cc
169
synth2.cc
|
|
@ -252,11 +252,14 @@ bool NetCase::synth_async(Design*des, NetScope*scope,
|
|||
NexusSet&nex_map, NetBus&nex_out,
|
||||
NetBus&accumulated_nex_out)
|
||||
{
|
||||
if (type()==NetCase::EQZ)
|
||||
return synth_async_casez_(des, scope, nex_map, nex_out, accumulated_nex_out);
|
||||
|
||||
/* Synthesize the select expression. */
|
||||
NetNet*esig = expr_->synthesize(des, scope, expr_);
|
||||
|
||||
unsigned sel_width = esig->vector_width();
|
||||
assert(sel_width > 0);
|
||||
ivl_assert(*this, sel_width > 0);
|
||||
|
||||
ivl_assert(*this, nex_map.size() == nex_out.pin_count());
|
||||
|
||||
|
|
@ -288,7 +291,7 @@ bool NetCase::synth_async(Design*des, NetScope*scope,
|
|||
map<unsigned long,NetProc*>statement_map;
|
||||
NetProc*statement_default = 0;
|
||||
|
||||
for (unsigned item = 0 ; item < nitems_ ; item += 1) {
|
||||
for (size_t item = 0 ; item < items_.size() ; item += 1) {
|
||||
if (items_[item].guard == 0) {
|
||||
statement_default = items_[item].statement;
|
||||
continue;
|
||||
|
|
@ -417,6 +420,168 @@ bool NetCase::synth_async(Design*des, NetScope*scope,
|
|||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* casez statements are hard to implement as a single wide mux because
|
||||
* the test doesn't really map to a select input. Instead, implement
|
||||
* it as a chain of binary muxes. This gives the synthesizer my
|
||||
* flexibility, and is more typically what is desired from a casez anyhow.
|
||||
*/
|
||||
bool NetCase::synth_async_casez_(Design*des, NetScope*scope,
|
||||
NexusSet&nex_map, NetBus&nex_out,
|
||||
NetBus&accumulated_nex_out)
|
||||
{
|
||||
/* Synthesize the select expression. */
|
||||
NetNet*esig = expr_->synthesize(des, scope, expr_);
|
||||
|
||||
unsigned sel_width = esig->vector_width();
|
||||
ivl_assert(*this, sel_width > 0);
|
||||
|
||||
ivl_assert(*this, nex_map.size() == nex_out.pin_count());
|
||||
|
||||
vector<unsigned>mux_width (nex_out.pin_count());
|
||||
for (unsigned idx = 0 ; idx < nex_out.pin_count() ; idx += 1) {
|
||||
mux_width[idx] = nex_map[idx].wid;
|
||||
if (debug_synth2) {
|
||||
cerr << get_fileline() << ": NetCase::synth_async_casez_: "
|
||||
<< "idx=" << idx
|
||||
<< ", mux_width[idx]=" << mux_width[idx] << endl;
|
||||
}
|
||||
}
|
||||
|
||||
// The accumulated_nex_out is taken as the input for this
|
||||
// statement. Since there are collection of statements that
|
||||
// start at this same point, we save all these inputs and
|
||||
// reuse them for each statement.
|
||||
NetBus statement_input (scope, nex_out.pin_count());
|
||||
for (unsigned idx = 0 ; idx < nex_out.pin_count() ; idx += 1) {
|
||||
connect(statement_input.pin(idx), accumulated_nex_out.pin(idx));
|
||||
}
|
||||
|
||||
// Look for a default statement.
|
||||
NetProc*statement_default = 0;
|
||||
for (size_t item = 0 ; item < items_.size() ; item += 1) {
|
||||
if (items_[item].guard != 0)
|
||||
continue;
|
||||
|
||||
ivl_assert(*this, statement_default==0);
|
||||
statement_default = items_[item].statement;
|
||||
}
|
||||
|
||||
NetBus default_bus (scope, nex_out.pin_count());
|
||||
if (statement_default) {
|
||||
bool flag = synth_async_block_substatement_(des, scope, nex_map,
|
||||
accumulated_nex_out,
|
||||
statement_default);
|
||||
if (!flag) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (unsigned idx = 0 ; idx < default_bus.pin_count() ; idx += 1) {
|
||||
connect(default_bus.pin(idx), accumulated_nex_out.pin(idx));
|
||||
accumulated_nex_out.pin(idx).unlink();
|
||||
}
|
||||
|
||||
if (debug_synth2) {
|
||||
cerr << get_fileline() << ": NetCase::synth_async_casez_: "
|
||||
<< "synthesize default clause at " << statement_default->get_fileline()
|
||||
<< " is done." << endl;
|
||||
}
|
||||
}
|
||||
|
||||
netvector_t*condit_type = new netvector_t(IVL_VT_LOGIC, 0, 0);
|
||||
|
||||
NetCaseCmp::kind_t case_kind;
|
||||
switch (type()) {
|
||||
case NetCase::EQ:
|
||||
case_kind = NetCaseCmp::EEQ;
|
||||
break;
|
||||
case NetCase::EQX:
|
||||
case_kind = NetCaseCmp::XEQ;
|
||||
break;
|
||||
case NetCase::EQZ:
|
||||
case_kind = NetCaseCmp::ZEQ;
|
||||
break;
|
||||
}
|
||||
|
||||
// Process the items from last to first. We generate a
|
||||
// true/false must, with the select being the comparison of
|
||||
// the case select with the guard expression. The true input
|
||||
// (data1) is the current statement, and the false input is
|
||||
// the result of a later statement.
|
||||
vector<NetMux*>mux_prev (mux_width.size());
|
||||
for (size_t idx = 0 ; idx < items_.size() ; idx += 1) {
|
||||
size_t item = items_.size()-idx-1;
|
||||
if (items_[item].guard == 0)
|
||||
continue;
|
||||
|
||||
NetProc*stmt = items_[item].statement;
|
||||
ivl_assert(*this, stmt);
|
||||
|
||||
NetExpr*guard_expr = items_[item].guard;
|
||||
NetNet*guard = guard_expr->synthesize(des, scope, guard_expr);
|
||||
|
||||
NetCaseCmp*condit_dev = new NetCaseCmp(scope, scope->local_symbol(),
|
||||
sel_width, case_kind);
|
||||
des->add_node(condit_dev);
|
||||
condit_dev->set_line(*this);
|
||||
// Note that the expression that may have windcards must
|
||||
// go in the pin(2) input. This is the definiton of the
|
||||
// NetCaseCmp statement.
|
||||
connect(condit_dev->pin(2), esig->pin(0));
|
||||
connect(condit_dev->pin(1), guard->pin(0));
|
||||
|
||||
NetNet*condit = new NetNet(scope, scope->local_symbol(),
|
||||
NetNet::TRI, condit_type);
|
||||
condit->set_line(*this);
|
||||
condit->local_flag(true);
|
||||
connect(condit_dev->pin(0), condit->pin(0));
|
||||
|
||||
// Synthesize the guarded statement.
|
||||
NetBus true_bus (scope, nex_out.pin_count());
|
||||
for (unsigned pin = 0 ; pin < nex_map.size() ; pin += 1)
|
||||
connect(true_bus.pin(pin), statement_input.pin(pin));
|
||||
|
||||
synth_async_block_substatement_(des, scope, nex_map, true_bus, stmt);
|
||||
|
||||
for (unsigned mdx = 0 ; mdx < mux_width.size() ; mdx += 1) {
|
||||
NetMux*mux_cur = new NetMux(scope, scope->local_symbol(),
|
||||
mux_width[mdx], 2, 1);
|
||||
des->add_node(mux_cur);
|
||||
mux_cur->set_line(*this);
|
||||
connect(mux_cur->pin_Sel(), condit->pin(0));
|
||||
|
||||
connect(mux_cur->pin_Data(1), true_bus.pin(mdx));
|
||||
|
||||
// If there is a previous mux, then use that as the
|
||||
// false clause input. Otherwise, use the default.
|
||||
if (mux_prev[mdx]) {
|
||||
connect(mux_cur->pin_Data(0), mux_prev[mdx]->pin_Result());
|
||||
} else {
|
||||
connect(mux_cur->pin_Data(0), default_bus.pin(mdx));
|
||||
}
|
||||
|
||||
// Make a NetNet for the result.
|
||||
ivl_variable_type_t mux_data_type = IVL_VT_LOGIC;
|
||||
netvector_t*tmp_vec = new netvector_t(mux_data_type, mux_width[mdx]-1, 0);
|
||||
NetNet*tmp = new NetNet(scope, scope->local_symbol(),
|
||||
NetNet::TRI, tmp_vec);
|
||||
tmp->local_flag(true);
|
||||
ivl_assert(*this, tmp->vector_width() != 0);
|
||||
connect(mux_cur->pin_Result(), tmp->pin(0));
|
||||
|
||||
// This mux becomes the "false" input to the next mux.
|
||||
mux_prev[mdx] = mux_cur;
|
||||
}
|
||||
}
|
||||
|
||||
// Connect the last mux to the output.
|
||||
for (size_t mdx = 0 ; mdx < mux_prev.size() ; mdx += 1) {
|
||||
connect(mux_prev[mdx]->pin_Result(), nex_out.pin(mdx));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* A condit statement (if (cond) ... else ... ;) infers an A-B mux,
|
||||
* with the cond expression acting as a select input. If the cond
|
||||
|
|
|
|||
|
|
@ -1164,6 +1164,8 @@ extern "C" ivl_nexus_t ivl_lpm_data(ivl_lpm_t net, unsigned idx)
|
|||
case IVL_LPM_ADD:
|
||||
case IVL_LPM_CMP_EEQ:
|
||||
case IVL_LPM_CMP_EQ:
|
||||
case IVL_LPM_CMP_EQX:
|
||||
case IVL_LPM_CMP_EQZ:
|
||||
case IVL_LPM_CMP_GE:
|
||||
case IVL_LPM_CMP_GT:
|
||||
case IVL_LPM_CMP_NE:
|
||||
|
|
@ -1309,6 +1311,8 @@ extern "C" ivl_nexus_t ivl_lpm_q(ivl_lpm_t net)
|
|||
case IVL_LPM_CMP_EQ:
|
||||
case IVL_LPM_CMP_NE:
|
||||
case IVL_LPM_CMP_EEQ:
|
||||
case IVL_LPM_CMP_EQX:
|
||||
case IVL_LPM_CMP_EQZ:
|
||||
case IVL_LPM_CMP_NEE:
|
||||
case IVL_LPM_DIVIDE:
|
||||
case IVL_LPM_MOD:
|
||||
|
|
@ -1447,6 +1451,8 @@ extern "C" int ivl_lpm_signed(ivl_lpm_t net)
|
|||
case IVL_LPM_CAST_REAL:
|
||||
case IVL_LPM_CMP_EEQ:
|
||||
case IVL_LPM_CMP_EQ:
|
||||
case IVL_LPM_CMP_EQX:
|
||||
case IVL_LPM_CMP_EQZ:
|
||||
case IVL_LPM_CMP_GE:
|
||||
case IVL_LPM_CMP_GT:
|
||||
case IVL_LPM_CMP_NE:
|
||||
|
|
@ -1521,6 +1527,8 @@ extern "C" unsigned ivl_lpm_size(ivl_lpm_t net)
|
|||
case IVL_LPM_ADD:
|
||||
case IVL_LPM_CMP_EEQ:
|
||||
case IVL_LPM_CMP_EQ:
|
||||
case IVL_LPM_CMP_EQX:
|
||||
case IVL_LPM_CMP_EQZ:
|
||||
case IVL_LPM_CMP_GE:
|
||||
case IVL_LPM_CMP_GT:
|
||||
case IVL_LPM_CMP_NE:
|
||||
|
|
|
|||
15
t-dll.cc
15
t-dll.cc
|
|
@ -1190,7 +1190,20 @@ bool dll_target::ureduce(const NetUReduce*net)
|
|||
void dll_target::net_case_cmp(const NetCaseCmp*net)
|
||||
{
|
||||
struct ivl_lpm_s*obj = new struct ivl_lpm_s;
|
||||
obj->type = net->eeq()? IVL_LPM_CMP_EEQ : IVL_LPM_CMP_NEE;
|
||||
switch (net->kind()) {
|
||||
case NetCaseCmp::EEQ:
|
||||
obj->type = IVL_LPM_CMP_EEQ;
|
||||
break;
|
||||
case NetCaseCmp::NEQ:
|
||||
obj->type = IVL_LPM_CMP_NEE;
|
||||
break;
|
||||
case NetCaseCmp::XEQ:
|
||||
obj->type = IVL_LPM_CMP_EQX;
|
||||
break;
|
||||
case NetCaseCmp::ZEQ:
|
||||
obj->type = IVL_LPM_CMP_EQZ;
|
||||
break;
|
||||
}
|
||||
obj->name = net->name();
|
||||
obj->scope = find_scope(des_, net->scope());
|
||||
assert(obj->scope);
|
||||
|
|
|
|||
|
|
@ -59,6 +59,15 @@ static void scan_lpms_equality(ivl_scope_t, ivl_lpm_t lpm, struct sizer_statisti
|
|||
stats.gate_count += 2*wid;
|
||||
}
|
||||
|
||||
static void scan_lpms_equality_wild(ivl_scope_t, ivl_lpm_t lpm, struct sizer_statistics&stats)
|
||||
{
|
||||
unsigned wid = ivl_lpm_width(lpm);
|
||||
|
||||
stats.equality_wc_count[wid] += 1;
|
||||
|
||||
stats.gate_count += 2*wid;
|
||||
}
|
||||
|
||||
/*
|
||||
* Count magnitude comparators as 2m gates.
|
||||
* Also keep a count of comparators by width, just out of curiosity.
|
||||
|
|
@ -129,6 +138,11 @@ void scan_lpms(ivl_scope_t scope, struct sizer_statistics&stats)
|
|||
scan_lpms_equality(scope, lpm, stats);
|
||||
break;
|
||||
|
||||
case IVL_LPM_CMP_EQX:
|
||||
case IVL_LPM_CMP_EQZ:
|
||||
scan_lpms_equality_wild(scope, lpm, stats);
|
||||
break;
|
||||
|
||||
case IVL_LPM_CMP_GE:
|
||||
case IVL_LPM_CMP_GT:
|
||||
scan_lpms_magnitude(scope, lpm, stats);
|
||||
|
|
|
|||
|
|
@ -164,6 +164,11 @@ static void show_stats(struct sizer_statistics&stats)
|
|||
fprintf(sizer_out, " EQUALITY[%u]: %u units\n", cur->first, cur->second);
|
||||
}
|
||||
|
||||
for (map<unsigned,unsigned>::const_iterator cur = stats.equality_wc_count.begin()
|
||||
; cur != stats.equality_wc_count.end() ; ++ cur) {
|
||||
fprintf(sizer_out, " EQUALITY_WC[%u]: %u units\n", cur->first, cur->second);
|
||||
}
|
||||
|
||||
for (map<unsigned,unsigned>::const_iterator cur = stats.magnitude_count.begin()
|
||||
; cur != stats.magnitude_count.end() ; ++ cur) {
|
||||
fprintf(sizer_out, " MAGNITUDE[%u]: %u units\n", cur->first, cur->second);
|
||||
|
|
@ -214,6 +219,10 @@ struct sizer_statistics& sizer_statistics::operator += (const sizer_statistics&t
|
|||
; cur != that.equality_count.end() ; ++ cur)
|
||||
equality_count[cur->first] += cur->second;
|
||||
|
||||
for (map<unsigned,unsigned>::const_iterator cur = that.equality_wc_count.begin()
|
||||
; cur != that.equality_wc_count.end() ; ++ cur)
|
||||
equality_wc_count[cur->first] += cur->second;
|
||||
|
||||
for (map<unsigned,unsigned>::const_iterator cur = that.magnitude_count.begin()
|
||||
; cur != that.magnitude_count.end() ; ++ cur)
|
||||
magnitude_count[cur->first] += cur->second;
|
||||
|
|
|
|||
|
|
@ -33,6 +33,8 @@ struct sizer_statistics {
|
|||
std::map<unsigned,unsigned> adder_count;
|
||||
// count equality comparators
|
||||
std::map<unsigned,unsigned> equality_count;
|
||||
// count equality (with wildcard) comparators
|
||||
std::map<unsigned,unsigned> equality_wc_count;
|
||||
// Count magnitude comparators
|
||||
std::map<unsigned,unsigned> magnitude_count;
|
||||
// Count mux's of various dimension
|
||||
|
|
|
|||
|
|
@ -371,13 +371,31 @@ static void show_lpm_divide(ivl_lpm_t net)
|
|||
show_lpm_arithmetic_pins(net);
|
||||
}
|
||||
|
||||
/* IVL_LPM_CMP_EEQ/NEE
|
||||
/* IVL_LPM_CMP_EEQ/EQX/EQZ/NEE
|
||||
* This LPM node supports two-input compare. The output width is
|
||||
* actually always 1, the lpm_width is the expected width of the inputs.
|
||||
*/
|
||||
static void show_lpm_cmp_eeq(ivl_lpm_t net)
|
||||
{
|
||||
const char*str = (ivl_lpm_type(net) == IVL_LPM_CMP_EEQ)? "EEQ" : "NEE";
|
||||
const char*str;
|
||||
switch (ivl_lpm_type(net)) {
|
||||
case IVL_LPM_CMP_EEQ:
|
||||
str = "EEQ";
|
||||
break;
|
||||
case IVL_LPM_CMP_EQX:
|
||||
str = "EQX";
|
||||
break;
|
||||
case IVL_LPM_CMP_EQZ:
|
||||
str = "EQZ";
|
||||
break;
|
||||
case IVL_LPM_CMP_NEE:
|
||||
str = "NEE";
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
break;
|
||||
}
|
||||
|
||||
unsigned width = ivl_lpm_width(net);
|
||||
|
||||
fprintf(out, " LPM_CMP_%s %s: <width=%u>\n", str,
|
||||
|
|
@ -934,6 +952,8 @@ static void show_lpm(ivl_lpm_t net)
|
|||
break;
|
||||
|
||||
case IVL_LPM_CMP_EEQ:
|
||||
case IVL_LPM_CMP_EQX:
|
||||
case IVL_LPM_CMP_EQZ:
|
||||
case IVL_LPM_CMP_NEE:
|
||||
show_lpm_cmp_eeq(net);
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -450,6 +450,8 @@ static char* draw_net_input_drive(ivl_nexus_t nex, ivl_nexus_ptr_t nptr)
|
|||
case IVL_LPM_CONCATZ:
|
||||
case IVL_LPM_CMP_EEQ:
|
||||
case IVL_LPM_CMP_EQ:
|
||||
case IVL_LPM_CMP_EQX:
|
||||
case IVL_LPM_CMP_EQZ:
|
||||
case IVL_LPM_CMP_GE:
|
||||
case IVL_LPM_CMP_GT:
|
||||
case IVL_LPM_CMP_NE:
|
||||
|
|
|
|||
|
|
@ -1280,6 +1280,8 @@ static const char* draw_lpm_output_delay(ivl_lpm_t net, ivl_variable_type_t dt)
|
|||
switch (ivl_lpm_type(net)) {
|
||||
case IVL_LPM_CMP_EEQ:
|
||||
case IVL_LPM_CMP_EQ:
|
||||
case IVL_LPM_CMP_EQX:
|
||||
case IVL_LPM_CMP_EQZ:
|
||||
case IVL_LPM_CMP_GE:
|
||||
case IVL_LPM_CMP_GT:
|
||||
case IVL_LPM_CMP_NE:
|
||||
|
|
@ -1489,6 +1491,16 @@ static void draw_lpm_cmp(ivl_lpm_t net)
|
|||
type = "eq";
|
||||
signed_string = "";
|
||||
break;
|
||||
case IVL_LPM_CMP_EQX:
|
||||
assert(dtc != IVL_VT_REAL);
|
||||
type = "eqx";
|
||||
signed_string = "";
|
||||
break;
|
||||
case IVL_LPM_CMP_EQZ:
|
||||
assert(dtc != IVL_VT_REAL);
|
||||
type = "eqz";
|
||||
signed_string = "";
|
||||
break;
|
||||
case IVL_LPM_CMP_GE:
|
||||
if (dtc == IVL_VT_REAL) {
|
||||
type = "ge.r";
|
||||
|
|
@ -2060,6 +2072,8 @@ static void draw_lpm_in_scope(ivl_lpm_t net)
|
|||
|
||||
case IVL_LPM_CMP_EEQ:
|
||||
case IVL_LPM_CMP_EQ:
|
||||
case IVL_LPM_CMP_EQX:
|
||||
case IVL_LPM_CMP_EQZ:
|
||||
case IVL_LPM_CMP_GE:
|
||||
case IVL_LPM_CMP_GT:
|
||||
case IVL_LPM_CMP_NE:
|
||||
|
|
|
|||
77
vvp/arith.cc
77
vvp/arith.cc
|
|
@ -670,6 +670,83 @@ void vvp_cmp_eq::recv_vec4(vvp_net_ptr_t ptr, const vvp_vector4_t&bit,
|
|||
net->send_vec4(res, 0);
|
||||
}
|
||||
|
||||
vvp_cmp_eqx::vvp_cmp_eqx(unsigned wid)
|
||||
: vvp_arith_(wid)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* Compare Vector a and Vector b. If in any bit position the a and b
|
||||
* bits are known and different, then the result is 0. Otherwise, if
|
||||
* there are X/Z bits anywhere in A or B, the result is X. Finally,
|
||||
* the result is 1.
|
||||
*/
|
||||
void vvp_cmp_eqx::recv_vec4(vvp_net_ptr_t ptr, const vvp_vector4_t&bit,
|
||||
vvp_context_t)
|
||||
{
|
||||
dispatch_operand_(ptr, bit);
|
||||
|
||||
if (op_a_.size() != op_b_.size()) {
|
||||
cerr << "COMPARISON size mismatch. "
|
||||
<< "a=" << op_a_ << ", b=" << op_b_ << endl;
|
||||
assert(0);
|
||||
}
|
||||
|
||||
vvp_vector4_t res (1);
|
||||
res.set_bit(0, BIT4_1);
|
||||
|
||||
for (unsigned idx = 0 ; idx < op_a_.size() ; idx += 1) {
|
||||
vvp_bit4_t a = op_a_.value(idx);
|
||||
vvp_bit4_t b = op_b_.value(idx);
|
||||
|
||||
if (b == BIT4_X)
|
||||
continue;
|
||||
if (b == BIT4_Z)
|
||||
continue;
|
||||
if (a != b) {
|
||||
res.set_bit(0, BIT4_0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
vvp_net_t*net = ptr.ptr();
|
||||
net->send_vec4(res, 0);
|
||||
}
|
||||
|
||||
vvp_cmp_eqz::vvp_cmp_eqz(unsigned wid)
|
||||
: vvp_arith_(wid)
|
||||
{
|
||||
}
|
||||
|
||||
void vvp_cmp_eqz::recv_vec4(vvp_net_ptr_t ptr, const vvp_vector4_t&bit,
|
||||
vvp_context_t)
|
||||
{
|
||||
dispatch_operand_(ptr, bit);
|
||||
|
||||
if (op_a_.size() != op_b_.size()) {
|
||||
cerr << "COMPARISON size mismatch. "
|
||||
<< "a=" << op_a_ << ", b=" << op_b_ << endl;
|
||||
assert(0);
|
||||
}
|
||||
|
||||
vvp_vector4_t res (1);
|
||||
res.set_bit(0, BIT4_1);
|
||||
|
||||
for (unsigned idx = 0 ; idx < op_a_.size() ; idx += 1) {
|
||||
vvp_bit4_t a = op_a_.value(idx);
|
||||
vvp_bit4_t b = op_b_.value(idx);
|
||||
|
||||
if (b == BIT4_Z)
|
||||
continue;
|
||||
if (a != b) {
|
||||
res.set_bit(0, BIT4_0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
vvp_net_t*net = ptr.ptr();
|
||||
net->send_vec4(res, 0);
|
||||
}
|
||||
|
||||
vvp_cmp_ne::vvp_cmp_ne(unsigned wid)
|
||||
: vvp_arith_(wid)
|
||||
|
|
|
|||
18
vvp/arith.h
18
vvp/arith.h
|
|
@ -157,6 +157,24 @@ class vvp_cmp_eq : public vvp_arith_ {
|
|||
|
||||
};
|
||||
|
||||
class vvp_cmp_eqx : public vvp_arith_ {
|
||||
|
||||
public:
|
||||
explicit vvp_cmp_eqx(unsigned wid);
|
||||
void recv_vec4(vvp_net_ptr_t ptr, const vvp_vector4_t&bit,
|
||||
vvp_context_t);
|
||||
|
||||
};
|
||||
|
||||
class vvp_cmp_eqz : public vvp_arith_ {
|
||||
|
||||
public:
|
||||
explicit vvp_cmp_eqz(unsigned wid);
|
||||
void recv_vec4(vvp_net_ptr_t ptr, const vvp_vector4_t&bit,
|
||||
vvp_context_t);
|
||||
|
||||
};
|
||||
|
||||
class vvp_cmp_ne : public vvp_arith_ {
|
||||
|
||||
public:
|
||||
|
|
|
|||
|
|
@ -1245,6 +1245,34 @@ void compile_cmp_eq(char*label, long wid, unsigned argc, struct symb_s*argv)
|
|||
make_arith(arith, label, argc, argv);
|
||||
}
|
||||
|
||||
void compile_cmp_eqx(char*label, long wid, unsigned argc, struct symb_s*argv)
|
||||
{
|
||||
assert( wid > 0 );
|
||||
|
||||
if (argc != 2) {
|
||||
fprintf(stderr, "%s .cmp/eqx has wrong number of symbols\n",label);
|
||||
compile_errors += 1;
|
||||
return;
|
||||
}
|
||||
|
||||
vvp_arith_ *arith = new vvp_cmp_eqx(wid);
|
||||
make_arith(arith, label, argc, argv);
|
||||
}
|
||||
|
||||
void compile_cmp_eqz(char*label, long wid, unsigned argc, struct symb_s*argv)
|
||||
{
|
||||
assert( wid > 0 );
|
||||
|
||||
if (argc != 2) {
|
||||
fprintf(stderr, "%s .cmp/eqz has wrong number of symbols\n",label);
|
||||
compile_errors += 1;
|
||||
return;
|
||||
}
|
||||
|
||||
vvp_arith_ *arith = new vvp_cmp_eqz(wid);
|
||||
make_arith(arith, label, argc, argv);
|
||||
}
|
||||
|
||||
void compile_cmp_eq_r(char*label, unsigned argc, struct symb_s*argv)
|
||||
{
|
||||
if (argc != 2) {
|
||||
|
|
|
|||
|
|
@ -181,6 +181,10 @@ extern void compile_cmp_nee(char*label, long width,
|
|||
unsigned argc, struct symb_s*argv);
|
||||
extern void compile_cmp_eq(char*label, long width,
|
||||
unsigned argc, struct symb_s*argv);
|
||||
extern void compile_cmp_eqx(char*label, long width,
|
||||
unsigned argc, struct symb_s*argv);
|
||||
extern void compile_cmp_eqz(char*label, long width,
|
||||
unsigned argc, struct symb_s*argv);
|
||||
extern void compile_cmp_ne(char*label, long width,
|
||||
unsigned argc, struct symb_s*argv);
|
||||
extern void compile_cmp_ge(char*label, long width, bool signed_flag,
|
||||
|
|
|
|||
|
|
@ -134,6 +134,8 @@ static char* strdupnew(char const *str)
|
|||
".cast/real.s" { return K_CAST_REAL_S; }
|
||||
".class" { return K_CLASS; }
|
||||
".cmp/eeq" { return K_CMP_EEQ; }
|
||||
".cmp/eqx" { return K_CMP_EQX; }
|
||||
".cmp/eqz" { return K_CMP_EQZ; }
|
||||
".cmp/eq" { return K_CMP_EQ; }
|
||||
".cmp/eq.r" { return K_CMP_EQ_R; }
|
||||
".cmp/nee" { return K_CMP_NEE; }
|
||||
|
|
|
|||
13
vvp/parse.y
13
vvp/parse.y
|
|
@ -80,7 +80,8 @@ static struct __vpiModPath*modpath_dst = 0;
|
|||
%token K_ARRAY K_ARRAY_2U K_ARRAY_2S K_ARRAY_I K_ARRAY_R K_ARRAY_S K_ARRAY_STR K_ARRAY_PORT
|
||||
%token K_CAST_INT K_CAST_REAL K_CAST_REAL_S K_CAST_2
|
||||
%token K_CLASS
|
||||
%token K_CMP_EEQ K_CMP_EQ K_CMP_EQ_R K_CMP_NEE K_CMP_NE K_CMP_NE_R
|
||||
%token K_CMP_EEQ K_CMP_EQ K_CMP_EQX K_CMP_EQZ
|
||||
%token K_CMP_EQ_R K_CMP_NEE K_CMP_NE K_CMP_NE_R
|
||||
%token K_CMP_GE K_CMP_GE_R K_CMP_GE_S K_CMP_GT K_CMP_GT_R K_CMP_GT_S
|
||||
%token K_CONCAT K_CONCAT8 K_DEBUG K_DELAY K_DFF
|
||||
%token K_ENUM2 K_ENUM2_S K_ENUM4 K_ENUM4_S K_EVENT K_EVENT_OR
|
||||
|
|
@ -414,6 +415,16 @@ statement
|
|||
compile_cmp_eq($1, $3, obj.cnt, obj.vect);
|
||||
}
|
||||
|
||||
| T_LABEL K_CMP_EQX T_NUMBER ',' symbols ';'
|
||||
{ struct symbv_s obj = $5;
|
||||
compile_cmp_eqx($1, $3, obj.cnt, obj.vect);
|
||||
}
|
||||
|
||||
| T_LABEL K_CMP_EQZ T_NUMBER ',' symbols ';'
|
||||
{ struct symbv_s obj = $5;
|
||||
compile_cmp_eqz($1, $3, obj.cnt, obj.vect);
|
||||
}
|
||||
|
||||
| T_LABEL K_CMP_EQ_R T_NUMBER ',' symbols ';'
|
||||
{ struct symbv_s obj = $5;
|
||||
compile_cmp_eq_r($1, obj.cnt, obj.vect);
|
||||
|
|
|
|||
Loading…
Reference in New Issue