363 lines
9.5 KiB
C++
363 lines
9.5 KiB
C++
/*
|
|
* nextpnr -- Next Generation Place and Route
|
|
*
|
|
* Copyright (C) 2018 gatecat <gatecat@ds0.me>
|
|
* Copyright (C) 2021 William D. Jones <wjones@wdj-consulting.com>
|
|
*
|
|
* 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.
|
|
*
|
|
*/
|
|
|
|
#include "config.h"
|
|
#include <boost/range/adaptor/reversed.hpp>
|
|
#include <iomanip>
|
|
#include <set>
|
|
#include "log.h"
|
|
|
|
NEXTPNR_NAMESPACE_BEGIN
|
|
|
|
#define fmt(x) (static_cast<const std::ostringstream &>(std::ostringstream() << x).str())
|
|
|
|
inline std::string to_string(const std::vector<bool> &bv)
|
|
{
|
|
std::ostringstream os;
|
|
for (auto bit : boost::adaptors::reverse(bv))
|
|
os << (bit ? '1' : '0');
|
|
return os.str();
|
|
}
|
|
|
|
inline std::istream &operator>>(std::istream &in, std::vector<bool> &bv)
|
|
{
|
|
bv.clear();
|
|
std::string s;
|
|
in >> s;
|
|
for (auto c : boost::adaptors::reverse(s)) {
|
|
assert((c == '0') || (c == '1'));
|
|
bv.push_back((c == '1'));
|
|
}
|
|
return in;
|
|
}
|
|
|
|
struct ConfigBit
|
|
{
|
|
int frame;
|
|
int bit;
|
|
bool inv;
|
|
};
|
|
|
|
static ConfigBit cbit_from_str(const std::string &s)
|
|
{
|
|
size_t idx = 0;
|
|
ConfigBit b;
|
|
if (s[idx] == '!') {
|
|
b.inv = true;
|
|
++idx;
|
|
} else {
|
|
b.inv = false;
|
|
}
|
|
NPNR_ASSERT(s[idx] == 'F');
|
|
++idx;
|
|
size_t b_pos = s.find('B');
|
|
NPNR_ASSERT(b_pos != std::string::npos);
|
|
b.frame = stoi(s.substr(idx, b_pos - idx));
|
|
b.bit = stoi(s.substr(b_pos + 1));
|
|
return b;
|
|
}
|
|
|
|
inline std::string to_string(ConfigBit b)
|
|
{
|
|
std::ostringstream ss;
|
|
if (b.inv)
|
|
ss << "!";
|
|
ss << "F" << b.frame;
|
|
ss << "B" << b.bit;
|
|
return ss.str();
|
|
}
|
|
|
|
// Skip whitespace, optionally including newlines
|
|
inline void skip_blank(std::istream &in, bool nl = false)
|
|
{
|
|
int c = in.peek();
|
|
while (in && (((c == ' ') || (c == '\t')) || (nl && ((c == '\n') || (c == '\r'))))) {
|
|
in.get();
|
|
c = in.peek();
|
|
}
|
|
}
|
|
// Return true if end of line (or file)
|
|
inline bool skip_check_eol(std::istream &in)
|
|
{
|
|
skip_blank(in, false);
|
|
if (!in)
|
|
return false;
|
|
int c = in.peek();
|
|
// Comments count as end of line
|
|
if (c == '#') {
|
|
in.get();
|
|
c = in.peek();
|
|
while (in && c != EOF && c != '\n') {
|
|
in.get();
|
|
c = in.peek();
|
|
}
|
|
return true;
|
|
}
|
|
return (c == EOF || c == '\n');
|
|
}
|
|
|
|
// Skip past blank lines and comments
|
|
inline void skip(std::istream &in)
|
|
{
|
|
skip_blank(in, true);
|
|
while (in && (in.peek() == '#')) {
|
|
// Skip comment line
|
|
skip_check_eol(in);
|
|
skip_blank(in, true);
|
|
}
|
|
}
|
|
|
|
// Return true if at the end of a record (or file)
|
|
inline bool skip_check_eor(std::istream &in)
|
|
{
|
|
skip(in);
|
|
int c = in.peek();
|
|
return (c == EOF || c == '.');
|
|
}
|
|
|
|
// Return true if at the end of file
|
|
inline bool skip_check_eof(std::istream &in)
|
|
{
|
|
skip(in);
|
|
int c = in.peek();
|
|
return (c == EOF);
|
|
}
|
|
|
|
std::ostream &operator<<(std::ostream &out, const ConfigArc &arc)
|
|
{
|
|
out << "arc: " << arc.sink << " " << arc.source << std::endl;
|
|
return out;
|
|
}
|
|
|
|
std::istream &operator>>(std::istream &in, ConfigArc &arc)
|
|
{
|
|
in >> arc.sink;
|
|
in >> arc.source;
|
|
return in;
|
|
}
|
|
|
|
std::ostream &operator<<(std::ostream &out, const ConfigWord &cw)
|
|
{
|
|
out << "word: " << cw.name << " " << to_string(cw.value) << std::endl;
|
|
return out;
|
|
}
|
|
|
|
std::istream &operator>>(std::istream &in, ConfigWord &cw)
|
|
{
|
|
in >> cw.name;
|
|
in >> cw.value;
|
|
return in;
|
|
}
|
|
|
|
std::ostream &operator<<(std::ostream &out, const ConfigEnum &cw)
|
|
{
|
|
out << "enum: " << cw.name << " " << cw.value << std::endl;
|
|
return out;
|
|
}
|
|
|
|
std::istream &operator>>(std::istream &in, ConfigEnum &ce)
|
|
{
|
|
in >> ce.name;
|
|
in >> ce.value;
|
|
return in;
|
|
}
|
|
|
|
std::ostream &operator<<(std::ostream &out, const ConfigUnknown &cu)
|
|
{
|
|
out << "unknown: " << to_string(ConfigBit{cu.frame, cu.bit, false}) << std::endl;
|
|
return out;
|
|
}
|
|
|
|
std::istream &operator>>(std::istream &in, ConfigUnknown &cu)
|
|
{
|
|
std::string s;
|
|
in >> s;
|
|
ConfigBit c = cbit_from_str(s);
|
|
cu.frame = c.frame;
|
|
cu.bit = c.bit;
|
|
assert(!c.inv);
|
|
return in;
|
|
}
|
|
|
|
std::ostream &operator<<(std::ostream &out, const TileConfig &tc)
|
|
{
|
|
for (const auto &arc : tc.carcs)
|
|
out << arc;
|
|
for (const auto &cword : tc.cwords)
|
|
out << cword;
|
|
for (const auto &cenum : tc.cenums)
|
|
out << cenum;
|
|
for (const auto &cunk : tc.cunknowns)
|
|
out << cunk;
|
|
return out;
|
|
}
|
|
|
|
std::istream &operator>>(std::istream &in, TileConfig &tc)
|
|
{
|
|
tc.carcs.clear();
|
|
tc.cwords.clear();
|
|
tc.cenums.clear();
|
|
while (!skip_check_eor(in)) {
|
|
std::string type;
|
|
in >> type;
|
|
if (type == "arc:") {
|
|
ConfigArc a;
|
|
in >> a;
|
|
tc.carcs.push_back(a);
|
|
} else if (type == "word:") {
|
|
ConfigWord w;
|
|
in >> w;
|
|
tc.cwords.push_back(w);
|
|
} else if (type == "enum:") {
|
|
ConfigEnum e;
|
|
in >> e;
|
|
tc.cenums.push_back(e);
|
|
} else if (type == "unknown:") {
|
|
ConfigUnknown u;
|
|
in >> u;
|
|
tc.cunknowns.push_back(u);
|
|
} else {
|
|
NPNR_ASSERT_FALSE_STR("unexpected token " + type + " while reading config text");
|
|
}
|
|
}
|
|
return in;
|
|
}
|
|
|
|
void TileConfig::add_arc(const std::string &sink, const std::string &source) { carcs.push_back({sink, source}); }
|
|
|
|
void TileConfig::add_word(const std::string &name, const std::vector<bool> &value) { cwords.push_back({name, value}); }
|
|
|
|
void TileConfig::add_enum(const std::string &name, const std::string &value) { cenums.push_back({name, value}); }
|
|
|
|
void TileConfig::add_unknown(int frame, int bit) { cunknowns.push_back({frame, bit}); }
|
|
|
|
std::string TileConfig::to_string() const
|
|
{
|
|
std::stringstream ss;
|
|
ss << *this;
|
|
return ss.str();
|
|
}
|
|
|
|
TileConfig TileConfig::from_string(const std::string &str)
|
|
{
|
|
std::stringstream ss(str);
|
|
TileConfig tc;
|
|
ss >> tc;
|
|
return tc;
|
|
}
|
|
|
|
bool TileConfig::empty() const { return carcs.empty() && cwords.empty() && cenums.empty() && cunknowns.empty(); }
|
|
|
|
std::ostream &operator<<(std::ostream &out, const ChipConfig &cc)
|
|
{
|
|
out << ".device " << cc.chip_name << std::endl << std::endl;
|
|
out << ".variant " << cc.chip_variant << std::endl << std::endl;
|
|
for (const auto &meta : cc.metadata)
|
|
out << ".comment " << meta << std::endl;
|
|
for (const auto &sc : cc.sysconfig)
|
|
out << ".sysconfig " << sc.first << " " << sc.second << std::endl;
|
|
out << std::endl;
|
|
for (const auto &tile : cc.tiles) {
|
|
if (!tile.second.empty()) {
|
|
out << ".tile " << tile.first << std::endl;
|
|
out << tile.second;
|
|
out << std::endl;
|
|
}
|
|
}
|
|
for (const auto &bram : cc.bram_data) {
|
|
out << ".bram_init " << bram.first << std::endl;
|
|
std::ios_base::fmtflags f(out.flags());
|
|
for (size_t i = 0; i < bram.second.size(); i++) {
|
|
out << std::setw(3) << std::setfill('0') << std::hex << bram.second.at(i);
|
|
if (i % 8 == 7)
|
|
out << std::endl;
|
|
else
|
|
out << " ";
|
|
}
|
|
out.flags(f);
|
|
out << std::endl;
|
|
}
|
|
for (const auto &tg : cc.tilegroups) {
|
|
out << ".tile_group";
|
|
for (const auto &tile : tg.tiles) {
|
|
out << " " << tile;
|
|
}
|
|
out << std::endl;
|
|
out << tg.config;
|
|
out << std::endl;
|
|
}
|
|
return out;
|
|
}
|
|
|
|
std::istream &operator>>(std::istream &in, ChipConfig &cc)
|
|
{
|
|
while (!skip_check_eof(in)) {
|
|
std::string verb;
|
|
in >> verb;
|
|
if (verb == ".device") {
|
|
in >> cc.chip_name;
|
|
} else if (verb == ".variant") {
|
|
in >> cc.chip_variant;
|
|
} else if (verb == ".comment") {
|
|
std::string line;
|
|
getline(in, line);
|
|
cc.metadata.push_back(line);
|
|
} else if (verb == ".sysconfig") {
|
|
std::string key, value;
|
|
in >> key >> value;
|
|
cc.sysconfig[key] = value;
|
|
} else if (verb == ".tile") {
|
|
std::string tilename;
|
|
in >> tilename;
|
|
TileConfig tc;
|
|
in >> tc;
|
|
cc.tiles[tilename] = tc;
|
|
} else if (verb == ".tile_group") {
|
|
TileGroup tg;
|
|
std::string line;
|
|
getline(in, line);
|
|
std::stringstream ss2(line);
|
|
|
|
std::string tile;
|
|
while (ss2) {
|
|
ss2 >> tile;
|
|
tg.tiles.push_back(tile);
|
|
}
|
|
in >> tg.config;
|
|
cc.tilegroups.push_back(tg);
|
|
} else if (verb == ".bram_init") {
|
|
uint16_t bram;
|
|
in >> bram;
|
|
std::ios_base::fmtflags f(in.flags());
|
|
while (!skip_check_eor(in)) {
|
|
uint16_t value;
|
|
in >> std::hex >> value;
|
|
cc.bram_data[bram].push_back(value);
|
|
}
|
|
in.flags(f);
|
|
} else {
|
|
log_error("unrecognised config entry %s\n", verb.c_str());
|
|
}
|
|
}
|
|
return in;
|
|
}
|
|
|
|
NEXTPNR_NAMESPACE_END
|