ice40: Emit feed-through LUTs for PLL/LOCK
This commit is contained in:
parent
db31c0625b
commit
69233385f8
@ -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); }
|
||||||
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
|
NEXTPNR_NAMESPACE_END
|
||||||
|
@ -617,7 +617,7 @@ def add_pll_clock_output(bel, ec, ec_entry):
|
|||||||
}
|
}
|
||||||
|
|
||||||
wire_downhill_belports[wire_idx] = {(bel, port),}
|
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))]
|
io_wire = wire_names[(io_x, io_y, 'io_{}/D_IN_0'.format(io_z))]
|
||||||
wire_downhill[wire_idx] = {io_wire,}
|
wire_downhill[wire_idx] = {io_wire,}
|
||||||
|
157
ice40/pack.cc
157
ice40/pack.cc
@ -629,6 +629,162 @@ static void pack_special(Context *ctx)
|
|||||||
newname = "PLLOUT_A";
|
newname = "PLLOUT_A";
|
||||||
replace_port(ci, ctx->id(pi.name.c_str(ctx)), packed.get(), ctx->id(newname));
|
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));
|
new_cells.push_back(std::move(packed));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -649,6 +805,7 @@ bool Arch::pack()
|
|||||||
log_break();
|
log_break();
|
||||||
pack_constants(ctx);
|
pack_constants(ctx);
|
||||||
promote_globals(ctx);
|
promote_globals(ctx);
|
||||||
|
|
||||||
pack_io(ctx);
|
pack_io(ctx);
|
||||||
pack_lut_lutffs(ctx);
|
pack_lut_lutffs(ctx);
|
||||||
pack_nonlut_ffs(ctx);
|
pack_nonlut_ffs(ctx);
|
||||||
|
Loading…
Reference in New Issue
Block a user