/* * Copyright (c) 1998-2021 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 * 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 "config.h" # include # include # include # include # include # include "compiler.h" # include "netlist.h" # include "netmisc.h" # include "netclass.h" # include "netdarray.h" # include "netenum.h" # include "netparray.h" # include "netscalar.h" # include "netqueue.h" # include "netstruct.h" # include "netvector.h" # include "ivl_assert.h" using namespace std; ostream& operator<< (ostream&o, NetNet::Type t) { switch (t) { case NetNet::NONE: o << "net_none"; break; case NetNet::IMPLICIT: o << "wire /*implicit*/"; break; case NetNet::IMPLICIT_REG: o << "reg /*implicit*/"; break; case NetNet::REG: o << "reg"; break; case NetNet::SUPPLY0: o << "supply0"; break; case NetNet::SUPPLY1: o << "supply1"; break; case NetNet::TRI: o << "tri"; break; case NetNet::TRI0: o << "tri0"; break; case NetNet::TRI1: o << "tri1"; break; case NetNet::TRIAND: o << "triand"; break; case NetNet::TRIOR: o << "trior"; break; case NetNet::WAND: o << "wand"; break; case NetNet::WOR: o << "wor"; break; case NetNet::WIRE: o << "wire"; break; case NetNet::UNRESOLVED_WIRE: o << "uwire"; } return o; } unsigned count_signals(const Link&pin) { unsigned count = 0; const Nexus*nex = pin.nexus(); for (const Link*clnk = nex->first_nlink() ; clnk ; clnk = clnk->next_nlink()) { const NetPins*cur; unsigned cpin; clnk->cur_link(cur, cpin); if (dynamic_cast(cur)) count += 1; } return count; } const NetNet* find_link_signal(const NetObj*net, unsigned pin, unsigned&bidx) { const Nexus*nex = net->pin(pin).nexus(); for (const Link*clnk = nex->first_nlink() ; clnk ; clnk = clnk->next_nlink()) { const NetPins*cur; unsigned cpin; clnk->cur_link(cur, cpin); const NetNet*sig = dynamic_cast(cur); if (sig) { bidx = cpin; return sig; } } return 0; } Link* find_next_output(Link*lnk) { Link*cur = lnk->next_nlink(); while (cur != lnk) { if (cur->get_dir() == Link::OUTPUT) return cur; cur = cur->next_nlink(); if (cur == 0) cur = lnk->nexus()->first_nlink(); } return 0; } void NetPins::devirtualize_pins(void) { if (pins_) return; if (npins_ > array_size_limit) { cerr << get_fileline() << ": error: pin count " << npins_ << " exceeds " << array_size_limit << " (set by -pARRAY_SIZE_LIMIT)" << endl; ivl_assert(*this, 0); } if (debug_optimizer && npins_ > 1000) cerr << "debug: devirtualizing " << npins_ << " pins." << endl; pins_ = new Link[npins_]; pins_[0].pin_zero_ = true; pins_[0].node_ = this; pins_[0].dir_ = default_dir_; for (unsigned idx = 1 ; idx < npins_ ; idx += 1) { pins_[idx].pin_zero_ = false; pins_[idx].pin_ = idx; pins_[idx].dir_ = default_dir_; } } bool NetPins::pins_are_virtual(void) const { return pins_ == NULL; } NetPins::NetPins(unsigned npins) : npins_(npins) { default_dir_ = Link::PASSIVE; pins_ = NULL; // Wait until someone asks. if (disable_virtual_pins) devirtualize_pins(); // Ask. Bummer. } NetPins::~NetPins() { if (pins_) { ivl_assert(*this, pins_[0].node_ == this); ivl_assert(*this, pins_[0].pin_zero_); delete[] pins_; } } Link& NetPins::pin(unsigned idx) { if (!pins_) devirtualize_pins(); if (idx >= npins_) { cerr << get_fileline() << ": internal error: pin("< 0); * because it would happen before we get to print a useful * message in the NetNet constructor */ } NetObj::~NetObj() { } NetScope* NetObj::scope() { return scope_; } const NetScope* NetObj::scope() const { return scope_; } NetNode::NetNode(NetScope*s, perm_string n, unsigned npins) : NetObj(s, n, npins), node_next_(0), node_prev_(0), design_(0) { } NetNode::~NetNode() { if (design_) design_->del_node(this); } NetBranch::NetBranch(ivl_discipline_t dis) : NetPins(2), IslandBranch(dis) { pin(0).set_dir(Link::PASSIVE); pin(1).set_dir(Link::PASSIVE); } NetBranch::~NetBranch() { } NetBus::NetBus(NetScope*s, unsigned pin_count__) : NetObj(s, perm_string::literal(""), pin_count__) { for (unsigned idx = 0 ; idx &unpacked) { unsigned long sum = 1; for (list::const_iterator cur = unpacked.begin() ; cur != unpacked.end() ; ++cur) { // Special case: If there are any undefined dimensions, // then give up on trying to create pins for the net. if (! cur->defined()) return 0; sum *= cur->width(); } if (sum >= UINT_MAX) return 0; return sum; } template static unsigned calculate_count(T*type) { long wid = type->packed_width(); if (wid >= 0) return wid; else return 1; } void NetNet::calculate_slice_widths_from_packed_dims_(void) { ivl_assert(*this, net_type_); if (!net_type_->packed()) return; slice_dims_ = net_type_->slice_dimensions(); // Special case: There are no actual packed dimensions, so // build up a fake dimension of "1". if (slice_dims_.empty()) { slice_wids_.resize(1); slice_wids_[0] = net_type_->packed_width(); return; } slice_wids_.resize(slice_dims_.size()); ivl_assert(*this, ! slice_wids_.empty()); slice_wids_[0] = netrange_width(slice_dims_); vector::const_iterator cur = slice_dims_.begin(); for (size_t idx = 1 ; idx < slice_wids_.size() ; idx += 1, ++cur) { slice_wids_[idx] = slice_wids_[idx-1] / cur->width(); } } const list NetNet::not_an_array; NetNet::NetNet(NetScope*s, perm_string n, Type t, const list&unpacked, ivl_type_t use_net_type) : NetObj(s, n, calculate_count(unpacked)), type_(t), port_type_(NOT_A_PORT), local_flag_(false), net_type_(use_net_type), discipline_(0), unpacked_dims_(unpacked.size()), eref_count_(0), lref_count_(0) { calculate_slice_widths_from_packed_dims_(); size_t idx = 0; for (list::const_iterator cur = unpacked.begin() ; cur != unpacked.end() ; ++cur, idx += 1) { unpacked_dims_[idx] = *cur; } ivl_assert(*this, idx == unpacked_dims_.size()); ivl_assert(*this, s); if (pin_count() == 0) { cerr << "Invalid array dimensions: " << unpacked << endl; ivl_assert(*this, 0); } initialize_dir_(); s->add_signal(this); } NetNet::NetNet(NetScope*s, perm_string n, Type t, ivl_type_t type) : NetObj(s, n, 1), type_(t), port_type_(NOT_A_PORT), local_flag_(false), net_type_(type), discipline_(0), eref_count_(0), lref_count_(0) { calculate_slice_widths_from_packed_dims_(); initialize_dir_(); s->add_signal(this); } NetNet::~NetNet() { if (eref_count_ > 0) { cerr << get_fileline() << ": internal error: attempt to delete " << "signal ``" << name() << "'' which has " << "expression references." << endl; dump_net(cerr, 4); } ivl_assert(*this, eref_count_ == 0); if (lref_count_ > 0) { cerr << get_fileline() << ": internal error: attempt to delete " << "signal ``" << name() << "'' which has " << "assign references." << endl; dump_net(cerr, 4); } ivl_assert(*this, lref_count_ == 0); if (scope()) scope()->rem_signal(this); } NetNet::Type NetNet::type() const { return type_; } void NetNet::type(NetNet::Type t) { if (type_ == t) return; type_ = t; initialize_dir_(); } NetNet::PortType NetNet::port_type() const { return port_type_; } void NetNet::port_type(NetNet::PortType t) { port_type_ = t; } int NetNet::get_module_port_index() const { return port_index_; } void NetNet::set_module_port_index(unsigned idx) { port_index_ = idx; ivl_assert(*this, port_index_ >= 0); } ivl_variable_type_t NetNet::data_type() const { ivl_assert(*this, net_type_); return net_type_->base_type(); } bool NetNet::get_signed() const { ivl_assert(*this, net_type_); return net_type_->get_signed(); } bool NetNet::get_scalar() const { ivl_assert(*this, net_type_); return net_type_->get_scalar(); } const netenum_t*NetNet::enumeration(void) const { return dynamic_cast (net_type_); } const netstruct_t*NetNet::struct_type(void) const { ivl_type_t cur_type = net_type_; while (cur_type) { if (const netdarray_t*da = dynamic_cast (cur_type)) { cur_type = da->element_type(); continue; } if (const netparray_t*da = dynamic_cast (cur_type)) { cur_type = da->element_type(); continue; } if (const netstruct_t*st = dynamic_cast (cur_type)) return st; else return 0; } ivl_assert(*this, 0); return 0; } const netdarray_t* NetNet::darray_type(void) const { return dynamic_cast (net_type_); } const netqueue_t* NetNet::queue_type(void) const { return dynamic_cast (net_type_); } const netclass_t* NetNet::class_type(void) const { return dynamic_cast (net_type_); } /* * "depth" is the number of index expressions that the user is using * to index this identifier. So consider if Net was declared like so: * * reg [5:0][3:0] foo; * * In this case, slice_width(2) == 1 (slice_width(N) where N is the * number of dimensions will always be 1.) and represents * $bits(foo[a][b]). Then, slice_width(1)==4 ($bits(foo[a]) and slice_width(0)==24. * * NOTE: The caller should already have accounted for unpacked * dimensions. The "depth" is only for the packed dimensions. */ unsigned long NetNet::slice_width(size_t depth) const { if (depth > slice_wids_.size()) return 0; if (depth == slice_wids_.size()) return 1; return slice_wids_[depth]; } ivl_discipline_t NetNet::get_discipline() const { return discipline_; } void NetNet::set_discipline(ivl_discipline_t dis) { ivl_assert(*this, discipline_ == 0); discipline_ = dis; } bool NetNet::sb_is_valid(const list&indices, long sb) const { ivl_assert(*this, indices.size()+1 == packed_dims().size()); ivl_assert(*this, packed_dims().size() == 1); const netrange_t&rng = packed_dims().back(); if (rng.get_msb() >= rng.get_lsb()) return (sb <= rng.get_msb()) && (sb >= rng.get_lsb()); else return (sb <= rng.get_lsb()) && (sb >= rng.get_msb()); } long NetNet::sb_to_idx(const list&indices, long sb) const { ivl_assert(*this, indices.size()+1 == packed_dims().size()); vector::const_iterator pcur = packed_dims().end(); -- pcur; long acc_off; long acc_wid = pcur->width(); if (pcur->get_msb() >= pcur->get_lsb()) acc_off = sb - pcur->get_lsb(); else acc_off = pcur->get_lsb() - sb; // The acc_off is the position within the innermost // dimension. If this is a multi-dimension packed array then // we need to add in the canonical address of the current slice. if (! indices.empty()) { list::const_iterator icur = indices.end(); do { -- icur; -- pcur; long tmp_off; if (pcur->get_msb() >= pcur->get_lsb()) tmp_off = *icur - pcur->get_lsb(); else tmp_off = pcur->get_lsb() - *icur; acc_off += tmp_off * acc_wid; acc_wid *= pcur->width(); } while (icur != indices.begin()); } return acc_off; } bool NetNet::sb_to_slice(const list&indices, long sb, long&loff, unsigned long&lwid) const { ivl_assert(*this, indices.size() < packed_dims().size()); return prefix_to_slice(packed_dims(), indices, sb, loff, lwid); } unsigned NetNet::unpacked_count() const { unsigned c = 1; for (size_t idx = 0 ; idx < unpacked_dims_.size() ; idx += 1) { c *= unpacked_dims_[idx].width(); } return c; } void NetNet::incr_eref() { eref_count_ += 1; } void NetNet::decr_eref() { ivl_assert(*this, eref_count_ > 0); eref_count_ -= 1; } unsigned NetNet::peek_eref() const { return eref_count_; } /* * Test each of the bits in the range, and set them. If any bits are * already set then return true. */ bool NetNet::test_and_set_part_driver(unsigned pmsb, unsigned plsb, int widx) { if (lref_mask_.empty()) lref_mask_.resize(vector_width() * pin_count()); // If indexing a word that doesn't exist, then pretend this is // never driven. if (widx < 0) return false; if (widx >= (int)pin_count()) return false; bool rc = false; unsigned word_base = vector_width() * widx; for (unsigned idx = plsb ; idx <= pmsb ; idx += 1) { if (lref_mask_[idx+word_base]) rc = true; else lref_mask_[idx+word_base] = true; } return rc; } void NetNet::incr_lref() { lref_count_ += 1; } void NetNet::decr_lref() { ivl_assert(*this, lref_count_ > 0); lref_count_ -= 1; } unsigned NetNet::get_refs() const { return lref_count_ + eref_count_; } void NetNet::add_delay_path(NetDelaySrc*path) { delay_paths_.push_back(path); } unsigned NetNet::delay_paths(void)const { return delay_paths_.size(); } const NetDelaySrc* NetNet::delay_path(unsigned idx) const { ivl_assert(*this, idx < delay_paths_.size()); return delay_paths_[idx]; } NetPartSelect::NetPartSelect(NetNet*sig, unsigned off, unsigned wid, NetPartSelect::dir_t dir__, bool signed_flag__) : NetNode(sig->scope(), sig->scope()->local_symbol(), 2), off_(off), wid_(wid), dir_(dir__), signed_flag_(signed_flag__) { set_line(*sig); switch (dir_) { case NetPartSelect::VP: pin(0).set_dir(Link::OUTPUT); pin(1).set_dir(Link::INPUT); break; case NetPartSelect::PV: pin(0).set_dir(Link::INPUT); pin(1).set_dir(Link::OUTPUT); break; } connect(pin(1), sig->pin(0)); } NetPartSelect::NetPartSelect(NetNet*sig, NetNet*sel, unsigned wid, bool signed_flag__) : NetNode(sig->scope(), sig->scope()->local_symbol(), 3), off_(0), wid_(wid), dir_(VP), signed_flag_(signed_flag__) { switch (dir_) { case NetPartSelect::VP: pin(0).set_dir(Link::OUTPUT); pin(1).set_dir(Link::INPUT); break; case NetPartSelect::PV: /* Only a vector to part can be a variable select. */ ivl_assert(*this, 0); } pin(2).set_dir(Link::INPUT); connect(pin(1), sig->pin(0)); connect(pin(2), sel->pin(0)); } NetPartSelect::~NetPartSelect() { } unsigned NetPartSelect::width() const { return wid_; } unsigned NetPartSelect::base() const { return off_; } NetSubstitute::NetSubstitute(NetNet*sig, NetNet*sub, unsigned wid, unsigned off) : NetNode(sig->scope(), sig->scope()->local_symbol(), 3), wid_(wid), off_(off) { pin(0).set_dir(Link::OUTPUT); pin(1).set_dir(Link::INPUT); pin(2).set_dir(Link::INPUT); connect(pin(1), sig->pin(0)); connect(pin(2), sub->pin(0)); } NetSubstitute::~NetSubstitute() { } NetProc::NetProc() : next_(0) { } NetProc::~NetProc() { } NetProcTop::NetProcTop(NetScope*s, ivl_process_type_t t, NetProc*st) : type_(t), statement_(st), scope_(s) { synthesized_design_ = 0; } NetProcTop::~NetProcTop() { if (!synthesized_design_) { delete statement_; return; } NexusSet nex_set; statement_->nex_output(nex_set); delete statement_; bool flag = false; for (unsigned idx = 0 ; idx < nex_set.size() ; idx += 1) { NetNet*net = nex_set[idx].lnk.nexus()->pick_any_net(); if (net->peek_lref() > 0) { cerr << get_fileline() << ": warning: '" << net->name() << "' is driven by more than one process." << endl; flag = true; } } if (flag) { cerr << get_fileline() << ": sorry: Cannot synthesize signals " "that are driven by more than one process." << endl; synthesized_design_->errors += 1; } } NetProc* NetProcTop::statement() { return statement_; } const NetProc* NetProcTop::statement() const { return statement_; } NetScope* NetProcTop::scope() { return scope_; } const NetScope* NetProcTop::scope() const { return scope_; } NetAnalogTop::NetAnalogTop(NetScope*scope__, ivl_process_type_t t, NetProc*st) : type_(t), statement_(st), scope_(scope__) { next_ = 0; } NetAnalogTop::~NetAnalogTop() { } NetProc* NetAnalogTop::statement() { return statement_; } const NetProc* NetAnalogTop::statement() const { return statement_; } NetScope* NetAnalogTop::scope() { return scope_; } const NetScope* NetAnalogTop::scope() const { return scope_; } NetCastInt2::NetCastInt2(NetScope*scope__, perm_string n, unsigned width__) : NetNode(scope__, n, 2), width_(width__) { pin(0).set_dir(Link::OUTPUT); pin(1).set_dir(Link::INPUT); } NetCastInt4::NetCastInt4(NetScope*scope__, perm_string n, unsigned width__) : NetNode(scope__, n, 2), width_(width__) { pin(0).set_dir(Link::OUTPUT); pin(1).set_dir(Link::INPUT); } NetCastReal::NetCastReal(NetScope*scope__, perm_string n, bool signed_flag__) : NetNode(scope__, n, 2), signed_flag_(signed_flag__) { pin(0).set_dir(Link::OUTPUT); pin(1).set_dir(Link::INPUT); } NetConcat::NetConcat(NetScope*scope__, perm_string n, unsigned wid, unsigned cnt, bool trans_flag) : NetNode(scope__, n, cnt+1), width_(wid), transparent_(trans_flag) { pin(0).set_dir(Link::OUTPUT); for (unsigned idx = 1 ; idx < cnt+1 ; idx += 1) { pin(idx).set_dir(Link::INPUT); } } NetConcat::~NetConcat() { } unsigned NetConcat::width() const { return width_; } NetReplicate::NetReplicate(NetScope*scope__, perm_string n, unsigned wid, unsigned rpt) : NetNode(scope__, n, 2), width_(wid), repeat_(rpt) { pin(0).set_dir(Link::OUTPUT); pin(1).set_dir(Link::INPUT); } NetReplicate::~NetReplicate() { } unsigned NetReplicate::width() const { return width_; } unsigned NetReplicate::repeat() const { return repeat_; } /* * The NetFF class represents an LPM_FF device. The pinout is assigned * like so: * 0 -- Clock * 1 -- Enable * 2 -- Aset * 3 -- Aclr * 4 -- Sset * 5 -- Sclr * 6 -- Data * 7 -- Q * ... */ NetFF::NetFF(NetScope*s, perm_string n, bool negedge__, unsigned width__) : NetNode(s, n, 8), negedge_(negedge__), width_(width__) { pin_Clock().set_dir(Link::INPUT); pin_Enable().set_dir(Link::INPUT); pin_Aset().set_dir(Link::INPUT); pin_Aclr().set_dir(Link::INPUT); pin_Sset().set_dir(Link::INPUT); pin_Sclr().set_dir(Link::INPUT); pin_Data().set_dir(Link::INPUT); pin_Q().set_dir(Link::OUTPUT); } NetFF::~NetFF() { } bool NetFF::is_negedge() const { return negedge_; } unsigned NetFF::width() const { return width_; } Link& NetFF::pin_Clock() { return pin(0); } const Link& NetFF::pin_Clock() const { return pin(0); } Link& NetFF::pin_Enable() { return pin(1); } const Link& NetFF::pin_Enable() const { return pin(1); } Link& NetFF::pin_Aset() { return pin(2); } const Link& NetFF::pin_Aset() const { return pin(2); } Link& NetFF::pin_Aclr() { return pin(3); } const Link& NetFF::pin_Aclr() const { return pin(3); } Link& NetFF::pin_Sset() { return pin(4); } const Link& NetFF::pin_Sset() const { return pin(4); } Link& NetFF::pin_Sclr() { return pin(5); } const Link& NetFF::pin_Sclr() const { return pin(5); } Link& NetFF::pin_Data() { return pin(6); } const Link& NetFF::pin_Data() const { return pin(6); } Link& NetFF::pin_Q() { return pin(7); } const Link& NetFF::pin_Q() const { return pin(7); } void NetFF::aset_value(const verinum&val) { aset_value_ = val; } const verinum& NetFF::aset_value() const { return aset_value_; } void NetFF::sset_value(const verinum&val) { sset_value_ = val; } 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) { pin(0).set_dir(Link::OUTPUT); pin(1).set_dir(Link::INPUT); } NetAbs::~NetAbs() { } unsigned NetAbs::width() const { return width_; } /* * The NetAddSub class represents an LPM_ADD_SUB device. The pinout is * assigned like so: * 0 -- Cout * 1 -- DataA (normally a vector) * 2 -- DataB (normally a vector) * 3 -- Result (normally a vector) */ NetAddSub::NetAddSub(NetScope*s, perm_string n, unsigned w) : NetNode(s, n, 4), width_(w) { pin(0).set_dir(Link::OUTPUT); // Cout pin(1).set_dir(Link::INPUT); // DataA pin(2).set_dir(Link::INPUT); // DataB pin(3).set_dir(Link::OUTPUT); // Result } NetAddSub::~NetAddSub() { } unsigned NetAddSub::width()const { return width_; } Link& NetAddSub::pin_Cout() { return pin(0); } const Link& NetAddSub::pin_Cout() const { return pin(0); } Link& NetAddSub::pin_DataA() { return pin(1); } const Link& NetAddSub::pin_DataA() const { return pin(1); } Link& NetAddSub::pin_DataB() { return pin(2); } const Link& NetAddSub::pin_DataB() const { return pin(2); } Link& NetAddSub::pin_Result() { return pin(3); } const Link& NetAddSub::pin_Result() const { return pin(3); } NetArrayDq::NetArrayDq(NetScope*s, perm_string n, NetNet*mem__, unsigned awid) : NetNode(s, n, 2), mem_(mem__), awidth_(awid) { pin(0).set_dir(Link::OUTPUT); // Result pin(1).set_dir(Link::INPUT); // Address // Increment the expression reference count for the target // memory so that it is not deleted underneath me. mem_->incr_eref(); } NetArrayDq::~NetArrayDq() { } unsigned NetArrayDq::width() const { return mem_->vector_width(); } unsigned NetArrayDq::awidth() const { return awidth_; } const NetNet* NetArrayDq::mem() const { return mem_; } Link& NetArrayDq::pin_Result() { return pin(0); } Link& NetArrayDq::pin_Address() { return pin(1); } const Link& NetArrayDq::pin_Result() const { return pin(0); } const Link& NetArrayDq::pin_Address() const { return pin(1); } /* * The pinout for the NetCLShift is: * 0 -- Result * 1 -- Data * 2 -- Distance */ NetCLShift::NetCLShift(NetScope*s, perm_string n, unsigned width__, unsigned width_dist__, bool right_flag__, bool signed_flag__) : NetNode(s, n, 3), width_(width__), width_dist_(width_dist__), right_flag_(right_flag__), signed_flag_(signed_flag__) { pin(0).set_dir(Link::OUTPUT); // Result pin(1).set_dir(Link::INPUT); // Data pin(2).set_dir(Link::INPUT); // Distance } NetCLShift::~NetCLShift() { } unsigned NetCLShift::width() const { return width_; } unsigned NetCLShift::width_dist() const { return width_dist_; } bool NetCLShift::right_flag() const { return right_flag_; } bool NetCLShift::signed_flag() const { return signed_flag_; } Link& NetCLShift::pin_Data() { return pin(1); } const Link& NetCLShift::pin_Data() const { return pin(1); } Link& NetCLShift::pin_Result() { return pin(0); } const Link& NetCLShift::pin_Result() const { return pin(0); } Link& NetCLShift::pin_Distance() { return pin(2); } const Link& NetCLShift::pin_Distance() const { return pin(2); } NetCompare::NetCompare(NetScope*s, perm_string n, unsigned wi) : NetNode(s, n, 8), width_(wi) { signed_flag_ = false; pin(0).set_dir(Link::OUTPUT); // AGB pin(1).set_dir(Link::OUTPUT); // AGEB pin(2).set_dir(Link::OUTPUT); // AEB pin(3).set_dir(Link::OUTPUT); // ANEB pin(4).set_dir(Link::OUTPUT); // ALB pin(5).set_dir(Link::OUTPUT); // ALEB pin(6).set_dir(Link::INPUT); // DataA pin(7).set_dir(Link::INPUT); // DataB } NetCompare::~NetCompare() { } unsigned NetCompare::width() const { return width_; } bool NetCompare::get_signed() const { return signed_flag_; } void NetCompare::set_signed(bool flag) { signed_flag_ = flag; } Link& NetCompare::pin_AGB() { return pin(0); } const Link& NetCompare::pin_AGB() const { return pin(0); } Link& NetCompare::pin_AGEB() { return pin(1); } const Link& NetCompare::pin_AGEB() const { return pin(1); } Link& NetCompare::pin_AEB() { return pin(2); } const Link& NetCompare::pin_AEB() const { return pin(2); } Link& NetCompare::pin_ANEB() { return pin(3); } const Link& NetCompare::pin_ANEB() const { return pin(3); } Link& NetCompare::pin_ALB() { return pin(4); } const Link& NetCompare::pin_ALB() const { return pin(4); } Link& NetCompare::pin_ALEB() { return pin(5); } const Link& NetCompare::pin_ALEB() const { return pin(5); } Link& NetCompare::pin_DataA() { return pin(6); } const Link& NetCompare::pin_DataA() const { return pin(6); } Link& NetCompare::pin_DataB() { return pin(7); } const Link& NetCompare::pin_DataB() const { return pin(7); } NetDivide::NetDivide(NetScope*sc, perm_string n, unsigned wr, unsigned wa, unsigned wb) : NetNode(sc, n, 3), width_r_(wr), width_a_(wa), width_b_(wb), signed_flag_(false) { pin(0).set_dir(Link::OUTPUT); // Result pin(1).set_dir(Link::INPUT); // DataA pin(2).set_dir(Link::INPUT); // DataB } NetDivide::~NetDivide() { } unsigned NetDivide::width_r() const { return width_r_; } unsigned NetDivide::width_a() const { return width_a_; } unsigned NetDivide::width_b() const { return width_b_; } void NetDivide::set_signed(bool flag) { signed_flag_ = flag; } bool NetDivide::get_signed() const { return signed_flag_; } Link& NetDivide::pin_Result() { return pin(0); } const Link& NetDivide::pin_Result() const { return pin(0); } Link& NetDivide::pin_DataA() { return pin(1); } const Link& NetDivide::pin_DataA() const { return pin(1); } Link& NetDivide::pin_DataB() { return pin(2); } const Link& NetDivide::pin_DataB() const { return pin(2); } NetLiteral::NetLiteral(NetScope*sc, perm_string n, const verireal&val) : NetNode(sc, n, 1), real_(val) { pin(0).set_dir(Link::OUTPUT); } NetLiteral::~NetLiteral() { } ivl_variable_type_t NetLiteral::data_type() const { return IVL_VT_REAL; } const verireal& NetLiteral::value_real() const { return real_; } NetMult::NetMult(NetScope*sc, perm_string n, unsigned wr, unsigned wa, unsigned wb) : NetNode(sc, n, 3), signed_(false), width_r_(wr), width_a_(wa), width_b_(wb) { pin(0).set_dir(Link::OUTPUT); // Result pin(1).set_dir(Link::INPUT); // DataA pin(2).set_dir(Link::INPUT); // DataB } NetMult::~NetMult() { } void NetMult::set_signed(bool flag) { signed_ = flag; } bool NetMult::get_signed() const { return signed_; } unsigned NetMult::width_r() const { return width_r_; } unsigned NetMult::width_a() const { return width_a_; } unsigned NetMult::width_b() const { return width_b_; } Link& NetMult::pin_Result() { return pin(0); } const Link& NetMult::pin_Result() const { return pin(0); } Link& NetMult::pin_DataA() { return pin(1); } const Link& NetMult::pin_DataA() const { return pin(1); } Link& NetMult::pin_DataB() { return pin(2); } const Link& NetMult::pin_DataB() const { return pin(2); } NetPow::NetPow(NetScope*sc, perm_string n, unsigned wr, unsigned wa, unsigned wb) : NetNode(sc, n, 3), signed_(false), width_r_(wr), width_a_(wa), width_b_(wb) { pin(0).set_dir(Link::OUTPUT); // Result pin(1).set_dir(Link::INPUT); // DataA pin(2).set_dir(Link::INPUT); // DataB } NetPow::~NetPow() { } void NetPow::set_signed(bool flag) { signed_ = flag; } bool NetPow::get_signed() const { return signed_; } unsigned NetPow::width_r() const { return width_r_; } unsigned NetPow::width_a() const { return width_a_; } unsigned NetPow::width_b() const { return width_b_; } Link& NetPow::pin_Result() { return pin(0); } const Link& NetPow::pin_Result() const { return pin(0); } Link& NetPow::pin_DataA() { return pin(1); } const Link& NetPow::pin_DataA() const { return pin(1); } Link& NetPow::pin_DataB() { return pin(2); } const Link& NetPow::pin_DataB() const { return pin(2); } /* * The NetMux class represents an LPM_MUX device. The pinout is assigned * like so: * 0 -- Result * 1 -- Sel * 2+N -- Data[N] (N is the size of the mux) */ NetMux::NetMux(NetScope*s, perm_string n, unsigned wi, unsigned si, unsigned sw) : NetNode(s, n, 2+si), width_(wi), size_(si), swidth_(sw) { pin(0).set_dir(Link::OUTPUT); // Q pin(1).set_dir(Link::INPUT); // Sel for (unsigned idx = 0 ; idx < size_ ; idx += 1) { pin_Data(idx).set_dir(Link::INPUT); // Data[idx] } } NetMux::~NetMux() { } unsigned NetMux::width()const { return width_; } unsigned NetMux::size() const { return size_; } unsigned NetMux::sel_width() const { return swidth_; } Link& NetMux::pin_Result() { return pin(0); } const Link& NetMux::pin_Result() const { return pin(0); } Link& NetMux::pin_Sel() { return pin(1); } const Link& NetMux::pin_Sel() const { return pin(1); } Link& NetMux::pin_Data(unsigned s) { ivl_assert(*this, s < size_); return pin(2+s); } const Link& NetMux::pin_Data(unsigned s) const { ivl_assert(*this, s < size_); return pin(2+s); } NetSignExtend::NetSignExtend(NetScope*s, perm_string n, unsigned w) : NetNode(s, n, 2), width_(w) { pin(0).set_dir(Link::OUTPUT); pin(1).set_dir(Link::INPUT); } NetSignExtend::~NetSignExtend() { } unsigned NetSignExtend::width() const { return width_; } NetBUFZ::NetBUFZ(NetScope*s, perm_string n, unsigned w, bool trans) : NetNode(s, n, 2), width_(w), transparent_(trans) { pin(0).set_dir(Link::OUTPUT); pin(1).set_dir(Link::INPUT); } NetBUFZ::~NetBUFZ() { } unsigned NetBUFZ::width() const { return width_; } 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); pin(2).set_dir(Link::INPUT); } NetCaseCmp::~NetCaseCmp() { } unsigned NetCaseCmp::width() const { return width_; } NetCondit::NetCondit(NetExpr*ex, NetProc*i, NetProc*e) : expr_(ex), if_(i), else_(e) { } NetCondit::~NetCondit() { delete expr_; delete if_; delete else_; } const NetExpr* NetCondit::expr() const { return expr_; } NetExpr* NetCondit::expr() { return expr_; } void NetCondit::set_expr(NetExpr*ex) { delete expr_; expr_ = ex; } NetProc* NetCondit::if_clause() { return if_; } NetProc* NetCondit::else_clause() { return else_; } NetConst::NetConst(NetScope*s, perm_string n, verinum::V v) : NetNode(s, n, 1), value_(v, 1) { pin(0).set_dir(Link::OUTPUT); } NetConst::NetConst(NetScope*s, perm_string n, const verinum&val) : NetNode(s, n, 1), value_(val) { pin(0).set_dir(Link::OUTPUT); } NetConst::~NetConst() { } verinum::V NetConst::value(unsigned idx) const { ivl_assert(*this, idx < width()); return value_[idx]; } NetBaseDef::NetBaseDef(NetScope*s, const vector&po, const std::vector&pd) : scope_(s), ports_(po), pdefaults_(pd) { proc_ = 0; } NetBaseDef::~NetBaseDef() { } const NetScope* NetBaseDef::scope() const { return scope_; } NetScope*NetBaseDef::scope() { return scope_; } unsigned NetBaseDef::port_count() const { return ports_.size(); } NetNet* NetBaseDef::port(unsigned idx) const { assert(idx < ports_.size()); return ports_[idx]; } NetExpr* NetBaseDef::port_defe(unsigned idx) const { assert(idx < pdefaults_.size()); return pdefaults_[idx]; } void NetBaseDef::set_proc(NetProc*st) { assert(proc_ == 0); assert(st != 0); proc_ = st; } const NetProc* NetBaseDef::proc() const { return proc_; } NetFuncDef::NetFuncDef(NetScope*s, NetNet*result, const vector&po, const vector&pd) : NetBaseDef(s, po, pd), result_sig_(result) { } NetFuncDef::~NetFuncDef() { } const NetNet* NetFuncDef::return_sig() const { return result_sig_; } NetSTask::NetSTask(const char*na, ivl_sfunc_as_task_t sfat, const vector&pa) : name_(0), sfunc_as_task_(sfat), parms_(pa) { name_ = lex_strings.add(na); ivl_assert(*this, name_[0] == '$'); } NetSTask::~NetSTask() { for (unsigned idx = 0 ; idx < parms_.size() ; idx += 1) delete parms_[idx]; /* The name_ string is perm-allocated in lex_strings. */ } const char*NetSTask::name() const { return name_; } ivl_sfunc_as_task_t NetSTask::sfunc_as_task() const { return sfunc_as_task_; } unsigned NetSTask::nparms() const { return parms_.size(); } const NetExpr* NetSTask::parm(unsigned idx) const { return parms_[idx]; } NetEUFunc::NetEUFunc(NetScope*scope, NetScope*def, NetESignal*res, vector&p, bool nc) : NetExpr(res->net_type()), scope_(scope), func_(def), result_sig_(res), parms_(p), need_const_(nc) { } NetEUFunc::~NetEUFunc() { for (unsigned idx = 0 ; idx < parms_.size() ; idx += 1) delete parms_[idx]; } #if 0 const string NetEUFunc::name() const { return func_->name(); } #endif const NetESignal*NetEUFunc::result_sig() const { return result_sig_; } unsigned NetEUFunc::parm_count() const { return parms_.size(); } const NetExpr* NetEUFunc::parm(unsigned idx) const { ivl_assert(*this, idx < parms_.size()); return parms_[idx]; } const NetScope* NetEUFunc::func() const { return func_; } NetUTask::NetUTask(NetScope*def) : task_(def) { } NetUTask::~NetUTask() { } const NetScope* NetUTask::task() const { return task_; } NetAlloc::NetAlloc(NetScope*scope__) : scope_(scope__) { } NetAlloc::~NetAlloc() { } #if 0 const string NetAlloc::name() const { return scope_->name(); } #endif const NetScope* NetAlloc::scope() const { return scope_; } NetFree::NetFree(NetScope*scope__) : scope_(scope__) { } NetFree::~NetFree() { } #if 0 const string NetFree::name() const { return scope_->name(); } #endif const NetScope* NetFree::scope() const { return scope_; } /* * Create a bitwise operator node from the opcode and the left and * right expressions. */ NetEBBits::NetEBBits(char op__, NetExpr*l, NetExpr*r, unsigned wid, bool signed_flag) : NetEBinary(op__, l, r, wid, signed_flag) { } NetEBBits::~NetEBBits() { } ivl_variable_type_t NetEBBits::expr_type() const { if (left_->expr_type() == IVL_VT_LOGIC || right_->expr_type() == IVL_VT_LOGIC) return IVL_VT_LOGIC; return IVL_VT_BOOL; } NetEBinary::NetEBinary(char op__, NetExpr*l, NetExpr*r, unsigned wid, bool signed_flag) : op_(op__), left_(l), right_(r) { expr_width(wid); cast_signed_base_(signed_flag); } NetEBinary::~NetEBinary() { delete left_; delete right_; } bool NetEBinary::has_width() const { return left_->has_width() && right_->has_width(); } NetEBLogic::NetEBLogic(char op__, NetExpr*l, NetExpr*r) : NetEBinary(op__, l, r, 1, false) { } NetEBLogic::~NetEBLogic() { } ivl_variable_type_t NetEBLogic::expr_type() const { if (left_->expr_type() == IVL_VT_LOGIC || right_->expr_type() == IVL_VT_LOGIC) return IVL_VT_LOGIC; return IVL_VT_BOOL; } NetEConst::NetEConst(const verinum&val) : NetExpr(val.len()), value_(val) { cast_signed_base_(value_.has_sign()); } NetEConst::NetEConst(ivl_type_t type, const verinum&val) : NetExpr(type), value_(val) { ivl_assert(*this, type->packed()); ivl_assert(*this, type->packed_width() == val.len()); ivl_assert(*this, type->get_signed() == val.has_sign()); } NetEConst::~NetEConst() { } void NetEConst::cast_signed(bool flag) { cast_signed_base_(flag); value_.has_sign(flag); } const verinum& NetEConst::value() const { return value_; } bool NetEConst::has_width() const { return value_.has_len(); } ivl_variable_type_t NetEConst::expr_type() const { if (value_.len() == 0) return IVL_VT_LOGIC; if (value_.is_string()) return IVL_VT_BOOL; if (value_.is_defined()) return IVL_VT_BOOL; return IVL_VT_LOGIC; } void NetEConst::trim() { if (value_.is_string()) return; value_.has_len(false); value_ = trim_vnum(value_); expr_width(value_.len()); } NetEConstParam::NetEConstParam(const NetScope*s, perm_string n, const verinum&v) : NetEConst(v), scope_(s), name_(n) { cast_signed_base_(v.has_sign()); } NetEConstParam::~NetEConstParam() { } perm_string NetEConstParam::name() const { return name_; } const NetScope* NetEConstParam::scope() const { return scope_; } NetEEvent::NetEEvent(NetEvent*e) : event_(e) { e->exprref_ += 1; } NetEEvent::~NetEEvent() { } const NetEvent* NetEEvent::event() const { return event_; } NetEScope::NetEScope(NetScope*s) : scope_(s) { } NetEScope::~NetEScope() { } const NetScope* NetEScope::scope() const { return scope_; } NetESignal::NetESignal(NetNet*n) : NetExpr(n->net_type()), net_(n), word_(0) { net_->incr_eref(); set_line(*n); } NetESignal::NetESignal(NetNet*n, NetExpr*w) : NetExpr(n->vector_width()), net_(n), word_(w) { net_->incr_eref(); set_line(*n); // If it is an array we don't have a type for it yet. But for array // elements the NetNet returns the element type. if (word_) set_net_type(net_->net_type()); } NetESignal::~NetESignal() { net_->decr_eref(); } perm_string NetESignal::name() const { return net_->name(); } const NetExpr* NetESignal::word_index() const { return word_; } unsigned NetESignal::vector_width() const { return net_->vector_width(); } const NetNet* NetESignal::sig() const { return net_; } NetNet* NetESignal::sig() { return net_; } /* * The lsi() and msi() methods should be removed from the NetESignal * class, to be replaced with packed dimensions aware methods of * getting at dimensions. */ long NetESignal::lsi() const { const vector&packed = net_->packed_dims(); ivl_assert(*this, packed.size() == 1); return packed.back().get_lsb(); } long NetESignal::msi() const { const vector&packed = net_->packed_dims(); ivl_assert(*this, packed.size() == 1); return packed.back().get_msb(); } ivl_variable_type_t NetESignal::expr_type() const { if (net_->darray_type()) return IVL_VT_DARRAY; else return net_->data_type(); } /* * Make a ternary operator from all the sub-expressions. The condition * expression is self-determined, but the true and false expressions * should have the same width. NOTE: This matching of the widths really * has to be done in elaboration. */ NetETernary::NetETernary(NetExpr*c, NetExpr*t, NetExpr*f, unsigned wid, bool signed_flag) : cond_(c), true_val_(t), false_val_(f) { expr_width(wid); cast_signed_base_(signed_flag); } NetETernary::~NetETernary() { delete cond_; delete true_val_; delete false_val_; } const netenum_t* NetETernary::enumeration() const { // If the condition can evaluate to an ambiguous value, // the result may be blended, and so is not guaranteed // to be a valid enumeration value. if (cond_->expr_type() != IVL_VT_BOOL) return 0; if (true_val_->enumeration() != false_val_->enumeration()) return 0; return true_val_->enumeration(); } const NetExpr* NetETernary::cond_expr() const { return cond_; } const NetExpr* NetETernary::true_expr() const { return true_val_; } const NetExpr* NetETernary::false_expr() const { return false_val_; } ivl_variable_type_t NetETernary::expr_type() const { ivl_assert(*this, true_val_); ivl_assert(*this, false_val_); ivl_variable_type_t tru = true_val_->expr_type(); ivl_variable_type_t fal = false_val_->expr_type(); ivl_variable_type_t sel = cond_->expr_type(); if (tru == IVL_VT_LOGIC && fal == IVL_VT_BOOL) return IVL_VT_LOGIC; if (tru == IVL_VT_BOOL && fal == IVL_VT_LOGIC) return IVL_VT_LOGIC; if (sel == IVL_VT_LOGIC && (tru == IVL_VT_LOGIC || tru == IVL_VT_BOOL) && (fal == IVL_VT_LOGIC || fal == IVL_VT_BOOL)) return IVL_VT_LOGIC; if (tru == IVL_VT_REAL && (fal == IVL_VT_LOGIC || fal == IVL_VT_BOOL)) return IVL_VT_REAL; if (fal == IVL_VT_REAL && (tru == IVL_VT_LOGIC || tru == IVL_VT_BOOL)) return IVL_VT_REAL; if (tru != fal) { cerr << get_fileline() << ": internal error:" << " Unexpected ?: type clash:" << " tru=" << tru << ", fal=" << fal << endl; } ivl_assert(*this, tru == fal); return tru; } NetEUnary::NetEUnary(char op__, NetExpr*ex, unsigned wid, bool signed_flag) : NetExpr(wid), op_(op__), expr_(ex) { cast_signed_base_(signed_flag); } NetEUnary::~NetEUnary() { delete expr_; } ivl_variable_type_t NetEUnary::expr_type() const { return expr_->expr_type(); } NetEUBits::NetEUBits(char op__, NetExpr*ex, unsigned wid, bool signed_flag) : NetEUnary(op__, ex, wid, signed_flag) { } NetEUBits::~NetEUBits() { } ivl_variable_type_t NetEUBits::expr_type() const { return expr_->expr_type(); } NetEUReduce::NetEUReduce(char op__, NetExpr*ex) : NetEUnary(op__, ex, 1, false) { } NetEUReduce::~NetEUReduce() { } ivl_variable_type_t NetEUReduce::expr_type() const { return expr_->expr_type(); } NetECast::NetECast(char op__, NetExpr*ex, unsigned wid, bool signed_flag) : NetEUnary(op__, ex, wid, signed_flag) { } NetECast::~NetECast() { } ivl_variable_type_t NetECast::expr_type() const { ivl_variable_type_t ret = IVL_VT_NO_TYPE; switch (op_) { case 'v': ret = IVL_VT_LOGIC; break; case 'r': ret = IVL_VT_REAL; break; case '2': ret = IVL_VT_BOOL; break; default: ivl_assert(*this, 0); } return ret; } NetLogic::NetLogic(NetScope*s, perm_string n, unsigned pins, TYPE t, unsigned wid, bool is_cassign__) : NetNode(s, n, pins), type_(t), width_(wid), is_cassign_(is_cassign__) { pin(0).set_dir(Link::OUTPUT); for (unsigned idx = 1 ; idx < pins ; idx += 1) { pin(idx).set_dir(Link::INPUT); } } NetLogic::TYPE NetLogic::type() const { return type_; } unsigned NetLogic::width() const { return width_; } bool NetLogic::is_cassign() const { return is_cassign_; } NetUReduce::NetUReduce(NetScope*scope__, perm_string n, NetUReduce::TYPE t, unsigned wid) : NetNode(scope__, n, 2), type_(t), width_(wid) { pin(0).set_dir(Link::OUTPUT); pin(1).set_dir(Link::INPUT); } NetUReduce::TYPE NetUReduce::type() const { return type_; } unsigned NetUReduce::width() const { return width_; } NetTaskDef::NetTaskDef(NetScope*n, const vector&po, const vector&pd) : NetBaseDef(n, po, pd) { } NetTaskDef::~NetTaskDef() { delete proc_; } /* * These are the delay_type() functions. They are used to determine * the type of delay for the given object. */ /* * This function implements the following table: * * in_A in_B out * NO NO NO * NO ZERO ZERO * NO POS POS * NO DEF POS * ZERO NO ZERO * ZERO ZERO ZERO * ZERO POS POS * ZERO DEF POS * POS NO POS * POS ZERO POS * POS POS POS * POS DEF POS * DEF NO POS * DEF ZERO POS * DEF POS POS * DEF DEF DEF * * It is used to combine two delay values. */ static DelayType combine_delays(const DelayType a, const DelayType b) { /* The default is POSSIBLE_DELAY. */ DelayType result = POSSIBLE_DELAY; /* If both are no or zero delay then we return ZERO_DELAY. */ if ((a == NO_DELAY || a == ZERO_DELAY) && (b == NO_DELAY || b == ZERO_DELAY)) { result = ZERO_DELAY; } /* Except if both are no delay then we return NO_DELAY. */ if (a == NO_DELAY && b == NO_DELAY) { result = NO_DELAY; } /* If both are definite delay then we return DEFINITE_DELAY. */ if (a == DEFINITE_DELAY && b == DEFINITE_DELAY) { result = DEFINITE_DELAY; } return result; } /* * This is used to see what we can find out about the delay when it * is given as an expression. We also use this for loop expressions. */ static DelayType delay_type_from_expr(const NetExpr*expr) { DelayType result = POSSIBLE_DELAY; if (const NetEConst*e = dynamic_cast(expr)) { if (e->value().is_zero()) result = ZERO_DELAY; else result = DEFINITE_DELAY; } if (const NetECReal*e = dynamic_cast(expr)) { if (e->value().as_double() == 0.0) result = ZERO_DELAY; else result = DEFINITE_DELAY; } return result; } /* * The looping structures can use the same basic code so put it here * instead of duplicating it for each one (repeat and while). */ static DelayType get_loop_delay_type(const NetExpr*expr, const NetProc*proc, bool print_delay) { DelayType result; switch (delay_type_from_expr(expr)) { /* We have a constant false expression so the body never runs. */ case ZERO_DELAY: result = NO_DELAY; break; /* We have a constant true expression so the body always runs. */ case DEFINITE_DELAY: if (proc) { result = proc->delay_type(print_delay); } else { result = NO_DELAY; } break; /* We don't know if the body will run so reduce a DEFINITE_DELAY * to a POSSIBLE_DELAY. All other stay the same. */ case POSSIBLE_DELAY: if (proc) { result = combine_delays(NO_DELAY, proc->delay_type(print_delay)); } else { result = NO_DELAY; } break; /* This should never happen since delay_type_from_expr() only * returns three different values. */ default: result = NO_DELAY; ivl_assert(*expr, 0); } return result; } /* The default object does not have any delay. */ DelayType NetProc::delay_type(bool /* print_delay */ ) const { return NO_DELAY; } DelayType NetBlock::delay_type(bool print_delay) const { // A join_none has no delay. if (type() == PARA_JOIN_NONE) return NO_DELAY; DelayType result; // A join_any has the minimum delay. if (type() == PARA_JOIN_ANY) { result = DEFINITE_DELAY; for (const NetProc*cur = proc_first(); cur; cur = proc_next(cur)) { DelayType dt = cur->delay_type(print_delay); if (dt < result) result = dt; if ((dt == NO_DELAY) && !print_delay) break; } // A begin or join has the maximum delay. } else { result = NO_DELAY; for (const NetProc*cur = proc_first(); cur; cur = proc_next(cur)) { DelayType dt = cur->delay_type(print_delay); if (dt > result) result = dt; if ((dt == DEFINITE_DELAY) && !print_delay) break; } } return result; } DelayType NetCase::delay_type(bool print_delay) const { DelayType result = NO_DELAY; bool def_stmt = false; unsigned nstmts = nitems(); for (unsigned idx = 0; idx < nstmts; idx += 1) { if (!expr(idx)) def_stmt = true; DelayType dt = stat(idx) ? stat(idx)->delay_type(print_delay) : NO_DELAY; if (idx == 0) { result = dt; } else { result = combine_delays(result, dt); } } // FIXME: If all the cases are covered (e.g. an enum) then this is not true. /* If we don't have a default statement we don't know for sure * that we have a delay. */ if (!def_stmt) result = combine_delays(NO_DELAY, result); return result; } DelayType NetCondit::delay_type(bool print_delay) const { DelayType if_type = if_ ? if_->delay_type(print_delay) : NO_DELAY; DelayType el_type = else_? else_->delay_type(print_delay) : NO_DELAY; return combine_delays(if_type, el_type); } /* * A do/while will execute the body at least once. */ DelayType NetDoWhile::delay_type(bool print_delay) const { if (proc_) return proc_->delay_type(print_delay); return ZERO_DELAY; } DelayType NetEvWait::delay_type(bool print_delay) const { if (print_delay) { cerr << get_fileline() << ": error: an event control is not allowed " "in an always_comb, always_ff or always_latch process." << endl; } return DEFINITE_DELAY; } DelayType NetForever::delay_type(bool print_delay) const { if (statement_) return statement_->delay_type(print_delay); return ZERO_DELAY; } DelayType NetForLoop::delay_type(bool print_delay) const { return get_loop_delay_type(condition_, statement_, print_delay); } DelayType NetPDelay::delay_type(bool print_delay) const { if (print_delay) { cerr << get_fileline() << ": error: a blocking delay is not allowed " "in an always_comb, always_ff or always_latch process." << endl; } if (expr_) { if (statement_) { return combine_delays(delay_type_from_expr(expr_), statement_->delay_type(print_delay)); } else { return delay_type_from_expr(expr_); } } if (delay() > 0) return DEFINITE_DELAY; if (statement_) { return combine_delays(ZERO_DELAY, statement_->delay_type(print_delay)); } else { return ZERO_DELAY; } } DelayType NetRepeat::delay_type(bool print_delay) const { return get_loop_delay_type(expr_, statement_, print_delay); } DelayType NetTaskDef::delay_type(bool print_delay) const { if (proc_) { return proc_->delay_type(print_delay); } else { return NO_DELAY; } } DelayType NetUTask::delay_type(bool print_delay) const { // Is this a void function call in a final block? if (task()->type() == NetScope::FUNC) { return NO_DELAY; } else { return task()->task_def()->delay_type(print_delay); } } static bool do_expr_event_match(const NetExpr*expr, const NetEvWait*evwt) { // The event wait should only have a single event. if (evwt->nevents() != 1) return false; // The event should have a single probe. const NetEvent *evt = evwt->event(0); if (evt->nprobe() != 1) return false; // The probe should be for any edge. const NetEvProbe *prb = evt->probe(0); if (prb->edge() != NetEvProbe::ANYEDGE) return false; // Create a NexusSet from the event probe signals. NexusSet *ns_evwt = new NexusSet; for (unsigned idx =0; idx < prb->pin_count(); idx += 1) { if (! prb->pin(idx).is_linked()) { delete ns_evwt; return false; } // Casting away const is safe since this nexus set is only being read. ns_evwt->add(const_cast (prb->pin(idx).nexus()), 0, prb->pin(idx).nexus()->vector_width()); } // Get the NexusSet for the expression. NexusSet *ns_expr = expr->nex_input(); // Make sure the event and expression NexusSets match exactly. if (ns_evwt->size() != ns_expr->size()) { delete ns_evwt; delete ns_expr; return false; } ns_expr->rem(*ns_evwt); delete ns_evwt; if (ns_expr->size() != 0) { delete ns_expr; return false; } delete ns_expr; return true; } static bool while_is_wait(const NetExpr*expr, const NetProc*stmt) { if (const NetEvWait*evwt = dynamic_cast(stmt)) { if (evwt->statement()) return false; const NetEBComp*cond = dynamic_cast(expr); if (! cond) return false; if (cond->op() != 'N') return false; const NetEConst*cval = dynamic_cast(cond->right()); if (! cval) return false; const verinum val = cval->value(); if (val.len() != 1) return false; if (val.get(0) != verinum::V1) return false; if (! do_expr_event_match(cond->left(), evwt)) return false; if (evwt->get_lineno() != cond->get_lineno()) return false; if (evwt->get_file() != cond->get_file()) return false; return true; } return false; } DelayType NetWhile::delay_type(bool print_delay) const { // If the wait was a constant value the compiler already removed it // so we know we can only have a possible delay. if (while_is_wait(cond_, proc_)) { if (print_delay) { cerr << get_fileline() << ": error: a wait statement is " "not allowed in an " "always_comb, always_ff or always_latch process." << endl; } return POSSIBLE_DELAY; } return get_loop_delay_type(cond_, proc_, print_delay); } /* * These are the check_synth() functions. They are used to print * a warning if the item is not synthesizable. */ static const char * get_process_type_as_string(ivl_process_type_t pr_type) { switch (pr_type) { case IVL_PR_ALWAYS_COMB: return "in an always_comb process."; break; case IVL_PR_ALWAYS_FF: return "in an always_ff process."; break; case IVL_PR_ALWAYS_LATCH: return "in an always_latch process."; break; default: assert(0); return 0; } } static void print_synth_warning(const NetProc *net_proc, const char *name, ivl_process_type_t pr_type) { cerr << net_proc->get_fileline() << ": warning: " << name << " statement cannot be synthesized " << get_process_type_as_string(pr_type) << endl; } static void check_if_logic_l_value(const NetAssignBase *base, ivl_process_type_t pr_type) { if (base->l_val_count() != 1) return; const NetAssign_*lval = base->l_val(0); if (! lval) return; NetNet*sig = lval->sig(); if (! sig) return; if ((sig->data_type() != IVL_VT_BOOL) && (sig->data_type() != IVL_VT_LOGIC)) { cerr << base->get_fileline() << ": warning: Assinging to a " "non-integral variable ("<< sig->name() << ") cannot be synthesized " << get_process_type_as_string(pr_type) << endl; } } /* By default elements can be synthesized or ignored. */ bool NetProc::check_synth(ivl_process_type_t /* pr_type */, const NetScope* /* scope */ ) const { return false; } // FIXME: User function calls still need to be checked (NetEUFunc). // : Non-constant system functions need a warning (NetESFunc). // : Constant functions should already be elaborated. /* By default assign elements can be synthesized. */ bool NetAssignBase::check_synth(ivl_process_type_t /* pr_type */, const NetScope* /* scope */ ) const { return false; } bool NetAssign::check_synth(ivl_process_type_t pr_type, const NetScope* /* scope */ ) const { check_if_logic_l_value(this, pr_type); // FIXME: Check that ff/latch only use this for internal signals. return false; } bool NetAssignNB::check_synth(ivl_process_type_t pr_type, const NetScope* /* scope */ ) const { bool result = false; if (pr_type == IVL_PR_ALWAYS_COMB) { cerr << get_fileline() << ": warning: A non-blocking assignment " "should not be used in an always_comb process." << endl; } if (event_) { cerr << get_fileline() << ": error: A non-blocking assignment " "cannot be synthesized with an event control " << get_process_type_as_string(pr_type) << endl; result = true; } check_if_logic_l_value(this, pr_type); return result; } bool NetBlock::check_synth(ivl_process_type_t pr_type, const NetScope* scope) const { bool result = false; // Only a begin/end can be synthesized. if (type() != SEQU) { cerr << get_fileline() << ": error: A fork/"; switch (type()) { case PARA: cerr << "join"; break; case PARA_JOIN_ANY: cerr << "join_any"; break; case PARA_JOIN_NONE: cerr << "join_none"; break; default: ivl_assert(*this, 0); } cerr << " statement cannot be synthesized " << get_process_type_as_string(pr_type) << endl; result = true; } const NetScope*save_scope = scope; if (subscope()) scope = subscope(); if (scope != save_scope) { result |= scope->check_synth(pr_type, scope); } for (const NetProc*cur = proc_first(); cur; cur = proc_next(cur)) { result |= cur->check_synth(pr_type, scope); } return result; } bool NetCase::check_synth(ivl_process_type_t pr_type, const NetScope* scope) const { bool result = false; for (unsigned idx = 0; idx < nitems(); idx += 1) { if (stat(idx)) result |= stat(idx)->check_synth(pr_type, scope); } // FIXME: Check for ff/latch/comb structures. return result; } bool NetCAssign::check_synth(ivl_process_type_t pr_type, const NetScope* /* scope */ ) const { print_synth_warning(this, "A procedural assign", pr_type); return false; } bool NetCondit::check_synth(ivl_process_type_t pr_type, const NetScope* scope) const { bool result = false; if (if_) result |= if_->check_synth(pr_type, scope); if (else_) result |= else_->check_synth(pr_type, scope); // FIXME: Check for ff/latch/comb structures. return result; } bool NetDeassign::check_synth(ivl_process_type_t pr_type, const NetScope* /* scope */ ) const { print_synth_warning(this, "A procedural deassign", pr_type); return false; } bool NetDisable::check_synth(ivl_process_type_t pr_type, const NetScope* scope) const { while (scope) { if (scope != target_) scope = scope->parent(); else break; } if (! scope) { cerr << get_fileline() << ": warning: A disable statement can " "only be synthesized when disabling an enclosing block " << get_process_type_as_string(pr_type) << endl; } return false; } bool NetDoWhile::check_synth(ivl_process_type_t pr_type, const NetScope* scope) const { bool result = false; print_synth_warning(this, "A do/while", pr_type); if (proc_) result |= proc_->check_synth(pr_type, scope); return result; } bool NetEvTrig::check_synth(ivl_process_type_t pr_type, const NetScope* /* scope */ ) const { print_synth_warning(this, "An event trigger", pr_type); return false; } bool NetEvNBTrig::check_synth(ivl_process_type_t pr_type, const NetScope* /* scope */ ) const { print_synth_warning(this, "A non-blocking event trigger", pr_type); return false; } // The delay check above has already marked this as an error. bool NetEvWait::check_synth(ivl_process_type_t pr_type, const NetScope* scope) const { bool result = false; if (statement_) result |= statement_->check_synth(pr_type, scope); return result; } bool NetForce::check_synth(ivl_process_type_t pr_type, const NetScope* /* scope */ ) const { print_synth_warning(this, "A force", pr_type); return false; } bool NetForever::check_synth(ivl_process_type_t pr_type, const NetScope* scope) const { bool result = false; print_synth_warning(this, "A forever", pr_type); if (statement_) result |= statement_->check_synth(pr_type, scope); return result; } /* * A bunch of private routines to verify that a for loop has the correct * structure for synthesis. */ static void print_for_idx_warning(const NetProc*proc, const char*check, ivl_process_type_t pr_type, NetNet*idx) { cerr << proc->get_fileline() << ": warning: A for statement must use " "the index (" << idx->name() << ") in the " << check << " expression to be synthesized " << get_process_type_as_string(pr_type) << endl; } static void check_for_const_synth(const NetExpr*expr, const NetProc*proc, const char*str, ivl_process_type_t pr_type) { if (! dynamic_cast(expr)) { cerr << proc-> get_fileline() << ": warning: A for " "statement must " << str << " value to be synthesized " << get_process_type_as_string(pr_type) << endl; } } static void check_for_bin_synth(const NetExpr*left,const NetExpr*right, const char*str, const char*check, const NetProc*proc, ivl_process_type_t pr_type, NetNet*index) { const NetESignal*lsig = dynamic_cast(left); const NetESignal*rsig = dynamic_cast(right); if (lsig && (lsig->sig() == index)) { check_for_const_synth(right, proc, str, pr_type); } else if (rsig && (rsig->sig() == index)) { check_for_const_synth(left, proc, str, pr_type); } else { print_for_idx_warning(proc, check, pr_type, index); } } static void print_for_step_warning(const NetProc*proc, ivl_process_type_t pr_type) { cerr << proc->get_fileline() << ": warning: A for statement step must " "be a simple assignment statement to be synthesized " << get_process_type_as_string(pr_type) << endl; } static void print_for_step_warning(const NetProc*proc, ivl_process_type_t pr_type, NetNet*idx) { cerr << proc->get_fileline() << ": warning: A for statement step must " "be an assignment to the index variable (" << idx->name() << ") to be synthesized " << get_process_type_as_string(pr_type) << endl; } static void check_for_bstep_synth(const NetExpr*expr, const NetProc*proc, ivl_process_type_t pr_type, NetNet*index) { if (const NetECast*tmp = dynamic_cast(expr)) { expr = tmp->expr(); } if (const NetEBAdd*tmp = dynamic_cast(expr)) { check_for_bin_synth(tmp->left(), tmp->right(), "change by a constant", "step", proc, pr_type, index); } else { cerr << proc->get_fileline() << ": warning: A for statement " "step must be a simple binary +/- " "to be synthesized " << get_process_type_as_string(pr_type) << endl; } } static void check_for_step_synth(const NetAssign*assign, const NetProc*proc, ivl_process_type_t pr_type, NetNet*index) { if (assign->l_val_count() != 1) { print_for_step_warning(proc, pr_type); } else if (assign->l_val(0)->sig() != index) { print_for_step_warning(proc, pr_type, index); } else { switch (assign->assign_operator()) { case '+': case '-': check_for_const_synth(assign->rval(), proc, "have a constant step", pr_type); break; case 0: check_for_bstep_synth(assign->rval(), proc, pr_type, index); break; default: cerr << proc->get_fileline() << ": warning: A for statement " "step does not support operator '" << assign->assign_operator() << "' it must be +/- to be synthesized " << get_process_type_as_string(pr_type) << endl; break; } } } bool NetForLoop::check_synth(ivl_process_type_t pr_type, const NetScope* scope) const { bool result = false; // FIXME: What about an enum (NetEConstEnum)? if (! dynamic_cast(init_expr_)) { cerr << get_fileline() << ": warning: A for statement must " "have a constant initial value to be synthesized " << get_process_type_as_string(pr_type) << endl; } // FIXME: Do the following also need to be supported in the condition? // It would seem like they are hard to use to find the bounds. // From NetEBinary // What about NetEBits sig & constant, etc. // From NetEUnary // What about NetEUBits ! sig or ! (sig == constat) // What about NetEUReduce &signal if (const NetESignal*tmp = dynamic_cast(condition_)) { if (tmp->sig() != index_) { print_for_idx_warning(this, "condition", pr_type, index_); } } else if (const NetEBComp*cmp = dynamic_cast(condition_)) { check_for_bin_synth(cmp->left(), cmp->right(), "compare against a constant", "condition", this, pr_type, index_); } else { print_for_idx_warning(this, "condition", pr_type, index_); } if (const NetAssign*tmp = dynamic_cast(step_statement_)) { check_for_step_synth(tmp, this, pr_type, index_); } else { print_for_step_warning(this, pr_type); } if (statement_) result |= statement_->check_synth(pr_type, scope); return result; } // The delay check above has already marked this as an error. bool NetPDelay::check_synth(ivl_process_type_t /* pr_type */, const NetScope* /* scope */ ) const { return false; } bool NetRelease::check_synth(ivl_process_type_t pr_type, const NetScope* /* scope */ ) const { print_synth_warning(this, "A release", pr_type); return false; } bool NetRepeat::check_synth(ivl_process_type_t pr_type, const NetScope* scope) const { bool result = false; print_synth_warning(this, "A repeat", pr_type); if (statement_) result |= statement_->check_synth(pr_type, scope); return result; } bool NetScope::check_synth(ivl_process_type_t pr_type, const NetScope* /* scope */) const { bool result = false; // Skip local events/signals for (NetEvent*cur = events_ ; cur ; cur = cur->snext_) { if (cur->local_flag()) continue; cerr << cur->get_fileline() << ": warning: An event (" << cur->name() << ") cannot be synthesized " << get_process_type_as_string(pr_type) << endl; } for (signals_map_iter_t cur = signals_map_.begin(); cur != signals_map_.end() ; ++ cur) { const NetNet*sig = cur->second; if ((sig->data_type() != IVL_VT_BOOL) && (sig->data_type() != IVL_VT_LOGIC)) { cerr << sig->get_fileline() << ": warning: A non-integral " "variable (" << sig->name() << ") cannot be " "synthesized " << get_process_type_as_string(pr_type) << endl; } } return result; } bool NetSTask::check_synth(ivl_process_type_t pr_type, const NetScope* /* scope */) const { if (strcmp(name(), "$ivl_darray_method$delete") == 0) { cerr << get_fileline() << ": warning: Dynamic array " "delete method cannot be synthesized " << get_process_type_as_string(pr_type) << endl; } else { cerr << get_fileline() << ": warning: System task (" << name() << ") cannot be synthesized " << get_process_type_as_string(pr_type) << endl; } return false; } /* * This function is called to make sure the task/function can be used * in a context where it must be synthesizable, such as in an always_comb * or always_ff. * * If this is a function, then the function must be void. */ bool NetBaseDef::check_synth(ivl_process_type_t pr_type, const NetScope* /* scope */) const { bool result = false; const NetScope *tscope = this->scope(); result |= tscope->check_synth(pr_type, tscope); if (! tscope->is_auto()) { cerr << tscope->get_def_file() << ":" << tscope->get_def_lineno() << ": warning: user task (" << tscope->basename() << ") must be automatic to be synthesized " << get_process_type_as_string(pr_type) << endl; } if (proc_) result |= proc_->check_synth(pr_type, tscope); return result; } bool NetUTask::check_synth(ivl_process_type_t pr_type, const NetScope* scope) const { const NetScope* task_scope = task(); if (task_scope->type() == NetScope::FUNC) { // This can happen if this a void function. return task_scope->func_def()->check_synth(pr_type, scope); } else { return task_scope->task_def()->check_synth(pr_type, scope); } } bool NetWhile::check_synth(ivl_process_type_t pr_type, const NetScope* scope) const { bool result = false; // A wait is already maked as an error in the delay check above. if (! while_is_wait(cond_, proc_)) { print_synth_warning(this, "A while", pr_type); if (proc_) result |= proc_->check_synth(pr_type, scope); } return result; }