/* * nextpnr -- Next Generation Place and Route * * Copyright (C) 2019-2023 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 #include #include #include "extra_data.h" #include "himbaechel_api.h" #include "log.h" #include "nextpnr.h" #include "util.h" #include "xilinx.h" #define HIMBAECHEL_CONSTIDS "uarch/xilinx/constids.inc" #include "himbaechel_constids.h" NEXTPNR_NAMESPACE_BEGIN void XilinxImpl::parse_xdc(const std::string &filename) { std::ifstream in(filename); if (!in) log_error("failed to open XDC file '%s'\n", filename.c_str()); std::string line; std::string linebuf; int lineno = 0; auto isempty = [](const std::string &str) { return std::all_of(str.begin(), str.end(), [](char c) { return std::isspace(c); }); }; auto strip_quotes = [](const std::string &str) { if (str.at(0) == '"') { NPNR_ASSERT(str.back() == '"'); return str.substr(1, str.size() - 2); } else if (str.at(0) == '{') { NPNR_ASSERT(str.back() == '}'); return str.substr(1, str.size() - 2); } else { return str; } }; auto split_to_args = [](const std::string &str, bool group_brackets) { std::vector split_args; std::string buffer; auto flush = [&]() { if (!buffer.empty()) split_args.push_back(buffer); buffer.clear(); }; int brcount = 0; for (char c : str) { if ((c == '[' || c == '{') && group_brackets) { ++brcount; } if ((c == ']' || c == '}') && group_brackets) { --brcount; buffer += c; if (brcount == 0) flush(); continue; } if (std::isspace(c)) { if (brcount == 0) { flush(); continue; } } buffer += c; } flush(); return split_args; }; auto get_cells = [&](std::string str) { std::vector tgt_cells; if (str.empty() || str.front() != '[') log_error("failed to parse target (on line %d)\n", lineno); str = str.substr(1, str.size() - 2); auto split = split_to_args(str, false); if (split.size() < 1) log_error("failed to parse target (on line %d)\n", lineno); if (split.front() != "get_ports") log_error("targets other than 'get_ports' are not supported (on line %d)\n", lineno); if (split.size() < 2) log_error("failed to parse target (on line %d)\n", lineno); IdString cellname = ctx->id(strip_quotes(split.at(1))); if (ctx->cells.count(cellname)) tgt_cells.push_back(ctx->cells.at(cellname).get()); return tgt_cells; }; auto get_nets = [&](std::string str) { std::vector tgt_nets; if (str.empty() || str.front() != '[') log_error("failed to parse target (on line %d)\n", lineno); str = str.substr(1, str.size() - 2); auto split = split_to_args(str, false); if (split.size() < 1) log_error("failed to parse target (on line %d)\n", lineno); if (split.front() != "get_ports" && split.front() != "get_nets") log_error("targets other than 'get_ports' or 'get_nets' are not supported (on line %d)\n", lineno); if (split.size() < 2) log_error("failed to parse target (on line %d)\n", lineno); IdString netname = ctx->id(split.at(1)); NetInfo *maybe_net = ctx->getNetByAlias(netname); if (maybe_net != nullptr) tgt_nets.push_back(maybe_net); return tgt_nets; }; while (std::getline(in, line)) { ++lineno; // Trim comments, from # until end of the line size_t cstart = line.find('#'); if (cstart != std::string::npos) line = line.substr(0, cstart); if (isempty(line)) continue; std::vector arguments = split_to_args(line, true); if (arguments.empty()) continue; std::string &cmd = arguments.front(); if (cmd == "set_property") { std::vector> arg_pairs; if (arguments.size() != 4) log_error("expected four arguments to 'set_property' (on line %d)\n", lineno); else if (arguments.at(1) == "-dict") { std::vector dict_args = split_to_args(strip_quotes(arguments.at(2)), false); if ((dict_args.size() % 2) != 0) log_error("expected an even number of argument for dictionary (on line %d)\n", lineno); arg_pairs.reserve(dict_args.size() / 2); for (int cursor = 0; cursor + 1 < int(dict_args.size()); cursor += 2) { arg_pairs.emplace_back(std::move(dict_args.at(cursor)), std::move(dict_args.at(cursor + 1))); } } else { arg_pairs.emplace_back(std::move(arguments.at(1)), std::move(arguments.at(2))); } if (arg_pairs.size() == 1 && arg_pairs.front().first == "INTERNAL_VREF") // get_iobanks not supported continue; if (arguments.at(3).size() > 2 && arguments.at(3) == "[current_design]") { log_warning("[current_design] isn't supported, ignoring (on line %d)\n", lineno); continue; } std::vector dest = get_cells(arguments.at(3)); for (auto c : dest) for (const auto &pair : arg_pairs) c->attrs[ctx->id(pair.first)] = std::string(pair.second); } else if (cmd == "create_clock") { double period = 0; bool got_period = false; int cursor = 1; for (cursor = 1; cursor < int(arguments.size()); cursor++) { std::string opt = arguments.at(cursor); if (opt == "-add") ; else if (opt == "-name" || opt == "-waveform") cursor++; else if (opt == "-period") { cursor++; period = std::stod(arguments.at(cursor)); got_period = true; } else break; } if (!got_period) log_error("found create_clock without period (on line %d)", lineno); std::vector dest = get_nets(arguments.at(cursor)); for (auto n : dest) { n->clkconstr = std::unique_ptr(new ClockConstraint); n->clkconstr->period = DelayPair(ctx->getDelayFromNS(period)); n->clkconstr->high = DelayPair(ctx->getDelayFromNS(period / 2)); n->clkconstr->low = DelayPair(ctx->getDelayFromNS(period / 2)); } } else { log_info("ignoring unsupported XDC command '%s' (on line %d)\n", cmd.c_str(), lineno); } } if (!isempty(linebuf)) log_error("unexpected end of XDC file\n"); } NEXTPNR_NAMESPACE_END