| #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!\n"); | |
| } | |
| // 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 |