gowin: add support for wide LUTs.
* A hardwired MUX within each logical cell is used. * The delay is equal 0. * No user placement constraints. * The output route contains dummy PIPs. They are ignored by gowin_pack, but it may be worth removing them. Signed-off-by: YRabbit <rabbit@yrabbit.cyou>
This commit is contained in:
parent
4f17a1711a
commit
c72ea15472
108
gowin/arch.cc
108
gowin/arch.cc
@ -583,6 +583,87 @@ void Arch::read_cst(std::istream &in)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add all MUXes for the cell
|
||||||
|
void Arch::addMuxBels(const DatabasePOD *db, int row, int col)
|
||||||
|
{
|
||||||
|
IdString belname, bel_id;
|
||||||
|
char buf[40];
|
||||||
|
int z;
|
||||||
|
// XXX do real delay
|
||||||
|
DelayQuad delay = DelayQuad(0);
|
||||||
|
// make all wide luts with these parameters
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
char type; // MUX type 5,6,7,8
|
||||||
|
char bel_idx; // just bel name suffix
|
||||||
|
char in_prefix[2]; // input from F or OF
|
||||||
|
char in_idx[2]; // input from bel with idx
|
||||||
|
} const mux_names[] = {{'5', '0', "", {'0', '1'}}, {'6', '0', "O", {'2', '0'}}, {'5', '1', "", {'2', '3'}},
|
||||||
|
{'7', '0', "O", {'5', '1'}}, {'5', '2', "", {'4', '5'}}, {'6', '1', "O", {'6', '4'}},
|
||||||
|
{'5', '3', "", {'6', '7'}}, {'8', '0', "O", {'3', '3'}}};
|
||||||
|
|
||||||
|
// 4 MUX2_LUT5, 2 MUX2_LUT6, 1 MUX2_LUT7, 1 MUX2_LUT8
|
||||||
|
for (int j = 0; j < 8; ++j) {
|
||||||
|
z = j + mux_0_z;
|
||||||
|
|
||||||
|
int grow = row + 1;
|
||||||
|
int gcol = col + 1;
|
||||||
|
|
||||||
|
// no MUX2_LUT8 in the last column
|
||||||
|
if (j == 7 && col == getGridDimX() - 1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// bel
|
||||||
|
snprintf(buf, 40, "R%dC%d_MUX2_LUT%c%c", grow, gcol, mux_names[j].type, mux_names[j].bel_idx);
|
||||||
|
belname = id(buf);
|
||||||
|
snprintf(buf, 40, "GW_MUX2_LUT%c", mux_names[j].type);
|
||||||
|
bel_id = id(buf);
|
||||||
|
addBel(belname, bel_id, Loc(col, row, z), false);
|
||||||
|
|
||||||
|
// dummy wires
|
||||||
|
snprintf(buf, 40, "I0MUX%d", j);
|
||||||
|
IdString id_wire_i0 = id(buf);
|
||||||
|
IdString wire_i0_name = wireToGlobal(row, col, db, id_wire_i0);
|
||||||
|
addWire(wire_i0_name, id_wire_i0, col, row);
|
||||||
|
|
||||||
|
snprintf(buf, 40, "I1MUX%d", j);
|
||||||
|
IdString id_wire_i1 = id(buf);
|
||||||
|
IdString wire_i1_name = wireToGlobal(row, col, db, id_wire_i1);
|
||||||
|
addWire(wire_i1_name, id_wire_i1, col, row);
|
||||||
|
|
||||||
|
// dummy left pip
|
||||||
|
snprintf(buf, 40, "%sF%c", mux_names[j].in_prefix, mux_names[j].in_idx[0]);
|
||||||
|
IdString id_src_F = id(buf);
|
||||||
|
// LUT8's I0 is wired to the right cell
|
||||||
|
IdString src_F;
|
||||||
|
int src_col = col;
|
||||||
|
if (j == 7) {
|
||||||
|
++src_col;
|
||||||
|
}
|
||||||
|
src_F = wireToGlobal(row, src_col, db, id_src_F);
|
||||||
|
snprintf(buf, 40, "R%dC%d_%s__%s", grow, gcol, id_src_F.c_str(this), id_wire_i0.c_str(this));
|
||||||
|
addPip(id(buf), id_wire_i0, src_F, wire_i0_name, delay, Loc(col, row, 0));
|
||||||
|
|
||||||
|
// dummy right pip
|
||||||
|
snprintf(buf, 40, "%sF%c", mux_names[j].in_prefix, mux_names[j].in_idx[1]);
|
||||||
|
id_src_F = id(buf);
|
||||||
|
src_F = wireToGlobal(row, col, db, id_src_F);
|
||||||
|
snprintf(buf, 40, "R%dC%d_%s__%s", grow, gcol, id_src_F.c_str(this), id_wire_i1.c_str(this));
|
||||||
|
addPip(id(buf), id_wire_i1, src_F, wire_i1_name, delay, Loc(col, row, 0));
|
||||||
|
|
||||||
|
// the MUX ports
|
||||||
|
snprintf(buf, 40, "R%dC%d_OF%d", grow, gcol, j);
|
||||||
|
addBelOutput(belname, id_OF, id(buf));
|
||||||
|
snprintf(buf, 40, "R%dC%d_SEL%d", grow, gcol, j);
|
||||||
|
addBelInput(belname, id_SEL, id(buf));
|
||||||
|
snprintf(buf, 40, "R%dC%d_I0MUX%d", grow, gcol, j);
|
||||||
|
addBelInput(belname, id_I0, id(buf));
|
||||||
|
snprintf(buf, 40, "R%dC%d_I1MUX%d", grow, gcol, j);
|
||||||
|
addBelInput(belname, id_I1, id(buf));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Arch::Arch(ArchArgs args) : args(args)
|
Arch::Arch(ArchArgs args) : args(args)
|
||||||
{
|
{
|
||||||
family = args.family;
|
family = args.family;
|
||||||
@ -645,7 +726,9 @@ Arch::Arch(ArchArgs args) : args(args)
|
|||||||
}
|
}
|
||||||
// setup db
|
// setup db
|
||||||
char buf[32];
|
char buf[32];
|
||||||
for (int i = 0; i < db->rows * db->cols; i++) {
|
// The reverse order of the enumeration simplifies the creation
|
||||||
|
// of MUX2_LUT8s: they need the existence of the wire on the right.
|
||||||
|
for (int i = db->rows * db->cols - 1; i >= 0; --i) {
|
||||||
int row = i / db->cols;
|
int row = i / db->cols;
|
||||||
int col = i % db->cols;
|
int col = i % db->cols;
|
||||||
const TilePOD *tile = db->grid[i].get();
|
const TilePOD *tile = db->grid[i].get();
|
||||||
@ -718,6 +801,9 @@ Arch::Arch(ArchArgs args) : args(args)
|
|||||||
snprintf(buf, 32, "R%dC%d_Q%d", row + 1, col + 1, z);
|
snprintf(buf, 32, "R%dC%d_Q%d", row + 1, col + 1, z);
|
||||||
addBelOutput(belname, id_Q, id(buf));
|
addBelOutput(belname, id_Q, id(buf));
|
||||||
}
|
}
|
||||||
|
if (z == 0) {
|
||||||
|
addMuxBels(db, row, col);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case ID_IOBJ:
|
case ID_IOBJ:
|
||||||
z++; /* fall-through*/
|
z++; /* fall-through*/
|
||||||
@ -1094,6 +1180,7 @@ ArcBounds Arch::getRouteBoundingBox(WireId src, WireId dst) const
|
|||||||
bool Arch::place()
|
bool Arch::place()
|
||||||
{
|
{
|
||||||
std::string placer = str_or_default(settings, id("placer"), defaultPlacer);
|
std::string placer = str_or_default(settings, id("placer"), defaultPlacer);
|
||||||
|
bool retVal;
|
||||||
if (placer == "heap") {
|
if (placer == "heap") {
|
||||||
bool have_iobuf_or_constr = false;
|
bool have_iobuf_or_constr = false;
|
||||||
for (auto &cell : cells) {
|
for (auto &cell : cells) {
|
||||||
@ -1103,7 +1190,6 @@ bool Arch::place()
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
bool retVal;
|
|
||||||
if (!have_iobuf_or_constr) {
|
if (!have_iobuf_or_constr) {
|
||||||
log_warning("Unable to use HeAP due to a lack of IO buffers or constrained cells as anchors; reverting to "
|
log_warning("Unable to use HeAP due to a lack of IO buffers or constrained cells as anchors; reverting to "
|
||||||
"SA.\n");
|
"SA.\n");
|
||||||
@ -1116,15 +1202,21 @@ bool Arch::place()
|
|||||||
}
|
}
|
||||||
getCtx()->settings[getCtx()->id("place")] = 1;
|
getCtx()->settings[getCtx()->id("place")] = 1;
|
||||||
archInfoToAttributes();
|
archInfoToAttributes();
|
||||||
return retVal;
|
|
||||||
} else if (placer == "sa") {
|
} else if (placer == "sa") {
|
||||||
bool retVal = placer1(getCtx(), Placer1Cfg(getCtx()));
|
retVal = placer1(getCtx(), Placer1Cfg(getCtx()));
|
||||||
getCtx()->settings[getCtx()->id("place")] = 1;
|
getCtx()->settings[getCtx()->id("place")] = 1;
|
||||||
archInfoToAttributes();
|
archInfoToAttributes();
|
||||||
return retVal;
|
return retVal;
|
||||||
} else {
|
} else {
|
||||||
log_error("Gowin architecture does not support placer '%s'\n", placer.c_str());
|
log_error("Gowin architecture does not support placer '%s'\n", placer.c_str());
|
||||||
}
|
}
|
||||||
|
// debug placement
|
||||||
|
if (getCtx()->debug) {
|
||||||
|
for (auto &cell : getCtx()->cells) {
|
||||||
|
log_info("Placed: %s -> %s\n", cell.first.c_str(getCtx()), getCtx()->nameOfBel(cell.second->bel));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Arch::route()
|
bool Arch::route()
|
||||||
@ -1183,13 +1275,15 @@ TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port
|
|||||||
|
|
||||||
bool Arch::isBelLocationValid(BelId bel) const
|
bool Arch::isBelLocationValid(BelId bel) const
|
||||||
{
|
{
|
||||||
std::vector<const CellInfo *> cells;
|
|
||||||
Loc loc = getBelLocation(bel);
|
Loc loc = getBelLocation(bel);
|
||||||
|
|
||||||
|
std::vector<const CellInfo *> cells;
|
||||||
for (auto tbel : getBelsByTile(loc.x, loc.y)) {
|
for (auto tbel : getBelsByTile(loc.x, loc.y)) {
|
||||||
CellInfo *bound = getBoundBelCell(tbel);
|
CellInfo *bound = getBoundBelCell(tbel);
|
||||||
if (bound != nullptr)
|
if (bound != nullptr)
|
||||||
cells.push_back(bound);
|
cells.push_back(bound);
|
||||||
}
|
}
|
||||||
|
|
||||||
return cellsCompatible(cells.data(), int(cells.size()));
|
return cellsCompatible(cells.data(), int(cells.size()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1213,6 +1307,7 @@ void Arch::assignArchInfo()
|
|||||||
for (auto &cell : getCtx()->cells) {
|
for (auto &cell : getCtx()->cells) {
|
||||||
IdString cname = cell.first;
|
IdString cname = cell.first;
|
||||||
CellInfo *ci = cell.second.get();
|
CellInfo *ci = cell.second.get();
|
||||||
|
ci->is_slice = false;
|
||||||
if (ci->type == id("SLICE")) {
|
if (ci->type == id("SLICE")) {
|
||||||
ci->is_slice = true;
|
ci->is_slice = true;
|
||||||
ci->ff_used = ci->params.at(id_FF_USED).as_bool();
|
ci->ff_used = ci->params.at(id_FF_USED).as_bool();
|
||||||
@ -1238,9 +1333,6 @@ void Arch::assignArchInfo()
|
|||||||
DelayQuad delay = delayLookup(speed->lut.timings.get(), speed->lut.num_timings, port_delay[i]);
|
DelayQuad delay = delayLookup(speed->lut.timings.get(), speed->lut.num_timings, port_delay[i]);
|
||||||
addCellTimingDelay(cname, ports[i], id_F, delay);
|
addCellTimingDelay(cname, ports[i], id_F, delay);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
|
||||||
ci->is_slice = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -330,6 +330,7 @@ struct Arch : BaseArch<ArchRanges>
|
|||||||
IdString wireToGlobal(int &row, int &col, const DatabasePOD *db, IdString &wire);
|
IdString wireToGlobal(int &row, int &col, const DatabasePOD *db, IdString &wire);
|
||||||
DelayQuad getWireTypeDelay(IdString wire);
|
DelayQuad getWireTypeDelay(IdString wire);
|
||||||
void read_cst(std::istream &in);
|
void read_cst(std::istream &in);
|
||||||
|
void addMuxBels(const DatabasePOD *db, int row, int col);
|
||||||
|
|
||||||
// ---------------------------------------------------------------
|
// ---------------------------------------------------------------
|
||||||
// Common Arch API. Every arch must provide the following methods.
|
// Common Arch API. Every arch must provide the following methods.
|
||||||
@ -444,6 +445,8 @@ struct Arch : BaseArch<ArchRanges>
|
|||||||
// Internal usage
|
// Internal usage
|
||||||
void assignArchInfo() override;
|
void assignArchInfo() override;
|
||||||
bool cellsCompatible(const CellInfo **cells, int count) const;
|
bool cellsCompatible(const CellInfo **cells, int count) const;
|
||||||
|
// start Z for the MUX2LUT5 bels
|
||||||
|
int const mux_0_z = 10;
|
||||||
|
|
||||||
std::vector<IdString> cell_types;
|
std::vector<IdString> cell_types;
|
||||||
|
|
||||||
|
@ -65,6 +65,7 @@ struct ArchCellInfo : BaseClusterInfo
|
|||||||
IdString ff_type;
|
IdString ff_type;
|
||||||
// Is a slice type primitive
|
// Is a slice type primitive
|
||||||
bool is_slice;
|
bool is_slice;
|
||||||
|
|
||||||
// Only packing rule for slice type primitives is a single clock per tile
|
// Only packing rule for slice type primitives is a single clock per tile
|
||||||
const NetInfo *slice_clk;
|
const NetInfo *slice_clk;
|
||||||
const NetInfo *slice_ce;
|
const NetInfo *slice_ce;
|
||||||
|
@ -58,6 +58,10 @@ std::unique_ptr<CellInfo> create_generic_cell(Context *ctx, IdString type, std::
|
|||||||
add_port(ctx, new_cell.get(), id_Q, PORT_OUT);
|
add_port(ctx, new_cell.get(), id_Q, PORT_OUT);
|
||||||
add_port(ctx, new_cell.get(), id_CE, PORT_IN);
|
add_port(ctx, new_cell.get(), id_CE, PORT_IN);
|
||||||
add_port(ctx, new_cell.get(), id_LSR, PORT_IN);
|
add_port(ctx, new_cell.get(), id_LSR, PORT_IN);
|
||||||
|
} else if (type == id_GW_MUX2_LUT5 || type == id_GW_MUX2_LUT6 || type == id_GW_MUX2_LUT7 ||
|
||||||
|
type == id_GW_MUX2_LUT7 || type == id_GW_MUX2_LUT8) {
|
||||||
|
add_port(ctx, new_cell.get(), id_SEL, PORT_IN);
|
||||||
|
add_port(ctx, new_cell.get(), id_OF, PORT_OUT);
|
||||||
} else if (type == id_IOB) {
|
} else if (type == id_IOB) {
|
||||||
new_cell->params[id_INPUT_USED] = 0;
|
new_cell->params[id_INPUT_USED] = 0;
|
||||||
new_cell->params[id_OUTPUT_USED] = 0;
|
new_cell->params[id_OUTPUT_USED] = 0;
|
||||||
@ -68,7 +72,7 @@ std::unique_ptr<CellInfo> create_generic_cell(Context *ctx, IdString type, std::
|
|||||||
add_port(ctx, new_cell.get(), id_EN, PORT_IN);
|
add_port(ctx, new_cell.get(), id_EN, PORT_IN);
|
||||||
add_port(ctx, new_cell.get(), id_O, PORT_OUT);
|
add_port(ctx, new_cell.get(), id_O, PORT_OUT);
|
||||||
} else {
|
} else {
|
||||||
log_error("unable to create generic cell of type %s", type.c_str(ctx));
|
log_error("unable to create generic cell of type %s\n", type.c_str(ctx));
|
||||||
}
|
}
|
||||||
return new_cell;
|
return new_cell;
|
||||||
}
|
}
|
||||||
@ -76,6 +80,23 @@ std::unique_ptr<CellInfo> create_generic_cell(Context *ctx, IdString type, std::
|
|||||||
void lut_to_lc(const Context *ctx, CellInfo *lut, CellInfo *lc, bool no_dff)
|
void lut_to_lc(const Context *ctx, CellInfo *lut, CellInfo *lc, bool no_dff)
|
||||||
{
|
{
|
||||||
lc->params[id_INIT] = lut->params[id_INIT];
|
lc->params[id_INIT] = lut->params[id_INIT];
|
||||||
|
lc->cluster = lut->cluster;
|
||||||
|
lc->constr_x = lut->constr_x;
|
||||||
|
lc->constr_y = lut->constr_y;
|
||||||
|
lc->constr_z = lut->constr_z;
|
||||||
|
|
||||||
|
// add itself to the cluster root children list
|
||||||
|
if (lc->cluster != ClusterId()) {
|
||||||
|
CellInfo *cluster_root = ctx->cells.at(lc->cluster).get();
|
||||||
|
lc->constr_x += cluster_root->constr_x;
|
||||||
|
lc->constr_y += cluster_root->constr_y;
|
||||||
|
lc->constr_z += cluster_root->constr_z;
|
||||||
|
if (cluster_root->cluster != cluster_root->name) {
|
||||||
|
lc->cluster = cluster_root->cluster;
|
||||||
|
cluster_root = ctx->cells.at(cluster_root->cluster).get();
|
||||||
|
}
|
||||||
|
cluster_root->constr_children.push_back(lc);
|
||||||
|
}
|
||||||
|
|
||||||
IdString sim_names[4] = {id_I0, id_I1, id_I2, id_I3};
|
IdString sim_names[4] = {id_I0, id_I1, id_I2, id_I3};
|
||||||
IdString wire_names[4] = {id_A, id_B, id_C, id_D};
|
IdString wire_names[4] = {id_A, id_B, id_C, id_D};
|
||||||
|
@ -43,6 +43,40 @@ inline bool is_lut(const BaseCtx *ctx, const CellInfo *cell)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Return true if a cell is a wide LUT mux
|
||||||
|
inline bool is_widelut(const BaseCtx *ctx, const CellInfo *cell)
|
||||||
|
{
|
||||||
|
switch (cell->type.index) {
|
||||||
|
case ID_MUX2_LUT5:
|
||||||
|
case ID_MUX2_LUT6:
|
||||||
|
case ID_MUX2_LUT7:
|
||||||
|
case ID_MUX2_LUT8:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// is MUX2_LUT5
|
||||||
|
inline bool is_mux2_lut5(const BaseCtx *ctx, const CellInfo *cell) { return (cell->type.index == ID_MUX2_LUT5); }
|
||||||
|
|
||||||
|
inline bool is_gw_mux2_lut5(const BaseCtx *ctx, const CellInfo *cell) { return (cell->type.index == ID_GW_MUX2_LUT5); }
|
||||||
|
|
||||||
|
// is MUX2_LUT6
|
||||||
|
inline bool is_mux2_lut6(const BaseCtx *ctx, const CellInfo *cell) { return (cell->type.index == ID_MUX2_LUT6); }
|
||||||
|
|
||||||
|
inline bool is_gw_mux2_lut6(const BaseCtx *ctx, const CellInfo *cell) { return (cell->type.index == ID_GW_MUX2_LUT6); }
|
||||||
|
|
||||||
|
// is MUX2_LUT7
|
||||||
|
inline bool is_mux2_lut7(const BaseCtx *ctx, const CellInfo *cell) { return (cell->type.index == ID_MUX2_LUT7); }
|
||||||
|
|
||||||
|
inline bool is_gw_mux2_lut7(const BaseCtx *ctx, const CellInfo *cell) { return (cell->type.index == ID_GW_MUX2_LUT7); }
|
||||||
|
|
||||||
|
// is MUX2_LUT8
|
||||||
|
inline bool is_mux2_lut8(const BaseCtx *ctx, const CellInfo *cell) { return (cell->type.index == ID_MUX2_LUT8); }
|
||||||
|
|
||||||
|
inline bool is_gw_mux2_lut8(const BaseCtx *ctx, const CellInfo *cell) { return (cell->type.index == ID_GW_MUX2_LUT8); }
|
||||||
|
|
||||||
// Return true if a cell is a flipflop
|
// Return true if a cell is a flipflop
|
||||||
inline bool is_ff(const BaseCtx *ctx, const CellInfo *cell)
|
inline bool is_ff(const BaseCtx *ctx, const CellInfo *cell)
|
||||||
{
|
{
|
||||||
|
@ -358,6 +358,16 @@ X(IOBH)
|
|||||||
X(IOBI)
|
X(IOBI)
|
||||||
X(IOBJ)
|
X(IOBJ)
|
||||||
|
|
||||||
|
// Wide LUTs
|
||||||
|
X(MUX2_LUT5)
|
||||||
|
X(MUX2_LUT6)
|
||||||
|
X(MUX2_LUT7)
|
||||||
|
X(MUX2_LUT8)
|
||||||
|
X(GW_MUX2_LUT5)
|
||||||
|
X(GW_MUX2_LUT6)
|
||||||
|
X(GW_MUX2_LUT7)
|
||||||
|
X(GW_MUX2_LUT8)
|
||||||
|
|
||||||
// DFF types
|
// DFF types
|
||||||
X(DFF)
|
X(DFF)
|
||||||
X(DFFE)
|
X(DFFE)
|
||||||
@ -409,6 +419,9 @@ X(I1)
|
|||||||
X(I2)
|
X(I2)
|
||||||
X(I3)
|
X(I3)
|
||||||
X(OEN)
|
X(OEN)
|
||||||
|
X(S0)
|
||||||
|
X(SEL)
|
||||||
|
X(OF)
|
||||||
|
|
||||||
// timing
|
// timing
|
||||||
X(X0)
|
X(X0)
|
||||||
|
261
gowin/pack.cc
261
gowin/pack.cc
@ -28,6 +28,266 @@
|
|||||||
|
|
||||||
NEXTPNR_NAMESPACE_BEGIN
|
NEXTPNR_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
// pack MUX2_LUT5
|
||||||
|
static void pack_mux2_lut5(Context *ctx, CellInfo *ci, pool<IdString> &packed_cells, pool<IdString> &delete_nets,
|
||||||
|
std::vector<std::unique_ptr<CellInfo>> &new_cells)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (bool_or_default(ci->attrs, ctx->id("SINGLE_INPUT_MUX"))) {
|
||||||
|
// find the muxed LUT
|
||||||
|
NetInfo *i1 = ci->ports.at(id_I1).net;
|
||||||
|
|
||||||
|
CellInfo *lut1 = net_driven_by(ctx, i1, is_lut, id_F);
|
||||||
|
if (lut1 == nullptr) {
|
||||||
|
log_error("MUX2_LUT5 '%s' port I1 isn't connected to the LUT\n", ci->name.c_str(ctx));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (ctx->verbose) {
|
||||||
|
log_info("found attached lut1 %s\n", ctx->nameOf(lut1));
|
||||||
|
}
|
||||||
|
|
||||||
|
// XXX enable the placement constraints
|
||||||
|
auto mux_bel = ci->attrs.find(ctx->id("BEL"));
|
||||||
|
auto lut1_bel = lut1->attrs.find(ctx->id("BEL"));
|
||||||
|
if (lut1_bel != lut1->attrs.end() || mux_bel != ci->attrs.end()) {
|
||||||
|
log_error("MUX2_LUT5 '%s' placement restrictions are not yet supported\n", ci->name.c_str(ctx));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<CellInfo> packed = create_generic_cell(ctx, ctx->id("GW_MUX2_LUT5"), ci->name.str(ctx) + "_LC");
|
||||||
|
if (ctx->verbose) {
|
||||||
|
log_info("packed cell %s into %s\n", ctx->nameOf(ci), ctx->nameOf(packed.get()));
|
||||||
|
}
|
||||||
|
// mux is the cluster root
|
||||||
|
packed->cluster = packed->name;
|
||||||
|
lut1->cluster = packed->name;
|
||||||
|
lut1->constr_z = -ctx->mux_0_z + 1;
|
||||||
|
packed->constr_children.clear();
|
||||||
|
|
||||||
|
// reconnect MUX ports
|
||||||
|
replace_port(ci, id_O, packed.get(), id_OF);
|
||||||
|
replace_port(ci, id_I1, packed.get(), id_I1);
|
||||||
|
|
||||||
|
// remove cells
|
||||||
|
packed_cells.insert(ci->name);
|
||||||
|
// new MUX cell
|
||||||
|
new_cells.push_back(std::move(packed));
|
||||||
|
} else {
|
||||||
|
// find the muxed LUTs
|
||||||
|
NetInfo *i0 = ci->ports.at(id_I0).net;
|
||||||
|
NetInfo *i1 = ci->ports.at(id_I1).net;
|
||||||
|
|
||||||
|
CellInfo *lut0 = net_driven_by(ctx, i0, is_lut, id_F);
|
||||||
|
CellInfo *lut1 = net_driven_by(ctx, i1, is_lut, id_F);
|
||||||
|
if (lut0 == nullptr || lut1 == nullptr) {
|
||||||
|
log_error("MUX2_LUT5 '%s' port I0 or I1 isn't connected to the LUT\n", ci->name.c_str(ctx));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (ctx->verbose) {
|
||||||
|
log_info("found attached lut0 %s\n", ctx->nameOf(lut0));
|
||||||
|
log_info("found attached lut1 %s\n", ctx->nameOf(lut1));
|
||||||
|
}
|
||||||
|
|
||||||
|
// XXX enable the placement constraints
|
||||||
|
auto mux_bel = ci->attrs.find(ctx->id("BEL"));
|
||||||
|
auto lut0_bel = lut0->attrs.find(ctx->id("BEL"));
|
||||||
|
auto lut1_bel = lut1->attrs.find(ctx->id("BEL"));
|
||||||
|
if (lut0_bel != lut0->attrs.end() || lut1_bel != lut1->attrs.end() || mux_bel != ci->attrs.end()) {
|
||||||
|
log_error("MUX2_LUT5 '%s' placement restrictions are not yet supported\n", ci->name.c_str(ctx));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<CellInfo> packed = create_generic_cell(ctx, ctx->id("GW_MUX2_LUT5"), ci->name.str(ctx) + "_LC");
|
||||||
|
if (ctx->verbose) {
|
||||||
|
log_info("packed cell %s into %s\n", ctx->nameOf(ci), ctx->nameOf(packed.get()));
|
||||||
|
}
|
||||||
|
// mux is the cluster root
|
||||||
|
packed->cluster = packed->name;
|
||||||
|
lut0->cluster = packed->name;
|
||||||
|
lut0->constr_z = -ctx->mux_0_z;
|
||||||
|
lut1->cluster = packed->name;
|
||||||
|
lut1->constr_z = -ctx->mux_0_z + 1;
|
||||||
|
packed->constr_children.clear();
|
||||||
|
|
||||||
|
// reconnect MUX ports
|
||||||
|
replace_port(ci, id_O, packed.get(), id_OF);
|
||||||
|
replace_port(ci, id_S0, packed.get(), id_SEL);
|
||||||
|
replace_port(ci, id_I0, packed.get(), id_I0);
|
||||||
|
replace_port(ci, id_I1, packed.get(), id_I1);
|
||||||
|
|
||||||
|
// remove cells
|
||||||
|
packed_cells.insert(ci->name);
|
||||||
|
// new MUX cell
|
||||||
|
new_cells.push_back(std::move(packed));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Common MUX2 packing routine
|
||||||
|
static void pack_mux2_lut(Context *ctx, CellInfo *ci, bool (*pred)(const BaseCtx *, const CellInfo *),
|
||||||
|
char const type_suffix, IdString const type_id, int const x[2], int const z[2],
|
||||||
|
pool<IdString> &packed_cells, pool<IdString> &delete_nets,
|
||||||
|
std::vector<std::unique_ptr<CellInfo>> &new_cells)
|
||||||
|
{
|
||||||
|
// find the muxed LUTs
|
||||||
|
NetInfo *i0 = ci->ports.at(id_I0).net;
|
||||||
|
NetInfo *i1 = ci->ports.at(id_I1).net;
|
||||||
|
|
||||||
|
CellInfo *mux0 = net_driven_by(ctx, i0, pred, id_OF);
|
||||||
|
CellInfo *mux1 = net_driven_by(ctx, i1, pred, id_OF);
|
||||||
|
if (mux0 == nullptr || mux1 == nullptr) {
|
||||||
|
log_error("MUX2_LUT%c '%s' port I0 or I1 isn't connected to the MUX\n", type_suffix, ci->name.c_str(ctx));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (ctx->verbose) {
|
||||||
|
log_info("found attached mux0 %s\n", ctx->nameOf(mux0));
|
||||||
|
log_info("found attached mux1 %s\n", ctx->nameOf(mux1));
|
||||||
|
}
|
||||||
|
|
||||||
|
// XXX enable the placement constraints
|
||||||
|
auto mux_bel = ci->attrs.find(ctx->id("BEL"));
|
||||||
|
auto mux0_bel = mux0->attrs.find(ctx->id("BEL"));
|
||||||
|
auto mux1_bel = mux1->attrs.find(ctx->id("BEL"));
|
||||||
|
if (mux0_bel != mux0->attrs.end() || mux1_bel != mux1->attrs.end() || mux_bel != ci->attrs.end()) {
|
||||||
|
log_error("MUX2_LUT%c '%s' placement restrictions are not yet supported\n", type_suffix, ci->name.c_str(ctx));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<CellInfo> packed = create_generic_cell(ctx, type_id, ci->name.str(ctx) + "_LC");
|
||||||
|
if (ctx->verbose) {
|
||||||
|
log_info("packed cell %s into %s\n", ctx->nameOf(ci), ctx->nameOf(packed.get()));
|
||||||
|
}
|
||||||
|
// mux is the cluster root
|
||||||
|
packed->cluster = packed->name;
|
||||||
|
mux0->cluster = packed->name;
|
||||||
|
mux0->constr_x = x[0];
|
||||||
|
mux0->constr_z = z[0];
|
||||||
|
for (auto &child : mux0->constr_children) {
|
||||||
|
child->cluster = packed->name;
|
||||||
|
child->constr_x += mux0->constr_x;
|
||||||
|
child->constr_z += mux0->constr_z;
|
||||||
|
packed->constr_children.push_back(child);
|
||||||
|
}
|
||||||
|
mux0->constr_children.clear();
|
||||||
|
mux1->cluster = packed->name;
|
||||||
|
mux1->constr_x = x[1];
|
||||||
|
mux1->constr_z = z[1];
|
||||||
|
for (auto &child : mux1->constr_children) {
|
||||||
|
child->cluster = packed->name;
|
||||||
|
child->constr_x += mux1->constr_x;
|
||||||
|
child->constr_z += mux1->constr_z;
|
||||||
|
packed->constr_children.push_back(child);
|
||||||
|
}
|
||||||
|
mux1->constr_children.clear();
|
||||||
|
packed->constr_children.push_back(mux0);
|
||||||
|
packed->constr_children.push_back(mux1);
|
||||||
|
|
||||||
|
// reconnect MUX ports
|
||||||
|
replace_port(ci, id_O, packed.get(), id_OF);
|
||||||
|
replace_port(ci, id_S0, packed.get(), id_SEL);
|
||||||
|
replace_port(ci, id_I0, packed.get(), id_I0);
|
||||||
|
replace_port(ci, id_I1, packed.get(), id_I1);
|
||||||
|
|
||||||
|
// remove cells
|
||||||
|
packed_cells.insert(ci->name);
|
||||||
|
// new MUX cell
|
||||||
|
new_cells.push_back(std::move(packed));
|
||||||
|
}
|
||||||
|
|
||||||
|
// pack MUX2_LUT6
|
||||||
|
static void pack_mux2_lut6(Context *ctx, CellInfo *ci, pool<IdString> &packed_cells, pool<IdString> &delete_nets,
|
||||||
|
std::vector<std::unique_ptr<CellInfo>> &new_cells)
|
||||||
|
{
|
||||||
|
static int x[] = {0, 0};
|
||||||
|
static int z[] = {+1, -1};
|
||||||
|
pack_mux2_lut(ctx, ci, is_gw_mux2_lut5, '6', id_GW_MUX2_LUT6, x, z, packed_cells, delete_nets, new_cells);
|
||||||
|
}
|
||||||
|
|
||||||
|
// pack MUX2_LUT7
|
||||||
|
static void pack_mux2_lut7(Context *ctx, CellInfo *ci, pool<IdString> &packed_cells, pool<IdString> &delete_nets,
|
||||||
|
std::vector<std::unique_ptr<CellInfo>> &new_cells)
|
||||||
|
{
|
||||||
|
static int x[] = {0, 0};
|
||||||
|
static int z[] = {+2, -2};
|
||||||
|
pack_mux2_lut(ctx, ci, is_gw_mux2_lut6, '7', id_GW_MUX2_LUT7, x, z, packed_cells, delete_nets, new_cells);
|
||||||
|
}
|
||||||
|
|
||||||
|
// pack MUX2_LUT8
|
||||||
|
static void pack_mux2_lut8(Context *ctx, CellInfo *ci, pool<IdString> &packed_cells, pool<IdString> &delete_nets,
|
||||||
|
std::vector<std::unique_ptr<CellInfo>> &new_cells)
|
||||||
|
{
|
||||||
|
static int x[] = {1, 0};
|
||||||
|
static int z[] = {-4, -4};
|
||||||
|
pack_mux2_lut(ctx, ci, is_gw_mux2_lut7, '8', id_GW_MUX2_LUT8, x, z, packed_cells, delete_nets, new_cells);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pack wide LUTs
|
||||||
|
static void pack_wideluts(Context *ctx)
|
||||||
|
{
|
||||||
|
log_info("Packing wide LUTs..\n");
|
||||||
|
|
||||||
|
pool<IdString> packed_cells;
|
||||||
|
pool<IdString> delete_nets;
|
||||||
|
std::vector<std::unique_ptr<CellInfo>> new_cells;
|
||||||
|
|
||||||
|
pool<IdString> mux2lut6;
|
||||||
|
pool<IdString> mux2lut7;
|
||||||
|
pool<IdString> mux2lut8;
|
||||||
|
|
||||||
|
// do MUX2_LUT5 and collect LUT6/7/8
|
||||||
|
log_info("Packing LUT5s..\n");
|
||||||
|
for (auto &cell : ctx->cells) {
|
||||||
|
CellInfo *ci = cell.second.get();
|
||||||
|
if (ctx->verbose) {
|
||||||
|
log_info("cell '%s' is of type '%s'\n", ctx->nameOf(ci), ci->type.c_str(ctx));
|
||||||
|
}
|
||||||
|
if (is_widelut(ctx, ci)) {
|
||||||
|
if (is_mux2_lut5(ctx, ci)) {
|
||||||
|
pack_mux2_lut5(ctx, ci, packed_cells, delete_nets, new_cells);
|
||||||
|
} else {
|
||||||
|
if (is_mux2_lut6(ctx, ci)) {
|
||||||
|
mux2lut6.insert(ci->name);
|
||||||
|
} else {
|
||||||
|
if (is_mux2_lut7(ctx, ci)) {
|
||||||
|
mux2lut7.insert(ci->name);
|
||||||
|
} else {
|
||||||
|
if (is_mux2_lut8(ctx, ci)) {
|
||||||
|
mux2lut8.insert(ci->name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// do MUX_LUT6
|
||||||
|
log_info("Packing LUT6s..\n");
|
||||||
|
for (auto &cell_name : mux2lut6) {
|
||||||
|
pack_mux2_lut6(ctx, ctx->cells[cell_name].get(), packed_cells, delete_nets, new_cells);
|
||||||
|
}
|
||||||
|
|
||||||
|
// do MUX_LUT7
|
||||||
|
log_info("Packing LUT7s..\n");
|
||||||
|
for (auto &cell_name : mux2lut7) {
|
||||||
|
pack_mux2_lut7(ctx, ctx->cells[cell_name].get(), packed_cells, delete_nets, new_cells);
|
||||||
|
}
|
||||||
|
|
||||||
|
// do MUX_LUT8
|
||||||
|
log_info("Packing LUT8s..\n");
|
||||||
|
for (auto &cell_name : mux2lut8) {
|
||||||
|
pack_mux2_lut8(ctx, ctx->cells[cell_name].get(), packed_cells, delete_nets, new_cells);
|
||||||
|
}
|
||||||
|
|
||||||
|
// actual delete, erase and move cells/nets
|
||||||
|
for (auto pcell : packed_cells) {
|
||||||
|
ctx->cells.erase(pcell);
|
||||||
|
}
|
||||||
|
for (auto dnet : delete_nets) {
|
||||||
|
ctx->nets.erase(dnet);
|
||||||
|
}
|
||||||
|
for (auto &ncell : new_cells) {
|
||||||
|
ctx->cells[ncell->name] = std::move(ncell);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Pack LUTs and LUT-FF pairs
|
// Pack LUTs and LUT-FF pairs
|
||||||
static void pack_lut_lutffs(Context *ctx)
|
static void pack_lut_lutffs(Context *ctx)
|
||||||
{
|
{
|
||||||
@ -287,6 +547,7 @@ bool Arch::pack()
|
|||||||
log_break();
|
log_break();
|
||||||
pack_constants(ctx);
|
pack_constants(ctx);
|
||||||
pack_io(ctx);
|
pack_io(ctx);
|
||||||
|
pack_wideluts(ctx);
|
||||||
pack_lut_lutffs(ctx);
|
pack_lut_lutffs(ctx);
|
||||||
pack_nonlut_ffs(ctx);
|
pack_nonlut_ffs(ctx);
|
||||||
ctx->settings[ctx->id("pack")] = 1;
|
ctx->settings[ctx->id("pack")] = 1;
|
||||||
|
Loading…
Reference in New Issue
Block a user