Merge pull request #143 from daveshah1/ecp5_muxes
ecp5: Adding support for LUT extension muxes up to LUT7
This commit is contained in:
commit
bbeab72ad9
@ -556,7 +556,8 @@ bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort
|
|||||||
if (cell->type == id_TRELLIS_SLICE) {
|
if (cell->type == id_TRELLIS_SLICE) {
|
||||||
bool has_carry = str_or_default(cell->params, id("MODE"), "LOGIC") == "CCU2";
|
bool has_carry = str_or_default(cell->params, id("MODE"), "LOGIC") == "CCU2";
|
||||||
if (fromPort == id_A0 || fromPort == id_B0 || fromPort == id_C0 || fromPort == id_D0 || fromPort == id_A1 ||
|
if (fromPort == id_A0 || fromPort == id_B0 || fromPort == id_C0 || fromPort == id_D0 || fromPort == id_A1 ||
|
||||||
fromPort == id_B1 || fromPort == id_C1 || fromPort == id_D1 || fromPort == id_M0 || fromPort == id_FCI) {
|
fromPort == id_B1 || fromPort == id_C1 || fromPort == id_D1 || fromPort == id_M0 || fromPort == id_M1 ||
|
||||||
|
fromPort == id_FXA || fromPort == id_FXB || fromPort == id_FCI) {
|
||||||
return getDelayFromTimingDatabase(has_carry ? id_SCCU2C : id_SLOGICB, fromPort, toPort, delay);
|
return getDelayFromTimingDatabase(has_carry ? id_SCCU2C : id_SLOGICB, fromPort, toPort, delay);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,6 +75,8 @@ bool Arch::isBelLocationValid(BelId bel) const
|
|||||||
bel_cells.push_back(cell_other);
|
bel_cells.push_back(cell_other);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (getBoundBelCell(bel) != nullptr && getBoundBelCell(bel)->sliceInfo.has_l6mux && ((bel_loc.z % 2) == 1))
|
||||||
|
return false;
|
||||||
return slicesCompatible(bel_cells);
|
return slicesCompatible(bel_cells);
|
||||||
} else {
|
} else {
|
||||||
CellInfo *cell = getBoundBelCell(bel);
|
CellInfo *cell = getBoundBelCell(bel);
|
||||||
@ -92,6 +94,10 @@ bool Arch::isValidBelForCell(CellInfo *cell, BelId bel) const
|
|||||||
|
|
||||||
std::vector<const CellInfo *> bel_cells;
|
std::vector<const CellInfo *> bel_cells;
|
||||||
Loc bel_loc = getBelLocation(bel);
|
Loc bel_loc = getBelLocation(bel);
|
||||||
|
|
||||||
|
if (cell->sliceInfo.has_l6mux && ((bel_loc.z % 2) == 1))
|
||||||
|
return false;
|
||||||
|
|
||||||
for (auto bel_other : getBelsByTile(bel_loc.x, bel_loc.y)) {
|
for (auto bel_other : getBelsByTile(bel_loc.x, bel_loc.y)) {
|
||||||
CellInfo *cell_other = getBoundBelCell(bel_other);
|
CellInfo *cell_other = getBoundBelCell(bel_other);
|
||||||
if (cell_other != nullptr && bel_other != bel) {
|
if (cell_other != nullptr && bel_other != bel) {
|
||||||
|
@ -158,6 +158,7 @@ struct ArchCellInfo
|
|||||||
struct
|
struct
|
||||||
{
|
{
|
||||||
bool using_dff;
|
bool using_dff;
|
||||||
|
bool has_l6mux;
|
||||||
IdString clk_sig, lsr_sig, clkmux, lsrmux, srmode;
|
IdString clk_sig, lsr_sig, clkmux, lsrmux, srmode;
|
||||||
} sliceInfo;
|
} sliceInfo;
|
||||||
};
|
};
|
||||||
|
@ -36,7 +36,7 @@ inline bool is_ff(const BaseCtx *ctx, const CellInfo *cell) { return cell->type
|
|||||||
|
|
||||||
inline bool is_carry(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("CCU2C"); }
|
inline bool is_carry(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("CCU2C"); }
|
||||||
|
|
||||||
inline bool is_lc(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("TRELLIS_LC"); }
|
inline bool is_lc(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("TRELLIS_SLICE"); }
|
||||||
|
|
||||||
inline bool is_trellis_io(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("TRELLIS_IO"); }
|
inline bool is_trellis_io(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("TRELLIS_IO"); }
|
||||||
|
|
||||||
|
163
ecp5/pack.cc
163
ecp5/pack.cc
@ -357,9 +357,9 @@ class Ecp5Packer
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Pass to pack LUT5s into a newly created slice
|
// Pass to pack LUT5s into a newly created slice
|
||||||
void pack_lut5s()
|
void pack_lut5xs()
|
||||||
{
|
{
|
||||||
log_info("Packing LUT5s...\n");
|
log_info("Packing LUT5-7s...\n");
|
||||||
for (auto cell : sorted(ctx->cells)) {
|
for (auto cell : sorted(ctx->cells)) {
|
||||||
CellInfo *ci = cell.second;
|
CellInfo *ci = cell.second;
|
||||||
if (is_pfumx(ctx, ci)) {
|
if (is_pfumx(ctx, ci)) {
|
||||||
@ -377,6 +377,8 @@ class Ecp5Packer
|
|||||||
log_error("PFUMX '%s' has BLUT driven by cell other than a LUT\n", ci->name.c_str(ctx));
|
log_error("PFUMX '%s' has BLUT driven by cell other than a LUT\n", ci->name.c_str(ctx));
|
||||||
if (lut1 == nullptr)
|
if (lut1 == nullptr)
|
||||||
log_error("PFUMX '%s' has ALUT driven by cell other than a LUT\n", ci->name.c_str(ctx));
|
log_error("PFUMX '%s' has ALUT driven by cell other than a LUT\n", ci->name.c_str(ctx));
|
||||||
|
if (ctx->verbose)
|
||||||
|
log_info(" mux '%s' forms part of a LUT5\n", cell.first.c_str(ctx));
|
||||||
replace_port(lut0, ctx->id("A"), packed.get(), ctx->id("A0"));
|
replace_port(lut0, ctx->id("A"), packed.get(), ctx->id("A0"));
|
||||||
replace_port(lut0, ctx->id("B"), packed.get(), ctx->id("B0"));
|
replace_port(lut0, ctx->id("B"), packed.get(), ctx->id("B0"));
|
||||||
replace_port(lut0, ctx->id("C"), packed.get(), ctx->id("C0"));
|
replace_port(lut0, ctx->id("C"), packed.get(), ctx->id("C0"));
|
||||||
@ -412,8 +414,157 @@ class Ecp5Packer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
flush_cells();
|
flush_cells();
|
||||||
}
|
// Pack LUT6s
|
||||||
|
for (auto cell : sorted(ctx->cells)) {
|
||||||
|
CellInfo *ci = cell.second;
|
||||||
|
if (is_l6mux(ctx, ci)) {
|
||||||
|
NetInfo *ofx0_0 = ci->ports.at(ctx->id("D0")).net;
|
||||||
|
if (ofx0_0 == nullptr)
|
||||||
|
log_error("L6MUX21 '%s' has disconnected port 'D0'\n", ci->name.c_str(ctx));
|
||||||
|
NetInfo *ofx0_1 = ci->ports.at(ctx->id("D1")).net;
|
||||||
|
if (ofx0_1 == nullptr)
|
||||||
|
log_error("L6MUX21 '%s' has disconnected port 'D1'\n", ci->name.c_str(ctx));
|
||||||
|
CellInfo *slice0 = net_driven_by(ctx, ofx0_0, is_lc, ctx->id("OFX0"));
|
||||||
|
CellInfo *slice1 = net_driven_by(ctx, ofx0_1, is_lc, ctx->id("OFX0"));
|
||||||
|
if (slice0 == nullptr) {
|
||||||
|
if (!net_driven_by(ctx, ofx0_0, is_l6mux, ctx->id("Z")) &&
|
||||||
|
!net_driven_by(ctx, ofx0_0, is_lc, ctx->id("OFX1")))
|
||||||
|
log_error("L6MUX21 '%s' has D0 driven by cell other than a SLICE OFX0 but not a LUT7 mux "
|
||||||
|
"('%s.%s')\n",
|
||||||
|
ci->name.c_str(ctx), ofx0_0->driver.cell->name.c_str(ctx),
|
||||||
|
ofx0_0->driver.port.c_str(ctx));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (slice1 == nullptr) {
|
||||||
|
if (!net_driven_by(ctx, ofx0_1, is_l6mux, ctx->id("Z")) &&
|
||||||
|
!net_driven_by(ctx, ofx0_1, is_lc, ctx->id("OFX1")))
|
||||||
|
log_error("L6MUX21 '%s' has D1 driven by cell other than a SLICE OFX0 but not a LUT7 mux "
|
||||||
|
"('%s.%s')\n",
|
||||||
|
ci->name.c_str(ctx), ofx0_0->driver.cell->name.c_str(ctx),
|
||||||
|
ofx0_0->driver.port.c_str(ctx));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (ctx->verbose)
|
||||||
|
log_info(" mux '%s' forms part of a LUT6\n", cell.first.c_str(ctx));
|
||||||
|
replace_port(ci, ctx->id("D0"), slice1, id_FXA);
|
||||||
|
replace_port(ci, ctx->id("D1"), slice1, id_FXB);
|
||||||
|
replace_port(ci, ctx->id("SD"), slice1, id_M1);
|
||||||
|
replace_port(ci, ctx->id("Z"), slice1, id_OFX1);
|
||||||
|
slice0->constr_z = 1;
|
||||||
|
slice0->constr_x = 0;
|
||||||
|
slice0->constr_y = 0;
|
||||||
|
slice0->constr_parent = slice1;
|
||||||
|
slice1->constr_z = 0;
|
||||||
|
slice1->constr_abs_z = true;
|
||||||
|
slice1->constr_children.push_back(slice0);
|
||||||
|
|
||||||
|
if (lutffPairs.find(ci->name) != lutffPairs.end()) {
|
||||||
|
CellInfo *ff = ctx->cells.at(lutffPairs[ci->name]).get();
|
||||||
|
ff_to_slice(ctx, ff, slice1, 1, true);
|
||||||
|
packed_cells.insert(ff->name);
|
||||||
|
sliceUsage[slice1->name].ff1_used = true;
|
||||||
|
lutffPairs.erase(ci->name);
|
||||||
|
fflutPairs.erase(ff->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
packed_cells.insert(ci->name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
flush_cells();
|
||||||
|
// Pack LUT7s
|
||||||
|
for (auto cell : sorted(ctx->cells)) {
|
||||||
|
CellInfo *ci = cell.second;
|
||||||
|
if (is_l6mux(ctx, ci)) {
|
||||||
|
NetInfo *ofx1_0 = ci->ports.at(ctx->id("D0")).net;
|
||||||
|
if (ofx1_0 == nullptr)
|
||||||
|
log_error("L6MUX21 '%s' has disconnected port 'D0'\n", ci->name.c_str(ctx));
|
||||||
|
NetInfo *ofx1_1 = ci->ports.at(ctx->id("D1")).net;
|
||||||
|
if (ofx1_1 == nullptr)
|
||||||
|
log_error("L6MUX21 '%s' has disconnected port 'D1'\n", ci->name.c_str(ctx));
|
||||||
|
CellInfo *slice1 = net_driven_by(ctx, ofx1_0, is_lc, ctx->id("OFX1"));
|
||||||
|
CellInfo *slice3 = net_driven_by(ctx, ofx1_1, is_lc, ctx->id("OFX1"));
|
||||||
|
if (slice1 == nullptr)
|
||||||
|
log_error("L6MUX21 '%s' has D0 driven by cell other than a SLICE OFX ('%s.%s')\n",
|
||||||
|
ci->name.c_str(ctx), ofx1_0->driver.cell->name.c_str(ctx),
|
||||||
|
ofx1_0->driver.port.c_str(ctx));
|
||||||
|
if (slice3 == nullptr)
|
||||||
|
log_error("L6MUX21 '%s' has D1 driven by cell other than a SLICE OFX ('%s.%s')\n",
|
||||||
|
ci->name.c_str(ctx), ofx1_1->driver.cell->name.c_str(ctx),
|
||||||
|
ofx1_1->driver.port.c_str(ctx));
|
||||||
|
|
||||||
|
NetInfo *fxa_0 = slice1->ports.at(id_FXA).net;
|
||||||
|
if (fxa_0 == nullptr)
|
||||||
|
log_error("SLICE '%s' has disconnected port 'FXA'\n", slice1->name.c_str(ctx));
|
||||||
|
NetInfo *fxa_1 = slice3->ports.at(id_FXA).net;
|
||||||
|
if (fxa_1 == nullptr)
|
||||||
|
log_error("SLICE '%s' has disconnected port 'FXA'\n", slice3->name.c_str(ctx));
|
||||||
|
|
||||||
|
CellInfo *slice0 = net_driven_by(ctx, fxa_0, is_lc, ctx->id("OFX0"));
|
||||||
|
CellInfo *slice2 = net_driven_by(ctx, fxa_1, is_lc, ctx->id("OFX0"));
|
||||||
|
if (slice0 == nullptr)
|
||||||
|
log_error("SLICE '%s' has FXA driven by cell other than a SLICE OFX0 ('%s.%s')\n",
|
||||||
|
slice1->name.c_str(ctx), fxa_0->driver.cell->name.c_str(ctx),
|
||||||
|
fxa_0->driver.port.c_str(ctx));
|
||||||
|
if (slice2 == nullptr)
|
||||||
|
log_error("SLICE '%s' has FXA driven by cell other than a SLICE OFX0 ('%s.%s')\n",
|
||||||
|
slice3->name.c_str(ctx), fxa_1->driver.cell->name.c_str(ctx),
|
||||||
|
fxa_1->driver.port.c_str(ctx));
|
||||||
|
|
||||||
|
replace_port(ci, ctx->id("D0"), slice2, id_FXA);
|
||||||
|
replace_port(ci, ctx->id("D1"), slice2, id_FXB);
|
||||||
|
replace_port(ci, ctx->id("SD"), slice2, id_M1);
|
||||||
|
replace_port(ci, ctx->id("Z"), slice2, id_OFX1);
|
||||||
|
|
||||||
|
for (auto slice : {slice0, slice1, slice2, slice3}) {
|
||||||
|
slice->constr_children.clear();
|
||||||
|
slice->constr_abs_z = false;
|
||||||
|
slice->constr_x = slice->UNCONSTR;
|
||||||
|
slice->constr_y = slice->UNCONSTR;
|
||||||
|
slice->constr_z = slice->UNCONSTR;
|
||||||
|
slice->constr_parent = nullptr;
|
||||||
|
}
|
||||||
|
slice3->constr_children.clear();
|
||||||
|
slice3->constr_abs_z = true;
|
||||||
|
slice3->constr_z = 0;
|
||||||
|
|
||||||
|
slice2->constr_children.clear();
|
||||||
|
slice2->constr_abs_z = true;
|
||||||
|
slice2->constr_z = 1;
|
||||||
|
slice2->constr_x = 0;
|
||||||
|
slice2->constr_y = 0;
|
||||||
|
slice2->constr_parent = slice3;
|
||||||
|
slice3->constr_children.push_back(slice2);
|
||||||
|
|
||||||
|
slice1->constr_children.clear();
|
||||||
|
slice1->constr_abs_z = true;
|
||||||
|
slice1->constr_z = 2;
|
||||||
|
slice1->constr_x = 0;
|
||||||
|
slice1->constr_y = 0;
|
||||||
|
slice1->constr_parent = slice3;
|
||||||
|
slice3->constr_children.push_back(slice1);
|
||||||
|
|
||||||
|
slice0->constr_children.clear();
|
||||||
|
slice0->constr_abs_z = true;
|
||||||
|
slice0->constr_z = 3;
|
||||||
|
slice0->constr_x = 0;
|
||||||
|
slice0->constr_y = 0;
|
||||||
|
slice0->constr_parent = slice3;
|
||||||
|
slice3->constr_children.push_back(slice0);
|
||||||
|
|
||||||
|
if (lutffPairs.find(ci->name) != lutffPairs.end()) {
|
||||||
|
CellInfo *ff = ctx->cells.at(lutffPairs[ci->name]).get();
|
||||||
|
ff_to_slice(ctx, ff, slice2, 1, true);
|
||||||
|
packed_cells.insert(ff->name);
|
||||||
|
sliceUsage[slice2->name].ff1_used = true;
|
||||||
|
lutffPairs.erase(ci->name);
|
||||||
|
fflutPairs.erase(ff->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
packed_cells.insert(ci->name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
flush_cells();
|
||||||
|
}
|
||||||
// Create a feed in to the carry chain
|
// Create a feed in to the carry chain
|
||||||
CellInfo *make_carry_feed_in(NetInfo *carry, PortRef chain_in)
|
CellInfo *make_carry_feed_in(NetInfo *carry, PortRef chain_in)
|
||||||
{
|
{
|
||||||
@ -1183,7 +1334,7 @@ class Ecp5Packer
|
|||||||
pack_dram();
|
pack_dram();
|
||||||
pack_carries();
|
pack_carries();
|
||||||
find_lutff_pairs();
|
find_lutff_pairs();
|
||||||
pack_lut5s();
|
pack_lut5xs();
|
||||||
pair_luts();
|
pair_luts();
|
||||||
pack_lut_pairs();
|
pack_lut_pairs();
|
||||||
pack_remaining_luts();
|
pack_remaining_luts();
|
||||||
@ -1252,6 +1403,10 @@ void Arch::assignArchInfo()
|
|||||||
ci->sliceInfo.clkmux = id(str_or_default(ci->params, id_CLKMUX, "CLK"));
|
ci->sliceInfo.clkmux = id(str_or_default(ci->params, id_CLKMUX, "CLK"));
|
||||||
ci->sliceInfo.lsrmux = id(str_or_default(ci->params, id_LSRMUX, "LSR"));
|
ci->sliceInfo.lsrmux = id(str_or_default(ci->params, id_LSRMUX, "LSR"));
|
||||||
ci->sliceInfo.srmode = id(str_or_default(ci->params, id_SRMODE, "LSR_OVER_CE"));
|
ci->sliceInfo.srmode = id(str_or_default(ci->params, id_SRMODE, "LSR_OVER_CE"));
|
||||||
|
ci->sliceInfo.has_l6mux = false;
|
||||||
|
if (ci->ports.count(id_FXA) && ci->ports[id_FXA].net != nullptr &&
|
||||||
|
ci->ports[id_FXA].net->driver.port == id_OFX0)
|
||||||
|
ci->sliceInfo.has_l6mux = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user