mirror of https://github.com/YosysHQ/nextpnr.git
gatemate: optimizations and cleanups (#1517)
* Add log output * Optimize CC_LUT1 * Update tests * Optimize CC_LUT2 as well * Use init enumerations * Merge DFF in MX4 * Move repack code * Move ramio code to pack_cpe * Merge LUT1/2 to ADDF inputs * Note actual CPE ports * Merge DFF in ADDF * Update FF params and ports first * Check if DFFs are compatible before merging * Optimize DFF/Latch * Add reporting of optimized cells * Optimize MX2/4 * Add statistics * Use special nets for VCC/GND to skip using name * Add warning for carry chain split * Merge FFs where possible * Cleanup * Keep statistics out for now * Add logs for packing sections * review fixes --------- Co-authored-by: Lofty <dan.ravensloft@gmail.com>
This commit is contained in:
parent
4831e50843
commit
2d7d1e2408
|
|
@ -179,112 +179,16 @@ bool GateMateImpl::getClusterPlacement(ClusterId cluster, BelId root_bel,
|
|||
return getChildPlacement(root_cell, root_loc, placement);
|
||||
}
|
||||
|
||||
void GateMateImpl::rename_param(CellInfo *cell, IdString name, IdString new_name, int width)
|
||||
{
|
||||
if (cell->params.count(name)) {
|
||||
cell->params[new_name] = Property(int_or_default(cell->params, name, 0), width);
|
||||
cell->unsetParam(name);
|
||||
}
|
||||
}
|
||||
|
||||
void GateMateImpl::prePlace() { assign_cell_info(); }
|
||||
|
||||
void GateMateImpl::postPlace()
|
||||
void GateMateImpl::postPlace() { repack(); }
|
||||
|
||||
void GateMateImpl::preRoute()
|
||||
{
|
||||
ctx->assignArchInfo();
|
||||
std::vector<IdString> delete_cells;
|
||||
for (auto &cell : ctx->cells) {
|
||||
if (cell.second->type.in(id_CPE_L2T4)) {
|
||||
Loc l = ctx->getBelLocation(cell.second->bel);
|
||||
if (l.z == CPE_LT_L_Z) {
|
||||
if (!cell.second->params.count(id_INIT_L20))
|
||||
cell.second->params[id_INIT_L20] = Property(0b1100, 4);
|
||||
}
|
||||
cell.second->params[id_L2T4_UPPER] = Property((l.z == CPE_LT_U_Z) ? 1 : 0, 1);
|
||||
} else if (cell.second->type.in(id_CPE_LT_L)) {
|
||||
BelId bel = cell.second->bel;
|
||||
PlaceStrength strength = cell.second->belStrength;
|
||||
uint8_t func = int_or_default(cell.second->params, id_C_FUNCTION, 0);
|
||||
Loc loc = ctx->getBelLocation(bel);
|
||||
loc.z = CPE_LT_FULL_Z;
|
||||
ctx->unbindBel(bel);
|
||||
ctx->bindBel(ctx->getBelByLocation(loc), cell.second.get(), strength);
|
||||
cell.second->renamePort(id_IN1, id_IN5);
|
||||
cell.second->renamePort(id_IN2, id_IN6);
|
||||
cell.second->renamePort(id_IN3, id_IN7);
|
||||
cell.second->renamePort(id_IN4, id_IN8);
|
||||
cell.second->renamePort(id_OUT, id_OUT1);
|
||||
cell.second->renamePort(id_CPOUT, id_CPOUT1);
|
||||
if (!cell.second->params.count(id_INIT_L20))
|
||||
cell.second->params[id_INIT_L20] = Property(0b1100, 4);
|
||||
rename_param(cell.second.get(), id_INIT_L00, id_INIT_L02, 4);
|
||||
rename_param(cell.second.get(), id_INIT_L01, id_INIT_L03, 4);
|
||||
rename_param(cell.second.get(), id_INIT_L10, id_INIT_L11, 4);
|
||||
|
||||
switch (func) {
|
||||
case C_ADDF:
|
||||
cell.second->type = id_CPE_ADDF;
|
||||
break;
|
||||
case C_ADDF2:
|
||||
cell.second->type = id_CPE_ADDF2;
|
||||
break;
|
||||
case C_MULT:
|
||||
cell.second->type = id_CPE_MULT;
|
||||
break;
|
||||
case C_MX4:
|
||||
cell.second->type = id_CPE_MX4;
|
||||
break;
|
||||
case C_EN_CIN:
|
||||
log_error("EN_CIN should be using L2T4.\n");
|
||||
break;
|
||||
case C_CONCAT:
|
||||
cell.second->type = id_CPE_CONCAT;
|
||||
break;
|
||||
case C_ADDCIN:
|
||||
log_error("ADDCIN should be using L2T4.\n");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
loc.z = CPE_LT_U_Z;
|
||||
CellInfo *upper = ctx->getBoundBelCell(ctx->getBelByLocation(loc));
|
||||
if (upper->params.count(id_INIT_L00))
|
||||
cell.second->params[id_INIT_L00] = Property(int_or_default(upper->params, id_INIT_L00, 0), 4);
|
||||
if (upper->params.count(id_INIT_L01))
|
||||
cell.second->params[id_INIT_L01] = Property(int_or_default(upper->params, id_INIT_L01, 0), 4);
|
||||
if (upper->params.count(id_INIT_L10))
|
||||
cell.second->params[id_INIT_L10] = Property(int_or_default(upper->params, id_INIT_L10, 0), 4);
|
||||
if (upper->params.count(id_C_I1))
|
||||
cell.second->params[id_C_I1] = Property(int_or_default(upper->params, id_C_I1, 0), 1);
|
||||
if (upper->params.count(id_C_I2))
|
||||
cell.second->params[id_C_I2] = Property(int_or_default(upper->params, id_C_I2, 0), 1);
|
||||
upper->movePortTo(id_IN1, cell.second.get(), id_IN1);
|
||||
upper->movePortTo(id_IN2, cell.second.get(), id_IN2);
|
||||
upper->movePortTo(id_IN3, cell.second.get(), id_IN3);
|
||||
upper->movePortTo(id_IN4, cell.second.get(), id_IN4);
|
||||
upper->movePortTo(id_OUT, cell.second.get(), id_OUT2);
|
||||
upper->movePortTo(id_CPOUT, cell.second.get(), id_CPOUT2);
|
||||
|
||||
}
|
||||
// Mark for deletion
|
||||
else if (cell.second->type.in(id_CPE_LT_U, id_CPE_DUMMY)) {
|
||||
delete_cells.push_back(cell.second->name);
|
||||
}
|
||||
}
|
||||
for (auto pcell : delete_cells) {
|
||||
for (auto &port : ctx->cells[pcell]->ports) {
|
||||
ctx->cells[pcell]->disconnectPort(port.first);
|
||||
}
|
||||
ctx->unbindBel(ctx->cells[pcell]->bel);
|
||||
ctx->cells.erase(pcell);
|
||||
}
|
||||
delete_cells.clear();
|
||||
ctx->assignArchInfo();
|
||||
route_clock();
|
||||
}
|
||||
|
||||
void GateMateImpl::preRoute() { route_clock(); }
|
||||
|
||||
void GateMateImpl::postRoute()
|
||||
{
|
||||
ctx->assignArchInfo();
|
||||
|
|
@ -301,6 +205,25 @@ void GateMateImpl::configurePlacerHeap(PlacerHeapCfg &cfg)
|
|||
cfg.placeAllAtOnce = true;
|
||||
}
|
||||
|
||||
int GateMateImpl::get_dff_config(CellInfo *dff) const
|
||||
{
|
||||
int val = 0;
|
||||
val |= int_or_default(dff->params, id_C_CPE_EN, 0);
|
||||
val <<= 2;
|
||||
val |= int_or_default(dff->params, id_C_CPE_CLK, 0);
|
||||
val <<= 2;
|
||||
val |= int_or_default(dff->params, id_C_CPE_RES, 0);
|
||||
val <<= 2;
|
||||
val |= int_or_default(dff->params, id_C_CPE_SET, 0);
|
||||
val <<= 2;
|
||||
val |= int_or_default(dff->params, id_C_EN_SR, 0);
|
||||
val <<= 1;
|
||||
val |= int_or_default(dff->params, id_C_L_D, 0);
|
||||
val <<= 1;
|
||||
val |= int_or_default(dff->params, id_FF_INIT, 0);
|
||||
return val;
|
||||
}
|
||||
|
||||
void GateMateImpl::assign_cell_info()
|
||||
{
|
||||
fast_cell_info.resize(ctx->cells.size());
|
||||
|
|
@ -311,20 +234,7 @@ void GateMateImpl::assign_cell_info()
|
|||
fc.ff_en = ci->getPort(id_EN);
|
||||
fc.ff_clk = ci->getPort(id_CLK);
|
||||
fc.ff_sr = ci->getPort(id_SR);
|
||||
fc.ff_config = 0;
|
||||
fc.ff_config |= int_or_default(ci->params, id_C_CPE_EN, 0);
|
||||
fc.ff_config <<= 2;
|
||||
fc.ff_config |= int_or_default(ci->params, id_C_CPE_CLK, 0);
|
||||
fc.ff_config <<= 2;
|
||||
fc.ff_config |= int_or_default(ci->params, id_C_CPE_RES, 0);
|
||||
fc.ff_config <<= 2;
|
||||
fc.ff_config |= int_or_default(ci->params, id_C_CPE_SET, 0);
|
||||
fc.ff_config <<= 2;
|
||||
fc.ff_config |= int_or_default(ci->params, id_C_EN_SR, 0);
|
||||
fc.ff_config <<= 1;
|
||||
fc.ff_config |= int_or_default(ci->params, id_C_L_D, 0);
|
||||
fc.ff_config <<= 1;
|
||||
fc.ff_config |= int_or_default(ci->params, id_FF_INIT, 0);
|
||||
fc.ff_config = get_dff_config(ci);
|
||||
fc.dff_used = true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -63,7 +63,8 @@ struct GateMateImpl : HimbaechelAPI
|
|||
bool isPipInverting(PipId pip) const override;
|
||||
|
||||
const GateMateTileExtraDataPOD *tile_extra_data(int tile) const;
|
||||
void rename_param(CellInfo *cell, IdString name, IdString new_name, int width);
|
||||
|
||||
int get_dff_config(CellInfo *dff) const;
|
||||
|
||||
std::set<IdString> available_pads;
|
||||
std::map<BelId, const PadInfoPOD *> bel_to_pad;
|
||||
|
|
@ -83,6 +84,7 @@ struct GateMateImpl : HimbaechelAPI
|
|||
|
||||
void assign_cell_info();
|
||||
void route_clock();
|
||||
void repack();
|
||||
|
||||
const GateMateBelExtraDataPOD *bel_extra_data(BelId bel) const;
|
||||
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ int extract_bits(const dict<KeyType, Property> &ct, const KeyType &key, int star
|
|||
}
|
||||
|
||||
template <typename T>
|
||||
std::vector<std::vector<T>> splitNestedVector(const std::vector<std::vector<T>> &input, size_t maxSize = 32)
|
||||
std::vector<std::vector<T>> splitNestedVector(const std::vector<std::vector<T>> &input, size_t maxSize = 64)
|
||||
{
|
||||
std::vector<std::vector<T>> result;
|
||||
|
||||
|
|
@ -73,6 +73,8 @@ std::vector<std::vector<T>> splitNestedVector(const std::vector<std::vector<T>>
|
|||
size_t end = std::min(i + maxSize, inner.size());
|
||||
result.emplace_back(inner.begin() + i, inner.begin() + end);
|
||||
i = end;
|
||||
if (i < inner.size())
|
||||
log_warning("Carry chain has been split, expect timing penalty.\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -32,6 +32,8 @@ void GateMatePacker::flush_cells()
|
|||
for (auto &port : ctx->cells[pcell]->ports) {
|
||||
ctx->cells[pcell]->disconnectPort(port.first);
|
||||
}
|
||||
if (ctx->cells[pcell]->bel != BelId())
|
||||
ctx->unbindBel(ctx->cells[pcell]->bel);
|
||||
ctx->cells.erase(pcell);
|
||||
}
|
||||
packed_cells.clear();
|
||||
|
|
@ -39,183 +41,13 @@ void GateMatePacker::flush_cells()
|
|||
|
||||
void GateMatePacker::disconnect_if_gnd(CellInfo *cell, IdString input)
|
||||
{
|
||||
NetInfo *net = cell->getPort(input);
|
||||
if (!net)
|
||||
return;
|
||||
if (net->name.in(ctx->id("$PACKER_GND"))) {
|
||||
if (cell->getPort(input) == net_PACKER_GND)
|
||||
cell->disconnectPort(input);
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<CellInfo *, CellInfo *> GateMatePacker::move_ram_i(CellInfo *cell, IdString origPort, bool place, Loc cpe_loc)
|
||||
{
|
||||
CellInfo *cpe_half = nullptr;
|
||||
CellInfo *cpe_ramio = nullptr;
|
||||
NetInfo *net = cell->getPort(origPort);
|
||||
if (net) {
|
||||
cpe_ramio = create_cell_ptr(id_CPE_RAMI, ctx->idf("%s$%s_rami", cell->name.c_str(ctx), origPort.c_str(ctx)));
|
||||
if (place) {
|
||||
cell->constr_children.push_back(cpe_ramio);
|
||||
cpe_ramio->cluster = cell->cluster;
|
||||
cpe_ramio->constr_abs_z = false;
|
||||
cpe_ramio->constr_z = PLACE_DB_CONSTR + origPort.index;
|
||||
} else {
|
||||
BelId b = ctx->getBelByLocation(cpe_loc);
|
||||
ctx->bindBel(b, cpe_ramio, PlaceStrength::STRENGTH_FIXED);
|
||||
}
|
||||
CellInfo *cpe_half =
|
||||
create_cell_ptr(id_CPE_DUMMY, ctx->idf("%s$%s_cpe", cell->name.c_str(ctx), origPort.c_str(ctx)));
|
||||
if (place) {
|
||||
cpe_ramio->constr_children.push_back(cpe_half);
|
||||
cpe_half->cluster = cell->cluster;
|
||||
cpe_half->constr_abs_z = false;
|
||||
cpe_half->constr_z = -4;
|
||||
} else {
|
||||
BelId b = ctx->getBelByLocation(Loc(cpe_loc.x, cpe_loc.y, cpe_loc.z - 4));
|
||||
ctx->bindBel(b, cpe_half, PlaceStrength::STRENGTH_FIXED);
|
||||
}
|
||||
|
||||
cpe_ramio->params[id_C_RAM_I] = Property(1, 1);
|
||||
|
||||
NetInfo *ram_i = ctx->createNet(ctx->idf("%s$ram_i", cpe_ramio->name.c_str(ctx)));
|
||||
cell->movePortTo(origPort, cpe_ramio, id_OUT);
|
||||
cell->connectPort(origPort, ram_i);
|
||||
cpe_ramio->connectPort(id_RAM_I, ram_i);
|
||||
}
|
||||
return std::make_pair(cpe_half, cpe_ramio);
|
||||
}
|
||||
|
||||
std::pair<CellInfo *, CellInfo *> GateMatePacker::move_ram_o(CellInfo *cell, IdString origPort, bool place, Loc cpe_loc)
|
||||
{
|
||||
CellInfo *cpe_half = nullptr;
|
||||
CellInfo *cpe_ramio = nullptr;
|
||||
NetInfo *net = cell->getPort(origPort);
|
||||
if (net) {
|
||||
cpe_ramio = create_cell_ptr(id_CPE_RAMO, ctx->idf("%s$%s_ramo", cell->name.c_str(ctx), origPort.c_str(ctx)));
|
||||
if (place) {
|
||||
cell->constr_children.push_back(cpe_ramio);
|
||||
cpe_ramio->cluster = cell->cluster;
|
||||
cpe_ramio->constr_abs_z = false;
|
||||
cpe_ramio->constr_z = PLACE_DB_CONSTR + origPort.index;
|
||||
} else {
|
||||
BelId b = ctx->getBelByLocation(cpe_loc);
|
||||
ctx->bindBel(b, cpe_ramio, PlaceStrength::STRENGTH_FIXED);
|
||||
}
|
||||
cpe_half = create_cell_ptr(id_CPE_L2T4, ctx->idf("%s$%s_cpe", cell->name.c_str(ctx), origPort.c_str(ctx)));
|
||||
if (place) {
|
||||
cpe_ramio->constr_children.push_back(cpe_half);
|
||||
cpe_half->cluster = cell->cluster;
|
||||
cpe_half->constr_abs_z = false;
|
||||
cpe_half->constr_z = -4;
|
||||
} else {
|
||||
BelId b = ctx->getBelByLocation(Loc(cpe_loc.x, cpe_loc.y, cpe_loc.z - 4));
|
||||
ctx->bindBel(b, cpe_half, PlaceStrength::STRENGTH_FIXED);
|
||||
}
|
||||
if (net->name == ctx->id("$PACKER_GND")) {
|
||||
cpe_half->params[id_INIT_L00] = Property(0b0000, 4);
|
||||
cell->disconnectPort(origPort);
|
||||
} else if (net->name == ctx->id("$PACKER_VCC")) {
|
||||
cpe_half->params[id_INIT_L00] = Property(0b1111, 4);
|
||||
cell->disconnectPort(origPort);
|
||||
} else {
|
||||
cpe_half->params[id_INIT_L00] = Property(0b1010, 4);
|
||||
cell->movePortTo(origPort, cpe_half, id_IN1);
|
||||
}
|
||||
cpe_half->params[id_INIT_L10] = Property(0b1010, 4);
|
||||
|
||||
cpe_ramio->params[id_C_RAM_O] = Property(1, 1);
|
||||
NetInfo *ram_o = ctx->createNet(ctx->idf("%s$ram_o", cpe_half->name.c_str(ctx)));
|
||||
cell->connectPort(origPort, ram_o);
|
||||
cpe_ramio->connectPort(id_RAM_O, ram_o);
|
||||
|
||||
NetInfo *out = ctx->createNet(ctx->idf("%s$out", cpe_half->name.c_str(ctx)));
|
||||
cpe_half->connectPort(id_OUT, out);
|
||||
cpe_ramio->connectPort(id_I, out);
|
||||
}
|
||||
return std::make_pair(cpe_half, cpe_ramio);
|
||||
}
|
||||
|
||||
std::pair<CellInfo *, CellInfo *> GateMatePacker::move_ram_io(CellInfo *cell, IdString iPort, IdString oPort,
|
||||
bool place, Loc cpe_loc)
|
||||
{
|
||||
NetInfo *i_net = cell->getPort(iPort);
|
||||
NetInfo *o_net = cell->getPort(oPort);
|
||||
if (!i_net && !o_net)
|
||||
return std::make_pair(nullptr, nullptr);
|
||||
|
||||
CellInfo *cpe_ramio =
|
||||
create_cell_ptr(id_CPE_RAMIO, ctx->idf("%s$%s_ramio", cell->name.c_str(ctx), oPort.c_str(ctx)));
|
||||
if (place) {
|
||||
cell->constr_children.push_back(cpe_ramio);
|
||||
cpe_ramio->cluster = cell->cluster;
|
||||
cpe_ramio->constr_abs_z = false;
|
||||
cpe_ramio->constr_z = PLACE_DB_CONSTR + oPort.index;
|
||||
} else {
|
||||
BelId b = ctx->getBelByLocation(cpe_loc);
|
||||
ctx->bindBel(b, cpe_ramio, PlaceStrength::STRENGTH_FIXED);
|
||||
}
|
||||
CellInfo *cpe_half = create_cell_ptr(id_CPE_L2T4, ctx->idf("%s$%s_cpe", cell->name.c_str(ctx), oPort.c_str(ctx)));
|
||||
if (place) {
|
||||
cpe_ramio->constr_children.push_back(cpe_half);
|
||||
cpe_half->cluster = cell->cluster;
|
||||
cpe_half->constr_abs_z = false;
|
||||
cpe_half->constr_z = -4;
|
||||
} else {
|
||||
BelId b = ctx->getBelByLocation(Loc(cpe_loc.x, cpe_loc.y, cpe_loc.z - 4));
|
||||
ctx->bindBel(b, cpe_half, PlaceStrength::STRENGTH_FIXED);
|
||||
}
|
||||
|
||||
if (o_net) {
|
||||
if (o_net->name == ctx->id("$PACKER_GND")) {
|
||||
cpe_half->params[id_INIT_L00] = Property(0b0000, 4);
|
||||
cell->disconnectPort(oPort);
|
||||
} else if (o_net->name == ctx->id("$PACKER_VCC")) {
|
||||
cpe_half->params[id_INIT_L00] = Property(0b1111, 4);
|
||||
cell->disconnectPort(oPort);
|
||||
} else {
|
||||
cpe_half->params[id_INIT_L00] = Property(0b1010, 4);
|
||||
cell->movePortTo(oPort, cpe_half, id_IN1);
|
||||
}
|
||||
cpe_half->params[id_INIT_L10] = Property(0b1010, 4);
|
||||
cpe_ramio->params[id_C_RAM_O] = Property(1, 1);
|
||||
|
||||
NetInfo *ram_o = ctx->createNet(ctx->idf("%s$ram_o", cpe_half->name.c_str(ctx)));
|
||||
cell->connectPort(oPort, ram_o);
|
||||
cpe_ramio->connectPort(id_RAM_O, ram_o);
|
||||
|
||||
NetInfo *out = ctx->createNet(ctx->idf("%s$out", cpe_half->name.c_str(ctx)));
|
||||
cpe_half->connectPort(id_OUT, out);
|
||||
cpe_ramio->connectPort(id_I, out);
|
||||
}
|
||||
if (i_net) {
|
||||
cpe_ramio->params[id_C_RAM_I] = Property(1, 1);
|
||||
|
||||
NetInfo *ram_i = ctx->createNet(ctx->idf("%s$ram_i", cpe_half->name.c_str(ctx)));
|
||||
cell->movePortTo(iPort, cpe_ramio, id_OUT);
|
||||
cell->connectPort(iPort, ram_i);
|
||||
cpe_ramio->connectPort(id_RAM_I, ram_i);
|
||||
}
|
||||
return std::make_pair(cpe_half, cpe_ramio);
|
||||
}
|
||||
|
||||
std::pair<CellInfo *, CellInfo *> GateMatePacker::move_ram_i_fixed(CellInfo *cell, IdString origPort, Loc fixed)
|
||||
{
|
||||
return move_ram_i(cell, origPort, false, uarch->getRelativeConstraint(fixed, origPort));
|
||||
}
|
||||
|
||||
std::pair<CellInfo *, CellInfo *> GateMatePacker::move_ram_o_fixed(CellInfo *cell, IdString origPort, Loc fixed)
|
||||
{
|
||||
return move_ram_o(cell, origPort, false, uarch->getRelativeConstraint(fixed, origPort));
|
||||
}
|
||||
|
||||
std::pair<CellInfo *, CellInfo *> GateMatePacker::move_ram_io_fixed(CellInfo *cell, IdString iPort, IdString oPort,
|
||||
Loc fixed)
|
||||
{
|
||||
return move_ram_io(cell, iPort, oPort, false, uarch->getRelativeConstraint(fixed, oPort));
|
||||
}
|
||||
|
||||
void GateMatePacker::pack_misc()
|
||||
{
|
||||
log_info("Packing misc..\n");
|
||||
for (auto &cell : ctx->cells) {
|
||||
CellInfo &ci = *cell.second;
|
||||
if (!ci.type.in(id_CC_USR_RSTN))
|
||||
|
|
@ -251,7 +83,7 @@ void GateMatePacker::pack_misc()
|
|||
}
|
||||
}
|
||||
|
||||
void GateMatePacker::remove_not_used()
|
||||
void GateMatePacker::disconnect_not_used()
|
||||
{
|
||||
for (auto &cell : ctx->cells) {
|
||||
CellInfo &ci = *cell.second;
|
||||
|
|
@ -283,6 +115,269 @@ void GateMatePacker::copy_constraint(NetInfo *in_net, NetInfo *out_net)
|
|||
}
|
||||
}
|
||||
|
||||
void GateMatePacker::move_connections(NetInfo *from_net, NetInfo *to_net)
|
||||
{
|
||||
for (const auto &usr : from_net->users) {
|
||||
IdString port = usr.port;
|
||||
usr.cell->disconnectPort(port);
|
||||
usr.cell->connectPort(port, to_net);
|
||||
}
|
||||
}
|
||||
|
||||
void GateMatePacker::count_cell(CellInfo &ci)
|
||||
{
|
||||
packed_cells.insert(ci.name);
|
||||
count_per_type[ci.type]++;
|
||||
count++;
|
||||
}
|
||||
|
||||
void GateMatePacker::optimize_lut()
|
||||
{
|
||||
for (auto &cell : ctx->cells) {
|
||||
CellInfo &ci = *cell.second;
|
||||
if (!ci.type.in(id_CC_LUT1, id_CC_LUT2))
|
||||
continue;
|
||||
NetInfo *o_net = ci.getPort(id_O);
|
||||
if (!o_net) {
|
||||
count_cell(ci);
|
||||
continue;
|
||||
}
|
||||
|
||||
uint8_t val = int_or_default(ci.params, id_INIT, 0);
|
||||
if (ci.type == id_CC_LUT1)
|
||||
val = val << 2 | val;
|
||||
switch (val) {
|
||||
case LUT_ZERO: // constant 0
|
||||
move_connections(o_net, net_PACKER_GND);
|
||||
count_cell(ci);
|
||||
break;
|
||||
case LUT_D0: // propagate
|
||||
move_connections(o_net, ci.getPort(id_I0));
|
||||
count_cell(ci);
|
||||
break;
|
||||
case LUT_D1: // propagate
|
||||
move_connections(o_net, ci.getPort(id_I1));
|
||||
count_cell(ci);
|
||||
break;
|
||||
case LUT_ONE: // constant 1
|
||||
move_connections(o_net, net_PACKER_VCC);
|
||||
count_cell(ci);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
flush_cells();
|
||||
}
|
||||
|
||||
void GateMatePacker::optimize_mx()
|
||||
{
|
||||
for (auto &cell : ctx->cells) {
|
||||
CellInfo &ci = *cell.second;
|
||||
if (!ci.type.in(id_CC_MX2, id_CC_MX4))
|
||||
continue;
|
||||
NetInfo *y_net = ci.getPort(id_Y);
|
||||
if (!y_net) {
|
||||
count_cell(ci);
|
||||
continue;
|
||||
}
|
||||
if (ci.type == id_CC_MX2) {
|
||||
if (ci.getPort(id_S0) == net_PACKER_GND) {
|
||||
move_connections(y_net, ci.getPort(id_D0));
|
||||
count_cell(ci);
|
||||
continue;
|
||||
} else if (ci.getPort(id_S0) == net_PACKER_VCC) {
|
||||
move_connections(y_net, ci.getPort(id_D1));
|
||||
count_cell(ci);
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
if ((ci.getPort(id_S1) == net_PACKER_GND) && (ci.getPort(id_S0) == net_PACKER_GND)) {
|
||||
move_connections(y_net, ci.getPort(id_D0));
|
||||
count_cell(ci);
|
||||
continue;
|
||||
} else if ((ci.getPort(id_S1) == net_PACKER_GND) && (ci.getPort(id_S0) == net_PACKER_VCC)) {
|
||||
move_connections(y_net, ci.getPort(id_D1));
|
||||
count_cell(ci);
|
||||
continue;
|
||||
} else if ((ci.getPort(id_S1) == net_PACKER_VCC) && (ci.getPort(id_S0) == net_PACKER_GND)) {
|
||||
move_connections(y_net, ci.getPort(id_D2));
|
||||
count_cell(ci);
|
||||
continue;
|
||||
} else if ((ci.getPort(id_S1) == net_PACKER_VCC) && (ci.getPort(id_S0) == net_PACKER_VCC)) {
|
||||
move_connections(y_net, ci.getPort(id_D3));
|
||||
count_cell(ci);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
flush_cells();
|
||||
}
|
||||
|
||||
void GateMatePacker::optimize_ff()
|
||||
{
|
||||
for (auto &cell : ctx->cells) {
|
||||
CellInfo &ci = *cell.second;
|
||||
if (!ci.type.in(id_CC_DFF, id_CC_DLT))
|
||||
continue;
|
||||
|
||||
NetInfo *q_net = ci.getPort(id_Q);
|
||||
if (!q_net) {
|
||||
count_cell(ci);
|
||||
continue;
|
||||
}
|
||||
|
||||
int cpe_clk = int_or_default(ci.params, id_C_CPE_CLK, 0);
|
||||
int cpe_en = int_or_default(ci.params, id_C_CPE_EN, 0);
|
||||
int cpe_res = int_or_default(ci.params, id_C_CPE_RES, 0);
|
||||
int cpe_set = int_or_default(ci.params, id_C_CPE_SET, 0);
|
||||
int ff_init = int_or_default(ci.params, id_FF_INIT, 0);
|
||||
bool ff_has_init = (ff_init >> 1) & 1;
|
||||
bool ff_init_value = ff_init & 1;
|
||||
|
||||
if (cpe_res == 0) { // RES is always ON
|
||||
move_connections(q_net, net_PACKER_GND);
|
||||
count_cell(ci);
|
||||
continue;
|
||||
}
|
||||
if (cpe_set == 0) { // SET is always ON
|
||||
move_connections(q_net, net_PACKER_VCC);
|
||||
count_cell(ci);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ci.type == id_CC_DFF) {
|
||||
if ((cpe_en == 0 || cpe_clk == 0) && ci.getPort(id_SR) == nullptr) {
|
||||
// Only when there is no SR signal
|
||||
// EN always OFF (never loads) or CLK never triggers
|
||||
move_connections(q_net,
|
||||
ff_has_init ? (ff_init_value ? net_PACKER_VCC : net_PACKER_GND) : net_PACKER_GND);
|
||||
count_cell(ci);
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
if (cpe_clk == 3 && ci.getPort(id_SR) == nullptr && cpe_res == 3 && cpe_set == 3) {
|
||||
// Clamp G if there is no set or reset
|
||||
move_connections(q_net, ci.getPort(id_D));
|
||||
count_cell(ci);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
flush_cells();
|
||||
}
|
||||
|
||||
void GateMatePacker::cleanup()
|
||||
{
|
||||
log_info("Running cleanups..\n");
|
||||
dff_update_params();
|
||||
int i = 1;
|
||||
do {
|
||||
count = 0;
|
||||
disconnect_not_used();
|
||||
optimize_lut();
|
||||
optimize_mx();
|
||||
optimize_ff();
|
||||
for (auto c : count_per_type)
|
||||
log_info(" %6d %s cells removed (iteration %d)\n", c.second, c.first.c_str(ctx), i);
|
||||
count_per_type.clear();
|
||||
i++;
|
||||
} while (count != 0);
|
||||
}
|
||||
|
||||
void GateMatePacker::rename_param(CellInfo *cell, IdString name, IdString new_name, int width)
|
||||
{
|
||||
if (cell->params.count(name)) {
|
||||
cell->params[new_name] = Property(int_or_default(cell->params, name, 0), width);
|
||||
cell->unsetParam(name);
|
||||
}
|
||||
}
|
||||
|
||||
void GateMatePacker::repack()
|
||||
{
|
||||
log_info("Repacking CPEs..\n");
|
||||
for (auto &cell : ctx->cells) {
|
||||
if (cell.second->type.in(id_CPE_L2T4)) {
|
||||
Loc l = ctx->getBelLocation(cell.second->bel);
|
||||
if (l.z == CPE_LT_L_Z) {
|
||||
if (!cell.second->params.count(id_INIT_L20))
|
||||
cell.second->params[id_INIT_L20] = Property(LUT_D1, 4);
|
||||
}
|
||||
cell.second->params[id_L2T4_UPPER] = Property((l.z == CPE_LT_U_Z) ? 1 : 0, 1);
|
||||
} else if (cell.second->type.in(id_CPE_LT_L)) {
|
||||
BelId bel = cell.second->bel;
|
||||
PlaceStrength strength = cell.second->belStrength;
|
||||
uint8_t func = int_or_default(cell.second->params, id_C_FUNCTION, 0);
|
||||
Loc loc = ctx->getBelLocation(bel);
|
||||
loc.z = CPE_LT_FULL_Z;
|
||||
ctx->unbindBel(bel);
|
||||
ctx->bindBel(ctx->getBelByLocation(loc), cell.second.get(), strength);
|
||||
cell.second->renamePort(id_IN1, id_IN5);
|
||||
cell.second->renamePort(id_IN2, id_IN6);
|
||||
cell.second->renamePort(id_IN3, id_IN7);
|
||||
cell.second->renamePort(id_IN4, id_IN8);
|
||||
cell.second->renamePort(id_OUT, id_OUT1);
|
||||
cell.second->renamePort(id_CPOUT, id_CPOUT1);
|
||||
if (!cell.second->params.count(id_INIT_L20))
|
||||
cell.second->params[id_INIT_L20] = Property(LUT_D1, 4);
|
||||
rename_param(cell.second.get(), id_INIT_L00, id_INIT_L02, 4);
|
||||
rename_param(cell.second.get(), id_INIT_L01, id_INIT_L03, 4);
|
||||
rename_param(cell.second.get(), id_INIT_L10, id_INIT_L11, 4);
|
||||
|
||||
switch (func) {
|
||||
case C_ADDF:
|
||||
cell.second->type = id_CPE_ADDF;
|
||||
break;
|
||||
case C_ADDF2:
|
||||
cell.second->type = id_CPE_ADDF2;
|
||||
break;
|
||||
case C_MULT:
|
||||
cell.second->type = id_CPE_MULT;
|
||||
break;
|
||||
case C_MX4:
|
||||
cell.second->type = id_CPE_MX4;
|
||||
break;
|
||||
case C_EN_CIN:
|
||||
log_error("EN_CIN should be using L2T4.\n");
|
||||
break;
|
||||
case C_CONCAT:
|
||||
cell.second->type = id_CPE_CONCAT;
|
||||
break;
|
||||
case C_ADDCIN:
|
||||
log_error("ADDCIN should be using L2T4.\n");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
loc.z = CPE_LT_U_Z;
|
||||
CellInfo *upper = ctx->getBoundBelCell(ctx->getBelByLocation(loc));
|
||||
if (upper->params.count(id_INIT_L00))
|
||||
cell.second->params[id_INIT_L00] = Property(int_or_default(upper->params, id_INIT_L00, 0), 4);
|
||||
if (upper->params.count(id_INIT_L01))
|
||||
cell.second->params[id_INIT_L01] = Property(int_or_default(upper->params, id_INIT_L01, 0), 4);
|
||||
if (upper->params.count(id_INIT_L10))
|
||||
cell.second->params[id_INIT_L10] = Property(int_or_default(upper->params, id_INIT_L10, 0), 4);
|
||||
if (upper->params.count(id_C_I1))
|
||||
cell.second->params[id_C_I1] = Property(int_or_default(upper->params, id_C_I1, 0), 1);
|
||||
if (upper->params.count(id_C_I2))
|
||||
cell.second->params[id_C_I2] = Property(int_or_default(upper->params, id_C_I2, 0), 1);
|
||||
upper->movePortTo(id_IN1, cell.second.get(), id_IN1);
|
||||
upper->movePortTo(id_IN2, cell.second.get(), id_IN2);
|
||||
upper->movePortTo(id_IN3, cell.second.get(), id_IN3);
|
||||
upper->movePortTo(id_IN4, cell.second.get(), id_IN4);
|
||||
upper->movePortTo(id_OUT, cell.second.get(), id_OUT2);
|
||||
upper->movePortTo(id_CPOUT, cell.second.get(), id_CPOUT2);
|
||||
|
||||
}
|
||||
// Mark for deletion
|
||||
else if (cell.second->type.in(id_CPE_LT_U, id_CPE_DUMMY)) {
|
||||
packed_cells.insert(cell.second->name);
|
||||
}
|
||||
}
|
||||
flush_cells();
|
||||
}
|
||||
|
||||
void GateMateImpl::pack()
|
||||
{
|
||||
const ArchArgs &args = ctx->args;
|
||||
|
|
@ -292,7 +387,7 @@ void GateMateImpl::pack()
|
|||
|
||||
GateMatePacker packer(ctx, this);
|
||||
packer.pack_constants();
|
||||
packer.remove_not_used();
|
||||
packer.cleanup();
|
||||
packer.pack_io();
|
||||
packer.insert_pll_bufg();
|
||||
packer.sort_bufg();
|
||||
|
|
@ -309,4 +404,10 @@ void GateMateImpl::pack()
|
|||
packer.remove_clocking();
|
||||
}
|
||||
|
||||
void GateMateImpl::repack()
|
||||
{
|
||||
GateMatePacker packer(ctx, this);
|
||||
packer.repack();
|
||||
}
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
|
|
|
|||
|
|
@ -64,14 +64,25 @@ struct GateMatePacker
|
|||
|
||||
void remove_constants();
|
||||
void remove_clocking();
|
||||
void remove_not_used();
|
||||
|
||||
void cleanup();
|
||||
void repack();
|
||||
|
||||
private:
|
||||
void rename_param(CellInfo *cell, IdString name, IdString new_name, int width);
|
||||
void dff_to_cpe(CellInfo *dff);
|
||||
void dff_update_params();
|
||||
void insert_bufg(CellInfo *cell, IdString port);
|
||||
void disconnect_if_gnd(CellInfo *cell, IdString input);
|
||||
void pll_out(CellInfo *cell, IdString origPort, Loc fixed);
|
||||
|
||||
void disconnect_not_used();
|
||||
void optimize_lut();
|
||||
void optimize_mx();
|
||||
void optimize_ff();
|
||||
void count_cell(CellInfo &ci);
|
||||
void move_connections(NetInfo *from_net, NetInfo *to_net);
|
||||
|
||||
PllCfgRecord get_pll_settings(double f_ref, double f_core, int mode, int low_jitter, bool pdiv0_mux, bool feedback);
|
||||
|
||||
std::pair<CellInfo *, CellInfo *> move_ram_i(CellInfo *cell, IdString origPort, bool place = true,
|
||||
|
|
@ -86,6 +97,8 @@ struct GateMatePacker
|
|||
uint8_t ram_ctrl_signal(CellInfo *cell, IdString port, bool alt);
|
||||
uint8_t ram_clk_signal(CellInfo *cell, IdString port);
|
||||
bool is_gpio_valid_dff(CellInfo *dff);
|
||||
bool are_ffs_compatible(CellInfo *dff, CellInfo *other);
|
||||
|
||||
// Cell creating
|
||||
CellInfo *create_cell_ptr(IdString type, IdString name);
|
||||
void flush_cells();
|
||||
|
|
@ -101,6 +114,10 @@ struct GateMatePacker
|
|||
GateMateImpl *uarch;
|
||||
|
||||
HimbaechelHelpers h;
|
||||
NetInfo *net_PACKER_VCC;
|
||||
NetInfo *net_PACKER_GND;
|
||||
int count;
|
||||
std::map<IdString, int> count_per_type;
|
||||
};
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
|
|
|
|||
|
|
@ -31,10 +31,10 @@ uint8_t GateMatePacker::ram_ctrl_signal(CellInfo *cell, IdString port, bool alt)
|
|||
{
|
||||
NetInfo *net = cell->getPort(port);
|
||||
if (net) {
|
||||
if (net->name == ctx->id("$PACKER_GND")) {
|
||||
if (net == net_PACKER_GND) {
|
||||
cell->disconnectPort(port);
|
||||
return 0b00000011;
|
||||
} else if (net->name == ctx->id("$PACKER_VCC")) {
|
||||
} else if (net == net_PACKER_VCC) {
|
||||
cell->disconnectPort(port);
|
||||
return 0b00010011;
|
||||
} else {
|
||||
|
|
@ -242,6 +242,7 @@ void GateMatePacker::pack_ram()
|
|||
std::vector<std::pair<CellInfo *, CellInfo *>> rams;
|
||||
std::vector<std::pair<CellInfo *, CellInfo *>> rams_merged[2];
|
||||
std::map<CellInfo *, CellInfo *> ram_cascade;
|
||||
log_info("Packing RAMs..\n");
|
||||
for (auto &cell : ctx->cells) {
|
||||
CellInfo &ci = *cell.second;
|
||||
if (!ci.type.in(id_CC_BRAM_20K, id_CC_BRAM_40K, id_CC_FIFO_40K))
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ void GateMatePacker::sort_bufg()
|
|||
ItemBufG(CellInfo *cell, int32_t fan_out) : cell(cell), fan_out(fan_out) {}
|
||||
};
|
||||
|
||||
log_info("Sort BUFGs..\n");
|
||||
std::vector<ItemBufG> bufg;
|
||||
for (auto &cell : ctx->cells) {
|
||||
CellInfo &ci = *cell.second;
|
||||
|
|
@ -281,6 +282,7 @@ void GateMatePacker::insert_bufg(CellInfo *cell, IdString port)
|
|||
|
||||
void GateMatePacker::insert_pll_bufg()
|
||||
{
|
||||
log_info("Insert BUFGs for PLLs..\n");
|
||||
for (int i = 0; i < uarch->dies; i++) {
|
||||
Loc fixed_loc = uarch->locations[std::make_pair(id_CLKIN, i)];
|
||||
clkin.push_back(create_cell_ptr(id_CLKIN, ctx->idf("CLKIN%d", i)));
|
||||
|
|
@ -308,6 +310,7 @@ void GateMatePacker::insert_pll_bufg()
|
|||
|
||||
void GateMatePacker::remove_clocking()
|
||||
{
|
||||
log_info("Remove unused clocking cells..\n");
|
||||
auto remove_unused_cells = [&](std::vector<CellInfo *> &cells, const char *type) {
|
||||
for (auto cell : cells) {
|
||||
bool used = false;
|
||||
|
|
@ -333,7 +336,7 @@ void GateMatePacker::remove_clocking()
|
|||
void GateMatePacker::pack_pll()
|
||||
{
|
||||
std::vector<int> pll_index(uarch->dies);
|
||||
log_info("Packing PLLss..\n");
|
||||
log_info("Packing PLLs..\n");
|
||||
for (auto &cell : ctx->cells) {
|
||||
CellInfo &ci = *cell.second;
|
||||
if (!ci.type.in(id_CC_PLL, id_CC_PLL_ADV))
|
||||
|
|
@ -572,11 +575,11 @@ void GateMatePacker::pack_pll()
|
|||
out_clk_max = out_clk;
|
||||
}
|
||||
NetInfo *select_net = ci.getPort(id_USR_SEL_A_B);
|
||||
if (select_net == nullptr || select_net->name == ctx->id("$PACKER_GND")) {
|
||||
if (select_net == nullptr || select_net == net_PACKER_GND) {
|
||||
ci.params[ctx->id("SET_SEL")] = Property(0b0, 1);
|
||||
ci.params[ctx->id("USR_SET")] = Property(0b0, 1);
|
||||
ci.disconnectPort(id_USR_SEL_A_B);
|
||||
} else if (select_net->name == ctx->id("$PACKER_VCC")) {
|
||||
} else if (select_net == net_PACKER_VCC) {
|
||||
ci.params[ctx->id("SET_SEL")] = Property(0b1, 1);
|
||||
ci.params[ctx->id("USR_SET")] = Property(0b0, 1);
|
||||
ci.disconnectPort(id_USR_SEL_A_B);
|
||||
|
|
|
|||
|
|
@ -29,6 +29,21 @@ NEXTPNR_NAMESPACE_BEGIN
|
|||
// Return true if a cell is a flipflop
|
||||
inline bool is_dff(const BaseCtx *ctx, const CellInfo *cell) { return cell->type.in(id_CC_DFF, id_CC_DLT); }
|
||||
|
||||
bool GateMatePacker::are_ffs_compatible(CellInfo *dff, CellInfo *other)
|
||||
{
|
||||
if (!other)
|
||||
return true;
|
||||
if (dff->getPort(id_CLK) != other->getPort(id_CLK))
|
||||
return false;
|
||||
if (dff->getPort(id_EN) != other->getPort(id_EN))
|
||||
return false;
|
||||
if (dff->getPort(id_SR) != other->getPort(id_SR))
|
||||
return false;
|
||||
if (uarch->get_dff_config(dff) != uarch->get_dff_config(other))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void GateMatePacker::dff_to_cpe(CellInfo *dff)
|
||||
{
|
||||
bool invert;
|
||||
|
|
@ -37,10 +52,10 @@ void GateMatePacker::dff_to_cpe(CellInfo *dff)
|
|||
NetInfo *g_net = dff->getPort(id_G);
|
||||
invert = bool_or_default(dff->params, id_G_INV, 0);
|
||||
if (g_net) {
|
||||
if (g_net->name == ctx->id("$PACKER_GND")) {
|
||||
if (g_net == net_PACKER_GND) {
|
||||
dff->params[id_C_CPE_CLK] = Property(invert ? 0b11 : 0b00, 2);
|
||||
dff->disconnectPort(id_G);
|
||||
} else if (g_net->name == ctx->id("$PACKER_VCC")) {
|
||||
} else if (g_net == net_PACKER_VCC) {
|
||||
dff->params[id_C_CPE_CLK] = Property(invert ? 0b00 : 0b11, 2);
|
||||
dff->disconnectPort(id_G);
|
||||
} else {
|
||||
|
|
@ -58,10 +73,10 @@ void GateMatePacker::dff_to_cpe(CellInfo *dff)
|
|||
NetInfo *en_net = dff->getPort(id_EN);
|
||||
bool invert = bool_or_default(dff->params, id_EN_INV, 0);
|
||||
if (en_net) {
|
||||
if (en_net->name == ctx->id("$PACKER_GND")) {
|
||||
if (en_net == net_PACKER_GND) {
|
||||
dff->params[id_C_CPE_EN] = Property(invert ? 0b11 : 0b00, 2);
|
||||
dff->disconnectPort(id_EN);
|
||||
} else if (en_net->name == ctx->id("$PACKER_VCC")) {
|
||||
} else if (en_net == net_PACKER_VCC) {
|
||||
dff->params[id_C_CPE_EN] = Property(invert ? 0b00 : 0b11, 2);
|
||||
dff->disconnectPort(id_EN);
|
||||
} else {
|
||||
|
|
@ -75,10 +90,10 @@ void GateMatePacker::dff_to_cpe(CellInfo *dff)
|
|||
NetInfo *clk_net = dff->getPort(id_CLK);
|
||||
invert = bool_or_default(dff->params, id_CLK_INV, 0);
|
||||
if (clk_net) {
|
||||
if (clk_net->name == ctx->id("$PACKER_GND")) {
|
||||
if (clk_net == net_PACKER_GND) {
|
||||
dff->params[id_C_CPE_CLK] = Property(invert ? 0b11 : 0b00, 2);
|
||||
dff->disconnectPort(id_CLK);
|
||||
} else if (clk_net->name == ctx->id("$PACKER_VCC")) {
|
||||
} else if (clk_net == net_PACKER_VCC) {
|
||||
dff->params[id_C_CPE_CLK] = Property(invert ? 0b00 : 0b11, 2);
|
||||
dff->disconnectPort(id_CLK);
|
||||
} else {
|
||||
|
|
@ -94,8 +109,8 @@ void GateMatePacker::dff_to_cpe(CellInfo *dff)
|
|||
invert = bool_or_default(dff->params, id_SR_INV, 0);
|
||||
bool sr_val = bool_or_default(dff->params, id_SR_VAL, 0);
|
||||
if (sr_net) {
|
||||
if (sr_net->name.in(ctx->id("$PACKER_GND"), ctx->id("$PACKER_VCC"))) {
|
||||
bool sr_signal = sr_net->name == ctx->id("$PACKER_VCC");
|
||||
if (sr_net == net_PACKER_VCC || sr_net == net_PACKER_GND) {
|
||||
bool sr_signal = sr_net == net_PACKER_VCC;
|
||||
if (sr_signal ^ invert) {
|
||||
if (sr_val) {
|
||||
dff->params[id_C_CPE_RES] = Property(0b11, 2);
|
||||
|
|
@ -141,14 +156,37 @@ void GateMatePacker::dff_to_cpe(CellInfo *dff)
|
|||
}
|
||||
}
|
||||
|
||||
void GateMatePacker::dff_update_params()
|
||||
{
|
||||
for (auto &cell : ctx->cells) {
|
||||
CellInfo &ci = *cell.second;
|
||||
if (!ci.type.in(id_CC_DFF, id_CC_DLT))
|
||||
continue;
|
||||
dff_to_cpe(&ci);
|
||||
}
|
||||
}
|
||||
|
||||
void GateMatePacker::pack_cpe()
|
||||
{
|
||||
log_info("Packing CPEs..\n");
|
||||
std::vector<CellInfo *> l2t5_list;
|
||||
|
||||
auto merge_dff = [&](CellInfo &ci, CellInfo *dff) {
|
||||
dff->cluster = ci.name;
|
||||
dff->constr_abs_z = false;
|
||||
dff->constr_z = +2;
|
||||
ci.cluster = ci.name;
|
||||
ci.constr_children.push_back(dff);
|
||||
dff->renamePort(id_D, id_DIN);
|
||||
dff->renamePort(id_Q, id_DOUT);
|
||||
dff->type = (dff->type == id_CC_DLT) ? id_CPE_LATCH : id_CPE_FF;
|
||||
};
|
||||
|
||||
for (auto &cell : ctx->cells) {
|
||||
CellInfo &ci = *cell.second;
|
||||
if (!ci.type.in(id_CC_L2T4, id_CC_L2T5, id_CC_LUT2, id_CC_LUT1, id_CC_MX2))
|
||||
continue;
|
||||
bool is_l2t5 = false;
|
||||
if (ci.type == id_CC_L2T5) {
|
||||
l2t5_list.push_back(&ci);
|
||||
ci.renamePort(id_I0, id_IN1);
|
||||
|
|
@ -157,13 +195,14 @@ void GateMatePacker::pack_cpe()
|
|||
ci.renamePort(id_I3, id_IN4);
|
||||
|
||||
ci.renamePort(id_O, id_OUT);
|
||||
uarch->rename_param(&ci, id_INIT_L02, id_INIT_L00, 4);
|
||||
uarch->rename_param(&ci, id_INIT_L03, id_INIT_L01, 4);
|
||||
uarch->rename_param(&ci, id_INIT_L11, id_INIT_L10, 4);
|
||||
rename_param(&ci, id_INIT_L02, id_INIT_L00, 4);
|
||||
rename_param(&ci, id_INIT_L03, id_INIT_L01, 4);
|
||||
rename_param(&ci, id_INIT_L11, id_INIT_L10, 4);
|
||||
ci.cluster = ci.name;
|
||||
ci.constr_abs_z = true;
|
||||
ci.constr_z = CPE_LT_L_Z;
|
||||
ci.type = id_CPE_L2T4;
|
||||
is_l2t5 = true;
|
||||
} else if (ci.type == id_CC_MX2) {
|
||||
ci.renamePort(id_D1, id_IN1);
|
||||
NetInfo *sel = ci.getPort(id_S0);
|
||||
|
|
@ -173,9 +212,9 @@ void GateMatePacker::pack_cpe()
|
|||
ci.connectPort(id_IN3, sel);
|
||||
ci.renamePort(id_D0, id_IN4);
|
||||
ci.disconnectPort(id_D1);
|
||||
ci.params[id_INIT_L00] = Property(0b1000, 4); // AND
|
||||
ci.params[id_INIT_L01] = Property(0b0100, 4); // AND inv D0
|
||||
ci.params[id_INIT_L10] = Property(0b1110, 4); // OR
|
||||
ci.params[id_INIT_L00] = Property(LUT_AND, 4);
|
||||
ci.params[id_INIT_L01] = Property(LUT_AND_INV_D0, 4);
|
||||
ci.params[id_INIT_L10] = Property(LUT_OR, 4);
|
||||
ci.renamePort(id_Y, id_OUT);
|
||||
ci.type = id_CPE_L2T4;
|
||||
} else {
|
||||
|
|
@ -190,35 +229,64 @@ void GateMatePacker::pack_cpe()
|
|||
val = val << 2 | val;
|
||||
ci.params[id_INIT_L00] = Property(val, 4);
|
||||
ci.unsetParam(id_INIT);
|
||||
ci.params[id_INIT_L10] = Property(0b1010, 4);
|
||||
ci.params[id_INIT_L10] = Property(LUT_D0, 4);
|
||||
}
|
||||
ci.type = id_CPE_L2T4;
|
||||
}
|
||||
NetInfo *o = ci.getPort(id_OUT);
|
||||
if (o) {
|
||||
CellInfo *dff = net_only_drives(ctx, o, is_dff, id_D, true);
|
||||
if (dff) {
|
||||
dff->cluster = ci.name;
|
||||
dff->constr_abs_z = false;
|
||||
dff->constr_z = +2;
|
||||
ci.cluster = ci.name;
|
||||
ci.constr_children.push_back(dff);
|
||||
dff->renamePort(id_D, id_DIN);
|
||||
dff->renamePort(id_Q, id_DOUT);
|
||||
dff_to_cpe(dff);
|
||||
dff->type = (dff->type == id_CC_DLT) ? id_CPE_LATCH : id_CPE_FF;
|
||||
if (o->users.entries() == 1) {
|
||||
// When only it is driving FF
|
||||
CellInfo *dff = net_only_drives(ctx, o, is_dff, id_D, true);
|
||||
if (dff)
|
||||
merge_dff(ci, dff);
|
||||
} else if (!is_l2t5) {
|
||||
CellInfo *dff = net_only_drives(ctx, o, is_dff, id_D, false);
|
||||
// When driving FF + other logic
|
||||
if (dff) {
|
||||
// Make sure main logic is in upper half
|
||||
ci.constr_abs_z = true;
|
||||
ci.constr_z = CPE_LT_U_Z;
|
||||
|
||||
merge_dff(ci, dff);
|
||||
|
||||
// Lower half propagate output from upper one
|
||||
CellInfo *lower = create_cell_ptr(id_CPE_L2T4, ctx->idf("%s$lower", ci.name.c_str(ctx)));
|
||||
ci.constr_children.push_back(lower);
|
||||
lower->cluster = ci.name;
|
||||
lower->constr_abs_z = true;
|
||||
lower->constr_z = CPE_LT_L_Z;
|
||||
lower->params[id_INIT_L20] = Property(LUT_D0, 4);
|
||||
ci.movePortTo(id_OUT, lower, id_OUT);
|
||||
|
||||
// Reconnect net
|
||||
NetInfo *ci_out_conn = ctx->createNet(ctx->idf("%s$out", ci.name.c_str(ctx)));
|
||||
ci.connectPort(id_OUT, ci_out_conn);
|
||||
lower->ports[id_COMBIN].name = id_COMBIN;
|
||||
lower->ports[id_COMBIN].type = PORT_IN;
|
||||
lower->connectPort(id_COMBIN, ci_out_conn);
|
||||
dff->disconnectPort(id_DIN);
|
||||
dff->connectPort(id_DIN, ci_out_conn);
|
||||
|
||||
// Attach if only remaining cell is FF
|
||||
CellInfo *other = net_only_drives(ctx, o, is_dff, id_D, true);
|
||||
if (other && are_ffs_compatible(dff, other)) {
|
||||
merge_dff(ci, other);
|
||||
other->constr_abs_z = true;
|
||||
other->constr_z = 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto ci : l2t5_list) {
|
||||
CellInfo *upper = create_cell_ptr(id_CPE_L2T4, ctx->idf("%s$upper", ci->name.c_str(ctx)));
|
||||
upper->cluster = ci->name;
|
||||
upper->constr_abs_z = true;
|
||||
upper->constr_z = CPE_LT_U_Z;
|
||||
ci->movePortTo(id_I4, upper, id_IN1);
|
||||
upper->params[id_INIT_L00] = Property(0b1010, 4);
|
||||
upper->params[id_INIT_L10] = Property(0b1010, 4);
|
||||
upper->params[id_INIT_L00] = Property(LUT_D0, 4);
|
||||
upper->params[id_INIT_L10] = Property(LUT_D0, 4);
|
||||
ci->constr_children.push_back(upper);
|
||||
|
||||
NetInfo *ci_out_conn = ctx->createNet(ctx->idf("%s$combin", ci->name.c_str(ctx)));
|
||||
|
|
@ -228,7 +296,6 @@ void GateMatePacker::pack_cpe()
|
|||
ci->connectPort(id_COMBIN, ci_out_conn);
|
||||
}
|
||||
l2t5_list.clear();
|
||||
|
||||
flush_cells();
|
||||
|
||||
std::vector<CellInfo *> mux_list;
|
||||
|
|
@ -251,9 +318,10 @@ void GateMatePacker::pack_cpe()
|
|||
for (int i = 0; i < 4; i++) {
|
||||
NetInfo *net = ci.getPort(ctx->idf("D%d", i));
|
||||
if (net) {
|
||||
if (net->name.in(ctx->id("$PACKER_GND"), ctx->id("$PACKER_VCC"))) {
|
||||
if (net->name == ctx->id("$PACKER_VCC"))
|
||||
invert |= 1 << i;
|
||||
if (net == net_PACKER_GND) {
|
||||
ci.disconnectPort(ctx->idf("D%d", i));
|
||||
} else if (net == net_PACKER_VCC) {
|
||||
invert |= 1 << i;
|
||||
ci.disconnectPort(ctx->idf("D%d", i));
|
||||
} else {
|
||||
select |= 1 << i;
|
||||
|
|
@ -261,10 +329,10 @@ void GateMatePacker::pack_cpe()
|
|||
}
|
||||
}
|
||||
ci.params[id_C_FUNCTION] = Property(C_MX4, 3);
|
||||
ci.params[id_INIT_L02] = Property(0b1100, 4); // IN6
|
||||
ci.params[id_INIT_L03] = Property(0b1100, 4); // IN8
|
||||
ci.params[id_INIT_L02] = Property(LUT_D1, 4); // IN6
|
||||
ci.params[id_INIT_L03] = Property(LUT_D1, 4); // IN8
|
||||
ci.params[id_INIT_L11] = Property(invert, 4); // Inversion bits
|
||||
// ci.params[id_INIT_L20] = Property(0b1100, 4); // Always D1
|
||||
ci.params[id_INIT_L20] = Property(LUT_D1, 4); // Always D1
|
||||
ci.type = id_CPE_LT_L;
|
||||
|
||||
CellInfo *upper = create_cell_ptr(id_CPE_LT_U, ctx->idf("%s$upper", ci.name.c_str(ctx)));
|
||||
|
|
@ -279,6 +347,13 @@ void GateMatePacker::pack_cpe()
|
|||
ci.movePortTo(id_D2, upper, id_IN3);
|
||||
ci.movePortTo(id_D3, upper, id_IN4);
|
||||
ci.constr_children.push_back(upper);
|
||||
|
||||
NetInfo *o = ci.getPort(id_OUT);
|
||||
if (o) {
|
||||
CellInfo *dff = net_only_drives(ctx, o, is_dff, id_D, true);
|
||||
if (dff)
|
||||
merge_dff(ci, dff);
|
||||
}
|
||||
}
|
||||
mux_list.clear();
|
||||
|
||||
|
|
@ -299,18 +374,17 @@ void GateMatePacker::pack_cpe()
|
|||
ci.constr_children.push_back(lt);
|
||||
ci.renamePort(id_Q, id_DOUT);
|
||||
NetInfo *d_net = ci.getPort(id_D);
|
||||
if (d_net->name == ctx->id("$PACKER_GND")) {
|
||||
lt->params[id_INIT_L00] = Property(0b0000, 4);
|
||||
if (d_net == net_PACKER_GND) {
|
||||
lt->params[id_INIT_L00] = Property(LUT_ZERO, 4);
|
||||
ci.disconnectPort(id_D);
|
||||
} else if (d_net->name == ctx->id("$PACKER_VCC")) {
|
||||
lt->params[id_INIT_L00] = Property(0b1111, 4);
|
||||
} else if (d_net == net_PACKER_VCC) {
|
||||
lt->params[id_INIT_L00] = Property(LUT_ONE, 4);
|
||||
ci.disconnectPort(id_D);
|
||||
} else {
|
||||
lt->params[id_INIT_L00] = Property(0b1010, 4);
|
||||
lt->params[id_INIT_L00] = Property(LUT_D0, 4);
|
||||
}
|
||||
lt->params[id_INIT_L10] = Property(0b1010, 4);
|
||||
lt->params[id_INIT_L10] = Property(LUT_D0, 4);
|
||||
ci.movePortTo(id_D, lt, id_IN1);
|
||||
dff_to_cpe(&ci);
|
||||
ci.type = (ci.type == id_CC_DLT) ? id_CPE_LATCH : id_CPE_FF;
|
||||
NetInfo *conn = ctx->createNet(ctx->idf("%s$di", ci.name.c_str(ctx)));
|
||||
lt->connectPort(id_OUT, conn);
|
||||
|
|
@ -407,6 +481,56 @@ void GateMatePacker::pack_addf()
|
|||
}
|
||||
flush_cells();
|
||||
|
||||
auto merge_input = [&](CellInfo *cell, CellInfo *target, IdString port, IdString config, IdString in1,
|
||||
IdString in2) {
|
||||
NetInfo *net = cell->getPort(port);
|
||||
if (net == net_PACKER_GND) {
|
||||
target->params[config] = Property(LUT_ZERO, 4);
|
||||
cell->disconnectPort(port);
|
||||
} else if (net == net_PACKER_VCC) {
|
||||
target->params[config] = Property(LUT_ONE, 4);
|
||||
cell->disconnectPort(port);
|
||||
} else {
|
||||
if (net && net->driver.cell && net->driver.cell->type.in(id_CC_LUT1, id_CC_LUT2) &&
|
||||
(net->users.entries() == 1)) {
|
||||
CellInfo *lut2 = net->driver.cell;
|
||||
uint8_t val = int_or_default(lut2->params, id_INIT, 0);
|
||||
if (lut2->type == id_CC_LUT1)
|
||||
val = val << 2 | val;
|
||||
|
||||
target->params[config] = Property(val, 4);
|
||||
lut2->movePortTo(id_I0, target, in1);
|
||||
lut2->movePortTo(id_I1, target, in2);
|
||||
cell->disconnectPort(port);
|
||||
packed_cells.insert(lut2->name);
|
||||
} else {
|
||||
if (cell == target)
|
||||
cell->renamePort(port, in1);
|
||||
else
|
||||
cell->movePortTo(port, target, in1);
|
||||
target->params[config] = Property(LUT_D0, 4);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
auto merge_dff = [&](CellInfo *cell, IdString port, CellInfo *other) -> CellInfo * {
|
||||
NetInfo *o = cell->getPort(port);
|
||||
if (o) {
|
||||
CellInfo *dff = net_only_drives(ctx, o, is_dff, id_D, true);
|
||||
if (dff && are_ffs_compatible(dff, other)) {
|
||||
dff->cluster = cell->cluster;
|
||||
dff->constr_abs_z = false;
|
||||
dff->constr_z = +2;
|
||||
cell->constr_children.push_back(dff);
|
||||
dff->renamePort(id_D, id_DIN);
|
||||
dff->renamePort(id_Q, id_DOUT);
|
||||
dff->type = (dff->type == id_CC_DLT) ? id_CPE_LATCH : id_CPE_FF;
|
||||
return dff;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
};
|
||||
|
||||
for (auto &grp : splitNestedVector(groups)) {
|
||||
CellInfo *root = grp.front();
|
||||
root->cluster = root->name;
|
||||
|
|
@ -423,8 +547,8 @@ void GateMatePacker::pack_addf()
|
|||
ci_lower->cluster = root->name;
|
||||
ci_lower->constr_abs_z = false;
|
||||
ci_lower->constr_y = -1;
|
||||
ci_lower->params[id_INIT_L00] = Property(0b0000, 4); // zero
|
||||
ci_lower->params[id_INIT_L10] = Property(0b1010, 4); // D0
|
||||
ci_lower->params[id_INIT_L00] = Property(LUT_ZERO, 4);
|
||||
ci_lower->params[id_INIT_L10] = Property(LUT_D0, 4);
|
||||
|
||||
CellInfo *ci_cplines = create_cell_ptr(id_CPE_CPLINES, ctx->idf("%s$ci_cplines", root->name.c_str(ctx)));
|
||||
ci_cplines->params[id_C_SELY1] = Property(1, 1);
|
||||
|
|
@ -439,15 +563,15 @@ void GateMatePacker::pack_addf()
|
|||
ci_cplines->connectPort(id_OUT1, ci_out_conn);
|
||||
|
||||
NetInfo *ci_net = root->getPort(id_CI);
|
||||
if (ci_net->name == ctx->id("$PACKER_GND")) {
|
||||
ci_lower->params[id_INIT_L00] = Property(0b0000, 4);
|
||||
if (ci_net == net_PACKER_GND) {
|
||||
ci_lower->params[id_INIT_L00] = Property(LUT_ZERO, 4);
|
||||
root->disconnectPort(id_CI);
|
||||
} else if (ci_net->name == ctx->id("$PACKER_VCC")) {
|
||||
ci_lower->params[id_INIT_L00] = Property(0b1111, 4);
|
||||
} else if (ci_net == net_PACKER_VCC) {
|
||||
ci_lower->params[id_INIT_L00] = Property(LUT_ONE, 4);
|
||||
root->disconnectPort(id_CI);
|
||||
} else {
|
||||
root->movePortTo(id_CI, ci_lower, id_IN1);
|
||||
ci_lower->params[id_INIT_L00] = Property(0b1010, 4); // IN5
|
||||
root->movePortTo(id_CI, ci_lower, id_IN1); // IN5
|
||||
ci_lower->params[id_INIT_L00] = Property(LUT_D0, 4);
|
||||
}
|
||||
|
||||
NetInfo *ci_conn = ctx->createNet(ctx->idf("%s$ci_net", root->name.c_str(ctx)));
|
||||
|
|
@ -469,35 +593,14 @@ void GateMatePacker::pack_addf()
|
|||
|
||||
bool merged = cy->type != id_CC_ADDF;
|
||||
if (merged) {
|
||||
NetInfo *a_net = cy->getPort(id_A2);
|
||||
if (a_net->name == ctx->id("$PACKER_GND")) {
|
||||
cy->params[id_INIT_L02] = Property(0b0000, 4);
|
||||
cy->disconnectPort(id_A2);
|
||||
} else if (a_net->name == ctx->id("$PACKER_VCC")) {
|
||||
cy->params[id_INIT_L02] = Property(0b1111, 4);
|
||||
cy->disconnectPort(id_A2);
|
||||
} else {
|
||||
cy->renamePort(id_A2, id_IN1);
|
||||
cy->params[id_INIT_L02] = Property(0b1010, 4); // IN1
|
||||
}
|
||||
NetInfo *b_net = cy->getPort(id_B2);
|
||||
if (b_net->name == ctx->id("$PACKER_GND")) {
|
||||
cy->params[id_INIT_L03] = Property(0b0000, 4);
|
||||
cy->disconnectPort(id_B2);
|
||||
} else if (b_net->name == ctx->id("$PACKER_VCC")) {
|
||||
cy->params[id_INIT_L03] = Property(0b1111, 4);
|
||||
cy->disconnectPort(id_B2);
|
||||
} else {
|
||||
cy->renamePort(id_B2, id_IN3);
|
||||
cy->params[id_INIT_L03] = Property(0b1010, 4); // IN3
|
||||
}
|
||||
cy->params[id_INIT_L11] = Property(0b0110, 4); // XOR
|
||||
cy->renamePort(id_S2, id_OUT);
|
||||
merge_input(cy, cy, id_A2, id_INIT_L02, id_IN1, id_IN2); // IN5,IN6
|
||||
merge_input(cy, cy, id_B2, id_INIT_L03, id_IN3, id_IN4); // IN7,IN8
|
||||
cy->params[id_INIT_L11] = Property(LUT_XOR, 4);
|
||||
} else {
|
||||
cy->params[id_INIT_L02] = Property(0b0000, 4); // 0
|
||||
cy->params[id_INIT_L03] = Property(0b0000, 4); // 0
|
||||
cy->params[id_INIT_L11] = Property(0b0110, 4); // XOR
|
||||
cy->params[id_INIT_L20] = Property(0b0110, 4); // XOR
|
||||
cy->params[id_INIT_L02] = Property(LUT_ZERO, 4);
|
||||
cy->params[id_INIT_L03] = Property(LUT_ZERO, 4);
|
||||
cy->params[id_INIT_L11] = Property(LUT_XOR, 4);
|
||||
cy->params[id_INIT_L20] = Property(LUT_XOR, 4);
|
||||
}
|
||||
cy->params[id_C_FUNCTION] = Property(merged ? C_ADDF2 : C_ADDF, 3);
|
||||
cy->type = id_CPE_LT_L;
|
||||
|
|
@ -508,36 +611,18 @@ void GateMatePacker::pack_addf()
|
|||
upper->constr_abs_z = false;
|
||||
upper->constr_y = +i;
|
||||
upper->constr_z = -1;
|
||||
CellInfo *other_dff = nullptr;
|
||||
if (merged) {
|
||||
cy->movePortTo(id_S, upper, id_OUT);
|
||||
cy->renamePort(id_S2, id_OUT);
|
||||
other_dff = merge_dff(upper, id_OUT, other_dff);
|
||||
} else {
|
||||
cy->renamePort(id_S, id_OUT);
|
||||
}
|
||||
|
||||
NetInfo *a_net = cy->getPort(id_A);
|
||||
if (a_net->name == ctx->id("$PACKER_GND")) {
|
||||
upper->params[id_INIT_L00] = Property(0b0000, 4);
|
||||
cy->disconnectPort(id_A);
|
||||
} else if (a_net->name == ctx->id("$PACKER_VCC")) {
|
||||
upper->params[id_INIT_L00] = Property(0b1111, 4);
|
||||
cy->disconnectPort(id_A);
|
||||
} else {
|
||||
cy->movePortTo(id_A, upper, id_IN1);
|
||||
upper->params[id_INIT_L00] = Property(0b1010, 4); // IN1
|
||||
}
|
||||
NetInfo *b_net = cy->getPort(id_B);
|
||||
if (b_net->name == ctx->id("$PACKER_GND")) {
|
||||
upper->params[id_INIT_L01] = Property(0b0000, 4);
|
||||
cy->disconnectPort(id_B);
|
||||
} else if (b_net->name == ctx->id("$PACKER_VCC")) {
|
||||
upper->params[id_INIT_L01] = Property(0b1111, 4);
|
||||
cy->disconnectPort(id_B);
|
||||
} else {
|
||||
cy->movePortTo(id_B, upper, id_IN3);
|
||||
upper->params[id_INIT_L01] = Property(0b1010, 4); // IN3
|
||||
}
|
||||
|
||||
upper->params[id_INIT_L10] = Property(0b0110, 4); // XOR
|
||||
merge_dff(cy, id_OUT, other_dff);
|
||||
merge_input(cy, upper, id_A, id_INIT_L00, id_IN1, id_IN2);
|
||||
merge_input(cy, upper, id_B, id_INIT_L01, id_IN3, id_IN4);
|
||||
upper->params[id_INIT_L10] = Property(LUT_XOR, 4);
|
||||
upper->params[id_C_FUNCTION] = Property(merged ? C_ADDF2 : C_ADDF, 3);
|
||||
|
||||
if (i == grp.size() - 1) {
|
||||
|
|
@ -555,8 +640,8 @@ void GateMatePacker::pack_addf()
|
|||
co_lower->constr_abs_z = false;
|
||||
co_lower->constr_y = +i + 1;
|
||||
co_lower->params[id_C_FUNCTION] = Property(C_EN_CIN, 3);
|
||||
co_lower->params[id_INIT_L10] = Property(0b1100, 4);
|
||||
co_lower->params[id_INIT_L20] = Property(0b1100, 4);
|
||||
co_lower->params[id_INIT_L10] = Property(LUT_D1, 4);
|
||||
co_lower->params[id_INIT_L20] = Property(LUT_D1, 4);
|
||||
|
||||
NetInfo *co_conn = ctx->createNet(ctx->idf("%s$co_net", cy->name.c_str(ctx)));
|
||||
|
||||
|
|
@ -587,21 +672,24 @@ void GateMatePacker::pack_addf()
|
|||
}
|
||||
}
|
||||
}
|
||||
flush_cells();
|
||||
}
|
||||
|
||||
void GateMatePacker::pack_constants()
|
||||
{
|
||||
log_info("Packing constants..\n");
|
||||
// Replace constants with LUTs
|
||||
const dict<IdString, Property> vcc_params = {{id_INIT_L10, Property(0b1111, 4)}};
|
||||
const dict<IdString, Property> gnd_params = {{id_INIT_L10, Property(0b0000, 4)}};
|
||||
const dict<IdString, Property> vcc_params = {{id_INIT_L10, Property(LUT_ONE, 4)}};
|
||||
const dict<IdString, Property> gnd_params = {{id_INIT_L10, Property(LUT_ZERO, 4)}};
|
||||
|
||||
h.replace_constants(CellTypePort(id_CPE_L2T4, id_OUT), CellTypePort(id_CPE_L2T4, id_OUT), vcc_params, gnd_params);
|
||||
net_PACKER_VCC = ctx->nets.at(ctx->id("$PACKER_VCC")).get();
|
||||
net_PACKER_GND = ctx->nets.at(ctx->id("$PACKER_GND")).get();
|
||||
}
|
||||
|
||||
void GateMatePacker::remove_constants()
|
||||
{
|
||||
log_info("Removing constants..\n");
|
||||
log_info("Removing unused constants..\n");
|
||||
auto fnd_cell = ctx->cells.find(ctx->id("$PACKER_VCC_DRV"));
|
||||
if (fnd_cell != ctx->cells.end()) {
|
||||
auto fnd_net = ctx->nets.find(ctx->id("$PACKER_VCC"));
|
||||
|
|
@ -628,4 +716,171 @@ void GateMatePacker::remove_constants()
|
|||
}
|
||||
}
|
||||
|
||||
std::pair<CellInfo *, CellInfo *> GateMatePacker::move_ram_i(CellInfo *cell, IdString origPort, bool place, Loc cpe_loc)
|
||||
{
|
||||
CellInfo *cpe_half = nullptr;
|
||||
CellInfo *cpe_ramio = nullptr;
|
||||
NetInfo *net = cell->getPort(origPort);
|
||||
if (net) {
|
||||
cpe_ramio = create_cell_ptr(id_CPE_RAMI, ctx->idf("%s$%s_rami", cell->name.c_str(ctx), origPort.c_str(ctx)));
|
||||
if (place) {
|
||||
cell->constr_children.push_back(cpe_ramio);
|
||||
cpe_ramio->cluster = cell->cluster;
|
||||
cpe_ramio->constr_abs_z = false;
|
||||
cpe_ramio->constr_z = PLACE_DB_CONSTR + origPort.index;
|
||||
} else {
|
||||
BelId b = ctx->getBelByLocation(cpe_loc);
|
||||
ctx->bindBel(b, cpe_ramio, PlaceStrength::STRENGTH_FIXED);
|
||||
}
|
||||
CellInfo *cpe_half =
|
||||
create_cell_ptr(id_CPE_DUMMY, ctx->idf("%s$%s_cpe", cell->name.c_str(ctx), origPort.c_str(ctx)));
|
||||
if (place) {
|
||||
cpe_ramio->constr_children.push_back(cpe_half);
|
||||
cpe_half->cluster = cell->cluster;
|
||||
cpe_half->constr_abs_z = false;
|
||||
cpe_half->constr_z = -4;
|
||||
} else {
|
||||
BelId b = ctx->getBelByLocation(Loc(cpe_loc.x, cpe_loc.y, cpe_loc.z - 4));
|
||||
ctx->bindBel(b, cpe_half, PlaceStrength::STRENGTH_FIXED);
|
||||
}
|
||||
|
||||
cpe_ramio->params[id_C_RAM_I] = Property(1, 1);
|
||||
|
||||
NetInfo *ram_i = ctx->createNet(ctx->idf("%s$ram_i", cpe_ramio->name.c_str(ctx)));
|
||||
cell->movePortTo(origPort, cpe_ramio, id_OUT);
|
||||
cell->connectPort(origPort, ram_i);
|
||||
cpe_ramio->connectPort(id_RAM_I, ram_i);
|
||||
}
|
||||
return std::make_pair(cpe_half, cpe_ramio);
|
||||
}
|
||||
|
||||
std::pair<CellInfo *, CellInfo *> GateMatePacker::move_ram_o(CellInfo *cell, IdString origPort, bool place, Loc cpe_loc)
|
||||
{
|
||||
CellInfo *cpe_half = nullptr;
|
||||
CellInfo *cpe_ramio = nullptr;
|
||||
NetInfo *net = cell->getPort(origPort);
|
||||
if (net) {
|
||||
cpe_ramio = create_cell_ptr(id_CPE_RAMO, ctx->idf("%s$%s_ramo", cell->name.c_str(ctx), origPort.c_str(ctx)));
|
||||
if (place) {
|
||||
cell->constr_children.push_back(cpe_ramio);
|
||||
cpe_ramio->cluster = cell->cluster;
|
||||
cpe_ramio->constr_abs_z = false;
|
||||
cpe_ramio->constr_z = PLACE_DB_CONSTR + origPort.index;
|
||||
} else {
|
||||
BelId b = ctx->getBelByLocation(cpe_loc);
|
||||
ctx->bindBel(b, cpe_ramio, PlaceStrength::STRENGTH_FIXED);
|
||||
}
|
||||
cpe_half = create_cell_ptr(id_CPE_L2T4, ctx->idf("%s$%s_cpe", cell->name.c_str(ctx), origPort.c_str(ctx)));
|
||||
if (place) {
|
||||
cpe_ramio->constr_children.push_back(cpe_half);
|
||||
cpe_half->cluster = cell->cluster;
|
||||
cpe_half->constr_abs_z = false;
|
||||
cpe_half->constr_z = -4;
|
||||
} else {
|
||||
BelId b = ctx->getBelByLocation(Loc(cpe_loc.x, cpe_loc.y, cpe_loc.z - 4));
|
||||
ctx->bindBel(b, cpe_half, PlaceStrength::STRENGTH_FIXED);
|
||||
}
|
||||
if (net == net_PACKER_GND) {
|
||||
cpe_half->params[id_INIT_L00] = Property(LUT_ZERO, 4);
|
||||
cell->disconnectPort(origPort);
|
||||
} else if (net == net_PACKER_VCC) {
|
||||
cpe_half->params[id_INIT_L00] = Property(LUT_ONE, 4);
|
||||
cell->disconnectPort(origPort);
|
||||
} else {
|
||||
cpe_half->params[id_INIT_L00] = Property(LUT_D0, 4);
|
||||
cell->movePortTo(origPort, cpe_half, id_IN1);
|
||||
}
|
||||
cpe_half->params[id_INIT_L10] = Property(LUT_D0, 4);
|
||||
|
||||
cpe_ramio->params[id_C_RAM_O] = Property(1, 1);
|
||||
NetInfo *ram_o = ctx->createNet(ctx->idf("%s$ram_o", cpe_half->name.c_str(ctx)));
|
||||
cell->connectPort(origPort, ram_o);
|
||||
cpe_ramio->connectPort(id_RAM_O, ram_o);
|
||||
|
||||
NetInfo *out = ctx->createNet(ctx->idf("%s$out", cpe_half->name.c_str(ctx)));
|
||||
cpe_half->connectPort(id_OUT, out);
|
||||
cpe_ramio->connectPort(id_I, out);
|
||||
}
|
||||
return std::make_pair(cpe_half, cpe_ramio);
|
||||
}
|
||||
|
||||
std::pair<CellInfo *, CellInfo *> GateMatePacker::move_ram_io(CellInfo *cell, IdString iPort, IdString oPort,
|
||||
bool place, Loc cpe_loc)
|
||||
{
|
||||
NetInfo *i_net = cell->getPort(iPort);
|
||||
NetInfo *o_net = cell->getPort(oPort);
|
||||
if (!i_net && !o_net)
|
||||
return std::make_pair(nullptr, nullptr);
|
||||
|
||||
CellInfo *cpe_ramio =
|
||||
create_cell_ptr(id_CPE_RAMIO, ctx->idf("%s$%s_ramio", cell->name.c_str(ctx), oPort.c_str(ctx)));
|
||||
if (place) {
|
||||
cell->constr_children.push_back(cpe_ramio);
|
||||
cpe_ramio->cluster = cell->cluster;
|
||||
cpe_ramio->constr_abs_z = false;
|
||||
cpe_ramio->constr_z = PLACE_DB_CONSTR + oPort.index;
|
||||
} else {
|
||||
BelId b = ctx->getBelByLocation(cpe_loc);
|
||||
ctx->bindBel(b, cpe_ramio, PlaceStrength::STRENGTH_FIXED);
|
||||
}
|
||||
CellInfo *cpe_half = create_cell_ptr(id_CPE_L2T4, ctx->idf("%s$%s_cpe", cell->name.c_str(ctx), oPort.c_str(ctx)));
|
||||
if (place) {
|
||||
cpe_ramio->constr_children.push_back(cpe_half);
|
||||
cpe_half->cluster = cell->cluster;
|
||||
cpe_half->constr_abs_z = false;
|
||||
cpe_half->constr_z = -4;
|
||||
} else {
|
||||
BelId b = ctx->getBelByLocation(Loc(cpe_loc.x, cpe_loc.y, cpe_loc.z - 4));
|
||||
ctx->bindBel(b, cpe_half, PlaceStrength::STRENGTH_FIXED);
|
||||
}
|
||||
|
||||
if (o_net) {
|
||||
if (o_net == net_PACKER_GND) {
|
||||
cpe_half->params[id_INIT_L00] = Property(LUT_ZERO, 4);
|
||||
cell->disconnectPort(oPort);
|
||||
} else if (o_net == net_PACKER_VCC) {
|
||||
cpe_half->params[id_INIT_L00] = Property(LUT_ONE, 4);
|
||||
cell->disconnectPort(oPort);
|
||||
} else {
|
||||
cpe_half->params[id_INIT_L00] = Property(LUT_D0, 4);
|
||||
cell->movePortTo(oPort, cpe_half, id_IN1);
|
||||
}
|
||||
cpe_half->params[id_INIT_L10] = Property(LUT_D0, 4);
|
||||
cpe_ramio->params[id_C_RAM_O] = Property(1, 1);
|
||||
|
||||
NetInfo *ram_o = ctx->createNet(ctx->idf("%s$ram_o", cpe_half->name.c_str(ctx)));
|
||||
cell->connectPort(oPort, ram_o);
|
||||
cpe_ramio->connectPort(id_RAM_O, ram_o);
|
||||
|
||||
NetInfo *out = ctx->createNet(ctx->idf("%s$out", cpe_half->name.c_str(ctx)));
|
||||
cpe_half->connectPort(id_OUT, out);
|
||||
cpe_ramio->connectPort(id_I, out);
|
||||
}
|
||||
if (i_net) {
|
||||
cpe_ramio->params[id_C_RAM_I] = Property(1, 1);
|
||||
|
||||
NetInfo *ram_i = ctx->createNet(ctx->idf("%s$ram_i", cpe_half->name.c_str(ctx)));
|
||||
cell->movePortTo(iPort, cpe_ramio, id_OUT);
|
||||
cell->connectPort(iPort, ram_i);
|
||||
cpe_ramio->connectPort(id_RAM_I, ram_i);
|
||||
}
|
||||
return std::make_pair(cpe_half, cpe_ramio);
|
||||
}
|
||||
|
||||
std::pair<CellInfo *, CellInfo *> GateMatePacker::move_ram_i_fixed(CellInfo *cell, IdString origPort, Loc fixed)
|
||||
{
|
||||
return move_ram_i(cell, origPort, false, uarch->getRelativeConstraint(fixed, origPort));
|
||||
}
|
||||
|
||||
std::pair<CellInfo *, CellInfo *> GateMatePacker::move_ram_o_fixed(CellInfo *cell, IdString origPort, Loc fixed)
|
||||
{
|
||||
return move_ram_o(cell, origPort, false, uarch->getRelativeConstraint(fixed, origPort));
|
||||
}
|
||||
|
||||
std::pair<CellInfo *, CellInfo *> GateMatePacker::move_ram_io_fixed(CellInfo *cell, IdString iPort, IdString oPort,
|
||||
Loc fixed)
|
||||
{
|
||||
return move_ram_io(cell, iPort, oPort, false, uarch->getRelativeConstraint(fixed, oPort));
|
||||
}
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ std::string get_die_name(int total_dies, int die)
|
|||
|
||||
void GateMatePacker::pack_io()
|
||||
{
|
||||
log_info("Packing IOs..\n");
|
||||
// Trim nextpnr IOBs - assume IO buffer insertion has been done in synthesis
|
||||
for (auto &port : ctx->ports) {
|
||||
if (!ctx->cells.count(port.first))
|
||||
|
|
@ -317,6 +318,7 @@ void GateMatePacker::pack_io()
|
|||
|
||||
void GateMatePacker::pack_io_sel()
|
||||
{
|
||||
log_info("Packing IO SELs..\n");
|
||||
std::vector<CellInfo *> cells;
|
||||
for (auto &cell : ctx->cells) {
|
||||
CellInfo &ci = *cell.second;
|
||||
|
|
@ -331,9 +333,9 @@ void GateMatePacker::pack_io_sel()
|
|||
auto set_out_clk = [&](CellInfo *cell, CellInfo *target) -> bool {
|
||||
NetInfo *clk_net = cell->getPort(id_CLK);
|
||||
if (clk_net) {
|
||||
if (clk_net->name == ctx->id("$PACKER_GND")) {
|
||||
if (clk_net == net_PACKER_GND) {
|
||||
cell->disconnectPort(id_CLK);
|
||||
} else if (clk_net->name == ctx->id("$PACKER_VCC")) {
|
||||
} else if (clk_net == net_PACKER_VCC) {
|
||||
cell->disconnectPort(id_CLK);
|
||||
} else {
|
||||
if (!global_signals.count(clk_net)) {
|
||||
|
|
@ -352,9 +354,9 @@ void GateMatePacker::pack_io_sel()
|
|||
auto set_in_clk = [&](CellInfo *cell, CellInfo *target) {
|
||||
NetInfo *clk_net = cell->getPort(id_CLK);
|
||||
if (clk_net) {
|
||||
if (clk_net->name == ctx->id("$PACKER_GND")) {
|
||||
if (clk_net == net_PACKER_GND) {
|
||||
cell->disconnectPort(id_CLK);
|
||||
} else if (clk_net->name == ctx->id("$PACKER_VCC")) {
|
||||
} else if (clk_net == net_PACKER_VCC) {
|
||||
cell->disconnectPort(id_CLK);
|
||||
} else {
|
||||
if (!global_signals.count(clk_net)) {
|
||||
|
|
@ -439,9 +441,11 @@ void GateMatePacker::pack_io_sel()
|
|||
NetInfo *do_net = ci.getPort(id_A);
|
||||
bool use_custom_clock = false;
|
||||
if (do_net) {
|
||||
if (do_net->name.in(ctx->id("$PACKER_GND"), ctx->id("$PACKER_VCC"))) {
|
||||
ci.params[id_OUT23_14_SEL] =
|
||||
Property(do_net->name == ctx->id("$PACKER_VCC") ? Property::State::S1 : Property::State::S0);
|
||||
if (do_net == net_PACKER_GND) {
|
||||
ci.params[id_OUT23_14_SEL] = Property(Property::State::S0);
|
||||
ci.disconnectPort(id_A);
|
||||
} else if (do_net == net_PACKER_VCC) {
|
||||
ci.params[id_OUT23_14_SEL] = Property(Property::State::S1);
|
||||
ci.disconnectPort(id_A);
|
||||
} else {
|
||||
ci.params[id_OUT_SIGNAL] = Property(Property::State::S1);
|
||||
|
|
@ -522,7 +526,7 @@ void GateMatePacker::pack_io_sel()
|
|||
for (int i = 0; i < 4; i++) {
|
||||
CellInfo *cpe = move_ram_o_fixed(&ci, ctx->idf("OUT%d", i + 1), root_loc).first;
|
||||
if (cpe && i == 2)
|
||||
cpe->params[id_INIT_L10] = Property(0b0101, 4); // Invert CPE out for output enable (OUT3)
|
||||
cpe->params[id_INIT_L10] = Property(LUT_INV_D0, 4); // Invert CPE out for output enable (OUT3)
|
||||
}
|
||||
}
|
||||
flush_cells();
|
||||
|
|
@ -533,11 +537,11 @@ bool GateMatePacker::is_gpio_valid_dff(CellInfo *dff)
|
|||
NetInfo *en_net = dff->getPort(id_EN);
|
||||
bool invert = bool_or_default(dff->params, id_EN_INV, 0);
|
||||
if (en_net) {
|
||||
if (en_net->name == ctx->id("$PACKER_GND")) {
|
||||
if (en_net == net_PACKER_GND) {
|
||||
if (!invert)
|
||||
return false;
|
||||
dff->disconnectPort(id_EN);
|
||||
} else if (en_net->name == ctx->id("$PACKER_VCC")) {
|
||||
} else if (en_net == net_PACKER_VCC) {
|
||||
if (invert)
|
||||
return false;
|
||||
dff->disconnectPort(id_EN);
|
||||
|
|
@ -550,8 +554,8 @@ bool GateMatePacker::is_gpio_valid_dff(CellInfo *dff)
|
|||
NetInfo *sr_net = dff->getPort(id_SR);
|
||||
invert = bool_or_default(dff->params, id_SR_INV, 0);
|
||||
if (sr_net) {
|
||||
if (sr_net->name.in(ctx->id("$PACKER_GND"), ctx->id("$PACKER_VCC"))) {
|
||||
bool sr_signal = sr_net->name == ctx->id("$PACKER_VCC");
|
||||
if ((sr_net == net_PACKER_GND) || (sr_net == net_PACKER_VCC)) {
|
||||
bool sr_signal = sr_net == net_PACKER_VCC;
|
||||
if (sr_signal ^ invert)
|
||||
log_error("Currently unsupported DFF configuration for '%s'\n.", dff->name.c_str(ctx));
|
||||
dff->disconnectPort(id_SR);
|
||||
|
|
@ -565,9 +569,9 @@ bool GateMatePacker::is_gpio_valid_dff(CellInfo *dff)
|
|||
// Sanity check for CLK signal, that it must exist
|
||||
NetInfo *clk_net = dff->getPort(id_CLK);
|
||||
if (clk_net) {
|
||||
if (clk_net->name == ctx->id("$PACKER_GND")) {
|
||||
if (clk_net == net_PACKER_GND) {
|
||||
return false;
|
||||
} else if (clk_net->name == ctx->id("$PACKER_VCC")) {
|
||||
} else if (clk_net == net_PACKER_VCC) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -293,6 +293,7 @@ static const DefaultParam serdes_defaults[] = {
|
|||
|
||||
void GateMatePacker::pack_serdes()
|
||||
{
|
||||
log_info("Packing SERDES..\n");
|
||||
for (auto &cell : ctx->cells) {
|
||||
CellInfo &ci = *cell.second;
|
||||
if (!ci.type.in(id_CC_SERDES))
|
||||
|
|
|
|||
|
|
@ -49,9 +49,9 @@ TEST_F(GateMateTest, remove_lut1_zero)
|
|||
|
||||
CellInfo *obuf = create_cell_ptr(id_CC_OBUF, "obuf");
|
||||
|
||||
direct_connect(lut1, id_O, obuf, id_A);
|
||||
lut1->connectPorts(id_O, obuf, id_A);
|
||||
|
||||
ASSERT_EQ(ctx->cells.size(), 1LU);
|
||||
ASSERT_EQ(ctx->cells.size(), 2LU);
|
||||
ctx->uarch->pack();
|
||||
ASSERT_EQ(ctx->cells.size(), 1LU);
|
||||
}
|
||||
|
|
@ -63,7 +63,7 @@ TEST_F(GateMateTest, remove_lut1_one)
|
|||
|
||||
CellInfo *obuf = create_cell_ptr(id_CC_OBUF, "obuf");
|
||||
|
||||
direct_connect(lut1, id_O, obuf, id_A);
|
||||
lut1->connectPorts(id_O, obuf, id_A);
|
||||
|
||||
ASSERT_EQ(ctx->cells.size(), 2LU);
|
||||
ctx->uarch->pack();
|
||||
|
|
@ -78,14 +78,14 @@ TEST_F(GateMateTest, remove_lut1_pass)
|
|||
CellInfo *obuf = create_cell_ptr(id_CC_OBUF, "obuf");
|
||||
CellInfo *ibuf = create_cell_ptr(id_CC_IBUF, "ibuf");
|
||||
|
||||
direct_connect(ibuf, id_Y, lut1, id_I0);
|
||||
direct_connect(lut1, id_O, obuf, id_A);
|
||||
ibuf->connectPorts(id_Y, lut1, id_I0);
|
||||
lut1->connectPorts(id_O, obuf, id_A);
|
||||
|
||||
ASSERT_EQ(ctx->cells.size(), 3LU);
|
||||
ctx->uarch->pack();
|
||||
// Expect IBUF -> CPE -> OBUF
|
||||
// LUT removed, but CPE for driving OBUF added
|
||||
ASSERT_EQ(ctx->cells.size(), 3LU);
|
||||
ASSERT_EQ(ctx->cells.size(), 4LU);
|
||||
}
|
||||
|
||||
TEST_F(GateMateTest, remove_lut1_inv)
|
||||
|
|
@ -96,14 +96,14 @@ TEST_F(GateMateTest, remove_lut1_inv)
|
|||
CellInfo *obuf = create_cell_ptr(id_CC_OBUF, "obuf");
|
||||
CellInfo *ibuf = create_cell_ptr(id_CC_IBUF, "ibuf");
|
||||
|
||||
direct_connect(ibuf, id_Y, lut1, id_I0);
|
||||
direct_connect(lut1, id_O, obuf, id_A);
|
||||
ibuf->connectPorts(id_Y, lut1, id_I0);
|
||||
lut1->connectPorts(id_O, obuf, id_A);
|
||||
|
||||
ASSERT_EQ(ctx->cells.size(), 3LU);
|
||||
ctx->uarch->pack();
|
||||
// Expect IBUF -> CPE -> OBUF
|
||||
// LUT merged, but CPE for driving OBUF added
|
||||
ASSERT_EQ(ctx->cells.size(), 3LU);
|
||||
ASSERT_EQ(ctx->cells.size(), 5LU);
|
||||
}
|
||||
|
||||
TEST_F(GateMateTest, remove_lut1_not_driven)
|
||||
|
|
@ -123,5 +123,5 @@ TEST_F(GateMateTest, remove_lut1_not_driven)
|
|||
ctx->uarch->pack();
|
||||
// Expect IBUF -> CPE -> OBUF
|
||||
// LUT1 removed as not used, but CPE for driving OBUF added
|
||||
ASSERT_EQ(ctx->cells.size(), 3LU);
|
||||
ASSERT_EQ(ctx->cells.size(), 4LU);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -201,11 +201,4 @@ CellInfo *GateMateTest::create_cell_ptr(IdString type, std::string name)
|
|||
return cell;
|
||||
}
|
||||
|
||||
void GateMateTest::direct_connect(CellInfo *o_cell, IdString o_port, CellInfo *i_cell, IdString i_port)
|
||||
{
|
||||
NetInfo *net = ctx->createNet(ctx->idf("%s_%s", o_cell->name.c_str(ctx), o_port.c_str(ctx)));
|
||||
o_cell->connectPort(o_port, net);
|
||||
i_cell->connectPort(i_port, net);
|
||||
}
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
|
|
|
|||
|
|
@ -34,7 +34,6 @@ class GateMateTest : public ::testing::Test
|
|||
virtual void TearDown() override;
|
||||
|
||||
CellInfo *create_cell_ptr(IdString type, std::string name);
|
||||
void direct_connect(CellInfo *o_cell, IdString o_port, CellInfo *i_cell, IdString i_port);
|
||||
|
||||
ArchArgs chipArgs;
|
||||
Context *ctx;
|
||||
|
|
|
|||
Loading…
Reference in New Issue