Skip to content

Commit e3529d3

Browse files
committed
machxo2: Global placement and clock routing from nexus
Signed-off-by: gatecat <[email protected]>
1 parent 9177189 commit e3529d3

File tree

6 files changed

+450
-66
lines changed

6 files changed

+450
-66
lines changed

.github/workflows/arch_ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ jobs:
1515
DEPS_PATH: ${{ github.workspace }}/deps
1616
YOSYS_REVISION: bd7ee79486d4e8788f36de8c25a3fb2df451d682
1717
ICESTORM_REVISION: 9f66f9ce16941c6417813cb87653c735a78b53ae
18-
TRELLIS_REVISION: c99b22d35e4109333e7549e56cec139569a17392
18+
TRELLIS_REVISION: f1e5710099313d2e1862d4ef2582293f6b7ee122
1919
PRJOXIDE_REVISION: c3fb1526cf4a2165e15b74f4a994d153c7695fe4
2020
MISTRAL_REVISION: ebfc0dd2cc7d6d2159b641a397c88554840e93c9
2121
APYCULA_REVISION: 0.5.1a1

machxo2/arch.cc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -433,6 +433,8 @@ bool Arch::route()
433433

434434
assignArchInfo();
435435

436+
route_globals();
437+
436438
bool result;
437439
if (router == "router1") {
438440
result = router1(getCtx(), Router1Cfg(getCtx()));

machxo2/arch.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -967,6 +967,9 @@ struct Arch : BaseArch<ArchRanges>
967967
// Apply LPF constraints to the context
968968
bool apply_lpf(std::string filename, std::istream &in);
969969

970+
// Global clock routing
971+
void route_globals();
972+
970973
static const std::string defaultPlacer;
971974
static const std::vector<std::string> availablePlacers;
972975
static const std::string defaultRouter;

machxo2/bitstream.cc

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,9 +86,14 @@ struct MachXO2Bitgen
8686
// clang-format on
8787
};
8888

89-
if (prefix2 == "G_" || prefix2 == "L_" || prefix2 == "R_" || prefix7 == "BRANCH_")
89+
if (prefix2 == "G_" || prefix7 == "BRANCH_")
9090
return basename;
9191

