ice40: Emit feed-through LUTs for PLL/LOCK

This commit is contained in:
Sergiusz Bazanski 2018-07-23 21:49:02 +01:00
parent db31c0625b
commit 69233385f8
3 changed files with 159 additions and 5 deletions

View File

@ -173,9 +173,6 @@ struct Loc
bool operator==(const Loc &other) const { return (x == other.x) && (y == other.y) && (z == other.z); }
bool operator!=(const Loc &other) const { return (x != other.x) || (y != other.y) || (z == other.z); }
Loc(int x, int y, int z) : x(x), y(y), z(z) {}
Loc() {}
};
NEXTPNR_NAMESPACE_END

View File

@ -617,7 +617,7 @@ def add_pll_clock_output(bel, ec, ec_entry):
}
wire_downhill_belports[wire_idx] = {(bel, port),}
bel_wires[bel].append((wire_idx, port))
bel_wires[bel].append((wire_idx, port, beltypes['PLL']))
io_wire = wire_names[(io_x, io_y, 'io_{}/D_IN_0'.format(io_z))]
wire_downhill[wire_idx] = {io_wire,}

View File

@ -629,6 +629,162 @@ static void pack_special(Context *ctx)
newname = "PLLOUT_A";
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()) {
//FIXME replace by getBelsByType when implemented
for (auto bel : ctx->getBels()) {
if (ctx->getBelType(bel) != TYPE_ICESTORM_PLL)
continue;
log_info(" constrained '%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;
}
if (!constrained) {
log_error(" could not constrain '%s' to any PLL Bel\n", packed->name.c_str(ctx));
}
}
// The LOCK signal on iCE40 PLLs goes through the neigh_op_bnl_1 wire.
// In practice, this means the LOCK signal can only directly reach LUT
// inputs.
// If we have a net connected to LOCK, make sure it only drives LUTs.
auto port = packed->ports[ctx->id("LOCK")];
if (port.net != nullptr) {
bool found_lut = false;
bool all_luts = true;
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++;
} else {
all_luts = false;
}
}
if (found_lut && all_luts) {
// Every user is a LUT, carry on now.
} else if (found_lut && !all_luts && 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));
// Create pass-through LUT.
std::unique_ptr<CellInfo> pt =
create_ice_cell(ctx, ctx->id("ICESTORM_LC"), ci->name.str(ctx) + "$nextpnr_ice40_pack_pll_lc");
packed_cells.insert(pt->name);
pt->params[ctx->id("LUT_INIT")] = "255"; // all lower bits lit, so output is always I3
// Create net to which all non-LUTs will be moved.
std::unique_ptr<NetInfo> out_net = std::unique_ptr<NetInfo>(new NetInfo);
out_net->name = ctx->id(ci->name.str(ctx) + "$nextnr_ice40_pack_pll_net");
out_net->driver.cell = pt.get();
out_net->driver.port = ctx->id("O");
pt->ports.at(ctx->id("O")).net = out_net.get();
// Move all non-LUTs to new net.
std::vector<PortRef> new_users;
for (const auto &user : port.net->users) {
if (user.cell->type == ctx->id("ICESTORM_LC")) {
new_users.push_back(user);
}
// Rewrite pointer into net in user.
user.cell->ports[user.port].net = out_net.get();
// Add user to net.
PortRef pr;
pr.cell = user.cell;
pr.port = user.port;
out_net->users.push_back(pr);
}
// Add LUT to new users.
PortRef pr;
pr.cell = pt.get();
pr.port = ctx->id("I3");
new_users.push_back(pr);
pt->ports.at(ctx->id("I3")).net = port.net;
// Replace users of the original net.
port.net->users = new_users;
ctx->nets[out_net->name] = std::move(out_net);
new_cells.push_back(std::move(pt));
} else {
// Strategy: create a pass-through LUT, move every user behind it.
log_info(" LUT strategy for %s: move all users to new LUT\n", port.name.c_str(ctx));
// Create pass-through LUT.
std::unique_ptr<CellInfo> pt =
create_ice_cell(ctx, ctx->id("ICESTORM_LC"), ci->name.str(ctx) + "$nextpnr_ice40_pack_pll_lc");
packed_cells.insert(pt->name);
pt->params[ctx->id("LUT_INIT")] = "255"; // all lower bits lit, so output is always I3
// Create net to which all current users will be moved.
std::unique_ptr<NetInfo> out_net = std::unique_ptr<NetInfo>(new NetInfo);
out_net->name = ctx->id(ci->name.str(ctx) + "$nextnr_ice40_pack_pll_net");
out_net->driver.cell = pt.get();
out_net->driver.port = ctx->id("O");
pt->ports.at(ctx->id("O")).net = out_net.get();
// Move all users to new net.
for (const auto &user : port.net->users) {
// Rewrite pointer into net in user.
user.cell->ports[user.port].net = out_net.get();
// Add user to net.
PortRef pr;
pr.cell = user.cell;
pr.port = user.port;
out_net->users.push_back(pr);
}
// Add LUT to new users.
std::vector<PortRef> new_users;
PortRef pr;
pr.cell = pt.get();
pr.port = ctx->id("I3");
new_users.push_back(pr);
pt->ports.at(ctx->id("I3")).net = port.net;
// Replace users of the original net.
port.net->users = new_users;
ctx->nets[out_net->name] = std::move(out_net);
new_cells.push_back(std::move(pt));
}
// Find wire driven by this port.
const auto &pll_beli = ctx->chip_info->bel_data[pll_bel.index];
const WireInfoPOD *wirei = nullptr;
for (int i = 0; i < pll_beli.num_bel_wires; i++) {
auto bel_port = ctx->portPinToId(pll_beli.bel_wires[i].port);
if (port.name != bel_port)
continue;
wirei = &ctx->chip_info->wire_data[pll_beli.bel_wires[i].wire_index];
break;
}
NPNR_ASSERT(wirei != nullptr);
// Now, constrain all LUTs on the output of the signal to be at
// the correct Bel relative to the PLL Bel.
int x = wirei->x;
int y = wirei->y;
int z = 0;
for (const auto &user : port.net->users) {
NPNR_ASSERT(user.cell != nullptr);
NPNR_ASSERT(user.cell->type == ctx->id("ICESTORM_LC"));
// TODO(q3k): handle when the Bel might be already the
// target of another constraint.
NPNR_ASSERT(z < 8);
auto target_bel = ctx->getBelByLocation(Loc(x, y, z++));
auto target_bel_name = ctx->getBelName(target_bel).str(ctx);
user.cell->attrs[ctx->id("BEL")] = target_bel_name;
log_info(" constrained '%s' to %s\n", user.cell->name.c_str(ctx), target_bel_name.c_str());
}
}
new_cells.push_back(std::move(packed));
}
}
@ -649,6 +805,7 @@ bool Arch::pack()
log_break();
pack_constants(ctx);
promote_globals(ctx);
pack_io(ctx);
pack_lut_lutffs(ctx);
pack_nonlut_ffs(ctx);