aboutsummaryrefslogtreecommitdiffstats
path: root/ice40/pack.cc
diff options
context:
space:
mode:
Diffstat (limited to 'ice40/pack.cc')
-rw-r--r--ice40/pack.cc190
1 files changed, 161 insertions, 29 deletions
diff --git a/ice40/pack.cc b/ice40/pack.cc
index e71db46f..390cbf57 100644
--- a/ice40/pack.cc
+++ b/ice40/pack.cc
@@ -265,6 +265,8 @@ static void pack_ram(Context *ctx)
std::unique_ptr<CellInfo> packed =
create_ice_cell(ctx, ctx->id("ICESTORM_RAM"), ci->name.str(ctx) + "_RAM");
packed_cells.insert(ci->name);
+ for (auto attr : ci->attrs)
+ packed->attrs[attr.first] = attr.second;
for (auto param : ci->params)
packed->params[param.first] = param.second;
packed->params[ctx->id("NEG_CLK_W")] =
@@ -381,13 +383,14 @@ 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())
+ log_error("BEL '%s' has no global buffer connection available\n", ctx->getBelName(bel).c_str(ctx));
+
for (auto src_bel : ctx->getWireBelPins(wire)) {
if (ctx->getBelType(src_bel.bel) == id_SB_GB && src_bel.pin == id_GLOBAL_BUFFER_OUTPUT) {
gb_bel = src_bel.bel;
@@ -395,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
@@ -478,9 +490,6 @@ static void pack_io(Context *ctx)
}
packed_cells.insert(ci->name);
std::copy(ci->attrs.begin(), ci->attrs.end(), std::inserter(sb->attrs, sb->attrs.begin()));
- if (!sb->attrs.count(ctx->id("BEL")))
- log_warning("IO '%s' is not constrained to a pin and will be automatically placed\n",
- ci->name.c_str(ctx));
} else if (is_sb_io(ctx, ci) || is_sb_gb_io(ctx, ci)) {
NetInfo *net = ci->ports.at(ctx->id("PACKAGE_PIN")).net;
if ((net != nullptr) && (net->users.size() > 1))
@@ -499,6 +508,19 @@ static void pack_io(Context *ctx)
// Make it a normal SB_IO with global marker
ci->type = ctx->id("SB_IO");
ci->attrs[ctx->id("GLOBAL")] = "1";
+ } else if (is_sb_io(ctx, ci)) {
+ // Disconnect unused inputs
+ NetInfo *net_in0 = ci->ports.count(id_D_IN_0) ? ci->ports[id_D_IN_0].net : nullptr;
+ NetInfo *net_in1 = ci->ports.count(id_D_IN_1) ? ci->ports[id_D_IN_1].net : nullptr;
+
+ if (net_in0 != nullptr && net_in0->users.size() == 0) {
+ delete_nets.insert(net_in0->name);
+ ci->ports[id_D_IN_0].net = nullptr;
+ }
+ if (net_in1 != nullptr && net_in1->users.size() == 0) {
+ delete_nets.insert(net_in1->name);
+ ci->ports[id_D_IN_1].net = nullptr;
+ }
}
}
for (auto pcell : packed_cells) {
@@ -688,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;
@@ -704,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
@@ -767,6 +792,8 @@ static void place_plls(Context *ctx)
io_cell->name.c_str(ctx));
if (pll_used_bels.count(found_bel)) {
CellInfo *conflict_cell = pll_used_bels.at(found_bel);
+ if (conflict_cell == ci)
+ continue;
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));
}
@@ -808,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
@@ -826,6 +854,47 @@ 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) {
+ CellInfo *ci = placed_pll.second;
+
+ // Used global connections
+ bool gb_a_used = ci->ports.count(id_PLLOUT_A_GLOBAL) && (ci->ports[id_PLLOUT_A_GLOBAL].net != nullptr) &&
+ (ci->ports[id_PLLOUT_A_GLOBAL].net->users.size() > 0);
+ bool gb_b_used = is_sb_pll40_dual(ctx, ci) && ci->ports.count(id_PLLOUT_B_GLOBAL) &&
+ (ci->ports[id_PLLOUT_B_GLOBAL].net != nullptr) &&
+ (ci->ports[id_PLLOUT_B_GLOBAL].net->users.size() > 0);
+
+ // Check for conflict
+ 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_a_used && (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));
+ }
+ if (gb_b_used && (gb_bel == gb_b)) {
+ 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++) {
@@ -844,6 +913,13 @@ static void place_plls(Context *ctx)
log_error("PLL '%s' is of CORE type but doesn't have a valid REFERENCECLK connection\n",
ci->name.c_str(ctx));
+ // Used global connections
+ bool gb_a_used = ci->ports.count(id_PLLOUT_A_GLOBAL) && (ci->ports[id_PLLOUT_A_GLOBAL].net != nullptr) &&
+ (ci->ports[id_PLLOUT_A_GLOBAL].net->users.size() > 0);
+ bool gb_b_used = is_sb_pll40_dual(ctx, ci) && ci->ports.count(id_PLLOUT_B_GLOBAL) &&
+ (ci->ports[id_PLLOUT_B_GLOBAL].net != nullptr) &&
+ (ci->ports[id_PLLOUT_B_GLOBAL].net->users.size() > 0);
+
// Could this be a PAD PLL ?
bool could_be_pad = false;
BelId pad_bel;
@@ -853,8 +929,11 @@ static void place_plls(Context *ctx)
// Find a BEL for it
BelId found_bel;
for (auto bel_pll : pll_all_bels) {
+ 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);
@@ -862,6 +941,10 @@ static void place_plls(Context *ctx)
}
if (bel2io.count(pll_io_b.bel) && is_sb_pll40_dual(ctx, ci))
continue;
+ if (gb_a_used && bel2gb.count(gb_a))
+ continue;
+ if (gb_b_used && bel2gb.count(gb_b))
+ continue;
found_bel = bel_pll.first;
break;
}
@@ -978,9 +1061,14 @@ static void pack_special(Context *ctx)
create_ice_cell(ctx, ctx->id("ICESTORM_HFOSC"), ci->name.str(ctx) + "_OSC");
packed_cells.insert(ci->name);
cell_place_unique(ctx, packed.get());
+ packed->params[ctx->id("TRIM_EN")] = str_or_default(ci->params, ctx->id("TRIM_EN"), "0b0");
packed->params[ctx->id("CLKHF_DIV")] = str_or_default(ci->params, ctx->id("CLKHF_DIV"), "0b00");
replace_port(ci, ctx->id("CLKHFEN"), packed.get(), ctx->id("CLKHFEN"));
replace_port(ci, ctx->id("CLKHFPU"), packed.get(), ctx->id("CLKHFPU"));
+ for (int i = 0; i < 10; i++) {
+ auto port = ctx->id("TRIM" + std::to_string(i));
+ replace_port(ci, port, packed.get(), port);
+ }
if (bool_or_default(ci->attrs, ctx->id("ROUTE_THROUGH_FABRIC"))) {
replace_port(ci, ctx->id("CLKHF"), packed.get(), ctx->id("CLKHF_FABRIC"));
} else {
@@ -994,6 +1082,8 @@ static void pack_special(Context *ctx)
std::unique_ptr<CellInfo> packed =
create_ice_cell(ctx, ctx->id("ICESTORM_SPRAM"), ci->name.str(ctx) + "_RAM");
packed_cells.insert(ci->name);
+ for (auto attr : ci->attrs)
+ packed->attrs[attr.first] = attr.second;
for (auto port : ci->ports) {
PortInfo &pi = port.second;
std::string newname = pi.name.str(ctx);
@@ -1049,7 +1139,24 @@ static void pack_special(Context *ctx)
} else if (is_sb_ledda_ip(ctx, ci)) {
/* Force placement (no choices anyway) */
cell_place_unique(ctx, ci);
-
+ } else if (is_sb_i2c(ctx, ci) || is_sb_spi(ctx, ci)) {
+ const std::map<std::tuple<IdString, std::string>, Loc> map_ba74 = {
+ {std::make_tuple(id_SB_SPI, "0b0000"), Loc(0, 0, 0)},
+ {std::make_tuple(id_SB_I2C, "0b0001"), Loc(0, 31, 0)},
+ {std::make_tuple(id_SB_SPI, "0b0010"), Loc(25, 0, 1)},
+ {std::make_tuple(id_SB_I2C, "0b0011"), Loc(25, 31, 0)},
+ };
+ if (map_ba74.find(std::make_tuple(ci->type, ci->params[ctx->id("BUS_ADDR74")])) == map_ba74.end())
+ log_error("Invalid value for BUS_ADDR74 for cell '%s' of type '%s'\n", ci->name.c_str(ctx),
+ ci->type.c_str(ctx));
+ Loc bel_loc = map_ba74.at(std::make_tuple(ci->type, ci->params[ctx->id("BUS_ADDR74")]));
+ BelId bel = ctx->getBelByLocation(bel_loc);
+ if (bel == BelId() || ctx->getBelType(bel) != ci->type)
+ log_error("Unable to find placement for cell '%s' of type '%s'\n", ci->name.c_str(ctx),
+ ci->type.c_str(ctx));
+ IdString bel_name = ctx->getBelName(bel);
+ ci->attrs[ctx->id("BEL")] = bel_name.str(ctx);
+ log_info(" constrained %s '%s' to %s\n", ci->type.c_str(ctx), ci->name.c_str(ctx), bel_name.c_str(ctx));
} else if (is_sb_pll40(ctx, ci)) {
bool is_pad = is_sb_pll40_pad(ctx, ci);
bool is_core = !is_pad;
@@ -1058,7 +1165,12 @@ static void pack_special(Context *ctx)
create_ice_cell(ctx, ctx->id("ICESTORM_PLL"), ci->name.str(ctx) + "_PLL");
packed->attrs[ctx->id("TYPE")] = ci->type.str(ctx);
packed_cells.insert(ci->name);
-
+ if (!is_sb_pll40_dual(ctx, ci)) {
+ // Remove second output, so a buffer isn't created for it, for these
+ // cell types with only one output
+ packed->ports.erase(ctx->id("PLLOUT_B"));
+ packed->ports.erase(ctx->id("PLLOUT_B_GLOBAL"));
+ }
for (auto attr : ci->attrs)
packed->attrs[attr.first] = attr.second;
for (auto param : ci->params)
@@ -1083,13 +1195,17 @@ static void pack_special(Context *ctx)
}
auto feedback_path = packed->params[ctx->id("FEEDBACK_PATH")];
- packed->params[ctx->id("FEEDBACK_PATH")] =
- feedback_path == "DELAY"
- ? "0"
- : feedback_path == "SIMPLE" ? "1"
- : feedback_path == "PHASE_AND_DELAY"
- ? "2"
- : feedback_path == "EXTERNAL" ? "6" : feedback_path;
+ std::string fbp_value = feedback_path == "DELAY"
+ ? "0"
+ : feedback_path == "SIMPLE"
+ ? "1"
+ : feedback_path == "PHASE_AND_DELAY"
+ ? "2"
+ : feedback_path == "EXTERNAL" ? "6" : feedback_path;
+ if (!std::all_of(fbp_value.begin(), fbp_value.end(), isdigit))
+ log_error("PLL '%s' has unsupported FEEDBACK_PATH value '%s'\n", ci->name.c_str(ctx),
+ feedback_path.c_str());
+ packed->params[ctx->id("FEEDBACK_PATH")] = fbp_value;
packed->params[ctx->id("PLLTYPE")] = std::to_string(sb_pll40_type(ctx, ci));
NetInfo *pad_packagepin_net = nullptr;
@@ -1177,20 +1293,26 @@ static void pack_special(Context *ctx)
log_info(" PLL '%s' has LOCK output, need to pass all outputs via LUT\n", ci->name.c_str(ctx));
bool found_lut = false;
bool all_luts = true;
+ bool found_carry = false;
unsigned int lut_count = 0;
for (const auto &user : port.net->users) {
NPNR_ASSERT(user.cell != nullptr);
if (user.cell->type == ctx->id("ICESTORM_LC")) {
- found_lut = true;
- lut_count++;
+ if (bool_or_default(user.cell->params, ctx->id("CARRY_ENABLE"), false)) {
+ found_carry = true;
+ all_luts = false;
+ } else {
+ found_lut = true;
+ lut_count++;
+ }
} else {
all_luts = false;
}
}
- if (found_lut && all_luts) {
+ if (found_lut && all_luts && lut_count < 8) {
// Every user is a LUT, carry on now.
- } else if (found_lut && !all_luts && lut_count < 8) {
+ } else if (found_lut && !all_luts && !found_carry && lut_count < 8) {
// Strategy: create a pass-through LUT, move all non-LUT users behind it.
log_info(" LUT strategy for %s: move non-LUT users to new LUT\n", port.name.c_str(ctx));
auto pt = spliceLUT(ctx, packed.get(), port.name, true);
@@ -1237,10 +1359,20 @@ static void pack_special(Context *ctx)
else
continue;
- std::unique_ptr<CellInfo> gb =
- create_padin_gbuf(ctx, packed.get(), pi.name,
- "$gbuf_" + ci->name.str(ctx) + "_pllout_" + (is_b_port ? "b" : "a"));
- new_cells.push_back(std::move(gb));
+ // Only if there is actually a net ...
+ if (pi.net != nullptr) {
+ // ... and it's used
+ if (pi.net->users.size() > 0) {
+ std::unique_ptr<CellInfo> gb =
+ create_padin_gbuf(ctx, packed.get(), pi.name,
+ "$gbuf_" + ci->name.str(ctx) + "_pllout_" + (is_b_port ? "b" : "a"));
+ new_cells.push_back(std::move(gb));
+ } else {
+ // If not, remove it to avoid routing issues
+ ctx->nets.erase(pi.net->name);
+ packed->ports[pi.name].net = nullptr;
+ }
+ }
}
new_cells.push_back(std::move(packed));