Skip to content

Commit

Permalink
machxo2: Global placement and clock routing from nexus
Browse files Browse the repository at this point in the history
Signed-off-by: gatecat <[email protected]>
  • Loading branch information
gatecat committed May 8, 2023
1 parent 9177189 commit e3529d3
Show file tree
Hide file tree
Showing 6 changed files with 450 additions and 66 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/arch_ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
DEPS_PATH: ${{ github.workspace }}/deps
YOSYS_REVISION: bd7ee79486d4e8788f36de8c25a3fb2df451d682
ICESTORM_REVISION: 9f66f9ce16941c6417813cb87653c735a78b53ae
TRELLIS_REVISION: c99b22d35e4109333e7549e56cec139569a17392
TRELLIS_REVISION: f1e5710099313d2e1862d4ef2582293f6b7ee122
PRJOXIDE_REVISION: c3fb1526cf4a2165e15b74f4a994d153c7695fe4
MISTRAL_REVISION: ebfc0dd2cc7d6d2159b641a397c88554840e93c9
APYCULA_REVISION: 0.5.1a1
Expand Down
2 changes: 2 additions & 0 deletions machxo2/arch.cc
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,8 @@ bool Arch::route()

assignArchInfo();

route_globals();

bool result;
if (router == "router1") {
result = router1(getCtx(), Router1Cfg(getCtx()));
Expand Down
3 changes: 3 additions & 0 deletions machxo2/arch.h
Original file line number Diff line number Diff line change
Expand Up @@ -967,6 +967,9 @@ struct Arch : BaseArch<ArchRanges>
// Apply LPF constraints to the context
bool apply_lpf(std::string filename, std::istream &in);

// Global clock routing
void route_globals();

static const std::string defaultPlacer;
static const std::vector<std::string> availablePlacers;
static const std::string defaultRouter;
Expand Down
7 changes: 6 additions & 1 deletion machxo2/bitstream.cc
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,14 @@ struct MachXO2Bitgen
// clang-format on
};

if (prefix2 == "G_" || prefix2 == "L_" || prefix2 == "R_" || prefix7 == "BRANCH_")
if (prefix2 == "G_" || prefix7 == "BRANCH_")
return basename;

if (prefix2 == "L_" || prefix2 == "R_") {
if (loc.x == 0 || loc.x == (ctx->getGridDimX() - 1))
return "G_" + basename.substr(2);
return basename;
}
if (prefix2 == "U_" || prefix2 == "D_") {
// We needded to keep U_ and D_ prefixes to generate the routing
// graph connections properly, but in truth they are not relevant
Expand Down
178 changes: 178 additions & 0 deletions machxo2/globals.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2020-23 gatecat <[email protected]>
*
*
* 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 "log.h"
#include "nextpnr.h"
#include "util.h"

#include <queue>

NEXTPNR_NAMESPACE_BEGIN

struct MachxoGlobalRouter
{
Context *ctx;

MachxoGlobalRouter(Context *ctx) : ctx(ctx){};

// When routing globals; we allow global->local for some tricky cases but never local->local
bool global_pip_filter(PipId pip) const
{
WireId src = ctx->getPipSrcWire(pip);
const char *s = ctx->tile_info(src)->wire_data[src.index].name.get();
if ((s[0] == 'H' || s[0] == 'V') && s[1] == '0')
return false;
return true;
}

// Dedicated backwards BFS routing for global networks
template <typename Tfilt>
bool backwards_bfs_route(NetInfo *net, store_index<PortRef> user_idx, int iter_limit, bool strict, Tfilt pip_filter)
{
// Queue of wires to visit
std::queue<WireId> visit;
// Wire -> upstream pip
dict<WireId, PipId> backtrace;

// Lookup source and destination wires
WireId src = ctx->getNetinfoSourceWire(net);
WireId dst = ctx->getNetinfoSinkWire(net, net->users.at(user_idx), 0);

if (src == WireId())
log_error("Net '%s' has an invalid source port %s.%s\n", ctx->nameOf(net), ctx->nameOf(net->driver.cell),
ctx->nameOf(net->driver.port));

if (dst == WireId())
log_error("Net '%s' has an invalid sink port %s.%s\n", ctx->nameOf(net),
ctx->nameOf(net->users.at(user_idx).cell), ctx->nameOf(net->users.at(user_idx).port));

if (ctx->getBoundWireNet(src) != net)
ctx->bindWire(src, net, STRENGTH_LOCKED);

if (src == dst) {
// Nothing more to do
return true;
}

visit.push(dst);
backtrace[dst] = PipId();

int iter = 0;

while (!visit.empty() && (iter++ < iter_limit)) {
WireId cursor = visit.front();
visit.pop();
// Search uphill pips
for (PipId pip : ctx->getPipsUphill(cursor)) {
// Skip pip if unavailable, and not because it's already used for this net
if (!ctx->checkPipAvail(pip) && ctx->getBoundPipNet(pip) != net)
continue;
WireId prev = ctx->getPipSrcWire(pip);
// Ditto for the upstream wire
if (!ctx->checkWireAvail(prev) && ctx->getBoundWireNet(prev) != net)
continue;
// Skip already visited wires
if (backtrace.count(prev))
continue;
// Apply our custom pip filter
if (!pip_filter(pip))
continue;
// Add to the queue
visit.push(prev);
backtrace[prev] = pip;
// Check if we are done yet
if (prev == src)
goto done;
}
if (false) {
done:
break;
}
}

if (backtrace.count(src)) {
WireId cursor = src;
std::vector<PipId> pips;
// Create a list of pips on the routed path
while (true) {
PipId pip = backtrace.at(cursor);
if (pip == PipId())
break;
pips.push_back(pip);
cursor = ctx->getPipDstWire(pip);
}
// Reverse that list
std::reverse(pips.begin(), pips.end());
// Bind pips until we hit already-bound routing
for (PipId pip : pips) {
WireId dst = ctx->getPipDstWire(pip);
if (ctx->getBoundWireNet(dst) == net)
break;
ctx->bindPip(pip, net, STRENGTH_LOCKED);
}
return true;
} else {
if (strict)
log_error("Failed to route net '%s' from %s to %s using dedicated routing.\n", ctx->nameOf(net),
ctx->nameOfWire(src), ctx->nameOfWire(dst));
return false;
}
}

bool is_relaxed_sink(const PortRef &sink) const
{
// Cases where global clocks are driving fabric
if ((sink.cell->type == id_TRELLIS_COMB && sink.port != id_WCK) ||
(sink.cell->type == id_TRELLIS_FF && sink.port != id_CLK))
return true;
return false;
}

void route_clk_net(NetInfo *net)
{
for (auto usr : net->users.enumerate())
backwards_bfs_route(net, usr.index, 1000000, true,
[&](PipId pip) { return (is_relaxed_sink(usr.value) || global_pip_filter(pip)); });
log_info(" routed net '%s' using global resources\n", ctx->nameOf(net));
}

void operator()()
{
log_info("Routing globals...\n");
for (auto &net : ctx->nets) {
NetInfo *ni = net.second.get();
CellInfo *drv = ni->driver.cell;
if (drv == nullptr)
continue;
if (drv->type.in(id_DCCA, id_DCMA)) {
route_clk_net(ni);
continue;
}
}
}
};

void Arch::route_globals()
{
MachxoGlobalRouter glb_router(getCtx());
glb_router();
}

NEXTPNR_NAMESPACE_END
Loading

0 comments on commit e3529d3

Please sign in to comment.