ice40: Take placed SB_GBs into account when placing PLLs

Because the PLLs drive global networks, we need to account for
already existing and placed SB_GBs when trying to place/pack them.

Theses can be user instanciated SB_GBs with BEL attribute, or
SB_GB_IOs that got converted during the IO packing.

This patch assumes that:
 - If a PLL is used the output A global network is always used, even
   if there is no connection to the global output pin
 - If a PLL with a singe output is used, then the B output global
   network is still free to be used by whatever.

Signed-off-by: Sylvain Munaut <tnt@246tNt.com>
This commit is contained in:
Sylvain Munaut 2019-04-16 10:12:28 +02:00
parent 250c914763
commit 9dd68aa0e2

View File

@ -383,12 +383,9 @@ static void pack_constants(Context *ctx)
}
}
static std::unique_ptr<CellInfo> create_padin_gbuf(Context *ctx, CellInfo *cell, IdString port_name,
std::string gbuf_name)
static BelId find_padin_gbuf(Context *ctx, BelId bel, IdString port_name)
{
// Find the matching SB_GB BEL connected to the same global network
BelId gb_bel;
BelId bel = ctx->getBelByName(ctx->id(cell->attrs[ctx->id("BEL")]));
auto wire = ctx->getBelPinWire(bel, port_name);
if (wire == WireId())
@ -401,6 +398,15 @@ static std::unique_ptr<CellInfo> create_padin_gbuf(Context *ctx, CellInfo *cell,
}
}
return gb_bel;
}
static std::unique_ptr<CellInfo> create_padin_gbuf(Context *ctx, CellInfo *cell, IdString port_name,
std::string gbuf_name)
{
// Find the matching SB_GB BEL connected to the same global network
BelId bel = ctx->getBelByName(ctx->id(cell->attrs[ctx->id("BEL")]));
BelId gb_bel = find_padin_gbuf(ctx, bel, port_name);
NPNR_ASSERT(gb_bel != BelId());
// Create a SB_GB Cell and lock it there
@ -704,14 +710,15 @@ static void promote_globals(Context *ctx)
// Figure out where to place PLLs
static void place_plls(Context *ctx)
{
std::map<BelId, std::pair<BelPin, BelPin>> pll_all_bels;
std::map<BelId, std::tuple<BelPin, BelId, BelPin, BelId>> pll_all_bels;
std::map<BelId, CellInfo *> pll_used_bels;
std::vector<CellInfo *> pll_cells;
std::map<BelId, CellInfo *> bel2io;
std::map<BelId, CellInfo *> bel2gb;
log_info("Placing PLLs..\n");
// Find all the PLLs BELs and matching IO sites
// Find all the PLLs BELs and matching IO sites and global networks
for (auto bel : ctx->getBels()) {
if (ctx->getBelType(bel) != id_ICESTORM_PLL)
continue;
@ -720,8 +727,10 @@ static void place_plls(Context *ctx)
auto io_a_pin = ctx->getIOBSharingPLLPin(bel, id_PLLOUT_A);
auto io_b_pin = ctx->getIOBSharingPLLPin(bel, id_PLLOUT_B);
auto gb_a = find_padin_gbuf(ctx, bel, id_PLLOUT_A_GLOBAL);
auto gb_b = find_padin_gbuf(ctx, bel, id_PLLOUT_B_GLOBAL);
pll_all_bels[bel] = std::make_pair(io_a_pin, io_b_pin);
pll_all_bels[bel] = std::make_tuple(io_a_pin, gb_a, io_b_pin, gb_b);
}
// Find all the PLLs cells we need to place and do pre-checks
@ -826,7 +835,8 @@ static void place_plls(Context *ctx)
for (auto placed_pll : pll_used_bels) {
BelPin pll_io_a, pll_io_b;
std::tie(pll_io_a, pll_io_b) = pll_all_bels[placed_pll.first];
BelId gb_a, gb_b;
std::tie(pll_io_a, gb_a, pll_io_b, gb_b) = pll_all_bels[placed_pll.first];
if (io_bel == pll_io_a.bel) {
// All the PAD type PLL stuff already checked above,so only
// check for conflict with a user placed CORE PLL
@ -844,6 +854,37 @@ static void place_plls(Context *ctx)
bel2io[io_bel] = io_ci;
}
// Scan all SB_GBs to check for conflicts with PLL BELs
for (auto gb_cell : sorted(ctx->cells)) {
CellInfo *gb_ci = gb_cell.second;
if (!is_gbuf(ctx, gb_ci))
continue;
// Only consider the bound ones
if (!gb_ci->attrs.count(ctx->id("BEL")))
continue;
// Check all placed PLL (either forced by user, or forced by PACKAGEPIN)
BelId gb_bel = ctx->getBelByName(ctx->id(gb_ci->attrs[ctx->id("BEL")]));
for (auto placed_pll : pll_used_bels) {
BelPin pll_io_a, pll_io_b;
BelId gb_a, gb_b;
std::tie(pll_io_a, gb_a, pll_io_b, gb_b) = pll_all_bels[placed_pll.first];
if (gb_bel == gb_a) {
log_error("PLL '%s' A output conflict with SB_GB '%s'\n", placed_pll.second->name.c_str(ctx),
gb_cell.second->name.c_str(ctx));
} else if (gb_bel == gb_b) {
if (is_sb_pll40_dual(ctx, placed_pll.second))
log_error("PLL '%s' B output conflicts with SB_GB '%s'\n", placed_pll.second->name.c_str(ctx),
gb_cell.second->name.c_str(ctx));
}
}
// Save for later checks
bel2gb[gb_bel] = gb_ci;
}
// Scan all the CORE PLLs and place them in remaining available PLL BELs
// (in two pass ... first do the dual ones, harder to place, then single port)
for (int i = 0; i < 2; i++) {
@ -874,7 +915,8 @@ static void place_plls(Context *ctx)
if (pll_used_bels.count(bel_pll.first))
continue;
BelPin pll_io_a, pll_io_b;
std::tie(pll_io_a, pll_io_b) = bel_pll.second;
BelId gb_a, gb_b;
std::tie(pll_io_a, gb_a, pll_io_b, gb_b) = bel_pll.second;
if (bel2io.count(pll_io_a.bel)) {
if (pll_io_a.bel == pad_bel)
could_be_pad = !bel2io.count(pll_io_b.bel) || !is_sb_pll40_dual(ctx, ci);
@ -882,6 +924,10 @@ static void place_plls(Context *ctx)
}
if (bel2io.count(pll_io_b.bel) && is_sb_pll40_dual(ctx, ci))
continue;
if (bel2gb.count(gb_a))
continue;
if (bel2gb.count(gb_b) && is_sb_pll40_dual(ctx, ci))
continue;
found_bel = bel_pll.first;
break;
}