202 lines
7.9 KiB
C++
202 lines
7.9 KiB
C++
/*
|
|
* nextpnr -- Next Generation Place and Route
|
|
*
|
|
* Copyright (C) 2019-2023 gatecat <gatecat@ds0.me>
|
|
*
|
|
* 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 <boost/algorithm/string.hpp>
|
|
#include <fstream>
|
|
#include <regex>
|
|
|
|
#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<std::string> 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<CellInfo *> 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<NetInfo *> 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<std::string> arguments = split_to_args(line, true);
|
|
if (arguments.empty())
|
|
continue;
|
|
std::string &cmd = arguments.front();
|
|
if (cmd == "set_property") {
|
|
std::vector<std::pair<std::string, std::string>> 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<std::string> 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<CellInfo *> 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<NetInfo *> dest = get_nets(arguments.at(cursor));
|
|
for (auto n : dest) {
|
|
n->clkconstr = std::unique_ptr<ClockConstraint>(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
|