ecp5: Adding configuration data structures
Signed-off-by: David Shah <davey1576@gmail.com>
This commit is contained in:
parent
2743d0fa9d
commit
305145ffe4
304
ecp5/config.cc
Normal file
304
ecp5/config.cc
Normal file
@ -0,0 +1,304 @@
|
||||
/*
|
||||
* nextpnr -- Next Generation Place and Route
|
||||
*
|
||||
* Copyright (C) 2018 David Shah <david@symbioticeda.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 "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;
|
||||
for (const auto &meta : cc.metadata)
|
||||
out << ".comment " << meta << 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;
|
||||
}
|
||||
}
|
||||
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 == ".comment") {
|
||||
std::string line;
|
||||
getline(in, line);
|
||||
cc.metadata.push_back(line);
|
||||
} else if (verb == ".tile") {
|
||||
std::string tilename;
|
||||
in >> tilename;
|
||||
TileConfig tc;
|
||||
in >> tc;
|
||||
cc.tiles[tilename] = tc;
|
||||
} else {
|
||||
log_error("unrecognised config entry %s\n", verb.c_str());
|
||||
}
|
||||
}
|
||||
return in;
|
||||
}
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
115
ecp5/config.h
Normal file
115
ecp5/config.h
Normal file
@ -0,0 +1,115 @@
|
||||
/*
|
||||
* nextpnr -- Next Generation Place and Route
|
||||
*
|
||||
* Copyright (C) 2018 David Shah <david@symbioticeda.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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ECP5_CONFIG_H
|
||||
#define ECP5_CONFIG_H
|
||||
|
||||
#include "nextpnr.h"
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
// This represents configuration at "FASM" level, in terms of routing arcs and non-routing configuration settings -
|
||||
// either words or enums.
|
||||
|
||||
// A connection in a tile
|
||||
struct ConfigArc
|
||||
{
|
||||
std::string sink;
|
||||
std::string source;
|
||||
inline bool operator==(const ConfigArc &other) const { return other.source == source && other.sink == sink; }
|
||||
};
|
||||
|
||||
std::ostream &operator<<(std::ostream &out, const ConfigArc &arc);
|
||||
|
||||
std::istream &operator>>(std::istream &in, ConfigArc &arc);
|
||||
|
||||
// A configuration setting in a tile that takes one or more bits (such as LUT init)
|
||||
struct ConfigWord
|
||||
{
|
||||
std::string name;
|
||||
std::vector<bool> value;
|
||||
inline bool operator==(const ConfigWord &other) const { return other.name == name && other.value == value; }
|
||||
};
|
||||
|
||||
std::ostream &operator<<(std::ostream &out, const ConfigWord &cw);
|
||||
|
||||
std::istream &operator>>(std::istream &in, ConfigWord &cw);
|
||||
|
||||
// A configuration setting in a tile that takes an enumeration value (such as IO type)
|
||||
struct ConfigEnum
|
||||
{
|
||||
std::string name;
|
||||
std::string value;
|
||||
inline bool operator==(const ConfigEnum &other) const { return other.name == name && other.value == value; }
|
||||
};
|
||||
|
||||
std::ostream &operator<<(std::ostream &out, const ConfigEnum &ce);
|
||||
|
||||
std::istream &operator>>(std::istream &in, ConfigEnum &ce);
|
||||
|
||||
// An unknown bit, specified by position only
|
||||
struct ConfigUnknown
|
||||
{
|
||||
int frame, bit;
|
||||
inline bool operator==(const ConfigUnknown &other) const { return other.frame == frame && other.bit == bit; }
|
||||
};
|
||||
|
||||
std::ostream &operator<<(std::ostream &out, const ConfigUnknown &tc);
|
||||
|
||||
std::istream &operator>>(std::istream &in, ConfigUnknown &ce);
|
||||
|
||||
struct TileConfig
|
||||
{
|
||||
std::vector<ConfigArc> carcs;
|
||||
std::vector<ConfigWord> cwords;
|
||||
std::vector<ConfigEnum> cenums;
|
||||
std::vector<ConfigUnknown> cunknowns;
|
||||
int total_known_bits = 0;
|
||||
|
||||
void add_arc(const std::string &sink, const std::string &source);
|
||||
void add_word(const std::string &name, const std::vector<bool> &value);
|
||||
void add_enum(const std::string &name, const std::string &value);
|
||||
void add_unknown(int frame, int bit);
|
||||
|
||||
std::string to_string() const;
|
||||
static TileConfig from_string(const std::string &str);
|
||||
|
||||
bool empty() const;
|
||||
};
|
||||
|
||||
std::ostream &operator<<(std::ostream &out, const TileConfig &tc);
|
||||
|
||||
std::istream &operator>>(std::istream &in, TileConfig &ce);
|
||||
|
||||
// This represents the configuration of a chip at a high level
|
||||
class ChipConfig
|
||||
{
|
||||
public:
|
||||
std::string chip_name;
|
||||
std::vector<std::string> metadata;
|
||||
std::map<std::string, TileConfig> tiles;
|
||||
};
|
||||
|
||||
std::ostream &operator<<(std::ostream &out, const ChipConfig &cc);
|
||||
|
||||
std::istream &operator>>(std::istream &in, ChipConfig &cc);
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user