From 30f122854a6dd00f81fc15e7c9384644dd90385b Mon Sep 17 00:00:00 2001 From: David Shah Date: Thu, 26 Jul 2018 13:05:15 +0200 Subject: [PATCH 01/19] ecp5: Helper function and arch tweaks for global router Signed-off-by: David Shah --- ecp5/arch.cc | 6 ++++++ ecp5/arch.h | 23 ++++++++++++++++++++++ ecp5/globals.cc | 52 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 81 insertions(+) create mode 100644 ecp5/globals.cc diff --git a/ecp5/arch.cc b/ecp5/arch.cc index 82ebfba1..6b701a32 100644 --- a/ecp5/arch.cc +++ b/ecp5/arch.cc @@ -579,4 +579,10 @@ std::vector> Arch::getTilesAtLocation(int ro return ret; } +GlobalInfoPOD Arch::globalInfoAtLoc(Location loc) +{ + int locidx = loc.y * chip_info->width + loc.x; + return chip_info->location_glbinfo[locidx]; +} + NEXTPNR_NAMESPACE_END diff --git a/ecp5/arch.h b/ecp5/arch.h index da86d4e2..1908f122 100644 --- a/ecp5/arch.h +++ b/ecp5/arch.h @@ -640,6 +640,21 @@ struct Arch : BaseCtx return range; } + IdString getWireBasename(WireId wire) const { return id(locInfo(wire)->wire_data[wire.index].name.get()); } + + WireId getWireByLocAndBasename(Location loc, std::string basename) const + { + WireId wireId; + wireId.location = loc; + for (int i = 0; i < locInfo(wireId)->num_wires; i++) { + if (locInfo(wireId)->wire_data[i].name.get() == basename) { + wireId.index = i; + return wireId; + } + } + return WireId(); + } + // ------------------------------------------------- PipId getPipByName(IdString name) const; @@ -891,6 +906,14 @@ struct Arch : BaseCtx } NPNR_ASSERT_FALSE_STR("no tile at (" + std::to_string(col) + ", " + std::to_string(row) + ") with type in set"); } + + GlobalInfoPOD globalInfoAtLoc(Location loc); + + IdString id_trellis_slice; + IdString id_clk, id_lsr; + IdString id_clkmux, id_lsrmux; + IdString id_srmode, id_mode; + }; NEXTPNR_NAMESPACE_END diff --git a/ecp5/globals.cc b/ecp5/globals.cc new file mode 100644 index 00000000..d435ca93 --- /dev/null +++ b/ecp5/globals.cc @@ -0,0 +1,52 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 David Shah + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "nextpnr.h" + +NEXTPNR_NAMESPACE_BEGIN + +class Ecp5GlobalRouter +{ + public: + Ecp5GlobalRouter(Context *ctx) : ctx(ctx){}; + + PipId find_tap_pip(WireId tile_glb) + { + std::string wireName = ctx->getWireBasename(tile_glb).str(ctx); + std::string glbName = wireName.substr(2); + TapDirection td = ctx->globalInfoAtLoc(tile_glb.location).tap_dir; + WireId tap_wire; + Location tap_loc; + tap_loc.x = ctx->globalInfoAtLoc(tile_glb.location).tap_col; + tap_loc.y = tile_glb.location.y; + if (td == TAP_DIR_LEFT) { + tap_wire = ctx->getWireByLocAndBasename(tap_loc, "L_" + glbName); + } else { + tap_wire = ctx->getWireByLocAndBasename(tap_loc, "R_" + glbName); + } + return *(ctx->getPipsUphill(tap_wire).begin()); + } + + private: + Context *ctx; +}; + +void route_ecp5_globals(Context *ctx); + +NEXTPNR_NAMESPACE_END From 7d48acff52cfa31cf7873d7462a3a4e6395ee520 Mon Sep 17 00:00:00 2001 From: David Shah Date: Sun, 29 Jul 2018 10:07:58 +0200 Subject: [PATCH 02/19] ecp5: Clock usage counter function Signed-off-by: David Shah --- ecp5/globals.cc | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/ecp5/globals.cc b/ecp5/globals.cc index d435ca93..770eb7e2 100644 --- a/ecp5/globals.cc +++ b/ecp5/globals.cc @@ -18,6 +18,7 @@ */ #include "nextpnr.h" +#include NEXTPNR_NAMESPACE_BEGIN @@ -25,6 +26,39 @@ class Ecp5GlobalRouter { public: Ecp5GlobalRouter(Context *ctx) : ctx(ctx){}; + private: + + bool is_clock_port(const PortRef &user) { + if (user.cell->type == ctx->id("TRELLIS_LC") && user.port == ctx->id("CLK")) + return true; + return false; + } + + std::vector get_clocks() + { + std::unordered_map clockCount; + for (auto &net : ctx->nets) { + NetInfo *ni = net.second.get(); + clockCount[ni->name] = 0; + for (const auto &user : ni->users) { + if (is_clock_port(user)) + clockCount[ni->name]++; + } + } + std::vector clocks; + while (clocks.size() < 16) { + auto max = std::max_element(clockCount.begin(), clockCount.end(), []( + const decltype(clockCount)::value_type &a, const decltype(clockCount)::value_type &b + ) { + return a.second < b.second; + }); + if (max == clockCount.end() || max->second < 3) + break; + clocks.push_back(ctx->nets.at(max->first).get()); + clockCount.erase(max->first); + } + return clocks; + } PipId find_tap_pip(WireId tile_glb) { @@ -43,7 +77,6 @@ class Ecp5GlobalRouter return *(ctx->getPipsUphill(tap_wire).begin()); } - private: Context *ctx; }; From d43138b022c84efc01895f51f15244d8a42a5156 Mon Sep 17 00:00:00 2001 From: David Shah Date: Sun, 29 Jul 2018 10:35:05 +0200 Subject: [PATCH 03/19] ecp5: Global routing algorithm up to TAPs Signed-off-by: David Shah --- ecp5/globals.cc | 85 +++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 75 insertions(+), 10 deletions(-) diff --git a/ecp5/globals.cc b/ecp5/globals.cc index 770eb7e2..9d51316c 100644 --- a/ecp5/globals.cc +++ b/ecp5/globals.cc @@ -17,8 +17,14 @@ * */ -#include "nextpnr.h" #include +#include +#include +#include "nextpnr.h" + +#include "log.h" + +#define fmt_str(x) (static_cast(std::ostringstream() << x).str()) NEXTPNR_NAMESPACE_BEGIN @@ -26,15 +32,16 @@ class Ecp5GlobalRouter { public: Ecp5GlobalRouter(Context *ctx) : ctx(ctx){}; - private: - bool is_clock_port(const PortRef &user) { + private: + bool is_clock_port(const PortRef &user) + { if (user.cell->type == ctx->id("TRELLIS_LC") && user.port == ctx->id("CLK")) return true; return false; } - std::vector get_clocks() + std::vector get_clocks() { std::unordered_map clockCount; for (auto &net : ctx->nets) { @@ -45,13 +52,11 @@ class Ecp5GlobalRouter clockCount[ni->name]++; } } - std::vector clocks; + std::vector clocks; while (clocks.size() < 16) { - auto max = std::max_element(clockCount.begin(), clockCount.end(), []( - const decltype(clockCount)::value_type &a, const decltype(clockCount)::value_type &b - ) { - return a.second < b.second; - }); + auto max = std::max_element(clockCount.begin(), clockCount.end(), + [](const decltype(clockCount)::value_type &a, + const decltype(clockCount)::value_type &b) { return a.second < b.second; }); if (max == clockCount.end() || max->second < 3) break; clocks.push_back(ctx->nets.at(max->first).get()); @@ -77,6 +82,66 @@ class Ecp5GlobalRouter return *(ctx->getPipsUphill(tap_wire).begin()); } + void route_logic_tile_global(IdString net, int global_index, PortRef user) + { + WireId userWire = ctx->getBelPinWire(user.cell->bel, ctx->portPinFromId(user.port)); + WireId globalWire; + IdString global_name = ctx->id(fmt_str("G_HPBX" << std::setw(2) << std::setfill('0') << global_index << "00")); + std::queue upstream; + std::unordered_map backtrace; + upstream.push(userWire); + bool already_routed = false; + // Search back from the pin until we reach the global network + while (true) { + WireId next = upstream.front(); + upstream.pop(); + + if (ctx->getBoundWireNet(next) == net) { + already_routed = true; + globalWire = next; + break; + } + + if (ctx->getWireBasename(next) == global_name) { + globalWire = next; + break; + } + if (ctx->checkWireAvail(next)) { + for (auto pip : ctx->getPipsUphill(next)) { + WireId src = ctx->getPipSrcWire(pip); + backtrace[src] = pip; + upstream.push(src); + } + } + if (upstream.size() > 3000) { + log_error("failed to route HPBX%02d00 to %s.%s\n", global_index, + ctx->getBelName(user.cell->bel).c_str(ctx), user.port.c_str(ctx)); + } + } + // Set all the pips we found along the way + WireId cursor = userWire; + while (true) { + auto fnd = backtrace.find(cursor); + if (fnd == backtrace.end()) + break; + ctx->bindPip(fnd->second, net, STRENGTH_LOCKED); + cursor = ctx->getPipSrcWire(fnd->second); + } + // If the global network inside the tile isn't already set up, + // we also need to bind the buffers along the way + if (!already_routed) { + ctx->bindWire(cursor, net, STRENGTH_LOCKED); + PipId tap_pip = find_tap_pip(cursor); + IdString tap_net = ctx->getBoundPipNet(tap_pip); + if (tap_net == IdString()) { + ctx->bindPip(tap_pip, net, STRENGTH_LOCKED); + // TODO: SPINE + } else { + NPNR_ASSERT(tap_net == net); + } + } + } + Context *ctx; }; From bc10a5646d337ef1cf412aaf58eef51e16eb4474 Mon Sep 17 00:00:00 2001 From: David Shah Date: Tue, 31 Jul 2018 14:03:35 +0200 Subject: [PATCH 04/19] ecp5: Working on global router Signed-off-by: David Shah --- ecp5/globals.cc | 88 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/ecp5/globals.cc b/ecp5/globals.cc index 9d51316c..453429e6 100644 --- a/ecp5/globals.cc +++ b/ecp5/globals.cc @@ -28,6 +28,21 @@ NEXTPNR_NAMESPACE_BEGIN +static std::string get_quad_name(GlobalQuadrant quad) +{ + switch (quad) { + case QUAD_UL: + return "UL"; + case QUAD_UR: + return "UR"; + case QUAD_LL: + return "LL"; + case QUAD_LR: + return "LR"; + } + return ""; +} + class Ecp5GlobalRouter { public: @@ -142,6 +157,79 @@ class Ecp5GlobalRouter } } + bool is_global_io(CellInfo *io, std::string &glb_name) + { + std::string func_name = ctx->getPioFunctionName(io->bel); + if (func_name.substr(0, 5) == "PCLKT") { + func_name.erase(func_name.find('_'), 1); + glb_name = "G_" + func_name; + return true; + } + return false; + } + + WireId get_global_wire(GlobalQuadrant quad, int network) + { + return ctx->getWireByLocAndBasename(Location(0, 0), get_quad_name(quad) + "PCLK" + std::to_string(network)); + } + + void simple_router(IdString net, WireId src, WireId dst) + { + std::queue visit; + std::unordered_map backtrace; + visit.push(src); + WireId cursor; + while (true) { + if (visit.empty() || visit.size() > 50000) { + log_error("cannot route global from %s to %s.\n", ctx->getWireName(src).c_str(ctx), + ctx->getWireName(dst).c_str(ctx)); + } + cursor = visit.back(); + visit.pop(); + IdString bound = ctx->getBoundWireNet(cursor); + if (bound == net) { + break; + } else if (bound != IdString()) { + continue; + } + if (cursor == dst) + break; + for (auto dh : ctx->getPipsDownhill(cursor)) { + WireId pipDst = ctx->getPipDstWire(dh); + if (backtrace.count(pipDst)) + continue; + backtrace[pipDst] = dh; + visit.push(pipDst); + } + } + while (true) { + auto fnd = backtrace.find(cursor); + if (fnd == backtrace.end()) + break; + ctx->bindPip(fnd->second, net, STRENGTH_LOCKED); + cursor = ctx->getPipSrcWire(fnd->second); + } + } + + void route_onto_global(NetInfo *net, int network) + { + WireId glb_src; + if (net->driver.cell->type == ctx->id("TRELLIS_IO")) { + std::string ioglb; + if (!is_global_io(net->driver.cell, ioglb)) + goto non_dedicated; + glb_src = ctx->getWireByLocAndBasename(Location(0, 0), ioglb); + } + for (int quad = QUAD_UL; quad < QUAD_LR + 1; quad++) { + WireId glb_dst = get_global_wire(GlobalQuadrant(quad), network); + simple_router(net->name, glb_src, glb_dst); + } + if (false) { + non_dedicated: + log_error("FIXME: currenly global networks can only be driven by dedicated global input pins"); + } + } + Context *ctx; }; From 97b12fa741a48efed1014aecf8283eabb2919d20 Mon Sep 17 00:00:00 2001 From: David Shah Date: Mon, 6 Aug 2018 12:24:41 +0200 Subject: [PATCH 05/19] ecp5: Add DCC Bels, fix global router post-rebase Signed-off-by: David Shah --- ecp5/arch.cc | 1 + ecp5/constids.inc | 4 ++++ ecp5/globals.cc | 14 +++++++------- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/ecp5/arch.cc b/ecp5/arch.cc index 6b701a32..3aa24aca 100644 --- a/ecp5/arch.cc +++ b/ecp5/arch.cc @@ -43,6 +43,7 @@ static std::tuple split_identifier_name(const std::string // ----------------------------------------------------------------------- + void IdString::initialize_arch(const BaseCtx *ctx) { #define X(t) initialize_add(ctx, #t, ID_##t); diff --git a/ecp5/constids.inc b/ecp5/constids.inc index 12eb4a5a..bd55fa90 100644 --- a/ecp5/constids.inc +++ b/ecp5/constids.inc @@ -47,6 +47,10 @@ X(B) X(TRELLIS_SLICE) X(TRELLIS_IO) +X(DCCA) X(CLKMUX) X(LSRMUX) X(SRMODE) + +X(CLKI) +X(CLKO) diff --git a/ecp5/globals.cc b/ecp5/globals.cc index 453429e6..df7f4461 100644 --- a/ecp5/globals.cc +++ b/ecp5/globals.cc @@ -97,7 +97,7 @@ class Ecp5GlobalRouter return *(ctx->getPipsUphill(tap_wire).begin()); } - void route_logic_tile_global(IdString net, int global_index, PortRef user) + void route_logic_tile_global(NetInfo *net, int global_index, PortRef user) { WireId userWire = ctx->getBelPinWire(user.cell->bel, ctx->portPinFromId(user.port)); WireId globalWire; @@ -147,8 +147,8 @@ class Ecp5GlobalRouter if (!already_routed) { ctx->bindWire(cursor, net, STRENGTH_LOCKED); PipId tap_pip = find_tap_pip(cursor); - IdString tap_net = ctx->getBoundPipNet(tap_pip); - if (tap_net == IdString()) { + NetInfo *tap_net = ctx->getBoundPipNet(tap_pip); + if (tap_net == nullptr) { ctx->bindPip(tap_pip, net, STRENGTH_LOCKED); // TODO: SPINE } else { @@ -173,7 +173,7 @@ class Ecp5GlobalRouter return ctx->getWireByLocAndBasename(Location(0, 0), get_quad_name(quad) + "PCLK" + std::to_string(network)); } - void simple_router(IdString net, WireId src, WireId dst) + void simple_router(NetInfo *net, WireId src, WireId dst) { std::queue visit; std::unordered_map backtrace; @@ -186,10 +186,10 @@ class Ecp5GlobalRouter } cursor = visit.back(); visit.pop(); - IdString bound = ctx->getBoundWireNet(cursor); + NetInfo *bound = ctx->getBoundWireNet(cursor); if (bound == net) { break; - } else if (bound != IdString()) { + } else if (bound != nullptr) { continue; } if (cursor == dst) @@ -222,7 +222,7 @@ class Ecp5GlobalRouter } for (int quad = QUAD_UL; quad < QUAD_LR + 1; quad++) { WireId glb_dst = get_global_wire(GlobalQuadrant(quad), network); - simple_router(net->name, glb_src, glb_dst); + simple_router(net, glb_src, glb_dst); } if (false) { non_dedicated: From dfdaaa6f57a0a9f87878491a24b659e94aaec5fd Mon Sep 17 00:00:00 2001 From: David Shah Date: Tue, 7 Aug 2018 12:16:51 +0200 Subject: [PATCH 06/19] ecp5: Adding DCCA insertion function Signed-off-by: David Shah --- ecp5/cells.cc | 4 ++++ ecp5/globals.cc | 29 +++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/ecp5/cells.cc b/ecp5/cells.cc index e3532f36..c7afdbc2 100644 --- a/ecp5/cells.cc +++ b/ecp5/cells.cc @@ -124,6 +124,10 @@ std::unique_ptr create_ecp5_cell(Context *ctx, IdString type, std::str add_port(ctx, new_cell.get(), "C", PORT_IN); add_port(ctx, new_cell.get(), "D", PORT_IN); add_port(ctx, new_cell.get(), "Z", PORT_OUT); + } else if (type == ctx->id("DCCA")) { + add_port(ctx, new_cell.get(), "CLKI", PORT_IN); + add_port(ctx, new_cell.get(), "CLKO", PORT_OUT); + add_port(ctx, new_cell.get(), "CE", PORT_IN); } else { log_error("unable to create ECP5 cell of type %s", type.c_str(ctx)); } diff --git a/ecp5/globals.cc b/ecp5/globals.cc index df7f4461..3d7d7518 100644 --- a/ecp5/globals.cc +++ b/ecp5/globals.cc @@ -21,6 +21,7 @@ #include #include #include "nextpnr.h" +#include "cells.h" #include "log.h" @@ -230,6 +231,34 @@ class Ecp5GlobalRouter } } + + // Insert a DCC into a net to promote it to a global + NetInfo *insert_dcc(NetInfo *net) + { + auto dcc = create_ecp5_cell(ctx, ctx->id("DCCA"), "$gbuf$" + net->name.str(ctx)); + + std::unique_ptr glbnet = std::unique_ptr(new NetInfo); + glbnet->name = ctx->id("$glbnet$" + net->name.str(ctx)); + glbnet->driver.cell = dcc.get(); + glbnet->driver.port = ctx->id("CLKO"); + + for (auto user : net->users) { + user.cell->ports.at(user.port).net = glbnet.get(); + } + net->users.clear(); + + dcc->ports[ctx->id("CLKI")].net = net; + PortRef clki_pr; + clki_pr.port = ctx->id("CLKI"); + clki_pr.cell = dcc.get(); + net->users.push_back(clki_pr); + + ctx->cells[dcc->name] = std::move(dcc); + NetInfo *glbptr = glbnet.get(); + ctx->nets[glbnet->name] = std::move(glbnet); + return glbptr; + } + Context *ctx; }; From 24414614d2896b6cbb62457e90f5d997d9a1d32a Mon Sep 17 00:00:00 2001 From: David Shah Date: Tue, 7 Aug 2018 15:12:55 +0200 Subject: [PATCH 07/19] ecp5: Import SPINE data to database Signed-off-by: David Shah --- ecp5/arch.h | 2 ++ ecp5/trellis_import.py | 9 ++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/ecp5/arch.h b/ecp5/arch.h index 1908f122..bbab3918 100644 --- a/ecp5/arch.h +++ b/ecp5/arch.h @@ -147,6 +147,8 @@ NPNR_PACKED_STRUCT(struct GlobalInfoPOD { int16_t tap_col; TapDirection tap_dir; GlobalQuadrant quad; + int16_t spine_row; + int16_t spine_col; }); NPNR_PACKED_STRUCT(struct ChipInfoPOD { diff --git a/ecp5/trellis_import.py b/ecp5/trellis_import.py index de8e9958..9a26b605 100755 --- a/ecp5/trellis_import.py +++ b/ecp5/trellis_import.py @@ -128,7 +128,12 @@ def process_loc_globals(chip): for x in range(0, max_col+1): quad = chip.global_data.get_quadrant(y, x) tapdrv = chip.global_data.get_tap_driver(y, x) - global_data[x, y] = (quadrants.index(quad), int(tapdrv.dir), tapdrv.col) + if tapdrv.col == x: + spinedrv = chip.global_data.get_spine_driver(quad, x) + spine = (spinedrv.second, spinedrv.first) + else: + spine = (-1, -1) + global_data[x, y] = (quadrants.index(quad), int(tapdrv.dir), tapdrv.col, spine) def get_wire_type(name): if "H00" in name or "V00" in name: @@ -282,6 +287,8 @@ def write_database(dev_name, chip, ddrg, endianness): bba.u16(global_data[x, y][2], "tap_col") bba.u8(global_data[x, y][1], "tap_dir") bba.u8(global_data[x, y][0], "quad") + bba.u16(global_data[x, y][3][1], "spine_row") + bba.u16(global_data[x, y][3][0], "spine_col") for package, pkgdata in sorted(packages.items()): bba.l("package_data_%s" % package, "PackagePinPOD") From c8674652dc9ee7d28bc8ded1c0a9ac7a6e168176 Mon Sep 17 00:00:00 2001 From: David Shah Date: Tue, 7 Aug 2018 15:53:41 +0200 Subject: [PATCH 08/19] ecp5: Add SPINE routing to global router Signed-off-by: David Shah --- ecp5/globals.cc | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/ecp5/globals.cc b/ecp5/globals.cc index 3d7d7518..155d4ecb 100644 --- a/ecp5/globals.cc +++ b/ecp5/globals.cc @@ -98,6 +98,16 @@ class Ecp5GlobalRouter return *(ctx->getPipsUphill(tap_wire).begin()); } + PipId find_spine_pip(WireId tap_wire) + { + std::string wireName = ctx->getWireBasename(tap_wire).str(ctx); + Location spine_loc; + spine_loc.x = ctx->globalInfoAtLoc(tap_wire.location).spine_col; + spine_loc.y = ctx->globalInfoAtLoc(tap_wire.location).spine_row; + WireId spine_wire = ctx->getWireByLocAndBasename(spine_loc, wireName); + return *(ctx->getPipsUphill(spine_wire).begin()); + } + void route_logic_tile_global(NetInfo *net, int global_index, PortRef user) { WireId userWire = ctx->getBelPinWire(user.cell->bel, ctx->portPinFromId(user.port)); @@ -151,7 +161,13 @@ class Ecp5GlobalRouter NetInfo *tap_net = ctx->getBoundPipNet(tap_pip); if (tap_net == nullptr) { ctx->bindPip(tap_pip, net, STRENGTH_LOCKED); - // TODO: SPINE + PipId spine_pip = find_spine_pip(ctx->getPipSrcWire(tap_pip)); + NetInfo *spine_net = ctx->getBoundPipNet(spine_pip); + if (spine_net == nullptr) { + ctx->bindPip(spine_pip, net, STRENGTH_LOCKED); + } else { + NPNR_ASSERT(spine_net == net); + } } else { NPNR_ASSERT(tap_net == net); } From f7a270a1d868b632bd178f1927d2270aa11e053d Mon Sep 17 00:00:00 2001 From: David Shah Date: Sat, 29 Sep 2018 16:15:17 +0100 Subject: [PATCH 09/19] ecp5: Fix globals.cc following API update Signed-off-by: David Shah --- ecp5/globals.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ecp5/globals.cc b/ecp5/globals.cc index 155d4ecb..5c5c7c13 100644 --- a/ecp5/globals.cc +++ b/ecp5/globals.cc @@ -110,7 +110,7 @@ class Ecp5GlobalRouter void route_logic_tile_global(NetInfo *net, int global_index, PortRef user) { - WireId userWire = ctx->getBelPinWire(user.cell->bel, ctx->portPinFromId(user.port)); + WireId userWire = ctx->getBelPinWire(user.cell->bel, user.port); WireId globalWire; IdString global_name = ctx->id(fmt_str("G_HPBX" << std::setw(2) << std::setfill('0') << global_index << "00")); std::queue upstream; From 4cd582478b84c5d73faf7b43452b4976a612e685 Mon Sep 17 00:00:00 2001 From: David Shah Date: Sat, 29 Sep 2018 16:37:18 +0100 Subject: [PATCH 10/19] ecp5: Adding main global promoter/router function Signed-off-by: David Shah --- ecp5/globals.cc | 76 +++++++++++++++++++++++++++++++++++-------------- ecp5/globals.h | 26 +++++++++++++++++ 2 files changed, 81 insertions(+), 21 deletions(-) create mode 100644 ecp5/globals.h diff --git a/ecp5/globals.cc b/ecp5/globals.cc index 5c5c7c13..b68bed28 100644 --- a/ecp5/globals.cc +++ b/ecp5/globals.cc @@ -22,7 +22,7 @@ #include #include "nextpnr.h" #include "cells.h" - +#include "globals.h" #include "log.h" #define fmt_str(x) (static_cast(std::ostringstream() << x).str()) @@ -73,7 +73,7 @@ class Ecp5GlobalRouter auto max = std::max_element(clockCount.begin(), clockCount.end(), [](const decltype(clockCount)::value_type &a, const decltype(clockCount)::value_type &b) { return a.second < b.second; }); - if (max == clockCount.end() || max->second < 3) + if (max == clockCount.end() || max->second < 5) break; clocks.push_back(ctx->nets.at(max->first).get()); clockCount.erase(max->first); @@ -190,7 +190,7 @@ class Ecp5GlobalRouter return ctx->getWireByLocAndBasename(Location(0, 0), get_quad_name(quad) + "PCLK" + std::to_string(network)); } - void simple_router(NetInfo *net, WireId src, WireId dst) + bool simple_router(NetInfo *net, WireId src, WireId dst, bool allow_fail = false) { std::queue visit; std::unordered_map backtrace; @@ -198,6 +198,8 @@ class Ecp5GlobalRouter WireId cursor; while (true) { if (visit.empty() || visit.size() > 50000) { + if (allow_fail) + return false; log_error("cannot route global from %s to %s.\n", ctx->getWireName(src).c_str(ctx), ctx->getWireName(dst).c_str(ctx)); } @@ -226,58 +228,90 @@ class Ecp5GlobalRouter ctx->bindPip(fnd->second, net, STRENGTH_LOCKED); cursor = ctx->getPipSrcWire(fnd->second); } + return true; } - void route_onto_global(NetInfo *net, int network) + bool route_onto_global(NetInfo *net, int network) { WireId glb_src; - if (net->driver.cell->type == ctx->id("TRELLIS_IO")) { - std::string ioglb; - if (!is_global_io(net->driver.cell, ioglb)) - goto non_dedicated; - glb_src = ctx->getWireByLocAndBasename(Location(0, 0), ioglb); - } + NPNR_ASSERT(net->driver.cell->type == id_DCCA); + glb_src = ctx->getNetinfoSourceWire(net); for (int quad = QUAD_UL; quad < QUAD_LR + 1; quad++) { WireId glb_dst = get_global_wire(GlobalQuadrant(quad), network); - simple_router(net, glb_src, glb_dst); - } - if (false) { - non_dedicated: - log_error("FIXME: currenly global networks can only be driven by dedicated global input pins"); + bool routed = simple_router(net, glb_src, glb_dst); + if (!routed) + return false; } + return true; } + // Attempt to place a DCC + void place_dcc(CellInfo *dcc) { + for (auto bel : ctx->getBels()) { + if (ctx->getBelType(bel) == id_DCCA && ctx->checkBelAvail(bel)) { + if (ctx->isValidBelForCell(dcc, bel)) { + ctx->bindBel(bel, dcc, STRENGTH_LOCKED); + return; + } + } + } + NPNR_ASSERT_FALSE("failed to place dcca"); + } + // Insert a DCC into a net to promote it to a global NetInfo *insert_dcc(NetInfo *net) { - auto dcc = create_ecp5_cell(ctx, ctx->id("DCCA"), "$gbuf$" + net->name.str(ctx)); + auto dcc = create_ecp5_cell(ctx, id_DCCA, "$gbuf$" + net->name.str(ctx)); std::unique_ptr glbnet = std::unique_ptr(new NetInfo); glbnet->name = ctx->id("$glbnet$" + net->name.str(ctx)); glbnet->driver.cell = dcc.get(); - glbnet->driver.port = ctx->id("CLKO"); + glbnet->driver.port = id_CLKO; for (auto user : net->users) { user.cell->ports.at(user.port).net = glbnet.get(); } net->users.clear(); - dcc->ports[ctx->id("CLKI")].net = net; + dcc->ports[id_CLKI].net = net; PortRef clki_pr; - clki_pr.port = ctx->id("CLKI"); + clki_pr.port = id_CLKI; clki_pr.cell = dcc.get(); net->users.push_back(clki_pr); + place_dcc(dcc.get()); + ctx->cells[dcc->name] = std::move(dcc); NetInfo *glbptr = glbnet.get(); ctx->nets[glbnet->name] = std::move(glbnet); return glbptr; } - Context *ctx; + + +public: + void promote_and_route_globals() { + log_info("Promoting and routing globals..."); + auto clocks = get_clocks(); + int glbid = 0; + for (auto clock : clocks) { + log_info(" promoting clock net %s to global %d\n", clock->name.c_str(ctx), glbid); + auto old_users = clock->users; + NetInfo *global = insert_dcc(clock); + bool routed = route_onto_global(global, glbid); + NPNR_ASSERT(routed); + for (const auto &user : global->users) { + route_logic_tile_global(global, glbid, user); + } + glbid++; + } + } + }; -void route_ecp5_globals(Context *ctx); +void route_ecp5_globals(Context *ctx) { + Ecp5GlobalRouter(ctx).promote_and_route_globals(); +} NEXTPNR_NAMESPACE_END diff --git a/ecp5/globals.h b/ecp5/globals.h new file mode 100644 index 00000000..23e25c8d --- /dev/null +++ b/ecp5/globals.h @@ -0,0 +1,26 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 David Shah + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include "nextpnr.h" + +NEXTPNR_NAMESPACE_BEGIN + +void route_ecp5_globals(Context *ctx); + +NEXTPNR_NAMESPACE_END \ No newline at end of file From 2a0bb2be29a58017c31dd813cc061268abb292f8 Mon Sep 17 00:00:00 2001 From: David Shah Date: Sat, 29 Sep 2018 16:49:29 +0100 Subject: [PATCH 11/19] ecp5: Integrate global router and debug naming Signed-off-by: David Shah --- ecp5/arch.cc | 6 +++++- ecp5/globals.cc | 6 ++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/ecp5/arch.cc b/ecp5/arch.cc index 3aa24aca..861aeef2 100644 --- a/ecp5/arch.cc +++ b/ecp5/arch.cc @@ -27,6 +27,7 @@ #include "placer1.h" #include "router1.h" #include "util.h" +#include "globals.h" NEXTPNR_NAMESPACE_BEGIN @@ -390,7 +391,10 @@ bool Arch::getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay bool Arch::place() { return placer1(getCtx(), Placer1Cfg(getCtx())); } -bool Arch::route() { return router1(getCtx(), Router1Cfg(getCtx())); } +bool Arch::route() { + route_ecp5_globals(getCtx()); + return router1(getCtx(), Router1Cfg(getCtx())); +} // ----------------------------------------------------------------------- diff --git a/ecp5/globals.cc b/ecp5/globals.cc index b68bed28..0c95cd23 100644 --- a/ecp5/globals.cc +++ b/ecp5/globals.cc @@ -52,7 +52,7 @@ class Ecp5GlobalRouter private: bool is_clock_port(const PortRef &user) { - if (user.cell->type == ctx->id("TRELLIS_LC") && user.port == ctx->id("CLK")) + if (user.cell->type == id_TRELLIS_SLICE && user.port == id_CLK) return true; return false; } @@ -67,6 +67,7 @@ class Ecp5GlobalRouter if (is_clock_port(user)) clockCount[ni->name]++; } + //log_info("clkcount %s: %d\n", ni->name.c_str(ctx),clockCount[ni->name]); } std::vector clocks; while (clocks.size() < 16) { @@ -187,7 +188,7 @@ class Ecp5GlobalRouter WireId get_global_wire(GlobalQuadrant quad, int network) { - return ctx->getWireByLocAndBasename(Location(0, 0), get_quad_name(quad) + "PCLK" + std::to_string(network)); + return ctx->getWireByLocAndBasename(Location(0, 0), "G_" + get_quad_name(quad) + "PCLK" + std::to_string(network)); } bool simple_router(NetInfo *net, WireId src, WireId dst, bool allow_fail = false) @@ -238,6 +239,7 @@ class Ecp5GlobalRouter glb_src = ctx->getNetinfoSourceWire(net); for (int quad = QUAD_UL; quad < QUAD_LR + 1; quad++) { WireId glb_dst = get_global_wire(GlobalQuadrant(quad), network); + NPNR_ASSERT(glb_dst != WireId()); bool routed = simple_router(net, glb_src, glb_dst); if (!routed) return false; From 9ff5d5a73534d19c78cc2b8c9cf4bd4d351978fa Mon Sep 17 00:00:00 2001 From: David Shah Date: Sat, 29 Sep 2018 17:01:19 +0100 Subject: [PATCH 12/19] ecp5: Fixing global router bugs Signed-off-by: David Shah --- ecp5/globals.cc | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/ecp5/globals.cc b/ecp5/globals.cc index 0c95cd23..53bf303c 100644 --- a/ecp5/globals.cc +++ b/ecp5/globals.cc @@ -198,17 +198,19 @@ class Ecp5GlobalRouter visit.push(src); WireId cursor; while (true) { + if (visit.empty() || visit.size() > 50000) { if (allow_fail) return false; log_error("cannot route global from %s to %s.\n", ctx->getWireName(src).c_str(ctx), ctx->getWireName(dst).c_str(ctx)); } - cursor = visit.back(); + cursor = visit.front(); visit.pop(); NetInfo *bound = ctx->getBoundWireNet(cursor); + if (ctx->verbose) + log_info(" exploring %s\n", ctx->getWireName(cursor).c_str(ctx)); if (bound == net) { - break; } else if (bound != nullptr) { continue; } @@ -216,6 +218,8 @@ class Ecp5GlobalRouter break; for (auto dh : ctx->getPipsDownhill(cursor)) { WireId pipDst = ctx->getPipDstWire(dh); + if (ctx->verbose) + log_info(" downhill -> %s\n", ctx->getWireName(pipDst).c_str(ctx)); if (backtrace.count(pipDst)) continue; backtrace[pipDst] = dh; @@ -226,9 +230,16 @@ class Ecp5GlobalRouter auto fnd = backtrace.find(cursor); if (fnd == backtrace.end()) break; + NetInfo * bound = ctx->getBoundWireNet(cursor); + if (bound != nullptr) { + NPNR_ASSERT(bound == net); + break; + } ctx->bindPip(fnd->second, net, STRENGTH_LOCKED); cursor = ctx->getPipSrcWire(fnd->second); } + if (ctx->getBoundWireNet(src) == nullptr) + ctx->bindWire(src, net, STRENGTH_LOCKED); return true; } @@ -294,7 +305,7 @@ class Ecp5GlobalRouter public: void promote_and_route_globals() { - log_info("Promoting and routing globals..."); + log_info("Promoting and routing globals...\n"); auto clocks = get_clocks(); int glbid = 0; for (auto clock : clocks) { From c2a062d254a749cb194ed7fcd44072b69bc9f2ff Mon Sep 17 00:00:00 2001 From: David Shah Date: Sat, 29 Sep 2018 17:13:50 +0100 Subject: [PATCH 13/19] ecp5: Fixing global to global user routing Signed-off-by: David Shah --- ecp5/globals.cc | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/ecp5/globals.cc b/ecp5/globals.cc index 53bf303c..09e298d0 100644 --- a/ecp5/globals.cc +++ b/ecp5/globals.cc @@ -92,10 +92,13 @@ class Ecp5GlobalRouter tap_loc.x = ctx->globalInfoAtLoc(tile_glb.location).tap_col; tap_loc.y = tile_glb.location.y; if (td == TAP_DIR_LEFT) { + log_info(" finding tap %d, %d, %s\n", tap_loc.x, tap_loc.y, ("L_" + glbName).c_str()); tap_wire = ctx->getWireByLocAndBasename(tap_loc, "L_" + glbName); } else { + log_info(" finding tap %d, %d, %s\n", tap_loc.x, tap_loc.y, ("R_" + glbName).c_str()); tap_wire = ctx->getWireByLocAndBasename(tap_loc, "R_" + glbName); } + NPNR_ASSERT(tap_wire != WireId()); return *(ctx->getPipsUphill(tap_wire).begin()); } @@ -118,9 +121,10 @@ class Ecp5GlobalRouter std::unordered_map backtrace; upstream.push(userWire); bool already_routed = false; + WireId next; // Search back from the pin until we reach the global network while (true) { - WireId next = upstream.front(); + next = upstream.front(); upstream.pop(); if (ctx->getBoundWireNet(next) == net) { @@ -145,20 +149,23 @@ class Ecp5GlobalRouter ctx->getBelName(user.cell->bel).c_str(ctx), user.port.c_str(ctx)); } } + log_info(" routing net %s from %s\n", net->name.c_str(ctx), ctx->getWireName(next).c_str(ctx)); // Set all the pips we found along the way - WireId cursor = userWire; + WireId cursor = next; while (true) { auto fnd = backtrace.find(cursor); if (fnd == backtrace.end()) break; ctx->bindPip(fnd->second, net, STRENGTH_LOCKED); - cursor = ctx->getPipSrcWire(fnd->second); + cursor = ctx->getPipDstWire(fnd->second); + log_info(" via %s\n", ctx->getWireName(cursor).c_str(ctx)); + } // If the global network inside the tile isn't already set up, // we also need to bind the buffers along the way if (!already_routed) { - ctx->bindWire(cursor, net, STRENGTH_LOCKED); - PipId tap_pip = find_tap_pip(cursor); + ctx->bindWire(next, net, STRENGTH_LOCKED); + PipId tap_pip = find_tap_pip(next); NetInfo *tap_net = ctx->getBoundPipNet(tap_pip); if (tap_net == nullptr) { ctx->bindPip(tap_pip, net, STRENGTH_LOCKED); @@ -281,7 +288,7 @@ class Ecp5GlobalRouter glbnet->name = ctx->id("$glbnet$" + net->name.str(ctx)); glbnet->driver.cell = dcc.get(); glbnet->driver.port = id_CLKO; - + glbnet->users = net->users; for (auto user : net->users) { user.cell->ports.at(user.port).net = glbnet.get(); } From c5f9a12bb103626e980bd4b843d270eb3ae64d41 Mon Sep 17 00:00:00 2001 From: David Shah Date: Sat, 29 Sep 2018 17:36:08 +0100 Subject: [PATCH 14/19] ecp5: Global router produces a working bitstream Signed-off-by: David Shah --- common/nextpnr.cc | 7 +++++++ common/router1.cc | 6 ++++++ ecp5/arch.cc | 2 ++ ecp5/bitstream.cc | 2 ++ 4 files changed, 17 insertions(+) diff --git a/common/nextpnr.cc b/common/nextpnr.cc index b04679ad..d47a8525 100644 --- a/common/nextpnr.cc +++ b/common/nextpnr.cc @@ -99,8 +99,15 @@ delay_t Context::getNetinfoRouteDelay(const NetInfo *net_info, const PortRef &us while (cursor != WireId() && cursor != src_wire) { auto it = net_info->wires.find(cursor); + if (it == net_info->wires.end()) break; +#ifdef ARCH_ECP5 + // ECP5 global nets currently appear part-unrouted due to arch database limitations + // Don't touch them in the router + if (it->second.strength == STRENGTH_LOCKED) + return 0; +#endif PipId pip = it->second.pip; delay += getPipDelay(pip).maxDelay(); delay += getWireDelay(cursor).maxDelay(); diff --git a/common/router1.cc b/common/router1.cc index 5cd4414c..d29cdd68 100644 --- a/common/router1.cc +++ b/common/router1.cc @@ -532,6 +532,12 @@ void addNetRouteJobs(Context *ctx, const Router1Cfg &cfg, IdString net_name, { NetInfo *net_info = ctx->nets.at(net_name).get(); +#ifdef ARCH_ECP5 + // ECP5 global nets currently appear part-unrouted due to arch database limitations + // Don't touch them in the router + if (!net_info->wires.empty() && net_info->wires.begin()->second.strength == STRENGTH_LOCKED) + return; +#endif if (net_info->driver.cell == nullptr) return; diff --git a/ecp5/arch.cc b/ecp5/arch.cc index 861aeef2..1479e6ca 100644 --- a/ecp5/arch.cc +++ b/ecp5/arch.cc @@ -568,6 +568,8 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, Id if (port == id_O) return TMG_STARTPOINT; return TMG_IGNORE; + } else if (cell->type == id_DCCA) { + return TMG_IGNORE; } else { NPNR_ASSERT_FALSE_STR("no timing data for cell type '" + cell->type.str(this) + "'"); } diff --git a/ecp5/bitstream.cc b/ecp5/bitstream.cc index a1edf9e5..f04b1269 100644 --- a/ecp5/bitstream.cc +++ b/ecp5/bitstream.cc @@ -294,6 +294,8 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex if (dir == "INPUT" && !is_differential(ioType_from_str(iotype))) { cc.tiles[pio_tile].add_enum(pio + ".HYSTERESIS", "ON"); } + } else if (ci->type == ctx->id("DCCA")) { + // Nothing to do } else { NPNR_ASSERT_FALSE("unsupported cell type"); } From 5e46d1eb98bc398b7172b1d9f0aa5505673c6198 Mon Sep 17 00:00:00 2001 From: David Shah Date: Sat, 29 Sep 2018 17:38:39 +0100 Subject: [PATCH 15/19] ecp5: Remove excessive debugging from global promoter Signed-off-by: David Shah --- ecp5/globals.cc | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/ecp5/globals.cc b/ecp5/globals.cc index 09e298d0..b74c700b 100644 --- a/ecp5/globals.cc +++ b/ecp5/globals.cc @@ -92,10 +92,8 @@ class Ecp5GlobalRouter tap_loc.x = ctx->globalInfoAtLoc(tile_glb.location).tap_col; tap_loc.y = tile_glb.location.y; if (td == TAP_DIR_LEFT) { - log_info(" finding tap %d, %d, %s\n", tap_loc.x, tap_loc.y, ("L_" + glbName).c_str()); tap_wire = ctx->getWireByLocAndBasename(tap_loc, "L_" + glbName); } else { - log_info(" finding tap %d, %d, %s\n", tap_loc.x, tap_loc.y, ("R_" + glbName).c_str()); tap_wire = ctx->getWireByLocAndBasename(tap_loc, "R_" + glbName); } NPNR_ASSERT(tap_wire != WireId()); @@ -149,7 +147,6 @@ class Ecp5GlobalRouter ctx->getBelName(user.cell->bel).c_str(ctx), user.port.c_str(ctx)); } } - log_info(" routing net %s from %s\n", net->name.c_str(ctx), ctx->getWireName(next).c_str(ctx)); // Set all the pips we found along the way WireId cursor = next; while (true) { @@ -158,7 +155,6 @@ class Ecp5GlobalRouter break; ctx->bindPip(fnd->second, net, STRENGTH_LOCKED); cursor = ctx->getPipDstWire(fnd->second); - log_info(" via %s\n", ctx->getWireName(cursor).c_str(ctx)); } // If the global network inside the tile isn't already set up, @@ -215,8 +211,6 @@ class Ecp5GlobalRouter cursor = visit.front(); visit.pop(); NetInfo *bound = ctx->getBoundWireNet(cursor); - if (ctx->verbose) - log_info(" exploring %s\n", ctx->getWireName(cursor).c_str(ctx)); if (bound == net) { } else if (bound != nullptr) { continue; @@ -225,8 +219,6 @@ class Ecp5GlobalRouter break; for (auto dh : ctx->getPipsDownhill(cursor)) { WireId pipDst = ctx->getPipDstWire(dh); - if (ctx->verbose) - log_info(" downhill -> %s\n", ctx->getWireName(pipDst).c_str(ctx)); if (backtrace.count(pipDst)) continue; backtrace[pipDst] = dh; @@ -244,7 +236,7 @@ class Ecp5GlobalRouter } ctx->bindPip(fnd->second, net, STRENGTH_LOCKED); cursor = ctx->getPipSrcWire(fnd->second); - } + }\ if (ctx->getBoundWireNet(src) == nullptr) ctx->bindWire(src, net, STRENGTH_LOCKED); return true; From f46f205782a8f2e977fefe8161ad7ff1a9ec6ad1 Mon Sep 17 00:00:00 2001 From: David Shah Date: Sat, 29 Sep 2018 18:06:08 +0100 Subject: [PATCH 16/19] ecp5: Fix handling of global to fabric connections Signed-off-by: David Shah --- ecp5/globals.cc | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/ecp5/globals.cc b/ecp5/globals.cc index b74c700b..db3ba413 100644 --- a/ecp5/globals.cc +++ b/ecp5/globals.cc @@ -142,7 +142,7 @@ class Ecp5GlobalRouter upstream.push(src); } } - if (upstream.size() > 3000) { + if (upstream.size() > 30000) { log_error("failed to route HPBX%02d00 to %s.%s\n", global_index, ctx->getBelName(user.cell->bel).c_str(ctx), user.port.c_str(ctx)); } @@ -306,8 +306,26 @@ public: void promote_and_route_globals() { log_info("Promoting and routing globals...\n"); auto clocks = get_clocks(); - int glbid = 0; + std::set all_globals, fab_globals; + for (int i = 0; i < 16; i++) { + all_globals.insert(i); + if (i < 8) + fab_globals.insert(i); + } for (auto clock : clocks) { + bool drives_fabric = std::any_of(clock->users.begin(), clock->users.end(), [this](const PortRef &port) { + return !is_clock_port(port); + }); + int glbid; + if (drives_fabric) { + if (fab_globals.empty()) + continue; + glbid = *(fab_globals.begin()); + } else { + glbid = *(all_globals.begin()); + } + all_globals.erase(glbid); + fab_globals.erase(glbid); log_info(" promoting clock net %s to global %d\n", clock->name.c_str(ctx), glbid); auto old_users = clock->users; NetInfo *global = insert_dcc(clock); @@ -316,7 +334,6 @@ public: for (const auto &user : global->users) { route_logic_tile_global(global, glbid, user); } - glbid++; } } From 11cdc197bc48c9ce10dd9b718d44603adf2591b9 Mon Sep 17 00:00:00 2001 From: David Shah Date: Sat, 29 Sep 2018 18:29:23 +0100 Subject: [PATCH 17/19] ecp5: Fix global buffer connectivity and timing Signed-off-by: David Shah --- ecp5/arch.cc | 12 ++++++++++++ ecp5/globals.cc | 2 ++ 2 files changed, 14 insertions(+) diff --git a/ecp5/arch.cc b/ecp5/arch.cc index 1479e6ca..d42e77fa 100644 --- a/ecp5/arch.cc +++ b/ecp5/arch.cc @@ -28,6 +28,7 @@ #include "router1.h" #include "util.h" #include "globals.h" +#include "timing.h" NEXTPNR_NAMESPACE_BEGIN @@ -393,6 +394,7 @@ bool Arch::place() { return placer1(getCtx(), Placer1Cfg(getCtx())); } bool Arch::route() { route_ecp5_globals(getCtx()); + assign_budget(getCtx(), true); return router1(getCtx(), Router1Cfg(getCtx())); } @@ -523,6 +525,12 @@ bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort return true; } return false; + } else if (cell->type == id_DCCA) { + if (fromPort == id_CLKI && toPort == id_CLKO) { + delay.delay = 0; + return true; + } + return false; } else { return false; } @@ -569,6 +577,10 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, Id return TMG_STARTPOINT; return TMG_IGNORE; } else if (cell->type == id_DCCA) { + if (port == id_CLKI) + return TMG_COMB_INPUT; + if (port == id_CLKO) + return TMG_COMB_OUTPUT; return TMG_IGNORE; } else { NPNR_ASSERT_FALSE_STR("no timing data for cell type '" + cell->type.str(this) + "'"); diff --git a/ecp5/globals.cc b/ecp5/globals.cc index db3ba413..15ef05d6 100644 --- a/ecp5/globals.cc +++ b/ecp5/globals.cc @@ -280,6 +280,8 @@ class Ecp5GlobalRouter glbnet->name = ctx->id("$glbnet$" + net->name.str(ctx)); glbnet->driver.cell = dcc.get(); glbnet->driver.port = id_CLKO; + dcc->ports[id_CLKO].net = glbnet.get(); + glbnet->users = net->users; for (auto user : net->users) { user.cell->ports.at(user.port).net = glbnet.get(); From ab063b2456a6b1c3893af9a809d3cb276a6f8c21 Mon Sep 17 00:00:00 2001 From: David Shah Date: Sat, 29 Sep 2018 18:37:17 +0100 Subject: [PATCH 18/19] clangformat Signed-off-by: David Shah --- ecp5/arch.cc | 8 ++++---- ecp5/arch.h | 1 - ecp5/globals.cc | 34 +++++++++++++++------------------- 3 files changed, 19 insertions(+), 24 deletions(-) diff --git a/ecp5/arch.cc b/ecp5/arch.cc index d42e77fa..830dfc7c 100644 --- a/ecp5/arch.cc +++ b/ecp5/arch.cc @@ -22,13 +22,13 @@ #include #include #include "gfx.h" +#include "globals.h" #include "log.h" #include "nextpnr.h" #include "placer1.h" #include "router1.h" -#include "util.h" -#include "globals.h" #include "timing.h" +#include "util.h" NEXTPNR_NAMESPACE_BEGIN @@ -45,7 +45,6 @@ static std::tuple split_identifier_name(const std::string // ----------------------------------------------------------------------- - void IdString::initialize_arch(const BaseCtx *ctx) { #define X(t) initialize_add(ctx, #t, ID_##t); @@ -392,7 +391,8 @@ bool Arch::getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay bool Arch::place() { return placer1(getCtx(), Placer1Cfg(getCtx())); } -bool Arch::route() { +bool Arch::route() +{ route_ecp5_globals(getCtx()); assign_budget(getCtx(), true); return router1(getCtx(), Router1Cfg(getCtx())); diff --git a/ecp5/arch.h b/ecp5/arch.h index bbab3918..9eac3c9f 100644 --- a/ecp5/arch.h +++ b/ecp5/arch.h @@ -915,7 +915,6 @@ struct Arch : BaseCtx IdString id_clk, id_lsr; IdString id_clkmux, id_lsrmux; IdString id_srmode, id_mode; - }; NEXTPNR_NAMESPACE_END diff --git a/ecp5/globals.cc b/ecp5/globals.cc index 15ef05d6..22fcbb05 100644 --- a/ecp5/globals.cc +++ b/ecp5/globals.cc @@ -17,13 +17,13 @@ * */ +#include "globals.h" #include #include #include -#include "nextpnr.h" #include "cells.h" -#include "globals.h" #include "log.h" +#include "nextpnr.h" #define fmt_str(x) (static_cast(std::ostringstream() << x).str()) @@ -67,7 +67,7 @@ class Ecp5GlobalRouter if (is_clock_port(user)) clockCount[ni->name]++; } - //log_info("clkcount %s: %d\n", ni->name.c_str(ctx),clockCount[ni->name]); + // log_info("clkcount %s: %d\n", ni->name.c_str(ctx),clockCount[ni->name]); } std::vector clocks; while (clocks.size() < 16) { @@ -155,7 +155,6 @@ class Ecp5GlobalRouter break; ctx->bindPip(fnd->second, net, STRENGTH_LOCKED); cursor = ctx->getPipDstWire(fnd->second); - } // If the global network inside the tile isn't already set up, // we also need to bind the buffers along the way @@ -191,7 +190,8 @@ class Ecp5GlobalRouter WireId get_global_wire(GlobalQuadrant quad, int network) { - return ctx->getWireByLocAndBasename(Location(0, 0), "G_" + get_quad_name(quad) + "PCLK" + std::to_string(network)); + return ctx->getWireByLocAndBasename(Location(0, 0), + "G_" + get_quad_name(quad) + "PCLK" + std::to_string(network)); } bool simple_router(NetInfo *net, WireId src, WireId dst, bool allow_fail = false) @@ -229,14 +229,14 @@ class Ecp5GlobalRouter auto fnd = backtrace.find(cursor); if (fnd == backtrace.end()) break; - NetInfo * bound = ctx->getBoundWireNet(cursor); + NetInfo *bound = ctx->getBoundWireNet(cursor); if (bound != nullptr) { NPNR_ASSERT(bound == net); break; } ctx->bindPip(fnd->second, net, STRENGTH_LOCKED); cursor = ctx->getPipSrcWire(fnd->second); - }\ + } if (ctx->getBoundWireNet(src) == nullptr) ctx->bindWire(src, net, STRENGTH_LOCKED); return true; @@ -257,9 +257,9 @@ class Ecp5GlobalRouter return true; } - // Attempt to place a DCC - void place_dcc(CellInfo *dcc) { + void place_dcc(CellInfo *dcc) + { for (auto bel : ctx->getBels()) { if (ctx->getBelType(bel) == id_DCCA && ctx->checkBelAvail(bel)) { if (ctx->isValidBelForCell(dcc, bel)) { @@ -303,9 +303,9 @@ class Ecp5GlobalRouter } Context *ctx; - -public: - void promote_and_route_globals() { + public: + void promote_and_route_globals() + { log_info("Promoting and routing globals...\n"); auto clocks = get_clocks(); std::set all_globals, fab_globals; @@ -315,9 +315,8 @@ public: fab_globals.insert(i); } for (auto clock : clocks) { - bool drives_fabric = std::any_of(clock->users.begin(), clock->users.end(), [this](const PortRef &port) { - return !is_clock_port(port); - }); + bool drives_fabric = std::any_of(clock->users.begin(), clock->users.end(), + [this](const PortRef &port) { return !is_clock_port(port); }); int glbid; if (drives_fabric) { if (fab_globals.empty()) @@ -338,11 +337,8 @@ public: } } } - }; -void route_ecp5_globals(Context *ctx) { - Ecp5GlobalRouter(ctx).promote_and_route_globals(); -} +void route_ecp5_globals(Context *ctx) { Ecp5GlobalRouter(ctx).promote_and_route_globals(); } NEXTPNR_NAMESPACE_END From 0e0ad26f07354938820b6acd1d422fee3208767e Mon Sep 17 00:00:00 2001 From: David Shah Date: Sat, 29 Sep 2018 19:31:49 +0100 Subject: [PATCH 19/19] ecp5: Use ArchNetInfo to mark global nets to ignore Signed-off-by: David Shah --- common/nextpnr.cc | 12 ++++++------ common/router1.cc | 2 +- ecp5/archdefs.h | 2 ++ ecp5/globals.cc | 1 + 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/common/nextpnr.cc b/common/nextpnr.cc index d47a8525..068bca6f 100644 --- a/common/nextpnr.cc +++ b/common/nextpnr.cc @@ -89,6 +89,11 @@ WireId Context::getNetinfoSinkWire(const NetInfo *net_info, const PortRef &user_ delay_t Context::getNetinfoRouteDelay(const NetInfo *net_info, const PortRef &user_info) const { +#ifdef ARCH_ECP5 + if (net_info->is_global) + return 0; +#endif + WireId src_wire = getNetinfoSourceWire(net_info); if (src_wire == WireId()) return 0; @@ -102,12 +107,7 @@ delay_t Context::getNetinfoRouteDelay(const NetInfo *net_info, const PortRef &us if (it == net_info->wires.end()) break; -#ifdef ARCH_ECP5 - // ECP5 global nets currently appear part-unrouted due to arch database limitations - // Don't touch them in the router - if (it->second.strength == STRENGTH_LOCKED) - return 0; -#endif + PipId pip = it->second.pip; delay += getPipDelay(pip).maxDelay(); delay += getWireDelay(cursor).maxDelay(); diff --git a/common/router1.cc b/common/router1.cc index d29cdd68..c4708de7 100644 --- a/common/router1.cc +++ b/common/router1.cc @@ -535,7 +535,7 @@ void addNetRouteJobs(Context *ctx, const Router1Cfg &cfg, IdString net_name, #ifdef ARCH_ECP5 // ECP5 global nets currently appear part-unrouted due to arch database limitations // Don't touch them in the router - if (!net_info->wires.empty() && net_info->wires.begin()->second.strength == STRENGTH_LOCKED) + if (net_info->is_global) return; #endif if (net_info->driver.cell == nullptr) diff --git a/ecp5/archdefs.h b/ecp5/archdefs.h index c4e1413f..b5cdea3c 100644 --- a/ecp5/archdefs.h +++ b/ecp5/archdefs.h @@ -136,7 +136,9 @@ struct DecalId struct ArchNetInfo { + bool is_global = false; }; + struct ArchCellInfo { struct diff --git a/ecp5/globals.cc b/ecp5/globals.cc index 22fcbb05..91d224e5 100644 --- a/ecp5/globals.cc +++ b/ecp5/globals.cc @@ -280,6 +280,7 @@ class Ecp5GlobalRouter glbnet->name = ctx->id("$glbnet$" + net->name.str(ctx)); glbnet->driver.cell = dcc.get(); glbnet->driver.port = id_CLKO; + glbnet->is_global = true; dcc->ports[id_CLKO].net = glbnet.get(); glbnet->users = net->users;