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_OUT2)
|
||||||
X(HCLK_OUT3)
|
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));
|
log_info("Can't use the long wires. The %s network will use normal routing.\n", net.c_str(ctx));
|
||||||
//}
|
//}
|
||||||
} else {
|
} else {
|
||||||
log_info("BUFG isn't supported\n");
|
auto ni = ctx->nets.find(net);
|
||||||
continue;
|
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;
|
} break;
|
||||||
case ioloc: { // IO_LOC name pin
|
case ioloc: { // IO_LOC name pin
|
||||||
|
@ -67,29 +67,14 @@ struct GowinGlobalRouter
|
|||||||
|
|
||||||
// Dedicated backwards BFS routing for global networks
|
// Dedicated backwards BFS routing for global networks
|
||||||
template <typename Tfilt>
|
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
|
// Queue of wires to visit
|
||||||
std::queue<WireId> visit;
|
std::queue<WireId> visit;
|
||||||
// Wire -> upstream pip
|
// Wire -> upstream pip
|
||||||
dict<WireId, PipId> backtrace;
|
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) {
|
if (src == dst) {
|
||||||
// Nothing more to do
|
// Nothing more to do
|
||||||
return true;
|
return true;
|
||||||
@ -106,24 +91,29 @@ struct GowinGlobalRouter
|
|||||||
// Search uphill pips
|
// Search uphill pips
|
||||||
for (PipId pip : ctx->getPipsUphill(cursor)) {
|
for (PipId pip : ctx->getPipsUphill(cursor)) {
|
||||||
// Skip pip if unavailable, and not because it's already used for this net
|
// 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;
|
continue;
|
||||||
|
}
|
||||||
WireId prev = ctx->getPipSrcWire(pip);
|
WireId prev = ctx->getPipSrcWire(pip);
|
||||||
// Ditto for the upstream wire
|
// Ditto for the upstream wire
|
||||||
if (!ctx->checkWireAvail(prev) && ctx->getBoundWireNet(prev) != net)
|
if (!ctx->checkWireAvail(prev) && ctx->getBoundWireNet(prev) != net) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
// Skip already visited wires
|
// Skip already visited wires
|
||||||
if (backtrace.count(prev))
|
if (backtrace.count(prev)) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
// Apply our custom pip filter
|
// Apply our custom pip filter
|
||||||
if (!pip_filter(pip))
|
if (!pip_filter(pip)) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
// Add to the queue
|
// Add to the queue
|
||||||
visit.push(prev);
|
visit.push(prev);
|
||||||
backtrace[prev] = pip;
|
backtrace[prev] = pip;
|
||||||
// Check if we are done yet
|
// Check if we are done yet
|
||||||
if (prev == src)
|
if (prev == src) {
|
||||||
goto done;
|
goto done;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (false) {
|
if (false) {
|
||||||
done:
|
done:
|
||||||
@ -137,18 +127,22 @@ struct GowinGlobalRouter
|
|||||||
// Create a list of pips on the routed path
|
// Create a list of pips on the routed path
|
||||||
while (true) {
|
while (true) {
|
||||||
PipId pip = backtrace.at(cursor);
|
PipId pip = backtrace.at(cursor);
|
||||||
if (pip == PipId())
|
if (pip == PipId()) {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
pips.push_back(pip);
|
pips.push_back(pip);
|
||||||
cursor = ctx->getPipDstWire(pip);
|
cursor = ctx->getPipDstWire(pip);
|
||||||
|
// log_info(">> %s:%s\n", ctx->getPipName(pip).str(ctx).c_str(), ctx->nameOfWire(cursor));
|
||||||
}
|
}
|
||||||
// Reverse that list
|
// Reverse that list
|
||||||
std::reverse(pips.begin(), pips.end());
|
std::reverse(pips.begin(), pips.end());
|
||||||
// Bind pips until we hit already-bound routing
|
// Bind pips until we hit already-bound routing
|
||||||
for (PipId pip : pips) {
|
for (PipId pip : pips) {
|
||||||
WireId dst = ctx->getPipDstWire(pip);
|
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;
|
break;
|
||||||
|
}
|
||||||
ctx->bindPip(pip, net, STRENGTH_LOCKED);
|
ctx->bindPip(pip, net, STRENGTH_LOCKED);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -159,29 +153,90 @@ struct GowinGlobalRouter
|
|||||||
} else {
|
} else {
|
||||||
log_warning("Failed to route net '%s' from %s to %s using dedicated routing.\n", ctx->nameOf(net),
|
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->nameOfWire(src), ctx->nameOfWire(dst));
|
||||||
ctx->unbindWire(src);
|
|
||||||
return false;
|
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;
|
bool routed = false;
|
||||||
for (auto usr : net->users.enumerate()) {
|
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));
|
return (is_relaxed_sink(usr.value) || global_pip_filter(pip));
|
||||||
});
|
});
|
||||||
if (!routed) {
|
if (!routed) {
|
||||||
break;
|
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));
|
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)
|
bool driver_is_clksrc(const PortRef &driver)
|
||||||
{
|
{
|
||||||
// dedicated pins
|
// dedicated pins
|
||||||
@ -219,11 +274,27 @@ struct GowinGlobalRouter
|
|||||||
void run(void)
|
void run(void)
|
||||||
{
|
{
|
||||||
log_info("Routing globals...\n");
|
log_info("Routing globals...\n");
|
||||||
|
// buffered nets first
|
||||||
for (auto &net : ctx->nets) {
|
for (auto &net : ctx->nets) {
|
||||||
NetInfo *ni = net.second.get();
|
NetInfo *ni = net.second.get();
|
||||||
CellInfo *drv = ni->driver.cell;
|
CellInfo *drv = ni->driver.cell;
|
||||||
if (drv == nullptr)
|
if (drv == nullptr) {
|
||||||
continue;
|
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)) {
|
if (driver_is_clksrc(ni->driver)) {
|
||||||
route_clk_net(ni);
|
route_clk_net(ni);
|
||||||
continue;
|
continue;
|
||||||
|
@ -88,6 +88,8 @@ enum
|
|||||||
IDES16_Z = 72,
|
IDES16_Z = 72,
|
||||||
OSER16_Z = 73,
|
OSER16_Z = 73,
|
||||||
|
|
||||||
|
BUFG_Z = 74, // : 81 reserve just in case
|
||||||
|
|
||||||
OSC_Z = 274,
|
OSC_Z = 274,
|
||||||
PLL_Z = 275,
|
PLL_Z = 275,
|
||||||
GSR_Z = 276,
|
GSR_Z = 276,
|
||||||
|
@ -32,6 +32,8 @@ IOLOGICA_Z = 70
|
|||||||
IDES16_Z = 72
|
IDES16_Z = 72
|
||||||
OSER16_Z = 73
|
OSER16_Z = 73
|
||||||
|
|
||||||
|
BUFG_Z = 74 # : 81 reserve just in case
|
||||||
|
|
||||||
OSC_Z = 274
|
OSC_Z = 274
|
||||||
PLL_Z = 275
|
PLL_Z = 275
|
||||||
GSR_Z = 276
|
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)
|
tt.add_bel_pin(bel, port, wire, PinType.OUTPUT)
|
||||||
else:
|
else:
|
||||||
tt.add_bel_pin(bel, port, wire, PinType.INPUT)
|
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):
|
def create_tiletype(create_func, chip: Chip, db: chipdb, x: int, y: int, ttyp: int):
|
||||||
has_extra_func = (y, x) in db.extra_func
|
has_extra_func = (y, x) in db.extra_func
|
||||||
|
@ -110,7 +110,7 @@ struct GowinPacker
|
|||||||
auto &ci = *cell.second;
|
auto &ci = *cell.second;
|
||||||
if (!ci.type.in(ctx->id("$nextpnr_ibuf"), ctx->id("$nextpnr_obuf"), ctx->id("$nextpnr_iobuf")))
|
if (!ci.type.in(ctx->id("$nextpnr_ibuf"), ctx->id("$nextpnr_obuf"), ctx->id("$nextpnr_iobuf")))
|
||||||
continue;
|
continue;
|
||||||
NetInfo *i = ci.getPort(ctx->id("I"));
|
NetInfo *i = ci.getPort(id_I);
|
||||||
if (i && i->driver.cell) {
|
if (i && i->driver.cell) {
|
||||||
if (!top_ports.count(CellTypePort(i->driver)))
|
if (!top_ports.count(CellTypePort(i->driver)))
|
||||||
log_error("Top-level port '%s' driven by illegal port %s.%s\n", ctx->nameOf(&ci),
|
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;
|
i->driver.cell->attrs[attr.first] = attr.second;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
NetInfo *o = ci.getPort(ctx->id("O"));
|
NetInfo *o = ci.getPort(id_O);
|
||||||
if (o) {
|
if (o) {
|
||||||
for (auto &usr : o->users) {
|
for (auto &usr : o->users) {
|
||||||
if (!top_ports.count(CellTypePort(usr)))
|
if (!top_ports.count(CellTypePort(usr)))
|
||||||
@ -128,9 +128,21 @@ struct GowinPacker
|
|||||||
for (const auto &attr : ci.attrs) {
|
for (const auto &attr : ci.attrs) {
|
||||||
usr.cell->attrs[attr.first] = attr.second;
|
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 (io && io->driver.cell) {
|
||||||
if (!top_ports.count(CellTypePort(io->driver)))
|
if (!top_ports.count(CellTypePort(io->driver)))
|
||||||
log_error("Top-level port '%s' driven by illegal port %s.%s\n", ctx->nameOf(&ci),
|
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;
|
io->driver.cell->attrs[attr.first] = attr.second;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ci.disconnectPort(ctx->id("I"));
|
ci.disconnectPort(id_I);
|
||||||
ci.disconnectPort(ctx->id("O"));
|
ci.disconnectPort(id_O);
|
||||||
ci.disconnectPort(ctx->id("IO"));
|
ci.disconnectPort(id_IO);
|
||||||
to_remove.push_back(ci.name);
|
to_remove.push_back(ci.name);
|
||||||
}
|
}
|
||||||
for (IdString cell_name : to_remove)
|
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)
|
void run(void)
|
||||||
{
|
{
|
||||||
handle_constants();
|
handle_constants();
|
||||||
@ -1348,6 +1395,9 @@ struct GowinPacker
|
|||||||
ctx->check();
|
ctx->check();
|
||||||
|
|
||||||
pack_ram16sdp4();
|
pack_ram16sdp4();
|
||||||
|
ctx->check();
|
||||||
|
|
||||||
|
pack_buffered_nets();
|
||||||
|
|
||||||
ctx->fixupHierarchy();
|
ctx->fixupHierarchy();
|
||||||
ctx->check();
|
ctx->check();
|
||||||
|
Loading…
Reference in New Issue
Block a user