/* * nextpnr -- Next Generation Place and Route * * Copyright (C) 2018 Clifford Wolf * Copyright (C) 2018 David Shah * * 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 "nextpnr.h" #include "placer1.h" #include "router1.h" NEXTPNR_NAMESPACE_BEGIN static std::tuple split_identifier_name(const std::string &name) { size_t first_slash = name.find('/'); NPNR_ASSERT(first_slash != std::string::npos); size_t second_slash = name.find('/', first_slash + 1); NPNR_ASSERT(second_slash != std::string::npos); return std::make_tuple(std::stoi(name.substr(1, first_slash)), std::stoi(name.substr(first_slash + 2, second_slash - first_slash)), name.substr(second_slash + 1)); }; // ----------------------------------------------------------------------- void IdString::initialize_arch(const BaseCtx *ctx) { // Nothing here -- IdString is actually initialized in the constructor, // because we need to have bba loaded. } // Given a device name, figure out what family it belongs to. static Arch::Family device_to_family(const std::string &device) { size_t pos = 0; // Skip the prefix. if (device.substr(0, 2) == "xc") pos += 2; else if (device.substr(0, 2) == "xa") pos += 2; else if (device.substr(0, 2) == "xq") pos += 2; else if (device.substr(0, 3) == "xqr") pos += 3; auto raw = device.substr(pos); if (raw.substr(0, 2) == "vu" || raw.substr(0, 2) == "ku") { // Ultrascale or Ultrascale+ (needs to be checked before original Virtex). if (raw.substr(raw.size() - 1) == "p") return Arch::FAMILY_ULTRASCALE_PLUS; return Arch::FAMILY_ULTRASCALE; } else if (raw.substr(0, 2) == "zu") { // Zynq Ultrascale+. return Arch::FAMILY_ULTRASCALE_PLUS; } else if (raw.substr(0, 1) == "7") { // 7 Series. return Arch::FAMILY_SERIES7; } else if (raw.substr(0, 2) == "6s") { return Arch::FAMILY_SPARTAN6; } else if (raw.substr(0, 2) == "6v") { return Arch::FAMILY_VIRTEX6; } else if (raw.substr(0, 2) == "5v") { return Arch::FAMILY_VIRTEX5; } else if (raw.substr(0, 2) == "4v") { return Arch::FAMILY_VIRTEX4; } else if (raw.substr(0, 3) == "3sd") { // Needs to be checked before other Spartan 3 variants. return Arch::FAMILY_SPARTAN3ADSP; } else if (raw.substr(0, 2) == "3s") { // One of many Spartan 3 variants. if (raw.substr(raw.size() - 1) == "e") return Arch::FAMILY_SPARTAN3E; if (raw.substr(raw.size() - 1) == "a") return Arch::FAMILY_SPARTAN3A; if (raw.substr(raw.size() - 2) == "an") return Arch::FAMILY_SPARTAN3A; return Arch::FAMILY_SPARTAN3; } else if (raw.substr(0, 3) == "2vp") { // Virtex 2 Pro. return Arch::FAMILY_VIRTEX2P; } else if (raw.substr(0, 2) == "2v") { // Virtex 2. return Arch::FAMILY_VIRTEX2; } else if (raw.substr(0, 1) == "v" || raw.substr(0, 2) == "2s") { // Virtex or Virtex E. if (raw.substr(raw.size() - 1) == "e") return Arch::FAMILY_VIRTEXE; return Arch::FAMILY_VIRTEX; } else if (raw.substr(0, 1) == "s") { // Spartan or Spartan XL. if (raw.size() >= 2 && raw.substr(raw.size() - 2) == "xl") return Arch::FAMILY_SPARTANXL; return Arch::FAMILY_XC4000E; } else if (raw.substr(0, 2) == "40") { // One of the xc4000 families. if (raw.substr(raw.size() - 1) == "e") return Arch::FAMILY_XC4000E; if (raw.substr(raw.size() - 2) == "ex") return Arch::FAMILY_XC4000EX; if (raw.substr(raw.size() - 2) == "xl") return Arch::FAMILY_XC4000EX; if (raw.size() >= 3 && raw.substr(raw.size() - 3) == "xla") return Arch::FAMILY_XC4000XLA; if (raw.substr(raw.size() - 2) == "xv") return Arch::FAMILY_XC4000XV; } log_error("Unknown device family.\n"); } static std::string family_name(Arch::Family family) { switch(family) { case Arch::FAMILY_XC4000E: return "xc4000e"; case Arch::FAMILY_XC4000EX: return "xc4000ex"; case Arch::FAMILY_XC4000XLA: return "xc4000xla"; case Arch::FAMILY_XC4000XV: return "xc4000xv"; case Arch::FAMILY_SPARTANXL: return "spartanxl"; case Arch::FAMILY_VIRTEX: return "virtex"; case Arch::FAMILY_VIRTEXE: return "virtexe"; case Arch::FAMILY_VIRTEX2: return "virtex2"; case Arch::FAMILY_VIRTEX2P: return "virtex2p"; case Arch::FAMILY_SPARTAN3: return "spartan3"; case Arch::FAMILY_SPARTAN3E: return "spartan3e"; case Arch::FAMILY_SPARTAN3A: return "spartan3a"; case Arch::FAMILY_SPARTAN3ADSP: return "spartan3adsp"; case Arch::FAMILY_VIRTEX4: return "virtex4"; case Arch::FAMILY_VIRTEX5: return "virtex5"; case Arch::FAMILY_VIRTEX6: return "virtex6"; case Arch::FAMILY_SPARTAN6: return "spartan6"; case Arch::FAMILY_SERIES7: return "series7"; case Arch::FAMILY_ULTRASCALE: return "ultrascale"; case Arch::FAMILY_ULTRASCALE_PLUS: return "ultrascaleplus"; } NPNR_ASSERT_FALSE("strange family"); return ""; } Arch::Arch(ArchArgs args) : args(args) { // Select and load family bba. family = device_to_family(args.device); std::string fname = family_name(family); std::string family_filename = EXTERNAL_CHIPDB_ROOT "/leuctra/" + fname + ".bin"; try { mmap.open(family_filename); if (!mmap.is_open()) log_error("Unable to read chipdb %s\n", family_filename.c_str()); } catch (...) { log_error("Unable to read chipdb %s\n", family_filename.c_str()); } family_info = reinterpret_cast(mmap.data()); if (family_info->format_tag != DB_FORMAT_TAG_CURRENT) log_error("Chipdb %s has wrong format tag\n", family_filename.c_str()); // Slurp IdStrings. // Entry 0 must be "". NPNR_ASSERT(family_info->idstrings[0].get()[0] == 0); for (int i = 1; i < family_info->num_idstrings; i++) IdString::initialize_add(this, family_info->idstrings[i].get(), i); // Make double sure we got the right family. if (IdString(family_info->name_id).str(this) != fname) log_error("Chipdb %s is for strange family\n", family_filename.c_str()); // Search for the device. int dev_name_id = id(args.device).index; for (int i = 0; i < family_info->num_devices; i++) { if (family_info->devices[i].name_id == dev_name_id) { device_info = family_info->devices[i].device.get(); break; } } if (device_info == nullptr) log_error("Unknown device.\n"); // Find the right package. int pkg_name_id = id(args.package).index; if (pkg_name_id == 0) { package_info = &device_info->packages[0]; } else { for (int i = 0; i < device_info->num_packages; i++) { if (device_info->packages[i].name_id == pkg_name_id) { package_info = &device_info->packages[i]; break; } } } if (package_info == nullptr) log_error("Unknown package.\n"); } // ----------------------------------------------------------------------- BelId Arch::getBelByName(IdString name) const { BelId ret; auto it = bel_by_name.find(name); if (it != bel_by_name.end()) return it->second; Location loc; std::string basename; std::tie(loc.x, loc.y, basename) = split_identifier_name(name.str(this)); ret.location = loc; IdString basename_id = id(basename); auto &tt = getTileType(loc.x, loc.y); for (int i = 0; i < tt.num_bels; i++) { if (tt.bels[i].name_id == basename_id.index) { ret.index = i; bel_by_name[name] = ret; return ret; } } return BelId(); } WireId Arch::getBelPinWire(BelId bel, IdString pin) const { WireId ret; NPNR_ASSERT(bel != BelId()); int num_bel_wires = getBelTypeInfo(bel).num_pins; const BelTypePinPOD *bel_type_wires = getBelTypeInfo(bel).pins.get(); for (int i = 0; i < num_bel_wires; i++) if (bel_type_wires[i].name_id == pin.index) { ret.location = bel.location; ret.index = getTileTypeBel(bel).pin_wires[i]; break; } return ret; } PortType Arch::getBelPinType(BelId bel, IdString pin) const { NPNR_ASSERT(bel != BelId()); int num_bel_wires = getBelTypeInfo(bel).num_pins; const BelTypePinPOD *bel_type_wires = getBelTypeInfo(bel).pins.get(); for (int i = 0; i < num_bel_wires; i++) if (bel_type_wires[i].name_id == pin.index) { bool is_in = bel_type_wires[i].flags & BelTypePinPOD::FLAG_INPUT; bool is_out = bel_type_wires[i].flags & BelTypePinPOD::FLAG_OUTPUT; if (is_in && is_out) return PORT_INOUT; if (is_in) return PORT_IN; assert(is_out); return PORT_OUT; } return PORT_INOUT; } // ----------------------------------------------------------------------- WireId Arch::getWireByName(IdString name) const { WireId ret; auto it = wire_by_name.find(name); if (it != wire_by_name.end()) return it->second; Location loc; std::string basename; std::tie(loc.x, loc.y, basename) = split_identifier_name(name.str(this)); ret.location = loc; IdString basename_id = id(basename); auto &tt = getTileType(loc.x, loc.y); for (int i = 0; i < tt.num_wires; i++) { if (tt.wires[i].name_id == basename_id.index) { ret.index = i; wire_by_name[name] = ret; return ret; } } return WireId(); } // ----------------------------------------------------------------------- PipId Arch::getPipByName(IdString name) const { auto it = pip_by_name.find(name); if (it != pip_by_name.end()) return it->second; PipId ret; Location loc; std::string basename; std::tie(loc.x, loc.y, basename) = split_identifier_name(name.str(this)); ret.location = loc; AllPipRange range; range.b.cursor_tile = loc.x + device_info->width * loc.y; range.b.cursor_kind = PIP_KIND_PIP; range.b.cursor_index = 0; range.b.cursor_subindex = -1; range.b.device = device_info; range.b.family = family_info; ++range.b; range.e.cursor_tile = loc.x + device_info->width * loc.y + 1; range.e.cursor_kind = PIP_KIND_PIP; range.e.cursor_index = 0; range.e.cursor_subindex = -1; range.e.device = device_info; range.e.family = family_info; ++range.e; for (const auto& curr: range) { pip_by_name[getPipName(curr)] = curr; } if (pip_by_name.find(name) == pip_by_name.end()) NPNR_ASSERT_FALSE_STR("no pip named " + name.str(this)); return pip_by_name[name]; } IdString Arch::getPipName(PipId pip) const { NPNR_ASSERT(pip != PipId()); int x = pip.location.x; int y = pip.location.y; if (pip.kind == PIP_KIND_PIP) { std::string src_name = getWireBasename(getPipSrcWire(pip)).str(this); std::string dst_name = getWireBasename(getPipDstWire(pip)).str(this); return id("X" + std::to_string(x) + "/Y" + std::to_string(y) + "/" + src_name + ".->." + dst_name); } else { auto &tt = getTileType(pip.location); std::string port_name = IdString(tt.ports[pip.index].name_id).str(this); std::string dst_name = getWireBasename(getPipDstWire(pip)).str(this); return id("X" + std::to_string(x) + "/Y" + std::to_string(y) + "/" + port_name + "/" + std::to_string(pip.subindex) + ".->." + dst_name); } } // ----------------------------------------------------------------------- void PipIterator::operator++() { cursor_index++; auto &ttw = arch->getTileTypeWire(wire); if (stage == STAGE_PIPS) { int num; if (mode == MODE_UPHILL) num = ttw.num_pip_dst_xrefs; else num = ttw.num_pip_src_xrefs; if (cursor_index == num) { cursor_index = 0; stage = STAGE_PORTS; } } if (stage == STAGE_PORTS) { while (true) { if (cursor_index == ttw.num_port_xrefs) { cursor_index = 0; stage = STAGE_END; break; } // Make sure the port is connected. auto &tile = arch->getTile(wire.location); auto &xref = ttw.port_xrefs[cursor_index]; auto &conn = tile.conns[xref.port_idx]; if (conn.port_idx != -1) { // Make sure the wire in a port is connected. Location other_loc; other_loc.x = conn.tile_x; other_loc.y = conn.tile_y; auto &other_tt = arch->getTileType(other_loc); if (other_tt.ports[conn.port_idx].wires[xref.wire_idx] != -1) break; } cursor_index++; } } } PipId PipIterator::operator*() const { PipId ret; auto &ttw = arch->getTileTypeWire(wire); ret.location = wire.location; if (mode == MODE_UPHILL) { if (stage == STAGE_PIPS) { ret.kind = PIP_KIND_PIP; ret.index = ttw.pip_dst_xrefs[cursor_index]; } else { ret.kind = PIP_KIND_PORT; auto &xref = ttw.port_xrefs[cursor_index]; ret.index = xref.port_idx; ret.subindex = xref.wire_idx; } } else { if (stage == STAGE_PIPS) { ret.kind = PIP_KIND_PIP; ret.index = ttw.pip_src_xrefs[cursor_index]; } else { ret.kind = PIP_KIND_PORT; auto &tile = arch->getTile(wire.location); auto &xref = ttw.port_xrefs[cursor_index]; auto &conn = tile.conns[xref.port_idx]; ret.location.x = conn.tile_x; ret.location.y = conn.tile_y; ret.index = conn.port_idx; ret.subindex = xref.wire_idx; } } return ret; } // ----------------------------------------------------------------------- // // XXX package pins std::vector Arch::getBelPins(BelId bel) const { std::vector ret; NPNR_ASSERT(bel != BelId()); int num_bel_wires = getBelTypeInfo(bel).num_pins; const BelTypePinPOD *bel_type_wires = getBelTypeInfo(bel).pins.get(); for (int i = 0; i < num_bel_wires; i++) { IdString id; id.index = bel_type_wires[i].name_id; ret.push_back(id); } return ret; } // ----------------------------------------------------------------------- delay_t Arch::estimateDelay(WireId src, WireId dst) const { // TODO return 13; } delay_t Arch::predictDelay(const NetInfo *net_info, const PortRef &sink) const { // TODO return 13; } bool Arch::getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay_t &budget) const { return false; } // ----------------------------------------------------------------------- bool Arch::place() { return placer1(getCtx(), Placer1Cfg(getCtx())); } bool Arch::route() { return router1(getCtx(), Router1Cfg(getCtx())); } // ----------------------------------------------------------------------- std::vector Arch::getDecalGraphics(DecalId decal) const { std::vector ret; if (decal.type == DecalId::TYPE_BEL) { BelId bel; bel.index = decal.z; bel.location = decal.location; int max_z = getTileBelDimZ(bel.location.x, bel.location.y); int z = bel.index; //auto bel_type = getBelType(bel); GraphicElement el; el.type = GraphicElement::TYPE_BOX; el.style = decal.active ? GraphicElement::STYLE_ACTIVE : GraphicElement::STYLE_INACTIVE; el.x1 = bel.location.x + 0.05; el.x2 = bel.location.x + 0.95; el.y1 = bel.location.y + (z + 0.05) / max_z; el.y2 = bel.location.y + (z + 0.95) / max_z; ret.push_back(el); } return ret; } DecalXY Arch::getBelDecal(BelId bel) const { DecalXY decalxy; decalxy.decal.type = DecalId::TYPE_BEL; decalxy.decal.location = bel.location; decalxy.decal.z = bel.index; decalxy.decal.active = !checkBelAvail(bel); return decalxy; } DecalXY Arch::getWireDecal(WireId wire) const { return {}; } DecalXY Arch::getPipDecal(PipId pip) const { return {}; }; DecalXY Arch::getGroupDecal(GroupId pip) const { return {}; }; // ----------------------------------------------------------------------- bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, DelayInfo &delay) const { // XXX delay.min_delay = 11; delay.max_delay = 13; return true; } TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, int &clockInfoCount) const { // XXX return TMG_IGNORE; } TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port, int index) const { TimingClockingInfo info; info.setup = getDelayFromNS(0); info.hold = getDelayFromNS(0); info.clockToQ = getDelayFromNS(0); // XXX return info; } NEXTPNR_NAMESPACE_END