|  | /* | 
|  | *  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 |