aboutsummaryrefslogtreecommitdiffstats
path: root/ice40
diff options
context:
space:
mode:
authorSylvain Munaut <tnt@246tNt.com>2018-11-28 10:04:06 +0100
committerSylvain Munaut <tnt@246tNt.com>2018-11-28 16:04:43 +0100
commita65b12e8d6d291c04a3982448250014c7de3cbd3 (patch)
tree8acf7e344a9531223166e3057aa533f55ff82d32 /ice40
parent7a2ef27d6c27425bc39ef3a71b0df8b1c608d599 (diff)
downloadnextpnr-a65b12e8d6d291c04a3982448250014c7de3cbd3.tar.gz
nextpnr-a65b12e8d6d291c04a3982448250014c7de3cbd3.tar.bz2
nextpnr-a65b12e8d6d291c04a3982448250014c7de3cbd3.zip
ice40: Revamp the whole PLL placement/validity check logic
We do a pre-pass on all the PLLs to place them before packing. To place them: - First pass with all the PADs PLLs since those can only fit at one specific BEL depending on the input connection - Second pass with all the dual outputs CORE PLLs. Those can go anywhere where there is no conflicts with their A & B outputs and used IO pins - Third pass with the single output CORE PLLs. Those have the least constrains. During theses passes, we also check the validity of all their connections. Signed-off-by: Sylvain Munaut <tnt@246tNt.com>
Diffstat (limited to 'ice40')
-rw-r--r--ice40/pack.cc272
1 files changed, 200 insertions, 72 deletions
diff --git a/ice40/pack.cc b/ice40/pack.cc
index 60464230..65b5a222 100644
--- a/ice40/pack.cc
+++ b/ice40/pack.cc
@@ -680,6 +680,190 @@ 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, CellInfo *> pll_used_bels;
+ std::vector<CellInfo *> pll_cells;
+ std::map<BelId, CellInfo *> bel2io;
+
+ log_info("Placing PLLs..\n");
+
+ // Find all the PLLs BELs and matching IO sites
+ for (auto bel : ctx->getBels()) {
+ if (ctx->getBelType(bel) != id_ICESTORM_PLL)
+ continue;
+ if (ctx->isBelLocked(bel))
+ continue;
+
+ auto io_a_pin = ctx->getIOBSharingPLLPin(bel, id_PLLOUT_A);
+ auto io_b_pin = ctx->getIOBSharingPLLPin(bel, id_PLLOUT_B);
+
+ pll_all_bels[bel] = std::make_pair(io_a_pin, io_b_pin);
+ }
+
+ // Find all the PLLs cells we need to place and do pre-checks
+ for (auto cell : sorted(ctx->cells)) {
+ CellInfo *ci = cell.second;
+ if (!is_sb_pll40(ctx, ci))
+ continue;
+
+ // If it's constrained already, add to already used list
+ if (ci->attrs.count(ctx->id("BEL"))) {
+ BelId bel_constrain = ctx->getBelByName(ctx->id(ci->attrs[ctx->id("BEL")]));
+ if (pll_all_bels.count(bel_constrain) == 0)
+ log_error("PLL '%s' is constrained to invalid BEL '%s'\n", ci->name.c_str(ctx),
+ ci->attrs[ctx->id("BEL")].c_str());
+ pll_used_bels[bel_constrain] = ci;
+ }
+
+ // Add it to our list of PLLs to process
+ pll_cells.push_back(ci);
+ }
+
+ // Scan all the PAD PLLs
+ for (auto ci : pll_cells) {
+ if (!is_sb_pll40_pad(ctx, ci))
+ continue;
+
+ // Check PACKAGEPIN connection
+ if (!ci->ports.count(ctx->id("PACKAGEPIN")))
+ log_error("PLL '%s' is of PAD type but doesn't have a PACKAGEPIN port\n", ci->name.c_str(ctx));
+
+ NetInfo *ni = ci->ports.at(ctx->id("PACKAGEPIN")).net;
+ if (ni == nullptr || ni->driver.cell == nullptr)
+ log_error("PLL '%s' is of PAD type but doesn't have a valid PACKAGEPIN connection\n", ci->name.c_str(ctx));
+
+ CellInfo *io_cell = ni->driver.cell;
+ if (io_cell->type != id_SB_IO || ni->driver.port != id_D_IN_0)
+ log_error("PLL '%s' has a PACKAGEPIN driven by an %s, should be directly connected to an input "
+ "SB_IO.D_IN_0 port\n",
+ ci->name.c_str(ctx), io_cell->type.c_str(ctx));
+ if (ni->users.size() != 1)
+ log_error("PLL '%s' clock input '%s' can only drive PLL\n", ci->name.c_str(ctx), ni->name.c_str(ctx));
+ if (!io_cell->attrs.count(ctx->id("BEL")))
+ log_error("PLL '%s' PACKAGEPIN SB_IO '%s' is unconstrained\n", ci->name.c_str(ctx),
+ io_cell->name.c_str(ctx));
+
+ BelId io_bel = ctx->getBelByName(ctx->id(io_cell->attrs.at(ctx->id("BEL"))));
+ BelId found_bel;
+
+ // Find the PLL BEL that would suit that connection
+ for (auto pll_bel : pll_all_bels) {
+ if (std::get<0>(pll_bel.second).bel == io_bel) {
+ found_bel = pll_bel.first;
+ break;
+ }
+ }
+
+ if (found_bel == BelId())
+ log_error("PLL '%s' PACKAGEPIN SB_IO '%s' is not connected to any PLL BEL\n", ci->name.c_str(ctx),
+ io_cell->name.c_str(ctx));
+ if (pll_used_bels.count(found_bel)) {
+ CellInfo *conflict_cell = pll_used_bels.at(found_bel);
+ log_error("PLL '%s' PACKAGEPIN forces it to BEL %s but BEL is already assigned to PLL '%s'\n",
+ ci->name.c_str(ctx), ctx->getBelName(found_bel).c_str(ctx), conflict_cell->name.c_str(ctx));
+ }
+
+ // Is it user constrained ?
+ if (ci->attrs.count(ctx->id("BEL"))) {
+ // Yes. Check it actually matches !
+ BelId bel_constrain = ctx->getBelByName(ctx->id(ci->attrs[ctx->id("BEL")]));
+ if (bel_constrain != found_bel)
+ log_error("PLL '%s' is user constrained to %s but can only be placed in %s based on its PACKAGEPIN "
+ "connection\n",
+ ci->name.c_str(ctx), ctx->getBelName(bel_constrain).c_str(ctx),
+ ctx->getBelName(found_bel).c_str(ctx));
+ } else {
+ // No, we can constrain it ourselves
+ ci->attrs[ctx->id("BEL")] = ctx->getBelName(found_bel).str(ctx);
+ pll_used_bels[found_bel] = ci;
+ }
+
+ // Inform user
+ log_info(" constrained PLL '%s' to %s\n", ci->name.c_str(ctx), ctx->getBelName(found_bel).c_str(ctx));
+ }
+
+ // Scan all SB_IOs to check for conflict with PLL BELs
+ for (auto io_cell : sorted(ctx->cells)) {
+ CellInfo *io_ci = io_cell.second;
+ if (!is_sb_io(ctx, io_ci))
+ continue;
+
+ // Only consider bound IO that are used as inputs
+ if (!io_ci->attrs.count(ctx->id("BEL")))
+ continue;
+ if ((!io_ci->ports.count(id_D_IN_0) || (io_ci->ports[id_D_IN_0].net == nullptr)) &&
+ (!io_ci->ports.count(id_D_IN_1) || (io_ci->ports[id_D_IN_1].net == nullptr)))
+ continue;
+
+ // Check all placed PLL (either forced by user, or forced by PACKAGEPIN)
+ BelId io_bel = ctx->getBelByName(ctx->id(io_ci->attrs[ctx->id("BEL")]));
+
+ 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];
+ 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
+ if (!is_sb_pll40_pad(ctx, placed_pll.second))
+ log_error("PLL '%s' A output conflict with SB_IO '%s' that's used as input\n",
+ placed_pll.second->name.c_str(ctx), io_cell.second->name.c_str(ctx));
+ } else if (io_bel == pll_io_b.bel) {
+ if (is_sb_pll40_dual(ctx, placed_pll.second))
+ log_error("PLL '%s' B output conflicts with SB_IO '%s' that's used as input\n",
+ placed_pll.second->name.c_str(ctx), io_cell.second->name.c_str(ctx));
+ }
+ }
+
+ // Save for later checks
+ bel2io[io_bel] = io_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++) {
+ for (auto ci : pll_cells) {
+ if (is_sb_pll40_pad(ctx, ci))
+ continue;
+ if (is_sb_pll40_dual(ctx, ci) ^ i)
+ continue;
+
+ // Check REFERENCECLK connection
+ if (!ci->ports.count(id_REFERENCECLK))
+ log_error("PLL '%s' is of CORE type but doesn't have a REFERENCECLK port\n", ci->name.c_str(ctx));
+
+ NetInfo *ni = ci->ports.at(id_REFERENCECLK).net;
+ if (ni == nullptr || ni->driver.cell == nullptr)
+ log_error("PLL '%s' is of CORE type but doesn't have a valid REFERENCECLK connection\n",
+ ci->name.c_str(ctx));
+
+ // Find a BEL for it
+ BelId found_bel;
+ for (auto bel_pll : pll_all_bels) {
+ BelPin pll_io_a, pll_io_b;
+ std::tie(pll_io_a, pll_io_b) = bel_pll.second;
+ if (bel2io.count(pll_io_a.bel))
+ continue;
+ if (bel2io.count(pll_io_b.bel) && is_sb_pll40_dual(ctx, ci))
+ continue;
+ found_bel = bel_pll.first;
+ break;
+ }
+
+ // Apply constrain & Inform user of result
+ if (found_bel == BelId())
+ log_error("PLL '%s' couldn't be placed anywhere, no suitable BEL found\n", ci->name.c_str(ctx));
+
+ log_info(" constrained PLL '%s' to %s\n", ci->name.c_str(ctx), ctx->getBelName(found_bel).c_str(ctx));
+
+ ci->attrs[ctx->id("BEL")] = ctx->getBelName(found_bel).str(ctx);
+ pll_used_bels[found_bel] = ci;
+ }
+ }
+}
+
// spliceLUT adds a pass-through LUT LC between the given cell's output port
// and either all users or only non_LUT users.
static std::unique_ptr<CellInfo> spliceLUT(Context *ctx, CellInfo *ci, IdString portId, bool onlyNonLUTs)
@@ -940,82 +1124,25 @@ static void pack_special(Context *ctx)
replace_port(ci, ctx->id(pi.name.c_str(ctx)), packed.get(), ctx->id(newname));
}
- // If PLL is not constrained already, do that - we need this
- // information to then constrain the LOCK LUT.
- BelId pll_bel;
- bool constrained = false;
- if (packed->attrs.find(ctx->id("BEL")) == packed->attrs.end()) {
- for (auto bel : ctx->getBels()) {
- if (ctx->getBelType(bel) != id_ICESTORM_PLL)
- continue;
- if (ctx->isBelLocked(bel))
- continue;
+ // PLL must have been placed already in place_plls()
+ BelId pll_bel = ctx->getBelByName(ctx->id(packed->attrs[ctx->id("BEL")]));
+ NPNR_ASSERT(pll_bel != BelId());
- // A PAD PLL must have its' PACKAGEPIN on the SB_IO that's shared
- // with PLLOUT_A.
- if (is_pad) {
- auto pll_sb_io_belpin = ctx->getIOBSharingPLLPin(bel, id_PLLOUT_A);
- NPNR_ASSERT(pad_packagepin_net != nullptr);
- auto pll_packagepin_driver = pad_packagepin_net->driver;
- NPNR_ASSERT(pll_packagepin_driver.cell != nullptr);
- if (pll_packagepin_driver.cell->type != ctx->id("SB_IO")) {
- log_error("PLL '%s' has a PACKAGEPIN driven by "
- "an %s, should be directly connected to an input SB_IO\n",
- ci->name.c_str(ctx), pll_packagepin_driver.cell->type.c_str(ctx));
- }
+ // Deal with PAD PLL peculiarities
+ if (is_pad) {
+ NPNR_ASSERT(pad_packagepin_net != nullptr);
+ auto pll_packagepin_driver = pad_packagepin_net->driver;
+ NPNR_ASSERT(pll_packagepin_driver.cell != nullptr);
+ auto packagepin_cell = pll_packagepin_driver.cell;
+ auto packagepin_bel_name = packagepin_cell->attrs.find(ctx->id("BEL"));
- auto packagepin_cell = pll_packagepin_driver.cell;
- auto packagepin_bel_name = packagepin_cell->attrs.find(ctx->id("BEL"));
- if (packagepin_bel_name == packagepin_cell->attrs.end()) {
- log_error("PLL '%s' PACKAGEPIN SB_IO '%s' is unconstrained\n", ci->name.c_str(ctx),
- packagepin_cell->name.c_str(ctx));
- }
- auto packagepin_bel = ctx->getBelByName(ctx->id(packagepin_bel_name->second));
- if (pll_sb_io_belpin.bel != packagepin_bel) {
- log_error("PLL '%s' PACKAGEPIN is connected to pin %s, can only be pin %s\n",
- ci->name.c_str(ctx), ctx->getBelPackagePin(packagepin_bel).c_str(),
- ctx->getBelPackagePin(pll_sb_io_belpin.bel).c_str());
- }
- if (pad_packagepin_net->users.size() != 1) {
- log_error("PLL '%s' clock input '%s' can only drive PLL\n", ci->name.c_str(ctx),
- pad_packagepin_net->name.c_str(ctx));
- }
- // Set an attribute about this PLL's PAD SB_IO.
- packed->attrs[ctx->id("BEL_PAD_INPUT")] = packagepin_bel_name->second;
- // Remove the connection from the SB_IO to the PLL.
- packagepin_cell->ports.erase(pll_packagepin_driver.port);
- }
-
- log_info(" constrained PLL '%s' to %s\n", packed->name.c_str(ctx),
- ctx->getBelName(bel).c_str(ctx));
- packed->attrs[ctx->id("BEL")] = ctx->getBelName(bel).str(ctx);
- pll_bel = bel;
- constrained = true;
- break;
- }
- if (!constrained) {
- log_error("Could not constrain PLL '%s' to any PLL Bel (too many PLLs?)\n",
- packed->name.c_str(ctx));
- }
- } else {
- pll_bel = ctx->getBelByName(ctx->id(packed->attrs[ctx->id("BEL")]));
- if (ctx->getBelType(pll_bel) != id_ICESTORM_PLL)
- log_error("PLL '%s' is constrained to BEL %s which isn't a ICESTORM_PLL BEL\n",
- packed->name.c_str(ctx), ctx->getBelName(pll_bel).c_str(ctx));
- if (ctx->isBelLocked(pll_bel))
- log_error("PLL '%s' is constrained to locked BEL %s\n", packed->name.c_str(ctx),
- ctx->getBelName(pll_bel).c_str(ctx));
- log_info(" constrained PLL '%s' to %s\n", packed->name.c_str(ctx),
- ctx->getBelName(pll_bel).c_str(ctx));
- }
+ // Set an attribute about this PLL's PAD SB_IO.
+ packed->attrs[ctx->id("BEL_PAD_INPUT")] = packagepin_bel_name->second;
- // Delete the original PACKAGEPIN net if needed.
- if (pad_packagepin_net != nullptr) {
- for (auto user : pad_packagepin_net->users) {
+ // Disconnect PACKAGEPIN (it's a physical HW link)
+ for (auto user : pad_packagepin_net->users)
user.cell->ports.erase(user.port);
- }
- if (pad_packagepin_net->driver.cell != nullptr)
- pad_packagepin_net->driver.cell->ports.erase(pad_packagepin_net->driver.port);
+ packagepin_cell->ports.erase(pll_packagepin_driver.port);
ctx->nets.erase(pad_packagepin_net->name);
pad_packagepin_net = nullptr;
}
@@ -1119,6 +1246,7 @@ bool Arch::pack()
pack_nonlut_ffs(ctx);
pack_carries(ctx);
pack_ram(ctx);
+ place_plls(ctx);
pack_special(ctx);
if (!bool_or_default(ctx->settings, ctx->id("no_promote_globals"), false))
promote_globals(ctx);