From 590b9050ff5cc618b4df50e0877b4bf6d9e7949d Mon Sep 17 00:00:00 2001 From: YRabbit Date: Thu, 23 Jun 2022 11:42:58 +1000 Subject: gowin: add a separate router for the clocks A simple router that takes advantage of the fact that in each cell with DFFs their CLK inputs can directly connect to the global clock network. Networks with a large number of such sinks are sought and then each network is assigned to the available independent global clock networks. There are limited possibilities for routing mixed networks, that is, when the sinks are not only CLKs: in this case an attempt is made to use wires such as SN10/20 and EW10/20, that is, one short transition can be added between the global clock network and the sink. * At this time, networks with a source other than the I/O pin are not supported. This is typical for Tangnano4k and runber boards. * Router is disabled by default, you need to specify option --enable-globals to activate * No new chip bases are required. This may change in the distant future. Signed-off-by: YRabbit --- gowin/arch.cc | 15 +++ gowin/arch.h | 1 + gowin/globals.cc | 335 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ gowin/globals.h | 26 +++++ gowin/main.cc | 16 ++- 5 files changed, 392 insertions(+), 1 deletion(-) create mode 100644 gowin/globals.cc create mode 100644 gowin/globals.h diff --git a/gowin/arch.cc b/gowin/arch.cc index 82f5018b..7b9097c9 100644 --- a/gowin/arch.cc +++ b/gowin/arch.cc @@ -25,6 +25,7 @@ #include #include "embed.h" #include "gfx.h" +#include "globals.h" #include "nextpnr.h" #include "placer1.h" #include "placer_heap.h" @@ -341,6 +342,14 @@ BelInfo &Arch::bel_info(IdString bel) return b->second; } +NetInfo &Arch::net_info(IdString net) +{ + auto b = nets.find(net); + if (b == nets.end()) + NPNR_ASSERT_FALSE_STR("no net named " + net.str(this)); + return *b->second; +} + void Arch::addWire(IdString name, IdString type, int x, int y) { NPNR_ASSERT(wires.count(name) == 0); @@ -657,6 +666,7 @@ bool aliasCompare(GlobalAliasPOD i, GlobalAliasPOD j) return (i.dest_row < j.dest_row) || (i.dest_row == j.dest_row && i.dest_col < j.dest_col) || (i.dest_row == j.dest_row && i.dest_col == j.dest_col && i.dest_id < j.dest_id); } + bool timingCompare(TimingPOD i, TimingPOD j) { return i.name_id < j.name_id; } template const T *genericLookup(const T *first, int len, const T val, C compare) @@ -1865,6 +1875,11 @@ bool Arch::place() bool Arch::route() { std::string router = str_or_default(settings, id_router, defaultRouter); + + if (bool_or_default(settings, id("arch.enable-globals"))) { + route_gowin_globals(getCtx()); + } + bool result; if (router == "router1") { result = router1(getCtx(), Router1Cfg(getCtx())); diff --git a/gowin/arch.h b/gowin/arch.h index 8bbbd514..c59f8eb3 100644 --- a/gowin/arch.h +++ b/gowin/arch.h @@ -289,6 +289,7 @@ struct Arch : BaseArch WireInfo &wire_info(IdString wire); PipInfo &pip_info(IdString pip); BelInfo &bel_info(IdString bel); + NetInfo &net_info(IdString net); std::vector bel_ids, wire_ids, pip_ids; diff --git a/gowin/globals.cc b/gowin/globals.cc new file mode 100644 index 00000000..5010cf79 --- /dev/null +++ b/gowin/globals.cc @@ -0,0 +1,335 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 gatecat + * + * 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 "globals.h" +#include +#include +#include +#include +#include "cells.h" +#include "log.h" +#include "nextpnr.h" +#include "place_common.h" +#include "util.h" + +NEXTPNR_NAMESPACE_BEGIN + +class GowinGlobalRouter +{ + public: + GowinGlobalRouter(Context *ctx) : ctx(ctx){}; + + private: + // wire -> clock# + dict 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; } + }; + + bool is_clock_port(PortRef const &user) + { + if (user.cell->type == id_SLICE && user.port == id_CLK) { + return true; + } + return false; + } + + 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->bels.at(driver.cell->bel); + if (bel.type != id_IOB) { + return WireId(); + } + 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(); + } + + // gather the clock nets + void gather_clock_nets(std::vector &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); + } + } + } + // 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)); + } + } + } + + // non clock port + // returns GB pip + IdString route_to_non_clock_port(WireId const dstWire, int clock, pool &used_pips, + pool &undo_wires) + { + static std::vector 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); + } + used_pips.insert(uphill); + if (ctx->verbose) { + log_info(" 1-hop Pip:%s\n", uphill.c_str(ctx)); + } + return gb; + } + } + } + return IdString(); + } + + // route one net + void route_net(onet_t const &net, int clock) + { + // For failed routing undo + pool used_pips; + pool 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 <- GB0 + 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)); + } + + if (used_pips.count(gb_pip_id)) { + if (ctx->verbose) { + log_info(" ^routed already^\n"); + } + 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 = 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"); + } + 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 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); + } + } + 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(" 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(" ^routed already^\n"); + } + 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; + } + } + 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; + } + 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"); + + std::vector 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); + } + } +}; + +void route_gowin_globals(Context *ctx) +{ + GowinGlobalRouter router(ctx); + router.route_globals(); +} + +NEXTPNR_NAMESPACE_END diff --git a/gowin/globals.h b/gowin/globals.h new file mode 100644 index 00000000..4731447c --- /dev/null +++ b/gowin/globals.h @@ -0,0 +1,26 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 gatecat + * + * 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" + +NEXTPNR_NAMESPACE_BEGIN + +void route_gowin_globals(Context *ctx); + +NEXTPNR_NAMESPACE_END diff --git a/gowin/main.cc b/gowin/main.cc index a45a49d4..36bd8656 100644 --- a/gowin/main.cc +++ b/gowin/main.cc @@ -51,6 +51,8 @@ po::options_description GowinCommandHandler::getArchOptions() specific.add_options()("device", po::value(), "device name"); specific.add_options()("family", po::value(), "family name"); specific.add_options()("cst", po::value(), "physical constraints file"); + specific.add_options()("enable-globals", "separate routing of the clocks"); + specific.add_options()("enable-auto-longwires", "automatic detection and routing of long wires"); return specific; } @@ -79,7 +81,19 @@ std::unique_ptr GowinCommandHandler::createContext(dict(new Context(chipArgs)); + + auto ctx = std::unique_ptr(new Context(chipArgs)); + // routing options + // the default values will change in the future + ctx->settings[ctx->id("arch.enable-globals")] = 0; + ctx->settings[ctx->id("arch.enable-auto-longwires")] = 0; + if (vm.count("enable-globals")) { + ctx->settings[ctx->id("arch.enable-globals")] = 1; + } + if (vm.count("enable-auto-longwires")) { + ctx->settings[ctx->id("arch.enable-auto-longwires")] = 1; + } + return ctx; } void GowinCommandHandler::customAfterLoad(Context *ctx) -- cgit v1.2.3 From 63f2acd42adf3884d153381696cf7845c6d74162 Mon Sep 17 00:00:00 2001 From: YRabbit Date: Fri, 24 Jun 2022 08:20:06 +1000 Subject: gowin: process the CLK ports of the ODDR[C] primitives Also removed the useless references. Signed-off-by: YRabbit --- gowin/globals.cc | 15 ++++++++------- gowin/globals.h | 1 + 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/gowin/globals.cc b/gowin/globals.cc index 5010cf79..ed75a938 100644 --- a/gowin/globals.cc +++ b/gowin/globals.cc @@ -2,6 +2,7 @@ * nextpnr -- Next Generation Place and Route * * Copyright (C) 2018 gatecat + * Copyright (C) 2022 YRabbit * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -63,7 +64,7 @@ class GowinGlobalRouter bool is_clock_port(PortRef const &user) { - if (user.cell->type == id_SLICE && user.port == id_CLK) { + if ((user.cell->type == id_SLICE || user.cell->type == id_ODDR || user.cell->type == id_ODDRC) && user.port == id_CLK) { return true; } return false; @@ -78,12 +79,12 @@ class GowinGlobalRouter return WireId(); } // clock IOs have pips output->SPINExx - BelInfo &bel = ctx->bels.at(driver.cell->bel); + BelInfo &bel = ctx->bel_info(driver.cell->bel); if (bel.type != id_IOB) { return WireId(); } WireId wire = bel.pins[id_O].wire; - for (auto const &pip : ctx->getPipsDownhill(wire)) { + for (auto const pip : ctx->getPipsDownhill(wire)) { if (ctx->wire_info(ctx->getPipDstWire(pip)).type.str(ctx).rfind("SPINE", 0) == 0) { return wire; } @@ -132,7 +133,7 @@ class GowinGlobalRouter static std::vector 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)) { + 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()) { @@ -197,7 +198,7 @@ class GowinGlobalRouter 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) { + for (IdString const undo : undo_wires) { used_wires.erase(undo); } return; @@ -243,7 +244,7 @@ class GowinGlobalRouter // meantime, we define in run-time in a completely suboptimal way. std::vector clock_spine; dstWire = ctx->getPipSrcWire(gt_pip_id); - for (auto const &uphill_pip : ctx->getPipsUphill(dstWire)) { + 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); @@ -275,7 +276,7 @@ class GowinGlobalRouter dstWire = ctx->getPipSrcWire(spine_pip_id); dstWireInfo = ctx->wire_info(dstWire); PipId io_pip_id = PipId(); - for (auto const &uphill_pip : ctx->getPipsUphill(dstWire)) { + for (auto const uphill_pip : ctx->getPipsUphill(dstWire)) { if (ctx->getPipSrcWire(uphill_pip) == net.clock_io_wire) { io_pip_id = uphill_pip; } diff --git a/gowin/globals.h b/gowin/globals.h index 4731447c..41a8727a 100644 --- a/gowin/globals.h +++ b/gowin/globals.h @@ -2,6 +2,7 @@ * nextpnr -- Next Generation Place and Route * * Copyright (C) 2018 gatecat + * Copyright (C) 2022 YRabbit * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above -- cgit v1.2.3 From 3172a38daeb4588d3aaa686816c09d64ccf08587 Mon Sep 17 00:00:00 2001 From: YRabbit Date: Mon, 4 Jul 2022 10:32:39 +1000 Subject: gowin: Let the placer know about global networks Refactor in order to detect networks that will be routed in a special mode earlier. This makes it possible to mark the source of such networks as a global buffer, thereby removing their influence on element placement. In addition, timing classes are set for some cells. Signed-off-by: YRabbit --- gowin/arch.cc | 56 ++++++- gowin/arch.h | 14 ++ gowin/globals.cc | 486 ++++++++++++++++++++++++++----------------------------- gowin/globals.h | 66 +++++++- 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 #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 struct RelPtr @@ -337,6 +339,11 @@ struct Arch : BaseArch 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 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 #include #include @@ -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 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 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 &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 &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 &used_pips, - pool &undo_wires) - { - static std::vector 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 &used_pips, pool &undo_wires) +{ + static std::vector 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 used_pips; - pool 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 <- GB0 - 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 used_pips; + pool 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 <- GB0 + 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 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 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 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 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 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 nets; + + bool is_clock_port(PortRef const &user); + std::pair clock_io(Context *ctx, PortRef const &driver); + void gather_clock_nets(Context *ctx, std::vector &clock_nets); + IdString route_to_non_clock_port(Context *ctx, WireId const dstWire, int clock, pool &used_pips, + pool &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()); -- cgit v1.2.3 From 5d915a550ba8a825fa29a2cab3851adea20f40fb Mon Sep 17 00:00:00 2001 From: YRabbit Date: Mon, 4 Jul 2022 11:23:21 +1000 Subject: gowin: fix compilation Signed-off-by: YRabbit --- gowin/arch.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/gowin/arch.cc b/gowin/arch.cc index a968c112..cdb932a6 100644 --- a/gowin/arch.cc +++ b/gowin/arch.cc @@ -2045,6 +2045,7 @@ void Arch::assignArchInfo() addCellTimingDelay(cname, id_I0, id_OF, delay); addCellTimingDelay(cname, id_I1, id_OF, delay); addCellTimingCombIn(cname, id_SEL); + break; } case ID_IOB: /* FALLTHRU */ -- cgit v1.2.3 From 1ebfe67daf2ec3e1e64150f09ab4c194f41d1d9d Mon Sep 17 00:00:00 2001 From: YRabbit Date: Tue, 5 Jul 2022 20:02:12 +1000 Subject: gowin: Remove unnecessary functions Signed-off-by: YRabbit --- gowin/arch.cc | 36 ++++++++---------------------------- gowin/arch.h | 6 +----- 2 files changed, 9 insertions(+), 33 deletions(-) diff --git a/gowin/arch.cc b/gowin/arch.cc index 82b44f91..6fe40f76 100644 --- a/gowin/arch.cc +++ b/gowin/arch.cc @@ -553,27 +553,7 @@ 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::addCellTimingClass(IdString cell, IdString port, TimingPortClass cls) {cellTiming[cell].portClasses[port] = cls;} void Arch::addCellTimingClock(IdString cell, IdString port) { cellTiming[cell].portClasses[port] = TMG_CLOCK_INPUT; } @@ -2042,8 +2022,8 @@ void Arch::assignArchInfo() // add timing paths addCellTimingClock(cname, id_CLK); - addCellTimingRegIn(cname, id_CE); - addCellTimingRegIn(cname, id_LSR); + addCellTimingClass(cname, id_CE, TMG_REGISTER_INPUT); + addCellTimingClass(cname, id_LSR, TMG_REGISTER_INPUT); IdString ports[4] = {id_A, id_B, id_C, id_D}; for (int i = 0; i < 4; i++) { DelayPair setup = @@ -2074,18 +2054,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); + addCellTimingClass(cname, id_SEL, TMG_COMB_INPUT); break; } case ID_IOB: /* FALLTHRU */ case ID_IOBS: - addCellTimingIO(cname, id_I); - addCellTimingIO(cname, id_O); + addCellTimingClass(cname, id_I, TMG_ENDPOINT); + addCellTimingClass(cname, id_O, TMG_STARTPOINT); break; case ID_BUFS: - addCellTimingCombIn(cname, id_I); - addCellTimingCombOut(cname, id_O); + addCellTimingClass(cname, id_I, TMG_ENDPOINT); + addCellTimingClass(cname, id_O, TMG_STARTPOINT); break; default: break; diff --git a/gowin/arch.h b/gowin/arch.h index d02a2488..034e4b86 100644 --- a/gowin/arch.h +++ b/gowin/arch.h @@ -339,11 +339,7 @@ struct Arch : BaseArch 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 addCellTimingClass(IdString cell, IdString port, TimingPortClass cls); 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); -- cgit v1.2.3