/* * nextpnr -- Next Generation Place and Route * * Copyright (C) 2018 Clifford 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 #include #include "nextpnr.h" #include "embed.h" #include "placer1.h" #include "placer_heap.h" #include "router1.h" #include "router2.h" #include "util.h" NEXTPNR_NAMESPACE_BEGIN // --------------------------------------------------------------- Arch::Arch(ArchArgs args) : chipName("generic"), args(args) { // Dummy for empty decals decal_graphics[IdString()]; } // ----------------------------------------------------------------------- void IdString::initialize_arch(const BaseCtx *ctx) { #define X(t) initialize_add(ctx, #t, ID_##t); #include "constids.inc" #undef X } // --------------------------------------------------------------- static const ChipInfoPOD *get_chip_info(ArchArgs::ArchArgsTypes chip) { std::string chipdb; if (chip == ArchArgs::LCMXO2_256HC) { chipdb = "machxo2/chipdb-256.bin"; } else if (chip == ArchArgs::LCMXO2_640HC) { chipdb = "machxo2/chipdb-640.bin"; } else if (chip == ArchArgs::LCMXO2_1200HC) { chipdb = "machxo2/chipdb-1200.bin"; } else if (chip == ArchArgs::LCMXO2_2000HC) { chipdb = "machxo2/chipdb-2000.bin"; } else if (chip == ArchArgs::LCMXO2_4000HC) { chipdb = "machxo2/chipdb-4000.bin"; } else if (chip == ArchArgs::LCMXO2_7000HC) { chipdb = "machxo2/chipdb-7000.bin"; } else { log_error("Unknown chip\n"); } auto ptr = reinterpret_cast *>(get_chipdb(chipdb)); if (ptr == nullptr) return nullptr; return ptr->get(); } bool Arch::isAvailable(ArchArgs::ArchArgsTypes chip) { return get_chip_info(chip) != nullptr; } // --------------------------------------------------------------- BelId Arch::getBelByName(IdString name) const { if (bels.count(name)) return name; return BelId(); } IdString Arch::getBelName(BelId bel) const { return bel; } Loc Arch::getBelLocation(BelId bel) const { auto &info = bels.at(bel); return Loc(info.x, info.y, info.z); } BelId Arch::getBelByLocation(Loc loc) const { auto it = bel_by_loc.find(loc); if (it != bel_by_loc.end()) return it->second; return BelId(); } const std::vector &Arch::getBelsByTile(int x, int y) const { return bels_by_tile.at(x).at(y); } bool Arch::getBelGlobalBuf(BelId bel) const { return bels.at(bel).gb; } uint32_t Arch::getBelChecksum(BelId bel) const { // FIXME return 0; } void Arch::bindBel(BelId bel, CellInfo *cell, PlaceStrength strength) { bels.at(bel).bound_cell = cell; cell->bel = bel; cell->belStrength = strength; refreshUiBel(bel); } void Arch::unbindBel(BelId bel) { bels.at(bel).bound_cell->bel = BelId(); bels.at(bel).bound_cell->belStrength = STRENGTH_NONE; bels.at(bel).bound_cell = nullptr; refreshUiBel(bel); } bool Arch::checkBelAvail(BelId bel) const { return bels.at(bel).bound_cell == nullptr; } CellInfo *Arch::getBoundBelCell(BelId bel) const { return bels.at(bel).bound_cell; } CellInfo *Arch::getConflictingBelCell(BelId bel) const { return bels.at(bel).bound_cell; } const std::vector &Arch::getBels() const { return bel_ids; } IdString Arch::getBelType(BelId bel) const { return bels.at(bel).type; } const std::map &Arch::getBelAttrs(BelId bel) const { return bels.at(bel).attrs; } WireId Arch::getBelPinWire(BelId bel, IdString pin) const { const auto &bdata = bels.at(bel); if (!bdata.pins.count(pin)) log_error("bel '%s' has no pin '%s'\n", bel.c_str(this), pin.c_str(this)); return bdata.pins.at(pin).wire; } PortType Arch::getBelPinType(BelId bel, IdString pin) const { return bels.at(bel).pins.at(pin).type; } std::vector Arch::getBelPins(BelId bel) const { std::vector ret; for (auto &it : bels.at(bel).pins) ret.push_back(it.first); return ret; } // --------------------------------------------------------------- WireId Arch::getWireByName(IdString name) const { if (wires.count(name)) return name; return WireId(); } IdString Arch::getWireName(WireId wire) const { return wire; } IdString Arch::getWireType(WireId wire) const { return wires.at(wire).type; } const std::map &Arch::getWireAttrs(WireId wire) const { return wires.at(wire).attrs; } uint32_t Arch::getWireChecksum(WireId wire) const { // FIXME return 0; } void Arch::bindWire(WireId wire, NetInfo *net, PlaceStrength strength) { wires.at(wire).bound_net = net; net->wires[wire].pip = PipId(); net->wires[wire].strength = strength; refreshUiWire(wire); } void Arch::unbindWire(WireId wire) { auto &net_wires = wires.at(wire).bound_net->wires; auto pip = net_wires.at(wire).pip; if (pip != PipId()) { pips.at(pip).bound_net = nullptr; refreshUiPip(pip); } net_wires.erase(wire); wires.at(wire).bound_net = nullptr; refreshUiWire(wire); } bool Arch::checkWireAvail(WireId wire) const { return wires.at(wire).bound_net == nullptr; } NetInfo *Arch::getBoundWireNet(WireId wire) const { return wires.at(wire).bound_net; } NetInfo *Arch::getConflictingWireNet(WireId wire) const { return wires.at(wire).bound_net; } const std::vector &Arch::getWireBelPins(WireId wire) const { return wires.at(wire).bel_pins; } const std::vector &Arch::getWires() const { return wire_ids; } // --------------------------------------------------------------- PipId Arch::getPipByName(IdString name) const { if (pips.count(name)) return name; return PipId(); } IdString Arch::getPipName(PipId pip) const { return pip; } IdString Arch::getPipType(PipId pip) const { return pips.at(pip).type; } const std::map &Arch::getPipAttrs(PipId pip) const { return pips.at(pip).attrs; } uint32_t Arch::getPipChecksum(PipId wire) const { // FIXME return 0; } void Arch::bindPip(PipId pip, NetInfo *net, PlaceStrength strength) { WireId wire = pips.at(pip).dstWire; pips.at(pip).bound_net = net; wires.at(wire).bound_net = net; net->wires[wire].pip = pip; net->wires[wire].strength = strength; refreshUiPip(pip); refreshUiWire(wire); } void Arch::unbindPip(PipId pip) { WireId wire = pips.at(pip).dstWire; wires.at(wire).bound_net->wires.erase(wire); pips.at(pip).bound_net = nullptr; wires.at(wire).bound_net = nullptr; refreshUiPip(pip); refreshUiWire(wire); } bool Arch::checkPipAvail(PipId pip) const { return pips.at(pip).bound_net == nullptr; } NetInfo *Arch::getBoundPipNet(PipId pip) const { return pips.at(pip).bound_net; } NetInfo *Arch::getConflictingPipNet(PipId pip) const { return pips.at(pip).bound_net; } WireId Arch::getConflictingPipWire(PipId pip) const { return pips.at(pip).bound_net ? pips.at(pip).dstWire : WireId(); } const std::vector &Arch::getPips() const { return pip_ids; } Loc Arch::getPipLocation(PipId pip) const { return pips.at(pip).loc; } WireId Arch::getPipSrcWire(PipId pip) const { return pips.at(pip).srcWire; } WireId Arch::getPipDstWire(PipId pip) const { return pips.at(pip).dstWire; } DelayInfo Arch::getPipDelay(PipId pip) const { return pips.at(pip).delay; } const std::vector &Arch::getPipsDownhill(WireId wire) const { return wires.at(wire).downhill; } const std::vector &Arch::getPipsUphill(WireId wire) const { return wires.at(wire).uphill; } const std::vector &Arch::getWireAliases(WireId wire) const { return wires.at(wire).aliases; } // --------------------------------------------------------------- GroupId Arch::getGroupByName(IdString name) const { return name; } IdString Arch::getGroupName(GroupId group) const { return group; } std::vector Arch::getGroups() const { std::vector ret; for (auto &it : groups) ret.push_back(it.first); return ret; } const std::vector &Arch::getGroupBels(GroupId group) const { return groups.at(group).bels; } const std::vector &Arch::getGroupWires(GroupId group) const { return groups.at(group).wires; } const std::vector &Arch::getGroupPips(GroupId group) const { return groups.at(group).pips; } const std::vector &Arch::getGroupGroups(GroupId group) const { return groups.at(group).groups; } // --------------------------------------------------------------- delay_t Arch::estimateDelay(WireId src, WireId dst) const { return 0; } delay_t Arch::predictDelay(const NetInfo *net_info, const PortRef &sink) const { return 0; } bool Arch::getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay_t &budget) const { return false; } ArcBounds Arch::getRouteBoundingBox(WireId src, WireId dst) const { ArcBounds bb; int src_x = wires.at(src).x; int src_y = wires.at(src).y; int dst_x = wires.at(dst).x; int dst_y = wires.at(dst).y; bb.x0 = src_x; bb.y0 = src_y; bb.x1 = src_x; bb.y1 = src_y; auto extend = [&](int x, int y) { bb.x0 = std::min(bb.x0, x); bb.x1 = std::max(bb.x1, x); bb.y0 = std::min(bb.y0, y); bb.y1 = std::max(bb.y1, y); }; extend(dst_x, dst_y); return bb; } // --------------------------------------------------------------- bool Arch::place() { std::string placer = str_or_default(settings, id("placer"), defaultPlacer); if (placer == "heap") { bool have_iobuf_or_constr = false; for (auto cell : sorted(cells)) { CellInfo *ci = cell.second; if (ci->type == id("GENERIC_IOB") || ci->bel != BelId() || ci->attrs.count(id("BEL"))) { have_iobuf_or_constr = true; break; } } bool retVal; if (!have_iobuf_or_constr) { log_warning("Unable to use HeAP due to a lack of IO buffers or constrained cells as anchors; reverting to " "SA.\n"); retVal = placer1(getCtx(), Placer1Cfg(getCtx())); } else { PlacerHeapCfg cfg(getCtx()); cfg.ioBufTypes.insert(id("GENERIC_IOB")); retVal = placer_heap(getCtx(), cfg); } getCtx()->settings[getCtx()->id("place")] = 1; archInfoToAttributes(); return retVal; } else if (placer == "sa") { bool retVal = placer1(getCtx(), Placer1Cfg(getCtx())); getCtx()->settings[getCtx()->id("place")] = 1; archInfoToAttributes(); return retVal; } else { log_error("Generic architecture does not support placer '%s'\n", placer.c_str()); } } bool Arch::route() { std::string router = str_or_default(settings, id("router"), defaultRouter); bool result; if (router == "router1") { result = router1(getCtx(), Router1Cfg(getCtx())); } else if (router == "router2") { router2(getCtx(), Router2Cfg(getCtx())); result = true; } else { log_error("iCE40 architecture does not support router '%s'\n", router.c_str()); } getCtx()->settings[getCtx()->id("route")] = 1; archInfoToAttributes(); return result; } // --------------------------------------------------------------- const std::vector &Arch::getDecalGraphics(DecalId decal) const { if (!decal_graphics.count(decal)) { std::cerr << "No decal named " << decal.str(this) << std::endl; log_error("No decal named %s!\n", decal.c_str(this)); } return decal_graphics.at(decal); } DecalXY Arch::getBelDecal(BelId bel) const { return bels.at(bel).decalxy; } DecalXY Arch::getWireDecal(WireId wire) const { return wires.at(wire).decalxy; } DecalXY Arch::getPipDecal(PipId pip) const { return pips.at(pip).decalxy; } DecalXY Arch::getGroupDecal(GroupId group) const { return groups.at(group).decalxy; } // --------------------------------------------------------------- bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, DelayInfo &delay) const { if (!cellTiming.count(cell->name)) return false; const auto &tmg = cellTiming.at(cell->name); auto fnd = tmg.combDelays.find(CellDelayKey{fromPort, toPort}); if (fnd != tmg.combDelays.end()) { delay = fnd->second; return true; } else { return false; } } // Get the port class, also setting clockPort if applicable TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, int &clockInfoCount) const { if (!cellTiming.count(cell->name)) return TMG_IGNORE; const auto &tmg = cellTiming.at(cell->name); if (tmg.clockingInfo.count(port)) clockInfoCount = int(tmg.clockingInfo.at(port).size()); else clockInfoCount = 0; return get_or_default(tmg.portClasses, port, TMG_IGNORE); } TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port, int index) const { NPNR_ASSERT(cellTiming.count(cell->name)); const auto &tmg = cellTiming.at(cell->name); NPNR_ASSERT(tmg.clockingInfo.count(port)); return tmg.clockingInfo.at(port).at(index); } bool Arch::isValidBelForCell(CellInfo *cell, BelId bel) const { std::vector cells; cells.push_back(cell); Loc loc = getBelLocation(bel); for (auto tbel : getBelsByTile(loc.x, loc.y)) { if (tbel == bel) continue; CellInfo *bound = getBoundBelCell(tbel); if (bound != nullptr) cells.push_back(bound); } return cellsCompatible(cells.data(), int(cells.size())); } bool Arch::isBelLocationValid(BelId bel) const { std::vector cells; Loc loc = getBelLocation(bel); for (auto tbel : getBelsByTile(loc.x, loc.y)) { CellInfo *bound = getBoundBelCell(tbel); if (bound != nullptr) cells.push_back(bound); } return cellsCompatible(cells.data(), int(cells.size())); } #ifdef WITH_HEAP const std::string Arch::defaultPlacer = "heap"; #else const std::string Arch::defaultPlacer = "sa"; #endif const std::vector Arch::availablePlacers = {"sa", #ifdef WITH_HEAP "heap" #endif }; const std::string Arch::defaultRouter = "router1"; const std::vector Arch::availableRouters = {"router1", "router2"}; void Arch::assignArchInfo() { } bool Arch::cellsCompatible(const CellInfo **cells, int count) const { const NetInfo *clk = nullptr; int group = -1; for (int i = 0; i < count; i++) { const CellInfo *ci = cells[i]; if (ci->is_slice && ci->slice_clk != nullptr) { if (clk == nullptr) clk = ci->slice_clk; else if (clk != ci->slice_clk) return false; } if (ci->user_group != -1) { if (group == -1) group = ci->user_group; else if (group != ci->user_group) return false; } } return true; } NEXTPNR_NAMESPACE_END