mistral: Add a basic global clock router
Signed-off-by: gatecat <gatecat@ds0.me>
This commit is contained in:
parent
ca2e328a5f
commit
bc7b8b63ed
@ -484,6 +484,8 @@ bool Arch::route()
|
||||
|
||||
lab_pre_route();
|
||||
|
||||
route_globals();
|
||||
|
||||
std::string router = str_or_default(settings, id_router, defaultRouter);
|
||||
bool result;
|
||||
if (router == "router1") {
|
||||
|
@ -503,6 +503,7 @@ struct Arch : BaseArch<ArchRanges>
|
||||
// -------------------------------------------------
|
||||
|
||||
bool is_clkbuf_cell(IdString cell_type) const; // globals.cc
|
||||
void route_globals(); // globals.cc
|
||||
|
||||
// -------------------------------------------------
|
||||
|
||||
|
@ -21,6 +21,8 @@
|
||||
#include "nextpnr.h"
|
||||
#include "util.h"
|
||||
|
||||
#include <queue>
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
void Arch::create_clkbuf(int x, int y)
|
||||
@ -60,4 +62,151 @@ void Arch::create_control(int x, int y)
|
||||
add_bel_pin(oscillator_bel, id_clkout1, PORT_OUT, get_port(CycloneV::CTRL, x, y, -1, CycloneV::CLK_OUT1, -1));
|
||||
}
|
||||
|
||||
struct MistralGlobalRouter
|
||||
{
|
||||
Context *ctx;
|
||||
|
||||
MistralGlobalRouter(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
|
||||
{
|
||||
auto src_type = CycloneV::rn2t(pip.src);
|
||||
return src_type != CycloneV::H14 && src_type != CycloneV::H6 && src_type != CycloneV::H3 &&
|
||||
src_type != CycloneV::V12 && src_type != CycloneV::V2 && src_type != CycloneV::V4 &&
|
||||
src_type != CycloneV::WM;
|
||||
}
|
||||
|
||||
// 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_MISTRAL_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_MISTRAL_CLKENA, id_MISTRAL_CLKBUF)) {
|
||||
route_clk_net(ni);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void Arch::route_globals()
|
||||
{
|
||||
MistralGlobalRouter router(getCtx());
|
||||
router();
|
||||
}
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
|
Loading…
Reference in New Issue
Block a user