diff --git a/design_dump.cc b/design_dump.cc index 0004cc3b4..65f602f3c 100644 --- a/design_dump.cc +++ b/design_dump.cc @@ -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 << ":"; diff --git a/expr_synth.cc b/expr_synth.cc index f709668b9..28f3c783e 100644 --- a/expr_synth.cc +++ b/expr_synth.cc @@ -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)); diff --git a/ivl_target.h b/ivl_target.h index 70b5b9db2..56f5fc476 100644 --- a/ivl_target.h +++ b/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); diff --git a/net_func_eval.cc b/net_func_eval.cc index a5b336b11..ecbe55780 100644 --- a/net_func_eval.cc +++ b/net_func_eval.cc @@ -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; diff --git a/net_nex_input.cc b/net_nex_input.cc index 50053bf04..c30f52368 100644 --- a/net_nex_input.cc +++ b/net_nex_input.cc @@ -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) diff --git a/net_nex_output.cc b/net_nex_output.cc index 232f0a122..a6cc52986 100644 --- a/net_nex_output.cc +++ b/net_nex_output.cc @@ -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) diff --git a/net_proc.cc b/net_proc.cc index 081a4ea40..c10bff7ab 100644 --- a/net_proc.cc +++ b/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; } diff --git a/netlist.cc b/netlist.cc index ebbf29d0f..2d266c943 100644 --- a/netlist.cc +++ b/netlist.cc @@ -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) { diff --git a/netlist.h b/netlist.h index 1f5d4e9be..87ea09ab7 100644 --- a/netlist.h +++ b/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&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::vectoritems_; }; /* diff --git a/synth2.cc b/synth2.cc index 8b5370b9a..104533b08 100644 --- a/synth2.cc +++ b/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, mapstatement_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()); + + vectormux_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. + vectormux_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 diff --git a/t-dll-api.cc b/t-dll-api.cc index 4fcd623f8..cd12d2dfd 100644 --- a/t-dll-api.cc +++ b/t-dll-api.cc @@ -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: diff --git a/t-dll.cc b/t-dll.cc index a068bb59b..6eff6ce08 100644 --- a/t-dll.cc +++ b/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); diff --git a/tgt-sizer/scan_lpms.cc b/tgt-sizer/scan_lpms.cc index a5c6b5aae..18dcbb7d8 100644 --- a/tgt-sizer/scan_lpms.cc +++ b/tgt-sizer/scan_lpms.cc @@ -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); diff --git a/tgt-sizer/sizer.cc b/tgt-sizer/sizer.cc index a729e6f2b..8eabea097 100644 --- a/tgt-sizer/sizer.cc +++ b/tgt-sizer/sizer.cc @@ -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::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::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::const_iterator cur = that.equality_wc_count.begin() + ; cur != that.equality_wc_count.end() ; ++ cur) + equality_wc_count[cur->first] += cur->second; + for (map::const_iterator cur = that.magnitude_count.begin() ; cur != that.magnitude_count.end() ; ++ cur) magnitude_count[cur->first] += cur->second; diff --git a/tgt-sizer/sizer_priv.h b/tgt-sizer/sizer_priv.h index 85ca43cb9..721c34602 100644 --- a/tgt-sizer/sizer_priv.h +++ b/tgt-sizer/sizer_priv.h @@ -33,6 +33,8 @@ struct sizer_statistics { std::map adder_count; // count equality comparators std::map equality_count; + // count equality (with wildcard) comparators + std::map equality_wc_count; // Count magnitude comparators std::map magnitude_count; // Count mux's of various dimension diff --git a/tgt-stub/stub.c b/tgt-stub/stub.c index de786f3e2..b8a279c13 100644 --- a/tgt-stub/stub.c +++ b/tgt-stub/stub.c @@ -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: \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; diff --git a/tgt-vvp/draw_net_input.c b/tgt-vvp/draw_net_input.c index 3ee73d36b..4a142f0e0 100644 --- a/tgt-vvp/draw_net_input.c +++ b/tgt-vvp/draw_net_input.c @@ -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: diff --git a/tgt-vvp/vvp_scope.c b/tgt-vvp/vvp_scope.c index 7a8632367..643316d23 100644 --- a/tgt-vvp/vvp_scope.c +++ b/tgt-vvp/vvp_scope.c @@ -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: diff --git a/vvp/arith.cc b/vvp/arith.cc index 7b23e3bd6..7fe9ebcef 100644 --- a/vvp/arith.cc +++ b/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) diff --git a/vvp/arith.h b/vvp/arith.h index bd1b99c5c..5f714e231 100644 --- a/vvp/arith.h +++ b/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: diff --git a/vvp/compile.cc b/vvp/compile.cc index f79b5b883..a8d33357e 100644 --- a/vvp/compile.cc +++ b/vvp/compile.cc @@ -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) { diff --git a/vvp/compile.h b/vvp/compile.h index 6685cc2d1..43ba3cb27 100644 --- a/vvp/compile.h +++ b/vvp/compile.h @@ -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, diff --git a/vvp/lexor.lex b/vvp/lexor.lex index a2d8cbf8f..6e59154ab 100644 --- a/vvp/lexor.lex +++ b/vvp/lexor.lex @@ -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; } diff --git a/vvp/parse.y b/vvp/parse.y index 8fff1c223..04f241fe5 100644 --- a/vvp/parse.y +++ b/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);