92+
if (prefix2 == "L_" || prefix2 == "R_") {
93+
if (loc.x == 0 || loc.x == (ctx->getGridDimX() - 1))
94+
return "G_" + basename.substr(2);
95+
return basename;
96+
}
9297
if (prefix2 == "U_" || prefix2 == "D_") {
9398
// We needded to keep U_ and D_ prefixes to generate the routing
9499
// graph connections properly, but in truth they are not relevant

machxo2/globals.cc

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
/*
2+
* nextpnr -- Next Generation Place and Route
3+
*
4+
* Copyright (C) 2020-23 gatecat <[email protected]>
5+
*
6+
*
7+
* Permission to use, copy, modify, and/or distribute this software for any
8+
* purpose with or without fee is hereby granted, provided that the above
9+
* copyright notice and this permission notice appear in all copies.
10+
*
11+
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12+
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13+
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14+
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15+
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16+
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17+
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18+
*
19+
*/
20+
21+
#include "log.h"
22+
#include "nextpnr.h"
23+
#include "util.h"
24+
25+
#include <queue>
26+
27+
NEXTPNR_NAMESPACE_BEGIN
28+
29+
struct MachxoGlobalRouter
30+
{
31+
Context *ctx;
32+
33+
MachxoGlobalRouter(Context *ctx) : ctx(ctx){};
34+
35+
// When routing globals; we allow global->local for some tricky cases but never local->local
36+
bool global_pip_filter(PipId pip) const
37+
{
38+
WireId src = ctx->getPipSrcWire(pip);
39+
const char *s = ctx->tile_info(src)->wire_data[src.index].name.get();
40+
if ((s[0] == 'H' || s[0] == 'V') && s[1] == '0')
41+
return false;
42+
return true;
43+
}
44+
45+
// Dedicated backwards BFS routing for global networks
46+
template <typename Tfilt>
47+
bool backwards_bfs_route(NetInfo *net, store_index<PortRef> user_idx, int iter_limit, bool strict, Tfilt pip_filter)
48+
{
49+
// Queue of wires to visit
50+
std::queue<WireId> visit;
51+
// Wire -> upstream pip
52+
dict<WireId, PipId> backtrace;
53+
54+
// Lookup source and destination wires
55+
WireId src = ctx->getNetinfoSourceWire(net);
56+
WireId dst = ctx->getNetinfoSinkWire(net, net->users.at(user_idx), 0);
57+
58+
if (src == WireId())
59+
log_error("Net '%s' has an invalid source port %s.%s\n", ctx->nameOf(net), ctx->nameOf(net->driver.cell),
60+
ctx->nameOf(net->driver.port));
61+
62+
if (dst == WireId())
63+
log_error("Net '%s' has an invalid sink port %s.%s\n", ctx->nameOf(net),
64+
ctx->nameOf(net->users.at(user_idx).cell), ctx->nameOf(net->users.at(user_idx).port));
65+
66+
if (ctx->getBoundWireNet(src) != net)
67+
ctx->bindWire(src, net, STRENGTH_LOCKED);
68+
69+
if (src == dst) {
70+
// Nothing more to do
71+
return true;
72+
}
73+
74+
visit.push(dst);
75+
backtrace[dst] = PipId();
76+
77+
int iter = 0;
78+
79+
while (!visit.empty() && (iter++ < iter_limit)) {
80+
WireId cursor = visit.front();
81+
visit.pop();
82+
// Search uphill pips
83+
for (PipId pip : ctx->getPipsUphill(cursor)) {
84+
// Skip pip if unavailable, and not because it's already used for this net
85+
if (!ctx->checkPipAvail(pip) && ctx->getBoundPipNet(pip) != net)
86+
continue;
87+
WireId prev = ctx->getPipSrcWire(pip);
88+
// Ditto for the upstream wire
89+
if (!ctx->checkWireAvail(prev) && ctx->getBoundWireNet(prev) != net)
90+
continue;
91+
// Skip already visited wires
92+
if (backtrace.count(prev))
93+
continue;
94+
// Apply our custom pip filter
95+
if (!pip_filter(pip))
96+
continue;
97+
// Add to the queue
98+
visit.push(prev);
99+
backtrace[prev] = pip;
100+
// Check if we are done yet
101+
if (prev == src)
102+
goto done;
103+
}
104+
if (false) {
105+
done:
106+
break;
107+
}
108+
}
109+
110+
if (backtrace.count(src)) {
111+
WireId cursor = src;
112+
std::vector<PipId> pips;
113+
// Create a list of pips on the routed path
114+
while (true) {
115+
PipId pip = backtrace.at(cursor);
116+
if (pip == PipId())
117+
break;
118+
pips.push_back(pip);
119+
cursor = ctx->getPipDstWire(pip);
120+
}
121+
// Reverse that list
122+
std::reverse(pips.begin(), pips.end());
123+
// Bind pips until we hit already-bound routing
124+
for (PipId pip : pips) {
125+
WireId dst = ctx->getPipDstWire(pip);
126+
if (ctx->getBoundWireNet(dst) == net)
127+
break;
128+
ctx->bindPip(pip, net, STRENGTH_LOCKED);
129+
}
130+
return true;
131+
} else {
132+
if (strict)
133+
log_error("Failed to route net '%s' from %s to %s using dedicated routing.\n", ctx->nameOf(net),
134+
ctx->nameOfWire(src), ctx->nameOfWire(dst));
135+
return false;
136+
}
137+
}
138+
139+
bool is_relaxed_sink(const PortRef &sink) const
140+
{
141+
// Cases where global clocks are driving fabric
142+
if ((sink.cell->type == id_TRELLIS_COMB && sink.port != id_WCK) ||
143+
(sink.cell->type == id_TRELLIS_FF && sink.port != id_CLK))
144+
return true;
145+
return false;
146+
}
147+
148+
void route_clk_net(NetInfo *net)
149+
{
150+
for (auto usr : net->users.enumerate())
151+
backwards_bfs_route(net, usr.index, 1000000, true,
152+
[&](PipId pip) { return (is_relaxed_sink(usr.value) || global_pip_filter(pip)); });
153+
log_info(" routed net '%s' using global resources\n", ctx->nameOf(net));
154+
}
155+
156+
void operator()()
157+
{
158+
log_info("Routing globals...\n");
159+
for (auto &net : ctx->nets) {
160+
NetInfo *ni = net.second.get();
161+
CellInfo *drv = ni->driver.cell;
162+
if (drv == nullptr)
163+
continue;
164+
if (drv->type.in(id_DCCA, id_DCMA)) {
165+
route_clk_net(ni);
166+
continue;
167+
}
168+
}
169+
}
170+
};
171+
172+
void Arch::route_globals()
173+
{
174+
MachxoGlobalRouter glb_router(getCtx());
175+
glb_router();
176+
}
177+
178+
NEXTPNR_NAMESPACE_END

0 commit comments

Comments
 (0)