/* * nextpnr -- Next Generation Place and Route * * Copyright (C) 2018 Clifford Wolf * Copyright (C) 2018 Serge Bazanski * * 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 #include "cells.h" #include "gfx.h" #include "log.h" #include "nextpnr.h" #include "placer1.h" #include "router1.h" #include "util.h" #include "torc/common/DirectoryTree.hpp" NEXTPNR_NAMESPACE_BEGIN std::unique_ptr torc_info; TorcInfo::TorcInfo(BaseCtx *ctx, const std::string &inDeviceName, const std::string &inPackageName) : TorcInfo(inDeviceName, inPackageName) { static const std::regex re_loc(".+_X(\\d+)Y(\\d+)"); std::cmatch what; tile_to_xy.resize(tiles.getTileCount()); for (TileIndex tileIndex(0); tileIndex < tiles.getTileCount(); tileIndex++) { const auto &tileInfo = tiles.getTileInfo(tileIndex); if (!std::regex_match(tileInfo.getName(), what, re_loc)) throw; const auto x = boost::lexical_cast(what.str(1)); const auto y = boost::lexical_cast(what.str(2)); tile_to_xy[tileIndex] = std::make_pair(x,y); } bel_to_site_index.reserve(sites.getSiteCount() * 4); bel_to_loc.reserve(sites.getSiteCount() * 4); site_index_to_bel.resize(sites.getSiteCount()); site_index_to_type.resize(sites.getSiteCount()); BelId b; b.index = 0; for (SiteIndex i(0); i < sites.getSiteCount(); ++i) { const auto &site = sites.getSite(i); const auto &pd = site.getPrimitiveDefPtr(); const auto &type = pd->getName(); int x, y; std::tie(x,y) = tile_to_xy[site.getTileIndex()]; if (type == "SLICEL" || type == "SLICEM") { bel_to_site_index.push_back(i); bel_to_site_index.push_back(i); bel_to_site_index.push_back(i); bel_to_site_index.push_back(i); site_index_to_type[i] = id_SLICE_LUT6; const auto site_name = site.getName(); if (!std::regex_match(site_name.c_str(), what, re_loc)) throw; const auto sx = boost::lexical_cast(what.str(1)); if ((sx & 1) == 0) { bel_to_loc.emplace_back(x, y, 0); bel_to_loc.emplace_back(x, y, 1); bel_to_loc.emplace_back(x, y, 2); bel_to_loc.emplace_back(x, y, 3); } else { bel_to_loc.emplace_back(x, y, 4); bel_to_loc.emplace_back(x, y, 5); bel_to_loc.emplace_back(x, y, 6); bel_to_loc.emplace_back(x, y, 7); } site_index_to_bel[i] = b; b.index += 4; } else if (type == "IOB33S" || type == "IOB33M") { bel_to_site_index.push_back(i); site_index_to_type[i] = id_IOB33; // TODO: Fix z when two IOBs on same tile bel_to_loc.emplace_back(x, y, 0); site_index_to_bel[i] = b; ++b.index; } else if (type == "IOB18S" || type == "IOB18M") { bel_to_site_index.push_back(i); site_index_to_type[i] = id_IOB18; // TODO: Fix z when two IOBs on same tile bel_to_loc.emplace_back(x, y, 0); site_index_to_bel[i] = b; ++b.index; } else { bel_to_site_index.push_back(i); site_index_to_type[i] = ctx->id(type); bel_to_loc.emplace_back(x, y, 0); site_index_to_bel[i] = b; ++b.index; } } num_bels = bel_to_site_index.size(); bel_to_site_index.shrink_to_fit(); bel_to_loc.shrink_to_fit(); const std::regex re_124("(.+_)?[NESW][NESWLR](\\d)((BEG(_[NS])?)|(END(_[NS])?)|[A-E])?\\d(_\\d)?"); const std::regex re_L("(.+_)?L(H|V|VB)(_L)?\\d+(_\\d)?"); const std::regex re_BYP("BYP(_ALT)?\\d"); const std::regex re_BYP_B("BYP_[BL]\\d"); const std::regex re_BOUNCE_NS("(BYP|FAN)_BOUNCE_[NS]3_\\d"); const std::regex re_FAN("FAN(_ALT)?\\d"); const std::regex re_CLB_I1_6("CLBL[LM]_(L|LL|M)_[A-D]([1-6])"); const std::regex bufg_i("CLK_BUFG_BUFGCTRL\\d+_I0"); const std::regex bufg_o("CLK_BUFG_BUFGCTRL\\d+_O"); const std::regex hrow("CLK_HROW_CLK[01]_[34]"); std::unordered_map> delay_lookup; std::unordered_map segment_to_anchor; Tilewire currentTilewire; WireId w; w.index = 0; for (TileIndex tileIndex(0); tileIndex < tiles.getTileCount(); tileIndex++) { // iterate over every wire in the tile const auto &tileInfo = tiles.getTileInfo(tileIndex); auto tileTypeIndex = tileInfo.getTypeIndex(); auto wireCount = tiles.getWireCount(tileTypeIndex); currentTilewire.setTileIndex(tileIndex); for (WireIndex wireIndex(0); wireIndex < wireCount; wireIndex++) { currentTilewire.setWireIndex(wireIndex); const auto ¤tSegment = segments.getTilewireSegment(currentTilewire); if (!currentSegment.isTrivial()) { auto r = segment_to_anchor.emplace(currentSegment, currentSegment.getAnchorTileIndex()); if (r.second) { TilewireVector segment; const_cast(*ddb).expandSegment(currentTilewire, segment, DDB::eExpandDirectionNone); // expand all of the arcs TilewireVector::const_iterator sep = segment.begin(); TilewireVector::const_iterator see = segment.end(); while(sep < see) { // expand the tilewire sinks const Tilewire& tilewire = *sep++; const auto &tileInfo = tiles.getTileInfo(tilewire.getTileIndex()); const auto &tileTypeName = tiles.getTileTypeName(tileInfo.getTypeIndex()); if (boost::starts_with(tileTypeName, "INT") || boost::starts_with(tileTypeName, "CLB")) { r.first->second = tilewire.getTileIndex(); break; } } } if (r.first->second != tileIndex) continue; segment_to_wire.emplace(currentSegment, w); } else trivial_to_wire.emplace(currentTilewire, w); wire_to_tilewire.push_back(currentTilewire); auto it = delay_lookup.find(tileTypeIndex); if (it == delay_lookup.end()) { auto wireCount = tiles.getWireCount(tileTypeIndex); std::vector tile_delays(wireCount); for (WireIndex wireIndex(0); wireIndex < wireCount; wireIndex++) { const WireInfo &wireInfo = tiles.getWireInfo(tileTypeIndex, wireIndex); auto wire_name = wireInfo.getName(); if (std::regex_match(wire_name, what, re_124)) { switch (what.str(2)[0]) { case '1': tile_delays[wireIndex] = 150; break; case '2': tile_delays[wireIndex] = 170; break; case '4': tile_delays[wireIndex] = 210; break; case '6': tile_delays[wireIndex] = 210; break; default: throw; } } else if (std::regex_match(wire_name, what, re_L)) { std::string l(what[2]); if (l == "H") tile_delays[wireIndex] = 360; else if (l == "VB") tile_delays[wireIndex] = 300; else if (l == "V") tile_delays[wireIndex] = 350; else throw; } else if (std::regex_match(wire_name, what, re_BYP)) { tile_delays[wireIndex] = 190; } else if (std::regex_match(wire_name, what, re_BYP_B)) { } else if (std::regex_match(wire_name, what, re_FAN)) { tile_delays[wireIndex] = 190; } else if (std::regex_match(wire_name, what, re_CLB_I1_6)) { switch (what.str(2)[0]) { case '1': tile_delays[wireIndex] = 280; break; case '2': tile_delays[wireIndex] = 280; break; case '3': tile_delays[wireIndex] = 180; break; case '4': tile_delays[wireIndex] = 180; break; case '5': tile_delays[wireIndex] = 80; break; case '6': tile_delays[wireIndex] = 40; break; default: throw; } } } it = delay_lookup.emplace(tileTypeIndex, std::move(tile_delays)).first; } assert(it != delay_lookup.end()); DelayInfo d; d.delay = it->second[currentTilewire.getWireIndex()]; wire_to_delay.emplace_back(std::move(d)); ++w.index; } } segment_to_anchor.clear(); wire_to_tilewire.shrink_to_fit(); wire_to_delay.shrink_to_fit(); num_wires = wire_to_tilewire.size(); wire_is_global.resize(num_wires); wire_to_pips_downhill.resize(num_wires); // std::unordered_map arc_to_pip; ArcVector arcs; ExtendedWireInfo ewi(*ddb); PipId p; p.index = 0; for (w.index = 0; w.index < num_wires; ++w.index) { const auto ¤tTilewire = wire_to_tilewire[w.index]; if (currentTilewire.isUndefined()) continue; const auto &tileInfo = tiles.getTileInfo(currentTilewire.getTileIndex()); const auto tileTypeName = tiles.getTileTypeName(tileInfo.getTypeIndex()); const bool clb = boost::starts_with( tileTypeName, "CLB"); // Disable all CLB route-throughs (i.e. LUT in->out, LUT A->AMUX, for now) auto &pips = wire_to_pips_downhill[w.index]; const bool clk_tile = boost::starts_with(tileTypeName, "CLK"); bool global_tile = false; arcs.clear(); //const_cast(*ddb).expandSegmentSinks(currentTilewire, arcs, DDB::eExpandDirectionNone, // false /* inUseTied */, true /*inUseRegular */, // true /* inUseIrregular */, !clb /* inUseRoutethrough */); { // expand the segment TilewireVector segment; const_cast(*ddb).expandSegment(currentTilewire, segment, DDB::eExpandDirectionNone); // expand all of the arcs TilewireVector::const_iterator sep = segment.begin(); TilewireVector::const_iterator see = segment.end(); while(sep < see) { // expand the tilewire sinks const Tilewire& tilewire = *sep++; const auto &tileInfo = tiles.getTileInfo(tilewire.getTileIndex()); const auto &tileTypeName = tiles.getTileTypeName(tileInfo.getTypeIndex()); global_tile = global_tile || boost::starts_with(tileTypeName, "CLK") || boost::starts_with(tileTypeName, "HCLK") || boost::starts_with(tileTypeName, "CFG"); TilewireVector sinks; const_cast(*ddb).expandTilewireSinks(tilewire, sinks, false /*inUseTied*/, true /*inUseRegular*/, true /*inUseIrregular*/, !clb /* inUseRoutethrough */); // rewrite the sinks as arcs TilewireVector::const_iterator sip = sinks.begin(); TilewireVector::const_iterator sie = sinks.end(); while(sip < sie) { Arc a(tilewire, *sip++); // Disable BUFG I0 -> O routethrough if (clk_tile) { ewi.set(a.getSourceTilewire()); if (std::regex_match(ewi.mWireName, bufg_i)) { ewi.set(a.getSinkTilewire()); if (std::regex_match(ewi.mWireName, bufg_o)) continue; } } // Disable entering HROW from INT_[LR].CLK[01] if (boost::starts_with(tileTypeName, "CLK_HROW")) { ewi.set(a.getSourceTilewire()); if (std::regex_match(ewi.mWireName, hrow)) continue; } pips.emplace_back(p); pip_to_arc.emplace_back(a); // arc_to_pip.emplace(a, p.index); ++p.index; } } } pips.shrink_to_fit(); if (global_tile) wire_is_global[w.index] = true; } pip_to_arc.shrink_to_fit(); num_pips = pip_to_arc.size(); height = (int)tiles.getRowCount(); width = (int)tiles.getColCount(); } TorcInfo::TorcInfo(const std::string& inDeviceName, const std::string &inPackageName) : ddb(new DDB(inDeviceName, inPackageName)), sites(ddb->getSites()), tiles(ddb->getTiles()), segments(ddb->getSegments()) { } // ----------------------------------------------------------------------- void IdString::initialize_arch(const BaseCtx *ctx) { #define X(t) initialize_add(ctx, #t, ID_##t); #include "constids.inc" #undef X } // ----------------------------------------------------------------------- Arch::Arch(ArchArgs args) : args(args) { torc::common::DirectoryTree directoryTree("/opt/torc/src/torc"); if (args.type == ArchArgs::Z020) { torc_info = std::unique_ptr(new TorcInfo(this, "xc7z020", args.package)); } else if (args.type == ArchArgs::VX980) { torc_info = std::unique_ptr(new TorcInfo(this, "xc7vx980t", args.package)); } else { log_error("Unsupported XC7 chip type.\n"); } width = torc_info->width; height = torc_info->height; /*if (getCtx()->verbose)*/ { log_info("Number of bels: %d\n", torc_info->num_bels); log_info("Number of wires: %d\n", torc_info->num_wires); log_info("Number of pips: %d\n", torc_info->num_pips); } bel_to_cell.resize(torc_info->num_bels); wire_to_net.resize(torc_info->num_wires); pip_to_net.resize(torc_info->num_pips); } // ----------------------------------------------------------------------- std::string Arch::getChipName() const { if (args.type == ArchArgs::Z020) { return "z020"; } else if (args.type == ArchArgs::VX980) { return "vx980"; } else { log_error("Unsupported XC7 chip type.\n"); } } // ----------------------------------------------------------------------- IdString Arch::archArgsToId(ArchArgs args) const { if (args.type == ArchArgs::Z020) return id("z020"); if (args.type == ArchArgs::VX980) return id("vx980"); return IdString(); } // ----------------------------------------------------------------------- static bool endsWith(const std::string& str, const std::string& suffix) { return str.size() >= suffix.size() && 0 == str.compare(str.size()-suffix.size(), suffix.size(), suffix); } BelId Arch::getBelByName(IdString name) const { std::string n = name.str(this); int ndx = 0; if (endsWith(n,"_A") || endsWith(n,"_B") || endsWith(n,"_C") || endsWith(n,"_D")) { ndx = (int)(n.back() - 'A'); n = n.substr(0,n.size()-2); } auto it = torc_info->sites.findSiteIndex(n); if (it != SiteIndex(-1)) { BelId id = torc_info->site_index_to_bel.at(it); id.index += ndx; return id; } return BelId(); } BelId Arch::getBelByLocation(Loc loc) const { BelId bel; if (bel_by_loc.empty()) { for (int i = 0; i < torc_info->num_bels; i++) { BelId b; b.index = i; bel_by_loc[getBelLocation(b)] = b; } } auto it = bel_by_loc.find(loc); if (it != bel_by_loc.end()) bel = it->second; return bel; } BelRange Arch::getBelsByTile(int x, int y) const { BelRange br; br.b.cursor = 0; br.e.cursor = 0; NPNR_ASSERT("TODO"); return br; } PortType Arch::getBelPinType(BelId bel, IdString pin) const { NPNR_ASSERT(bel != BelId()); NPNR_ASSERT("TODO"); return PORT_INOUT; } std::vector> Arch::getBelAttrs(BelId bel) const { std::vector> ret; return ret; } WireId Arch::getBelPinWire(BelId bel, IdString pin) const { auto pin_name = pin.str(this); auto bel_type = getBelType(bel); if (bel_type == id_SLICE_LUT6) { // For all LUT based inputs and outputs (I1-I6,O,OQ,OMUX) then change the I/O into the LUT if (pin_name[0] == 'I' || pin_name[0] == 'O') { switch (torc_info->bel_to_loc[bel.index].z) { case 0: case 4: pin_name[0] = 'A'; break; case 1: case 5: pin_name[0] = 'B'; break; case 2: case 6: pin_name[0] = 'C'; break; case 3: case 7: pin_name[0] = 'D'; break; default: throw; } } } else if (bel_type == id_PS7 || bel_type == id_MMCME2_ADV) { // e.g. Convert DDRARB[0] -> DDRARB0 pin_name.erase(std::remove_if(pin_name.begin(), pin_name.end(), boost::is_any_of("[]")), pin_name.end()); } auto site_index = torc_info->bel_to_site_index[bel.index]; const auto &site = torc_info->sites.getSite(site_index); auto &tw = site.getPinTilewire(pin_name); if (tw.isUndefined()) log_error("no wire found for site '%s' pin '%s' \n", torc_info->bel_to_name(bel.index).c_str(), pin_name.c_str()); return torc_info->tilewire_to_wire(tw); } std::vector Arch::getBelPins(BelId bel) const { std::vector ret; NPNR_ASSERT("TODO"); return ret; } // ----------------------------------------------------------------------- WireId Arch::getWireByName(IdString name) const { WireId ret; if (wire_by_name.empty()) { for (int i = 0; i < torc_info->num_wires; i++) wire_by_name[id(torc_info->wire_to_name(i))] = i; } auto it = wire_by_name.find(name); if (it != wire_by_name.end()) ret.index = it->second; return ret; } IdString Arch::getWireType(WireId wire) const { NPNR_ASSERT(wire != WireId()); NPNR_ASSERT("TODO"); return IdString(); } // ----------------------------------------------------------------------- std::vector> Arch::getWireAttrs(WireId wire) const { std::vector> ret; NPNR_ASSERT("TODO"); return ret; } // ----------------------------------------------------------------------- PipId Arch::getPipByName(IdString name) const { PipId ret; if (pip_by_name.empty()) { for (int i = 0; i < torc_info->num_pips; i++) { PipId pip; pip.index = i; pip_by_name[getPipName(pip)] = i; } } auto it = pip_by_name.find(name); if (it != pip_by_name.end()) ret.index = it->second; return ret; } IdString Arch::getPipName(PipId pip) const { NPNR_ASSERT(pip != PipId()); ExtendedWireInfo ewi_src(*torc_info->ddb, torc_info->pip_to_arc[pip.index].getSourceTilewire()); ExtendedWireInfo ewi_dst(*torc_info->ddb, torc_info->pip_to_arc[pip.index].getSinkTilewire()); std::stringstream pip_name; pip_name << ewi_src.mTileName << "." << ewi_src.mWireName << ".->." << ewi_dst.mWireName; return id(pip_name.str()); } std::vector> Arch::getPipAttrs(PipId pip) const { std::vector> ret; NPNR_ASSERT("TODO"); return ret; } // ----------------------------------------------------------------------- BelId Arch::getPackagePinBel(const std::string &pin) const { return getBelByName(id(pin)); } std::string Arch::getBelPackagePin(BelId bel) const { NPNR_ASSERT("TODO"); return ""; } // ----------------------------------------------------------------------- GroupId Arch::getGroupByName(IdString name) const { for (auto g : getGroups()) if (getGroupName(g) == name) return g; return GroupId(); } IdString Arch::getGroupName(GroupId group) const { std::string suffix; switch (group.type) { NPNR_ASSERT("TODO"); default: return IdString(); } return id("X" + std::to_string(group.x) + "/Y" + std::to_string(group.y) + "/" + suffix); } std::vector Arch::getGroups() const { std::vector ret; NPNR_ASSERT("TODO"); return ret; } std::vector Arch::getGroupBels(GroupId group) const { std::vector ret; return ret; } std::vector Arch::getGroupWires(GroupId group) const { std::vector ret; return ret; } std::vector Arch::getGroupPips(GroupId group) const { std::vector ret; NPNR_ASSERT("TODO"); return ret; } std::vector Arch::getGroupGroups(GroupId group) const { std::vector ret; NPNR_ASSERT("TODO"); return ret; } // ----------------------------------------------------------------------- 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())); } // ----------------------------------------------------------------------- DecalXY Arch::getBelDecal(BelId bel) const { DecalXY decalxy; decalxy.decal.type = DecalId::TYPE_BEL; decalxy.decal.index = bel.index; decalxy.decal.active = bel_to_cell.at(bel.index) != nullptr; return decalxy; } DecalXY Arch::getWireDecal(WireId wire) const { DecalXY decalxy; decalxy.decal.type = DecalId::TYPE_WIRE; decalxy.decal.index = wire.index; decalxy.decal.active = wire_to_net.at(wire.index) != nullptr; return decalxy; } DecalXY Arch::getPipDecal(PipId pip) const { DecalXY decalxy; decalxy.decal.type = DecalId::TYPE_PIP; decalxy.decal.index = pip.index; decalxy.decal.active = pip_to_net.at(pip.index) != nullptr; return decalxy; }; DecalXY Arch::getGroupDecal(GroupId group) const { DecalXY decalxy; decalxy.decal.type = DecalId::TYPE_GROUP; decalxy.decal.index = (group.type << 16) | (group.x << 8) | (group.y); decalxy.decal.active = true; return decalxy; }; std::vector Arch::getDecalGraphics(DecalId decal) const { std::vector ret; if (decal.type == DecalId::TYPE_BEL) { BelId bel; bel.index = decal.index; auto bel_type = getBelType(bel); int x = torc_info->bel_to_loc[bel.index].x; int y = torc_info->bel_to_loc[bel.index].y; int z = torc_info->bel_to_loc[bel.index].z; if (bel_type == id_SLICE_LUT6) { GraphicElement el; /*if (z>3) { z = z - 4; x -= logic_cell_x2- logic_cell_x1; }*/ el.type = GraphicElement::TYPE_BOX; el.style = decal.active ? GraphicElement::STYLE_ACTIVE : GraphicElement::STYLE_INACTIVE; el.x1 = x + logic_cell_x1; el.x2 = x + logic_cell_x2; el.y1 = y + logic_cell_y1 + (z)*logic_cell_pitch; el.y2 = y + logic_cell_y2 + (z)*logic_cell_pitch; ret.push_back(el); } } return ret; } // ----------------------------------------------------------------------- bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, DelayInfo &delay) const { if (cell->type == id_SLICE_LUT6) { if (fromPort.index >= id_I1.index && fromPort.index <= id_I6.index) { if (toPort == id_O) { delay.delay = 124; // Tilo return true; } if (toPort == id_OQ) { delay.delay = 95; // Tas return true; } } if (fromPort == id_CLK) { if (toPort == id_OQ) { delay.delay = 456; // Tcko return true; } } } else if (cell->type == id_BUFGCTRL) { return true; } return false; } // Get the port class, also setting clockPort to associated clock if applicable TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, int &clockInfoCount) const { if (cell->type == id_SLICE_LUT6) { if (port == id_CLK) return TMG_CLOCK_INPUT; if (port == id_CIN) return TMG_COMB_INPUT; if (port == id_COUT) return TMG_COMB_OUTPUT; if (port == id_O) { // LCs with no inputs are constant drivers if (cell->lcInfo.inputCount == 0) return TMG_IGNORE; return TMG_COMB_OUTPUT; } if (cell->lcInfo.dffEnable) { clockInfoCount = 1; if (port == id_OQ) return TMG_REGISTER_OUTPUT; return TMG_REGISTER_INPUT; } else { return TMG_COMB_INPUT; } // TODO // if (port == id_OMUX) } else if (cell->type == id_IOB33 || cell->type == id_IOB18) { if (port == id_I) return TMG_STARTPOINT; else if (port == id_O) return TMG_ENDPOINT; } else if (cell->type == id_BUFGCTRL) { if (port == id_O) return TMG_COMB_OUTPUT; return TMG_COMB_INPUT; } else if (cell->type == id_PS7) { // TODO return TMG_IGNORE; } else if (cell->type == id_MMCME2_ADV) { return TMG_IGNORE; } log_error("no timing info for port '%s' of cell type '%s'\n", port.c_str(this), cell->type.c_str(this)); } TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port, int index) const { TimingClockingInfo info; if (cell->type == id_SLICE_LUT6) { info.clock_port = id_CLK; info.edge = cell->lcInfo.negClk ? FALLING_EDGE : RISING_EDGE; if (port == id_OQ) { bool has_clktoq = getCellDelay(cell, id_CLK, id_OQ, info.clockToQ); NPNR_ASSERT(has_clktoq); } else { info.setup.delay = 124; // Tilo info.hold.delay = 0; } } else { NPNR_ASSERT_FALSE("unhandled cell type in getPortClockingInfo"); } return info; } bool Arch::isGlobalNet(const NetInfo *net) const { if (net == nullptr) return false; return net->driver.cell != nullptr && net->driver.cell->type == id_BUFGCTRL && net->driver.port == id_O; } // Assign arch arg info void Arch::assignArchInfo() { for (auto &net : getCtx()->nets) { NetInfo *ni = net.second.get(); if (isGlobalNet(ni)) ni->is_global = true; ni->is_enable = false; ni->is_reset = false; for (auto usr : ni->users) { if (is_enable_port(this, usr)) ni->is_enable = true; if (is_reset_port(this, usr)) ni->is_reset = true; } } for (auto &cell : getCtx()->cells) { CellInfo *ci = cell.second.get(); assignCellInfo(ci); } } void Arch::assignCellInfo(CellInfo *cell) { cell->belType = cell->type; if (cell->type == id_SLICE_LUT6) { cell->lcInfo.dffEnable = bool_or_default(cell->params, id_DFF_ENABLE); cell->lcInfo.carryEnable = bool_or_default(cell->params, id_CARRY_ENABLE); cell->lcInfo.negClk = bool_or_default(cell->params, id_NEG_CLK); cell->lcInfo.clk = get_net_or_empty(cell, id_CLK); cell->lcInfo.cen = get_net_or_empty(cell, id_CEN); cell->lcInfo.sr = get_net_or_empty(cell, id_SR); cell->lcInfo.inputCount = 0; if (get_net_or_empty(cell, id_I1)) cell->lcInfo.inputCount++; if (get_net_or_empty(cell, id_I2)) cell->lcInfo.inputCount++; if (get_net_or_empty(cell, id_I3)) cell->lcInfo.inputCount++; if (get_net_or_empty(cell, id_I4)) cell->lcInfo.inputCount++; if (get_net_or_empty(cell, id_I5)) cell->lcInfo.inputCount++; if (get_net_or_empty(cell, id_I6)) cell->lcInfo.inputCount++; } } NEXTPNR_NAMESPACE_END