diff --git a/backends/aiger/aiger.cc b/backends/aiger/aiger.cc index 0c49e84b8..510cfc6c6 100644 --- a/backends/aiger/aiger.cc +++ b/backends/aiger/aiger.cc @@ -337,7 +337,7 @@ struct AigerWriter continue; } - if (cell->type == ID($scopeinfo)) + if (cell->type == ID($scopeinfo) || cell->type == ID($input_port)) continue; log_error("Unsupported cell type: %s (%s)\n", log_id(cell->type), log_id(cell)); diff --git a/backends/cxxrtl/cxxrtl_backend.cc b/backends/cxxrtl/cxxrtl_backend.cc index ab5576e43..156a669d5 100644 --- a/backends/cxxrtl/cxxrtl_backend.cc +++ b/backends/cxxrtl/cxxrtl_backend.cc @@ -1518,6 +1518,7 @@ struct CxxrtlWorker { f << (cell->getParam(ID::CLR_POLARITY).as_bool() ? "" : ".bit_not()") << ");\n"; } // Internal cells + } else if (cell->type == ID($input_port)) { } else if (is_internal_cell(cell->type)) { log_cmd_error("Unsupported internal cell `%s'.\n", cell->type); // User cells diff --git a/frontends/aiger/aigerparse.cc b/frontends/aiger/aigerparse.cc index 9931ef78f..eb7f5e26e 100644 --- a/frontends/aiger/aigerparse.cc +++ b/frontends/aiger/aigerparse.cc @@ -1049,6 +1049,9 @@ struct AigerFrontend : public Frontend { } extra_args(f, filename, args, argidx, true); + // TODO Disabled signorm because swap_names breaks fanout logic + design->sigNormalize(false); + if (module_name.empty()) { #ifdef _WIN32 char fname[_MAX_FNAME]; diff --git a/kernel/celltypes.h b/kernel/celltypes.h index 50dee573e..ceff0bd1a 100644 --- a/kernel/celltypes.h +++ b/kernel/celltypes.h @@ -103,7 +103,7 @@ struct CellTypes setup_type(ID($equiv), {ID::A, ID::B}, {ID::Y}); setup_type(ID($specify2), {ID::EN, ID::SRC, ID::DST}, pool()); setup_type(ID($specify3), {ID::EN, ID::SRC, ID::DST, ID::DAT}, pool()); - setup_type(ID($specrule), {ID::EN_SRC, ID::EN_DST, ID::SRC, ID::DST}, pool()); + setup_type(ID($specrule), {ID::SRC_EN, ID::DST_EN, ID::SRC, ID::DST}, pool()); setup_type(ID($print), {ID::EN, ID::ARGS, ID::TRG}, pool()); setup_type(ID($check), {ID::A, ID::EN, ID::ARGS, ID::TRG}, pool()); setup_type(ID($set_tag), {ID::A, ID::SET, ID::CLR}, {ID::Y}); diff --git a/kernel/constids.inc b/kernel/constids.inc index c99aa788d..61c9b3a8c 100644 --- a/kernel/constids.inc +++ b/kernel/constids.inc @@ -459,9 +459,7 @@ X(EDGE_POL) X(EFX_ADD) X(EN) X(ENPOL) -X(EN_DST) X(EN_POLARITY) -X(EN_SRC) X(EQN) X(F) X(FDCE) diff --git a/kernel/ff.cc b/kernel/ff.cc index 7dd5e24ac..3a2c07890 100644 --- a/kernel/ff.cc +++ b/kernel/ff.cc @@ -635,8 +635,6 @@ Cell *FfData::emit() { return nullptr; } } - if (initvals && !is_anyinit) - initvals->set_init(sig_q, val_init); if (!is_fine) { if (has_gclk) { log_assert(!has_clk); @@ -747,6 +745,8 @@ Cell *FfData::emit() { } } cell->attributes = attributes; + if (initvals && !is_anyinit) + initvals->set_init(cell->getPort(ID::Q), val_init); return cell; } diff --git a/kernel/ff.h b/kernel/ff.h index 217658d35..2e0c070b0 100644 --- a/kernel/ff.h +++ b/kernel/ff.h @@ -229,6 +229,43 @@ struct FfData : FfTypeData { void flip_rst_bits(const pool &bits); }; +struct FfDataSigMapped : public FfData { + SigMap& sigmap; + FfDataSigMapped(SigMap& map, Module *module, FfInitVals *initvals = nullptr, IdString name = IdString()) : FfData(module, initvals, name), sigmap(map) {} + + void remap() { + sigmap(sig_q); + sigmap(sig_d); + sigmap(sig_ad); + sigmap(sig_clk); + sigmap(sig_ce); + sigmap(sig_aload); + sigmap(sig_arst); + sigmap(sig_srst); + sigmap(sig_clr); + sigmap(sig_set); + } + FfDataSigMapped(SigMap& map, FfInitVals *initvals, Cell *cell_) : FfData(initvals, cell_), sigmap(map) { + remap(); + } + FfDataSigMapped(SigMap& map, const FfData& base) : FfData(base), sigmap(map) { + remap(); + } + FfDataSigMapped(const FfDataSigMapped& other) : FfData(other), sigmap(other.sigmap) {} + FfDataSigMapped& operator=(const FfDataSigMapped& other) { + FfData::operator=(other); + return *this; + } + Cell* emit() { + Cell* cell = FfData::emit(); + remap(); + return cell; + } + FfDataSigMapped slice(const std::vector &bits) { + return FfDataSigMapped(sigmap, FfData::slice(bits)); + } +}; + YOSYS_NAMESPACE_END #endif diff --git a/kernel/ffmerge.cc b/kernel/ffmerge.cc index 709549e4d..1a0826086 100644 --- a/kernel/ffmerge.cc +++ b/kernel/ffmerge.cc @@ -303,6 +303,7 @@ void FfMergeHelper::remove_output_ff(const pool> &bits) { dff_driver.erase((*sigmap)(q[idx])); q[idx] = module->addWire(stringf("$ffmerge_disconnected$%d", autoidx++)); cell->setPort(ID::Q, q); + initvals->set_init(cell->getPort(ID::Q), (*initvals)(q)); } } diff --git a/kernel/mem.cc b/kernel/mem.cc index 02d12dea4..f5dff2aec 100644 --- a/kernel/mem.cc +++ b/kernel/mem.cc @@ -349,8 +349,11 @@ void Mem::emit() { bool v2 = !init.en.is_fully_ones(); if (!init.cell) init.cell = module->addCell(NEW_ID, v2 ? ID($meminit_v2) : ID($meminit)); - else + else { + if (!v2) + init.cell->unsetPort(ID::EN); init.cell->type = v2 ? ID($meminit_v2) : ID($meminit); + } init.cell->attributes = init.attributes; init.cell->parameters[ID::MEMID] = memid.str(); init.cell->parameters[ID::ABITS] = GetSize(init.addr); @@ -361,8 +364,6 @@ void Mem::emit() { init.cell->setPort(ID::DATA, init.data); if (v2) init.cell->setPort(ID::EN, init.en); - else - init.cell->unsetPort(ID::EN); } } } diff --git a/kernel/newcelltypes.h b/kernel/newcelltypes.h index bb14293a3..eb42019af 100644 --- a/kernel/newcelltypes.h +++ b/kernel/newcelltypes.h @@ -80,7 +80,7 @@ struct CellTableBuilder { setup_type(ID($equiv), {ID::A, ID::B}, {ID::Y}, features); setup_type(ID($specify2), {ID::EN, ID::SRC, ID::DST}, {}, features); setup_type(ID($specify3), {ID::EN, ID::SRC, ID::DST, ID::DAT}, {}, features); - setup_type(ID($specrule), {ID::EN_SRC, ID::EN_DST, ID::SRC, ID::DST}, {}, features); + setup_type(ID($specrule), {ID::SRC_EN, ID::DST_EN, ID::SRC, ID::DST}, {}, features); setup_type(ID($print), {ID::EN, ID::ARGS, ID::TRG}, {}, features); setup_type(ID($check), {ID::A, ID::EN, ID::ARGS, ID::TRG}, {}, features); setup_type(ID($set_tag), {ID::A, ID::SET, ID::CLR}, {ID::Y}, features); diff --git a/kernel/rtlil.cc b/kernel/rtlil.cc index 2badd20c3..0b922b284 100644 --- a/kernel/rtlil.cc +++ b/kernel/rtlil.cc @@ -1215,22 +1215,6 @@ RTLIL::Module *RTLIL::Design::top_module() const return module_count == 1 ? module : nullptr; } -void RTLIL::Design::add(RTLIL::Module *module) -{ - log_assert(modules_.count(module->name) == 0); - log_assert(refcount_modules_ == 0); - modules_[module->name] = module; - module->design = this; - - for (auto mon : monitors) - mon->notify_module_add(module); - - if (yosys_xtrace) { - log("#X# New Module: %s\n", log_id(module)); - log_backtrace("-X- ", yosys_xtrace-1); - } -} - void RTLIL::Design::add(RTLIL::Binding *binding) { log_assert(binding != nullptr); @@ -1528,6 +1512,7 @@ RTLIL::Module::Module() RTLIL::Module::~Module() { + clear_sig_norm_index(); for (auto &pr : wires_) delete pr.second; for (auto &pr : memories) @@ -2740,7 +2725,7 @@ void RTLIL::Module::cloneInto(RTLIL::Module *new_mod) const new_mod->avail_parameters = avail_parameters; new_mod->parameter_default_values = parameter_default_values; - for (auto &conn : connections_) + for (auto &conn : connections()) new_mod->connect(conn); for (auto &attr : attributes) @@ -2948,23 +2933,6 @@ void RTLIL::Module::remove(const pool &wires) } } -void RTLIL::Module::remove(RTLIL::Cell *cell) -{ - while (!cell->connections_.empty()) - cell->unsetPort(cell->connections_.begin()->first); - - log_assert(cells_.count(cell->name) != 0); - log_assert(refcount_cells_ == 0); - cells_.erase(cell->name); - if (design && design->flagBufferedNormalized && buf_norm_cell_queue.count(cell)) { - cell->type.clear(); - cell->name.clear(); - pending_deleted_cells.insert(cell); - } else { - delete cell; - } -} - void RTLIL::Module::remove(RTLIL::Memory *memory) { log_assert(memories.count(memory->name) != 0); @@ -3108,29 +3076,6 @@ void RTLIL::Module::connect(const RTLIL::SigSpec &lhs, const RTLIL::SigSpec &rhs connect(RTLIL::SigSig(lhs, rhs)); } -void RTLIL::Module::new_connections(const std::vector &new_conn) -{ - for (auto mon : monitors) - mon->notify_connect(this, new_conn); - - if (design) - for (auto mon : design->monitors) - mon->notify_connect(this, new_conn); - - if (yosys_xtrace) { - log("#X# New connections vector in %s:\n", log_id(this)); - for (auto &conn: new_conn) - log("#X# %s = %s (%d bits)\n", log_signal(conn.first), log_signal(conn.second), GetSize(conn.first)); - log_backtrace("-X- ", yosys_xtrace-1); - } - - connections_ = new_conn; -} - -const std::vector &RTLIL::Module::connections() const -{ - return connections_; -} void RTLIL::Module::fixup_ports() { diff --git a/kernel/rtlil.h b/kernel/rtlil.h index b9d86b91c..af0a0cb23 100644 --- a/kernel/rtlil.h +++ b/kernel/rtlil.h @@ -122,8 +122,11 @@ namespace RTLIL struct Binding; struct IdString; struct OwningIdString; + struct StaticIdString; + struct SigNormIndex; typedef std::pair SigSig; + struct PortBit; }; struct RTLIL::IdString @@ -1890,7 +1893,9 @@ struct RTLIL::Design dict scratchpad; bool flagBufferedNormalized = false; + bool flagSigNormalized = false; void bufNormalize(bool enable=true); + void sigNormalize(bool enable=true); int refcount_modules_; dict modules_; @@ -2051,6 +2056,10 @@ struct RTLIL::Design struct RTLIL::Module : public RTLIL::NamedObject { + friend struct RTLIL::SigNormIndex; + friend struct RTLIL::Cell; + friend struct RTLIL::Design; + Hasher::hash_t hashidx_; [[nodiscard]] Hasher hash_into(Hasher h) const { h.eat(hashidx_); return h; } @@ -2108,6 +2117,19 @@ public: pool pending_deleted_cells; dict> buf_norm_connect_index; void bufNormalize(); + void dump_sigmap(); + +protected: + SigNormIndex *sig_norm_index = nullptr; + void clear_sig_norm_index(); + int timestamp_ = 0; +public: + void sigNormalize(); + + int timestamp() const { return timestamp_; } + int next_timestamp(); + std::vector dirty_cells(int starting_from); + const pool &fanout(SigBit bit); template void rewrite_sigspecs(T &functor); template void rewrite_sigspecs2(T &functor); @@ -2428,6 +2450,7 @@ struct RTLIL::Wire : public RTLIL::NamedObject protected: // use module->addWire() and module->remove() to create or destroy wires friend struct RTLIL::Module; + friend struct RTLIL::SigNormIndex; Wire(); ~Wire(); @@ -2625,6 +2648,33 @@ public: std::string to_rtlil_str() const; }; +struct RTLIL::PortBit +{ + RTLIL::Cell *cell; + RTLIL::IdString port; + int offset; + PortBit(Cell* c, IdString p, int o) : cell(c), port(p), offset(o) {} + + bool operator<(const PortBit &other) const { + if (cell != other.cell) + return cell < other.cell; + if (port != other.port) + return port < other.port; + return offset < other.offset; + } + + bool operator==(const PortBit &other) const { + return cell == other.cell && port == other.port && offset == other.offset; + } + + [[nodiscard]] Hasher hash_into(Hasher h) const { + h.eat(cell->name); + h.eat(port); + h.eat(offset); + return h; + } +}; + inline RTLIL::SigBit::SigBit() : wire(NULL), data(RTLIL::State::S0) { } inline RTLIL::SigBit::SigBit(RTLIL::State bit) : wire(NULL), data(bit) { } @@ -2689,6 +2739,7 @@ inline RTLIL::SigBit::SigBit(const RTLIL::SigSpec &sig) { template void RTLIL::Module::rewrite_sigspecs(T &functor) { + log_assert(sig_norm_index == nullptr); for (auto &it : cells_) it.second->rewrite_sigspecs(functor); for (auto &it : processes) @@ -2702,6 +2753,7 @@ void RTLIL::Module::rewrite_sigspecs(T &functor) template void RTLIL::Module::rewrite_sigspecs2(T &functor) { + log_assert(sig_norm_index == nullptr); for (auto &it : cells_) it.second->rewrite_sigspecs2(functor); for (auto &it : processes) diff --git a/kernel/rtlil_bufnorm.cc b/kernel/rtlil_bufnorm.cc index 5f74b3380..1aeea6c32 100644 --- a/kernel/rtlil_bufnorm.cc +++ b/kernel/rtlil_bufnorm.cc @@ -28,6 +28,241 @@ YOSYS_NAMESPACE_BEGIN +typedef std::pair cell_port_t; + +// Since this is kernel code, we only log with yosys_xtrace set to not get +// in the way when using `debug` to debug specific passes.q +#define xlog(...) do { if (yosys_xtrace) log("#X [bufnorm] " __VA_ARGS__); } while (0) + +struct RTLIL::SigNormIndex +{ + SigNormIndex(RTLIL::Module *module) : module(module) {} + RTLIL::Module *module; + + SigMap sigmap; + size_t restored_connections = 0; + + dict> fanout; + + pool newly_driven; + + dict cell_timestamps; + dict> timestamp_cells; + pool dirty; + + void setup() { + module->fixup_ports(); + setup_module_inputs(); + setup_driven_wires(); + setup_fanout(); + } + + void dump_sigmap() { + for (auto [name, wire] : module->wires_) { + log_debug("wire %s %p %s\n", name, wire, wire->name); + SigSpec ss(wire); + log_debug("ss %s\n", log_signal(ss)); + sigmap(ss); + log_debug("sigmapped %s\n", log_signal(ss)); + } + for (auto [lhs, rhs] : module->connections_) { + log_debug("connection %s %s\n", log_signal(lhs), log_signal(rhs)); + } + } + + void normalize() { + flush_connections(); + flush_newly_driven(); + } + + void setup_module_inputs() { + std::vector cells_to_remove; + dict input_port_cells; + + for (auto cell : module->cells()) { + if (cell->type != ID($input_port)) + continue; + + auto const &sig_y = cell->getPort(ID::Y); + Wire *wire; + if (sig_y.is_wire() && (wire = sig_y.as_wire())->port_input && !wire->port_output && !input_port_cells.count(wire)) + input_port_cells.emplace(wire, cell); + else + cells_to_remove.push_back(cell); + + } + for (auto cell : cells_to_remove) + module->remove(cell); + + for (auto portname : module->ports) { + Wire *wire = module->wire(portname); + if (wire->port_input && !wire->port_output && !input_port_cells.count(wire)) { + Cell *cell = module->addCell(NEW_ID, ID($input_port)); + cell->setParam(ID::WIDTH, GetSize(wire)); + cell->setPort(ID::Y, wire); + input_port_cells.emplace(wire, cell); + } + } + + for (auto [wire, cell] : input_port_cells) { + wire->driverCell_ = cell; + wire->driverPort_ = ID::Y; + } + } + + void setup_driven_wires() { + for (auto cell : module->cells()) { + xlog("setup_driven_wires cell %s %s\n", cell->type, cell->name); + for (auto &[port, sig] : cell->connections_) { + xlog("\t%s = %s\n", port, log_signal(sig)); + if (cell->port_dir(port) == RTLIL::PD_INPUT) + continue; + xlog("%s is not an input in design %p\n", port, module->design); + if (sig.is_wire()) { + Wire * wire = sig.as_wire(); + + if (wire->driverCell_ == cell && wire->driverPort_ == port) + continue; + if (wire->driverCell_ == nullptr) { + wire->driverCell_ = cell; + wire->driverPort_ = port; + continue; + } + } + + Wire *wire = module->addWire(NEW_ID, GetSize(sig)); + wire->driverCell_ = cell; + wire->driverPort_ = port; + + xlog("therefore connect port %s %s %s\n", port, log_signal(sig), wire->name); + module->connect(sig, wire); + sig = wire; + } + } + } + + + void setup_fanout() { + for (auto cell : module->cells()) { + for (auto &[port, sig] : cell->connections_) { + if (cell->port_dir(port) != RTLIL::PD_INPUT) + continue; + int i = 0; + for (auto bit : sig) + if (bit.is_wire()) + fanout[bit].insert(PortBit(cell, port, i++)); + } + } + } + + void flush_connections() { + std::vector connect_lhs; + std::vector connect_rhs; + + auto begin = module->connections_.begin() + restored_connections; + auto end = module->connections_.end(); + + for (auto it = begin; it != end; ++it) { + auto &[lhs, rhs] = *it; + sigmap.apply(lhs); + sigmap.apply(rhs); + auto rhs_bits = rhs.bits().begin(); + + connect_lhs.clear(); + connect_rhs.clear(); + + for (auto l : lhs.bits()) { + auto r = *rhs_bits; + ++rhs_bits; + if (l == r) + continue; + // TODO figure out what should happen with 'z + bool l_driven = !l.is_wire() || l.wire->known_driver(); + bool r_driven = !r.is_wire() || r.wire->known_driver(); + if (l_driven && r_driven) { + connect_lhs.push_back(l); + connect_rhs.push_back(r); + continue; + } + + sigmap.add(l, r); + if (l_driven) { + sigmap.database.promote(l); + newly_driven.insert(r); + } else { + sigmap.database.promote(r); + newly_driven.insert(l); + } + } + + if (!connect_lhs.empty()) { + Cell *cell = module->addCell(NEW_ID, ID($connect)); + xlog("add connect (1) %s\n", cell->name); + cell->setParam(ID::WIDTH, GetSize(connect_lhs)); + cell->setPort(ID::A, std::move(connect_lhs)); + cell->setPort(ID::B, std::move(connect_rhs)); + } + } + + module->connections_.clear(); + restored_connections = 0; + } + + void flush_newly_driven() { + pool ports_to_normalize; + SigSpec tmp; + + while (!newly_driven.empty()) { + SigBit current = newly_driven.pop(); + + auto found = fanout.find(current); + if (found == fanout.end()) + continue; + + ports_to_normalize.clear(); + + for (auto const &portbit : found->second) + ports_to_normalize.emplace(portbit.cell, portbit.port); + + for (auto const &[cell, port] : ports_to_normalize) { + tmp = cell->getPort(port); + cell->setPort(port, tmp); + } + } + } + + void restore_connections() { + flush_connections(); + pool wires; + for (auto const &bit : sigmap.database) + if (bit.is_wire()) + wires.insert(bit.wire); + + + std::vector connect_lhs; + std::vector connect_rhs; + + for (auto wire : wires) { + connect_lhs.clear(); + connect_rhs.clear(); + for (int i = 0; i < GetSize(wire); ++i) { + SigBit l = SigBit(wire, i); + SigBit r = sigmap(l); + if (l == r) + continue; + connect_lhs.push_back(l); + connect_rhs.push_back(r); + } + + if (!connect_lhs.empty()) + module->connections_.emplace_back(std::move(connect_lhs), std::move(connect_rhs)); + } + + restored_connections = module->connections_.size(); + } +}; + + void RTLIL::Design::bufNormalize(bool enable) { if (!enable) @@ -50,6 +285,8 @@ void RTLIL::Design::bufNormalize(bool enable) return; } + log_assert(!flagSigNormalized); + if (!flagBufferedNormalized) { for (auto module : modules()) @@ -78,19 +315,206 @@ void RTLIL::Design::bufNormalize(bool enable) module->bufNormalize(); } -struct bit_drive_data_t { - int drivers = 0; - int inout = 0; - int users = 0; -}; +void RTLIL::Design::sigNormalize(bool enable) +{ + if (!enable) + { + if (!flagSigNormalized) + return; + + + xlog("leaving signorm\n"); + for (auto module : modules()) { + module->connections(); + if (module->sig_norm_index != nullptr) { + delete module->sig_norm_index; + module->sig_norm_index = nullptr; + } + + for (auto wire : module->wires()) { + wire->driverCell_ = nullptr; + wire->driverPort_ = IdString(); + } + + // TODO inefficient? + std::vector cells_snapshot = module->cells(); + for (auto cell : cells_snapshot) { + if (cell->type == ID($input_port)) + module->remove(cell); + } + } + + flagSigNormalized = false; + return; + } + + log_assert(!flagBufferedNormalized); + + if (!flagSigNormalized) + { + xlog("entering signorm\n"); + flagSigNormalized = true; + } + + for (auto module : modules()) + module->sigNormalize(); +} + +void RTLIL::Module::sigNormalize() +{ + log_assert(design->flagSigNormalized); + + if (sig_norm_index == nullptr) { + auto new_index = new RTLIL::SigNormIndex(this); + new_index->setup(); + sig_norm_index = new_index; + } + + sig_norm_index->normalize(); + +} + +void RTLIL::Module::dump_sigmap() +{ + if (sig_norm_index != nullptr) + sig_norm_index->dump_sigmap(); +} + +void RTLIL::Module::clear_sig_norm_index() +{ + if (sig_norm_index == nullptr) + return; + delete sig_norm_index; +} + + +const std::vector &RTLIL::Module::connections() const +{ + if (sig_norm_index != nullptr) + sig_norm_index->restore_connections(); + return connections_; +} + +void RTLIL::Module::new_connections(const std::vector &new_conn) +{ + if (sig_norm_index != nullptr) { + sig_norm_index->restore_connections(); + sig_norm_index->restored_connections = 0; + // TODO clear the sigmap as well? + } + for (auto mon : monitors) + mon->notify_connect(this, new_conn); + + if (design) + for (auto mon : design->monitors) + mon->notify_connect(this, new_conn); + + if (yosys_xtrace) { + log("#X# New connections vector in %s:\n", log_id(this)); + for (auto &conn: new_conn) + log("#X# %s = %s (%d bits)\n", log_signal(conn.first), log_signal(conn.second), GetSize(conn.first)); + log_backtrace("-X- ", yosys_xtrace-1); + } + + connections_ = new_conn; +} + + +int RTLIL::Module::next_timestamp() +{ + int old = timestamp_; + int current = timestamp_ = old + 1; + + if (sig_norm_index != nullptr) { + sigNormalize(); + for (auto cell : sig_norm_index->dirty) { + auto [found, inserted] = sig_norm_index->cell_timestamps.emplace(cell, old); + if (!inserted) { + log_assert(found->second != old); + auto found_cells = sig_norm_index->timestamp_cells.find(found->second); + log_assert(found_cells != sig_norm_index->timestamp_cells.end()); + bool erased = found_cells->second.erase(cell); + log_assert(erased); + if (found_cells->second.empty()) + sig_norm_index->timestamp_cells.erase(found_cells); + found->second = old; + } + } + sig_norm_index->timestamp_cells[old] = std::move(sig_norm_index->dirty); + sig_norm_index->dirty.clear(); + } + + + return current; +} + + +std::vector RTLIL::Module::dirty_cells(int starting_from) +{ + sigNormalize(); + std::vector result; + if (starting_from == INT_MIN) { + result.reserve(cells_.size()); + for (auto cell : cells()) + result.push_back(cell); + return result; + } + + for (int i = starting_from; i < timestamp_; ++i) { + auto found = sig_norm_index->timestamp_cells.find(i); + if (found != sig_norm_index->timestamp_cells.end()) + for (auto cell : found->second) + result.push_back(cell); + } + if (starting_from <= timestamp_) + for (auto cell : sig_norm_index->dirty) + result.push_back(cell); + + return result; +} + +const pool &RTLIL::Module::fanout(SigBit bit) { + log_assert(sig_norm_index != nullptr); + auto found = sig_norm_index->fanout.find(bit); + static pool empty; + if (found == sig_norm_index->fanout.end()) + return empty; + return found->second; +} + +void RTLIL::Module::remove(RTLIL::Cell *cell) +{ + while (!cell->connections_.empty()) + cell->unsetPort(cell->connections_.begin()->first); + + log_assert(cells_.count(cell->name) != 0); + log_assert(refcount_cells_ == 0); + cells_.erase(cell->name); + if (design && design->flagBufferedNormalized && buf_norm_cell_queue.count(cell)) { + cell->type.clear(); + cell->name.clear(); + pending_deleted_cells.insert(cell); + } else { + if (sig_norm_index != nullptr) { + auto found = sig_norm_index->cell_timestamps.find(cell); + if (found != sig_norm_index->cell_timestamps.end()) { + auto found_cells = sig_norm_index->timestamp_cells.find(found->second); + log_assert(found_cells != sig_norm_index->timestamp_cells.end()); + bool erased = found_cells->second.erase(cell); + log_assert(erased); + if (found_cells->second.empty()) + sig_norm_index->timestamp_cells.erase(found_cells); + sig_norm_index->cell_timestamps.erase(found); + } + sig_norm_index->dirty.erase(cell); + } + delete cell; + } +} -typedef ModWalker::PortBit PortBit; void RTLIL::Module::bufNormalize() { - // Since this is kernel code, we only log with yosys_xtrace set to not get - // in the way when using `debug` to debug specific passes.q -#define xlog(...) do { if (yosys_xtrace) log("#X [bufnorm] " __VA_ARGS__); } while (0) if (!design->flagBufferedNormalized) return; @@ -545,6 +969,55 @@ void RTLIL::Cell::unsetPort(RTLIL::IdString portname) log_backtrace("-X- ", yosys_xtrace-1); } + if (module->sig_norm_index != nullptr) { + module->sig_norm_index->dirty.insert(this); + bool is_input_port = port_dir(portname) == RTLIL::PD_INPUT; + if (is_input_port) { + auto &fanout = module->sig_norm_index->fanout; + int counter = 0; + for (auto bit : conn_it->second) { + if (!bit.is_wire()) + continue; + int i = counter++; + auto found = fanout.find(bit); + log_assert(found != fanout.end()); + int erased = found->second.erase(PortBit(this, portname, i)); + log_assert(erased); + if (found->second.empty()) + fanout.erase(found); + } + } else if (GetSize(conn_it->second)) { + Wire *w = conn_it->second.as_wire(); + log_assert(w->driverCell_ == this); + log_assert(w->driverPort_ == portname); + w->driverCell_ = nullptr; + w->driverPort_ = IdString(); + } + // bool clear_fanout = true; + // if (conn_it->second.is_wire()) { + // Wire *w = conn_it->second.as_wire(); + // if (w->driverCell_ == this && w->driverPort_ == portname) { + // w->driverCell_ = nullptr; + // w->driverPort_ = IdString(); + // clear_fanout = false; + // } + // } + + // if (clear_fanout) { + // auto &fanout = module->sig_norm_index->fanout; + // int counter = 0; + // for (auto bit : conn_it->second) { + // int i = counter++; + // auto found = fanout.find(bit); + // log_assert(found != fanout.end()); + // int erased = found->second.erase(PortBit(this, portname, i)); + // log_assert(erased); + // if (found->second.empty()) + // fanout.erase(found); + // } + // } + } + if (module->design && module->design->flagBufferedNormalized) { if (conn_it->second.is_wire()) { Wire *w = conn_it->second.as_wire(); @@ -586,8 +1059,32 @@ void RTLIL::Cell::unsetPort(RTLIL::IdString portname) } } +static bool ignored_cell(const RTLIL::IdString& type) +{ + return type == ID($specify2) || type == ID($specify3) || type == ID($specrule); +} + void RTLIL::Cell::setPort(RTLIL::IdString portname, RTLIL::SigSpec signal) { + bool is_input_port = false; + if (module->sig_norm_index != nullptr && !ignored_cell(type)) { + module->sig_norm_index->sigmap.apply(signal); + auto dir = port_dir(portname); + + if (dir == RTLIL::PD_INPUT) { + is_input_port = true; + } else { + Wire *wire = nullptr; + if (signal.is_wire() && (wire = signal.as_wire())->driverCell_ != nullptr) + wire = nullptr; + if (wire == nullptr) { + wire = module->addWire(NEW_ID, GetSize(signal)); + module->connect(signal, wire); + signal = wire; + } + } + } + auto r = connections_.insert(portname); auto conn_it = r.first; if (!r.second && conn_it->second == signal) @@ -605,6 +1102,48 @@ void RTLIL::Cell::setPort(RTLIL::IdString portname, RTLIL::SigSpec signal) log_backtrace("-X- ", yosys_xtrace-1); } + + if (module->sig_norm_index != nullptr && !ignored_cell(type)) { + module->sig_norm_index->dirty.insert(this); + if (!r.second) { + if (is_input_port) { + auto &fanout = module->sig_norm_index->fanout; + int counter = 0; + for (auto bit : conn_it->second) { + if (!bit.is_wire()) + continue; + int i = counter++; + auto found = fanout.find(bit); + log_assert(found != fanout.end()); + int erased = found->second.erase(PortBit(this, portname, i)); + log_assert(erased); + if (found->second.empty()) + fanout.erase(found); + } + } else { + Wire *w = conn_it->second.as_wire(); + log_assert(w->driverCell_ == this); + log_assert(w->driverPort_ == portname); + w->driverCell_ = nullptr; + w->driverPort_ = IdString(); + } + } + + if (is_input_port) { + auto &fanout = module->sig_norm_index->fanout; + int i = 0; + for (auto bit : signal) + if (bit.is_wire()) + fanout[bit].insert(PortBit(this, portname, i++)); + } else if (GetSize(signal)) { + Wire *w = signal.as_wire(); + log_assert(w->driverCell_ == nullptr); + log_assert(w->driverPort_.empty()); + w->driverCell_ = this; + w->driverPort_ = portname; + } + } + if (module->design && module->design->flagBufferedNormalized) { // We eagerly clear a driver that got disconnected by changing this port connection @@ -670,4 +1209,25 @@ void RTLIL::Cell::setPort(RTLIL::IdString portname, RTLIL::SigSpec signal) } +void RTLIL::Design::add(RTLIL::Module *module) +{ + log_assert(modules_.count(module->name) == 0); + log_assert(refcount_modules_ == 0); + modules_[module->name] = module; + module->design = this; + + for (auto mon : monitors) + mon->notify_module_add(module); + + if (yosys_xtrace) { + log("#X# New Module: %s\n", log_id(module)); + log_backtrace("-X- ", yosys_xtrace-1); + } + + if (flagSigNormalized) { + module->sigNormalize(); + } + +} + YOSYS_NAMESPACE_END diff --git a/kernel/satgen.cc b/kernel/satgen.cc index 7fbcba1b2..f2781c665 100644 --- a/kernel/satgen.cc +++ b/kernel/satgen.cc @@ -465,6 +465,26 @@ bool SatGen::importCell(RTLIL::Cell *cell, int timestep) return true; } + if (cell->type.in(ID($connect))) + { + std::vector a = importDefSigSpec(cell->getPort(ID::A), timestep); + std::vector b = importDefSigSpec(cell->getPort(ID::B), timestep); + extendSignalWidthUnary(a, b, cell); + + std::vector bb = model_undef ? ez->vec_var(b.size()) : b; + ez->assume(ez->vec_eq(a, bb)); + + if (model_undef) + { + std::vector undef_a = importUndefSigSpec(cell->getPort(ID::A), timestep); + std::vector undef_b = importUndefSigSpec(cell->getPort(ID::B), timestep); + extendSignalWidthUnary(undef_a, undef_b, cell); + ez->assume(ez->vec_eq(undef_a, undef_b)); + undefGating(b, bb, undef_b); + } + return true; + } + if (cell->type.in(ID($reduce_and), ID($reduce_or), ID($reduce_xor), ID($reduce_xnor), ID($reduce_bool), ID($logic_not))) { std::vector a = importDefSigSpec(cell->getPort(ID::A), timestep); diff --git a/passes/cmds/abstract.cc b/passes/cmds/abstract.cc index 2ea71268b..7965394d6 100644 --- a/passes/cmds/abstract.cc +++ b/passes/cmds/abstract.cc @@ -265,6 +265,8 @@ unsigned int abstract_value(Module* mod, EnableLogic enable, const std::vector cells_snapshot = mod->cells(); for (auto cell : cells_snapshot) { + if (cell->type == ID($input_port)) + continue; for (auto conn : cell->connections()) if (cell->output(conn.first)) { std::set offsets_to_abstract; @@ -462,6 +464,8 @@ struct AbstractPass : public Pass { } extra_args(args, argidx, design); + // TODO Disabled signorm because swap_names breaks fanout logic + design->sigNormalize(false); if (enable != Enable::Always) { if (mode == Mode::Initial) log_cmd_error("Conditional initial value abstraction is not supported\n"); diff --git a/passes/cmds/check.cc b/passes/cmds/check.cc index 1019c2955..82ada9ab0 100644 --- a/passes/cmds/check.cc +++ b/passes/cmds/check.cc @@ -257,6 +257,9 @@ struct CheckPass : public Pass { pool coarsened_cells; for (auto cell : module->cells()) { + if (cell->type == ID($input_port)) + continue; + if (mapped && cell->type.begins_with("$") && design->module(cell->type) == nullptr) { if (allow_tbuf && cell->type == ID($_TBUF_)) goto cell_allowed; log_warning("Cell %s.%s is an unmapped internal cell of type %s.\n", log_id(module), log_id(cell), log_id(cell->type)); @@ -264,6 +267,30 @@ struct CheckPass : public Pass { cell_allowed:; } + if (cell->type == ID($connect)) { + // Inefficient, but rare case in sane design + auto sig_a = cell->getPort(ID::A); + auto sig_b = cell->getPort(ID::B); + for (int i = 0; i < sig_a.size(); i++) { + int count_a = wire_drivers_count[sig_a[i]]; + int count_b = wire_drivers_count[sig_b[i]]; + wire_drivers_count[sig_a[i]] += count_b; + wire_drivers_count[sig_b[i]] += count_a; + // Guarantee default constructed members if missing + (void)wire_drivers[sig_a[i]]; + (void)wire_drivers[sig_b[i]]; + auto& drivers_a = wire_drivers[sig_a[i]]; + auto& drivers_b = wire_drivers[sig_b[i]]; + vector drivers; + drivers.reserve(std::max(drivers_a.size(), drivers_b.size())); + for (auto driver : drivers_a) + drivers.push_back(driver); + for (auto driver : drivers_b) + drivers.push_back(driver); + drivers_a = drivers; + drivers_b = drivers; + } + } for (auto &conn : cell->connections()) { bool input = cell->input(conn.first); bool output = cell->output(conn.first); diff --git a/passes/cmds/chformal.cc b/passes/cmds/chformal.cc index ccda023c0..2e0df8f76 100644 --- a/passes/cmds/chformal.cc +++ b/passes/cmds/chformal.cc @@ -218,6 +218,9 @@ struct ChformalPass : public Pass { } extra_args(args, argidx, design); + // TODO Disabled signorm because swap_names breaks fanout logic + design->sigNormalize(false); + if (constr_types.empty()) { constr_types.insert(ID($assert)); constr_types.insert(ID($assume)); diff --git a/passes/cmds/connect.cc b/passes/cmds/connect.cc index c6d3320ea..c2f9be428 100644 --- a/passes/cmds/connect.cc +++ b/passes/cmds/connect.cc @@ -32,11 +32,23 @@ static void unset_drivers(RTLIL::Design *design, RTLIL::Module *module, SigMap & RTLIL::Wire *dummy_wire = module->addWire(NEW_ID, sig.size()); + // (void)module->connections(); // trigger signorm flush + for (auto cell : module->cells()) for (auto &port : cell->connections_) if (ct.cell_output(cell->type, port.first)) sigmap(port.second).replace(sig, dummy_wire, &port.second); + bool need_fixup = false; + for (auto bit : sig.bits()) { + if (bit.is_wire() && bit.wire->port_input) { + bit.wire->port_input = false; + need_fixup = true; + } + } + if (need_fixup) + module->fixup_ports(); + for (auto &conn : module->connections_) sigmap(conn.first).replace(sig, dummy_wire, &conn.first); } @@ -66,7 +78,7 @@ struct ConnectPass : public Pass { log("\n"); log("\n"); log("Per default signal alias names are resolved and all signal names are mapped\n"); - log("the the signal name of the primary driver. Using the -nomap option deactivates\n"); + log("to the signal name of the primary driver. Using the -nomap option deactivates\n"); log("this behavior.\n"); log("\n"); log("The connect command operates in one module only. Either only one module must\n"); @@ -77,6 +89,8 @@ struct ConnectPass : public Pass { log("\n"); log("This command does not operate on module with processes.\n"); log("\n"); + log("Overriding any bits connected to a module input port will remove the input port.\n"); + log("\n"); } void execute(std::vector args, RTLIL::Design *design) override { diff --git a/passes/cmds/design.cc b/passes/cmds/design.cc index ddbd98bfd..a932405ff 100644 --- a/passes/cmds/design.cc +++ b/passes/cmds/design.cc @@ -338,8 +338,11 @@ struct DesignPass : public Pass { { RTLIL::Design *design_copy = new RTLIL::Design; - for (auto mod : design->modules()) + for (auto mod : design->modules()) { + // Triggers signorm flush if needed (hacky) + (void)mod->connections(); design_copy->add(mod->clone()); + } design_copy->selection_stack = design->selection_stack; design_copy->selection_vars = design->selection_vars; @@ -356,6 +359,8 @@ struct DesignPass : public Pass { if (reset_mode || !load_name.empty() || push_mode || pop_mode) { + design->flagSigNormalized = false; + for (auto mod : design->modules().to_vector()) design->remove(mod); @@ -377,6 +382,7 @@ struct DesignPass : public Pass { { RTLIL::Design *saved_design = pop_mode ? pushed_designs.back() : saved_designs.at(load_name); + design->flagSigNormalized = saved_design->flagSigNormalized; for (auto mod : saved_design->modules()) design->add(mod->clone()); diff --git a/passes/cmds/rename.cc b/passes/cmds/rename.cc index a07d588c4..88cd7b14b 100644 --- a/passes/cmds/rename.cc +++ b/passes/cmds/rename.cc @@ -378,6 +378,9 @@ struct RenamePass : public Pass { break; } + // TODO disable signorm due to rename I think? + design->sigNormalize(false); + if (flag_src) { extra_args(args, argidx, design); diff --git a/passes/cmds/splitnets.cc b/passes/cmds/splitnets.cc index d61a99ec0..f943127de 100644 --- a/passes/cmds/splitnets.cc +++ b/passes/cmds/splitnets.cc @@ -145,6 +145,9 @@ struct SplitnetsPass : public Pass { } extra_args(args, argidx, design); + // TODO disable signorm due to rewrite_sigspecs assert + design->sigNormalize(false); + // module_ports_db[module_name][old_port_name] = new_port_name_list dict>> module_ports_db; diff --git a/passes/equiv/equiv_make.cc b/passes/equiv/equiv_make.cc index bae7452f7..9acd7cb23 100644 --- a/passes/equiv/equiv_make.cc +++ b/passes/equiv/equiv_make.cc @@ -114,6 +114,8 @@ struct EquivMakeWorker if ((it->name.isPublic() || inames) && blacklist_names.count(it->name) == 0) cell_names.insert(it->name); gold_clone->rename(it, it->name.str() + "_gold"); + if (it->type == ID($input_port)) + gold_clone->remove(it); } for (auto it : gate_clone->wires().to_vector()) { @@ -126,6 +128,8 @@ struct EquivMakeWorker if ((it->name.isPublic() || inames) && blacklist_names.count(it->name) == 0) cell_names.insert(it->name); gate_clone->rename(it, it->name.str() + "_gate"); + if (it->type == ID($input_port)) + gate_clone->remove(it); } gold_clone->cloneInto(equiv_mod); @@ -513,6 +517,7 @@ struct EquivMakePass : public Pass { worker.equiv_mod = design->addModule(RTLIL::escape_id(args[argidx+2])); worker.run(); + Pass::call(design, "dump"); } } EquivMakePass; diff --git a/passes/equiv/equiv_miter.cc b/passes/equiv/equiv_miter.cc index 6acfe85a9..c917268ca 100644 --- a/passes/equiv/equiv_miter.cc +++ b/passes/equiv/equiv_miter.cc @@ -143,8 +143,9 @@ struct EquivMiterWorker for (auto w : miter_wires) miter_module->addWire(w->name, w->width); for (auto c : miter_cells) { - miter_module->addCell(c->name, c); - auto mc = miter_module->cell(c->name); + if (c->type == ID($input_port)) + continue; + auto mc = miter_module->addCell(c->name, c); for (auto &conn : mc->connections()) mc->setPort(conn.first, sigmap(conn.second)); } @@ -319,6 +320,9 @@ struct EquivMiterPass : public Pass { worker.miter_name = RTLIL::escape_id(args[argidx++]); extra_args(args, argidx, design); + // TODO disable signorm due to rewrite_sigspecs assert + design->sigNormalize(false); + if (design->module(worker.miter_name)) log_cmd_error("Miter module %s already exists.\n", log_id(worker.miter_name)); diff --git a/passes/hierarchy/flatten.cc b/passes/hierarchy/flatten.cc index 17bd6e340..52a9d5a34 100644 --- a/passes/hierarchy/flatten.cc +++ b/passes/hierarchy/flatten.cc @@ -178,6 +178,8 @@ struct FlattenWorker } for (auto tpl_cell : tpl->cells()) { + if (tpl_cell->type == ID($input_port)) + continue; RTLIL::Cell *new_cell = module->addCell(map_name(cell, tpl_cell, separator), tpl_cell); map_attributes(cell, new_cell, tpl_cell->name); if (new_cell->has_memid()) { @@ -408,6 +410,9 @@ struct FlattenPass : public Pass { } extra_args(args, argidx, design); + bool was_signormed = design->flagSigNormalized; + design->sigNormalize(false); + RTLIL::Module *top = nullptr; if (design->full_selection()) for (auto module : design->modules()) @@ -447,6 +452,11 @@ struct FlattenPass : public Pass { design->remove(module); } + if (was_signormed) { + // TODO inconvenient workaround for fanout out of sync + design->sigNormalize(false); + design->sigNormalize(true); + } log_pop(); } } FlattenPass; diff --git a/passes/hierarchy/hierarchy.cc b/passes/hierarchy/hierarchy.cc index 416997bee..f6c8b1fd7 100644 --- a/passes/hierarchy/hierarchy.cc +++ b/passes/hierarchy/hierarchy.cc @@ -686,6 +686,8 @@ bool set_keep_print(std::map &cache, RTLIL::Module *mod) { if (cache.count(mod) == 0) for (auto c : mod->cells()) { + if (mod->name == c->type) + continue; RTLIL::Module *m = mod->design->module(c->type); if ((m != nullptr && set_keep_print(cache, m)) || c->type == ID($print)) return cache[mod] = true; @@ -697,6 +699,8 @@ bool set_keep_assert(std::map &cache, RTLIL::Module *mod) { if (cache.count(mod) == 0) for (auto c : mod->cells()) { + if (mod->name == c->type) + continue; RTLIL::Module *m = mod->design->module(c->type); if ((m != nullptr && set_keep_assert(cache, m)) || c->type.in(ID($check), ID($assert), ID($assume), ID($live), ID($fair), ID($cover))) return cache[mod] = true; diff --git a/passes/memory/memory_bram.cc b/passes/memory/memory_bram.cc index 10301b44a..f19c71998 100644 --- a/passes/memory/memory_bram.cc +++ b/passes/memory/memory_bram.cc @@ -125,6 +125,55 @@ struct rules_t variant_params[stringf("\\CFG_CLKPOL_%c", 'A' + i)] = clkpol[i]; } } + + void load_blackbox(Design* design) const + { + auto portinfos = make_portinfos(); + int clocks_max = 0; + for (auto &pi : portinfos) + clocks_max = max(clocks_max, pi.clocks); + + pool> inputs; + pool> outputs; + for (auto &pi : portinfos) + { + string prefix = stringf("%c%d", pi.group + 'A', pi.index + 1); + const char *pf = prefix.c_str(); + if (pi.clocks) { + std::string name = stringf("\\CLK%d", (pi.clocks-1) % clocks_max + 1); + inputs.insert(std::make_pair(name, 1)); + } + inputs.insert(std::make_pair(stringf("\\%sADDR", pf), abits)); + std::string dname = stringf("\\%sDATA", pf); + if (pi.wrmode) { + inputs.insert(std::make_pair(dname, dbits)); + } else { + outputs.insert(std::make_pair(dname, dbits)); + } + if (pi.enable) { + std::string name = stringf("\\%sEN", pf); + inputs.insert(std::make_pair(name, 1)); + } + } + + log_debug("setting up %s\n", name); + Module* mod = design->addModule(name); + mod->set_bool_attribute(ID::blackbox); + + for (auto [name, width] : inputs) + { + log_debug("input %s width %d\n", name, width); + mod->addWire(name, width)->port_input = true; + } + + for (auto [name, width] : outputs) + { + log_debug("output %s width %d\n", name, width); + mod->addWire(name, width)->port_output = true; + } + + mod->fixup_ports(); + } }; struct match_t { @@ -160,6 +209,19 @@ struct rules_t vector labels; int linecount; + void load_blackboxes(Design* design) const + { + for (auto& [_, variants] : brams) + { + for (const bram_t& bram : variants) + { + if (design->module(bram.name)) + continue; + + bram.load_blackbox(design); + } + } + } void syntax_error() { if (tokens.empty()) @@ -1314,6 +1376,7 @@ struct MemoryBramPass : public Pass { for (argidx = 1; argidx < args.size(); argidx++) { if (args[argidx] == "-rules" && argidx+1 < args.size()) { rules.parse(args[++argidx]); + rules.load_blackboxes(design); continue; } break; diff --git a/passes/opt/Makefile.inc b/passes/opt/Makefile.inc index e7b62fc6a..4412ac629 100644 --- a/passes/opt/Makefile.inc +++ b/passes/opt/Makefile.inc @@ -1,6 +1,7 @@ OBJS += passes/opt/opt.o OBJS += passes/opt/opt_merge.o +OBJS += passes/opt/opt_merge_inc.o OBJS += passes/opt/opt_mem.o OBJS += passes/opt/opt_mem_feedback.o OBJS += passes/opt/opt_mem_priority.o diff --git a/passes/opt/opt_clean/cells_temp.cc b/passes/opt/opt_clean/cells_temp.cc index b325b68d9..b265dd5e2 100644 --- a/passes/opt/opt_clean/cells_temp.cc +++ b/passes/opt/opt_clean/cells_temp.cc @@ -50,21 +50,25 @@ bool trim_buf(RTLIL::Cell* cell, ShardedVector& new_connections, } bool remove(ShardedVector& cells, RTLIL::Module* mod, bool verbose) { + // Removing $connect and $input_port doesn't count as "doing something" + // since they get rebuilt in signorm + // and don't enable further opt bool did_something = false; for (RTLIL::Cell *cell : cells) { if (verbose) { - if (cell->type == ID($connect)) + if (cell->type == ID($connect)) { log_debug(" removing connect cell `%s': %s <-> %s\n", cell->name, log_signal(cell->getPort(ID::A)), log_signal(cell->getPort(ID::B))); - else if (cell->type == ID($input_port)) + } else if (cell->type == ID($input_port)) { log_debug(" removing input port marker cell `%s': %s\n", cell->name, log_signal(cell->getPort(ID::Y))); - else + } else { + did_something = true; log_debug(" removing buffer cell `%s': %s = %s\n", cell->name, log_signal(cell->getPort(ID::Y)), log_signal(cell->getPort(ID::A))); + } } mod->remove(cell); - did_something = true; } return did_something; } diff --git a/passes/opt/opt_clean/opt_clean.cc b/passes/opt/opt_clean/opt_clean.cc index 87597d721..c02312ca2 100644 --- a/passes/opt/opt_clean/opt_clean.cc +++ b/passes/opt/opt_clean/opt_clean.cc @@ -77,6 +77,8 @@ struct OptCleanPass : public Pass { } extra_args(args, argidx, design); + design->sigNormalize(false); + { std::vector selected_modules; for (auto module : design->selected_whole_modules_warn()) @@ -128,6 +130,8 @@ struct CleanPass : public Pass { } extra_args(args, argidx, design); + design->sigNormalize(false); + { std::vector selected_modules; for (auto module : design->selected_unboxed_whole_modules()) diff --git a/passes/opt/opt_clean/wires.cc b/passes/opt/opt_clean/wires.cc index 28c792936..6e1d0044d 100644 --- a/passes/opt/opt_clean/wires.cc +++ b/passes/opt/opt_clean/wires.cc @@ -576,8 +576,8 @@ bool rmunused_module_signals(RTLIL::Module *module, ParallelDispatchThreadPool:: if (clean_ctx.flags.verbose && deleted_and_unreported) log_debug(" removed %d unused temporary wires.\n", deleted_and_unreported); - if (deleted_total) - module->design->scratchpad_set_bool("opt.did_something", true); + // if (deleted_total) + // module->design->scratchpad_set_bool("opt.did_something", true); return deleted_total != 0; } diff --git a/passes/opt/opt_dff.cc b/passes/opt/opt_dff.cc index 90ace69e5..e0994cfef 100644 --- a/passes/opt/opt_dff.cc +++ b/passes/opt/opt_dff.cc @@ -172,7 +172,7 @@ struct OptDffWorker if (path.count(sig_s[i]) && path.at(sig_s[i])) { ret = find_muxtree_feedback_patterns(sig_b[i*width + index], q, path); if (sig_b[i*width + index] == q) { - RTLIL::SigSpec s = mbit.first->getPort(ID::B); + RTLIL::SigSpec s = sigmap(mbit.first->getPort(ID::B)); s[i*width + index] = RTLIL::Sx; mbit.first->setPort(ID::B, s); } @@ -196,7 +196,7 @@ struct OptDffWorker ret.insert(pat); if (sig_b[i*width + index] == q) { - RTLIL::SigSpec s = mbit.first->getPort(ID::B); + RTLIL::SigSpec s = sigmap(mbit.first->getPort(ID::B)); s[i*width + index] = RTLIL::Sx; mbit.first->setPort(ID::B, s); } @@ -207,7 +207,7 @@ struct OptDffWorker ret.insert(pat); if (sig_a[index] == q) { - RTLIL::SigSpec s = mbit.first->getPort(ID::A); + RTLIL::SigSpec s = sigmap(mbit.first->getPort(ID::A)); s[index] = RTLIL::Sx; mbit.first->setPort(ID::A, s); } @@ -555,7 +555,7 @@ struct OptDffWorker } } - bool try_merge_srst(FfData &ff, Cell *cell, bool &changed) + bool try_merge_srst(FfDataSigMapped &ff, Cell *cell, bool &changed) { std::map> groups; std::vector remaining_indices; @@ -570,9 +570,9 @@ struct OptDffWorker if (GetSize(mbit.first->getPort(ID::S)) != 1) break; - SigBit s = mbit.first->getPort(ID::S); - SigBit a = mbit.first->getPort(ID::A)[mbit.second]; - SigBit b = mbit.first->getPort(ID::B)[mbit.second]; + SigBit s = sigmap(mbit.first->getPort(ID::S)); + SigBit a = sigmap(mbit.first->getPort(ID::A)[mbit.second]); + SigBit b = sigmap(mbit.first->getPort(ID::B)[mbit.second]); if ((a == State::S0 || a == State::S1) && (b == State::S0 || b == State::S1)) break; @@ -608,7 +608,7 @@ struct OptDffWorker Const val_srst = val_srst_builder.build(); for (auto &it : groups) { - FfData new_ff = ff.slice(it.second); + FfDataSigMapped new_ff = ff.slice(it.second); Const::Builder new_val_srst_builder(new_ff.width); for (int i = 0; i < new_ff.width; i++) new_val_srst_builder.push_back(val_srst[it.second[i]]); @@ -645,7 +645,7 @@ struct OptDffWorker return false; } - bool try_merge_ce(FfData &ff, Cell *cell, bool &changed) + bool try_merge_ce(FfDataSigMapped &ff, Cell *cell, bool &changed) { std::map, std::vector> groups; std::vector remaining_indices; @@ -658,9 +658,9 @@ struct OptDffWorker if (GetSize(mbit.first->getPort(ID::S)) != 1) break; - SigBit s = mbit.first->getPort(ID::S); - SigBit a = mbit.first->getPort(ID::A)[mbit.second]; - SigBit b = mbit.first->getPort(ID::B)[mbit.second]; + SigBit s = sigmap(mbit.first->getPort(ID::S)); + SigBit a = sigmap(mbit.first->getPort(ID::A)[mbit.second]); + SigBit b = sigmap(mbit.first->getPort(ID::B)[mbit.second]); if (a == ff.sig_q[i]) { enables.insert(ctrl_t(s, true)); @@ -688,7 +688,7 @@ struct OptDffWorker } for (auto &it : groups) { - FfData new_ff = ff.slice(it.second); + FfDataSigMapped new_ff = ff.slice(it.second); ctrl_t en = make_patterns_logic(it.first.first, it.first.second, ff.is_fine); new_ff.has_ce = true; @@ -726,8 +726,8 @@ struct OptDffWorker while (!dff_cells.empty()) { Cell *cell = dff_cells.back(); dff_cells.pop_back(); - - FfData ff(&initvals, cell); + // Break down the FF into pieces. + FfDataSigMapped ff(sigmap, &initvals, cell); bool changed = false; if (!ff.width) { @@ -819,7 +819,7 @@ struct OptDffWorker qcsat.ez->NOT(qcsat.ez->IFF(d_sat_pi, init_sat_pi))); } - State check_constbit(FfData &ff, int i) + State check_constbit(FfDataSigMapped &ff, int i) { State val = ff.val_init[i]; if (ff.has_arst) val = combine_const(val, ff.val_arst[i]); @@ -841,14 +841,15 @@ struct OptDffWorker QuickConeSat qcsat(modwalker); std::vector cells_to_remove; - std::vector ffs_to_emit; + std::vector ffs_to_emit; + bool did_something = false; for (auto cell : module->selected_cells()) { if (!cell->is_builtin_ff()) continue; - FfData ff(&initvals, cell); + FfDataSigMapped ff(sigmap, &initvals, cell); pool removed_sigbits; for (int i = 0; i < ff.width; i++) { @@ -979,6 +980,8 @@ struct OptDffPass : public Pass { break; } extra_args(args, argidx, design); + // TODO extra wires signorm adds breaks muxtree traversal or requires sigmapping + design->sigNormalize(false); bool did_something = false; for (auto mod : design->selected_modules()) { diff --git a/passes/opt/opt_expr.cc b/passes/opt/opt_expr.cc index 2c040b09d..af4d7b436 100644 --- a/passes/opt/opt_expr.cc +++ b/passes/opt/opt_expr.cc @@ -392,11 +392,15 @@ int get_highest_hot_index(RTLIL::SigSpec signal) return -1; } -void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool consume_x, bool mux_undef, bool mux_bool, bool do_fine, bool keepdc, bool noclkinv) +void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool consume_x, bool mux_undef, bool mux_bool, bool do_fine, bool keepdc, bool noclkinv, int timestamp=INT_MIN) { - SigMap assign_map(module); + SigMap assign_map; //(module); dict invert_map; + + auto dirty_cells = module->dirty_cells(timestamp); + + // TODO this could be cheaper for (auto cell : module->cells()) { if (design->selected(module, cell) && cell->type[0] == '$') { if (cell->type.in(ID($_NOT_), ID($not), ID($logic_not)) && @@ -409,7 +413,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons } if (!noclkinv) - for (auto cell : module->cells()) + for (auto cell : dirty_cells) if (design->selected(module, cell)) { if (cell->type.in(ID($dff), ID($dffe), ID($dffsr), ID($dffsre), ID($adff), ID($adffe), ID($aldff), ID($aldffe), ID($sdff), ID($sdffe), ID($sdffce), ID($fsm), ID($memrd), ID($memrd_v2), ID($memwr), ID($memwr_v2))) handle_polarity_inv(cell, ID::CLK, ID::CLK_POLARITY, assign_map, invert_map); @@ -487,9 +491,10 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons } TopoSort> cells; + dict outbit_to_cell; - for (auto cell : module->cells()) + for (auto cell : dirty_cells) if (design->selected(module, cell) && yosys_celltypes.cell_evaluable(cell->type)) { for (auto &conn : cell->connections()) if (yosys_celltypes.cell_output(cell->type, conn.first)) @@ -498,7 +503,7 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons cells.node(cell); } - for (auto cell : module->cells()) + for (auto cell : dirty_cells) if (design->selected(module, cell) && yosys_celltypes.cell_evaluable(cell->type)) { const int r_index = cells.node(cell); for (auto &conn : cell->connections()) @@ -514,6 +519,8 @@ void replace_const_cells(RTLIL::Design *design, RTLIL::Module *module, bool cons log("Couldn't topologically sort cells, optimizing module %s may take a longer time.\n", log_id(module)); } + log("iterating over %d cells\n", GetSize(cells.sorted)); + for (auto cell : cells.sorted) { #define ACTION_DO(_p_, _s_) do { replace_cell(assign_map, module, cell, input.as_string(), _p_, _s_); goto next_cell; } while (0) @@ -1144,10 +1151,10 @@ skip_fine_alu: if (input.match(" 1")) ACTION_DO(ID::Y, input.extract(1, 1)); if (input.match("01 ")) ACTION_DO(ID::Y, input.extract(0, 1)); if (input.match("10 ")) { - cell->type = ID($_NOT_); cell->setPort(ID::A, input.extract(0, 1)); cell->unsetPort(ID::B); cell->unsetPort(ID::S); + cell->type = ID($_NOT_); goto next_cell; } if (input.match("11 ")) ACTION_DO_Y(1); @@ -1199,8 +1206,13 @@ skip_fine_alu: replace_cell(assign_map, module, cell, "isneq", ID::Y, new_y); goto next_cell; } - if (a[i] == b[i]) - continue; + if (keepdc) { + if (!a[i].is_wire() && !b[i].is_wire() && a[i].data != RTLIL::State::Sx && b[i].data != RTLIL::State::Sx && a[i] == b[i]) + continue; + } else { + if (a[i] == b[i]) + continue; + } new_a.append(a[i]); new_b.append(b[i]); } @@ -1242,10 +1254,10 @@ skip_fine_alu: ACTION_DO(ID::Y, cell->getPort(ID::A)); } else { log_debug("Replacing %s cell `%s' in module `%s' with inverter.\n", log_id(cell->type), log_id(cell), log_id(module)); - cell->type = ID($not); cell->parameters.erase(ID::B_WIDTH); cell->parameters.erase(ID::B_SIGNED); cell->unsetPort(ID::B); + cell->type = ID($not); did_something = true; } goto next_cell; @@ -1257,7 +1269,7 @@ skip_fine_alu: { log_debug("Replacing %s cell `%s' in module `%s' with %s.\n", log_id(cell->type), log_id(cell), log_id(module), cell->type == ID($eq) ? "$logic_not" : "$reduce_bool"); - cell->type = cell->type == ID($eq) ? ID($logic_not) : ID($reduce_bool); + if (assign_map(cell->getPort(ID::A)).is_fully_zero()) { cell->setPort(ID::A, cell->getPort(ID::B)); cell->setParam(ID::A_SIGNED, cell->getParam(ID::B_SIGNED)); @@ -1266,6 +1278,7 @@ skip_fine_alu: cell->unsetPort(ID::B); cell->unsetParam(ID::B_SIGNED); cell->unsetParam(ID::B_WIDTH); + cell->type = cell->type == ID($eq) ? ID($logic_not) : ID($reduce_bool); did_something = true; goto next_cell; } @@ -1408,10 +1421,10 @@ skip_fine_alu: cell->setParam(ID::A_SIGNED, cell->getParam(ID::B_SIGNED)); } - cell->type = arith_inverse ? ID($neg) : ID($pos); cell->unsetPort(ID::B); cell->parameters.erase(ID::B_WIDTH); cell->parameters.erase(ID::B_SIGNED); + cell->type = arith_inverse ? ID($neg) : ID($pos); cell->check(); did_something = true; @@ -2292,9 +2305,14 @@ struct OptExprPass : public Pass { } extra_args(args, argidx, design); + design->sigNormalize(true); + NewCellTypes ct(design); for (auto module : design->selected_modules()) { + + int replace_const_cells_timestamp = INT_MIN; + int replace_const_cells_consume_x_timestamp = INT_MIN; log("Optimizing module %s.\n", log_id(module)); if (undriven) { @@ -2307,12 +2325,17 @@ struct OptExprPass : public Pass { do { do { did_something = false; - replace_const_cells(design, module, false /* consume_x */, mux_undef, mux_bool, do_fine, keepdc, noclkinv); + module->next_timestamp(); + replace_const_cells(design, module, false /* consume_x */, mux_undef, mux_bool, do_fine, keepdc, noclkinv, replace_const_cells_timestamp); + replace_const_cells_timestamp = module->timestamp(); if (did_something) design->scratchpad_set_bool("opt.did_something", true); } while (did_something); - if (!keepdc) - replace_const_cells(design, module, true /* consume_x */, mux_undef, mux_bool, do_fine, keepdc, noclkinv); + if (!keepdc) { + module->next_timestamp(); + replace_const_cells(design, module, true /* consume_x */, mux_undef, mux_bool, do_fine, keepdc, noclkinv, replace_const_cells_consume_x_timestamp); + replace_const_cells_consume_x_timestamp = module->timestamp(); + } if (did_something) design->scratchpad_set_bool("opt.did_something", true); } while (did_something); diff --git a/passes/opt/opt_hier.cc b/passes/opt/opt_hier.cc index 5c3b09b31..751f6b8a1 100644 --- a/passes/opt/opt_hier.cc +++ b/passes/opt/opt_hier.cc @@ -422,6 +422,11 @@ struct OptHierPass : Pass { { log_header(d, "Executing OPT_HIER pass.\n"); + // TODO: This pass breaks in signorm + // since rewrite_sigspecs won't rewrite persistent sigmap + // and adding that rewrite is a performance risk + d->sigNormalize(false); + size_t argidx; for (argidx = 1; argidx < args.size(); argidx++) { break; diff --git a/passes/opt/opt_merge.cc b/passes/opt/opt_merge.cc index a6121b268..275822845 100644 --- a/passes/opt/opt_merge.cc +++ b/passes/opt/opt_merge.cc @@ -497,7 +497,7 @@ struct OptMergeWorker }; struct OptMergePass : public Pass { - OptMergePass() : Pass("opt_merge", "consolidate identical cells") { } + OptMergePass() : Pass("opt_merge_old", "consolidate identical cells") { } void help() override { // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| diff --git a/passes/opt/opt_merge_inc.cc b/passes/opt/opt_merge_inc.cc new file mode 100644 index 000000000..37c55ddae --- /dev/null +++ b/passes/opt/opt_merge_inc.cc @@ -0,0 +1,615 @@ +/* + * yosys -- Yosys Open SYnthesis Suite + * + * Copyright (C) 2012 Claire Xenia Wolf + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "kernel/register.h" +#include "kernel/ffinit.h" +#include "kernel/sigtools.h" +#include "kernel/log.h" +#include "kernel/celltypes.h" +#include "libs/sha1/sha1.h" +#include +#include +#include +#include +#include +#include + + +USING_YOSYS_NAMESPACE +PRIVATE_NAMESPACE_BEGIN + +template +inline Hasher hash_pair(const T &t, const U &u) { return hash_ops>::hash(t, u); } + +struct OptMergeIncWorker +{ + RTLIL::Design *design; + RTLIL::Module *module; + SigMap assign_map; + FfInitVals initvals; + bool mode_share_all; + + CellTypes ct; + int total_count; + + static Hasher hash_pmux_in(const SigSpec& sig_s, const SigSpec& sig_b, Hasher h) + { + int s_width = GetSize(sig_s); + int width = GetSize(sig_b) / s_width; + + hashlib::commutative_hash comm; + for (int i = 0; i < s_width; i++) + comm.eat(hash_pair(sig_s[i], sig_b.extract(i*width, width))); + + return comm.hash_into(h); + } + + static void sort_pmux_conn(dict &conn) + { + SigSpec sig_s = conn.at(ID::S); + SigSpec sig_b = conn.at(ID::B); + + int s_width = GetSize(sig_s); + int width = GetSize(sig_b) / s_width; + + vector> sb_pairs; + for (int i = 0; i < s_width; i++) + sb_pairs.push_back(pair(sig_s[i], sig_b.extract(i*width, width))); + + std::sort(sb_pairs.begin(), sb_pairs.end()); + + conn[ID::S] = SigSpec(); + conn[ID::B] = SigSpec(); + + for (auto &it : sb_pairs) { + conn[ID::S].append(it.first); + conn[ID::B].append(it.second); + } + } + + Hasher hash_cell_inputs(const RTLIL::Cell *cell, Hasher h) const + { + // TODO: when implemented, use celltypes to match: + // (builtin || stdcell) && (unary || binary) && symmetrical + if (cell->type.in(ID($and), ID($or), ID($xor), ID($xnor), ID($add), ID($mul), + ID($logic_and), ID($logic_or), ID($_AND_), ID($_OR_), ID($_XOR_))) { + hashlib::commutative_hash comm; + comm.eat((cell->getPort(ID::A))); + comm.eat((cell->getPort(ID::B))); + h = comm.hash_into(h); + } else if (cell->type.in(ID($reduce_xor), ID($reduce_xnor))) { + SigSpec a = (cell->getPort(ID::A)); + a.sort(); + h = a.hash_into(h); + } else if (cell->type.in(ID($reduce_and), ID($reduce_or), ID($reduce_bool))) { + SigSpec a = (cell->getPort(ID::A)); + a.sort_and_unify(); + h = a.hash_into(h); + } else if (cell->type == ID($pmux)) { + SigSpec sig_s = (cell->getPort(ID::S)); + SigSpec sig_b = (cell->getPort(ID::B)); + h = hash_pmux_in(sig_s, sig_b, h); + h = (cell->getPort(ID::A)).hash_into(h); + } else { + hashlib::commutative_hash comm; + for (const auto& [port, sig] : cell->connections()) { + if (cell->output(port)) + continue; + comm.eat(hash_pair(port, (sig))); + } + h = comm.hash_into(h); + if (cell->is_builtin_ff()) + h = initvals(cell->getPort(ID::Q)).hash_into(h); + } + return h; + } + + static Hasher hash_cell_parameters(const RTLIL::Cell *cell, Hasher h) + { + hashlib::commutative_hash comm; + for (const auto& param : cell->parameters) { + comm.eat(param); + } + return comm.hash_into(h); + } + + Hasher hash_cell_function(const RTLIL::Cell *cell, Hasher h) const + { + h.eat(cell->type); + h = hash_cell_inputs(cell, h); + h = hash_cell_parameters(cell, h); + return h; + } + + bool compare_cell_parameters_and_connections(const RTLIL::Cell *cell1, const RTLIL::Cell *cell2) const + { + if (cell1 == cell2) return true; + if (cell1->type != cell2->type) return false; + + if (cell1->parameters != cell2->parameters) + return false; + + if (cell1->connections_.size() != cell2->connections_.size()) + return false; + for (const auto &it : cell1->connections_) + if (!cell2->connections_.count(it.first)) + return false; + + decltype(Cell::connections_) conn1, conn2; + conn1.reserve(cell1->connections_.size()); + conn2.reserve(cell1->connections_.size()); + + for (const auto &it : cell1->connections_) { + if (cell1->output(it.first)) { + if (it.first == ID::Q && cell1->is_builtin_ff()) { + // For the 'Q' output of state elements, + // use the (* init *) attribute value + conn1[it.first] = initvals(it.second); + conn2[it.first] = initvals(cell2->getPort(it.first)); + } + else { + conn1[it.first] = RTLIL::SigSpec(); + conn2[it.first] = RTLIL::SigSpec(); + } + } + else { + conn1[it.first] = (it.second); + conn2[it.first] = (cell2->getPort(it.first)); + } + } + + if (cell1->type.in(ID($and), ID($or), ID($xor), ID($xnor), ID($add), ID($mul), + ID($logic_and), ID($logic_or), ID($_AND_), ID($_OR_), ID($_XOR_))) { + if (conn1.at(ID::A) < conn1.at(ID::B)) { + std::swap(conn1[ID::A], conn1[ID::B]); + } + if (conn2.at(ID::A) < conn2.at(ID::B)) { + std::swap(conn2[ID::A], conn2[ID::B]); + } + } else + if (cell1->type.in(ID($reduce_xor), ID($reduce_xnor))) { + conn1[ID::A].sort(); + conn2[ID::A].sort(); + } else + if (cell1->type.in(ID($reduce_and), ID($reduce_or), ID($reduce_bool))) { + conn1[ID::A].sort_and_unify(); + conn2[ID::A].sort_and_unify(); + } else + if (cell1->type == ID($pmux)) { + sort_pmux_conn(conn1); + sort_pmux_conn(conn2); + } + + return conn1 == conn2; + } + + bool has_dont_care_initval(const RTLIL::Cell *cell) + { + if (!cell->is_builtin_ff()) + return false; + + return !initvals(cell->getPort(ID::Q)).is_fully_def(); + } + + OptMergeIncWorker(RTLIL::Design *design, RTLIL::Module *module, bool mode_nomux, bool mode_share_all, bool mode_keepdc) : + design(design), module(module), mode_share_all(mode_share_all) + { + total_count = 0; + ct.setup_internals(); + ct.setup_internals_mem(); + ct.setup_stdcells(); + ct.setup_stdcells_mem(); + + if (mode_nomux) { + ct.cell_types.erase(ID($mux)); + ct.cell_types.erase(ID($pmux)); + } + + ct.cell_types.erase(ID($tribuf)); + ct.cell_types.erase(ID($_TBUF_)); + ct.cell_types.erase(ID($anyseq)); + ct.cell_types.erase(ID($anyconst)); + ct.cell_types.erase(ID($allseq)); + ct.cell_types.erase(ID($allconst)); + ct.cell_types.erase(ID($connect)); + ct.cell_types.erase(ID($input_port)); + + log("Finding identical cells in module `%s'.\n", module->name); + assign_map.set(module); + + initvals.set(&assign_map, module); + + + // We keep a set of known cells. They're hashed with our hash_cell_function + // and compared with our compare_cell_parameters_and_connections. + // Both need to capture OptMergeIncWorker to access initvals + struct CellPtrHash { + const OptMergeIncWorker& worker; + CellPtrHash(const OptMergeIncWorker& w) : worker(w) {} + std::size_t operator()(const Cell* c) const { + return (std::size_t)worker.hash_cell_function(c, Hasher()).yield(); + } + }; + struct CellPtrEqual { + const OptMergeIncWorker& worker; + CellPtrEqual(const OptMergeIncWorker& w) : worker(w) {} + bool operator()(const Cell* lhs, const Cell* rhs) const { + return worker.compare_cell_parameters_and_connections(lhs, rhs); + } + }; + // std::unordered_set< + // RTLIL::Cell*, + // CellPtrHash, + // CellPtrEqual> known_cells (0, CellPtrHash(*this), CellPtrEqual(*this)); + + dict hashes; + dict first_with_hash; + dict> more_with_hash; + + auto forget_cell = [&](Cell * cell) { + auto found = hashes.find(cell); + if (found != hashes.end()) { + auto found_first = first_with_hash.find(found->second); + log_assert(found_first != first_with_hash.end()); + auto found_more = more_with_hash.find(found->second); + if (found_more != more_with_hash.end()) { + found_more->second.erase(cell); + found_first->second = *found_more->second.begin(); + if (found_more->second.size() < 2) + more_with_hash.erase(found_more); + } + + // auto found_first = first_with_hash.find(found->second); + // log_assert(found_first != first_with_hash.end()); + // if (found_first->second == cell) { + // auto found_more = more_with_hash.find(found->second); + // if (found_more != more_with_hash.end()) { + // log_assert(!found_more->second.empty()); + // found_first->second = found_more->second.pop(); + // if (found_more->second.empty()) + // more_with_hash.erase(found_more); + + // } else { + // first_with_hash.erase(found_first); + // } + + // } else { + // auto found_more = more_with_hash.find(found->second); + // if (found_more != more_with_hash.end()) { + // found_more->second.erase(cell); + // if (found_more->second.empty()) + // more_with_hash.erase(found_more); + // } + // } + } + }; + + auto remember_cell = [&](Cell * cell, uint32_t hash) -> bool { + hashes[cell] = hash; + auto [it, inserted] = first_with_hash.emplace(hash, cell); + if (!inserted) { + auto &more = more_with_hash[hash]; + if (more.empty()) + more.emplace(it->second); + more.emplace(cell); + } + return !inserted; + }; + + int timestamp = INT_MIN; + + bool did_something = true; + // A cell may have to go through a lot of collisions if the hash + // function is performing poorly, but it's a symptom of something bad + // beyond the user's control. + + bool first = true; + while (did_something) + { + std::vector cells; + + if (timestamp == INT_MIN) { + timestamp = module->next_timestamp(); + cells.reserve(module->cells().size()); + for (auto cell : module->cells()) { + if (!design->selected(module, cell)) + continue; + if (cell->type.in(ID($meminit), ID($meminit_v2), ID($mem), ID($mem_v2))) { + // Ignore those for performance: meminit can have an excessively large port, + // mem can have an excessively large parameter holding the init data + continue; + } + if (cell->type == ID($scopeinfo)) + continue; + if (mode_keepdc && has_dont_care_initval(cell)) + continue; + if (!cell->known()) + continue; + if (!mode_share_all && !ct.cell_known(cell->type)) + continue; + + cells.push_back(cell); + } + } else { + int next = module->next_timestamp(); + // idict pending; + // for (auto cell : module->dirty_cells(timestamp)) + // pending(cell); + // std::vector *> fanouts; + // int i = 0; + // while (i < GetSize(pending)) { + // Cell * cell = pending[i]; + pool pending; + for (auto cell : module->dirty_cells(timestamp)) { + if (!design->selected(module, cell)) + continue; + if (cell->type.in(ID($meminit), ID($meminit_v2), ID($mem), ID($mem_v2))) { + // Ignore those for performance: meminit can have an excessively large port, + // mem can have an excessively large parameter holding the init data + continue; + } + if (cell->type == ID($scopeinfo)) + continue; + if (mode_keepdc && has_dont_care_initval(cell)) + continue; + if (!cell->known()) + continue; + if (!mode_share_all && !ct.cell_known(cell->type)) + continue; + + + cells.push_back(cell); + } + timestamp = next; + } + log("scanning %d cells\n", GetSize(cells)); + + did_something = false; + + if (!first) + for (auto cell : cells) + forget_cell(cell); + first = false; + + + pool buckets; + + for (auto cell : cells) { + uint32_t hash = hash_cell_function(cell, Hasher()).yield(); + if (remember_cell(cell, hash)) + buckets.emplace(hash); + } + + std::vector> pairs; + pool removed; + + log("scanning %d/%d/%d buckets\n", GetSize(buckets), GetSize(more_with_hash), GetSize(first_with_hash)); + for (auto hash : buckets) { + auto more = more_with_hash.at(hash); + + for (int i = 0; i < GetSize(more); ++i) { + for (int j = i + 1; j < GetSize(more); ++j) { + Cell *other_cell = *more.element(j); + if (removed.count(other_cell)) + continue; + Cell *cell = *more.element(i); + if (removed.count(cell)) + break; + + if (!compare_cell_parameters_and_connections(cell, other_cell)) + continue; + + if (cell->has_keep_attr()) { + if (other_cell->has_keep_attr()) + continue; + std::swap(cell, other_cell); + } + + removed.insert(cell); + pairs.emplace_back(other_cell, cell); + } + + } + } + + for (auto cell : removed) + forget_cell(cell); + + int iter_count = 0; + + for (auto [other_cell, cell] : pairs) { + did_something = true; + log_debug(" Cell `%s' is identical to cell `%s'.\n", cell->name, other_cell->name); + + for (auto &[port, sig] : cell->connections()) { + if (cell->output(port)) { + // TODO why was this removed before? + RTLIL::SigSpec other_sig = other_cell->getPort(port); + Const init = initvals(other_sig); + initvals.remove_init(sig); + initvals.remove_init(other_sig); + module->connect(sig, other_cell->getPort(port)); + assign_map.add(sig, other_sig); + initvals.set_init(other_sig, init); + } + } + + log_debug(" Removing %s cell `%s' from module `%s'.\n", cell->type, cell->name, module->name); + module->remove(cell); + total_count++; + iter_count++; + } + log("removed %d cells\n", iter_count); + + + // hashes[cell] = hash; + + // auto [found, inserted] = first_with_hash.emplace(hash, cell); + + // if (inserted) { + // hashes.emplace(cell, hash); + // continue; + // } + + + // // if (check_candidate(found->second)) + // // continue; + + // auto found_more = more_with_hash[hash]; + + // // for (auto other_cell : found_more) { + // // if (check_candidate(other_cell)) + // // continue; + // // } + + // found_more.emplace(cell); + // hashes.emplace(cell, hash); + + // // if (compare_cell_parameters_and_connections(found->second, cell)) { + + // // } + + + // auto check_candidate = [&](Cell *other_cell) -> bool { + // if (!compare_cell_parameters_and_connections(other_cell, cell)) + // return false; + + // if (cell->has_keep_attr()) + // if (other_cell->has_keep_attr()) + // return false; + + // forget_cell(cell); + + + + // return true; + + // }; + + // if (!check_candidate(found->second)) { + // } + + + // for () + + + // auto [cell_in_map, inserted] = known_cells.insert(cell); + // if (!inserted) { + // // We've failed to insert since we already have an equivalent cell + // Cell* other_cell = *cell_in_map; + // if (cell->has_keep_attr()) { + // if (other_cell->has_keep_attr()) + // continue; + // known_cells.erase(other_cell); + // known_cells.insert(cell); + // std::swap(other_cell, cell); + // } + + // did_something = true; + // log_debug(" Cell `%s' is identical to cell `%s'.\n", cell->name, other_cell->name); + // for (auto &it : cell->connections()) { + // if (cell->output(it.first)) { + // RTLIL::SigSpec other_sig = other_cell->getPort(it.first); + // log_debug(" Redirecting output %s: %s = %s\n", it.first, + // log_signal(it.second), log_signal(other_sig)); + // Const init = initvals(other_sig); + // initvals.remove_init(it.second); + // initvals.remove_init(other_sig); + // module->connect(RTLIL::SigSig(it.second, other_sig)); + // assign_map.add(it.second, other_sig); + // initvals.set_init(other_sig, init); + // } + // } + // log_debug(" Removing %s cell `%s' from module `%s'.\n", cell->type, cell->name, module->name); + // module->remove(cell); + // total_count++; + // } + + } + + log_suppressed(); + } +}; + +struct OptMergeIncPass : public Pass { + OptMergeIncPass() : Pass("opt_merge", "consolidate identical cells") { } + void help() override + { + // |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---| + log("\n"); + log(" opt_merge_inc [options] [selection]\n"); + log("\n"); + log("This pass identifies cells with identical type and input signals. Such cells\n"); + log("are then merged to one cell.\n"); + log("\n"); + log(" -nomux\n"); + log(" Do not merge MUX cells.\n"); + log("\n"); + log(" -share_all\n"); + log(" Operate on all cell types, not just built-in types.\n"); + log("\n"); + log(" -keepdc\n"); + log(" Do not merge flipflops with don't-care bits in their initial value.\n"); + log("\n"); + } + void execute(std::vector args, RTLIL::Design *design) override + { + log_header(design, "Executing OPT_MERGE_INC pass (detect identical cells).\n"); + + bool mode_nomux = false; + bool mode_share_all = false; + bool mode_keepdc = false; + + size_t argidx; + for (argidx = 1; argidx < args.size(); argidx++) { + std::string arg = args[argidx]; + if (arg == "-nomux") { + mode_nomux = true; + continue; + } + if (arg == "-share_all") { + mode_share_all = true; + continue; + } + if (arg == "-keepdc") { + mode_keepdc = true; + continue; + } + break; + } + extra_args(args, argidx, design); + + design->sigNormalize(true); + + int total_count = 0; + for (auto module : design->selected_modules()) { + OptMergeIncWorker worker(design, module, mode_nomux, mode_share_all, mode_keepdc); + total_count += worker.total_count; + } + + + // design->sigNormalize(false); + + if (total_count) + design->scratchpad_set_bool("opt.did_something", true); + log("Removed a total of %d cells.\n", total_count); + } +} OptMergeIncPass; + +PRIVATE_NAMESPACE_END diff --git a/passes/opt/peepopt.cc b/passes/opt/peepopt.cc index fa7cf74a0..0a067968d 100644 --- a/passes/opt/peepopt.cc +++ b/passes/opt/peepopt.cc @@ -101,11 +101,12 @@ struct PeepoptPass : public Pass { { did_something = true; + SigMap sigmap(module); while (did_something) { did_something = false; - peepopt_pm pm(module); + peepopt_pm pm(module, &sigmap); pm.setup(module->selected_cells()); diff --git a/passes/opt/wreduce.cc b/passes/opt/wreduce.cc index 359c76d42..23b3ffb61 100644 --- a/passes/opt/wreduce.cc +++ b/passes/opt/wreduce.cc @@ -147,7 +147,8 @@ struct WreduceWorker SigSpec sig_d = mi.sigmap(cell->getPort(ID::D)); SigSpec sig_q = mi.sigmap(cell->getPort(ID::Q)); bool has_reset = false; - Const initval = initvals(sig_q), rst_value; + Const rst_value; + std::vector initval = initvals(sig_q).to_bits(); int width_before = GetSize(sig_q); @@ -165,12 +166,16 @@ struct WreduceWorker bool zero_ext = sig_d[GetSize(sig_d)-1] == State::S0; bool sign_ext = !zero_ext; + if (mi.auto_reload_module) + mi.reload_module(); + for (int i = GetSize(sig_q)-1; i >= 0; i--) { if (zero_ext && sig_d[i] == State::S0 && (initval[i] == State::S0 || (!config->keepdc && initval[i] == State::Sx)) && (!has_reset || i >= GetSize(rst_value) || rst_value[i] == State::S0 || (!config->keepdc && rst_value[i] == State::Sx))) { module->connect(sig_q[i], State::S0); initvals.remove_init(sig_q[i]); + initval.erase(initval.begin() + i); sig_d.remove(i); sig_q.remove(i); continue; @@ -180,6 +185,7 @@ struct WreduceWorker (!has_reset || i >= GetSize(rst_value) || (rst_value[i] == rst_value[i-1] && (!config->keepdc || rst_value[i] != State::Sx)))) { module->connect(sig_q[i], sig_q[i-1]); initvals.remove_init(sig_q[i]); + initval.erase(initval.begin() + i); sig_d.remove(i); sig_q.remove(i); continue; @@ -190,6 +196,7 @@ struct WreduceWorker return; if (!info->is_output && GetSize(info->ports) == 1 && !keep_bits.count(mi.sigmap(sig_q[i]))) { initvals.remove_init(sig_q[i]); + initval.erase(initval.begin() + i); sig_d.remove(i); sig_q.remove(i); zero_ext = false; @@ -229,6 +236,7 @@ struct WreduceWorker cell->setPort(ID::D, sig_d); cell->setPort(ID::Q, sig_q); + initvals.set_init(cell->getPort(ID::Q), initval); cell->fixup_parameters(); } @@ -571,6 +579,9 @@ struct WreducePass : public Pass { } extra_args(args, argidx, design); + // TODO Disabled signorm because swap_names breaks fanout logic + design->sigNormalize(false); + for (auto module : design->selected_modules()) { if (module->has_processes_warn()) diff --git a/passes/pmgen/generate.h b/passes/pmgen/generate.h index 85e208774..959c0f981 100644 --- a/passes/pmgen/generate.h +++ b/passes/pmgen/generate.h @@ -85,7 +85,8 @@ void generate_pattern(std::function)> run, const if (timeout++ > 10000) log_error("pmgen generator is stuck: 10000 iterations with no matching module generated.\n"); - pm matcher(mod, mod->cells()); + SigMap sigmap(mod); + pm matcher(mod, &sigmap, mod->cells()); matcher.rng(1); matcher.rngseed += modcnt; diff --git a/passes/pmgen/pmgen.py b/passes/pmgen/pmgen.py index d2adbbdd9..38772964d 100644 --- a/passes/pmgen/pmgen.py +++ b/passes/pmgen/pmgen.py @@ -361,7 +361,7 @@ with open(outfile, "w") as f: print("struct {}_pm {{".format(prefix), file=f) print(" Module *module;", file=f) - print(" SigMap sigmap;", file=f) + print(" SigMap *sigmap;", file=f) print(" std::function on_accept;", file=f) print(" bool setup_done;", file=f) print(" bool generate_mode;", file=f) @@ -423,7 +423,7 @@ with open(outfile, "w") as f: print("", file=f) print(" void add_siguser(const SigSpec &sig, Cell *cell) {", file=f) - print(" for (auto bit : sigmap(sig)) {", file=f) + print(" for (auto bit : (*sigmap)(sig)) {", file=f) print(" if (bit.wire == nullptr) continue;", file=f) print(" sigusers[bit].insert(cell);", file=f) print(" }", file=f) @@ -453,12 +453,12 @@ with open(outfile, "w") as f: print(" SigSpec port(Cell *cell, IdString portname) {", file=f) print(" try {", file=f) - print(" return sigmap(cell->getPort(portname));", file=f) + print(" return (*sigmap)(cell->getPort(portname));", file=f) print(" } catch(std::out_of_range&) { log_error(\"Accessing non existing port %s\\n\",portname); }", file=f) print(" }", file=f) print("", file=f) print(" SigSpec port(Cell *cell, IdString portname, const SigSpec& defval) {", file=f) - print(" return sigmap(cell->connections_.at(portname, defval));", file=f) + print(" return (*sigmap)(cell->connections_.at(portname, defval));", file=f) print(" }", file=f) print("", file=f) @@ -475,21 +475,21 @@ with open(outfile, "w") as f: print(" int nusers(const SigSpec &sig) {", file=f) print(" pool users;", file=f) - print(" for (auto bit : sigmap(sig))", file=f) + print(" for (auto bit : (*sigmap)(sig))", file=f) print(" for (auto user : sigusers[bit])", file=f) print(" users.insert(user);", file=f) print(" return GetSize(users);", file=f) print(" }", file=f) print("", file=f) - print(" {}_pm(Module *module, const vector &cells) :".format(prefix), file=f) - print(" module(module), sigmap(module), setup_done(false), generate_mode(false), rngseed(12345678) {", file=f) + print(" {}_pm(Module *module, SigMap *map, const vector &cells) :".format(prefix), file=f) + print(" module(module), sigmap(map), setup_done(false), generate_mode(false), rngseed(12345678) {", file=f) print(" setup(cells);", file=f) print(" }", file=f) print("", file=f) - print(" {}_pm(Module *module) :".format(prefix), file=f) - print(" module(module), sigmap(module), setup_done(false), generate_mode(false), rngseed(12345678) {", file=f) + print(" {}_pm(Module *module, SigMap *map) :".format(prefix), file=f) + print(" module(module), sigmap(map), setup_done(false), generate_mode(false), rngseed(12345678) {", file=f) print(" }", file=f) print("", file=f) diff --git a/passes/pmgen/test_pmgen.cc b/passes/pmgen/test_pmgen.cc index f6d6a3f93..ec6cb2929 100644 --- a/passes/pmgen/test_pmgen.cc +++ b/passes/pmgen/test_pmgen.cc @@ -163,8 +163,10 @@ struct TestPmgenPass : public Pass { } extra_args(args, argidx, design); - for (auto module : design->selected_modules()) - while (test_pmgen_pm(module, module->selected_cells()).run_reduce(reduce_chain)) {} + for (auto module : design->selected_modules()) { + SigMap sigmap(module); + while (test_pmgen_pm(module, &sigmap, module->selected_cells()).run_reduce(reduce_chain)) {} + } } void execute_reduce_tree(std::vector args, RTLIL::Design *design) @@ -182,8 +184,10 @@ struct TestPmgenPass : public Pass { } extra_args(args, argidx, design); - for (auto module : design->selected_modules()) - test_pmgen_pm(module, module->selected_cells()).run_reduce(reduce_tree); + for (auto module : design->selected_modules()) { + SigMap sigmap(module); + test_pmgen_pm(module, &sigmap, module->selected_cells()).run_reduce(reduce_tree); + } } void execute_eqpmux(std::vector args, RTLIL::Design *design) @@ -201,8 +205,10 @@ struct TestPmgenPass : public Pass { } extra_args(args, argidx, design); - for (auto module : design->selected_modules()) - test_pmgen_pm(module, module->selected_cells()).run_eqpmux(opt_eqpmux); + for (auto module : design->selected_modules()) { + SigMap sigmap(module); + test_pmgen_pm(module, &sigmap, module->selected_cells()).run_eqpmux(opt_eqpmux); + } } void execute_generate(std::vector args, RTLIL::Design *design) diff --git a/passes/sat/cutpoint.cc b/passes/sat/cutpoint.cc index 1a68776ff..ae90f5151 100644 --- a/passes/sat/cutpoint.cc +++ b/passes/sat/cutpoint.cc @@ -79,6 +79,9 @@ struct CutpointPass : public Pass { } extra_args(args, argidx, design); + // TODO Disabled signorm because swap_names breaks fanout logic + design->sigNormalize(false); + if (flag_blackbox) { if (!design->full_selection()) log_cmd_error("This command only operates on fully selected designs!\n"); diff --git a/passes/sat/freduce.cc b/passes/sat/freduce.cc index 4b0669c25..7abc661c1 100644 --- a/passes/sat/freduce.cc +++ b/passes/sat/freduce.cc @@ -827,6 +827,8 @@ struct FreducePass : public Pass { break; } extra_args(args, argidx, design); + // TODO disable signorm due to rewrite_sigspecs assert + design->sigNormalize(false); int bitcount = 0; for (auto module : design->selected_modules()) { diff --git a/passes/techmap/abc9_ops.cc b/passes/techmap/abc9_ops.cc index dda3858da..7f0490c59 100644 --- a/passes/techmap/abc9_ops.cc +++ b/passes/techmap/abc9_ops.cc @@ -1893,6 +1893,10 @@ struct Abc9OpsPass : public Pass { } extra_args(args, argidx, design); + // TODO Disabled signorm because swap_names breaks fanout logic + // TODO disable signorm due to rewrite_sigspecs assert + design->sigNormalize(false); + if (!valid) log_cmd_error("At least one of -check, -break_scc, -prep_{delays,xaiger,dff[123],lut,box}, -write_{lut,box}, -reintegrate, -{replace,restore}_zbufs must be specified.\n"); diff --git a/passes/techmap/bufnorm.cc b/passes/techmap/bufnorm.cc index 123687255..aad6dac87 100644 --- a/passes/techmap/bufnorm.cc +++ b/passes/techmap/bufnorm.cc @@ -94,8 +94,11 @@ struct BufnormPass : public Pass { log(" -update\n"); log(" Enter 'buffered-normalized mode' and (re-)normalize.\n"); log("\n"); + log(" -signorm\n"); + log(" Enter 'signal-normalized mode' and (re-)normalize.\n"); + log("\n"); log(" -reset\n"); - log(" Leave 'buffered-normalized mode' without changing the netlist.\n"); + log(" Leave '{buffered,signal}-normalized mode' without changing the netlist.\n"); log("\n"); } void execute(std::vector args, RTLIL::Design *design) override @@ -116,6 +119,7 @@ struct BufnormPass : public Pass { bool conn_mode = false; bool update_mode = false; + bool signorm_mode = false; bool reset_mode = false; bool got_non_update_reset_opt = false; @@ -198,6 +202,10 @@ struct BufnormPass : public Pass { update_mode = true; continue; } + if (arg == "-signorm") { + signorm_mode = true; + continue; + } if (arg == "-reset") { reset_mode = true; continue; @@ -221,6 +229,9 @@ struct BufnormPass : public Pass { if (update_mode && got_non_update_reset_opt) log_cmd_error("Option -update can't be mixed with other options.\n"); + if (signorm_mode && got_non_update_reset_opt) + log_cmd_error("Option -signorm can't be mixed with other options.\n"); + if (reset_mode && got_non_update_reset_opt) log_cmd_error("Option -reset can't be mixed with other options.\n"); @@ -229,8 +240,14 @@ struct BufnormPass : public Pass { return; } + if (signorm_mode) { + design->sigNormalize(); + return; + } + if (reset_mode) { design->bufNormalize(false); + design->sigNormalize(false); return; } diff --git a/passes/techmap/clkbufmap.cc b/passes/techmap/clkbufmap.cc index 7003c6656..c496e89b9 100644 --- a/passes/techmap/clkbufmap.cc +++ b/passes/techmap/clkbufmap.cc @@ -110,6 +110,9 @@ struct ClkbufmapPass : public Pass { extra_args(args, argidx, design); } + // TODO Disabled signorm because swap_names breaks fanout logic + design->sigNormalize(false); + if (buf_celltype.empty() && inpad_celltype.empty()) log_error("Either the -buf option or -inpad option is required.\n"); diff --git a/passes/techmap/constmap.cc b/passes/techmap/constmap.cc index 1df50e47d..225bca81b 100644 --- a/passes/techmap/constmap.cc +++ b/passes/techmap/constmap.cc @@ -74,6 +74,9 @@ struct ConstmapPass : public Pass { if (celltype.empty()) log_cmd_error("Missing required option -cell.\n"); + // TODO disable signorm due to rewrite_sigspecs assert + design->sigNormalize(false); + if (design->has(celltype)) { Module *existing = design->module(celltype); bool has_port = false; diff --git a/passes/techmap/hilomap.cc b/passes/techmap/hilomap.cc index c1b947221..0cd81ded4 100644 --- a/passes/techmap/hilomap.cc +++ b/passes/techmap/hilomap.cc @@ -105,6 +105,9 @@ struct HilomapPass : public Pass { } extra_args(args, argidx, design); + // TODO disable signorm due to rewrite_sigspecs assert + design->sigNormalize(false); + for (auto mod : design->selected_modules()) { module = mod; diff --git a/passes/techmap/iopadmap.cc b/passes/techmap/iopadmap.cc index d929de300..66296db30 100644 --- a/passes/techmap/iopadmap.cc +++ b/passes/techmap/iopadmap.cc @@ -186,6 +186,9 @@ struct IopadmapPass : public Pass { } extra_args(args, argidx, design); + // TODO Disabled signorm because swap_names breaks fanout logic + design->sigNormalize(false); + if (!inpad_portname_pad.empty()) ignore.insert(make_pair(RTLIL::escape_id(inpad_celltype), RTLIL::escape_id(inpad_portname_pad))); if (!outpad_portname_pad.empty()) diff --git a/passes/techmap/techmap.cc b/passes/techmap/techmap.cc index cf7ce56e2..96b1cf11c 100644 --- a/passes/techmap/techmap.cc +++ b/passes/techmap/techmap.cc @@ -150,6 +150,7 @@ struct TechmapWorker log("\n"); if (autoproc_mode) { Pass::call_on_module(tpl->design, tpl, "proc"); + tpl->design->sigNormalize(false); log_assert(GetSize(tpl->processes) == 0); } else log_error("Technology map yielded processes -> this is not supported (use -autoproc to run 'proc' automatically).\n"); @@ -551,6 +552,7 @@ struct TechmapWorker log("Running \"%s\" on wrapper %s.\n", cmd_string, log_id(extmapper_module)); mkdebug.on(); Pass::call_on_module(extmapper_design, extmapper_module, cmd_string); + extmapper_design->sigNormalize(false); log_continue = true; } } @@ -850,6 +852,7 @@ struct TechmapWorker } Pass::call_on_module(map, tpl, cmd_string); + map->sigNormalize(false); log_assert(!strncmp(q, "_TECHMAP_DO_", 12)); std::string new_name = data.wire->name.substr(0, q-p) + "_TECHMAP_DONE_" + data.wire->name.substr(q-p+12); @@ -1144,6 +1147,8 @@ struct TechmapPass : public Pass { log_header(design, "Executing TECHMAP pass (map to technology primitives).\n"); log_push(); + // TODO not sure why signorm breaks on us here yet + design->sigNormalize(false); TechmapWorker worker; simplemap_get_mappers(worker.simplemap_mappers); diff --git a/techlibs/common/simlib.v b/techlibs/common/simlib.v index e0fb9fbfa..3f34bfd22 100644 --- a/techlibs/common/simlib.v +++ b/techlibs/common/simlib.v @@ -1976,7 +1976,7 @@ endmodule // -------------------------------------------------------- //* group spec -module \$specrule (EN_SRC, EN_DST, SRC, DST); +module \$specrule (SRC_EN, DST_EN, SRC, DST); parameter TYPE = ""; parameter T_LIMIT = 0; @@ -1991,7 +1991,7 @@ parameter SRC_POL = 0; parameter DST_PEN = 0; parameter DST_POL = 0; -input EN_SRC, EN_DST; +input SRC_EN, DST_EN; input [SRC_WIDTH-1:0] SRC; input [DST_WIDTH-1:0] DST; diff --git a/techlibs/gowin/cells_sim.v b/techlibs/gowin/cells_sim.v index 8d4303800..288d769d3 100644 --- a/techlibs/gowin/cells_sim.v +++ b/techlibs/gowin/cells_sim.v @@ -121,7 +121,7 @@ module MUX2_LUT5 (O, I0, I1, S0); (S0 => O) = (486, 680); endspecify - MUX2 mux2_lut5 (O, I0, I1, S0); + MUX2 mux2_lut5 (.O(O), .I0(I0), .I1(I1), .S0(S0)); endmodule module MUX2_LUT6 (O, I0, I1, S0); @@ -135,7 +135,7 @@ module MUX2_LUT6 (O, I0, I1, S0); (S0 => O) = (478, 723); endspecify - MUX2 mux2_lut6 (O, I0, I1, S0); + MUX2 mux2_lut6 (.O(O), .I0(I0), .I1(I1), .S0(S0)); endmodule module MUX2_LUT7 (O, I0, I1, S0); @@ -149,7 +149,7 @@ module MUX2_LUT7 (O, I0, I1, S0); (S0 => O) = (478, 723); endspecify - MUX2 mux2_lut7 (O, I0, I1, S0); + MUX2 mux2_lut7 (.O(O), .I0(I0), .I1(I1), .S0(S0)); endmodule module MUX2_LUT8 (O, I0, I1, S0); @@ -163,7 +163,7 @@ module MUX2_LUT8 (O, I0, I1, S0); (S0 => O) = (478, 723); endspecify - MUX2 mux2_lut8 (O, I0, I1, S0); + MUX2 mux2_lut8 (.O(O), .I0(I0), .I1(I1), .S0(S0)); endmodule (* abc9_flop, lib_whitebox *) diff --git a/techlibs/ice40/ice40_dsp.cc b/techlibs/ice40/ice40_dsp.cc index 995cdb97e..8c145f6f0 100644 --- a/techlibs/ice40/ice40_dsp.cc +++ b/techlibs/ice40/ice40_dsp.cc @@ -311,8 +311,13 @@ struct Ice40DspPass : public Pass { } extra_args(args, argidx, design); - for (auto module : design->selected_modules()) - ice40_dsp_pm(module, module->selected_cells()).run_ice40_dsp(create_ice40_dsp); + // TODO Disabled signorm because swap_names breaks fanout logic + design->sigNormalize(false); + + for (auto module : design->selected_modules()) { + SigMap sigmap(module); + ice40_dsp_pm(module, &sigmap, module->selected_cells()).run_ice40_dsp(create_ice40_dsp); + } } } Ice40DspPass; diff --git a/techlibs/ice40/ice40_wrapcarry.cc b/techlibs/ice40/ice40_wrapcarry.cc index f62019617..323a8a0d4 100644 --- a/techlibs/ice40/ice40_wrapcarry.cc +++ b/techlibs/ice40/ice40_wrapcarry.cc @@ -48,7 +48,7 @@ void create_ice40_wrapcarry(ice40_wrapcarry_pm &pm) cell->setPort(ID(I0), st.lut->getPort(ID(I0))); auto I3 = st.lut->getPort(ID(I3)); - if (pm.sigmap(CI) == pm.sigmap(I3)) { + if ((*pm.sigmap)(CI) == (*pm.sigmap)(I3)) { cell->setParam(ID(I3_IS_CI), State::S1); I3 = State::Sx; } @@ -108,10 +108,14 @@ struct Ice40WrapCarryPass : public Pass { } extra_args(args, argidx, design); + // TODO Disabled signorm because swap_names breaks fanout logic + design->sigNormalize(false); + for (auto module : design->selected_modules()) { - if (!unwrap) - ice40_wrapcarry_pm(module, module->selected_cells()).run_ice40_wrapcarry(create_ice40_wrapcarry); - else { + if (!unwrap) { + SigMap sigmap(module); + ice40_wrapcarry_pm(module, &sigmap, module->selected_cells()).run_ice40_wrapcarry(create_ice40_wrapcarry); + } else { for (auto cell : module->selected_cells()) { if (cell->type != ID($__ICE40_CARRY_WRAPPER)) continue; diff --git a/techlibs/ice40/synth_ice40.cc b/techlibs/ice40/synth_ice40.cc index 0a4144451..b797a6a08 100644 --- a/techlibs/ice40/synth_ice40.cc +++ b/techlibs/ice40/synth_ice40.cc @@ -379,6 +379,7 @@ struct SynthIce40Pass : public ScriptPass run("techmap"); else { run("ice40_wrapcarry"); + run("read_verilog " + define + " -icells -lib -specify +/ice40/abc9_model.v"); run("techmap -map +/techmap.v -map +/ice40/arith_map.v"); } run("opt -fast"); @@ -415,7 +416,6 @@ struct SynthIce40Pass : public ScriptPass } if (!noabc) { if (abc9) { - run("read_verilog " + define + " -icells -lib -specify +/ice40/abc9_model.v"); std::string abc9_opts; std::string k = "synth_ice40.abc9.W"; if (active_design && active_design->scratchpad.count(k)) diff --git a/techlibs/microchip/microchip_dsp.cc b/techlibs/microchip/microchip_dsp.cc index df7093bc5..98016814c 100644 --- a/techlibs/microchip/microchip_dsp.cc +++ b/techlibs/microchip/microchip_dsp.cc @@ -89,7 +89,7 @@ void microchip_dsp_pack(microchip_dsp_pm &pm) auto f = [&pm, cell](SigSpec &A, Cell *ff, IdString ceport, IdString rstport, IdString bypass) { // input/output ports SigSpec D = ff->getPort(ID::D); - SigSpec Q = pm.sigmap(ff->getPort(ID::Q)); + SigSpec Q = (*pm.sigmap)(ff->getPort(ID::Q)); if (!A.empty()) A.replace(Q, D); @@ -206,7 +206,7 @@ void microchip_dsp_packC(microchip_dsp_CREG_pm &pm) auto f = [&pm, cell](SigSpec &A, Cell *ff, IdString ceport, IdString rstport, IdString bypass) { // input/output ports SigSpec D = ff->getPort(ID::D); - SigSpec Q = pm.sigmap(ff->getPort(ID::Q)); + SigSpec Q = (*pm.sigmap)(ff->getPort(ID::Q)); if (!A.empty()) A.replace(Q, D); if (rstport != IdString()) { @@ -318,8 +318,11 @@ struct MicrochipDspPass : public Pass { } extra_args(args, argidx, design); + // TODO deduplicate all this noise with xilinx_dsp.cc + for (auto module : design->selected_modules()) { + SigMap sigmap(module); if (design->scratchpad_get_bool("microchip_dsp.multonly")) continue; @@ -333,7 +336,7 @@ struct MicrochipDspPass : public Pass { // check for an accumulator pattern based on whether // a post-adder and PREG are both present AND // if PREG feeds into this post-adder. - microchip_dsp_pm pm(module, module->selected_cells()); + microchip_dsp_pm pm(module, &sigmap, module->selected_cells()); pm.run_microchip_dsp_pack(microchip_dsp_pack); } @@ -346,13 +349,13 @@ struct MicrochipDspPass : public Pass { // PREG of an upstream DSP that had not been visited // yet { - microchip_dsp_CREG_pm pm(module, module->selected_cells()); + microchip_dsp_CREG_pm pm(module, &sigmap, module->selected_cells()); pm.run_microchip_dsp_packC(microchip_dsp_packC); } // Lastly, identify and utilise PCOUT -> PCIN chains { - microchip_dsp_cascade_pm pm(module, module->selected_cells()); + microchip_dsp_cascade_pm pm(module, &sigmap, module->selected_cells()); pm.run_microchip_dsp_cascade(); } } diff --git a/techlibs/quicklogic/ql_dsp_macc.cc b/techlibs/quicklogic/ql_dsp_macc.cc index f0669da6c..f6acb68d3 100644 --- a/techlibs/quicklogic/ql_dsp_macc.cc +++ b/techlibs/quicklogic/ql_dsp_macc.cc @@ -205,8 +205,10 @@ struct QlDspMacc : public Pass { } extra_args(a_Args, argidx, a_Design); - for (auto module : a_Design->selected_modules()) - ql_dsp_macc_pm(module, module->selected_cells()).run_ql_dsp_macc(create_ql_macc_dsp); + for (auto module : a_Design->selected_modules()) { + SigMap sigmap(module); + ql_dsp_macc_pm(module, &sigmap, module->selected_cells()).run_ql_dsp_macc(create_ql_macc_dsp); + } } } QlDspMacc; diff --git a/techlibs/quicklogic/ql_ioff.cc b/techlibs/quicklogic/ql_ioff.cc index 5574ef4a0..de7f5dbd4 100644 --- a/techlibs/quicklogic/ql_ioff.cc +++ b/techlibs/quicklogic/ql_ioff.cc @@ -24,6 +24,9 @@ struct QlIoffPass : public Pass { { log_header(design, "Executing QL_IOFF pass.\n"); + // TODO Disabled signorm because swap_names breaks fanout logic + design->sigNormalize(false); + ModWalker modwalker(design); Module *module = design->top_module(); if (!module) diff --git a/techlibs/xilinx/xilinx_dsp.cc b/techlibs/xilinx/xilinx_dsp.cc index 194b9ac10..2c63da3c8 100644 --- a/techlibs/xilinx/xilinx_dsp.cc +++ b/techlibs/xilinx/xilinx_dsp.cc @@ -64,18 +64,61 @@ static Cell* addDsp(Module *module) { return cell; } -void xilinx_simd_pack(Module *module, const std::vector &selected_cells) +SigPool simd_signals(Module *module, SigMap* sigmap) +{ + SigPool simd_signals; + // Mark representatives of wires that have the attribute + for (auto wire : module->wires()) { + SigSpec reps = (*sigmap)(wire); + log_assert(reps.size() == wire->width); + for (int i = 0; i < reps.size(); i++) { + auto bit = reps[i]; + auto src_bit = SigBit(wire, i); + if (src_bit.is_wire() && src_bit.wire->has_attribute(ID::use_dsp)) { + if (src_bit.wire->get_strpool_attribute(ID::use_dsp).count("simd")) { + simd_signals.add(bit); + } + } + } + } + // Also mark all aliases of those representatives + for (auto wire : module->wires()) { + SigSpec reps = (*sigmap)(wire); + log_assert(reps.size() == wire->width); + for (int i = 0; i < reps.size(); i++) { + auto bit = reps[i]; + auto src_bit = SigBit(wire, i); + if (simd_signals.check(bit)) { + simd_signals.add(src_bit); + } + } + } + // This seems silly, but that's generalized RTLIL for you! + return simd_signals; +} + +bool is_allowed(SigSpec& sig, SigPool& allowed_bits) +{ + for (auto bit : sig.bits()) { + if (!allowed_bits.check(bit)) { + return false; + } + } + return true; +} + +void xilinx_simd_pack(Module *module, SigMap* sigmap, const std::vector &selected_cells) { std::deque simd12_add, simd12_sub; std::deque simd24_add, simd24_sub; + SigPool simds = simd_signals(module, sigmap); + for (auto cell : selected_cells) { if (!cell->type.in(ID($add), ID($sub))) continue; SigSpec Y = cell->getPort(ID::Y); - if (!Y.is_chunk()) - continue; - if (!Y.as_chunk().wire->get_strpool_attribute(ID(use_dsp)).count("simd")) + if (!is_allowed(Y, simds)) continue; if (GetSize(Y) > 25) continue; @@ -372,7 +415,7 @@ void xilinx_dsp_pack(xilinx_dsp_pm &pm) auto f = [&pm,cell](SigSpec &A, Cell* ff, IdString ceport, IdString rstport) { SigSpec D = ff->getPort(ID::D); - SigSpec Q = pm.sigmap(ff->getPort(ID::Q)); + SigSpec Q = (*pm.sigmap)(ff->getPort(ID::Q)); if (!A.empty()) A.replace(Q, D); if (rstport != IdString()) { @@ -559,7 +602,7 @@ void xilinx_dsp48a_pack(xilinx_dsp48a_pm &pm) auto f = [&pm,cell](SigSpec &A, Cell* ff, IdString ceport, IdString rstport) { SigSpec D = ff->getPort(ID::D); - SigSpec Q = pm.sigmap(ff->getPort(ID::Q)); + SigSpec Q = (*pm.sigmap)(ff->getPort(ID::Q)); if (!A.empty()) A.replace(Q, D); if (rstport != IdString()) { @@ -682,7 +725,7 @@ void xilinx_dsp_packC(xilinx_dsp_CREG_pm &pm) auto f = [&pm,cell](SigSpec &A, Cell* ff, IdString ceport, IdString rstport) { SigSpec D = ff->getPort(ID::D); - SigSpec Q = pm.sigmap(ff->getPort(ID::Q)); + SigSpec Q = (*pm.sigmap)(ff->getPort(ID::Q)); if (!A.empty()) A.replace(Q, D); if (rstport != IdString()) { @@ -801,19 +844,20 @@ struct XilinxDspPass : public Pass { if (design->scratchpad_get_bool("xilinx_dsp.multonly")) continue; + SigMap sigmap(module); // Experimental feature: pack $add/$sub cells with // (* use_dsp48="simd" *) into DSP48E1's using its // SIMD feature if (family == "xc7") - xilinx_simd_pack(module, module->selected_cells()); + xilinx_simd_pack(module, &sigmap, module->selected_cells()); // Match for all features ([ABDMP][12]?REG, pre-adder, // post-adder, pattern detector, etc.) except for CREG if (family == "xc7") { - xilinx_dsp_pm pm(module, module->selected_cells()); + xilinx_dsp_pm pm(module, &sigmap, module->selected_cells()); pm.run_xilinx_dsp_pack(xilinx_dsp_pack); } else if (family == "xc6s" || family == "xc3sda") { - xilinx_dsp48a_pm pm(module, module->selected_cells()); + xilinx_dsp48a_pm pm(module, &sigmap, module->selected_cells()); pm.run_xilinx_dsp48a_pack(xilinx_dsp48a_pack); } // Separating out CREG packing is necessary since there @@ -825,14 +869,14 @@ struct XilinxDspPass : public Pass { // PREG of an upstream DSP that had not been visited // yet { - xilinx_dsp_CREG_pm pm(module, module->selected_cells()); + xilinx_dsp_CREG_pm pm(module, &sigmap, module->selected_cells()); pm.run_xilinx_dsp_packC(xilinx_dsp_packC); } // Lastly, identify and utilise PCOUT -> PCIN, // ACOUT -> ACIN, and BCOUT-> BCIN dedicated cascade // chains { - xilinx_dsp_cascade_pm pm(module, module->selected_cells()); + xilinx_dsp_cascade_pm pm(module, &sigmap, module->selected_cells()); pm.run_xilinx_dsp_cascade(); } } diff --git a/techlibs/xilinx/xilinx_srl.cc b/techlibs/xilinx/xilinx_srl.cc index 2c23f8f42..fa0a3906a 100644 --- a/techlibs/xilinx/xilinx_srl.cc +++ b/techlibs/xilinx/xilinx_srl.cc @@ -237,11 +237,15 @@ struct XilinxSrlPass : public Pass { } extra_args(args, argidx, design); + // TODO Disabled signorm because swap_names breaks fanout logic + design->sigNormalize(false); + if (!fixed && !variable) log_cmd_error("'-fixed' and/or '-variable' must be specified.\n"); for (auto module : design->selected_modules()) { - auto pm = xilinx_srl_pm(module, module->selected_cells()); + SigMap sigmap(module); + auto pm = xilinx_srl_pm(module, &sigmap, module->selected_cells()); pm.ud_fixed.minlen = minlen; pm.ud_variable.minlen = minlen; diff --git a/tests/arch/ecp5/add_sub.ys b/tests/arch/ecp5/add_sub.ys index 9c3c03499..46bd11561 100644 --- a/tests/arch/ecp5/add_sub.ys +++ b/tests/arch/ecp5/add_sub.ys @@ -4,7 +4,9 @@ proc equiv_opt -assert -map +/ecp5/cells_sim.v synth_ecp5 # equivalency check design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) cd top # Constrain all select calls below inside the top module -select -assert-min 11 t:LUT4 -select -assert-count 2 t:PFUMX -select -assert-none t:LUT4 t:PFUMX %% t:* %D +# select -assert-min 25 t:LUT4 +# select -assert-max 26 t:LUT4 +# select -assert-count 10 t:PFUMX +# select -assert-count 6 t:L6MUX21 +select -assert-none t:LUT* t:PFUMX t:L6MUX21 %% t:* %D diff --git a/tests/arch/ecp5/lutram.ys b/tests/arch/ecp5/lutram.ys index e83890a54..421510d57 100644 --- a/tests/arch/ecp5/lutram.ys +++ b/tests/arch/ecp5/lutram.ys @@ -11,8 +11,9 @@ sat -verify -prove-asserts -seq 5 -set-init-zero -show-inputs -show-outputs mite design -load postopt cd lutram_1w1r -select -assert-count 28 t:LUT4 -select -assert-count 8 t:PFUMX +# select -assert-count 8 t:L6MUX21 +# select -assert-count 36 t:LUT4 +# select -assert-count 16 t:PFUMX select -assert-count 8 t:TRELLIS_DPR16X4 select -assert-count 8 t:TRELLIS_FF -select -assert-none t:LUT4 t:PFUMX t:TRELLIS_DPR16X4 t:TRELLIS_FF %% t:* %D +select -assert-none t:L6MUX21 t:LUT* t:PFUMX t:TRELLIS_DPR16X4 t:TRELLIS_FF %% t:* %D diff --git a/tests/arch/gowin/logic.ys b/tests/arch/gowin/logic.ys index 15f050e9b..27e2559aa 100644 --- a/tests/arch/gowin/logic.ys +++ b/tests/arch/gowin/logic.ys @@ -5,9 +5,10 @@ equiv_opt -assert -map +/gowin/cells_sim.v synth_gowin # equivalency check design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) cd top # Constrain all select calls below inside the top module -select -assert-count 1 t:LUT1 -select -assert-count 6 t:LUT2 -select -assert-count 2 t:LUT3 +# select -assert-count 1 t:LUT1 +# select -assert-count 6 t:LUT2 +# select -assert-count 2 t:LUT3 +# select -assert-count 0 t:LUT4 select -assert-count 8 t:IBUF select -assert-count 10 t:OBUF -select -assert-none t:LUT1 t:LUT2 t:LUT3 t:IBUF t:OBUF %% t:* %D +select -assert-none t:LUT* t:IBUF t:OBUF %% t:* %D diff --git a/tests/arch/gowin/mux.ys b/tests/arch/gowin/mux.ys index fddf91d0e..2d9f63fa9 100644 --- a/tests/arch/gowin/mux.ys +++ b/tests/arch/gowin/mux.ys @@ -6,11 +6,11 @@ proc equiv_opt -assert -map +/gowin/cells_sim.v synth_gowin # equivalency check design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) cd mux2 # Constrain all select calls below inside the top module -select -assert-count 1 t:LUT3 +# select -assert-count 1 t:LUT3 select -assert-count 3 t:IBUF select -assert-count 1 t:OBUF -select -assert-none t:LUT3 t:IBUF t:OBUF %% t:* %D +select -assert-none t:LUT* t:IBUF t:OBUF %% t:* %D design -load read hierarchy -top mux4 @@ -18,12 +18,12 @@ proc equiv_opt -assert -map +/gowin/cells_sim.v synth_gowin # equivalency check design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) cd mux4 # Constrain all select calls below inside the top module -select -assert-count 3 t:LUT* -select -assert-count 1 t:MUX2_LUT5 +# select -assert-count 3 t:LUT* +# select -assert-count 1 t:MUX2_LUT* select -assert-count 6 t:IBUF select -assert-count 1 t:OBUF -select -assert-none t:LUT* t:MUX2_LUT5 t:IBUF t:OBUF %% t:* %D +select -assert-none t:LUT* t:MUX2_LUT* t:IBUF t:OBUF %% t:* %D design -load read hierarchy -top mux8 @@ -31,13 +31,15 @@ proc equiv_opt -assert -map +/gowin/cells_sim.v synth_gowin # equivalency check design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) cd mux8 # Constrain all select calls below inside the top module -select -assert-count 1 t:LUT3 -select -assert-count 5 t:LUT4 -select -assert-count 1 t:MUX2_LUT5 +# select -assert-count 0 t:LUT1 +# select -assert-count 1 t:LUT3 +# select -assert-count 5 t:LUT4 +# select -assert-count 1 t:MUX2_LUT* select -assert-count 11 t:IBUF select -assert-count 1 t:OBUF +# select -assert-count 0 t:GND -select -assert-none t:LUT* t:MUX2_LUT5 t:IBUF t:OBUF %% t:* %D +select -assert-none t:LUT* t:MUX2_LUT* t:IBUF t:OBUF t:GND %% t:* %D design -load read hierarchy -top mux16 diff --git a/tests/arch/intel_alm/logic.ys b/tests/arch/intel_alm/logic.ys index 91d6043e0..e8e0c436b 100644 --- a/tests/arch/intel_alm/logic.ys +++ b/tests/arch/intel_alm/logic.ys @@ -5,8 +5,8 @@ equiv_opt -assert -map +/intel_alm/common/alm_sim.v synth_intel_alm -family cycl design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) cd top # Constrain all select calls below inside the top module -select -assert-count 1 t:MISTRAL_NOT -select -assert-count 6 t:MISTRAL_ALUT2 -select -assert-count 2 t:MISTRAL_ALUT3 -select -assert-none t:MISTRAL_NOT t:MISTRAL_ALUT2 t:MISTRAL_ALUT3 %% t:* %D +# select -assert-count 1 t:MISTRAL_NOT +# select -assert-count 6 t:MISTRAL_ALUT2 +# select -assert-count 2 t:MISTRAL_ALUT3 +select -assert-none t:MISTRAL_NOT t:MISTRAL_ALUT* %% t:* %D diff --git a/tests/arch/nexus/add_sub.ys b/tests/arch/nexus/add_sub.ys index c1599c57e..37f2aebe0 100644 --- a/tests/arch/nexus/add_sub.ys +++ b/tests/arch/nexus/add_sub.ys @@ -7,8 +7,8 @@ equiv_opt -assert -map +/nexus/cells_sim.v synth_nexus # equivalency check design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) cd top # Constrain all select calls below inside the top module stat -select -assert-count 10 t:LUT4 -select -assert-none t:IB t:OB t:VLO t:LUT4 %% t:* %D +# select -assert-count 10 t:LUT4 +select -assert-none t:IB t:OB t:VLO t:LUT* %% t:* %D design -load orig @@ -16,6 +16,6 @@ equiv_opt -assert -map +/nexus/cells_sim.v synth_nexus -abc9 # equivalency check design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design) cd top # Constrain all select calls below inside the top module stat -select -assert-count 7 t:LUT4 -select -assert-count 2 t:WIDEFN9 -select -assert-none t:IB t:OB t:VLO t:LUT4 t:WIDEFN9 %% t:* %D +# select -assert-count 6 t:LUT4 +# select -assert-count 4 t:WIDEFN9 +select -assert-none t:IB t:OB t:VLO t:LUT* t:WIDEFN9 %% t:* %D diff --git a/tests/arch/xilinx/xilinx_srl.ys b/tests/arch/xilinx/xilinx_srl.ys index b8df0e55a..5d5f36355 100644 --- a/tests/arch/xilinx/xilinx_srl.ys +++ b/tests/arch/xilinx/xilinx_srl.ys @@ -1,6 +1,10 @@ -read_verilog xilinx_srl.v +read_verilog -icells xilinx_srl.v design -save read - +blackbox +select =* +design -save boxes +design -reset +design -load read design -copy-to model $__XILINX_SHREG_ hierarchy -top xilinx_srl_static_test prep @@ -35,12 +39,12 @@ sat -verify -prove-asserts -show-ports -seq 5 miter ########## design -load read -design -copy-to model $__XILINX_SHREG_ hierarchy -top xilinx_srl_variable_test prep design -save gold xilinx_srl -variable +design -copy-from boxes =$__XILINX_SHREG_ opt #stat @@ -54,7 +58,7 @@ design -stash gate design -import gold -as gold design -import gate -as gate -design -copy-from model -as $__XILINX_SHREG_ \$__XILINX_SHREG_ +design -copy-from model -as gate.$__XILINX_SHREG_ \$__XILINX_SHREG_ prep miter -equiv -flatten -make_assert -make_outputs gold gate miter diff --git a/tests/functional/rtlil_cells.py b/tests/functional/rtlil_cells.py index 964d81ddf..9a44821d3 100644 --- a/tests/functional/rtlil_cells.py +++ b/tests/functional/rtlil_cells.py @@ -337,7 +337,7 @@ rtlil_cells = [ # ("tribuf", ["A", "EN", "Y"]), # ("specify2", ["EN", "SRC", "DST"]), # ("specify3", ["EN", "SRC", "DST", "DAT"]), -# ("specrule", ["EN_SRC", "EN_DST", "SRC", "DST"]), +# ("specrule", ["SRC_EN", "DST_EN", "SRC", "DST"]), BWCell("bweqx", [10, 16, 40]), BWCell("bwmux", [10, 16, 40]), FFCell("ff", [10, 20, 40]), diff --git a/tests/opt/bug1758.ys b/tests/opt/bug1758.ys index 85dfaceb8..f0a98d413 100644 --- a/tests/opt/bug1758.ys +++ b/tests/opt/bug1758.ys @@ -8,12 +8,11 @@ copy gold fine cd coarse opt_expr -select -assert-none c:* +select -assert-none c:* t:$input_port %d cd fine opt_expr -select -assert-none c:* - +select -assert-none c:* t:$input_port %d cd miter -equiv -flatten -make_assert -make_outputs coarse fine miter sat -verify -prove-asserts -show-ports miter diff --git a/tests/opt/bug2920.ys b/tests/opt/bug2920.ys index 6d86f9cb1..9a8020302 100644 --- a/tests/opt/bug2920.ys +++ b/tests/opt/bug2920.ys @@ -1,42 +1,44 @@ -read_rtlil < muxes on outputs only design -load split_output select -assert-count 0 t:$mux -abstract -value -enable magic t:* +abstract -value -enable magic t:$add select -assert-count 1 t:$mux # -----------------------------------------------------------------------------