First working version of the pass Signed-off-by: Maciej Kurc <mkurc@antmicro.com>
diff --git a/dsp_ff-plugin/dsp_ff.cc b/dsp_ff-plugin/dsp_ff.cc index 0bb4f1f..952c0dd 100644 --- a/dsp_ff-plugin/dsp_ff.cc +++ b/dsp_ff-plugin/dsp_ff.cc
@@ -41,6 +41,22 @@ (port == ref.port) && (bit == ref.bit); } + + std::string as_string () const { + if (cell != nullptr) { + return stringf("%s.%s[%d]", + RTLIL::unescape_id(cell->name).c_str(), + RTLIL::unescape_id(port).c_str(), + bit + ); + } + else { + return stringf("%s[%d]", + RTLIL::unescape_id(port).c_str(), + bit + ); + } + } }; // .......................................... @@ -84,11 +100,20 @@ dict<RTLIL::IdString, RTLIL::Const> connect; }; + /// Describes a DSP cell type struct DspType { RTLIL::IdString name; + + /// A list of data ports with registers std::vector<DspPortType> ports; }; + /// Describes a changes made to a DSP cell + struct DspChanges { + pool<RTLIL::IdString> params; // Modified params + pool<RTLIL::IdString> conns; // Altered connections (ports) + }; + // .......................................... struct FlopData { @@ -505,6 +530,8 @@ // dict<RTLIL::SigBit, RTLIL::SigBit> m_NetMap; /// Cells to be removed (per module!) pool<RTLIL::Cell*> m_CellsToRemove; + /// DSP cells that got changed + dict<RTLIL::Cell*, DspChanges> m_DspChanges; /// DSP types dict<RTLIL::IdString, DspType> m_DspTypes; @@ -583,17 +610,7 @@ continue; } - if (cell->input(portRule.name)) { - processInputPort(cell, portRule); - } - else if (cell->output(portRule.name)) { - processOutputPort(cell, portRule); - } - else { - log(" The port '%s.%s' is neither input nor output!\n", - cell->type.c_str(), portRule.name.c_str()); - continue; - } + processPort(cell, portRule); } } @@ -655,12 +672,13 @@ return isOk; } -/* + bool checkFlopDataAgainstDspPort (const FlopData& a_FlopData, RTLIL::Cell* a_Cell, const DspPortType& a_PortRule) { const auto& flopType = m_FlopTypes.at(a_FlopData.type); + const auto& changes = m_DspChanges[a_Cell]; bool isOk = true; log_debug(" Checking connected flip-flop settings against the DSP port... "); @@ -668,7 +686,7 @@ // Check control signal connections for (const auto& it : a_PortRule.assoc) { const auto& key = it.first; - const auto& port = it.second; + const auto& port = it.second.first; SigBit conn(RTLIL::Sx); if (!port.empty() && a_Cell->hasPort(port)) { @@ -688,42 +706,53 @@ } } + auto checkParam = [&](const RTLIL::IdString& name, + const RTLIL::Const& curr, + const RTLIL::Const& next) + { + if (curr != next && changes.params.count(name)) { + log_debug("\n the param '%s' mismatch ('%s' instead of '%s')", + name.c_str(), curr.decode_string().c_str(), + next.decode_string().c_str()); + isOk = false; + return false; + } + return true; + }; + + // Check parameters to be mapped (by the port rule) + for (const auto& it : a_PortRule.params.map) { + if (a_Cell->hasParam(it.first) && a_FlopData.params.dsp.count(it.second)) { + const auto curr = a_Cell->getParam(it.first); + const auto flop = a_FlopData.params.dsp.at(it.second); + checkParam(it.first, curr, flop); + } + } + // Check parameters to be set (by the port rule) for (const auto& it : a_PortRule.params.set) { if (a_Cell->hasParam(it.first)) { const auto curr = a_Cell->getParam(it.first); - if (curr != it.second) { - log_debug("\n the param '%s' mismatch ('%s' instead of '%s')", - it.first.c_str(), curr.decode_string().c_str(), - it.second.decode_string().c_str()); - isOk = false; - } - } - } - - // Check parameters to be mapped (by the port rule) - for (const auto& it : a_PortRule.params.map) { - if (a_Cell->hasParam(it.first) && a_FlopData.params.count(it.second)) { - const auto curr = a_Cell->getParam(it.first); - const auto flop = a_FlopData.params.at(it.second); - if (curr != flop) { - log_debug("\n the param '%s' mismatch ('%s' instead of '%s')", - it.first.c_str(), curr.decode_string().c_str(), - flop.decode_string().c_str()); - isOk = false; - } + checkParam(it.first, curr, it.second); } } -// // Check parameters to be set (by the flip-flop type) -// for (const auto& it : flopType.params.set) { -// const auto curr = a_Cell->getParam(it.first); -// if (curr == it.second) { -// log_debug("\n the param '%s' is already set to '%s'", -// it.first.c_str(), it.second.decode_string().c_str()); -// isOk = false; -// } -// } + // Check parameters to be mapped (by the flip-flop rule) + for (const auto& it : flopType.params.map) { + if (a_Cell->hasParam(it.first) && a_FlopData.params.dsp.count(it.second)) { + const auto curr = a_Cell->getParam(it.first); + const auto flop = a_FlopData.params.dsp.at(it.second); + checkParam(it.first, curr, flop); + } + } + + // Check parameters to be set (by the flip-flop rule) + for (const auto& it : flopType.params.set) { + if (a_Cell->hasParam(it.first)) { + const auto curr = a_Cell->getParam(it.first); + checkParam(it.first, curr, it.second); + } + } if (isOk) { log_debug("Ok\n"); @@ -732,7 +761,6 @@ } return isOk; } -*/ static std::string sigBitName (const RTLIL::SigBit& a_SigBit) { if (a_SigBit.is_wire()) { @@ -754,16 +782,7 @@ // .......................................... - void processInputPort (RTLIL::Cell* a_Cell, const DspPortType& a_PortRule) { - - log_debug(" Attempting flip-flop integration for %s.%s of %s\n", - a_Cell->type.c_str(), a_PortRule.name.c_str(), a_Cell->name.c_str()); - - // TODO - log_debug(" TODO: An input port\n"); - } - - void processOutputPort (RTLIL::Cell* a_Cell, const DspPortType& a_PortRule) { + void processPort (RTLIL::Cell* a_Cell, const DspPortType& a_PortRule) { log_debug(" Attempting flip-flop integration for %s.%s of %s\n", a_Cell->type.c_str(), a_PortRule.name.c_str(), a_Cell->name.c_str()); @@ -790,22 +809,42 @@ continue; } + log_assert(a_Cell->output(a_PortRule.name) || a_Cell->input(a_PortRule.name)); + + pool<CellPin> others; + // Get sinks(s), discard the port completely if more than one sink // is found. - auto sinks = getSinks(CellPin(a_Cell, a_PortRule.name, i)); - if (sinks.size() > 1) { - log_debug(" multiple sinks found, cannot integrate.\n"); - return; + if (a_Cell->output(a_PortRule.name)) { + others = getSinks(CellPin(a_Cell, a_PortRule.name, i)); + if (others.size() > 1) { + log_debug(" multiple sinks found, cannot integrate.\n"); + return; + } + } + // Get driver. Discard if the driver drives something else too + // TODO: This is slow - we are first looking for a driver and then + // for all its sinks. + else if (a_Cell->input(a_PortRule.name)) { + auto driver = getDriver(CellPin(a_Cell, a_PortRule.name, i)); + if (driver.cell != nullptr) { + auto sinks = getSinks(driver); + if (sinks.size() > 1) { + log_debug(" multiple sinks found, cannot integrate.\n"); + return; + } + } + others.insert(driver); } - // No sinks - output unconnected - if (sinks.empty()) { + // No others - unconnected + if (others.empty()) { continue; } // Get the sink, check if this is a flip-flop - auto& sink = *sinks.begin(); - auto* flop = sink.cell; + auto& other = *others.begin(); + auto* flop = other.cell; if (flop == nullptr || !m_FlopTypes.count(flop->type)) { continue; } @@ -815,10 +854,17 @@ continue; } - // Check if the connection goes to the data input port + // Check if the connection goes to the data input/output port const auto& flopType = m_FlopTypes.at(flop->type); - if (flopType.ports.at(RTLIL::escape_id("d")) != sink.port) { - continue; + if (a_Cell->output(a_PortRule.name)) { + if (flopType.ports.at(RTLIL::escape_id("d")) != other.port) { + continue; + } + } + else if (a_Cell->input(a_PortRule.name)) { + if (flopType.ports.at(RTLIL::escape_id("q")) != other.port) { + continue; + } } // Skip if the flip-flop is going to be removed @@ -862,11 +908,10 @@ // Validate the flip flop data agains the DSP cell const auto& flopData = groups.begin()->first; - const auto& flopType = m_FlopTypes.at(flopData.type); -// if (!checkFlopDataAgainstDspPort(flopData, a_Cell, a_PortRule)) { -// log_debug(" flip-flop vs. DSP check failed\n"); -// return; -// } + if (!checkFlopDataAgainstDspPort(flopData, a_Cell, a_PortRule)) { + log_debug(" flip-flop vs. DSP check failed\n"); + return; + } // Debug log log(" %s %s.%s\n", a_Cell->type.c_str(), a_Cell->name.c_str(), a_PortRule.name.c_str()); @@ -882,6 +927,7 @@ } // Reconnect data signals, mark the flip-flop for removal + const auto& flopType = m_FlopTypes.at(flopData.type); for (size_t i=0; i<flops.size(); ++i) { auto* flop = flops[i].first; @@ -889,7 +935,14 @@ continue; } - const auto& port = flopType.ports.at(RTLIL::escape_id("q")); + RTLIL::IdString port; + if (a_Cell->output(a_PortRule.name)) { + port = flopType.ports.at(RTLIL::escape_id("q")); + } + else if (a_Cell->input(a_PortRule.name)) { + port = flopType.ports.at(RTLIL::escape_id("d")); + } + if (!flop->hasPort(port)) { log_error(" cell '%s' does not have port '%s'!\n", flop->type.c_str(), port.c_str()); @@ -920,6 +973,7 @@ log_debug(" connecting %s.%s to %s\n", a_Cell->type.c_str(), port.c_str(), sigBitName(conn).c_str()); a_Cell->setPort(port, conn); + m_DspChanges[a_Cell].conns.insert(port); } // Connect control signals according to DSP port rule @@ -927,6 +981,7 @@ log_debug(" connecting %s.%s to %s\n", a_Cell->type.c_str(), it.first.c_str(), it.second.as_string().c_str()); a_Cell->setPort(it.first, it.second); + m_DspChanges[a_Cell].conns.insert(it.first); } // Map parameters (port rule) @@ -935,6 +990,7 @@ const auto& param = flopData.params.dsp.at(it.second); log_debug(" setting param '%s' to '%s'\n", it.first.c_str(), param.decode_string().c_str()); a_Cell->setParam(it.first, param); + m_DspChanges[a_Cell].params.insert(it.first); } } @@ -944,6 +1000,7 @@ const auto& param = flopData.params.dsp.at(it.second); log_debug(" setting param '%s' to '%s'\n", it.first.c_str(), param.decode_string().c_str()); a_Cell->setParam(it.first, param); + m_DspChanges[a_Cell].params.insert(it.first); } } @@ -951,12 +1008,14 @@ for (const auto& it : a_PortRule.params.set) { log_debug(" setting param '%s' to '%s'\n", it.first.c_str(), it.second.decode_string().c_str()); a_Cell->setParam(it.first, it.second); + m_DspChanges[a_Cell].params.insert(it.first); } // Set parameters (flip-flop rule) for (const auto& it : flopType.params.set) { log_debug(" setting param '%s' to '%s'\n", it.first.c_str(), it.second.decode_string().c_str()); a_Cell->setParam(it.first, it.second); + m_DspChanges[a_Cell].params.insert(it.first); } } @@ -1025,13 +1084,11 @@ pool<CellPin> sinks; // The driver has to be an output pin - if (!a_Driver.cell->output(a_Driver.port)) { - return sinks; - } + log_assert(a_Driver.cell->output(a_Driver.port)); // Get the driver sigbit auto driverSigspec = a_Driver.cell->getPort(a_Driver.port); - auto driverSigbit = m_SigMap(driverSigspec.bits().at(a_Driver.bit)); + auto driverSigbit = m_SigMap(driverSigspec.bits().at(a_Driver.bit)); // Look for connected sinks for (auto cell : module->cells()) { @@ -1089,6 +1146,75 @@ return sinks; } + /// Finds a driver for the given cell pin + /// TODO: This is slow, need to make a lookup for that. + CellPin getDriver (const CellPin& a_Sink) { + auto module = a_Sink.cell->module; + + // The sink has to be an input pin + log_assert(a_Sink.cell->input(a_Sink.port)); + + // Get the sink sigbit + auto sinkSigspec = a_Sink.cell->getPort(a_Sink.port); + auto sinkSigbit = m_SigMap(sinkSigspec.bits().at(a_Sink.bit)); + + // Look for connected top-level input 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_input) { + continue; + } + + sigbit = m_SigMap(sigbit); + if (sigbit == sinkSigbit) { + CellPin(nullptr, sigbit.wire->name, bit); + } + } + } + + // Look for the driver among cells + for (auto cell : module->cells()) { + for (auto conn : cell->connections()) { + auto port = conn.first; + auto sigspec = conn.second; + + // Consider only outputs + if (!cell->output(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 driver pin of another cell + sigbit = m_SigMap(sigbit); + if (sigbit == sinkSigbit) { + return CellPin(cell, port, bit); + } + } + } + } + + // No driver found + return CellPin(nullptr, RTLIL::IdString(), -1); + } + } DspFF; PRIVATE_NAMESPACE_END