diff --git a/common/kernel/basectx.h b/common/kernel/basectx.h index c8791a2b96..3f021e82cc 100644 --- a/common/kernel/basectx.h +++ b/common/kernel/basectx.h @@ -213,7 +213,11 @@ struct BaseCtx NetInfo *getNetByAlias(IdString alias) const { - return nets.count(alias) ? nets.at(alias).get() : nets.at(net_aliases.at(alias)).get(); + if (nets.count(alias) > 0) + return nets.at(alias).get(); + if (net_aliases.count(alias) == 0) + return nullptr; + return nets.at(net_aliases.at(alias)).get(); } // Intended to simplify Python API diff --git a/himbaechel/uarch/xilinx/xdc.cc b/himbaechel/uarch/xilinx/xdc.cc index e6d76b599a..b0f41922a0 100644 --- a/himbaechel/uarch/xilinx/xdc.cc +++ b/himbaechel/uarch/xilinx/xdc.cc @@ -39,9 +39,11 @@ 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()); + log_info("Parsing XDC file...\n"); std::string line; std::string linebuf; int lineno = 0; + unsigned num_errors = 0; auto isempty = [](const std::string &str) { return std::all_of(str.begin(), str.end(), [](char c) { return std::isspace(c); }); @@ -109,8 +111,10 @@ void XilinxImpl::parse_xdc(const std::string &filename) 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); + if (str.empty()) + return tgt_nets; + if (str.front() != '[' || str.back() != ']') + log_error("failed to parse target '%s' (on line %d)\n", str.c_str(), lineno); str = str.substr(1, str.size() - 2); auto split = split_to_args(str, false); if (split.size() < 1) @@ -119,8 +123,19 @@ void XilinxImpl::parse_xdc(const std::string &filename) 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)); + str = strip_quotes(split.at(1)); + if (str.empty()) + return tgt_nets; + IdString netname = ctx->id(str); NetInfo *maybe_net = ctx->getNetByAlias(netname); + if (maybe_net != nullptr) { + tgt_nets.push_back(maybe_net); + return tgt_nets; + } + // Also test the lowercase variant, for better interoperability with synthesis tools + boost::algorithm::to_lower(str); + netname = ctx->id(str); + maybe_net = ctx->getNetByAlias(netname); if (maybe_net != nullptr) tgt_nets.push_back(maybe_net); return tgt_nets; @@ -139,14 +154,21 @@ void XilinxImpl::parse_xdc(const std::string &filename) 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); + if (arguments.size() < 4) { + log_nonfatal_error("expected at least four arguments to 'set_property' (on line %d)\n", lineno); + num_errors++; + goto nextline; + } 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); + if ((dict_args.size() % 2) != 0) { + log_nonfatal_error("expected an even number of argument for dictionary (on line %d)\n", lineno); + num_errors++; + goto nextline; + } 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))); @@ -154,48 +176,97 @@ void XilinxImpl::parse_xdc(const std::string &filename) } 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 + // Warning : ug835 has lowercase example, so probably supporting lowercase too is needed + if (arg_pairs.size() == 1 && arg_pairs.front().first == "INTERNAL_VREF") { // get_iobanks not supported + log_warning("INTERNAL_VREF isn't supported, ignoring (on line %d)\n", lineno); 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); + // All remaining arguments are supposed to designate cells + std::vector dest; + for (int cursor = 3; cursor < int(arguments.size()); cursor++) { + std::vector dest_loc = get_cells(arguments.at(cursor)); + if (dest_loc.empty()) + log_warning("found set_property with no cells matching '%s' (on line %d)\n", arguments.at(cursor).c_str(), lineno); + dest.insert(dest.end(), dest_loc.begin(), dest_loc.end()); + } + for (auto c : dest) { + for (const auto &pair : arg_pairs) { + IdString id_prop = ctx->id(pair.first); + if (ctx->debug) + log_info("applying property '%s' = '%s' to cell '%s' (on line %d)\n", pair.first.c_str(), pair.second.c_str(), c->name.c_str(ctx), lineno); + if(c->attrs.find(id_prop) != c->attrs.end()) { + log_nonfatal_error("found multiple properties '%s' for cell '%s' (on line %d)\n", pair.first.c_str(), c->name.c_str(ctx), lineno); + num_errors++; + } + c->attrs[id_prop] = 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") + if (opt == "-add") { + log_warning("ignoring unsupported XDC option '%s' (on line %d)\n", opt.c_str(), lineno); + } + else if (opt == "-name" || opt == "-waveform") { + log_warning("ignoring unsupported XDC option '%s' (on line %d)\n", opt.c_str(), lineno); cursor++; + } else if (opt == "-period") { cursor++; period = std::stod(arguments.at(cursor)); got_period = true; - } else + } + else break; } - if (!got_period) - log_error("found create_clock without period (on line %d)", lineno); - std::vector dest = get_nets(arguments.at(cursor)); + if (!got_period) { + log_nonfatal_error("found create_clock without period (on line %d)\n", lineno); + num_errors++; + goto nextline; + } + // All remaining arguments are supposed to designate ports/nets + std::vector dest; + if (cursor >= int(arguments.size())) + log_warning("found create_clock without designated nets (on line %d)\n", lineno); + for ( ; cursor < (int)arguments.size(); cursor++) { + std::vector dest_loc = get_nets(arguments.at(cursor)); + if (dest_loc.empty()) + log_warning("found create_clock with no nets matching '%s' (on line %d)\n", arguments.at(cursor).c_str(), lineno); + dest.insert(dest.end(), dest_loc.begin(), dest_loc.end()); + } for (auto n : dest) { + if (ctx->debug) + log_info("applying clock period constraint on net '%s' (on line %d)\n", n->name.c_str(ctx), lineno); + if (n->clkconstr.get() != nullptr) { + log_nonfatal_error("found multiple clock constraints on net '%s' (on line %d)\n", n->name.c_str(ctx), lineno); + num_errors++; + } 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); + log_warning("ignoring unsupported XDC command '%s' (on line %d)\n", cmd.c_str(), lineno); } + + nextline: + ; // Phony statement to have something legal after the label + } + if (!isempty(linebuf)) { + log_nonfatal_error("unexpected end of XDC file\n"); + num_errors++; + } + if (num_errors > 0) { + log_error("Stopping the program after %u errors found in XDC file\n", num_errors); } - if (!isempty(linebuf)) - log_error("unexpected end of XDC file\n"); } NEXTPNR_NAMESPACE_END