diff --git a/himbaechel/uarch/gatemate/CMakeLists.txt b/himbaechel/uarch/gatemate/CMakeLists.txt index 0e73bff0..6977ce9b 100644 --- a/himbaechel/uarch/gatemate/CMakeLists.txt +++ b/himbaechel/uarch/gatemate/CMakeLists.txt @@ -18,6 +18,7 @@ set(SOURCES pack_serdes.cc pack.h pll.cc + route_clock.cc ) set(TEST_SOURCES diff --git a/himbaechel/uarch/gatemate/extra_data.h b/himbaechel/uarch/gatemate/extra_data.h index 8ecbd243..8342ef5c 100644 --- a/himbaechel/uarch/gatemate/extra_data.h +++ b/himbaechel/uarch/gatemate/extra_data.h @@ -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 { diff --git a/himbaechel/uarch/gatemate/gatemate.cc b/himbaechel/uarch/gatemate/gatemate.cc index da90bf38..537dcd43 100644 --- a/himbaechel/uarch/gatemate/gatemate.cc +++ b/himbaechel/uarch/gatemate/gatemate.cc @@ -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(); diff --git a/himbaechel/uarch/gatemate/gatemate.h b/himbaechel/uarch/gatemate/gatemate.h index 20a5ec16..d59a8021 100644 --- a/himbaechel/uarch/gatemate/gatemate.h +++ b/himbaechel/uarch/gatemate/gatemate.h @@ -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; diff --git a/himbaechel/uarch/gatemate/gen/arch_gen.py b/himbaechel/uarch/gatemate/gen/arch_gen.py index 55c98443..cf66c5e5 100644 --- a/himbaechel/uarch/gatemate/gen/arch_gen.py +++ b/himbaechel/uarch/gatemate/gen/arch_gen.py @@ -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") diff --git a/himbaechel/uarch/gatemate/route_clock.cc b/himbaechel/uarch/gatemate/route_clock.cc new file mode 100644 index 00000000..ed291d5c --- /dev/null +++ b/himbaechel/uarch/gatemate/route_clock.cc @@ -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 +#include + +#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{}; + + 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(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, std::greater> visit; + dict 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