mirror of https://github.com/YosysHQ/nextpnr.git
GateMate clock router (#1483)
* gatemate: clock router Co-authored-by: Miodrag Milanovic <mmicko@gmail.com> * Re-add clock router pip binding * Refactoring * Require globals to use a BUFG * Fix misunderstanding of GPIO/RAM clocking * Add plane info to chipdb * Force clock routing along a specific plane * Remove overly-limiting condition * Move clock router into its own file * Clock router based on delay * Refine clock router conditions * More detailed clock routing output * Clean up debug messages * clangformat --------- Co-authored-by: Miodrag Milanovic <mmicko@gmail.com>
This commit is contained in:
parent
b1c147083d
commit
46fbe7c6d7
|
|
@ -18,6 +18,7 @@ set(SOURCES
|
|||
pack_serdes.cc
|
||||
pack.h
|
||||
pll.cc
|
||||
route_clock.cc
|
||||
)
|
||||
|
||||
set(TEST_SOURCES
|
||||
|
|
|
|||
|
|
@ -37,6 +37,9 @@ NPNR_PACKED_STRUCT(struct GateMatePipExtraDataPOD {
|
|||
uint8_t value;
|
||||
uint8_t flags;
|
||||
uint8_t type;
|
||||
uint8_t plane;
|
||||
uint8_t dummy1;
|
||||
uint16_t dummy2;
|
||||
});
|
||||
|
||||
NPNR_PACKED_STRUCT(struct GateMateBelPinConstraintPOD {
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
#include "gatemate.h"
|
||||
#include "design_utils.h"
|
||||
#include "log.h"
|
||||
#include "placer_heap.h"
|
||||
|
||||
#define GEN_INIT_CONSTIDS
|
||||
|
|
@ -264,6 +265,8 @@ void GateMateImpl::postPlace()
|
|||
}
|
||||
}
|
||||
|
||||
void GateMateImpl::preRoute() { route_clock(); }
|
||||
|
||||
void GateMateImpl::postRoute()
|
||||
{
|
||||
ctx->assignArchInfo();
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ struct GateMateImpl : HimbaechelAPI
|
|||
|
||||
void prePlace() override;
|
||||
void postPlace() override;
|
||||
void preRoute() override;
|
||||
void postRoute() override;
|
||||
|
||||
bool isBelLocationValid(BelId bel, bool explain_invalid = false) const override;
|
||||
|
|
@ -78,6 +79,7 @@ struct GateMateImpl : HimbaechelAPI
|
|||
void update_cpe_inv(CellInfo *cell, IdString port, IdString param);
|
||||
void update_cpe_mux(CellInfo *cell, IdString port, IdString param, int bit);
|
||||
void rename_param(CellInfo *cell, IdString name, IdString new_name, int width);
|
||||
void route_clock();
|
||||
|
||||
const GateMateBelExtraDataPOD *bel_extra_data(BelId bel) const;
|
||||
|
||||
|
|
|
|||
|
|
@ -63,6 +63,7 @@ class PipExtraData(BBAStruct):
|
|||
bits: int = 0
|
||||
value: int = 0
|
||||
invert: int = 0
|
||||
plane: int = 0
|
||||
|
||||
def serialise_lists(self, context: str, bba: BBAWriter):
|
||||
pass
|
||||
|
|
@ -72,6 +73,9 @@ class PipExtraData(BBAStruct):
|
|||
bba.u8(self.value)
|
||||
bba.u8(self.invert)
|
||||
bba.u8(self.pip_type)
|
||||
bba.u8(self.plane)
|
||||
bba.u8(0)
|
||||
bba.u16(0)
|
||||
|
||||
@dataclass
|
||||
class BelPinConstraint(BBAStruct):
|
||||
|
|
@ -169,7 +173,14 @@ def main():
|
|||
mux_flags = MUX_INVERT if mux.invert else 0
|
||||
mux_flags |= MUX_VISIBLE if mux.visible else 0
|
||||
mux_flags |= MUX_CONFIG if mux.config else 0
|
||||
pp.extra_data = PipExtraData(PIP_EXTRA_MUX, ch.strs.id(mux.name), mux.bits, mux.value, mux_flags)
|
||||
plane = 0
|
||||
if mux.name.startswith("IM"):
|
||||
plane = int(mux.name[4:6])
|
||||
if mux.name.startswith("SB_SML") or mux.name.startswith("SB_BIG"):
|
||||
plane = int(mux.name[8:10])
|
||||
if mux.name.startswith("SB_DRIVE"):
|
||||
plane = int(mux.name[10:12])
|
||||
pp.extra_data = PipExtraData(PIP_EXTRA_MUX, ch.strs.id(mux.name), mux.bits, mux.value, mux_flags, plane)
|
||||
if "GPIO" in type_name:
|
||||
tt.create_pip("GPIO.DI", "GPIO.IN1")
|
||||
tt.create_pip("GPIO.DI", "GPIO.IN2")
|
||||
|
|
|
|||
|
|
@ -0,0 +1,162 @@
|
|||
/*
|
||||
* nextpnr -- Next Generation Place and Route
|
||||
*
|
||||
* Copyright (C) 2024 The Project Peppercorn Authors.
|
||||
*
|
||||
* 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 <functional>
|
||||
#include <queue>
|
||||
|
||||
#include "gatemate.h"
|
||||
#include "log.h"
|
||||
#include "nextpnr_assertions.h"
|
||||
|
||||
#define HIMBAECHEL_CONSTIDS "uarch/gatemate/constids.inc"
|
||||
#include "himbaechel_constids.h"
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
namespace {
|
||||
|
||||
struct QueuedWire
|
||||
{
|
||||
explicit QueuedWire(WireId wire, float delay = 0.0) : wire{wire}, delay{delay} {};
|
||||
|
||||
WireId wire;
|
||||
float delay;
|
||||
|
||||
bool operator>(const QueuedWire &rhs) const { return this->delay > rhs.delay; }
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
void GateMateImpl::route_clock()
|
||||
{
|
||||
auto clk_nets = std::vector<NetInfo *>{};
|
||||
|
||||
auto feeds_clk_port = [](PortRef &port) {
|
||||
return port.cell->type.in(id_CPE_HALF, id_CPE_HALF_L, id_CPE_HALF_U) && port.port.in(id_CLK);
|
||||
};
|
||||
|
||||
auto pip_plane = [&](PipId pip) {
|
||||
const auto &extra_data =
|
||||
*reinterpret_cast<const GateMatePipExtraDataPOD *>(chip_pip_info(ctx->chip_info, pip).extra_data.get());
|
||||
if (extra_data.type != PipExtra::PIP_EXTRA_MUX)
|
||||
return uint8_t{0};
|
||||
return extra_data.plane;
|
||||
};
|
||||
|
||||
for (auto &net : ctx->nets) {
|
||||
NetInfo *glb_net = net.second.get();
|
||||
if (!glb_net->driver.cell || glb_net->driver.cell->type != id_BUFG)
|
||||
continue;
|
||||
|
||||
for (auto &usr : glb_net->users) {
|
||||
if (feeds_clk_port(usr)) {
|
||||
clk_nets.push_back(glb_net);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log_info("Routing clock nets...\n");
|
||||
for (auto glb_net : clk_nets) {
|
||||
log_info(" routing net '%s'\n", glb_net->name.c_str(ctx));
|
||||
ctx->bindWire(ctx->getNetinfoSourceWire(glb_net), glb_net, STRENGTH_LOCKED);
|
||||
|
||||
auto bufg_idx = ctx->getBelLocation(glb_net->driver.cell->bel).z;
|
||||
|
||||
for (auto &usr : glb_net->users) {
|
||||
std::priority_queue<QueuedWire, std::vector<QueuedWire>, std::greater<QueuedWire>> visit;
|
||||
dict<WireId, PipId> backtrace;
|
||||
WireId dest = WireId();
|
||||
// skip arcs that are not part of lowskew routing
|
||||
if (!feeds_clk_port(usr))
|
||||
continue;
|
||||
|
||||
auto cpe_loc = ctx->getBelLocation(usr.cell->bel);
|
||||
|
||||
auto sink_wire = ctx->getNetinfoSinkWire(glb_net, usr, 0);
|
||||
if (ctx->debug) {
|
||||
auto sink_wire_name = "(uninitialized)";
|
||||
if (sink_wire != WireId())
|
||||
sink_wire_name = ctx->nameOfWire(sink_wire);
|
||||
log_info(" routing arc to %s.%s (wire %s):\n", usr.cell->name.c_str(ctx), usr.port.c_str(ctx),
|
||||
sink_wire_name);
|
||||
}
|
||||
visit.push(QueuedWire(sink_wire));
|
||||
while (!visit.empty()) {
|
||||
QueuedWire curr = visit.top();
|
||||
visit.pop();
|
||||
if (curr.wire == ctx->getNetinfoSourceWire(glb_net)) {
|
||||
if (ctx->debug)
|
||||
log_info(" (%.3fns)\n", curr.delay);
|
||||
dest = curr.wire;
|
||||
break;
|
||||
}
|
||||
|
||||
PipId bound_pip;
|
||||
auto fnd_wire = glb_net->wires.find(curr.wire);
|
||||
if (fnd_wire != glb_net->wires.end()) {
|
||||
bound_pip = fnd_wire->second.pip;
|
||||
}
|
||||
|
||||
for (auto uh : ctx->getPipsUphill(curr.wire)) {
|
||||
if (!ctx->checkPipAvailForNet(uh, glb_net))
|
||||
continue;
|
||||
WireId src = ctx->getPipSrcWire(uh);
|
||||
if (backtrace.count(src))
|
||||
continue;
|
||||
if (!ctx->checkWireAvail(src) && ctx->getBoundWireNet(src) != glb_net)
|
||||
continue;
|
||||
if (bound_pip != PipId() && uh != bound_pip)
|
||||
continue;
|
||||
auto pip_loc = ctx->getPipLocation(uh);
|
||||
// Use only a specific plane to minimise congestion.
|
||||
if ((pip_loc.x != cpe_loc.x || pip_loc.y != cpe_loc.y) && pip_plane(uh) != (9 + bufg_idx))
|
||||
continue;
|
||||
backtrace[src] = uh;
|
||||
auto delay = ctx->getDelayNS(ctx->getPipDelay(uh).maxDelay() + ctx->getWireDelay(src).maxDelay() +
|
||||
ctx->getDelayEpsilon());
|
||||
visit.push(QueuedWire(src, curr.delay + delay));
|
||||
}
|
||||
}
|
||||
if (dest == WireId()) {
|
||||
log_info(" failed to find a route using dedicated resources. %s -> %s\n",
|
||||
glb_net->driver.cell->name.c_str(ctx), usr.cell->name.c_str(ctx));
|
||||
}
|
||||
while (backtrace.count(dest)) {
|
||||
auto uh = backtrace[dest];
|
||||
dest = ctx->getPipDstWire(uh);
|
||||
if (ctx->getBoundWireNet(dest) == glb_net) {
|
||||
NPNR_ASSERT(glb_net->wires.at(dest).pip == uh);
|
||||
if (ctx->debug)
|
||||
log_info(" pip %s --> %s (plane %hhd)\n", ctx->nameOfPip(uh),
|
||||
ctx->nameOfWire(dest), pip_plane(uh));
|
||||
} else {
|
||||
ctx->bindPip(uh, glb_net, STRENGTH_LOCKED);
|
||||
if (ctx->debug)
|
||||
log_info(" bind pip %s --> %s (plane %hhd)\n", ctx->nameOfPip(uh),
|
||||
ctx->nameOfWire(dest), pip_plane(uh));
|
||||
}
|
||||
if (dest == sink_wire)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
Loading…
Reference in New Issue