From fa1577f6f77b76d62c7e6fadeae31df4354054d5 Mon Sep 17 00:00:00 2001 From: YRabbit Date: Mon, 4 Sep 2023 22:20:08 +1000 Subject: [PATCH 1/2] gowin: Himbaechel. Extend clock router Now the clock router can place a buffer into the specified network, which divides the network into two parts: from the source to the buffer, routing occurs through any available PIPs, and after the buffer to the sink, only through a dedicated global clock network. This is made specifically for the Tangnano20k where the external oscillator is soldered to a regular non-clock pin. But it can be used for other purposes, you just need to remember that the recipient must be a CLK input or output pin. The port/network to set the buffer to is specified in the .CST file: CLOCK_LOC "name" BUFG; Signed-off-by: YRabbit --- himbaechel/uarch/gowin/constids.inc | 4 + himbaechel/uarch/gowin/cst.cc | 12 ++- himbaechel/uarch/gowin/globals.cc | 129 ++++++++++++++++++----- himbaechel/uarch/gowin/gowin.h | 2 + himbaechel/uarch/gowin/gowin_arch_gen.py | 14 +++ himbaechel/uarch/gowin/pack.cc | 62 +++++++++-- 6 files changed, 186 insertions(+), 37 deletions(-) diff --git a/himbaechel/uarch/gowin/constids.inc b/himbaechel/uarch/gowin/constids.inc index d9a7ca2bec..57938b0a4f 100644 --- a/himbaechel/uarch/gowin/constids.inc +++ b/himbaechel/uarch/gowin/constids.inc @@ -1097,3 +1097,7 @@ X(HCLK_OUT1) X(HCLK_OUT2) X(HCLK_OUT3) +// BUFG, clock buffers stuff +X(BUFG) +X(CLOCK) + diff --git a/himbaechel/uarch/gowin/cst.cc b/himbaechel/uarch/gowin/cst.cc index 8821eec8d8..e4df3f6119 100644 --- a/himbaechel/uarch/gowin/cst.cc +++ b/himbaechel/uarch/gowin/cst.cc @@ -129,8 +129,16 @@ struct GowinCstReader log_info("Can't use the long wires. The %s network will use normal routing.\n", net.c_str(ctx)); //} } else { - log_info("BUFG isn't supported\n"); - continue; + auto ni = ctx->nets.find(net); + if (ni == ctx->nets.end()) { + log_info("Net %s not found\n", net.c_str(ctx)); + continue; + } + if (ctx->debug) { + log_info("Mark net '%s' as CLOCK\n", net.c_str(ctx)); + } + // XXX YES for now. May be put the number here + ni->second->attrs[id_CLOCK] = Property(std::string("YES")); } } break; case ioloc: { // IO_LOC name pin diff --git a/himbaechel/uarch/gowin/globals.cc b/himbaechel/uarch/gowin/globals.cc index 86c39e3410..a34d4253f6 100644 --- a/himbaechel/uarch/gowin/globals.cc +++ b/himbaechel/uarch/gowin/globals.cc @@ -67,29 +67,14 @@ struct GowinGlobalRouter // Dedicated backwards BFS routing for global networks template - bool backwards_bfs_route(NetInfo *net, store_index user_idx, int iter_limit, bool strict, Tfilt pip_filter) + bool backwards_bfs_route(NetInfo *net, WireId src, WireId dst, int iter_limit, bool strict, Tfilt pip_filter) { + // log_info("%s:%s->%s\n", net->name.c_str(ctx), ctx->nameOfWire(src), ctx->nameOfWire(dst)); // Queue of wires to visit std::queue visit; // Wire -> upstream pip dict 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; @@ -106,24 +91,29 @@ struct GowinGlobalRouter // 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) + 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) + if (!ctx->checkWireAvail(prev) && ctx->getBoundWireNet(prev) != net) { continue; + } // Skip already visited wires - if (backtrace.count(prev)) + if (backtrace.count(prev)) { continue; + } // Apply our custom pip filter - if (!pip_filter(pip)) + if (!pip_filter(pip)) { continue; + } // Add to the queue visit.push(prev); backtrace[prev] = pip; // Check if we are done yet - if (prev == src) + if (prev == src) { goto done; + } } if (false) { done: @@ -137,18 +127,22 @@ struct GowinGlobalRouter // Create a list of pips on the routed path while (true) { PipId pip = backtrace.at(cursor); - if (pip == PipId()) + if (pip == PipId()) { break; + } pips.push_back(pip); cursor = ctx->getPipDstWire(pip); + // log_info(">> %s:%s\n", ctx->getPipName(pip).str(ctx).c_str(), ctx->nameOfWire(cursor)); } // 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) + // log_info("%s:%s\n", ctx->getPipName(pip).str(ctx).c_str(), ctx->nameOfWire(dst)); + if (ctx->getBoundWireNet(dst) == net) { break; + } ctx->bindPip(pip, net, STRENGTH_LOCKED); } return true; @@ -159,29 +153,90 @@ struct GowinGlobalRouter } else { log_warning("Failed to route net '%s' from %s to %s using dedicated routing.\n", ctx->nameOf(net), ctx->nameOfWire(src), ctx->nameOfWire(dst)); - ctx->unbindWire(src); return false; } } } - void route_clk_net(NetInfo *net) + bool route_direct_net(NetInfo *net) { + // Lookup source and destination wires + WireId src = ctx->getNetinfoSourceWire(net); + 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 (ctx->getBoundWireNet(src) != net) { + ctx->bindWire(src, net, STRENGTH_LOCKED); + } + bool routed = false; for (auto usr : net->users.enumerate()) { - routed = backwards_bfs_route(net, usr.index, 1000000, false, [&](PipId pip) { + WireId dst = ctx->getNetinfoSinkWire(net, net->users.at(usr.index), 0); + if (dst == WireId()) { + log_error("Net '%s' has an invalid sink port %s.%s\n", ctx->nameOf(net), + ctx->nameOf(net->users.at(usr.index).cell), ctx->nameOf(net->users.at(usr.index).port)); + } + routed = backwards_bfs_route(net, src, dst, 1000000, false, [&](PipId pip) { return (is_relaxed_sink(usr.value) || global_pip_filter(pip)); }); if (!routed) { break; } } + if (!routed) { + ctx->unbindWire(src); + } + return routed; + } + + void route_buffered_net(NetInfo *net) + { + // a) route net after buf using the buf input as source + CellInfo *buf_ci = net->driver.cell; + WireId src = ctx->getBelPinWire(buf_ci->bel, id_I); + + NetInfo *net_before_buf = buf_ci->getPort(id_I); + NPNR_ASSERT(net_before_buf != nullptr); - if (routed) { + 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)); + } + ctx->bindWire(src, net, STRENGTH_LOCKED); + + for (auto usr : net->users.enumerate()) { + WireId dst = ctx->getNetinfoSinkWire(net, net->users.at(usr.index), 0); + if (dst == WireId()) { + log_error("Net '%s' has an invalid sink port %s.%s\n", ctx->nameOf(net), + ctx->nameOf(net->users.at(usr.index).cell), ctx->nameOf(net->users.at(usr.index).port)); + } + // log_info(" usr wire: %s\n", ctx->nameOfWire(dst)); + backwards_bfs_route(net, src, dst, 1000000, true, + [&](PipId pip) { return (is_relaxed_sink(usr.value) || global_pip_filter(pip)); }); + } + + // b) route net before buf from whatever to the buf input + WireId dst = src; + CellInfo *true_src_ci = net_before_buf->driver.cell; + src = ctx->getBelPinWire(true_src_ci->bel, net_before_buf->driver.port); + ctx->bindWire(src, net, STRENGTH_LOCKED); + ctx->unbindWire(dst); + backwards_bfs_route(net, src, dst, 1000000, false, [&](PipId pip) { return true; }); + // remove net + buf_ci->movePortTo(id_O, true_src_ci, net_before_buf->driver.port); + net_before_buf->driver.cell = nullptr; + } + + void route_clk_net(NetInfo *net) + { + if (route_direct_net(net)) { log_info(" routed net '%s' using global resources\n", ctx->nameOf(net)); } } + bool driver_is_buf(const PortRef &driver) { return CellTypePort(driver) == CellTypePort(id_BUFG, id_O); } + bool driver_is_clksrc(const PortRef &driver) { // dedicated pins @@ -219,11 +274,27 @@ struct GowinGlobalRouter void run(void) { log_info("Routing globals...\n"); + // buffered nets first + for (auto &net : ctx->nets) { + NetInfo *ni = net.second.get(); + CellInfo *drv = ni->driver.cell; + if (drv == nullptr) { + continue; + } + if (driver_is_buf(ni->driver)) { + if (ctx->verbose) { + log_info("route buffered net '%s'\n", ctx->nameOf(ni)); + } + route_buffered_net(ni); + continue; + } + } for (auto &net : ctx->nets) { NetInfo *ni = net.second.get(); CellInfo *drv = ni->driver.cell; - if (drv == nullptr) + if (drv == nullptr) { continue; + } if (driver_is_clksrc(ni->driver)) { route_clk_net(ni); continue; diff --git a/himbaechel/uarch/gowin/gowin.h b/himbaechel/uarch/gowin/gowin.h index 7086bfdec8..f92049a5a5 100644 --- a/himbaechel/uarch/gowin/gowin.h +++ b/himbaechel/uarch/gowin/gowin.h @@ -88,6 +88,8 @@ enum IDES16_Z = 72, OSER16_Z = 73, + BUFG_Z = 74, // : 81 reserve just in case + OSC_Z = 274, PLL_Z = 275, GSR_Z = 276, diff --git a/himbaechel/uarch/gowin/gowin_arch_gen.py b/himbaechel/uarch/gowin/gowin_arch_gen.py index a074685c46..be507f384e 100644 --- a/himbaechel/uarch/gowin/gowin_arch_gen.py +++ b/himbaechel/uarch/gowin/gowin_arch_gen.py @@ -32,6 +32,8 @@ IDES16_Z = 72 OSER16_Z = 73 +BUFG_Z = 74 # : 81 reserve just in case + OSC_Z = 274 PLL_Z = 275 GSR_Z = 276 @@ -291,6 +293,18 @@ def create_extra_funcs(tt: TileType, db: chipdb, x: int, y: int): tt.add_bel_pin(bel, port, wire, PinType.OUTPUT) else: tt.add_bel_pin(bel, port, wire, PinType.INPUT) + if func == 'buf': + for buf_type, wires in desc.items(): + for i, wire in enumerate(wires): + if not tt.has_wire(wire): + tt.create_wire(wire, "TILE_CLK") + wire_out = f'{buf_type}{i}_O' + tt.create_wire(wire_out, "TILE_CLK") + # XXX make Z from buf_type + bel = tt.create_bel(f'{buf_type}{i}', buf_type, z = BUFG_Z + i) + bel.flags = BEL_FLAG_GLOBAL + tt.add_bel_pin(bel, "I", wire, PinType.INPUT) + tt.add_bel_pin(bel, "O", wire_out, PinType.OUTPUT) def create_tiletype(create_func, chip: Chip, db: chipdb, x: int, y: int, ttyp: int): has_extra_func = (y, x) in db.extra_func diff --git a/himbaechel/uarch/gowin/pack.cc b/himbaechel/uarch/gowin/pack.cc index 3529c230e7..e8cfecc73d 100644 --- a/himbaechel/uarch/gowin/pack.cc +++ b/himbaechel/uarch/gowin/pack.cc @@ -110,7 +110,7 @@ struct GowinPacker auto &ci = *cell.second; if (!ci.type.in(ctx->id("$nextpnr_ibuf"), ctx->id("$nextpnr_obuf"), ctx->id("$nextpnr_iobuf"))) continue; - NetInfo *i = ci.getPort(ctx->id("I")); + NetInfo *i = ci.getPort(id_I); if (i && i->driver.cell) { if (!top_ports.count(CellTypePort(i->driver))) log_error("Top-level port '%s' driven by illegal port %s.%s\n", ctx->nameOf(&ci), @@ -119,7 +119,7 @@ struct GowinPacker i->driver.cell->attrs[attr.first] = attr.second; } } - NetInfo *o = ci.getPort(ctx->id("O")); + NetInfo *o = ci.getPort(id_O); if (o) { for (auto &usr : o->users) { if (!top_ports.count(CellTypePort(usr))) @@ -128,9 +128,21 @@ struct GowinPacker for (const auto &attr : ci.attrs) { usr.cell->attrs[attr.first] = attr.second; } + // network/port attributes that can be set in the + // restriction file and that need to be transferred to real + // networks before nextpnr buffers are removed. + NetInfo *dst_net = usr.cell->getPort(id_O); + if (dst_net != nullptr) { + for (const auto &attr : o->attrs) { + if (!attr.first.in(id_CLOCK)) { + continue; + } + dst_net->attrs[attr.first] = attr.second; + } + } } } - NetInfo *io = ci.getPort(ctx->id("IO")); + NetInfo *io = ci.getPort(id_IO); if (io && io->driver.cell) { if (!top_ports.count(CellTypePort(io->driver))) log_error("Top-level port '%s' driven by illegal port %s.%s\n", ctx->nameOf(&ci), @@ -139,9 +151,9 @@ struct GowinPacker io->driver.cell->attrs[attr.first] = attr.second; } } - ci.disconnectPort(ctx->id("I")); - ci.disconnectPort(ctx->id("O")); - ci.disconnectPort(ctx->id("IO")); + ci.disconnectPort(id_I); + ci.disconnectPort(id_O); + ci.disconnectPort(id_IO); to_remove.push_back(ci.name); } for (IdString cell_name : to_remove) @@ -1317,6 +1329,41 @@ struct GowinPacker } } + // ========================================= + // Create entry points to the clock system + // ========================================= + void pack_buffered_nets() + { + log_info("Pack buffered nets..\n"); + + for (auto &net : ctx->nets) { + auto &ni = *net.second; + if (ni.driver.cell == nullptr || ni.attrs.count(id_CLOCK) == 0 || ni.users.empty()) { + continue; + } + + // make new BUF cell single user for the net driver + IdString buf_name = ctx->idf("%s_BUFG", net.first.c_str(ctx)); + ctx->createCell(buf_name, id_BUFG); + CellInfo *buf_ci = ctx->cells.at(buf_name).get(); + buf_ci->addInput(id_I); + auto s_net = std::make_unique(ctx->idf("$PACKER_BUF_%s", net.first.c_str(ctx))); + NetInfo *buf_ni = s_net.get(); + ctx->nets[s_net->name] = std::move(s_net); + + if (ctx->verbose) { + log_info("Create buf '%s' with IN net '%s'\n", buf_name.c_str(ctx), buf_ni->name.c_str(ctx)); + } + // move driver + CellInfo *driver_cell = ni.driver.cell; + IdString driver_port = ni.driver.port; + + driver_cell->movePortTo(driver_port, buf_ci, id_O); + buf_ci->connectPort(id_I, buf_ni); + driver_cell->connectPort(driver_port, buf_ni); + } + } + void run(void) { handle_constants(); @@ -1348,6 +1395,9 @@ struct GowinPacker ctx->check(); pack_ram16sdp4(); + ctx->check(); + + pack_buffered_nets(); ctx->fixupHierarchy(); ctx->check(); From 814cf2ad38f9d987201648fee23de7338c4e4338 Mon Sep 17 00:00:00 2001 From: YRabbit Date: Thu, 7 Sep 2023 19:04:42 +1000 Subject: [PATCH 2/2] gowin: Himbaechel. Use a more appropriate function Signed-off-by: YRabbit --- himbaechel/uarch/gowin/pack.cc | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/himbaechel/uarch/gowin/pack.cc b/himbaechel/uarch/gowin/pack.cc index e8cfecc73d..55cc24422f 100644 --- a/himbaechel/uarch/gowin/pack.cc +++ b/himbaechel/uarch/gowin/pack.cc @@ -1347,9 +1347,7 @@ struct GowinPacker ctx->createCell(buf_name, id_BUFG); CellInfo *buf_ci = ctx->cells.at(buf_name).get(); buf_ci->addInput(id_I); - auto s_net = std::make_unique(ctx->idf("$PACKER_BUF_%s", net.first.c_str(ctx))); - NetInfo *buf_ni = s_net.get(); - ctx->nets[s_net->name] = std::move(s_net); + NetInfo *buf_ni = ctx->createNet(ctx->idf("$PACKER_BUF_%s", net.first.c_str(ctx))); if (ctx->verbose) { log_info("Create buf '%s' with IN net '%s'\n", buf_name.c_str(ctx), buf_ni->name.c_str(ctx));