| #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 |