| /* | 
 |  *  yosys -- Yosys Open SYnthesis Suite | 
 |  * | 
 |  *  Copyright (C) 2012  Clifford Wolf <clifford@clifford.at> | 
 |  *  Copyright (C) 2020  The Symbiflow Authors | 
 |  * | 
 |  *  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/rtlil.h" | 
 | #include "kernel/sigtools.h" | 
 |  | 
 | USING_YOSYS_NAMESPACE | 
 | PRIVATE_NAMESPACE_BEGIN | 
 |  | 
 | /// A structure representing a pin | 
 | struct Pin { | 
 |     RTLIL::Cell *cell;    /// Cell pointer | 
 |     RTLIL::IdString port; /// Cell port name | 
 |     int bit;              /// Port bit index | 
 |  | 
 |     Pin(RTLIL::Cell *_cell, const RTLIL::IdString &_port, int _bit = 0) : cell(_cell), port(_port), bit(_bit) {} | 
 |  | 
 |     Pin(const Pin &ref) = default; | 
 |  | 
 |     unsigned int hash() const | 
 |     { | 
 |         if (cell == nullptr) { | 
 |             return mkhash_add(port.hash(), bit); | 
 |         } else { | 
 |             return mkhash_add(mkhash(cell->hash(), port.hash()), bit); | 
 |         } | 
 |     }; | 
 | }; | 
 |  | 
 | bool operator==(const Pin &lhs, const Pin &rhs) { return (lhs.cell == rhs.cell) && (lhs.port == rhs.port) && (lhs.bit == rhs.bit); } | 
 |  | 
 | struct IntegrateInv : public Pass { | 
 |  | 
 |     /// Temporary SigBit to SigBit helper map. | 
 |     SigMap m_SigMap; | 
 |     /// Map of SigBit objects to inverter cells. | 
 |     dict<RTLIL::SigBit, RTLIL::Cell *> m_InvMap; | 
 |     /// Map of inverter cells that can potentially be integrated and invertable | 
 |     /// pins that they are connected to | 
 |     dict<RTLIL::Cell *, pool<Pin>> m_Inverters; | 
 |     /// Map of invertable pins and names of parameters controlling inversions | 
 |     dict<Pin, RTLIL::IdString> m_InvParams; | 
 |  | 
 |     IntegrateInv() | 
 |         : Pass("integrateinv", "Integrates inverters ($_NOT_ cells) into ports " | 
 |                                "with 'invertible_pin' attribute set") | 
 |     { | 
 |     } | 
 |  | 
 |     void help() override | 
 |     { | 
 |         log("\n"); | 
 |         log("    integrateinv [selection]"); | 
 |         log("\n"); | 
 |         log("This pass integrates inverters into cells that have ports with the\n"); | 
 |         log("'invertible_pin' attribute set. The attribute should contain the name\n"); | 
 |         log("of a parameter controlling the inversion.\n"); | 
 |         log("\n"); | 
 |         log("This pass is essentially the opposite of the 'extractinv' pass.\n"); | 
 |         log("\n"); | 
 |     } | 
 |  | 
 |     void execute(std::vector<std::string> a_Args, RTLIL::Design *a_Design) override | 
 |     { | 
 |         log_header(a_Design, "Executing INTEGRATEINV pass (integrating pin inverters).\n"); | 
 |  | 
 |         extra_args(a_Args, 1, a_Design); | 
 |  | 
 |         // Process modules | 
 |         for (auto module : a_Design->selected_modules()) { | 
 |  | 
 |             // Setup the SigMap | 
 |             m_SigMap.clear(); | 
 |             m_SigMap.set(module); | 
 |  | 
 |             m_Inverters.clear(); | 
 |             m_InvParams.clear(); | 
 |  | 
 |             // Setup inverter map | 
 |             buildInverterMap(module); | 
 |  | 
 |             // Identify inverters that can be integrated and assign them with | 
 |             // lists of cells and ports to integrate with | 
 |             for (auto cell : module->selected_cells()) { | 
 |                 collectInverters(cell); | 
 |             } | 
 |  | 
 |             // Integrate inverters | 
 |             integrateInverters(); | 
 |         } | 
 |  | 
 |         // Clear maps | 
 |         m_SigMap.clear(); | 
 |  | 
 |         m_InvMap.clear(); | 
 |         m_Inverters.clear(); | 
 |         m_InvParams.clear(); | 
 |     } | 
 |  | 
 |     void buildInverterMap(RTLIL::Module *a_Module) | 
 |     { | 
 |         m_InvMap.clear(); | 
 |  | 
 |         for (auto cell : a_Module->cells()) { | 
 |  | 
 |             // Skip non-inverters | 
 |             if (cell->type != RTLIL::escape_id("$_NOT_")) { | 
 |                 continue; | 
 |             } | 
 |  | 
 |             // Get output connection | 
 |             auto sigspec = cell->getPort(RTLIL::escape_id("Y")); | 
 |             auto sigbit = m_SigMap(sigspec.bits().at(0)); | 
 |  | 
 |             // Store | 
 |             log_assert(m_InvMap.count(sigbit) == 0); | 
 |             m_InvMap[sigbit] = cell; | 
 |         } | 
 |     } | 
 |  | 
 |     void collectInverters(RTLIL::Cell *a_Cell) | 
 |     { | 
 |         auto module = a_Cell->module; | 
 |         auto design = module->design; | 
 |  | 
 |         for (auto conn : a_Cell->connections()) { | 
 |             auto port = conn.first; | 
 |             auto sigspec = conn.second; | 
 |  | 
 |             // Consider only inputs. | 
 |             if (!a_Cell->input(port)) { | 
 |                 continue; | 
 |             } | 
 |  | 
 |             // Get the cell module | 
 |             auto cellModule = design->module(a_Cell->type); | 
 |             if (!cellModule) { | 
 |                 continue; | 
 |             } | 
 |  | 
 |             // Get wire. | 
 |             auto wire = cellModule->wire(port); | 
 |             if (!wire) { | 
 |                 continue; | 
 |             } | 
 |  | 
 |             // Check if the pin has an embedded inverter. | 
 |             auto it = wire->attributes.find(ID::invertible_pin); | 
 |             if (it == wire->attributes.end()) { | 
 |                 continue; | 
 |             } | 
 |  | 
 |             // Decode the parameter name. | 
 |             RTLIL::IdString paramName = RTLIL::escape_id(it->second.decode_string()); | 
 |  | 
 |             // Look for connected inverters | 
 |             auto sigbits = sigspec.bits(); | 
 |             for (size_t bit = 0; bit < sigbits.size(); ++bit) { | 
 |  | 
 |                 auto sigbit = sigbits[bit]; | 
 |                 if (!sigbit.wire) { | 
 |                     continue; | 
 |                 } | 
 |  | 
 |                 sigbit = m_SigMap(sigbit); | 
 |  | 
 |                 // Get the inverter if any | 
 |                 if (!m_InvMap.count(sigbit)) { | 
 |                     continue; | 
 |                 } | 
 |                 auto inv = m_InvMap.at(sigbit); | 
 |  | 
 |                 // Save the inverter pin and the parameter name | 
 |                 auto pin = Pin(a_Cell, port, bit); | 
 |  | 
 |                 auto &list = m_Inverters[inv]; | 
 |                 list.insert(pin); | 
 |  | 
 |                 log_assert(m_InvParams.count(pin) == 0); | 
 |                 m_InvParams[pin] = paramName; | 
 |             } | 
 |         } | 
 |     } | 
 |  | 
 |     void integrateInverters() | 
 |     { | 
 |  | 
 |         for (auto it : m_Inverters) { | 
 |             auto inv = it.first; | 
 |             auto pins = it.second; | 
 |  | 
 |             // List all sinks of the inverter | 
 |             auto sinks = getSinksForDriver(Pin(inv, RTLIL::escape_id("Y"))); | 
 |  | 
 |             // If the inverter drives only invertable pins then integrate it | 
 |             if (sinks == pins) { | 
 |                 log("Integrating inverter %s into:\n", log_id(inv->name)); | 
 |  | 
 |                 // Integrate into each pin | 
 |                 for (auto pin : pins) { | 
 |                     log_assert(pin.cell != nullptr); | 
 |                     log(" %s.%s[%d]\n", log_id(pin.cell->name), log_id(pin.port), pin.bit); | 
 |  | 
 |                     // Change the connection | 
 |                     auto sigspec = pin.cell->getPort(pin.port); | 
 |                     auto sigbits = sigspec.bits(); | 
 |  | 
 |                     log_assert((size_t)pin.bit < sigbits.size()); | 
 |                     sigbits[pin.bit] = RTLIL::SigBit(inv->getPort(RTLIL::escape_id("A"))[0]); | 
 |                     pin.cell->setPort(pin.port, RTLIL::SigSpec(sigbits)); | 
 |  | 
 |                     // Get the control parameter | 
 |                     log_assert(m_InvParams.count(pin) != 0); | 
 |                     auto paramName = m_InvParams[pin]; | 
 |  | 
 |                     RTLIL::Const invMask; | 
 |                     auto param = pin.cell->parameters.find(paramName); | 
 |                     if (param == pin.cell->parameters.end()) { | 
 |                         invMask = RTLIL::Const(0, sigspec.size()); | 
 |                     } else { | 
 |                         invMask = RTLIL::Const(param->second); | 
 |                     } | 
 |  | 
 |                     // Check width. | 
 |                     if (invMask.size() != sigspec.size()) { | 
 |                         log_error("The inversion parameter needs to be the same width as " | 
 |                                   "the port (%s port %s parameter %s)", | 
 |                                   log_id(pin.cell->name), log_id(pin.port), log_id(paramName)); | 
 |                     } | 
 |  | 
 |                     // Toggle bit in the control parameter bitmask | 
 |                     if (invMask[pin.bit] == RTLIL::State::S0) { | 
 |                         invMask[pin.bit] = RTLIL::State::S1; | 
 |                     } else if (invMask[pin.bit] == RTLIL::State::S1) { | 
 |                         invMask[pin.bit] = RTLIL::State::S0; | 
 |                     } else { | 
 |                         log_error("The inversion parameter must contain only 0s and 1s (%s " | 
 |                                   "parameter %s)\n", | 
 |                                   log_id(pin.cell->name), log_id(paramName)); | 
 |                     } | 
 |  | 
 |                     // Set the parameter back | 
 |                     pin.cell->setParam(paramName, invMask); | 
 |                 } | 
 |  | 
 |                 // Remove the inverter | 
 |                 inv->module->remove(inv); | 
 |             } | 
 |         } | 
 |     } | 
 |  | 
 |     pool<Pin> getSinksForDriver(const Pin &a_Driver) | 
 |     { | 
 |         auto module = a_Driver.cell->module; | 
 |         pool<Pin> sinks; | 
 |  | 
 |         // The driver has to be an output pin | 
 |         if (!a_Driver.cell->output(a_Driver.port)) { | 
 |             return sinks; | 
 |         } | 
 |  | 
 |         // Get the driver sigbit | 
 |         auto driverSigspec = a_Driver.cell->getPort(a_Driver.port); | 
 |         auto driverSigbit = m_SigMap(driverSigspec.bits().at(a_Driver.bit)); | 
 |  | 
 |         // Look for connected sinks | 
 |         for (auto cell : module->cells()) { | 
 |             for (auto conn : cell->connections()) { | 
 |                 auto port = conn.first; | 
 |                 auto sigspec = conn.second; | 
 |  | 
 |                 // Consider only sinks (inputs) | 
 |                 if (!cell->input(port)) { | 
 |                     continue; | 
 |                 } | 
 |  | 
 |                 // Check all sigbits | 
 |                 auto sigbits = sigspec.bits(); | 
 |                 for (size_t bit = 0; bit < sigbits.size(); ++bit) { | 
 |  | 
 |                     auto sigbit = sigbits[bit]; | 
 |                     if (!sigbit.wire) { | 
 |                         continue; | 
 |                     } | 
 |  | 
 |                     // Got a sink pin of another cell | 
 |                     sigbit = m_SigMap(sigbit); | 
 |                     if (sigbit == driverSigbit) { | 
 |                         sinks.insert(Pin(cell, port, bit)); | 
 |                     } | 
 |                 } | 
 |             } | 
 |         } | 
 |  | 
 |         // Look for connected top-level output ports | 
 |         for (auto conn : module->connections()) { | 
 |             auto dst = conn.first; | 
 |             auto src = conn.second; | 
 |  | 
 |             auto sigbits = dst.bits(); | 
 |             for (size_t bit = 0; bit < sigbits.size(); ++bit) { | 
 |  | 
 |                 auto sigbit = sigbits[bit]; | 
 |                 if (!sigbit.wire) { | 
 |                     continue; | 
 |                 } | 
 |  | 
 |                 if (!sigbit.wire->port_output) { | 
 |                     continue; | 
 |                 } | 
 |  | 
 |                 sigbit = m_SigMap(sigbit); | 
 |                 if (sigbit == driverSigbit) { | 
 |                     sinks.insert(Pin(nullptr, sigbit.wire->name, bit)); | 
 |                 } | 
 |             } | 
 |         } | 
 |  | 
 |         return sinks; | 
 |     } | 
 |  | 
 | } IntegrateInv; | 
 |  | 
 | PRIVATE_NAMESPACE_END |