diff options
-rw-r--r-- | gowin/arch.cc | 56 | ||||
-rw-r--r-- | gowin/arch.h | 14 | ||||
-rw-r--r-- | gowin/globals.cc | 486 | ||||
-rw-r--r-- | gowin/globals.h | 66 | ||||
-rw-r--r-- | gowin/pack.cc | 4 |
5 files changed, 367 insertions, 259 deletions
diff --git a/gowin/arch.cc b/gowin/arch.cc index 7b9097c9..a968c112 100644 --- a/gowin/arch.cc +++ b/gowin/arch.cc @@ -25,7 +25,6 @@ #include <regex> #include "embed.h" #include "gfx.h" -#include "globals.h" #include "nextpnr.h" #include "placer1.h" #include "placer_heap.h" @@ -257,6 +256,8 @@ bool Arch::allocate_longwire(NetInfo *ni, int lw_idx) return true; } +void Arch::auto_longwires() {} + void Arch::fix_longwire_bels() { // After routing, it is clear which wires and in which bus SS00 and SS40 are used and @@ -552,6 +553,28 @@ void Arch::setDelayScaling(double scale, double offset) args.delayOffset = offset; } +void Arch::addCellTimingCombIn(IdString cell, IdString port) { cellTiming[cell].portClasses[port] = TMG_COMB_INPUT; } + +void Arch::addCellTimingCombOut(IdString cell, IdString port) { cellTiming[cell].portClasses[port] = TMG_COMB_OUTPUT; } + +void Arch::addCellTimingRegIn(IdString cell, IdString port) { cellTiming[cell].portClasses[port] = TMG_REGISTER_INPUT; } + +void Arch::addCellTimingRegOut(IdString cell, IdString port) +{ + cellTiming[cell].portClasses[port] = TMG_REGISTER_OUTPUT; +} + +void Arch::addCellTimingIO(IdString cell, IdString port) +{ + if (port == id_I) { + cellTiming[cell].portClasses[port] = TMG_ENDPOINT; + } else { + if (port == id_O) { + cellTiming[cell].portClasses[port] = TMG_STARTPOINT; + } + } +} + void Arch::addCellTimingClock(IdString cell, IdString port) { cellTiming[cell].portClasses[port] = TMG_CLOCK_INPUT; } void Arch::addCellTimingDelay(IdString cell, IdString fromPort, IdString toPort, DelayQuad delay) @@ -1989,6 +2012,8 @@ void Arch::assignArchInfo() // add timing paths addCellTimingClock(cname, id_CLK); + addCellTimingRegIn(cname, id_CE); + addCellTimingRegIn(cname, id_LSR); IdString ports[4] = {id_A, id_B, id_C, id_D}; for (int i = 0; i < 4; i++) { DelayPair setup = @@ -2019,7 +2044,18 @@ void Arch::assignArchInfo() delay = delay + delayLookup(speed->lut.timings.get(), speed->lut.num_timings, id_fx_ofx1); addCellTimingDelay(cname, id_I0, id_OF, delay); addCellTimingDelay(cname, id_I1, id_OF, delay); + addCellTimingCombIn(cname, id_SEL); } + case ID_IOB: + /* FALLTHRU */ + case ID_IOBS: + addCellTimingIO(cname, id_I); + addCellTimingIO(cname, id_O); + break; + case ID_BUFS: + addCellTimingCombIn(cname, id_I); + addCellTimingCombOut(cname, id_O); + break; default: break; } @@ -2063,4 +2099,22 @@ bool Arch::cellsCompatible(const CellInfo **cells, int count) const return true; } +void Arch::route_gowin_globals(Context *ctx) { globals_router.route_globals(ctx); } + +void Arch::mark_gowin_globals(Context *ctx) { globals_router.mark_globals(ctx); } +// --------------------------------------------------------------- +void Arch::pre_pack(Context *ctx) +{ + if (bool_or_default(settings, id("arch.enable-auto-longwires"))) { + auto_longwires(); + } +} + +void Arch::post_pack(Context *ctx) +{ + if (bool_or_default(settings, id("arch.enable-globals"))) { + mark_gowin_globals(ctx); + } +} + NEXTPNR_NAMESPACE_END diff --git a/gowin/arch.h b/gowin/arch.h index c59f8eb3..cd43aa7a 100644 --- a/gowin/arch.h +++ b/gowin/arch.h @@ -31,6 +31,8 @@ #include "nextpnr_namespaces.h" #include "nextpnr_types.h" +#include "globals.h" + NEXTPNR_NAMESPACE_BEGIN template <typename T> struct RelPtr @@ -337,6 +339,11 @@ struct Arch : BaseArch<ArchRanges> void setDelayScaling(double scale, double offset); void addCellTimingClock(IdString cell, IdString port); + void addCellTimingIO(IdString cell, IdString port); + void addCellTimingCombIn(IdString cell, IdString port); + void addCellTimingCombOut(IdString cell, IdString port); + void addCellTimingRegIn(IdString cell, IdString port); + void addCellTimingRegOut(IdString cell, IdString port); void addCellTimingDelay(IdString cell, IdString fromPort, IdString toPort, DelayQuad delay); void addCellTimingSetupHold(IdString cell, IdString port, IdString clock, DelayPair setup, DelayPair hold); void addCellTimingClockToOut(IdString cell, IdString port, IdString clock, DelayQuad clktoq); @@ -462,6 +469,13 @@ struct Arch : BaseArch<ArchRanges> bool haveBelType(int x, int y, IdString bel_type); bool allocate_longwire(NetInfo *ni, int lw_idx = -1); void fix_longwire_bels(); + void pre_pack(Context *ctx); + void post_pack(Context *ctx); + void auto_longwires(); + + GowinGlobalRouter globals_router; + void mark_gowin_globals(Context *ctx); + void route_gowin_globals(Context *ctx); // chip db version unsigned int const chipdb_version = 1; diff --git a/gowin/globals.cc b/gowin/globals.cc index ed75a938..1794dd4c 100644 --- a/gowin/globals.cc +++ b/gowin/globals.cc @@ -18,7 +18,6 @@ * */ -#include "globals.h" #include <algorithm> #include <iomanip> #include <iostream> @@ -31,306 +30,279 @@ NEXTPNR_NAMESPACE_BEGIN -class GowinGlobalRouter +bool GowinGlobalRouter::is_clock_port(PortRef const &user) { - public: - GowinGlobalRouter(Context *ctx) : ctx(ctx){}; - - private: - // wire -> clock# - dict<WireId, int> used_wires; - - // ordered nets - struct onet_t - { - IdString name; - int clock_ports; - WireId clock_io_wire; // IO wire if there is one - - onet_t() : name(IdString()), clock_ports(0), clock_io_wire(WireId()) {} - onet_t(IdString _name) : name(_name), clock_ports(0), clock_io_wire(WireId()) {} - - // sort - bool operator<(const onet_t &other) const - { - if ((clock_io_wire != WireId()) ^ (other.clock_io_wire != WireId())) { - return !(clock_io_wire != WireId()); - } - return clock_ports < other.clock_ports; - } - // search - bool operator==(const onet_t &other) const { return name == other.name; } - }; + if ((user.cell->type == id_SLICE || user.cell->type == id_ODDR || user.cell->type == id_ODDRC) && + user.port == id_CLK) { + return true; + } + return false; +} - bool is_clock_port(PortRef const &user) - { - if ((user.cell->type == id_SLICE || user.cell->type == id_ODDR || user.cell->type == id_ODDRC) && user.port == id_CLK) { - return true; - } - return false; +std::pair<WireId, BelId> GowinGlobalRouter::clock_io(Context *ctx, PortRef const &driver) +{ + // XXX normally all alternative functions of the pins should be passed + // in the chip database, but at the moment we find them from aliases/pips + // XXX check diff inputs too + if (driver.cell == nullptr || driver.cell->type != id_IOB || !driver.cell->attrs.count(id_BEL)) { + return std::make_pair(WireId(), BelId()); } + // clock IOs have pips output->SPINExx - WireId clock_io(PortRef const &driver) - { - // XXX normally all alternative functions of the pins should be passed - // in the chip database, but at the moment we find them from aliases/pips - // XXX check diff inputs too - if (driver.cell == nullptr || driver.cell->bel == BelId()) { - return WireId(); - } - // clock IOs have pips output->SPINExx - BelInfo &bel = ctx->bel_info(driver.cell->bel); - if (bel.type != id_IOB) { - return WireId(); + BelInfo &bel = ctx->bel_info(ctx->id(driver.cell->attrs[id_BEL].as_string())); + WireId wire = bel.pins[id_O].wire; + for (auto const pip : ctx->getPipsDownhill(wire)) { + if (ctx->wire_info(ctx->getPipDstWire(pip)).type.str(ctx).rfind("SPINE", 0) == 0) { + return std::make_pair(wire, bel.name); } - WireId wire = bel.pins[id_O].wire; - for (auto const pip : ctx->getPipsDownhill(wire)) { - if (ctx->wire_info(ctx->getPipDstWire(pip)).type.str(ctx).rfind("SPINE", 0) == 0) { - return wire; - } - } - return WireId(); } + return std::make_pair(WireId(), BelId()); +} - // gather the clock nets - void gather_clock_nets(std::vector<onet_t> &clock_nets) - { - for (auto const &net : ctx->nets) { - NetInfo const *ni = net.second.get(); - auto new_clock = clock_nets.end(); - WireId clock_wire = clock_io(ni->driver); - if (clock_wire != WireId()) { - clock_nets.emplace_back(net.first); - new_clock = --clock_nets.end(); - new_clock->clock_io_wire = clock_wire; - } - for (auto const &user : ni->users) { - if (is_clock_port(user)) { - if (new_clock == clock_nets.end()) { - clock_nets.emplace_back(net.first); - new_clock = --clock_nets.end(); - } - ++(new_clock->clock_ports); +// gather the clock nets +void GowinGlobalRouter::gather_clock_nets(Context *ctx, std::vector<globalnet_t> &clock_nets) +{ + for (auto const &net : ctx->nets) { + NetInfo const *ni = net.second.get(); + auto new_clock = clock_nets.end(); + auto clock_wire_bel = clock_io(ctx, ni->driver); + if (clock_wire_bel.first != WireId()) { + clock_nets.emplace_back(net.first); + new_clock = --clock_nets.end(); + new_clock->clock_io_wire = clock_wire_bel.first; + new_clock->clock_io_bel = clock_wire_bel.second; + } + for (auto const &user : ni->users) { + if (is_clock_port(user)) { + if (new_clock == clock_nets.end()) { + clock_nets.emplace_back(net.first); + new_clock = --clock_nets.end(); } + ++(new_clock->clock_ports); } } - // need to prioritize the nets - std::sort(clock_nets.begin(), clock_nets.end()); + } + // need to prioritize the nets + std::sort(clock_nets.begin(), clock_nets.end()); - if (ctx->verbose) { - for (auto const &net : clock_nets) { - log_info(" Net:%s, ports:%d, io:%s\n", net.name.c_str(ctx), net.clock_ports, - net.clock_io_wire == WireId() ? "No" : net.clock_io_wire.c_str(ctx)); - } + if (ctx->verbose) { + for (auto const &net : clock_nets) { + log_info(" Net:%s, ports:%d, io:%s\n", net.name.c_str(ctx), net.clock_ports, + net.clock_io_wire == WireId() ? "No" : net.clock_io_wire.c_str(ctx)); } } +} - // non clock port - // returns GB pip - IdString route_to_non_clock_port(WireId const dstWire, int clock, pool<IdString> &used_pips, - pool<IdString> &undo_wires) - { - static std::vector<IdString> one_hop = {id_S111, id_S121, id_N111, id_N121, id_W111, id_W121, id_E111, id_E121}; - char buf[40]; - // uphill pips - for (auto const uphill : ctx->getPipsUphill(dstWire)) { - WireId srcWire = ctx->getPipSrcWire(uphill); - if (find(one_hop.begin(), one_hop.end(), ctx->wire_info(ctx->getPipSrcWire(uphill)).type) != - one_hop.end()) { - // found one hop pip - if (used_wires.count(srcWire)) { - if (used_wires[srcWire] != clock) { - continue; - } +// non clock port +// returns GB pip +IdString GowinGlobalRouter::route_to_non_clock_port(Context *ctx, WireId const dstWire, int clock, + pool<IdString> &used_pips, pool<IdString> &undo_wires) +{ + static std::vector<IdString> one_hop = {id_S111, id_S121, id_N111, id_N121, id_W111, id_W121, id_E111, id_E121}; + char buf[40]; + // uphill pips + for (auto const uphill : ctx->getPipsUphill(dstWire)) { + WireId srcWire = ctx->getPipSrcWire(uphill); + if (find(one_hop.begin(), one_hop.end(), ctx->wire_info(ctx->getPipSrcWire(uphill)).type) != one_hop.end()) { + // found one hop pip + if (used_wires.count(srcWire)) { + if (used_wires[srcWire] != clock) { + continue; + } + } + WireInfo wi = ctx->wire_info(srcWire); + std::string wire_alias = srcWire.str(ctx).substr(srcWire.str(ctx).rfind("_") + 1); + snprintf(buf, sizeof(buf), "R%dC%d_GB%d0_%s", wi.y + 1, wi.x + 1, clock, wire_alias.c_str()); + IdString gb = ctx->id(buf); + auto up_pips = ctx->getPipsUphill(srcWire); + if (find(up_pips.begin(), up_pips.end(), gb) != up_pips.end()) { + if (!used_wires.count(srcWire)) { + used_wires.insert(std::make_pair(srcWire, clock)); + undo_wires.insert(srcWire); } - WireInfo wi = ctx->wire_info(srcWire); - std::string wire_alias = srcWire.str(ctx).substr(srcWire.str(ctx).rfind("_") + 1); - snprintf(buf, sizeof(buf), "R%dC%d_GB%d0_%s", wi.y + 1, wi.x + 1, clock, wire_alias.c_str()); - IdString gb = ctx->id(buf); - auto up_pips = ctx->getPipsUphill(srcWire); - if (find(up_pips.begin(), up_pips.end(), gb) != up_pips.end()) { - if (!used_wires.count(srcWire)) { - used_wires.insert(std::make_pair(srcWire, clock)); - undo_wires.insert(srcWire); - } - used_pips.insert(uphill); - if (ctx->verbose) { - log_info(" 1-hop Pip:%s\n", uphill.c_str(ctx)); - } - return gb; + used_pips.insert(uphill); + if (ctx->verbose) { + log_info(" 1-hop Pip:%s\n", uphill.c_str(ctx)); } + return gb; } } - return IdString(); } + return IdString(); +} - // route one net - void route_net(onet_t const &net, int clock) - { - // For failed routing undo - pool<IdString> used_pips; - pool<IdString> undo_wires; - - log_info(" Route net %s, use clock #%d.\n", net.name.c_str(ctx), clock); - for (auto const &user : ctx->net_info(net.name).users) { - // >>> port <- GB<clock>0 - WireId dstWire = ctx->getNetinfoSinkWire(&ctx->net_info(net.name), user, 0); - if (ctx->verbose) { - log_info(" Cell:%s, port:%s, wire:%s\n", user.cell->name.c_str(ctx), user.port.c_str(ctx), - dstWire.c_str(ctx)); - } - - char buf[30]; - PipId gb_pip_id; - if (user.port == id_CLK) { - WireInfo const wi = ctx->wire_info(dstWire); - snprintf(buf, sizeof(buf), "R%dC%d_GB%d0_%s", wi.y + 1, wi.x + 1, clock, - ctx->wire_info(dstWire).type.c_str(ctx)); - gb_pip_id = ctx->id(buf); - // sanity - NPNR_ASSERT(find(ctx->getPipsUphill(dstWire).begin(), ctx->getPipsUphill(dstWire).end(), gb_pip_id) != - ctx->getPipsUphill(dstWire).end()); - } else { - // Non clock port - gb_pip_id = route_to_non_clock_port(dstWire, clock, used_pips, undo_wires); - if (gb_pip_id == IdString()) { - if (ctx->verbose) { - log_info(" Can't find route to %s, net %s will be routed in a standard way.\n", - dstWire.c_str(ctx), net.name.c_str(ctx)); - } - for (IdString const undo : undo_wires) { - used_wires.erase(undo); - } - return; - } - } - if (ctx->verbose) { - log_info(" GB Pip:%s\n", gb_pip_id.c_str(ctx)); - } +// route one net +void GowinGlobalRouter::route_net(Context *ctx, globalnet_t const &net) +{ + // For failed routing undo + pool<IdString> used_pips; + pool<IdString> undo_wires; - if (used_pips.count(gb_pip_id)) { - if (ctx->verbose) { - log_info(" ^routed already^\n"); - } - continue; - } - used_pips.insert(gb_pip_id); + log_info(" Route net %s, use clock #%d.\n", net.name.c_str(ctx), net.clock); + for (auto const &user : ctx->net_info(net.name).users) { + // >>> port <- GB<clock>0 + WireId dstWire = ctx->getNetinfoSinkWire(&ctx->net_info(net.name), user, 0); + if (ctx->verbose) { + log_info(" Cell:%s, port:%s, wire:%s\n", user.cell->name.c_str(ctx), user.port.c_str(ctx), + dstWire.c_str(ctx)); + } - // >>> GBOx <- GTx0 - dstWire = ctx->getPipSrcWire(gb_pip_id); - WireInfo dstWireInfo = ctx->wire_info(dstWire); - int branch_tap_idx = clock > 3 ? 1 : 0; - snprintf(buf, sizeof(buf), "R%dC%d_GT%d0_GBO%d", dstWireInfo.y + 1, dstWireInfo.x + 1, branch_tap_idx, - branch_tap_idx); - PipId gt_pip_id = ctx->id(buf); - if (ctx->verbose) { - log_info(" GT Pip:%s\n", buf); - } + char buf[30]; + PipId gb_pip_id; + if (user.port == id_CLK) { + WireInfo const wi = ctx->wire_info(dstWire); + snprintf(buf, sizeof(buf), "R%dC%d_GB%d0_%s", wi.y + 1, wi.x + 1, net.clock, + ctx->wire_info(dstWire).type.c_str(ctx)); + gb_pip_id = ctx->id(buf); // sanity - NPNR_ASSERT(find(ctx->getPipsUphill(dstWire).begin(), ctx->getPipsUphill(dstWire).end(), gt_pip_id) != + NPNR_ASSERT(find(ctx->getPipsUphill(dstWire).begin(), ctx->getPipsUphill(dstWire).end(), gb_pip_id) != ctx->getPipsUphill(dstWire).end()); - // if already routed - if (used_pips.count(gt_pip_id)) { + } else { + // Non clock port + gb_pip_id = route_to_non_clock_port(ctx, dstWire, net.clock, used_pips, undo_wires); + if (gb_pip_id == IdString()) { if (ctx->verbose) { - log_info(" ^routed already^\n"); + log_info(" Can't find route to %s, net %s will be routed in a standard way.\n", dstWire.c_str(ctx), + net.name.c_str(ctx)); } - continue; - } - used_pips.insert(gt_pip_id); - - // >>> GTx0 <- SPINExx - // XXX no optimization here, we need to store - // the SPINE <-> clock# correspondence in the database. In the - // meantime, we define in run-time in a completely suboptimal way. - std::vector<std::string> clock_spine; - dstWire = ctx->getPipSrcWire(gt_pip_id); - for (auto const uphill_pip : ctx->getPipsUphill(dstWire)) { - std::string name = ctx->wire_info(ctx->getPipSrcWire(uphill_pip)).type.str(ctx); - if (name.rfind("SPINE", 0) == 0) { - clock_spine.push_back(name); + for (IdString const undo : undo_wires) { + used_wires.erase(undo); } + return; } - sort(clock_spine.begin(), clock_spine.end(), [](const std::string &a, const std::string &b) -> bool { - return (a.size() < b.size()) || (a.size() == b.size() && a < b); - }); - dstWireInfo = ctx->wire_info(dstWire); - snprintf(buf, sizeof(buf), "R%dC%d_%s_GT%d0", dstWireInfo.y + 1, dstWireInfo.x + 1, - clock_spine[clock - branch_tap_idx * 4].c_str(), branch_tap_idx); - PipId spine_pip_id = ctx->id(buf); + } + if (ctx->verbose) { + log_info(" GB Pip:%s\n", gb_pip_id.c_str(ctx)); + } + + if (used_pips.count(gb_pip_id)) { if (ctx->verbose) { - log_info(" Spine Pip:%s\n", buf); + log_info(" ^routed already^\n"); } - // sanity - NPNR_ASSERT(find(ctx->getPipsUphill(dstWire).begin(), ctx->getPipsUphill(dstWire).end(), spine_pip_id) != - ctx->getPipsUphill(dstWire).end()); - // if already routed - if (used_pips.count(spine_pip_id)) { - if (ctx->verbose) { - log_info(" ^routed already^\n"); - } - continue; + continue; + } + used_pips.insert(gb_pip_id); + + // >>> GBOx <- GTx0 + dstWire = ctx->getPipSrcWire(gb_pip_id); + WireInfo dstWireInfo = ctx->wire_info(dstWire); + int branch_tap_idx = net.clock > 3 ? 1 : 0; + snprintf(buf, sizeof(buf), "R%dC%d_GT%d0_GBO%d", dstWireInfo.y + 1, dstWireInfo.x + 1, branch_tap_idx, + branch_tap_idx); + PipId gt_pip_id = ctx->id(buf); + if (ctx->verbose) { + log_info(" GT Pip:%s\n", buf); + } + // sanity + NPNR_ASSERT(find(ctx->getPipsUphill(dstWire).begin(), ctx->getPipsUphill(dstWire).end(), gt_pip_id) != + ctx->getPipsUphill(dstWire).end()); + // if already routed + if (used_pips.count(gt_pip_id)) { + if (ctx->verbose) { + log_info(" ^routed already^\n"); } - used_pips.insert(spine_pip_id); + continue; + } + used_pips.insert(gt_pip_id); - // >>> SPINExx <- IO - dstWire = ctx->getPipSrcWire(spine_pip_id); - dstWireInfo = ctx->wire_info(dstWire); - PipId io_pip_id = PipId(); - for (auto const uphill_pip : ctx->getPipsUphill(dstWire)) { - if (ctx->getPipSrcWire(uphill_pip) == net.clock_io_wire) { - io_pip_id = uphill_pip; - } + // >>> GTx0 <- SPINExx + // XXX no optimization here, we need to store + // the SPINE <-> clock# correspondence in the database. In the + // meantime, we define in run-time in a completely suboptimal way. + std::vector<std::string> clock_spine; + dstWire = ctx->getPipSrcWire(gt_pip_id); + for (auto const uphill_pip : ctx->getPipsUphill(dstWire)) { + std::string name = ctx->wire_info(ctx->getPipSrcWire(uphill_pip)).type.str(ctx); + if (name.rfind("SPINE", 0) == 0) { + clock_spine.push_back(name); } - NPNR_ASSERT(io_pip_id != PipId()); + } + sort(clock_spine.begin(), clock_spine.end(), [](const std::string &a, const std::string &b) -> bool { + return (a.size() < b.size()) || (a.size() == b.size() && a < b); + }); + dstWireInfo = ctx->wire_info(dstWire); + snprintf(buf, sizeof(buf), "R%dC%d_%s_GT%d0", dstWireInfo.y + 1, dstWireInfo.x + 1, + clock_spine[net.clock - branch_tap_idx * 4].c_str(), branch_tap_idx); + PipId spine_pip_id = ctx->id(buf); + if (ctx->verbose) { + log_info(" Spine Pip:%s\n", buf); + } + // sanity + NPNR_ASSERT(find(ctx->getPipsUphill(dstWire).begin(), ctx->getPipsUphill(dstWire).end(), spine_pip_id) != + ctx->getPipsUphill(dstWire).end()); + // if already routed + if (used_pips.count(spine_pip_id)) { if (ctx->verbose) { - log_info(" IO Pip:%s\n", io_pip_id.c_str(ctx)); + log_info(" ^routed already^\n"); } - // if already routed - if (used_pips.count(io_pip_id)) { - if (ctx->verbose) { - log_info(" ^routed already^\n"); - } - continue; + continue; + } + used_pips.insert(spine_pip_id); + + // >>> SPINExx <- IO + dstWire = ctx->getPipSrcWire(spine_pip_id); + dstWireInfo = ctx->wire_info(dstWire); + PipId io_pip_id = PipId(); + for (auto const uphill_pip : ctx->getPipsUphill(dstWire)) { + if (ctx->getPipSrcWire(uphill_pip) == net.clock_io_wire) { + io_pip_id = uphill_pip; } - used_pips.insert(io_pip_id); } - log_info(" Net %s is routed.\n", net.name.c_str(ctx)); - for (auto const pip : used_pips) { - ctx->bindPip(pip, &ctx->net_info(net.name), STRENGTH_LOCKED); + NPNR_ASSERT(io_pip_id != PipId()); + if (ctx->verbose) { + log_info(" IO Pip:%s\n", io_pip_id.c_str(ctx)); + } + // if already routed + if (used_pips.count(io_pip_id)) { + if (ctx->verbose) { + log_info(" ^routed already^\n"); + } + continue; } - ctx->bindWire(net.clock_io_wire, &ctx->net_info(net.name), STRENGTH_LOCKED); + used_pips.insert(io_pip_id); } + log_info(" Net %s is routed.\n", net.name.c_str(ctx)); + for (auto const pip : used_pips) { + ctx->bindPip(pip, &ctx->net_info(net.name), STRENGTH_LOCKED); + } + ctx->bindWire(net.clock_io_wire, &ctx->net_info(net.name), STRENGTH_LOCKED); +} - public: - Context *ctx; - void route_globals() - { - log_info("Routing globals...\n"); +void GowinGlobalRouter::route_globals(Context *ctx) +{ + log_info("Routing globals...\n"); - std::vector<onet_t> clock_nets; - gather_clock_nets(clock_nets); - // XXX we need to use the list of indexes of clocks from the database - // use 6 clocks (XXX 3 for GW1NZ-1) - int max_clock = 3, cur_clock = -1; - for (auto const &net : clock_nets) { - // XXX only IO clock for now - if (net.clock_io_wire == WireId()) { - log_info(" Non IO clock, skip %s.\n", net.name.c_str(ctx)); - continue; - } - if (++cur_clock >= max_clock) { - log_info(" No more clock wires left, skip the remaining nets.\n"); - break; - } - route_net(net, cur_clock); - } + for (auto const &net : nets) { + route_net(ctx, net); } -}; +} -void route_gowin_globals(Context *ctx) +// Allocate networks that will be routed through the global system. +// Mark their driver cells as global buffers to exclude them from the analysis. +void GowinGlobalRouter::mark_globals(Context *ctx) { - GowinGlobalRouter router(ctx); - router.route_globals(); + log_info("Find global nets...\n"); + + std::vector<globalnet_t> clock_nets; + gather_clock_nets(ctx, clock_nets); + // XXX we need to use the list of indexes of clocks from the database + // use 6 clocks (XXX 3 for GW1NZ-1) + int max_clock = 3, cur_clock = -1; + for (auto &net : clock_nets) { + // XXX only IO clock for now + if (net.clock_io_wire == WireId()) { + log_info(" Non IO clock, skip %s.\n", net.name.c_str(ctx)); + continue; + } + if (++cur_clock >= max_clock) { + log_info(" No more clock wires left, skip the remaining nets.\n"); + break; + } + net.clock = cur_clock; + BelInfo &bi = ctx->bel_info(net.clock_io_bel); + bi.gb = true; + nets.emplace_back(net); + } } NEXTPNR_NAMESPACE_END diff --git a/gowin/globals.h b/gowin/globals.h index 41a8727a..69232d7c 100644 --- a/gowin/globals.h +++ b/gowin/globals.h @@ -18,10 +18,74 @@ * */ +#ifndef GOWIN_GLOBALS_H +#define GOWIN_GLOBALS_H + #include "nextpnr.h" NEXTPNR_NAMESPACE_BEGIN -void route_gowin_globals(Context *ctx); +class GowinGlobalRouter +{ + public: + GowinGlobalRouter() {} + + private: + // wire -> clock# + dict<WireId, int> used_wires; + + // ordered nets + struct globalnet_t + { + IdString name; + int clock_ports; + BelId clock_io_bel; + WireId clock_io_wire; // IO wire if there is one + int clock; // clock # + + globalnet_t() + { + name = IdString(); + clock_ports = 0; + clock_io_bel = BelId(); + clock_io_wire = WireId(); + clock = -1; + } + globalnet_t(IdString _name) + { + name = _name; + clock_ports = 0; + clock_io_bel = BelId(); + clock_io_wire = WireId(); + clock = -1; + } + + // sort + bool operator<(const globalnet_t &other) const + { + if ((clock_io_wire != WireId()) ^ (other.clock_io_wire != WireId())) { + return !(clock_io_wire != WireId()); + } + return clock_ports < other.clock_ports; + } + // search + bool operator==(const globalnet_t &other) const { return name == other.name; } + }; + + // discovered nets + std::vector<globalnet_t> nets; + + bool is_clock_port(PortRef const &user); + std::pair<WireId, BelId> clock_io(Context *ctx, PortRef const &driver); + void gather_clock_nets(Context *ctx, std::vector<globalnet_t> &clock_nets); + IdString route_to_non_clock_port(Context *ctx, WireId const dstWire, int clock, pool<IdString> &used_pips, + pool<IdString> &undo_wires); + void route_net(Context *ctx, globalnet_t const &net); + + public: + void mark_globals(Context *ctx); + void route_globals(Context *ctx); +}; NEXTPNR_NAMESPACE_END +#endif // GOWIN_GLOBALS_H diff --git a/gowin/pack.cc b/gowin/pack.cc index 83820142..5b304f10 100644 --- a/gowin/pack.cc +++ b/gowin/pack.cc @@ -26,6 +26,8 @@ #include "log.h" #include "util.h" +#include "globals.h" + NEXTPNR_NAMESPACE_BEGIN static void make_dummy_alu(Context *ctx, int alu_idx, CellInfo *ci, CellInfo *packed_head, @@ -1009,6 +1011,7 @@ bool Arch::pack() Context *ctx = getCtx(); try { log_break(); + pre_pack(ctx); pack_constants(ctx); pack_gsr(ctx); pack_io(ctx); @@ -1018,6 +1021,7 @@ bool Arch::pack() pack_alus(ctx); pack_lut_lutffs(ctx); pack_nonlut_ffs(ctx); + post_pack(ctx); ctx->settings[id_pack] = 1; ctx->assignArchInfo(); log_info("Checksum: 0x%08x\n", ctx->checksum()); |