nextpnr/common/kernel/util.h
gatecat 9b51c6e337 clangformat
Signed-off-by: gatecat <gatecat@ds0.me>
2024-09-30 14:51:33 +02:00

266 lines
7.9 KiB
C++

/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 gatecat <gatecat@ds0.me>
*
* 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 <map>
#include <set>
#include <string>
#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 <typename Container, typename KeyType, typename ValueType>
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 <typename Container, typename KeyType>
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 <typename KeyType>
std::string str_or_default(const dict<KeyType, Property> &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 <typename Container, typename KeyType> 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 <typename KeyType> int int_or_default(const dict<KeyType, Property> &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 <typename Container, typename KeyType>
bool bool_or_default(const Container &ct, const KeyType &key, bool def = false)
{
return bool(int_or_default(ct, key, int(def)));
};
// Get a bool from a map-style container, returning default if value is not found
// Also tolerate string representation of boolean for interoperability purposes
template <typename KeyType>
bool boolstr_or_default(const dict<KeyType, Property> &ct, const KeyType &key, bool def = false)
{
auto found = ct.find(key);
if (found == ct.end())
return def;
if (!found->second.is_string)
bool(found->second.as_int64());
const char *str = found->second.as_string().c_str();
if (!strcmp(str, "0") || !strcasecmp(str, "false"))
return false;
else if (!strcmp(str, "1") || !strcasecmp(str, "true"))
return true;
else
log_error("Expecting bool-compatible value but got '%s'.\n", found->second.as_string().c_str());
return false;
};
// Get a vector of bool from a map-style container, returning default if value is not found
// Also tolerate string representation of vector for interoperability purposes
template <typename KeyType>
bool boolvec_populate(const dict<KeyType, Property> &ct, const KeyType &key, std::vector<bool> &vec)
{
auto found = ct.find(key);
if (found == ct.end())
return false;
if (!found->second.is_string) {
size_t val = found->second.as_int64();
for (size_t i = 0; i < vec.size(); ++i, val >>= 1) {
vec[i] = (val & 0x1) != 0;
}
} else {
const std::string &str = found->second.as_string();
size_t i = 0;
for (auto it = str.crbegin(); it != str.crend() && i < vec.size(); ++i, ++it) {
vec[i] = *it == '1';
}
}
return true;
};
// Get only value from a forward iterator begin/end pair.
//
// Generates assertion failure if std::distance(begin, end) != 1.
template <typename ForwardIterator>
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 <typename ForwardRange> 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 <typename T, typename C = std::less<T>> struct TopoSort
{
bool analyze_loops, found_loops;
std::map<T, std::set<T, C>, C> database;
std::set<std::set<T, C>> loops;
std::vector<T> sorted;
TopoSort()
{
analyze_loops = true;
found_loops = false;
}
void node(T n)
{
if (database.count(n) == 0)
database[n] = std::set<T, C>();
}
void edge(T left, T right)
{
node(left);
database[right].insert(left);
}
void sort_worker(const T &n, std::set<T, C> &marked_cells, std::set<T, C> &active_cells,
std::vector<T> &active_stack)
{
if (active_cells.count(n)) {
found_loops = true;
if (analyze_loops) {
std::set<T, C> 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<T, C> marked_cells;
std::set<T, C> active_cells;
std::vector<T> 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 <typename T> 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 <typename T> reversed_range_t<T> reversed_range(T &obj) { return reversed_range_t<T>(obj); }
NEXTPNR_NAMESPACE_END
#endif