/* * nextpnr -- Next Generation Place and Route * * Copyright (C) 2018 gatecat * * 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 #include #include #include "log.h" NEXTPNR_NAMESPACE_BEGIN #define fmt(x) (static_cast(std::ostringstream() << x).str()) inline std::string to_string(const std::vector &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 &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 &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; 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 == ".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