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 <rabbit@yrabbit.cyou>
This commit is contained in:
parent
f9825c3130
commit
78ee20b5da
@ -1097,3 +1097,7 @@ X(HCLK_OUT1)
|
||||
X(HCLK_OUT2)
|
||||
X(HCLK_OUT3)
|
||||
|
||||
// BUFG, clock buffers stuff
|
||||
X(BUFG)
|
||||
X(CLOCK)
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
@ -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;
|
||||
}
|
||||
|
||||
if (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 (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)
|
||||
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) {
|
||||
continue;
|
||||
}
|
||||
if (driver_is_clksrc(ni->driver)) {
|
||||
route_clk_net(ni);
|
||||
continue;
|
||||
|
@ -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,
|
||||
|
@ -32,6 +32,8 @@ IOLOGICA_Z = 70
|
||||
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
|
||||
|
@ -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<NetInfo>(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();
|
||||
|
Loading…
Reference in New Issue
Block a user