/* * nextpnr -- Next Generation Place and Route * * Copyright (C) 2018 Claire Xenia Wolf * * 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 "context.h" #include "log.h" #include "nextpnr_namespaces.h" #include "util.h" NEXTPNR_NAMESPACE_BEGIN WireId Context::getNetinfoSourceWire(const NetInfo *net_info) const { if (net_info->driver.cell == nullptr) return WireId(); auto src_bel = net_info->driver.cell->bel; if (src_bel == BelId()) return WireId(); auto bel_pins = getBelPinsForCellPin(net_info->driver.cell, net_info->driver.port); auto iter = bel_pins.begin(); if (iter == bel_pins.end()) return WireId(); WireId driver = getBelPinWire(src_bel, *iter); ++iter; NPNR_ASSERT(iter == bel_pins.end()); // assert there is only one driver bel pin; return driver; } SSOArray Context::getNetinfoSinkWires(const NetInfo *net_info, const PortRef &user_info) const { auto dst_bel = user_info.cell->bel; if (dst_bel == BelId()) return SSOArray(0, WireId()); size_t bel_pin_count = 0; // We use an SSOArray here because it avoids any heap allocation for the 99.9% case of 1 or 2 sink wires // but as SSOArray doesn't (currently) support resizing to keep things simple it does mean we have to do // two loops for (auto s : getBelPinsForCellPin(user_info.cell, user_info.port)) { (void)s; // unused ++bel_pin_count; } SSOArray result(bel_pin_count, WireId()); bel_pin_count = 0; for (auto pin : getBelPinsForCellPin(user_info.cell, user_info.port)) { result[bel_pin_count++] = getBelPinWire(dst_bel, pin); } return result; } size_t Context::getNetinfoSinkWireCount(const NetInfo *net_info, const PortRef &sink) const { size_t count = 0; for (auto s : getNetinfoSinkWires(net_info, sink)) { (void)s; // unused ++count; } return count; } WireId Context::getNetinfoSinkWire(const NetInfo *net_info, const PortRef &sink, size_t phys_idx) const { size_t count = 0; for (auto s : getNetinfoSinkWires(net_info, sink)) { if (count == phys_idx) return s; ++count; } /* TODO: This should be an assertion failure, but for the zero-wire case of unplaced sinks; legacy code currently assumes WireId Remove once the refactoring process is complete. */ return WireId(); } delay_t Context::predictArcDelay(const NetInfo *net_info, const PortRef &sink) const { if (net_info->driver.cell == nullptr || net_info->driver.cell->bel == BelId() || sink.cell->bel == BelId()) return 0; IdString driver_pin, sink_pin; // Pick the first pin for a prediction; assume all will be similar enouhg for (auto pin : getBelPinsForCellPin(net_info->driver.cell, net_info->driver.port)) { driver_pin = pin; break; } for (auto pin : getBelPinsForCellPin(sink.cell, sink.port)) { sink_pin = pin; break; } if (driver_pin == IdString() || sink_pin == IdString()) return 0; return predictDelay(net_info->driver.cell->bel, driver_pin, sink.cell->bel, sink_pin); } delay_t Context::getNetinfoRouteDelay(const NetInfo *net_info, const PortRef &user_info) const { #ifdef ARCH_ECP5 if (net_info->is_global) return 0; #endif if (net_info->wires.empty()) return predictArcDelay(net_info, user_info); WireId src_wire = getNetinfoSourceWire(net_info); if (src_wire == WireId()) return 0; delay_t max_delay = 0; for (auto dst_wire : getNetinfoSinkWires(net_info, user_info)) { WireId cursor = dst_wire; delay_t delay = 0; while (cursor != WireId() && cursor != src_wire) { auto it = net_info->wires.find(cursor); if (it == net_info->wires.end()) break; PipId pip = it->second.pip; if (pip == PipId()) break; delay += getPipDelay(pip).maxDelay(); delay += getWireDelay(cursor).maxDelay(); cursor = getPipSrcWire(pip); } if (cursor == src_wire) max_delay = std::max(max_delay, delay + getWireDelay(src_wire).maxDelay()); // routed else max_delay = std::max(max_delay, predictArcDelay(net_info, user_info)); // unrouted } return max_delay; } static uint32_t xorshift32(uint32_t x) { x ^= x << 13; x ^= x >> 17; x ^= x << 5; return x; } uint32_t Context::checksum() const { uint32_t cksum = xorshift32(123456789); uint32_t cksum_nets_sum = 0; for (auto &it : nets) { auto &ni = *it.second; uint32_t x = 123456789; x = xorshift32(x + xorshift32(it.first.index)); x = xorshift32(x + xorshift32(ni.name.index)); if (ni.driver.cell) x = xorshift32(x + xorshift32(ni.driver.cell->name.index)); x = xorshift32(x + xorshift32(ni.driver.port.index)); x = xorshift32(x + xorshift32(getDelayChecksum(ni.driver.budget))); for (auto &u : ni.users) { if (u.cell) x = xorshift32(x + xorshift32(u.cell->name.index)); x = xorshift32(x + xorshift32(u.port.index)); x = xorshift32(x + xorshift32(getDelayChecksum(u.budget))); } uint32_t attr_x_sum = 0; for (auto &a : ni.attrs) { uint32_t attr_x = 123456789; attr_x = xorshift32(attr_x + xorshift32(a.first.index)); for (char ch : a.second.str) attr_x = xorshift32(attr_x + xorshift32((int)ch)); attr_x_sum += attr_x; } x = xorshift32(x + xorshift32(attr_x_sum)); uint32_t wire_x_sum = 0; for (auto &w : ni.wires) { uint32_t wire_x = 123456789; wire_x = xorshift32(wire_x + xorshift32(getWireChecksum(w.first))); wire_x = xorshift32(wire_x + xorshift32(getPipChecksum(w.second.pip))); wire_x = xorshift32(wire_x + xorshift32(int(w.second.strength))); wire_x_sum += wire_x; } x = xorshift32(x + xorshift32(wire_x_sum)); cksum_nets_sum += x; } cksum = xorshift32(cksum + xorshift32(cksum_nets_sum)); uint32_t cksum_cells_sum = 0; for (auto &it : cells) { auto &ci = *it.second; uint32_t x = 123456789; x = xorshift32(x + xorshift32(it.first.index)); x = xorshift32(x + xorshift32(ci.name.index)); x = xorshift32(x + xorshift32(ci.type.index)); uint32_t port_x_sum = 0; for (auto &p : ci.ports) { uint32_t port_x = 123456789; port_x = xorshift32(port_x + xorshift32(p.first.index)); port_x = xorshift32(port_x + xorshift32(p.second.name.index)); if (p.second.net) port_x = xorshift32(port_x + xorshift32(p.second.net->name.index)); port_x = xorshift32(port_x + xorshift32(p.second.type)); port_x_sum += port_x; } x = xorshift32(x + xorshift32(port_x_sum)); uint32_t attr_x_sum = 0; for (auto &a : ci.attrs) { uint32_t attr_x = 123456789; attr_x = xorshift32(attr_x + xorshift32(a.first.index)); for (char ch : a.second.str) attr_x = xorshift32(attr_x + xorshift32((int)ch)); attr_x_sum += attr_x; } x = xorshift32(x + xorshift32(attr_x_sum)); uint32_t param_x_sum = 0; for (auto &p : ci.params) { uint32_t param_x = 123456789; param_x = xorshift32(param_x + xorshift32(p.first.index)); for (char ch : p.second.str) param_x = xorshift32(param_x + xorshift32((int)ch)); param_x_sum += param_x; } x = xorshift32(x + xorshift32(param_x_sum)); x = xorshift32(x + xorshift32(getBelChecksum(ci.bel))); x = xorshift32(x + xorshift32(ci.belStrength)); cksum_cells_sum += x; } cksum = xorshift32(cksum + xorshift32(cksum_cells_sum)); return cksum; } void Context::check() const { bool check_failed = false; #define CHECK_FAIL(...) \ do { \ log_nonfatal_error(__VA_ARGS__); \ check_failed = true; \ } while (false) for (auto &n : nets) { auto ni = n.second.get(); if (n.first != ni->name) CHECK_FAIL("net key '%s' not equal to name '%s'\n", nameOf(n.first), nameOf(ni->name)); for (auto &w : ni->wires) { if (ni != getBoundWireNet(w.first)) CHECK_FAIL("net '%s' not bound to wire '%s' in wires map\n", nameOf(n.first), nameOfWire(w.first)); if (w.second.pip != PipId()) { if (w.first != getPipDstWire(w.second.pip)) CHECK_FAIL("net '%s' has dest mismatch '%s' vs '%s' in for pip '%s'\n", nameOf(n.first), nameOfWire(w.first), nameOfWire(getPipDstWire(w.second.pip)), nameOfPip(w.second.pip)); if (ni != getBoundPipNet(w.second.pip)) CHECK_FAIL("net '%s' not bound to pip '%s' in wires map\n", nameOf(n.first), nameOfPip(w.second.pip)); } } if (ni->driver.cell != nullptr) { if (!ni->driver.cell->ports.count(ni->driver.port)) { CHECK_FAIL("net '%s' driver port '%s' missing on cell '%s'\n", nameOf(n.first), nameOf(ni->driver.port), nameOf(ni->driver.cell)); } else { const NetInfo *p_net = ni->driver.cell->ports.at(ni->driver.port).net; if (p_net != ni) CHECK_FAIL("net '%s' driver port '%s.%s' connected to incorrect net '%s'\n", nameOf(n.first), nameOf(ni->driver.cell), nameOf(ni->driver.port), p_net ? nameOf(p_net) : ""); } } for (auto user : ni->users) { if (!user.cell->ports.count(user.port)) { CHECK_FAIL("net '%s' user port '%s' missing on cell '%s'\n", nameOf(n.first), nameOf(user.port), nameOf(user.cell)); } else { const NetInfo *p_net = user.cell->ports.at(user.port).net; if (p_net != ni) CHECK_FAIL("net '%s' user port '%s.%s' connected to incorrect net '%s'\n", nameOf(n.first), nameOf(user.cell), nameOf(user.port), p_net ? nameOf(p_net) : ""); } } } #ifdef CHECK_WIRES for (auto w : getWires()) { auto ni = getBoundWireNet(w); if (ni != nullptr) { if (!ni->wires.count(w)) CHECK_FAIL("wire '%s' missing in wires map of bound net '%s'\n", nameOfWire(w), nameOf(ni)); } } #endif for (auto &c : cells) { auto ci = c.second.get(); if (c.first != ci->name) CHECK_FAIL("cell key '%s' not equal to name '%s'\n", nameOf(c.first), nameOf(ci->name)); if (ci->bel != BelId()) { if (getBoundBelCell(c.second->bel) != ci) CHECK_FAIL("cell '%s' not bound to bel '%s' in bel field\n", nameOf(c.first), nameOfBel(ci->bel)); } for (auto &port : c.second->ports) { NetInfo *net = port.second.net; if (net != nullptr) { if (nets.find(net->name) == nets.end()) { CHECK_FAIL("cell port '%s.%s' connected to non-existent net '%s'\n", nameOf(c.first), nameOf(port.first), nameOf(net->name)); } else if (port.second.type == PORT_OUT) { if (net->driver.cell != c.second.get() || net->driver.port != port.first) { CHECK_FAIL("output cell port '%s.%s' not in driver field of net '%s'\n", nameOf(c.first), nameOf(port.first), nameOf(net)); } } else if (port.second.type == PORT_IN) { if (!port.second.user_idx) CHECK_FAIL("input cell port '%s.%s' on net '%s' has no user index\n", nameOf(c.first), nameOf(port.first), nameOf(net)); auto net_user = net->users.at(port.second.user_idx); if (net_user.cell != c.second.get() || net_user.port != port.first) CHECK_FAIL("input cell port '%s.%s' not in associated user entry of net '%s'\n", nameOf(c.first), nameOf(port.first), nameOf(net)); } } } } #undef CHECK_FAIL if (check_failed) log_error("INTERNAL CHECK FAILED: please report this error with the design and full log output. Failure " "details are above this message.\n"); } namespace { struct FixupHierarchyWorker { FixupHierarchyWorker(Context *ctx) : ctx(ctx){}; Context *ctx; void run() { trim_hierarchy(ctx->top_module); rebuild_hierarchy(); }; // Remove cells and nets that no longer exist in the netlist std::vector todelete_cells, todelete_nets; void trim_hierarchy(IdString path) { auto &h = ctx->hierarchy.at(path); todelete_cells.clear(); todelete_nets.clear(); for (auto &lc : h.leaf_cells) { if (!ctx->cells.count(lc.second)) todelete_cells.push_back(lc.first); } for (auto &n : h.nets) if (!ctx->nets.count(n.second)) todelete_nets.push_back(n.first); for (auto tdc : todelete_cells) { h.leaf_cells_by_gname.erase(h.leaf_cells.at(tdc)); h.leaf_cells.erase(tdc); } for (auto tdn : todelete_nets) { h.nets_by_gname.erase(h.nets.at(tdn)); h.nets.erase(tdn); } for (auto &sc : h.hier_cells) trim_hierarchy(sc.second); } IdString construct_local_name(HierarchicalCell &hc, IdString global_name, bool is_cell) { std::string gn = global_name.str(ctx); auto dp = gn.find_last_of('.'); if (dp != std::string::npos) gn = gn.substr(dp + 1); IdString name = ctx->id(gn); // Make sure name is unique int adder = 0; while (is_cell ? hc.leaf_cells.count(name) : hc.nets.count(name)) { ++adder; name = ctx->id(gn + "$" + std::to_string(adder)); } return name; } // Update hierarchy structure for nets and cells that have hiercell set void rebuild_hierarchy() { for (auto &cell : ctx->cells) { CellInfo *ci = cell.second.get(); if (ci->hierpath == IdString()) ci->hierpath = ctx->top_module; auto &hc = ctx->hierarchy.at(ci->hierpath); if (hc.leaf_cells_by_gname.count(ci->name)) continue; // already known IdString local_name = construct_local_name(hc, ci->name, true); hc.leaf_cells_by_gname[ci->name] = local_name; hc.leaf_cells[local_name] = ci->name; } } }; } // namespace void Context::fixupHierarchy() { FixupHierarchyWorker(this).run(); } NEXTPNR_NAMESPACE_END >378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468