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