blob: d981d46a4ecc7d4758ded2c40a740d1ef07ff984 [file] [log] [blame]
#include "BitDatabase.hpp"
#include "CRAM.hpp"
#include "TileConfig.hpp"
#include "Tile.hpp"
#include "RoutingGraph.hpp"
#include <algorithm>
#include <fstream>
#include <boost/thread/shared_lock_guard.hpp>
#include <boost/thread/lock_guard.hpp>
#include <boost/range/algorithm/copy.hpp>
#include <boost/range/adaptors.hpp>
namespace Trellis {
ConfigBit cbit_from_str(const string &s)
{
size_t idx = 0;
ConfigBit b;
if (s[idx] == '!') {
b.inv = true;
++idx;
} else {
b.inv = false;
}
assert(s[idx] == 'F');
++idx;
size_t b_pos = s.find('B');
assert(b_pos != string::npos);
b.frame = stoi(s.substr(idx, b_pos - idx));
b.bit = stoi(s.substr(b_pos + 1));
return b;
}
BitGroup::BitGroup()
{}
BitGroup::BitGroup(const CRAMDelta &delta)
{
for (const auto &bit: delta) {
if (bit.delta != 0)
bits.insert(ConfigBit{bit.frame, bit.bit, (bit.delta < 0)});
}
}
bool BitGroup::match(const CRAMView &tile) const
{
return all_of(bits.begin(), bits.end(), [tile](const ConfigBit &b) {
return tile.bit(b.frame, b.bit) != b.inv;
});
}
void BitGroup::set_group(CRAMView &tile) const
{
for (auto bit : bits)
tile.bit(bit.frame, bit.bit) = !bit.inv;
}
void BitGroup::clear_group(Trellis::CRAMView &tile) const
{
for (auto bit : bits)
tile.bit(bit.frame, bit.bit) = bit.inv;
}
void BitGroup::add_coverage(Trellis::BitSet &known_bits, bool value) const
{
for (const auto &b : bits) {
if (b.inv != value)
known_bits.insert(ConfigBit{b.frame, b.bit});
}
}
ostream &operator<<(ostream &out, const BitGroup &bits)
{
bool first = true;
if (bits.bits.empty()) {
out << "-";
} else {
for (auto bit : bits.bits) {
if (!first)
out << " ";
out << to_string(bit);
first = false;
}
}
return out;
}
istream &operator>>(istream &in, BitGroup &bits)
{
bits.bits.clear();
while (!skip_check_eol(in)) {
string s;
in >> s;
if (s == "-")
break;
bits.bits.insert(cbit_from_str(s));
}
return in;
}
vector<string> MuxBits::get_sources() const
{
vector<string> result;
boost::copy(arcs | boost::adaptors::map_keys, back_inserter(result));
return result;
}
boost::optional<string> MuxBits::get_driver(const CRAMView &tile, boost::optional<BitSet &> coverage) const
{
boost::optional<const ArcData &> bestmatch;
size_t bestbits = 0;
for (const auto &arc : arcs) {
if (arc.second.bits.match(tile) && arc.second.bits.bits.size() >= bestbits) {
bestmatch = arc.second;
bestbits = arc.second.bits.bits.size();
}
}
if (!bestmatch) {
return boost::optional<string>();
} else {
if (coverage)
bestmatch->bits.add_coverage(*coverage);
return boost::optional<string>(bestmatch->source);
}
}
void MuxBits::set_driver(Trellis::CRAMView &tile, const string &driver) const
{
auto drv = arcs.find(driver);
if (drv == arcs.end()) {
throw runtime_error("sink " + sink + " has no driver named " + driver);
}
drv->second.bits.set_group(tile);
}
ostream &operator<<(ostream &out, const MuxBits &mux)
{
out << ".mux " << mux.sink << endl;
for (const auto &arc : mux.arcs) {
out << arc.first << " " << arc.second.bits << endl;
}
return out;
}
istream &operator>>(istream &in, MuxBits &mux)
{
in >> mux.sink;
mux.arcs.clear();
// Read arc source-bits pairs until end of record
while (!skip_check_eor(in)) {
ArcData a;
a.sink = mux.sink;
in >> a.source >> a.bits;
mux.arcs[a.source] = a;
}
return in;
}
boost::optional<vector<bool>>
WordSettingBits::get_value(const CRAMView &tile, boost::optional<BitSet &> coverage) const
{
vector<bool> val;
transform(bits.begin(), bits.end(), back_inserter(val), [tile, coverage](const BitGroup &b) {
bool m = b.match(tile);
if (coverage)
b.add_coverage(*coverage, m);
return m;
});
if (val == defval)
return boost::optional<vector<bool>>();
else
return boost::optional<vector<bool>>(val);
}
void WordSettingBits::set_value(Trellis::CRAMView &tile, const vector<bool> &value) const
{
assert(value.size() == bits.size());
for (size_t i = 0; i < bits.size(); i++) {
if (value.at(i))
bits.at(i).set_group(tile);
else
bits.at(i).clear_group(tile);
}
}
ostream &operator<<(ostream &out, const WordSettingBits &ws)
{
out << ".config " << ws.name << " " << to_string(ws.defval) << endl;
for (const auto &bit : ws.bits) {
out << bit << endl;
}
return out;
}
istream &operator>>(istream &in, WordSettingBits &ws)
{
in >> ws.name;
bool have_default = false;
if (!skip_check_eol(in)) {
in >> ws.defval;
have_default = true;
}
ws.bits.clear();
while (!skip_check_eor(in)) {
BitGroup bg;
in >> bg;
ws.bits.push_back(bg);
}
if (!have_default) {
ws.defval.clear();
ws.defval.resize(ws.bits.size(), false);
}
return in;
}
void EnumSettingBits::set_defval(string val)
{
defval = val;
}
string EnumSettingBits::get_defval() const
{
if (defval)
return *defval;
else
return "";
}
vector<string> EnumSettingBits::get_options() const
{
vector<string> result;
boost::copy(options | boost::adaptors::map_keys, back_inserter(result));
return result;
}
boost::optional<string> EnumSettingBits::get_value(const CRAMView &tile, boost::optional<BitSet &> coverage) const
{
boost::optional<const pair<const string, BitGroup> &> bestmatch;
size_t bestbits = 0;
for (const auto &opt : options) {
if (opt.second.match(tile) && opt.second.bits.size() >= bestbits) {
bestmatch = opt;
bestbits = opt.second.bits.size();
}
}
if (!bestmatch) {
if (defval) {
return boost::optional<string>("_NONE_");
} else {
return boost::optional<string>();
}
} else {
if (coverage)
bestmatch->second.add_coverage(*coverage);
if (defval && (options.at(*defval) == bestmatch->second)) {
return boost::optional<string>();
} else {
return boost::optional<string>(bestmatch->first);
}
}
}
void EnumSettingBits::set_value(Trellis::CRAMView &tile, const string &value) const
{
if (value != "_NONE_") {
if(options.find(value) != options.end()) {
auto grp = options.at(value);
grp.set_group(tile);
}
else {
cerr << "EnumSettingBits::set_value: cannot set " << value << endl;
cerr << "In Options: " << endl;
for(auto it = options.begin(); it != options.end(); ++it){
cerr << it->first << " -> " << it->second << endl;
}
exit(1);
}
}
}
ostream &operator<<(ostream &out, const EnumSettingBits &es)
{
out << ".config_enum " << es.name;
if (es.defval)
out << " " << *(es.defval);
out << endl;
for (const auto &opt : es.options) {
out << opt.first << " " << opt.second << endl;
}
return out;
}
istream &operator>>(istream &in, EnumSettingBits &es)
{
in >> es.name;
if (!skip_check_eol(in)) {
string s;
in >> s;
es.defval = boost::make_optional(s);
} else {
es.defval = boost::optional<string>();
}
es.options.clear();
while (!skip_check_eor(in)) {
string opt;
BitGroup bg;
in >> opt >> bg;
es.options[opt] = bg;
}
return in;
}
ostream &operator<<(ostream &out, const FixedConnection &es)
{
out << ".fixed_conn " << es.sink << " " << es.source << endl;
return out;
}
istream &operator>>(istream &in, FixedConnection &es)
{
in >> es.sink >> es.source;
return in;
}
TileBitDatabase::TileBitDatabase(const string &filename) : filename(filename)
{
load();
}
void TileBitDatabase::config_to_tile_cram(const TileConfig &cfg, CRAMView &tile, bool is_tilegroup, set<string> *tg_matches) const
{
boost::shared_lock_guard<boost::shared_mutex> guard(db_mutex);
for (auto arc : cfg.carcs)
muxes.at(arc.sink).set_driver(tile, arc.source);
set<string> found_words, found_enums;
// Make sure "base" enums like IO type are applied first, other settings may overlay onto them later
const string base_prefix = "BASE_";
for (auto ce : cfg.cenums) {
if (ce.name.substr(0, base_prefix.length()) == base_prefix) {
if (is_tilegroup && !enums.count(ce.name))
continue;
if (is_tilegroup && !enums.at(ce.name).options.count(ce.value))
continue;
if (tg_matches)
tg_matches->insert(ce.name);
enums.at(ce.name).set_value(tile, ce.value);
found_enums.insert(ce.name);
}
}
for (auto cw : cfg.cwords) {
if (is_tilegroup && !words.count(cw.name))
continue;
if (tg_matches)
tg_matches->insert(cw.name);
words.at(cw.name).set_value(tile, cw.value);
found_words.insert(cw.name);
}
for (auto ce : cfg.cenums) {
if (ce.name.substr(0, base_prefix.length()) != base_prefix) {
if (is_tilegroup && !enums.count(ce.name))
continue;
if (is_tilegroup && !enums.at(ce.name).options.count(ce.value))
continue;
if (tg_matches)
tg_matches->insert(ce.name);
enums.at(ce.name).set_value(tile, ce.value);
found_enums.insert(ce.name);
}
}
for (auto unk : cfg.cunknowns) {
tile.bit(unk.frame, unk.bit) = 1;
}
// Apply default values if not overriden in cfg
if (!is_tilegroup) {
for (auto w : words)
if (found_words.find(w.first) == found_words.end())
w.second.set_value(tile, w.second.defval);
for (auto e : enums)
if (found_enums.find(e.first) == found_enums.end())
if (e.second.defval)
e.second.set_value(tile, *e.second.defval);
}
}
TileConfig TileBitDatabase::tile_cram_to_config(const CRAMView &tile) const
{
boost::shared_lock_guard<boost::shared_mutex> guard(db_mutex);
TileConfig cfg;
BitSet coverage;
for (auto mux : muxes) {
auto sink = mux.second.get_driver(tile, coverage);
if (sink && mux.second.arcs.at(*sink).bits.bits.size() > 0)
cfg.carcs.push_back(ConfigArc{mux.first, *sink});
}
for (auto cw : words) {
auto val = cw.second.get_value(tile, coverage);
if (val)
cfg.cwords.push_back(ConfigWord{cw.first, *val});
}
for (auto ce : enums) {
auto val = ce.second.get_value(tile, coverage);
if (val)
cfg.cenums.push_back(ConfigEnum{ce.first, *val});
}
for (int f = 0; f < tile.frames(); f++) {
for (int b = 0; b < tile.bits(); b++) {
if (tile.bit(f, b)) {
if (coverage.find(ConfigBit{f, b, false}) == coverage.end()) {
cfg.cunknowns.push_back(ConfigUnknown{f, b});
} else {
cfg.total_known_bits++;
}
}
}
};
return cfg;
}
void TileBitDatabase::load()
{
boost::lock_guard<boost::shared_mutex> guard(db_mutex);
ifstream in(filename);
if (!in) {
throw runtime_error("failed to open tilebit database file " + filename);
}
muxes.clear();
words.clear();
enums.clear();
while (!skip_check_eof(in)) {
string token;
in >> token;
if (token == ".mux") {
MuxBits mux;
in >> mux;
muxes[mux.sink] = mux;
} else if (token == ".config") {
WordSettingBits cw;
in >> cw;
words[cw.name] = cw;
} else if (token == ".config_enum") {
EnumSettingBits ce;
in >> ce;
enums[ce.name] = ce;
} else if (token == ".fixed_conn") {
FixedConnection c;
in >> c;
fixed_conns[c.sink].insert(c);
} else {
throw runtime_error("unexpected token " + token + " while parsing database file " + filename);
}
}
}
void TileBitDatabase::save()
{
boost::lock_guard<boost::shared_mutex> guard(db_mutex);
ofstream out(filename);
if (!out) {
throw runtime_error("failed to open tilebit database file " + filename + " for writing");
}
out << "# Routing Mux Bits" << endl;
for (auto mux : muxes)
out << mux.second << endl;
out << endl << "# Non-Routing Configuration" << endl;
for (auto word : words)
out << word.second << endl;
for (auto senum : enums)
out << senum.second << endl;
out << endl << "# Fixed Connections" << endl;
for (auto conns : fixed_conns)
for (auto conn2 : conns.second)
out << conn2 << endl;
dirty = false;
}
vector<string> TileBitDatabase::get_sinks() const
{
boost::shared_lock_guard<boost::shared_mutex> guard(db_mutex);
vector<string> result;
boost::copy(muxes | boost::adaptors::map_keys, back_inserter(result));
return result;
}
MuxBits TileBitDatabase::get_mux_data_for_sink(const string &sink) const
{
boost::shared_lock_guard<boost::shared_mutex> guard(db_mutex);
return muxes.at(sink);
}
vector<string> TileBitDatabase::get_settings_words() const
{
boost::shared_lock_guard<boost::shared_mutex> guard(db_mutex);
vector<string> result;
boost::copy(words | boost::adaptors::map_keys, back_inserter(result));
return result;
}
WordSettingBits TileBitDatabase::get_data_for_setword(const string &name) const
{
boost::shared_lock_guard<boost::shared_mutex> guard(db_mutex);
return words.at(name);
}
vector<string> TileBitDatabase::get_settings_enums() const
{
boost::shared_lock_guard<boost::shared_mutex> guard(db_mutex);
vector<string> result;
boost::copy(enums | boost::adaptors::map_keys, back_inserter(result));
return result;
}
EnumSettingBits TileBitDatabase::get_data_for_enum(const string &name) const
{
boost::shared_lock_guard<boost::shared_mutex> guard(db_mutex);
return enums.at(name);
}
vector<FixedConnection> TileBitDatabase::get_fixed_conns() const
{
boost::shared_lock_guard<boost::shared_mutex> guard(db_mutex);
vector<FixedConnection> result;
for (const auto &csink : fixed_conns) {
for (const auto &conn : csink.second) {
result.push_back(conn);
}
}
return result;
}
vector<pair<string, bool>> TileBitDatabase::get_downhill_wires(const string &wire) const
{
vector<pair<string, bool>> dhwires;
for (const auto &mux : muxes) {
for (const auto &arc : mux.second.arcs) {
if (arc.second.source == wire)
dhwires.push_back(make_pair(arc.second.sink, true));
}
}
for (const auto &csink : fixed_conns) {
for (const auto &conn : csink.second) {
if (conn.source == wire)
dhwires.push_back(make_pair(conn.sink, false));
}
}
return dhwires;
}
void TileBitDatabase::add_routing(const TileInfo &tile, RoutingGraph &graph) const
{
boost::shared_lock_guard<boost::shared_mutex> guard(db_mutex);
int row, col;
tie(row, col) = tile.get_row_col();
Location loc(col, row);
for (const auto &mux : muxes) {
RoutingId sink = graph.globalise_net(row, col, mux.second.sink);
if (sink == RoutingId())
continue;
for (const auto &arc : mux.second.arcs) {
RoutingId src = graph.globalise_net(row, col, arc.second.source);
if (src == RoutingId())
continue;
RoutingArc rarc;
rarc.id = graph.ident(arc.second.source + "->" + arc.second.sink);
rarc.source = src;
rarc.sink = sink;
rarc.tiletype = graph.ident(tile.type);
rarc.configurable = true;
graph.add_arc(loc, rarc);
}
}
for (const auto &fcs : fixed_conns) {
for (const auto &fc : fcs.second) {
RoutingId sink = graph.globalise_net(row, col, fc.sink);
if (sink == RoutingId())
continue;
RoutingId src = graph.globalise_net(row, col, fc.source);
if (src == RoutingId())
continue;
RoutingArc rarc;
rarc.id = graph.ident(fc.source + "=>" + fc.sink);
rarc.source = src;
rarc.sink = sink;
rarc.tiletype = graph.ident(tile.type);
rarc.configurable = false;
graph.add_arc(loc, rarc);
}
}
}
void TileBitDatabase::add_mux_arc(const ArcData &arc)
{
boost::lock_guard<boost::shared_mutex> guard(db_mutex);
dirty = true;
if (muxes.find(arc.sink) == muxes.end()) {
MuxBits mux;
mux.sink = arc.sink;
muxes[mux.sink] = mux;
}
MuxBits &curr = muxes.at(arc.sink);
auto found = curr.arcs.find(arc.source);
if (found == curr.arcs.end()) {
curr.arcs[arc.source] = arc;
} else {
if (found->second.bits == arc.bits) {
// In DB already, no-op
} else {
throw DatabaseConflictError(fmt("database conflict: arc " << arc.source << " -> " << arc.sink <<
" already in DB, but config bits " <<
arc.bits
<< " don't match existing DB bits " <<
found->second.bits));
}
}
}
void TileBitDatabase::add_setting_word(const WordSettingBits &wsb)
{
boost::lock_guard<boost::shared_mutex> guard(db_mutex);
dirty = true;
if (words.find(wsb.name) != words.end()) {
WordSettingBits &curr = words.at(wsb.name);
if (curr.bits.size() != wsb.bits.size()) {
throw DatabaseConflictError(fmt("word " << curr.name << " already exists in DB, but new size "
<< wsb.bits.size() << " does not match existing size "
<< curr.bits.size()));
}
for (size_t i = 0; i < curr.bits.size(); i++) {
if (!(curr.bits.at(i) == wsb.bits.at(i))) {
throw DatabaseConflictError(fmt("bit " << wsb.name << "[" << i << "] already in DB, but config bits "
<< wsb.bits.at(i) << " don't match existing DB bits "
<< curr.bits.at(i)));
}
}
} else {
words[wsb.name] = wsb;
}
}
void TileBitDatabase::add_setting_enum(const EnumSettingBits &esb)
{
boost::lock_guard<boost::shared_mutex> guard(db_mutex);
dirty = true;
if (enums.find(esb.name) != enums.end()) {
EnumSettingBits &curr = enums.at(esb.name);
for (const auto &opt : esb.options) {
if (curr.options.find(opt.first) == curr.options.end()) {
curr.options[opt.first] = opt.second;
} else {
if (curr.options.at(opt.first) == opt.second) {
// No-op
} else {
throw DatabaseConflictError(
fmt("option " << opt.first << " of " << esb.name << " already in DB, but config bits "
<< opt.second << " don't match existing DB bits "
<< curr.options.at(opt.first)));
}
}
}
}
enums[esb.name] = esb;
}
void TileBitDatabase::add_fixed_conn(const Trellis::FixedConnection &conn)
{
boost::lock_guard<boost::shared_mutex> guard(db_mutex);
fixed_conns[conn.sink].insert(conn);
dirty = true;
}
TileBitDatabase::TileBitDatabase(const TileBitDatabase &other)
{
UNUSED(other);
assert(false);
terminate();
}
void TileBitDatabase::remove_fixed_sink(const string &sink)
{
boost::lock_guard<boost::shared_mutex> guard(db_mutex);
fixed_conns.erase(sink);
}
void TileBitDatabase::remove_setting_enum(const string &enum_name)
{
boost::lock_guard<boost::shared_mutex> guard(db_mutex);
enums.erase(enum_name);
}
void TileBitDatabase::remove_setting_word(const string &word_name)
{
boost::lock_guard<boost::shared_mutex> guard(db_mutex);
words.erase(word_name);
}
DatabaseConflictError::DatabaseConflictError(const string &desc) : runtime_error(desc)
{}
TileBitDatabase::~TileBitDatabase()
{
if (dirty)
save();
}
}