|  | #include "kernel/register.h" | 
|  | #include "kernel/rtlil.h" | 
|  | #include "kernel/sigtools.h" | 
|  |  | 
|  | USING_YOSYS_NAMESPACE | 
|  | PRIVATE_NAMESPACE_BEGIN | 
|  |  | 
|  | // ============================================================================ | 
|  |  | 
|  | struct DspFF : public Pass { | 
|  |  | 
|  | /// A structure identifying specific pin in a cell instance | 
|  | struct CellPin { | 
|  | RTLIL::Cell *cell;    /// Cell pointer (nullptr for top-level ports) | 
|  | RTLIL::IdString port; /// Port name | 
|  | int bit;              /// Bit index | 
|  |  | 
|  | CellPin(RTLIL::Cell *_cell, const RTLIL::IdString &_port, int _bit = 0) : cell(_cell), port(_port), bit(_bit) {} | 
|  |  | 
|  | CellPin(const CellPin &ref) = default; | 
|  | CellPin(CellPin &&ref) = default; | 
|  |  | 
|  | unsigned int hash() const | 
|  | { | 
|  | unsigned int h = 0; | 
|  | if (cell != nullptr) { | 
|  | h = mkhash_add(h, cell->hash()); | 
|  | } | 
|  | h = mkhash_add(h, port.hash()); | 
|  | h = mkhash_add(h, bit); | 
|  | return h; | 
|  | } | 
|  |  | 
|  | bool operator==(const CellPin &ref) const { return (cell == ref.cell) && (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); | 
|  | } | 
|  | } | 
|  | }; | 
|  |  | 
|  | // .......................................... | 
|  |  | 
|  | /// Connection map | 
|  | struct ConnMap { | 
|  |  | 
|  | /// Maps source SigBit to all sinks it drives CellPin. | 
|  | dict<RTLIL::SigBit, std::vector<CellPin>> sinks; | 
|  | /// Maps source SigBit to its driver CellPin | 
|  | dict<RTLIL::SigBit, CellPin> drivers; | 
|  |  | 
|  | /// Builds the map | 
|  | void build(RTLIL::Module *module, const SigMap &sigmap) | 
|  | { | 
|  | clear(); | 
|  |  | 
|  | // Scan cell ports | 
|  | for (auto *cell : module->cells()) { | 
|  | for (const auto &it : cell->connections_) { | 
|  | const auto &port = it.first; | 
|  | const auto &sigbits = it.second.bits(); | 
|  | for (size_t i = 0; i < sigbits.size(); ++i) { | 
|  | auto sigbit = sigmap(sigbits[i]); | 
|  |  | 
|  | // This is an input port (sink)) | 
|  | if (cell->input(port)) { | 
|  | auto &vec = sinks[sigbit]; | 
|  | vec.push_back(CellPin(cell, port, i)); | 
|  | } | 
|  | // This is a source | 
|  | if (cell->output(port)) { | 
|  | drivers.insert(std::make_pair(sigbit, CellPin(cell, port, i))); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // Scan top-level ports | 
|  | for (auto &it : module->wires_) { | 
|  | auto *wire = it.second; | 
|  |  | 
|  | if (!wire->port_input && !wire->port_output) { | 
|  | continue; | 
|  | } | 
|  |  | 
|  | RTLIL::SigSpec sigspec(wire, wire->start_offset, wire->width); | 
|  | const auto &sigbits = sigspec.bits(); | 
|  | for (size_t i = 0; i < sigbits.size(); ++i) { | 
|  | auto sigbit = sigbits[i]; | 
|  | if (!sigbit.wire) { | 
|  | continue; | 
|  | } | 
|  |  | 
|  | // Output port (sink) | 
|  | if (sigbit.wire->port_output) { | 
|  | auto &vec = sinks[sigmap(sigbit)]; | 
|  | vec.push_back(CellPin(nullptr, sigbit.wire->name, i)); | 
|  | } | 
|  | // Input port (source) | 
|  | if (sigbit.wire->port_input) { | 
|  | drivers.insert(std::make_pair(sigbit, CellPin(nullptr, sigbit.wire->name, i))); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /// Clears the map | 
|  | void clear() | 
|  | { | 
|  | sinks.clear(); | 
|  | drivers.clear(); | 
|  | }; | 
|  | }; | 
|  |  | 
|  | // .......................................... | 
|  |  | 
|  | /// Describes a flip-flop type that can be integrated with a DSP cell | 
|  | struct FlopType { | 
|  | RTLIL::IdString name; | 
|  |  | 
|  | /// A dict of port names indexed by their functions (like "clk", "rst") | 
|  | dict<RTLIL::IdString, RTLIL::IdString> ports; | 
|  |  | 
|  | struct { | 
|  | /// A list of parameters that must match for all flip-flops | 
|  | std::vector<RTLIL::IdString> matching; | 
|  | /// A dict of parameter values that must match for a flip-flop | 
|  | dict<RTLIL::IdString, RTLIL::Const> required; | 
|  | /// A dict of parameters to be set in the DSP cell after integration | 
|  | dict<RTLIL::IdString, RTLIL::Const> set; | 
|  | /// A dict of parameters to be mapped to the DSP cell after integration | 
|  | dict<RTLIL::IdString, RTLIL::IdString> map; | 
|  | } params; | 
|  | }; | 
|  |  | 
|  | /// Describes a DSP cell port that has built-in register (flip-flops) | 
|  | struct PortType { | 
|  | RTLIL::IdString name; | 
|  |  | 
|  | /// Range of port pins that have FFs (low to high, inclusive) | 
|  | std::pair<int, int> bits; | 
|  | /// A dict of associated cell ports indexed by their function (like "clk, "rst") | 
|  | /// along with the default value to connect when unused. | 
|  | dict<RTLIL::IdString, std::pair<RTLIL::IdString, RTLIL::Const>> assoc; | 
|  | }; | 
|  |  | 
|  | /// Describes a DSP register | 
|  | struct RegisterType { | 
|  |  | 
|  | /// Control parameters | 
|  | struct { | 
|  | /// A dict of parameters to be set in the cell after integration | 
|  | dict<RTLIL::IdString, RTLIL::Const> set; | 
|  | /// A dict of parameters to be mapped to the cell after integration | 
|  | dict<RTLIL::IdString, RTLIL::IdString> map; | 
|  | } params; | 
|  |  | 
|  | /// A list of ports to be connected to specific constants after flip-flop | 
|  | /// integration. | 
|  | dict<RTLIL::IdString, RTLIL::Const> connect; | 
|  |  | 
|  | unsigned int hash() const | 
|  | { | 
|  | unsigned int h = 0; | 
|  | h = mkhash_add(h, params.set.hash()); | 
|  | h = mkhash_add(h, params.map.hash()); | 
|  | h = mkhash_add(h, connect.hash()); | 
|  | return h; | 
|  | } | 
|  |  | 
|  | bool operator==(const RegisterType &ref) const | 
|  | { | 
|  | return (params.set == ref.params.set) && (params.map == ref.params.map) && (connect == ref.connect); | 
|  | } | 
|  | }; | 
|  |  | 
|  | /// Describes a DSP cell type | 
|  | struct DspType { | 
|  | RTLIL::IdString name; | 
|  | dict<RegisterType, std::vector<PortType>> registers; | 
|  | }; | 
|  |  | 
|  | /// Describes a changes made to a DSP cell | 
|  | struct DspChanges { | 
|  | pool<RTLIL::IdString> params; // Modified params | 
|  | pool<RTLIL::IdString> conns;  // Altered connections (ports) | 
|  | }; | 
|  |  | 
|  | // .......................................... | 
|  |  | 
|  | /// Describes unique flip-flop configuration that is exclusive. | 
|  | struct FlopData { | 
|  | RTLIL::IdString type; | 
|  | dict<RTLIL::IdString, RTLIL::SigBit> conns; | 
|  | struct { | 
|  | dict<RTLIL::IdString, RTLIL::Const> flop; | 
|  | dict<RTLIL::IdString, RTLIL::Const> dsp; | 
|  | } params; | 
|  |  | 
|  | FlopData(const RTLIL::IdString &_type) : type(_type){}; | 
|  |  | 
|  | FlopData(const FlopData &ref) = default; | 
|  | FlopData(FlopData &&ref) = default; | 
|  |  | 
|  | unsigned int hash() const | 
|  | { | 
|  | unsigned int h = 0; | 
|  | h = mkhash_add(h, type.hash()); | 
|  | h = mkhash_add(h, conns.hash()); | 
|  | h = mkhash_add(h, params.flop.hash()); | 
|  | h = mkhash_add(h, params.dsp.hash()); | 
|  | return h; | 
|  | } | 
|  |  | 
|  | bool operator==(const FlopData &ref) const | 
|  | { | 
|  | return (type == ref.type) && (conns == ref.conns) && (params.flop == ref.params.flop) && (params.dsp == ref.params.dsp); | 
|  | } | 
|  | }; | 
|  |  | 
|  | // .......................................... | 
|  |  | 
|  | /// Loads FF and DSP integration rules from a file | 
|  | void load_rules(const std::string &a_FileName) | 
|  | { | 
|  |  | 
|  | // Parses a string and returns a vector of fields delimited by the | 
|  | // given character. | 
|  | auto getFields = [](const std::string &a_String, const char a_Delim = ' ', bool a_KeepEmpty = false) { | 
|  | std::vector<std::string> fields; | 
|  | std::stringstream ss(a_String); | 
|  |  | 
|  | while (ss.good()) { | 
|  | std::string field; | 
|  | std::getline(ss, field, a_Delim); | 
|  | if (!field.empty() || a_KeepEmpty) { | 
|  | fields.push_back(field); | 
|  | } | 
|  | } | 
|  |  | 
|  | return fields; | 
|  | }; | 
|  |  | 
|  | // Parses a vector of strings like "<name>=<value>" starting from the | 
|  | // second one on the list | 
|  | auto parseNameValue = [&](const std::vector<std::string> &strs) { | 
|  | const std::regex expr("(\\S+)=(\\S+)"); | 
|  | std::smatch match; | 
|  |  | 
|  | std::vector<std::pair<std::string, std::string>> vec; | 
|  |  | 
|  | for (size_t i = 1; i < strs.size(); ++i) { | 
|  | if (std::regex_match(strs[i], match, expr)) { | 
|  | vec.push_back(std::make_pair(match[1], match[2])); | 
|  | } else { | 
|  | log_error(" syntax error: '%s'\n", strs[i].c_str()); | 
|  | } | 
|  | } | 
|  |  | 
|  | return vec; | 
|  | }; | 
|  |  | 
|  | // Parses port name as "<name>[<hi>:<lo>]" or just "<name>" | 
|  | auto parsePortName = [&](const std::string &str) { | 
|  | const std::regex expr("^(.*)\\[([0-9]+):([0-9]+)\\]"); | 
|  | std::smatch match; | 
|  |  | 
|  | std::tuple<std::string, int, int> data; | 
|  | auto res = std::regex_match(str, match, expr); | 
|  | if (res) { | 
|  | data = std::make_tuple(std::string(match[1]), std::stoi(match[2]), std::stoi(match[3])); | 
|  |  | 
|  | if ((std::get<2>(data) > std::get<1>(data)) || std::get<2>(data) < 0 || std::get<1>(data) < 0) { | 
|  | log_error(" invalid port spec: '%s'\n", str.c_str()); | 
|  | } | 
|  | } else { | 
|  | data = std::make_tuple(str, -1, -1); | 
|  | } | 
|  |  | 
|  | return data; | 
|  | }; | 
|  |  | 
|  | std::ifstream file(a_FileName); | 
|  | std::string line; | 
|  |  | 
|  | log("Loading rules from '%s'...\n", a_FileName.c_str()); | 
|  | if (!file) { | 
|  | log_error(" Error opening file '%s'!\n", a_FileName.c_str()); | 
|  | } | 
|  |  | 
|  | // Parse each port as if it was associated with its own DSP register. | 
|  | // Group them each time a port definition is complete. | 
|  | PortType portType; | 
|  | RegisterType registerType; | 
|  |  | 
|  | std::vector<DspType> dspTypes; | 
|  | std::vector<FlopType> flopTypes; | 
|  |  | 
|  | std::vector<RTLIL::IdString> dspAliases; | 
|  | std::vector<std::string> portNames; | 
|  |  | 
|  | std::vector<std::string> tok; | 
|  |  | 
|  | // Parse the file | 
|  | while (1) { | 
|  |  | 
|  | // Get line | 
|  | std::getline(file, line); | 
|  | if (!file) { | 
|  | break; | 
|  | } | 
|  |  | 
|  | // Strip comment if any, skip empty lines | 
|  | size_t pos = line.find("#"); | 
|  | if (pos != std::string::npos) { | 
|  | line = line.substr(0, pos); | 
|  | } | 
|  | if (line.find_first_not_of(" \r\n\t") == std::string::npos) { | 
|  | continue; | 
|  | } | 
|  |  | 
|  | // Split the line | 
|  | const auto fields = getFields(line); | 
|  | log_assert(fields.size() >= 1); | 
|  |  | 
|  | // DSP section | 
|  | if (fields[0] == "dsp") { | 
|  | if (fields.size() < 2) { | 
|  | log_error(" syntax error: '%s'\n", line.c_str()); | 
|  | } | 
|  | if (!tok.empty()) { | 
|  | log_error(" unexpected keyword '%s'\n", fields[0].c_str()); | 
|  | } | 
|  | tok.push_back(fields[0]); | 
|  |  | 
|  | dspTypes.resize(dspTypes.size() + 1); | 
|  | dspTypes.back().name = RTLIL::escape_id(fields[1]); | 
|  |  | 
|  | dspAliases.clear(); | 
|  | for (size_t i = 2; i < fields.size(); ++i) { | 
|  | dspAliases.push_back(RTLIL::escape_id(fields[i])); | 
|  | } | 
|  | } else if (fields[0] == "enddsp") { | 
|  | if (fields.size() != 1) { | 
|  | log_error(" syntax error: '%s'\n", line.c_str()); | 
|  | } | 
|  | if (tok.size() != 1 || tok.back() != "dsp") { | 
|  | log_error(" unexpected keyword '%s'\n", fields[0].c_str()); | 
|  | } | 
|  | tok.pop_back(); | 
|  |  | 
|  | const auto dspType = dspTypes.back(); | 
|  |  | 
|  | for (const auto &alias : dspAliases) { | 
|  | dspTypes.push_back(dspType); | 
|  | dspTypes.back().name = alias; | 
|  | } | 
|  | } | 
|  |  | 
|  | // DSP port section | 
|  | else if (fields[0] == "port") { | 
|  | if (fields.size() < 2) { | 
|  | log_error(" syntax error: '%s'\n", line.c_str()); | 
|  | } | 
|  | if (tok.size() != 1 || tok.back() != "dsp") { | 
|  | log_error(" unexpected keyword '%s'\n", fields[0].c_str()); | 
|  | } | 
|  | tok.push_back(fields[0]); | 
|  |  | 
|  | auto spec = parsePortName(fields[1]); | 
|  |  | 
|  | portType = PortType(); | 
|  | portType.name = RTLIL::escape_id(std::get<0>(spec)); | 
|  | portType.bits = std::make_pair(std::get<2>(spec), std::get<1>(spec)); | 
|  | portType.assoc.insert(std::make_pair(RTLIL::escape_id("clk"), std::make_pair(RTLIL::IdString(), RTLIL::Sx))); | 
|  | portType.assoc.insert(std::make_pair(RTLIL::escape_id("rst"), std::make_pair(RTLIL::IdString(), RTLIL::Sx))); | 
|  | portType.assoc.insert(std::make_pair(RTLIL::escape_id("ena"), std::make_pair(RTLIL::IdString(), RTLIL::Sx))); | 
|  |  | 
|  | registerType = RegisterType(); | 
|  |  | 
|  | portNames.clear(); | 
|  | for (size_t i = 2; i < fields.size(); ++i) { | 
|  | portNames.push_back(fields[i]); | 
|  | } | 
|  |  | 
|  | } else if (fields[0] == "endport") { | 
|  | if (fields.size() != 1) { | 
|  | log_error(" syntax error: '%s'\n", line.c_str()); | 
|  | } | 
|  | if (tok.size() != 2 || tok.back() != "port") { | 
|  | log_error(" unexpected keyword '%s'\n", fields[0].c_str()); | 
|  | } | 
|  | tok.pop_back(); | 
|  |  | 
|  | // Store the DSP port | 
|  | auto &dspType = dspTypes.back(); | 
|  | dspType.registers[registerType].push_back(portType); | 
|  |  | 
|  | // Store any extra DSP ports belonging to the same register | 
|  | for (const auto &name : portNames) { | 
|  | auto spec = parsePortName(name); | 
|  |  | 
|  | PortType portTypeCopy = portType; | 
|  | portTypeCopy.name = RTLIL::escape_id(std::get<0>(spec)); | 
|  | portTypeCopy.bits = std::make_pair(std::get<2>(spec), std::get<1>(spec)); | 
|  |  | 
|  | dspType.registers[registerType].push_back(portTypeCopy); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Flip-flop type section | 
|  | else if (fields[0] == "ff") { | 
|  | if (fields.size() != 2) { | 
|  | log_error(" syntax error: '%s'\n", line.c_str()); | 
|  | } | 
|  | if (!tok.empty()) { | 
|  | log_error(" unexpected keyword '%s'\n", fields[0].c_str()); | 
|  | } | 
|  | tok.push_back(fields[0]); | 
|  |  | 
|  | flopTypes.resize(flopTypes.size() + 1); | 
|  | flopTypes.back().name = RTLIL::escape_id(fields[1]); | 
|  | flopTypes.back().ports.insert(std::make_pair(RTLIL::escape_id("clk"), RTLIL::IdString())); | 
|  | flopTypes.back().ports.insert(std::make_pair(RTLIL::escape_id("rst"), RTLIL::IdString())); | 
|  | flopTypes.back().ports.insert(std::make_pair(RTLIL::escape_id("ena"), RTLIL::IdString())); | 
|  | flopTypes.back().ports.insert(std::make_pair(RTLIL::escape_id("d"), RTLIL::IdString())); | 
|  | flopTypes.back().ports.insert(std::make_pair(RTLIL::escape_id("q"), RTLIL::IdString())); | 
|  | } else if (fields[0] == "endff") { | 
|  | if (fields.size() != 1) { | 
|  | log_error(" syntax error: '%s'\n", line.c_str()); | 
|  | } | 
|  | if (tok.size() != 1 || tok.back() != "ff") { | 
|  | log_error(" unexpected keyword '%s'\n", fields[0].c_str()); | 
|  | } | 
|  | tok.pop_back(); | 
|  | } | 
|  |  | 
|  | // Signals | 
|  | else if (fields[0] == "clk") { | 
|  | if (tok.size() == 0 || (tok.back() != "port" && tok.back() != "ff")) { | 
|  | log_error(" unexpected keyword '%s'\n", fields[0].c_str()); | 
|  | } | 
|  |  | 
|  | // Associated clock | 
|  | if (tok.back() == "port") { | 
|  | if (fields.size() != 3) { | 
|  | log_error(" syntax error: '%s'\n", line.c_str()); | 
|  | } | 
|  | portType.assoc[RTLIL::escape_id("clk")] = std::make_pair(RTLIL::escape_id(fields[1]), RTLIL::Const::from_string(fields[2])); | 
|  | } else if (tok.back() == "ff") { | 
|  | if (fields.size() != 2) { | 
|  | log_error(" syntax error: '%s'\n", line.c_str()); | 
|  | } | 
|  | flopTypes.back().ports[RTLIL::escape_id("clk")] = RTLIL::escape_id(fields[1]); | 
|  | } | 
|  | } else if (fields[0] == "rst") { | 
|  | if (tok.size() == 0 || (tok.back() != "port" && tok.back() != "ff")) { | 
|  | log_error(" unexpected keyword '%s'\n", fields[0].c_str()); | 
|  | } | 
|  |  | 
|  | // Associated reset | 
|  | if (tok.back() == "port") { | 
|  | if (fields.size() != 3) { | 
|  | log_error(" syntax error: '%s'\n", line.c_str()); | 
|  | } | 
|  | portType.assoc[RTLIL::escape_id("rst")] = std::make_pair(RTLIL::escape_id(fields[1]), RTLIL::Const::from_string(fields[2])); | 
|  | } else if (tok.back() == "ff") { | 
|  | if (fields.size() != 2) { | 
|  | log_error(" syntax error: '%s'\n", line.c_str()); | 
|  | } | 
|  | flopTypes.back().ports[RTLIL::escape_id("rst")] = RTLIL::escape_id(fields[1]); | 
|  | } | 
|  | } else if (fields[0] == "ena") { | 
|  | if (tok.size() == 0 || (tok.back() != "port" && tok.back() != "ff")) { | 
|  | log_error(" unexpected keyword '%s'\n", fields[0].c_str()); | 
|  | } | 
|  |  | 
|  | // Associated enable | 
|  | if (tok.back() == "port") { | 
|  | if (fields.size() != 3) { | 
|  | log_error(" syntax error: '%s'\n", line.c_str()); | 
|  | } | 
|  | portType.assoc[RTLIL::escape_id("ena")] = std::make_pair(RTLIL::escape_id(fields[1]), RTLIL::Const::from_string(fields[2])); | 
|  | } else if (tok.back() == "ff") { | 
|  | if (fields.size() != 2) { | 
|  | log_error(" syntax error: '%s'\n", line.c_str()); | 
|  | } | 
|  | flopTypes.back().ports[RTLIL::escape_id("ena")] = RTLIL::escape_id(fields[1]); | 
|  | } | 
|  | } | 
|  |  | 
|  | else if (fields[0] == "d") { | 
|  | if (fields.size() != 2) { | 
|  | log_error(" syntax error: '%s'\n", line.c_str()); | 
|  | } | 
|  | if (tok.size() == 0 || tok.back() != "ff") { | 
|  | log_error(" unexpected keyword '%s'\n", fields[0].c_str()); | 
|  | } | 
|  |  | 
|  | flopTypes.back().ports[RTLIL::escape_id("d")] = RTLIL::escape_id(fields[1]); | 
|  | } else if (fields[0] == "q") { | 
|  | if (fields.size() != 2) { | 
|  | log_error(" syntax error: '%s'\n", line.c_str()); | 
|  | } | 
|  | if (tok.size() == 0 || tok.back() != "ff") { | 
|  | log_error(" unexpected keyword '%s'\n", fields[0].c_str()); | 
|  | } | 
|  |  | 
|  | flopTypes.back().ports[RTLIL::escape_id("q")] = RTLIL::escape_id(fields[1]); | 
|  | } | 
|  |  | 
|  | // Parameters that must be set to certain values | 
|  | else if (fields[0] == "require") { | 
|  | if (fields.size() < 2) { | 
|  | log_error(" syntax error: '%s'\n", line.c_str()); | 
|  | } | 
|  | if (tok.size() == 0 || tok.back() != "ff") { | 
|  | log_error(" unexpected keyword '%s'\n", fields[0].c_str()); | 
|  | } | 
|  |  | 
|  | const auto vec = parseNameValue(fields); | 
|  | for (const auto &it : vec) { | 
|  | flopTypes.back().params.required.insert(std::make_pair(RTLIL::escape_id(it.first), RTLIL::Const(it.second))); | 
|  | } | 
|  | } | 
|  | // Parameters that has to match for a flip-flop | 
|  | else if (fields[0] == "match") { | 
|  | if (fields.size() < 2) { | 
|  | log_error(" syntax error: '%s'\n", line.c_str()); | 
|  | } | 
|  | if (tok.size() == 0 || tok.back() != "ff") { | 
|  | log_error(" unexpected keyword '%s'\n", fields[0].c_str()); | 
|  | } | 
|  |  | 
|  | for (size_t i = 1; i < fields.size(); ++i) { | 
|  | flopTypes.back().params.matching.push_back(RTLIL::escape_id(fields[i])); | 
|  | } | 
|  | } | 
|  | // Parameters to set | 
|  | else if (fields[0] == "set") { | 
|  | if (fields.size() < 2) { | 
|  | log_error(" syntax error: '%s'\n", line.c_str()); | 
|  | } | 
|  | if (tok.size() == 0 || (tok.back() != "port" && tok.back() != "ff")) { | 
|  | log_error(" unexpected keyword '%s'\n", fields[0].c_str()); | 
|  | } | 
|  |  | 
|  | const auto vec = parseNameValue(fields); | 
|  | dict<RTLIL::IdString, RTLIL::Const> set; | 
|  | for (const auto &it : vec) { | 
|  | set.insert(std::make_pair(RTLIL::escape_id(it.first), RTLIL::Const(it.second))); | 
|  | } | 
|  |  | 
|  | if (tok.back() == "port") { | 
|  | registerType.params.set.swap(set); | 
|  | } else if (tok.back() == "ff") { | 
|  | flopTypes.back().params.set.swap(set); | 
|  | } | 
|  | } | 
|  | // Parameters to copy / map | 
|  | else if (fields[0] == "map") { | 
|  | if (fields.size() < 2) { | 
|  | log_error(" syntax error: '%s'\n", line.c_str()); | 
|  | } | 
|  | if (tok.size() == 0 || (tok.back() != "port" && tok.back() != "ff")) { | 
|  | log_error(" unexpected keyword '%s'\n", fields[0].c_str()); | 
|  | } | 
|  |  | 
|  | const auto vec = parseNameValue(fields); | 
|  | dict<RTLIL::IdString, RTLIL::IdString> map; | 
|  | for (const auto &it : vec) { | 
|  | map.insert(std::make_pair(RTLIL::escape_id(it.first), RTLIL::escape_id(it.second))); | 
|  | } | 
|  |  | 
|  | if (tok.back() == "port") { | 
|  | registerType.params.map.swap(map); | 
|  | } else if (tok.back() == "ff") { | 
|  | flopTypes.back().params.map.swap(map); | 
|  | } | 
|  | } | 
|  | // Connections to make | 
|  | else if (fields[0] == "con") { | 
|  | if (fields.size() < 2) { | 
|  | log_error(" syntax error: '%s'\n", line.c_str()); | 
|  | } | 
|  | if (tok.size() == 0 || tok.back() != "port") { | 
|  | log_error(" unexpected keyword '%s'\n", fields[0].c_str()); | 
|  | } | 
|  |  | 
|  | const auto vec = parseNameValue(fields); | 
|  | for (const auto &it : vec) { | 
|  | registerType.connect.insert(std::make_pair(RTLIL::escape_id(it.first), RTLIL::Const(it.second))); | 
|  | } | 
|  | } | 
|  |  | 
|  | else { | 
|  | log_error(" unexpected keyword '%s'\n", fields[0].c_str()); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Convert lists to maps | 
|  | for (const auto &it : dspTypes) { | 
|  | if (m_DspTypes.count(it.name)) { | 
|  | log_error(" duplicated rule for DSP '%s'\n", it.name.c_str()); | 
|  | } | 
|  | m_DspTypes.insert(std::make_pair(it.name, it)); | 
|  | } | 
|  | for (const auto &it : flopTypes) { | 
|  | if (m_FlopTypes.count(it.name)) { | 
|  | log_error(" duplicated rule for flip-flop '%s'\n", it.name.c_str()); | 
|  | } | 
|  | m_FlopTypes.insert(std::make_pair(it.name, it)); | 
|  | } | 
|  | } | 
|  |  | 
|  | void dump_rules() | 
|  | { | 
|  |  | 
|  | // Dump DSP types | 
|  | log("DSP types:\n"); | 
|  | for (const auto &it1 : m_DspTypes) { | 
|  | const auto &dsp = it1.second; | 
|  | log(" %s\n", dsp.name.c_str()); | 
|  |  | 
|  | for (const auto &it2 : dsp.registers) { | 
|  | const auto ® = it2.first; | 
|  | const auto &ports = it2.second; | 
|  | log(" ports:\n"); | 
|  | for (const auto &port : ports) { | 
|  |  | 
|  | std::string range; | 
|  | if (port.bits.first != -1 && port.bits.second != -1) { | 
|  | range = stringf("[%d:%d]", port.bits.second, port.bits.first); | 
|  | } | 
|  |  | 
|  | log("  %s.%s%s\n", dsp.name.c_str(), port.name.c_str(), range.c_str()); | 
|  |  | 
|  | for (const auto &it : port.assoc) { | 
|  | log("   %.3s: %s\n", it.first.c_str(), !it.second.first.empty() ? it.second.first.c_str() : "<none>"); | 
|  | } | 
|  |  | 
|  | if (!reg.params.set.empty()) { | 
|  | log("   set params:\n"); | 
|  | for (const auto &it : reg.params.set) { | 
|  | log("    %s=%s\n", it.first.c_str(), it.second.decode_string().c_str()); | 
|  | } | 
|  | } | 
|  | if (!reg.params.map.empty()) { | 
|  | log("   map params:\n"); | 
|  | for (const auto &it : reg.params.map) { | 
|  | log("    %s=%s\n", it.first.c_str(), it.second.c_str()); | 
|  | } | 
|  | } | 
|  | if (!reg.connect.empty()) { | 
|  | log("   connect ports:\n"); | 
|  | for (const auto &it : reg.connect) { | 
|  | log("    %s.%s=%s\n", dsp.name.c_str(), it.first.c_str(), it.second.as_string().c_str()); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // Dump flop types | 
|  | log("Flip-flop types:\n"); | 
|  | for (const auto &it : m_FlopTypes) { | 
|  | const auto &ff = it.second; | 
|  | log(" %s\n", ff.name.c_str()); | 
|  |  | 
|  | for (const auto &it : ff.ports) { | 
|  | log("  %.3s: %s\n", it.first.c_str(), !it.second.empty() ? it.second.c_str() : "<none>"); | 
|  | } | 
|  |  | 
|  | if (!ff.params.required.empty()) { | 
|  | log("  required params:\n"); | 
|  | for (const auto &it : ff.params.required) { | 
|  | log("   %s=%s\n", it.first.c_str(), it.second.decode_string().c_str()); | 
|  | } | 
|  | } | 
|  | if (!ff.params.matching.empty()) { | 
|  | log("  params that must match:\n"); | 
|  | for (const auto &it : ff.params.matching) { | 
|  | log("   %s\n", it.c_str()); | 
|  | } | 
|  | } | 
|  | if (!ff.params.set.empty()) { | 
|  | log("  set params:\n"); | 
|  | for (const auto &it : ff.params.set) { | 
|  | log("   %s=%s\n", it.first.c_str(), it.second.decode_string().c_str()); | 
|  | } | 
|  | } | 
|  | if (!ff.params.map.empty()) { | 
|  | log("  map params:\n"); | 
|  | for (const auto &it : ff.params.map) { | 
|  | log("   %s=%s\n", it.first.c_str(), it.second.c_str()); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // .......................................... | 
|  |  | 
|  | /// Temporary SigBit to SigBit helper map. | 
|  | SigMap m_SigMap; | 
|  | /// Module connection map | 
|  | ConnMap m_ConnMap; | 
|  |  | 
|  | /// 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; | 
|  | /// Flip-flop types | 
|  | dict<RTLIL::IdString, FlopType> m_FlopTypes; | 
|  |  | 
|  | // .......................................... | 
|  |  | 
|  | DspFF() : Pass("dsp_ff", "Integrates flip-flop into DSP blocks") {} | 
|  |  | 
|  | void help() override | 
|  | { | 
|  | log("\n"); | 
|  | log("    dsp_ff -rules <rules.txt> [selection]\n"); | 
|  | log("\n"); | 
|  | log("Integrates flip-flops with DSP blocks and enables their internal registers.\n"); | 
|  | log("\n"); | 
|  | log("The pass loads a set of rules from the file given with the '-rules' parameter.\n"); | 
|  | log("The rules define what ports of a DSP module have internal registers and what\n"); | 
|  | log("has to be done to enable them. They also define compatible flip-flop cell\n"); | 
|  | log("types.\n"); | 
|  | log("\n"); | 
|  | log("The format of the rules file is the following:\n"); | 
|  | log("\n"); | 
|  | log("  # This is a comment\n"); | 
|  | log("\n"); | 
|  | log("  dsp <dsp_type> [<dsp_type> ...]\n"); | 
|  | log("    port <dsp_port> [<dsp_port> ...]\n"); | 
|  | log("      clk <associated clk> <default>\n"); | 
|  | log("     [rst <associated reset>] <default>\n"); | 
|  | log("     [ena <associated enable>] <default>\n"); | 
|  | log("\n"); | 
|  | log("     [set <param>=<value> [<param>=<value> ...]]\n"); | 
|  | log("     [map <dsp_param>=<ff_param> [<dsp_param>=<ff_param> ...]]\n"); | 
|  | log("     [con <port>=<const> [<port>=<const> ...]]\n"); | 
|  | log("    endport\n"); | 
|  | log("  enddsp\n"); | 
|  | log("\n"); | 
|  | log("  ff <ff_type>\n"); | 
|  | log("    clk <clock input>\n"); | 
|  | log("   [rst <reset input>]\n"); | 
|  | log("   [ena <enable input>]\n"); | 
|  | log("    d   <data input>\n"); | 
|  | log("    q   <data output>\n"); | 
|  | log("\n"); | 
|  | log("    require <param>=<value> [<param>=<value> ...]\n"); | 
|  | log("    match   <param> [<param> ...]\n"); | 
|  | log("\n"); | 
|  | log("    set <param>=<value> [<param>=<value> ...]\n"); | 
|  | log("    map <dsp_param>=<ff_param> [<dsp_param>=<ff_param> ...]\n"); | 
|  | log("  endff\n"); | 
|  | log("\n"); | 
|  | log("Each 'dsp' section defines a DSP cell type (can apply to multiple types).\n"); | 
|  | log("Within it each 'port' section defines a data port with internal register.\n"); | 
|  | log("There can be multiple port names given if they belong to the same control register.\n"); | 
|  | log("The port can be specified as a whole (eg. 'DATA') or as a subset of the whole\n"); | 
|  | log("(eg. 'DATA[7:0]').\n"); | 
|  | log("\n"); | 
|  | log("Statemenst 'clk', 'rst' and 'ena' define names of clock, reset and enable\n"); | 
|  | log("ports associated with the data port along with default constant values to\n"); | 
|  | log("connect them to when a given port has no counterpart in the flip-flop being\n"); | 
|  | log("integrated.\n"); | 
|  | log("\n"); | 
|  | log("The 'set' statement tells how to set control parameter(s) of the DSP that\n"); | 
|  | log("enable the input register on the port. The 'map' statement defines how to\n"); | 
|  | log("map parameter(s) of the flip-flip being integrated to the DSP. Finally the\n"); | 
|  | log("'con' statement informs how to connected control port(s) of the DSP to enable\n"); | 
|  | log("the register.\n"); | 
|  | log("\n"); | 
|  | log("Each 'ff' section defines a flip-flop type that can be integrated into a DSP\n"); | 
|  | log("cell. Inside this section 'clk', 'rst', 'ena', 'd' and 'q' define names of\n"); | 
|  | log("clock, reset, enable, data in and data out ports of the flip-flop respectively.\n"); | 
|  | log("\n"); | 
|  | log("The 'require' statement defines parameter(s) that must have specific value\n"); | 
|  | log("for a flip-flop to be considered for integration. The 'match' statement\n"); | 
|  | log("lists names of flip-flop parameters that must match on all flip-flops connected\n"); | 
|  | log("to a single DSP data port.\n"); | 
|  | log("\n"); | 
|  | log("The 'set' and 'map' statements serve the same function as in the DSP port\n"); | 
|  | log("section but here they may differ depending on the flip-flop type being\n"); | 
|  | log("integrated.\n"); | 
|  | } | 
|  |  | 
|  | void execute(std::vector<std::string> a_Args, RTLIL::Design *a_Design) override | 
|  | { | 
|  | log_header(a_Design, "Executing DSP_FF pass.\n"); | 
|  |  | 
|  | std::string rulesFile; | 
|  |  | 
|  | // Parse args | 
|  | size_t argidx; | 
|  | for (argidx = 1; argidx < a_Args.size(); argidx++) { | 
|  | if (a_Args[argidx] == "-rules" && (argidx + 1) < a_Args.size()) { | 
|  | rulesFile = a_Args[++argidx]; | 
|  | continue; | 
|  | } | 
|  |  | 
|  | break; | 
|  | } | 
|  | extra_args(a_Args, argidx, a_Design); | 
|  |  | 
|  | // Check args | 
|  | if (rulesFile.empty()) { | 
|  | log_cmd_error("No rules file specified!"); | 
|  | } | 
|  |  | 
|  | // Reset state | 
|  | m_CellsToRemove.clear(); | 
|  | m_DspChanges.clear(); | 
|  | m_DspTypes.clear(); | 
|  | m_FlopTypes.clear(); | 
|  |  | 
|  | // Load rules | 
|  | rewrite_filename(rulesFile); | 
|  | load_rules(rulesFile); | 
|  | if (log_force_debug) { | 
|  | dump_rules(); | 
|  | } | 
|  |  | 
|  | // Process modules | 
|  | for (auto module : a_Design->selected_modules()) { | 
|  |  | 
|  | // Setup the SigMap | 
|  | m_SigMap.clear(); | 
|  | m_SigMap.set(module); | 
|  |  | 
|  | // Build the connection map | 
|  | m_ConnMap.clear(); | 
|  | m_ConnMap.build(module, m_SigMap); | 
|  |  | 
|  | // Look for DSP cells | 
|  | for (auto cell : module->cells()) { | 
|  |  | 
|  | // Not a DSP | 
|  | if (!m_DspTypes.count(cell->type)) { | 
|  | continue; | 
|  | } | 
|  |  | 
|  | // Process all registers | 
|  | auto &dspType = m_DspTypes.at(cell->type); | 
|  | for (auto &rule : dspType.registers) { | 
|  | processRegister(cell, rule.first, rule.second); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Remove cells | 
|  | for (const auto &cell : m_CellsToRemove) { | 
|  | module->remove(cell); | 
|  | } | 
|  | m_CellsToRemove.clear(); | 
|  | } | 
|  |  | 
|  | // Clear maps | 
|  | m_SigMap.clear(); | 
|  | m_ConnMap.clear(); | 
|  | } | 
|  |  | 
|  | // .......................................... | 
|  |  | 
|  | bool checkFlop(RTLIL::Cell *a_Cell) | 
|  | { | 
|  | const auto &flopType = m_FlopTypes.at(a_Cell->type); | 
|  | bool isOk = true; | 
|  |  | 
|  | log_debug("checking connected flip-flop '%s' of type '%s'... ", a_Cell->name.c_str(), a_Cell->type.c_str()); | 
|  |  | 
|  | // Must not have the "keep" attribute | 
|  | if (a_Cell->has_keep_attr()) { | 
|  | log_debug("\n   the 'keep' attribute is set"); | 
|  | isOk = false; | 
|  | } | 
|  |  | 
|  | // Check if required parameters are set as they should be | 
|  | for (const auto &it : flopType.params.required) { | 
|  | const auto curr = a_Cell->getParam(it.first); | 
|  | if (curr != it.second) { | 
|  | log_debug("\n   param '%s' mismatch ('%s' instead of '%s')", it.first.c_str(), curr.decode_string().c_str(), | 
|  | it.second.decode_string().c_str()); | 
|  | isOk = false; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (isOk) { | 
|  | log_debug("Ok\n"); | 
|  | } else { | 
|  | log_debug("\n"); | 
|  | } | 
|  | return isOk; | 
|  | } | 
|  |  | 
|  | bool checkFlopDataAgainstDspRegister(const FlopData &a_FlopData, RTLIL::Cell *a_Cell, const RegisterType &a_Register, | 
|  | const std::vector<PortType> &a_Ports) | 
|  | { | 
|  | 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 register... "); | 
|  |  | 
|  | // Check control signal connections | 
|  | for (const auto &port : a_Ports) { | 
|  | for (const auto &it : port.assoc) { | 
|  | const auto &key = it.first; | 
|  | const auto &port = it.second.first; | 
|  |  | 
|  | SigBit conn(RTLIL::Sx); | 
|  | if (!port.empty() && a_Cell->hasPort(port)) { | 
|  | auto sigspec = a_Cell->getPort(port); | 
|  | auto sigbits = sigspec.bits(); | 
|  | log_assert(sigbits.size() <= 1); | 
|  | if (!sigbits.empty()) { | 
|  | conn = m_SigMap(sigbits[0]); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (conn.is_wire() || (!conn.is_wire() && conn.data != RTLIL::Sx)) { | 
|  | if (conn != a_FlopData.conns.at(key)) { | 
|  | log_debug("\n   connection to port '%s' mismatch", port.c_str()); | 
|  | isOk = false; | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | 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_Register.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_Register.params.set) { | 
|  | if (a_Cell->hasParam(it.first)) { | 
|  | const auto curr = a_Cell->getParam(it.first); | 
|  | checkParam(it.first, curr, it.second); | 
|  | } | 
|  | } | 
|  |  | 
|  | // 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"); | 
|  | } else { | 
|  | log_debug("\n"); | 
|  | } | 
|  | return isOk; | 
|  | } | 
|  |  | 
|  | /// Returns a string with either wire name or constant value for a SigBit | 
|  | static std::string sigBitName(const RTLIL::SigBit &a_SigBit) | 
|  | { | 
|  | if (a_SigBit.is_wire()) { | 
|  | RTLIL::Wire *w = a_SigBit.wire; | 
|  | return RTLIL::unescape_id(w->name); | 
|  | } else { | 
|  | switch (a_SigBit.data) { | 
|  | case RTLIL::State::S0: | 
|  | return "1'b0"; | 
|  | case RTLIL::State::S1: | 
|  | return "1'b1"; | 
|  | case RTLIL::State::Sx: | 
|  | return "1'bx"; | 
|  | case RTLIL::State::Sz: | 
|  | return "1'bz"; | 
|  | case RTLIL::State::Sa: | 
|  | return "-"; | 
|  | case RTLIL::State::Sm: | 
|  | return "m"; | 
|  | } | 
|  | return "?"; | 
|  | } | 
|  | } | 
|  |  | 
|  | // .......................................... | 
|  |  | 
|  | void processRegister(RTLIL::Cell *a_Cell, const RegisterType &a_Register, const std::vector<PortType> &a_Ports) | 
|  | { | 
|  |  | 
|  | // The cell register control parameter(s) must not be set | 
|  | for (const auto &it : a_Register.params.set) { | 
|  | const auto curr = a_Cell->getParam(it.first); | 
|  | if (curr == it.second) { | 
|  | log_debug(" the param '%s' is already set to '%s'\n", it.first.c_str(), it.second.decode_string().c_str()); | 
|  | return; | 
|  | } | 
|  | } | 
|  |  | 
|  | pool<FlopData> groups; | 
|  | dict<RTLIL::IdString, std::vector<RTLIL::Cell *>> flops; | 
|  |  | 
|  | // Process ports | 
|  | bool flopsOk = true; | 
|  | for (const auto &port : a_Ports) { | 
|  | log_debug(" attempting flip-flop integration for %s.%s of %s\n", a_Cell->type.c_str(), port.name.c_str(), a_Cell->name.c_str()); | 
|  |  | 
|  | if (!a_Cell->hasPort(port.name)) { | 
|  | log_debug("  port unconnected.\n"); | 
|  | continue; | 
|  | } | 
|  | log_assert(a_Cell->output(port.name) || a_Cell->input(port.name)); | 
|  |  | 
|  | // Get port connections | 
|  | auto sigspec = a_Cell->getPort(port.name); | 
|  | auto sigbits = sigspec.bits(); | 
|  |  | 
|  | flops[port.name] = std::vector<RTLIL::Cell *>(sigbits.size(), nullptr); | 
|  | for (size_t i = 0; i < sigbits.size(); ++i) { | 
|  | auto sigbit = m_SigMap(sigbits[i]); | 
|  |  | 
|  | log_debug("  %2zu. ", i); | 
|  |  | 
|  | // Port connected to a const. | 
|  | if (!sigbit.wire) { | 
|  | log_debug("constant\n"); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | // Skip bits out of the specified range | 
|  | if ((port.bits.first >= 0 && (int)i < port.bits.first) || (port.bits.second >= 0 && (int)i > port.bits.second)) { | 
|  | log_debug("(excluded)\n"); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | pool<CellPin> others; | 
|  |  | 
|  | // Get sinks(s), discard the port completely if more than one sink | 
|  | // is found. | 
|  | if (a_Cell->output(port.name)) { | 
|  | if (m_ConnMap.sinks.count(sigbit)) { | 
|  | for (const auto &sink : m_ConnMap.sinks.at(sigbit)) { | 
|  | if (sink.cell != nullptr && m_CellsToRemove.count(sink.cell)) { | 
|  | continue; | 
|  | } | 
|  | others.insert(sink); | 
|  | } | 
|  | } | 
|  |  | 
|  | } | 
|  | // Get driver. Discard if the driver drives something else too | 
|  | else if (a_Cell->input(port.name)) { | 
|  | if (m_ConnMap.drivers.count(sigbit)) { | 
|  | auto driver = m_ConnMap.drivers.at(sigbit); | 
|  |  | 
|  | if (m_ConnMap.sinks.count(sigbit)) { | 
|  | auto sinks = m_ConnMap.sinks.at(sigbit); | 
|  | if (sinks.size() > 1) { | 
|  | log_debug("multiple sinks (%zu)\n", others.size()); | 
|  | flopsOk = false; | 
|  | continue; | 
|  | } | 
|  | } | 
|  |  | 
|  | others.insert(driver); | 
|  | } | 
|  | } | 
|  |  | 
|  | // No others - unconnected | 
|  | if (others.empty()) { | 
|  | log_debug("unconnected\n"); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | if (others.size() > 1) { | 
|  | log_debug("multiple sinks (%zu)\n", others.size()); | 
|  | flopsOk = false; | 
|  | continue; | 
|  | } | 
|  |  | 
|  | // Get the sink, check if this is a flip-flop | 
|  | auto &other = *others.begin(); | 
|  | auto *flop = other.cell; | 
|  |  | 
|  | if (flop == nullptr) { | 
|  | if (!other.port.empty()) { | 
|  | log_debug("connection reaches module edge\n"); | 
|  | flopsOk = false; | 
|  | } | 
|  | log_debug("unconnected\n"); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | if (!m_FlopTypes.count(flop->type)) { | 
|  | log_debug("non-flip-flop connected\n"); | 
|  | flopsOk = false; | 
|  | continue; | 
|  | } | 
|  |  | 
|  | // Check if the connection goes to the data input/output port | 
|  | const auto &flopType = m_FlopTypes.at(flop->type); | 
|  | RTLIL::IdString flopPort; | 
|  | if (a_Cell->output(port.name)) { | 
|  | flopPort = flopType.ports.at(RTLIL::escape_id("d")); | 
|  | } else if (a_Cell->input(port.name)) { | 
|  | flopPort = flopType.ports.at(RTLIL::escape_id("q")); | 
|  | } | 
|  |  | 
|  | if (flopPort != other.port) { | 
|  | log_debug("connection to non-data port of a flip-flip"); | 
|  | flopsOk = false; | 
|  | continue; | 
|  | } | 
|  |  | 
|  | // Check the flip-flop configuration | 
|  | if (!checkFlop(flop)) { | 
|  | flopsOk = false; | 
|  | continue; | 
|  | } | 
|  |  | 
|  | // Get parameters to be mapped to the DSP according to the port | 
|  | // rule. | 
|  | dict<RTLIL::IdString, RTLIL::Const> mappedParams; | 
|  | for (const auto &it : a_Register.params.map) { | 
|  | if (flop->hasParam(it.second)) { | 
|  | const auto &value = flop->getParam(it.second); | 
|  | mappedParams.insert(std::make_pair(it.first, value)); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Store the flop and its data | 
|  | groups.insert(getFlopData(flop, mappedParams)); | 
|  | flops[port.name][i] = flop; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Cannot integrate for various reasons | 
|  | if (!flopsOk) { | 
|  | log_debug(" cannot use the DSP register\n"); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // No matching flip-flop groups | 
|  | if (groups.empty()) { | 
|  | log_debug(" no matching flip-flops found\n"); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Do not allow more than a single group | 
|  | if (groups.size() != 1) { | 
|  | log_debug(" %zu flip-flop groups, only a single one allowed\n", groups.size()); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Validate the flip flop data agains the DSP cell | 
|  | const auto &flopData = *groups.begin(); | 
|  | if (!checkFlopDataAgainstDspRegister(flopData, a_Cell, a_Register, a_Ports)) { | 
|  | log_debug(" flip-flops vs. DSP check failed\n"); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Log connections | 
|  | for (const auto &port : a_Ports) { | 
|  |  | 
|  | if (!flops.count(port.name)) { | 
|  | continue; | 
|  | } | 
|  |  | 
|  | log(" %s %s.%s\n", a_Cell->type.c_str(), a_Cell->name.c_str(), port.name.c_str()); | 
|  |  | 
|  | const auto &conns = flops.at(port.name); | 
|  | for (size_t i = 0; i < conns.size(); ++i) { | 
|  | if (conns[i] != nullptr) { | 
|  | log_debug("  %2zu. %s %s\n", i, conns[i]->type.c_str(), conns[i]->name.c_str()); | 
|  | } else if ((port.bits.first >= 0 && (int)i < port.bits.first) || (port.bits.second >= 0 && (int)i > port.bits.second)) { | 
|  | log_debug("  %2zu. (excluded)\n", i); | 
|  | } else { | 
|  | log_debug("  %2zu. None\n", i); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // Reconnect data signals, mark the flip-flop for removal | 
|  | const auto &flopType = m_FlopTypes.at(flopData.type); | 
|  | for (const auto &port : a_Ports) { | 
|  |  | 
|  | if (!flops.count(port.name)) { | 
|  | continue; | 
|  | } | 
|  |  | 
|  | const auto &conns = flops.at(port.name); | 
|  | auto sigspec = a_Cell->getPort(port.name); | 
|  | auto sigbits = sigspec.bits(); | 
|  |  | 
|  | for (size_t i = 0; i < conns.size(); ++i) { | 
|  |  | 
|  | auto *flop = conns[i]; | 
|  | if (flop == nullptr) { | 
|  | continue; | 
|  | } | 
|  |  | 
|  | RTLIL::IdString flopPort; | 
|  | if (a_Cell->output(port.name)) { | 
|  | flopPort = flopType.ports.at(RTLIL::escape_id("q")); | 
|  | } else if (a_Cell->input(port.name)) { | 
|  | flopPort = flopType.ports.at(RTLIL::escape_id("d")); | 
|  | } | 
|  |  | 
|  | if (!flop->hasPort(flopPort)) { | 
|  | log_error("cell '%s' does not have port '%s'!\n", flop->type.c_str(), flopPort.c_str()); | 
|  | } | 
|  |  | 
|  | sigbits[i] = SigBit(RTLIL::Sx); | 
|  | auto sigspec = flop->getPort(flopPort); | 
|  | log_assert(sigspec.bits().size() <= 1); | 
|  | if (sigspec.bits().size() == 1) { | 
|  | sigbits[i] = sigspec.bits()[0]; | 
|  | } | 
|  |  | 
|  | m_CellsToRemove.insert(flop); | 
|  | } | 
|  |  | 
|  | a_Cell->setPort(port.name, RTLIL::SigSpec(sigbits)); | 
|  | } | 
|  |  | 
|  | // Reconnect (map) control signals. Connect the default value if | 
|  | // a particular signal is not present in the flip-flop. | 
|  | for (const auto &port : a_Ports) { | 
|  | for (const auto &it : port.assoc) { | 
|  | const auto &key = it.first; | 
|  | const auto &port = it.second.first; | 
|  |  | 
|  | auto conn = RTLIL::SigBit(RTLIL::SigChunk(it.second.second)); | 
|  | if (flopData.conns.count(key)) { | 
|  | conn = flopData.conns.at(key); | 
|  | } | 
|  |  | 
|  | 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 the register rule | 
|  | for (const auto &it : a_Register.connect) { | 
|  | 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 (register rule) | 
|  | for (const auto &it : a_Register.params.map) { | 
|  | if (flopData.params.dsp.count(it.second)) { | 
|  | const auto ¶m = 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); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Map parameters (flip-flop rule) | 
|  | for (const auto &it : flopType.params.map) { | 
|  | if (flopData.params.dsp.count(it.second)) { | 
|  | const auto ¶m = 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); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Set parameters (port rule) | 
|  | for (const auto &it : a_Register.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); | 
|  | } | 
|  | } | 
|  |  | 
|  | // .......................................... | 
|  |  | 
|  | /// Collects flip-flop connectivity data and parameters which defines the | 
|  | /// group it belongs to. | 
|  | FlopData getFlopData(RTLIL::Cell *a_Cell, const dict<RTLIL::IdString, RTLIL::Const> &a_ExtraParams) | 
|  | { | 
|  | FlopData data(a_Cell->type); | 
|  |  | 
|  | log_assert(m_FlopTypes.count(a_Cell->type) != 0); | 
|  | const auto &flopType = m_FlopTypes.at(a_Cell->type); | 
|  |  | 
|  | // Gather connections to control ports | 
|  | for (const auto &it : flopType.ports) { | 
|  |  | 
|  | // Skip "D" and "Q" as they connection will always differ. | 
|  | if (it.first == RTLIL::escape_id("d") || it.first == RTLIL::escape_id("q")) { | 
|  | continue; | 
|  | } | 
|  |  | 
|  | if (!it.second.empty() && a_Cell->hasPort(it.second)) { | 
|  | auto sigspec = a_Cell->getPort(it.second); | 
|  | auto sigbits = sigspec.bits(); | 
|  | log_assert(sigbits.size() <= 1); | 
|  | if (!sigbits.empty()) { | 
|  | data.conns[it.first] = m_SigMap(sigbits[0]); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // Gather flip-flop parameters that need to match | 
|  | for (const auto &it : flopType.params.matching) { | 
|  | log_assert(a_Cell->hasParam(it)); | 
|  | data.params.flop.insert(std::make_pair(it, a_Cell->getParam(it))); | 
|  | } | 
|  |  | 
|  | // Gather flip-flop parameters to be mapped to the DSP as well | 
|  | for (const auto &it : flopType.params.map) { | 
|  | log_assert(a_Cell->hasParam(it.second)); | 
|  | data.params.flop.insert(std::make_pair(it.second, a_Cell->getParam(it.second))); | 
|  | } | 
|  |  | 
|  | // Gather DSP parameters and their values to be set to too | 
|  | for (const auto &it : flopType.params.set) { | 
|  | data.params.dsp.insert(it); | 
|  | } | 
|  |  | 
|  | // Append extra DSP parameters | 
|  | for (const auto &it : a_ExtraParams) { | 
|  | data.params.dsp.insert(it); | 
|  | } | 
|  |  | 
|  | return data; | 
|  | } | 
|  |  | 
|  | } DspFF; | 
|  |  | 
|  | PRIVATE_NAMESPACE_END |