Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gowin: Himbaechel. Extend clock router #1208

Merged
merged 2 commits into from
Sep 8, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions himbaechel/uarch/gowin/constids.inc
Original file line number Diff line number Diff line change
Expand Up @@ -1097,3 +1097,7 @@ X(HCLK_OUT1)
X(HCLK_OUT2)
X(HCLK_OUT3)

// BUFG, clock buffers stuff
X(BUFG)
X(CLOCK)

12 changes: 10 additions & 2 deletions himbaechel/uarch/gowin/cst.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
129 changes: 100 additions & 29 deletions himbaechel/uarch/gowin/globals.cc
Original file line number Diff line number Diff line change
Expand Up @@ -67,29 +67,14 @@ struct GowinGlobalRouter

// 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)
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<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;
Expand All @@ -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:
Expand All @@ -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;
Expand All @@ -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
Expand Down Expand Up @@ -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;
Expand Down
2 changes: 2 additions & 0 deletions himbaechel/uarch/gowin/gowin.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
14 changes: 14 additions & 0 deletions himbaechel/uarch/gowin/gowin_arch_gen.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
62 changes: 56 additions & 6 deletions himbaechel/uarch/gowin/pack.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand All @@ -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)))
Expand All @@ -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),
Expand All @@ -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)
Expand Down Expand Up @@ -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<NetInfo>(ctx->idf("$PACKER_BUF_%s", net.first.c_str(ctx)));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you use ctx->createNet here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

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();
Expand Down Expand Up @@ -1348,6 +1395,9 @@ struct GowinPacker
ctx->check();

pack_ram16sdp4();
ctx->check();

pack_buffered_nets();

ctx->fixupHierarchy();
ctx->check();
Expand Down