/* * nextpnr -- Next Generation Place and Route * * Copyright (C) 2018 David Shah * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * */ #ifndef UTIL_H #define UTIL_H #include #include #include #include "nextpnr.h" #include "log.h" NEXTPNR_NAMESPACE_BEGIN // Get a value from a map-style container, returning default if value is not // found template ValueType get_or_default(const Container &ct, const KeyType &key, ValueType def = ValueType()) { auto found = ct.find(key); if (found == ct.end()) return def; else return found->second; }; // Get a value from a map-style container, returning default if value is not // found (forces string) template std::string str_or_default(const Container &ct, const KeyType &key, std::string def = "") { auto found = ct.find(key); if (found == ct.end()) return def; else { return found->second; } }; template std::string str_or_default(const std::unordered_map &ct, const KeyType &key, std::string def = "") { auto found = ct.find(key); if (found == ct.end()) return def; else { if (!found->second.is_string) log_error("Expecting string value but got integer %d.\n", int(found->second.intval)); return found->second.as_string(); } }; // Get a value from a map-style container, converting to int, and returning // default if value is not found template int int_or_default(const Container &ct, const KeyType &key, int def = 0) { auto found = ct.find(key); if (found == ct.end()) return def; else return std::stoi(found->second); }; template int int_or_default(const std::unordered_map &ct, const KeyType &key, int def = 0) { auto found = ct.find(key); if (found == ct.end()) return def; else { if (found->second.is_string) { try { return std::stoi(found->second.as_string()); } catch (std::invalid_argument &e) { log_error("Expecting numeric value but got '%s'.\n", found->second.as_string().c_str()); } } else return found->second.as_int64(); } }; // As above, but convert to bool template bool bool_or_default(const Container &ct, const KeyType &key, bool def = false) { return bool(int_or_default(ct, key, int(def))); }; // Wrap an unordered_map, and allow it to be iterated over sorted by key template std::map sorted(const std::unordered_map> &orig) { std::map retVal; for (auto &item : orig) retVal.emplace(std::make_pair(item.first, item.second.get())); return retVal; }; // Wrap an unordered_map, and allow it to be iterated over sorted by key template std::map sorted_ref(std::unordered_map &orig) { std::map retVal; for (auto &item : orig) retVal.emplace(std::make_pair(item.first, std::ref(item.second))); return retVal; }; // Wrap an unordered_map, and allow it to be iterated over sorted by key template std::map sorted_cref(const std::unordered_map &orig) { std::map retVal; for (auto &item : orig) retVal.emplace(std::make_pair(item.first, std::ref(item.second))); return retVal; }; // Wrap an unordered_set, and allow it to be iterated over sorted by key template std::set sorted(const std::unordered_set &orig) { std::set retVal; for (auto &item : orig) retVal.insert(item); return retVal; }; // Return a net if port exists, or nullptr inline const NetInfo *get_net_or_empty(const CellInfo *cell, const IdString port) { auto found = cell->ports.find(port); if (found != cell->ports.end()) return found->second.net; else return nullptr; } inline NetInfo *get_net_or_empty(CellInfo *cell, const IdString port) { auto found = cell->ports.find(port); if (found != cell->ports.end()) return found->second.net; else return nullptr; } // Get only value from a forward iterator begin/end pair. // // Generates assertion failure if std::distance(begin, end) != 1. template inline const typename ForwardIterator::reference get_only_value(ForwardIterator begin, ForwardIterator end) { NPNR_ASSERT(begin != end); const typename ForwardIterator::reference ret = *begin; ++begin; NPNR_ASSERT(begin == end); return ret; } // Get only value from a forward iterator range pair. // // Generates assertion failure if std::distance(r.begin(), r.end()) != 1. template inline auto get_only_value(ForwardRange r) { auto b = r.begin(); auto e = r.end(); return get_only_value(b, e); } // From Yosys // https://github.com/YosysHQ/yosys/blob/0fb4224ebca86156a1296b9210116d9a9cbebeed/kernel/utils.h#L131 template > struct TopoSort { bool analyze_loops, found_loops; std::map, C> database; std::set> loops; std::vector sorted; TopoSort() { analyze_loops = true; found_loops = false; } void node(T n) { if (database.count(n) == 0) database[n] = std::set(); } void edge(T left, T right) { node(left); database[right].insert(left); } void sort_worker(const T &n, std::set &marked_cells, std::set &active_cells, std::vector &active_stack) { if (active_cells.count(n)) { found_loops = true; if (analyze_loops) { std::set loop; for (int i = int(active_stack.size()) - 1; i >= 0; i--) { loop.insert(active_stack[i]); if (active_stack[i] == n) break; } loops.insert(loop); } return; } if (marked_cells.count(n)) return; if (!database.at(n).empty()) { if (analyze_loops) active_stack.push_back(n); active_cells.insert(n); for (auto &left_n : database.at(n)) sort_worker(left_n, marked_cells, active_cells, active_stack); if (analyze_loops) active_stack.pop_back(); active_cells.erase(n); } marked_cells.insert(n); sorted.push_back(n); } bool sort() { loops.clear(); sorted.clear(); found_loops = false; std::set marked_cells; std::set active_cells; std::vector active_stack; for (auto &it : database) sort_worker(it.first, marked_cells, active_cells, active_stack); NPNR_ASSERT(sorted.size() == database.size()); return !found_loops; } }; template struct reversed_range_t { T &obj; explicit reversed_range_t(T &obj) : obj(obj){}; auto begin() { return obj.rbegin(); } auto end() { return obj.rend(); } }; template reversed_range_t reversed_range(T &obj) { return reversed_range_t(obj); } NEXTPNR_NAMESPACE_END #endif