mirror of https://github.com/YosysHQ/nextpnr.git
Use improved CPE model (#1503)
* CPE mapping improvements
* Use CP_OUT for adders
* Fixes
* Small fixes
* Cleanups
* Cleanup
* Cleanups
* Fixes
* Fixes
* Optimize
* Cleanup
* clangformat
* Cleanup
* Cleanup
* Bump required version of database
* Cleanup
* Resolve name conflicts
* Fix signal routing
* Make CPE_LATCH separate
* Add more timings models, need updated values
* Fixed warning
* multiplier support from lofty/gatemate-mult
* explicitly zero some params in B passthrough
* comment the relevant CPE inputs in check_multipliers
* Rename some of bels
* remove _lower from name
* refactor multiplier checking
* Revert "remove _lower from name"
This reverts commit daa1041bdf.
* Fixe net name to be unique
* Make sure we at least generate bitstream with all info
* Simplify zero
* Bounded cell type in gui
* typo fix
* Remove A passthrough inversion option
* Clean up CarryGenCell config
* Update a passthru to use new primitives
* Cleanup for adders
* Clean up MsbRoutingCell
* Cleanup
* Refactor A connection code
* Make it more as in PR #1513
* Added cplines to bpassthru and fixed constant driver for A
* Add parts
* Added comp out connections
* clangformat
* clangformat
* Clean up B passthrough connections
* wire up a bunch of intermediate signals
* Bit of cleanup
* handing of C_EN_IN
* C_EN_CIN fixes
* connect f_route to its lines
* fix cite for FRoutingCell
* fixup, oops
* connect multfab to its lines
* Commented line
* Connect CPOUTs
* Handle C_I params
* connect CINY1 for CarryGenCell
* fix carry gen CINX
* Update L2T4 model
* Updates for ADDCIN
* clangformat
* fix some issues with multfab and f_route
* look at C_I when doing inversion
* Only set some C_I signals when used
* Fix one more place
* do not use cplines so we can merge in one cell
* Cover cases that could be optimized out
* clangformat
* Cleanups
* Disable multiplier usage for now
---------
Co-authored-by: Lofty <dan.ravensloft@gmail.com>
This commit is contained in:
parent
1d4b0eeac4
commit
84d8e1abe7
|
|
@ -633,6 +633,8 @@ void DesignWidget::onSelectionChanged(int num, const QItemSelection &, const QIt
|
||||||
addProperty(topItem, QVariant::String, "Type", ctx->getBelType(bel).c_str(ctx));
|
addProperty(topItem, QVariant::String, "Type", ctx->getBelType(bel).c_str(ctx));
|
||||||
addProperty(topItem, QVariant::Bool, "Available", ctx->checkBelAvail(bel));
|
addProperty(topItem, QVariant::Bool, "Available", ctx->checkBelAvail(bel));
|
||||||
addProperty(topItem, QVariant::String, "Bound Cell", ctx->nameOf(ctx->getBoundBelCell(bel)), ElementType::CELL);
|
addProperty(topItem, QVariant::String, "Bound Cell", ctx->nameOf(ctx->getBoundBelCell(bel)), ElementType::CELL);
|
||||||
|
addProperty(topItem, QVariant::String, "Bound Cell Type",
|
||||||
|
ctx->getBoundBelCell(bel) ? ctx->getBoundBelCell(bel)->type.c_str(ctx) : "");
|
||||||
addProperty(topItem, QVariant::String, "Conflicting Cell", ctx->nameOf(ctx->getConflictingBelCell(bel)),
|
addProperty(topItem, QVariant::String, "Conflicting Cell", ctx->nameOf(ctx->getConflictingBelCell(bel)),
|
||||||
ElementType::CELL);
|
ElementType::CELL);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ set(SOURCES
|
||||||
pack_clocking.cc
|
pack_clocking.cc
|
||||||
pack_cpe.cc
|
pack_cpe.cc
|
||||||
pack_io.cc
|
pack_io.cc
|
||||||
|
pack_mult.cc
|
||||||
pack_serdes.cc
|
pack_serdes.cc
|
||||||
pack.h
|
pack.h
|
||||||
pll.cc
|
pll.cc
|
||||||
|
|
|
||||||
|
|
@ -81,12 +81,12 @@ struct BitstreamBackend
|
||||||
return invert;
|
return invert;
|
||||||
}
|
}
|
||||||
|
|
||||||
void update_cpe_lt(CellInfo *cell, IdString port, IdString init, dict<IdString, Property> ¶ms)
|
void update_cpe_lt(CellInfo *cell, IdString port, IdString init, dict<IdString, Property> ¶ms, bool even)
|
||||||
{
|
{
|
||||||
unsigned init_val = int_or_default(params, init);
|
unsigned init_val = int_or_default(params, init);
|
||||||
bool invert = need_inversion(cell, port);
|
bool invert = need_inversion(cell, port);
|
||||||
if (invert) {
|
if (invert) {
|
||||||
if (port.in(id_IN1, id_IN3))
|
if (even)
|
||||||
init_val = (init_val & 0b1010) >> 1 | (init_val & 0b0101) << 1;
|
init_val = (init_val & 0b1010) >> 1 | (init_val & 0b0101) << 1;
|
||||||
else
|
else
|
||||||
init_val = (init_val & 0b0011) << 2 | (init_val & 0b1100) >> 2;
|
init_val = (init_val & 0b0011) << 2 | (init_val & 0b1100) >> 2;
|
||||||
|
|
@ -106,10 +106,8 @@ struct BitstreamBackend
|
||||||
void update_cpe_mux(CellInfo *cell, IdString port, IdString param, int bit, dict<IdString, Property> ¶ms)
|
void update_cpe_mux(CellInfo *cell, IdString port, IdString param, int bit, dict<IdString, Property> ¶ms)
|
||||||
{
|
{
|
||||||
// Mux inversion data is contained in other CPE half
|
// Mux inversion data is contained in other CPE half
|
||||||
Loc l = ctx->getBelLocation(cell->bel);
|
|
||||||
CellInfo *cell_u = ctx->getBoundBelCell(ctx->getBelByLocation(Loc(l.x, l.y, 0)));
|
|
||||||
unsigned init_val = int_or_default(params, param);
|
unsigned init_val = int_or_default(params, param);
|
||||||
bool invert = need_inversion(cell_u, port);
|
bool invert = need_inversion(cell, port);
|
||||||
if (invert) {
|
if (invert) {
|
||||||
int old = (init_val >> bit) & 1;
|
int old = (init_val >> bit) & 1;
|
||||||
int val = (init_val & (~(1 << bit) & 0xf)) | ((!old) << bit);
|
int val = (init_val & (~(1 << bit) & 0xf)) | ((!old) << bit);
|
||||||
|
|
@ -178,10 +176,10 @@ struct BitstreamBackend
|
||||||
Loc l;
|
Loc l;
|
||||||
auto ti = *tile_extra_data(pip.tile);
|
auto ti = *tile_extra_data(pip.tile);
|
||||||
tile_xy(ctx->chip_info, pip.tile, l.x, l.y);
|
tile_xy(ctx->chip_info, pip.tile, l.x, l.y);
|
||||||
l.z = 0;
|
l.z = CPE_LT_U_Z;
|
||||||
BelId cpe_bel = ctx->getBelByLocation(l);
|
BelId cpe_bel = ctx->getBelByLocation(l);
|
||||||
// Only if switchbox is inside core (same as sharing location with CPE)
|
// Only if switchbox is inside core (same as sharing location with CPE)
|
||||||
if (cpe_bel != BelId() && ctx->getBelType(cpe_bel).in(id_CPE_HALF_L, id_CPE_HALF_U)) {
|
if (cpe_bel != BelId() && ctx->getBelType(cpe_bel) == id_CPE_LT_U) {
|
||||||
// Bitstream data for certain SB_DRIVES is located in other tiles
|
// Bitstream data for certain SB_DRIVES is located in other tiles
|
||||||
switch (word[14]) {
|
switch (word[14]) {
|
||||||
case '3':
|
case '3':
|
||||||
|
|
@ -219,8 +217,33 @@ struct BitstreamBackend
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void check_multipliers()
|
||||||
|
{
|
||||||
|
for (auto *mult : uarch->multipliers) {
|
||||||
|
NPNR_ASSERT(mult != nullptr);
|
||||||
|
|
||||||
|
auto should_be_inverted = mult->constr_x % 2 == 1;
|
||||||
|
|
||||||
|
// TODO: these are errors, but downgraded to allow providing *some* output.
|
||||||
|
|
||||||
|
// IN8
|
||||||
|
if (need_inversion(mult, id_IN8) != should_be_inverted)
|
||||||
|
log_warning("%s.IN8 has wrong inversion state\n", mult->name.c_str(ctx));
|
||||||
|
|
||||||
|
// IN5
|
||||||
|
if (need_inversion(mult, id_IN5) != should_be_inverted)
|
||||||
|
log_warning("%s.IN5 has wrong inversion state\n", mult->name.c_str(ctx));
|
||||||
|
|
||||||
|
// IN1
|
||||||
|
if (need_inversion(mult, id_IN1) != should_be_inverted)
|
||||||
|
log_warning("%s.IN1 has wrong inversion state\n", mult->name.c_str(ctx));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void write_bitstream()
|
void write_bitstream()
|
||||||
{
|
{
|
||||||
|
check_multipliers();
|
||||||
|
|
||||||
ChipConfig cc;
|
ChipConfig cc;
|
||||||
cc.chip_name = device;
|
cc.chip_name = device;
|
||||||
std::vector<std::array<int, 9>> bank(uarch->dies);
|
std::vector<std::array<int, 9>> bank(uarch->dies);
|
||||||
|
|
@ -241,30 +264,66 @@ struct BitstreamBackend
|
||||||
cc.tiles[loc].add_word(stringf("GPIO.%s", p.first.c_str(ctx)), p.second.as_bits());
|
cc.tiles[loc].add_word(stringf("GPIO.%s", p.first.c_str(ctx)), p.second.as_bits());
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case id_CPE_HALF_U.index:
|
case id_CPE_CPLINES.index:
|
||||||
case id_CPE_HALF_L.index: {
|
case id_CPE_COMP.index:
|
||||||
|
case id_CPE_L2T4.index:
|
||||||
|
case id_CPE_ADDF.index:
|
||||||
|
case id_CPE_ADDF2.index:
|
||||||
|
case id_CPE_MULT.index:
|
||||||
|
case id_CPE_MX4.index:
|
||||||
|
case id_CPE_CONCAT.index:
|
||||||
|
case id_CPE_FF.index:
|
||||||
|
case id_CPE_LATCH.index:
|
||||||
|
case id_CPE_RAMI.index:
|
||||||
|
case id_CPE_RAMO.index:
|
||||||
|
case id_CPE_RAMIO.index: {
|
||||||
// Update configuration bits based on signal inversion
|
// Update configuration bits based on signal inversion
|
||||||
dict<IdString, Property> params = cell.second->params;
|
dict<IdString, Property> params = cell.second->params;
|
||||||
uint8_t func = int_or_default(cell.second->params, id_C_FUNCTION, 0);
|
Loc l = ctx->getBelLocation(cell.second->bel);
|
||||||
if (cell.second->type.in(id_CPE_HALF_U) && func != C_MX4) {
|
params.erase(id_L2T4_UPPER);
|
||||||
update_cpe_lt(cell.second.get(), id_IN1, id_INIT_L00, params);
|
int c_i1 = int_or_default(params, id_C_I1, 0);
|
||||||
update_cpe_lt(cell.second.get(), id_IN2, id_INIT_L00, params);
|
int c_i2 = int_or_default(params, id_C_I2, 0);
|
||||||
update_cpe_lt(cell.second.get(), id_IN3, id_INIT_L01, params);
|
int c_i3 = int_or_default(params, id_C_I3, 0);
|
||||||
update_cpe_lt(cell.second.get(), id_IN4, id_INIT_L01, params);
|
int c_i4 = int_or_default(params, id_C_I4, 0);
|
||||||
}
|
if (cell.second->type.in(id_CPE_L2T4, id_CPE_LT_L, id_CPE_LT_U)) {
|
||||||
if (cell.second->type.in(id_CPE_HALF_L)) {
|
if (l.z == CPE_LT_U_Z) {
|
||||||
update_cpe_lt(cell.second.get(), id_IN1, id_INIT_L02, params);
|
update_cpe_lt(cell.second.get(), id_IN1, id_INIT_L00, params, true);
|
||||||
update_cpe_lt(cell.second.get(), id_IN2, id_INIT_L02, params);
|
update_cpe_lt(cell.second.get(), c_i1 ? id_PINY1 : id_IN2, id_INIT_L00, params, false);
|
||||||
update_cpe_lt(cell.second.get(), id_IN3, id_INIT_L03, params);
|
update_cpe_lt(cell.second.get(), id_IN3, id_INIT_L01, params, true);
|
||||||
update_cpe_lt(cell.second.get(), id_IN4, id_INIT_L03, params);
|
update_cpe_lt(cell.second.get(), c_i2 ? id_CINX : id_IN4, id_INIT_L01, params, false);
|
||||||
if (func == C_MX4) {
|
} else {
|
||||||
update_cpe_mux(cell.second.get(), id_IN1, id_INIT_L11, 0, params);
|
// These will be renamed later
|
||||||
update_cpe_mux(cell.second.get(), id_IN2, id_INIT_L11, 1, params);
|
update_cpe_lt(cell.second.get(), id_IN1, id_INIT_L00, params, true);
|
||||||
update_cpe_mux(cell.second.get(), id_IN3, id_INIT_L11, 2, params);
|
update_cpe_lt(cell.second.get(), c_i3 ? id_PINY1 : id_IN2, id_INIT_L00, params, false);
|
||||||
update_cpe_mux(cell.second.get(), id_IN4, id_INIT_L11, 3, params);
|
update_cpe_lt(cell.second.get(), id_IN3, id_INIT_L01, params, true);
|
||||||
|
update_cpe_lt(cell.second.get(), c_i4 ? id_PINX : id_IN4, id_INIT_L01, params, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (cell.second->type.in(id_CPE_HALF_U, id_CPE_HALF_L)) {
|
if (l.z == CPE_LT_FULL_Z) {
|
||||||
|
if (!cell.second->type.in(id_CPE_MULT)) {
|
||||||
|
if (cell.second->type.in(id_CPE_MX4)) {
|
||||||
|
update_cpe_mux(cell.second.get(), id_IN1, id_INIT_L11, 0, params);
|
||||||
|
update_cpe_mux(cell.second.get(), id_IN2, id_INIT_L11, 1, params);
|
||||||
|
update_cpe_mux(cell.second.get(), id_IN3, id_INIT_L11, 2, params);
|
||||||
|
update_cpe_mux(cell.second.get(), id_IN4, id_INIT_L11, 3, params);
|
||||||
|
update_cpe_lt(cell.second.get(), id_IN5, id_INIT_L02, params, true);
|
||||||
|
update_cpe_lt(cell.second.get(), c_i3 ? id_PINY1 : id_IN6, id_INIT_L02, params, false);
|
||||||
|
update_cpe_lt(cell.second.get(), id_IN7, id_INIT_L03, params, true);
|
||||||
|
update_cpe_lt(cell.second.get(), c_i4 ? id_PINX : id_IN8, id_INIT_L03, params, false);
|
||||||
|
} else {
|
||||||
|
update_cpe_lt(cell.second.get(), id_IN1, id_INIT_L00, params, true);
|
||||||
|
update_cpe_lt(cell.second.get(), c_i1 ? id_PINY1 : id_IN2, id_INIT_L00, params, false);
|
||||||
|
update_cpe_lt(cell.second.get(), id_IN3, id_INIT_L01, params, true);
|
||||||
|
update_cpe_lt(cell.second.get(), c_i2 ? id_CINX : id_IN4, id_INIT_L01, params, false);
|
||||||
|
update_cpe_lt(cell.second.get(), id_IN5, id_INIT_L02, params, true);
|
||||||
|
update_cpe_lt(cell.second.get(), c_i3 ? id_PINY1 : id_IN6, id_INIT_L02, params, false);
|
||||||
|
update_cpe_lt(cell.second.get(), id_IN7, id_INIT_L03, params, true);
|
||||||
|
update_cpe_lt(cell.second.get(), c_i4 ? id_PINX : id_IN8, id_INIT_L03, params, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cell.second->type.in(id_CPE_FF, id_CPE_LATCH)) {
|
||||||
update_cpe_inv(cell.second.get(), id_CLK, id_C_CPE_CLK, params);
|
update_cpe_inv(cell.second.get(), id_CLK, id_C_CPE_CLK, params);
|
||||||
update_cpe_inv(cell.second.get(), id_EN, id_C_CPE_EN, params);
|
update_cpe_inv(cell.second.get(), id_EN, id_C_CPE_EN, params);
|
||||||
bool set = int_or_default(params, id_C_EN_SR, 0) == 1;
|
bool set = int_or_default(params, id_C_EN_SR, 0) == 1;
|
||||||
|
|
@ -275,7 +334,43 @@ struct BitstreamBackend
|
||||||
}
|
}
|
||||||
int id = tile_extra_data(cell.second.get()->bel.tile)->prim_id;
|
int id = tile_extra_data(cell.second.get()->bel.tile)->prim_id;
|
||||||
for (auto &p : params) {
|
for (auto &p : params) {
|
||||||
cc.tiles[loc].add_word(stringf("CPE%d.%s", id, p.first.c_str(ctx)), p.second.as_bits());
|
IdString name = p.first;
|
||||||
|
switch (l.z) {
|
||||||
|
case CPE_LT_L_Z:
|
||||||
|
switch (p.first.index) {
|
||||||
|
case id_INIT_L00.index:
|
||||||
|
name = id_INIT_L02;
|
||||||
|
break;
|
||||||
|
case id_INIT_L01.index:
|
||||||
|
name = id_INIT_L03;
|
||||||
|
break;
|
||||||
|
case id_INIT_L10.index:
|
||||||
|
name = id_INIT_L11;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case CPE_RAMIO_U_Z:
|
||||||
|
switch (p.first.index) {
|
||||||
|
case id_C_RAM_I.index:
|
||||||
|
name = id_C_RAM_I2;
|
||||||
|
break;
|
||||||
|
case id_C_RAM_O.index:
|
||||||
|
name = id_C_RAM_O2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case CPE_RAMIO_L_Z:
|
||||||
|
switch (p.first.index) {
|
||||||
|
case id_C_RAM_I.index:
|
||||||
|
name = id_C_RAM_I1;
|
||||||
|
break;
|
||||||
|
case id_C_RAM_O.index:
|
||||||
|
name = id_C_RAM_O1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
cc.tiles[loc].add_word(stringf("CPE%d.%s", id, name.c_str(ctx)), p.second.as_bits());
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
case id_CLKIN.index: {
|
case id_CLKIN.index: {
|
||||||
|
|
|
||||||
|
|
@ -33,19 +33,30 @@ CellInfo *GateMatePacker::create_cell_ptr(IdString type, IdString name)
|
||||||
cell->ports[id].name = id;
|
cell->ports[id].name = id;
|
||||||
cell->ports[id].type = dir;
|
cell->ports[id].type = dir;
|
||||||
};
|
};
|
||||||
if (type.in(id_CPE_HALF, id_CPE_HALF_U, id_CPE_HALF_L)) {
|
if (type.in(id_CPE_LT, id_CPE_LT_U, id_CPE_LT_L, id_CPE_L2T4, id_CPE_DUMMY)) {
|
||||||
add_port(id_I1, PORT_IN);
|
add_port(id_IN1, PORT_IN);
|
||||||
add_port(id_I2, PORT_IN);
|
add_port(id_IN2, PORT_IN);
|
||||||
add_port(id_I3, PORT_IN);
|
add_port(id_IN3, PORT_IN);
|
||||||
add_port(id_I4, PORT_IN);
|
add_port(id_IN4, PORT_IN);
|
||||||
add_port(id_RAM_I, PORT_IN);
|
|
||||||
add_port(id_OUT, PORT_OUT);
|
add_port(id_OUT, PORT_OUT);
|
||||||
add_port(id_RAM_O, PORT_OUT);
|
add_port(id_CPOUT, PORT_OUT);
|
||||||
add_port(id_EN, PORT_IN);
|
// These are used to propagate alternate inputs for first LUT2 levels
|
||||||
add_port(id_CLK, PORT_IN);
|
add_port(id_CINX, PORT_IN);
|
||||||
add_port(id_SR, PORT_IN);
|
add_port(id_PINX, PORT_IN);
|
||||||
if (type == id_CPE_HALF_L) {
|
add_port(id_PINY1, PORT_IN);
|
||||||
|
// For EN_CIN input
|
||||||
|
add_port(id_CINY1, PORT_IN);
|
||||||
|
if (type.in(id_CPE_LT_L)) {
|
||||||
|
add_port(id_CINY2, PORT_IN);
|
||||||
|
add_port(id_PINY2, PORT_IN);
|
||||||
|
add_port(id_COMBIN, PORT_IN);
|
||||||
|
|
||||||
|
add_port(id_COUTX, PORT_OUT);
|
||||||
|
add_port(id_POUTX, PORT_OUT);
|
||||||
add_port(id_COUTY1, PORT_OUT);
|
add_port(id_COUTY1, PORT_OUT);
|
||||||
|
add_port(id_POUTY1, PORT_OUT);
|
||||||
|
add_port(id_COUTY2, PORT_OUT);
|
||||||
|
add_port(id_POUTY2, PORT_OUT);
|
||||||
}
|
}
|
||||||
} else if (type.in(id_CLKIN)) {
|
} else if (type.in(id_CLKIN)) {
|
||||||
for (int i = 0; i < 4; i++) {
|
for (int i = 0; i < 4; i++) {
|
||||||
|
|
@ -68,6 +79,33 @@ CellInfo *GateMatePacker::create_cell_ptr(IdString type, IdString name)
|
||||||
} else if (type.in(id_CC_BUFG)) {
|
} else if (type.in(id_CC_BUFG)) {
|
||||||
add_port(id_I, PORT_IN);
|
add_port(id_I, PORT_IN);
|
||||||
add_port(id_O, PORT_OUT);
|
add_port(id_O, PORT_OUT);
|
||||||
|
} else if (type.in(id_CPE_RAMIO, id_CPE_RAMI, id_CPE_RAMO)) {
|
||||||
|
add_port(id_I, PORT_IN);
|
||||||
|
add_port(id_RAM_I, PORT_IN);
|
||||||
|
add_port(id_RAM_O, PORT_OUT);
|
||||||
|
add_port(id_OUT, PORT_OUT);
|
||||||
|
} else if (type.in(id_CPE_COMP)) {
|
||||||
|
add_port(id_COMB1, PORT_IN);
|
||||||
|
add_port(id_COMB2, PORT_IN);
|
||||||
|
add_port(id_COMPOUT, PORT_OUT);
|
||||||
|
} else if (type.in(id_CPE_CPLINES)) {
|
||||||
|
add_port(id_OUT1, PORT_IN);
|
||||||
|
add_port(id_OUT2, PORT_IN);
|
||||||
|
add_port(id_COMPOUT, PORT_IN);
|
||||||
|
|
||||||
|
add_port(id_CINX, PORT_IN);
|
||||||
|
add_port(id_PINX, PORT_IN);
|
||||||
|
add_port(id_CINY1, PORT_IN);
|
||||||
|
add_port(id_PINY1, PORT_IN);
|
||||||
|
add_port(id_CINY2, PORT_IN);
|
||||||
|
add_port(id_PINY2, PORT_IN);
|
||||||
|
|
||||||
|
add_port(id_COUTX, PORT_OUT);
|
||||||
|
add_port(id_POUTX, PORT_OUT);
|
||||||
|
add_port(id_COUTY1, PORT_OUT);
|
||||||
|
add_port(id_POUTY1, PORT_OUT);
|
||||||
|
add_port(id_COUTY2, PORT_OUT);
|
||||||
|
add_port(id_POUTY2, PORT_OUT);
|
||||||
} else {
|
} else {
|
||||||
log_error("Trying to create unknown cell type %s\n", type.c_str(ctx));
|
log_error("Trying to create unknown cell type %s\n", type.c_str(ctx));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -903,37 +903,49 @@ X(VALID)
|
||||||
X(CC_USR_RSTN)
|
X(CC_USR_RSTN)
|
||||||
X(USR_RSTN)
|
X(USR_RSTN)
|
||||||
|
|
||||||
// hardware primitive CPE_HALF_U
|
// hardware primitive CPE_LT_U
|
||||||
X(CPE_HALF_U)
|
X(CPE_LT_U)
|
||||||
// CPE_HALF_U pins
|
// CPE_LT_U pins
|
||||||
X(RAM_I)
|
|
||||||
X(IN1)
|
X(IN1)
|
||||||
X(IN2)
|
X(IN2)
|
||||||
X(IN3)
|
X(IN3)
|
||||||
X(IN4)
|
X(IN4)
|
||||||
|
X(OUT)
|
||||||
|
X(CPOUT)
|
||||||
|
X(PINY1)
|
||||||
|
X(CINX)
|
||||||
|
|
||||||
|
// hardware primitive CPE_FF_U
|
||||||
|
X(CPE_FF_U)
|
||||||
|
// CPE_FF_U pins
|
||||||
|
X(DIN)
|
||||||
//X(CLK)
|
//X(CLK)
|
||||||
//X(EN)
|
//X(EN)
|
||||||
//X(SR)
|
//X(SR)
|
||||||
X(OUT)
|
X(DOUT)
|
||||||
|
|
||||||
|
// hardware primitive CPE_RAMIO_U
|
||||||
|
X(CPE_RAMIO_U)
|
||||||
|
// CPE_RAMIO_U pins
|
||||||
|
X(RAM_I)
|
||||||
|
//X(I)
|
||||||
|
//X(OUT)
|
||||||
X(RAM_O)
|
X(RAM_O)
|
||||||
|
|
||||||
// hardware primitive CPE_HALF_L
|
// hardware primitive CPE_LT_L
|
||||||
X(CPE_HALF_L)
|
X(CPE_LT_L)
|
||||||
// CPE_HALF_L pins
|
// CPE_LT_L pins
|
||||||
//X(RAM_I)
|
|
||||||
//X(IN1)
|
//X(IN1)
|
||||||
//X(IN2)
|
//X(IN2)
|
||||||
//X(IN3)
|
//X(IN3)
|
||||||
//X(IN4)
|
//X(IN4)
|
||||||
//X(CLK)
|
X(COMBIN)
|
||||||
//X(EN)
|
|
||||||
//X(SR)
|
|
||||||
//X(OUT)
|
//X(OUT)
|
||||||
//X(RAM_O)
|
//X(CPOUT)
|
||||||
X(CINX)
|
//X(CINX)
|
||||||
X(PINX)
|
X(PINX)
|
||||||
X(CINY1)
|
X(CINY1)
|
||||||
X(PINY1)
|
//X(PINY1)
|
||||||
X(CINY2)
|
X(CINY2)
|
||||||
X(PINY2)
|
X(PINY2)
|
||||||
X(COUTX)
|
X(COUTX)
|
||||||
|
|
@ -943,13 +955,88 @@ X(POUTY1)
|
||||||
X(COUTY2)
|
X(COUTY2)
|
||||||
X(POUTY2)
|
X(POUTY2)
|
||||||
|
|
||||||
|
// hardware primitive CPE_FF_L
|
||||||
|
X(CPE_FF_L)
|
||||||
|
// CPE_FF_L pins
|
||||||
|
//X(DIN)
|
||||||
|
//X(CLK)
|
||||||
|
//X(EN)
|
||||||
|
//X(SR)
|
||||||
|
//X(DOUT)
|
||||||
|
|
||||||
|
// hardware primitive CPE_RAMIO_L
|
||||||
|
X(CPE_RAMIO_L)
|
||||||
|
// CPE_RAMIO_L pins
|
||||||
|
//X(RAM_I)
|
||||||
|
//X(I)
|
||||||
|
//X(OUT)
|
||||||
|
//X(RAM_O)
|
||||||
|
|
||||||
|
// hardware primitive CPE_LT_FULL
|
||||||
|
X(CPE_LT_FULL)
|
||||||
|
// CPE_LT_FULL pins
|
||||||
|
//X(IN1)
|
||||||
|
//X(IN2)
|
||||||
|
//X(IN3)
|
||||||
|
//X(IN4)
|
||||||
|
X(IN5)
|
||||||
|
X(IN6)
|
||||||
|
X(IN7)
|
||||||
|
X(IN8)
|
||||||
|
X(OUT1)
|
||||||
|
X(OUT2)
|
||||||
|
X(CPOUT1)
|
||||||
|
X(CPOUT2)
|
||||||
|
X(MUXOUT)
|
||||||
|
//X(CINX)
|
||||||
|
//X(PINX)
|
||||||
|
//X(CINY1)
|
||||||
|
//X(PINY1)
|
||||||
|
//X(CINY2)
|
||||||
|
//X(PINY2)
|
||||||
|
//X(COUTX)
|
||||||
|
//X(POUTX)
|
||||||
|
//X(COUTY1)
|
||||||
|
//X(POUTY1)
|
||||||
|
//X(COUTY2)
|
||||||
|
//X(POUTY2)
|
||||||
|
//X(CLK)
|
||||||
|
//X(EN)
|
||||||
|
//X(SR)
|
||||||
|
|
||||||
|
// hardware primitive CPE_COMP
|
||||||
|
X(CPE_COMP)
|
||||||
|
// CPE_COMP pins
|
||||||
|
X(COMB1)
|
||||||
|
X(COMB2)
|
||||||
|
X(COMPOUT)
|
||||||
|
|
||||||
|
// hardware primitive CPE_CPLINES
|
||||||
|
X(CPE_CPLINES)
|
||||||
|
// CPE_CPLINES pins
|
||||||
|
//X(OUT1)
|
||||||
|
//X(OUT2)
|
||||||
|
//X(COMPOUT)
|
||||||
|
//X(CINX)
|
||||||
|
//X(PINX)
|
||||||
|
//X(CINY1)
|
||||||
|
//X(PINY1)
|
||||||
|
//X(CINY2)
|
||||||
|
//X(PINY2)
|
||||||
|
//X(COUTX)
|
||||||
|
//X(POUTX)
|
||||||
|
//X(COUTY1)
|
||||||
|
//X(POUTY1)
|
||||||
|
//X(COUTY2)
|
||||||
|
//X(POUTY2)
|
||||||
|
|
||||||
// hardware primitive GPIO
|
// hardware primitive GPIO
|
||||||
X(GPIO)
|
X(GPIO)
|
||||||
// GPIO pins
|
// GPIO pins
|
||||||
//X(IN1)
|
//X(IN1)
|
||||||
//X(IN2)
|
//X(IN2)
|
||||||
X(OUT1)
|
//X(OUT1)
|
||||||
X(OUT2)
|
//X(OUT2)
|
||||||
X(OUT3)
|
X(OUT3)
|
||||||
X(OUT4)
|
X(OUT4)
|
||||||
//X(DDR)
|
//X(DDR)
|
||||||
|
|
@ -2000,7 +2087,6 @@ X(C_PY2_I)
|
||||||
X(C_C_P)
|
X(C_C_P)
|
||||||
X(C_2D_IN)
|
X(C_2D_IN)
|
||||||
X(C_SN)
|
X(C_SN)
|
||||||
X(C_O)
|
|
||||||
X(C_O1)
|
X(C_O1)
|
||||||
X(C_O2)
|
X(C_O2)
|
||||||
X(C_BR)
|
X(C_BR)
|
||||||
|
|
@ -2017,8 +2103,6 @@ X(C_EN_SR)
|
||||||
X(C_CLKSEL)
|
X(C_CLKSEL)
|
||||||
X(C_ENSEL)
|
X(C_ENSEL)
|
||||||
X(FF_INIT)
|
X(FF_INIT)
|
||||||
// Timing model for CPE
|
|
||||||
X(CPE_DFF)
|
|
||||||
|
|
||||||
// GPIO configuration parameters
|
// GPIO configuration parameters
|
||||||
X(OPEN_DRAIN)
|
X(OPEN_DRAIN)
|
||||||
|
|
@ -2055,7 +2139,11 @@ X(LVDS_EN)
|
||||||
X(LVDS_IE)
|
X(LVDS_IE)
|
||||||
//X(LVDS_RTERM)
|
//X(LVDS_RTERM)
|
||||||
|
|
||||||
X(CPE_HALF)
|
X(CPE_LT)
|
||||||
|
X(CPE_FF)
|
||||||
|
X(CPE_RAMIO)
|
||||||
|
X(CPE_RAMI)
|
||||||
|
X(CPE_RAMO)
|
||||||
X(RAM_I1)
|
X(RAM_I1)
|
||||||
X(RAM_I2)
|
X(RAM_I2)
|
||||||
X(RAM_O1)
|
X(RAM_O1)
|
||||||
|
|
@ -2170,3 +2258,17 @@ X(CPE_LVDS_IBUF)
|
||||||
X(CPE_LVDS_OBUF)
|
X(CPE_LVDS_OBUF)
|
||||||
X(CPE_LVDS_TOBUF)
|
X(CPE_LVDS_TOBUF)
|
||||||
X(CPE_LVDS_IOBUF)
|
X(CPE_LVDS_IOBUF)
|
||||||
|
|
||||||
|
X(CPE_L2T4)
|
||||||
|
X(CPE_ADDF)
|
||||||
|
X(CPE_ADDF2)
|
||||||
|
X(CPE_MULT)
|
||||||
|
X(CPE_MX4)
|
||||||
|
//X(CPE_EN_CIN)
|
||||||
|
X(CPE_CONCAT)
|
||||||
|
//X(CPE_ADDCIN)
|
||||||
|
X(CPE_DUMMY)
|
||||||
|
X(CPE_LATCH)
|
||||||
|
X(L2T4_UPPER)
|
||||||
|
X(CPE_MX8)
|
||||||
|
X(CPE_BRIDGE)
|
||||||
|
|
|
||||||
|
|
@ -85,6 +85,19 @@ enum CPEFunction
|
||||||
C_ADDCIN = 7,
|
C_ADDCIN = 7,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum CPE_Z
|
||||||
|
{
|
||||||
|
CPE_LT_U_Z = 0,
|
||||||
|
CPE_LT_L_Z = 1,
|
||||||
|
CPE_FF_U_Z = 2,
|
||||||
|
CPE_FF_L_Z = 3,
|
||||||
|
CPE_RAMIO_U_Z = 4,
|
||||||
|
CPE_RAMIO_L_Z = 5,
|
||||||
|
CPE_COMP_Z = 6,
|
||||||
|
CPE_CPLINES_Z = 7,
|
||||||
|
CPE_LT_FULL_Z = 8,
|
||||||
|
};
|
||||||
|
|
||||||
enum ClusterPlacement
|
enum ClusterPlacement
|
||||||
{
|
{
|
||||||
PLACE_DB_CONSTR = 32,
|
PLACE_DB_CONSTR = 32,
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "gatemate.h"
|
#include "gatemate.h"
|
||||||
#include "design_utils.h"
|
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "placer_heap.h"
|
#include "placer_heap.h"
|
||||||
|
|
||||||
|
|
@ -91,9 +90,10 @@ bool GateMateImpl::isBelLocationValid(BelId bel, bool explain_invalid) const
|
||||||
if (cell->belStrength != PlaceStrength::STRENGTH_FIXED && tile_extra_data(bel.tile)->die != preferred_die)
|
if (cell->belStrength != PlaceStrength::STRENGTH_FIXED && tile_extra_data(bel.tile)->die != preferred_die)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (ctx->getBelType(bel).in(id_CPE_HALF, id_CPE_HALF_L, id_CPE_HALF_U)) {
|
if (getBelBucketForCellType(ctx->getBelType(bel)) == id_CPE_FF) {
|
||||||
Loc loc = ctx->getBelLocation(bel);
|
Loc loc = ctx->getBelLocation(bel);
|
||||||
const CellInfo *adj_half = ctx->getBoundBelCell(ctx->getBelByLocation(Loc(loc.x, loc.y, loc.z == 1 ? 0 : 1)));
|
const CellInfo *adj_half = ctx->getBoundBelCell(
|
||||||
|
ctx->getBelByLocation(Loc(loc.x, loc.y, loc.z == CPE_FF_L_Z ? CPE_FF_U_Z : CPE_FF_L_Z)));
|
||||||
if (adj_half) {
|
if (adj_half) {
|
||||||
const auto &half_data = fast_cell_info.at(cell->flat_index);
|
const auto &half_data = fast_cell_info.at(cell->flat_index);
|
||||||
if (half_data.dff_used) {
|
if (half_data.dff_used) {
|
||||||
|
|
@ -127,7 +127,7 @@ Loc GateMateImpl::getRelativeConstraint(Loc &root_loc, IdString id) const
|
||||||
child_loc.y = root_loc.y + p->constr_y;
|
child_loc.y = root_loc.y + p->constr_y;
|
||||||
child_loc.z = p->constr_z;
|
child_loc.z = p->constr_z;
|
||||||
} else {
|
} else {
|
||||||
log_error("Constrain info not available for pin.\n");
|
log_error("Constrain info not available for pin '%s'.\n", id.c_str(ctx));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log_error("Bel info not available for constraints.\n");
|
log_error("Bel info not available for constraints.\n");
|
||||||
|
|
@ -192,29 +192,95 @@ void GateMateImpl::prePlace() { assign_cell_info(); }
|
||||||
void GateMateImpl::postPlace()
|
void GateMateImpl::postPlace()
|
||||||
{
|
{
|
||||||
ctx->assignArchInfo();
|
ctx->assignArchInfo();
|
||||||
|
std::vector<IdString> delete_cells;
|
||||||
for (auto &cell : ctx->cells) {
|
for (auto &cell : ctx->cells) {
|
||||||
if (cell.second->type.in(id_CPE_HALF, id_CPE_HALF_U, id_CPE_HALF_L)) {
|
if (cell.second->type.in(id_CPE_L2T4)) {
|
||||||
Loc l = ctx->getBelLocation(cell.second->bel);
|
Loc l = ctx->getBelLocation(cell.second->bel);
|
||||||
if (l.z == 0) { // CPE_HALF_U
|
if (l.z == CPE_LT_L_Z) {
|
||||||
if (cell.second->params.count(id_C_O) && int_or_default(cell.second->params, id_C_O, 0) == 0)
|
|
||||||
cell.second->params[id_C_2D_IN] = Property(1, 1);
|
|
||||||
rename_param(cell.second.get(), id_C_O, id_C_O2, 2);
|
|
||||||
rename_param(cell.second.get(), id_C_RAM_I, id_C_RAM_I2, 1);
|
|
||||||
rename_param(cell.second.get(), id_C_RAM_O, id_C_RAM_O2, 1);
|
|
||||||
cell.second->type = id_CPE_HALF_U;
|
|
||||||
} else { // CPE_HALF_L
|
|
||||||
if (!cell.second->params.count(id_INIT_L20))
|
if (!cell.second->params.count(id_INIT_L20))
|
||||||
cell.second->params[id_INIT_L20] = Property(0b1100, 4);
|
cell.second->params[id_INIT_L20] = Property(0b1100, 4);
|
||||||
rename_param(cell.second.get(), id_C_O, id_C_O1, 2);
|
|
||||||
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);
|
|
||||||
rename_param(cell.second.get(), id_C_RAM_I, id_C_RAM_I1, 1);
|
|
||||||
rename_param(cell.second.get(), id_C_RAM_O, id_C_RAM_O1, 1);
|
|
||||||
cell.second->type = id_CPE_HALF_L;
|
|
||||||
}
|
}
|
||||||
|
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();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GateMateImpl::preRoute() { route_clock(); }
|
void GateMateImpl::preRoute() { route_clock(); }
|
||||||
|
|
@ -222,7 +288,6 @@ void GateMateImpl::preRoute() { route_clock(); }
|
||||||
void GateMateImpl::postRoute()
|
void GateMateImpl::postRoute()
|
||||||
{
|
{
|
||||||
ctx->assignArchInfo();
|
ctx->assignArchInfo();
|
||||||
print_utilisation(ctx);
|
|
||||||
|
|
||||||
const ArchArgs &args = ctx->args;
|
const ArchArgs &args = ctx->args;
|
||||||
if (args.options.count("out")) {
|
if (args.options.count("out")) {
|
||||||
|
|
@ -242,28 +307,25 @@ void GateMateImpl::assign_cell_info()
|
||||||
for (auto &cell : ctx->cells) {
|
for (auto &cell : ctx->cells) {
|
||||||
CellInfo *ci = cell.second.get();
|
CellInfo *ci = cell.second.get();
|
||||||
auto &fc = fast_cell_info.at(ci->flat_index);
|
auto &fc = fast_cell_info.at(ci->flat_index);
|
||||||
if (ci->type.in(id_CPE_HALF, id_CPE_HALF_U, id_CPE_HALF_L)) {
|
if (getBelBucketForCellType(ci->type) == id_CPE_FF) {
|
||||||
fc.signal_used = int_or_default(ci->params, id_C_O, -1);
|
|
||||||
fc.ff_en = ci->getPort(id_EN);
|
fc.ff_en = ci->getPort(id_EN);
|
||||||
fc.ff_clk = ci->getPort(id_CLK);
|
fc.ff_clk = ci->getPort(id_CLK);
|
||||||
fc.ff_sr = ci->getPort(id_SR);
|
fc.ff_sr = ci->getPort(id_SR);
|
||||||
fc.ff_config = 0;
|
fc.ff_config = 0;
|
||||||
if (fc.signal_used == 0) {
|
fc.ff_config |= int_or_default(ci->params, id_C_CPE_EN, 0);
|
||||||
fc.ff_config |= int_or_default(ci->params, id_C_CPE_EN, 0);
|
fc.ff_config <<= 2;
|
||||||
fc.ff_config <<= 2;
|
fc.ff_config |= int_or_default(ci->params, id_C_CPE_CLK, 0);
|
||||||
fc.ff_config |= int_or_default(ci->params, id_C_CPE_CLK, 0);
|
fc.ff_config <<= 2;
|
||||||
fc.ff_config <<= 2;
|
fc.ff_config |= int_or_default(ci->params, id_C_CPE_RES, 0);
|
||||||
fc.ff_config |= int_or_default(ci->params, id_C_CPE_RES, 0);
|
fc.ff_config <<= 2;
|
||||||
fc.ff_config <<= 2;
|
fc.ff_config |= int_or_default(ci->params, id_C_CPE_SET, 0);
|
||||||
fc.ff_config |= int_or_default(ci->params, id_C_CPE_SET, 0);
|
fc.ff_config <<= 2;
|
||||||
fc.ff_config <<= 2;
|
fc.ff_config |= int_or_default(ci->params, id_C_EN_SR, 0);
|
||||||
fc.ff_config |= int_or_default(ci->params, id_C_EN_SR, 0);
|
fc.ff_config <<= 1;
|
||||||
fc.ff_config <<= 1;
|
fc.ff_config |= int_or_default(ci->params, id_C_L_D, 0);
|
||||||
fc.ff_config |= int_or_default(ci->params, id_C_L_D, 0);
|
fc.ff_config <<= 1;
|
||||||
fc.ff_config <<= 1;
|
fc.ff_config |= int_or_default(ci->params, id_FF_INIT, 0);
|
||||||
fc.ff_config |= int_or_default(ci->params, id_FF_INIT, 0);
|
fc.dff_used = true;
|
||||||
fc.dff_used = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -274,8 +336,12 @@ IdString GateMateImpl::getBelBucketForCellType(IdString cell_type) const
|
||||||
if (cell_type.in(id_CPE_IBUF, id_CPE_OBUF, id_CPE_TOBUF, id_CPE_IOBUF, id_CPE_LVDS_IBUF, id_CPE_LVDS_TOBUF,
|
if (cell_type.in(id_CPE_IBUF, id_CPE_OBUF, id_CPE_TOBUF, id_CPE_IOBUF, id_CPE_LVDS_IBUF, id_CPE_LVDS_TOBUF,
|
||||||
id_CPE_LVDS_OBUF, id_CPE_LVDS_IOBUF))
|
id_CPE_LVDS_OBUF, id_CPE_LVDS_IOBUF))
|
||||||
return id_GPIO;
|
return id_GPIO;
|
||||||
else if (cell_type.in(id_CPE_HALF_U, id_CPE_HALF_L, id_CPE_HALF))
|
else if (cell_type.in(id_CPE_LT_U, id_CPE_LT_L, id_CPE_LT, id_CPE_L2T4))
|
||||||
return id_CPE_HALF;
|
return id_CPE_LT;
|
||||||
|
else if (cell_type.in(id_CPE_FF_U, id_CPE_FF_L, id_CPE_FF, id_CPE_LATCH))
|
||||||
|
return id_CPE_FF;
|
||||||
|
else if (cell_type.in(id_CPE_RAMIO, id_CPE_RAMI, id_CPE_RAMO))
|
||||||
|
return id_CPE_RAMIO;
|
||||||
else
|
else
|
||||||
return cell_type;
|
return cell_type;
|
||||||
}
|
}
|
||||||
|
|
@ -283,8 +349,12 @@ IdString GateMateImpl::getBelBucketForCellType(IdString cell_type) const
|
||||||
BelBucketId GateMateImpl::getBelBucketForBel(BelId bel) const
|
BelBucketId GateMateImpl::getBelBucketForBel(BelId bel) const
|
||||||
{
|
{
|
||||||
IdString bel_type = ctx->getBelType(bel);
|
IdString bel_type = ctx->getBelType(bel);
|
||||||
if (bel_type.in(id_CPE_HALF_U, id_CPE_HALF_L))
|
if (bel_type.in(id_CPE_LT_U, id_CPE_LT_L))
|
||||||
return id_CPE_HALF;
|
return id_CPE_LT;
|
||||||
|
else if (bel_type.in(id_CPE_FF_U, id_CPE_FF_L))
|
||||||
|
return id_CPE_FF;
|
||||||
|
else if (bel_type.in(id_CPE_RAMIO_U, id_CPE_RAMIO_L))
|
||||||
|
return id_CPE_RAMIO;
|
||||||
return bel_type;
|
return bel_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -294,10 +364,16 @@ bool GateMateImpl::isValidBelForCellType(IdString cell_type, BelId bel) const
|
||||||
if (bel_type == id_GPIO)
|
if (bel_type == id_GPIO)
|
||||||
return cell_type.in(id_CPE_IBUF, id_CPE_OBUF, id_CPE_TOBUF, id_CPE_IOBUF, id_CPE_LVDS_IBUF, id_CPE_LVDS_TOBUF,
|
return cell_type.in(id_CPE_IBUF, id_CPE_OBUF, id_CPE_TOBUF, id_CPE_IOBUF, id_CPE_LVDS_IBUF, id_CPE_LVDS_TOBUF,
|
||||||
id_CPE_LVDS_OBUF, id_CPE_LVDS_IOBUF);
|
id_CPE_LVDS_OBUF, id_CPE_LVDS_IOBUF);
|
||||||
else if (bel_type == id_CPE_HALF_U)
|
else if (bel_type == id_CPE_LT_U)
|
||||||
return cell_type.in(id_CPE_HALF_U, id_CPE_HALF);
|
return cell_type.in(id_CPE_LT_U, id_CPE_LT, id_CPE_L2T4, id_CPE_DUMMY);
|
||||||
else if (bel_type == id_CPE_HALF_L)
|
else if (bel_type == id_CPE_LT_L)
|
||||||
return cell_type.in(id_CPE_HALF_L, id_CPE_HALF);
|
return cell_type.in(id_CPE_LT_L, id_CPE_LT, id_CPE_L2T4, id_CPE_DUMMY);
|
||||||
|
else if (bel_type == id_CPE_FF_U)
|
||||||
|
return cell_type.in(id_CPE_FF_U, id_CPE_FF, id_CPE_LATCH);
|
||||||
|
else if (bel_type == id_CPE_FF_L)
|
||||||
|
return cell_type.in(id_CPE_FF_L, id_CPE_FF, id_CPE_LATCH);
|
||||||
|
else if (bel_type.in(id_CPE_RAMIO_U, id_CPE_RAMIO_L))
|
||||||
|
return cell_type.in(id_CPE_RAMIO, id_CPE_RAMI, id_CPE_RAMO);
|
||||||
else
|
else
|
||||||
return (bel_type == cell_type);
|
return (bel_type == cell_type);
|
||||||
}
|
}
|
||||||
|
|
@ -321,7 +397,8 @@ struct GateMateArch : HimbaechelArch
|
||||||
{
|
{
|
||||||
return device.size() > 6 && device.substr(0, 6) == "CCGM1A";
|
return device.size() > 6 && device.substr(0, 6) == "CCGM1A";
|
||||||
}
|
}
|
||||||
std::unique_ptr<HimbaechelAPI> create(const std::string &device, const dict<std::string, std::string> &args)
|
std::unique_ptr<HimbaechelAPI> create(const std::string &device,
|
||||||
|
const dict<std::string, std::string> &args) override
|
||||||
{
|
{
|
||||||
return std::make_unique<GateMateImpl>();
|
return std::make_unique<GateMateImpl>();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -63,6 +63,7 @@ struct GateMateImpl : HimbaechelAPI
|
||||||
bool isPipInverting(PipId pip) const override;
|
bool isPipInverting(PipId pip) const override;
|
||||||
|
|
||||||
const GateMateTileExtraDataPOD *tile_extra_data(int tile) const;
|
const GateMateTileExtraDataPOD *tile_extra_data(int tile) const;
|
||||||
|
void rename_param(CellInfo *cell, IdString name, IdString new_name, int width);
|
||||||
|
|
||||||
std::set<IdString> available_pads;
|
std::set<IdString> available_pads;
|
||||||
std::map<BelId, const PadInfoPOD *> bel_to_pad;
|
std::map<BelId, const PadInfoPOD *> bel_to_pad;
|
||||||
|
|
@ -70,6 +71,7 @@ struct GateMateImpl : HimbaechelAPI
|
||||||
dict<std::pair<IdString, int>, Loc> locations;
|
dict<std::pair<IdString, int>, Loc> locations;
|
||||||
int dies;
|
int dies;
|
||||||
int preferred_die;
|
int preferred_die;
|
||||||
|
std::vector<CellInfo *> multipliers;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool getChildPlacement(const BaseClusterInfo *cluster, Loc root_loc,
|
bool getChildPlacement(const BaseClusterInfo *cluster, Loc root_loc,
|
||||||
|
|
@ -80,7 +82,6 @@ struct GateMateImpl : HimbaechelAPI
|
||||||
void parse_ccf(const std::string &filename);
|
void parse_ccf(const std::string &filename);
|
||||||
|
|
||||||
void assign_cell_info();
|
void assign_cell_info();
|
||||||
void rename_param(CellInfo *cell, IdString name, IdString new_name, int width);
|
|
||||||
void route_clock();
|
void route_clock();
|
||||||
|
|
||||||
const GateMateBelExtraDataPOD *bel_extra_data(BelId bel) const;
|
const GateMateBelExtraDataPOD *bel_extra_data(BelId bel) const;
|
||||||
|
|
|
||||||
|
|
@ -134,32 +134,101 @@ def set_timings(ch):
|
||||||
speed = "DEFAULT"
|
speed = "DEFAULT"
|
||||||
tmg = ch.set_speed_grades([speed])
|
tmg = ch.set_speed_grades([speed])
|
||||||
|
|
||||||
lut = ch.timing.add_cell_variant(speed, "CPE_HALF_L")
|
lut = ch.timing.add_cell_variant(speed, "CPE_LT_L")
|
||||||
lut.add_comb_arc("IN1", "OUT", TimingValue(416, 418)) # IN5 to OUT1
|
lut.add_comb_arc("IN1", "OUT", TimingValue(416, 418)) # IN5 to OUT1
|
||||||
lut.add_comb_arc("IN2", "OUT", TimingValue(413, 422)) # IN6 to OUT1
|
lut.add_comb_arc("IN2", "OUT", TimingValue(413, 422)) # IN6 to OUT1
|
||||||
lut.add_comb_arc("IN3", "OUT", TimingValue(372, 374)) # IN7 to OUT1
|
lut.add_comb_arc("IN3", "OUT", TimingValue(372, 374)) # IN7 to OUT1
|
||||||
lut.add_comb_arc("IN4", "OUT", TimingValue(275, 385)) # IN8 to OUT1
|
lut.add_comb_arc("IN4", "OUT", TimingValue(275, 385)) # IN8 to OUT1
|
||||||
|
|
||||||
lut = ch.timing.add_cell_variant(speed, "CPE_HALF_U")
|
lut = ch.timing.add_cell_variant(speed, "CPE_LT_U")
|
||||||
lut.add_comb_arc("IN1", "OUT", TimingValue(479, 484)) # to OUT2
|
lut.add_comb_arc("IN1", "OUT", TimingValue(479, 484)) # to OUT2
|
||||||
lut.add_comb_arc("IN2", "OUT", TimingValue(471, 488)) # to OUT2
|
lut.add_comb_arc("IN2", "OUT", TimingValue(471, 488)) # to OUT2
|
||||||
lut.add_comb_arc("IN3", "OUT", TimingValue(446, 449)) # to OUT2
|
lut.add_comb_arc("IN3", "OUT", TimingValue(446, 449)) # to OUT2
|
||||||
lut.add_comb_arc("IN4", "OUT", TimingValue(443, 453)) # to OUT2
|
lut.add_comb_arc("IN4", "OUT", TimingValue(443, 453)) # to OUT2
|
||||||
|
|
||||||
lut = ch.timing.add_cell_variant(speed, "CPE_HALF")
|
lut = ch.timing.add_cell_variant(speed, "CPE_LT")
|
||||||
lut.add_comb_arc("IN1", "OUT", TimingValue(479, 484)) # to OUT2
|
lut.add_comb_arc("IN1", "OUT", TimingValue(479, 484)) # to OUT2
|
||||||
lut.add_comb_arc("IN2", "OUT", TimingValue(471, 488)) # to OUT2
|
lut.add_comb_arc("IN2", "OUT", TimingValue(471, 488)) # to OUT2
|
||||||
lut.add_comb_arc("IN3", "OUT", TimingValue(446, 449)) # to OUT2
|
lut.add_comb_arc("IN3", "OUT", TimingValue(446, 449)) # to OUT2
|
||||||
lut.add_comb_arc("IN4", "OUT", TimingValue(443, 453)) # to OUT2
|
lut.add_comb_arc("IN4", "OUT", TimingValue(443, 453)) # to OUT2
|
||||||
|
|
||||||
dff = ch.timing.add_cell_variant(speed, "CPE_DFF")
|
lut = ch.timing.add_cell_variant(speed, "CPE_L2T4")
|
||||||
dff.add_setup_hold("CLK", "IN1", ClockEdge.RISING, TimingValue(60), TimingValue(50))
|
lut.add_comb_arc("IN1", "OUT", TimingValue(479, 484)) # to OUT2
|
||||||
dff.add_setup_hold("CLK", "IN2", ClockEdge.RISING, TimingValue(60), TimingValue(50))
|
lut.add_comb_arc("IN2", "OUT", TimingValue(471, 488)) # to OUT2
|
||||||
dff.add_setup_hold("CLK", "IN3", ClockEdge.RISING, TimingValue(60), TimingValue(50))
|
lut.add_comb_arc("IN3", "OUT", TimingValue(446, 449)) # to OUT2
|
||||||
dff.add_setup_hold("CLK", "IN4", ClockEdge.RISING, TimingValue(60), TimingValue(50))
|
lut.add_comb_arc("IN4", "OUT", TimingValue(443, 453)) # to OUT2
|
||||||
dff.add_clock_out("CLK", "OUT", ClockEdge.RISING, TimingValue(60))
|
|
||||||
|
|
||||||
EXPECTED_VERSION = 1.2
|
lut = ch.timing.add_cell_variant(speed, "CPE_L2T5")
|
||||||
|
lut.add_comb_arc("IN1", "OUT1", TimingValue(479, 484)) # to OUT2
|
||||||
|
lut.add_comb_arc("IN2", "OUT1", TimingValue(471, 488)) # to OUT2
|
||||||
|
lut.add_comb_arc("IN3", "OUT1", TimingValue(446, 449)) # to OUT2
|
||||||
|
lut.add_comb_arc("IN4", "OUT1", TimingValue(443, 453)) # to OUT2
|
||||||
|
lut.add_comb_arc("IN5", "OUT1", TimingValue(416, 418)) # IN5 to OUT1
|
||||||
|
lut.add_comb_arc("IN6", "OUT1", TimingValue(413, 422)) # IN6 to OUT1
|
||||||
|
lut.add_comb_arc("IN7", "OUT1", TimingValue(372, 374)) # IN7 to OUT1
|
||||||
|
lut.add_comb_arc("IN8", "OUT1", TimingValue(275, 385)) # IN8 to OUT1
|
||||||
|
|
||||||
|
lut = ch.timing.add_cell_variant(speed, "CPE_MX4")
|
||||||
|
lut.add_comb_arc("IN1", "OUT1", TimingValue(479, 484)) # to OUT2
|
||||||
|
lut.add_comb_arc("IN2", "OUT1", TimingValue(471, 488)) # to OUT2
|
||||||
|
lut.add_comb_arc("IN3", "OUT1", TimingValue(446, 449)) # to OUT2
|
||||||
|
lut.add_comb_arc("IN4", "OUT1", TimingValue(443, 453)) # to OUT2
|
||||||
|
lut.add_comb_arc("IN5", "OUT1", TimingValue(416, 418)) # IN5 to OUT1
|
||||||
|
lut.add_comb_arc("IN6", "OUT1", TimingValue(413, 422)) # IN6 to OUT1
|
||||||
|
lut.add_comb_arc("IN7", "OUT1", TimingValue(372, 374)) # IN7 to OUT1
|
||||||
|
lut.add_comb_arc("IN8", "OUT1", TimingValue(275, 385)) # IN8 to OUT1
|
||||||
|
|
||||||
|
lut = ch.timing.add_cell_variant(speed, "CPE_CI")
|
||||||
|
lut.add_comb_arc("IN1", "COUTY1", TimingValue(479, 484)) # to OUT2
|
||||||
|
lut.add_comb_arc("IN2", "COUTY1", TimingValue(471, 488)) # to OUT2
|
||||||
|
lut.add_comb_arc("IN3", "COUTY1", TimingValue(446, 449)) # to OUT2
|
||||||
|
lut.add_comb_arc("IN4", "COUTY1", TimingValue(443, 453)) # to OUT2
|
||||||
|
|
||||||
|
lut = ch.timing.add_cell_variant(speed, "CPE_EN_CIN")
|
||||||
|
lut.add_comb_arc("CINY1", "OUT1", TimingValue(479, 484)) # to OUT2
|
||||||
|
|
||||||
|
lut = ch.timing.add_cell_variant(speed, "CPE_ADDF")
|
||||||
|
lut.add_comb_arc("IN1", "OUT2", TimingValue(479, 484)) # to OUT2
|
||||||
|
lut.add_comb_arc("IN2", "OUT2", TimingValue(471, 488)) # to OUT2
|
||||||
|
lut.add_comb_arc("IN3", "OUT2", TimingValue(446, 449)) # to OUT2
|
||||||
|
lut.add_comb_arc("IN4", "OUT2", TimingValue(443, 453)) # to OUT2
|
||||||
|
lut.add_comb_arc("IN5", "OUT1", TimingValue(416, 418)) # IN5 to OUT1
|
||||||
|
lut.add_comb_arc("IN6", "OUT1", TimingValue(413, 422)) # IN6 to OUT1
|
||||||
|
lut.add_comb_arc("IN7", "OUT1", TimingValue(372, 374)) # IN7 to OUT1
|
||||||
|
lut.add_comb_arc("IN8", "OUT1", TimingValue(275, 385)) # IN8 to OUT1
|
||||||
|
lut.add_comb_arc("CINY1", "COUTY1", TimingValue(479, 484))
|
||||||
|
|
||||||
|
lut = ch.timing.add_cell_variant(speed, "CPE_ADDF2")
|
||||||
|
lut.add_comb_arc("IN1", "OUT2", TimingValue(479, 484)) # to OUT2
|
||||||
|
lut.add_comb_arc("IN2", "OUT2", TimingValue(471, 488)) # to OUT2
|
||||||
|
lut.add_comb_arc("IN3", "OUT2", TimingValue(446, 449)) # to OUT2
|
||||||
|
lut.add_comb_arc("IN4", "OUT2", TimingValue(443, 453)) # to OUT2
|
||||||
|
lut.add_comb_arc("IN5", "OUT1", TimingValue(416, 418)) # IN5 to OUT1
|
||||||
|
lut.add_comb_arc("IN6", "OUT1", TimingValue(413, 422)) # IN6 to OUT1
|
||||||
|
lut.add_comb_arc("IN7", "OUT1", TimingValue(372, 374)) # IN7 to OUT1
|
||||||
|
lut.add_comb_arc("IN8", "OUT1", TimingValue(275, 385)) # IN8 to OUT1
|
||||||
|
lut.add_comb_arc("CINY1", "COUTY1", TimingValue(479, 484))
|
||||||
|
|
||||||
|
dff = ch.timing.add_cell_variant(speed, "CPE_FF")
|
||||||
|
dff.add_setup_hold("CLK", "DIN", ClockEdge.RISING, TimingValue(60), TimingValue(50))
|
||||||
|
dff.add_clock_out("CLK", "DOUT", ClockEdge.RISING, TimingValue(60))
|
||||||
|
|
||||||
|
dff = ch.timing.add_cell_variant(speed, "CPE_LATCH")
|
||||||
|
dff.add_setup_hold("CLK", "DIN", ClockEdge.RISING, TimingValue(60), TimingValue(50))
|
||||||
|
dff.add_clock_out("CLK", "DOUT", ClockEdge.RISING, TimingValue(60))
|
||||||
|
|
||||||
|
lut = ch.timing.add_cell_variant(speed, "CPE_RAMI")
|
||||||
|
lut.add_comb_arc("RAM_I", "OUT", TimingValue(0, 0))
|
||||||
|
|
||||||
|
lut = ch.timing.add_cell_variant(speed, "CPE_RAMO")
|
||||||
|
lut.add_comb_arc("I", "RAM_O", TimingValue(0, 0))
|
||||||
|
|
||||||
|
lut = ch.timing.add_cell_variant(speed, "CPE_RAMIO")
|
||||||
|
#lut.add_comb_arc("I", "OUT", TimingValue(0, 0))
|
||||||
|
lut.add_comb_arc("I", "RAM_O", TimingValue(0, 0))
|
||||||
|
lut.add_comb_arc("RAM_I", "OUT", TimingValue(0, 0))
|
||||||
|
|
||||||
|
EXPECTED_VERSION = 1.3
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
# Range needs to be +1, but we are adding +2 more to coordinates, since
|
# Range needs to be +1, but we are adding +2 more to coordinates, since
|
||||||
|
|
@ -199,9 +268,11 @@ def main():
|
||||||
tt.create_wire(wire.name, wire.type)
|
tt.create_wire(wire.name, wire.type)
|
||||||
for prim in sorted(die.get_primitives_for_type(type_name)):
|
for prim in sorted(die.get_primitives_for_type(type_name)):
|
||||||
bel = tt.create_bel(prim.name, prim.type, prim.z)
|
bel = tt.create_bel(prim.name, prim.type, prim.z)
|
||||||
|
if (prim.name in ["CPE_LT_FULL"]):
|
||||||
|
bel.flags |= BEL_FLAG_HIDDEN
|
||||||
extra = BelExtraData()
|
extra = BelExtraData()
|
||||||
for constr in sorted(die.get_pins_constraint(type_name, prim.name, prim.type)):
|
for constr in sorted(die.get_pins_constraint(type_name, prim.name, prim.type)):
|
||||||
extra.add_constraints(ch.strs.id(constr.name),constr.rel_x,constr.rel_y,0 if constr.pin_num==2 else 1)
|
extra.add_constraints(ch.strs.id(constr.name),constr.rel_x,constr.rel_y,4 if constr.pin_num==2 else 5)
|
||||||
bel.extra_data = extra
|
bel.extra_data = extra
|
||||||
for pin in sorted(die.get_primitive_pins(prim.type)):
|
for pin in sorted(die.get_primitive_pins(prim.type)):
|
||||||
tt.add_bel_pin(bel, pin.name, die.get_pin_connection_name(prim,pin), pin.dir)
|
tt.add_bel_pin(bel, pin.name, die.get_pin_connection_name(prim,pin), pin.dir)
|
||||||
|
|
@ -239,7 +310,7 @@ def main():
|
||||||
pkg = ch.create_package(package)
|
pkg = ch.create_package(package)
|
||||||
for pad in sorted(dev.get_package_pads(package)):
|
for pad in sorted(dev.get_package_pads(package)):
|
||||||
pp = pkg.create_pad(pad.name, f"X{pad.x+2}Y{pad.y+2}", pad.bel, pad.function, pad.bank, pad.flags)
|
pp = pkg.create_pad(pad.name, f"X{pad.x+2}Y{pad.y+2}", pad.bel, pad.function, pad.bank, pad.flags)
|
||||||
pp.extra_data = PadExtraData(pad.ddr.x+2, pad.ddr.y+2, pad.ddr.z)
|
pp.extra_data = PadExtraData(pad.ddr.x+2, pad.ddr.y+2, 4 if pad.ddr.z==0 else 5)
|
||||||
|
|
||||||
ch.write_bba(args.bba)
|
ch.write_bba(args.bba)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -34,20 +34,55 @@ void GateMateImpl::drawBel(std::vector<GraphicElement> &g, GraphicElement::style
|
||||||
el.type = GraphicElement::TYPE_BOX;
|
el.type = GraphicElement::TYPE_BOX;
|
||||||
el.style = style;
|
el.style = style;
|
||||||
switch (bel_type.index) {
|
switch (bel_type.index) {
|
||||||
case id_CPE_HALF_L.index:
|
case id_CPE_LT_FULL.index:
|
||||||
|
el.x1 = loc.x + 0.21;
|
||||||
|
el.x2 = el.x1 + 0.18;
|
||||||
|
el.y1 = loc.y + 0.25;
|
||||||
|
el.y2 = el.y1 + 0.50;
|
||||||
|
g.push_back(el);
|
||||||
|
break;
|
||||||
|
case id_CPE_LT_L.index:
|
||||||
el.x1 = loc.x + 0.20;
|
el.x1 = loc.x + 0.20;
|
||||||
el.x2 = el.x1 + 0.20;
|
el.x2 = el.x1 + 0.20;
|
||||||
el.y1 = loc.y + 0.25;
|
el.y1 = loc.y + 0.25;
|
||||||
el.y2 = el.y1 + 0.20;
|
el.y2 = el.y1 + 0.20;
|
||||||
g.push_back(el);
|
g.push_back(el);
|
||||||
break;
|
break;
|
||||||
case id_CPE_HALF_U.index:
|
case id_CPE_LT_U.index:
|
||||||
el.x1 = loc.x + 0.20;
|
el.x1 = loc.x + 0.20;
|
||||||
el.x2 = el.x1 + 0.20;
|
el.x2 = el.x1 + 0.20;
|
||||||
el.y1 = loc.y + 0.55;
|
el.y1 = loc.y + 0.55;
|
||||||
el.y2 = el.y1 + 0.20;
|
el.y2 = el.y1 + 0.20;
|
||||||
g.push_back(el);
|
g.push_back(el);
|
||||||
break;
|
break;
|
||||||
|
case id_CPE_FF_L.index:
|
||||||
|
el.x1 = loc.x + 0.45;
|
||||||
|
el.x2 = el.x1 + 0.10;
|
||||||
|
el.y1 = loc.y + 0.25;
|
||||||
|
el.y2 = el.y1 + 0.20;
|
||||||
|
g.push_back(el);
|
||||||
|
break;
|
||||||
|
case id_CPE_FF_U.index:
|
||||||
|
el.x1 = loc.x + 0.45;
|
||||||
|
el.x2 = el.x1 + 0.10;
|
||||||
|
el.y1 = loc.y + 0.55;
|
||||||
|
el.y2 = el.y1 + 0.20;
|
||||||
|
g.push_back(el);
|
||||||
|
break;
|
||||||
|
case id_CPE_RAMIO_L.index:
|
||||||
|
el.x1 = loc.x + 0.60;
|
||||||
|
el.x2 = el.x1 + 0.10;
|
||||||
|
el.y1 = loc.y + 0.25;
|
||||||
|
el.y2 = el.y1 + 0.20;
|
||||||
|
g.push_back(el);
|
||||||
|
break;
|
||||||
|
case id_CPE_RAMIO_U.index:
|
||||||
|
el.x1 = loc.x + 0.60;
|
||||||
|
el.x2 = el.x1 + 0.10;
|
||||||
|
el.y1 = loc.y + 0.55;
|
||||||
|
el.y2 = el.y1 + 0.20;
|
||||||
|
g.push_back(el);
|
||||||
|
break;
|
||||||
case id_RAM.index:
|
case id_RAM.index:
|
||||||
el.x1 = loc.x + 0.70;
|
el.x1 = loc.x + 0.70;
|
||||||
el.x2 = el.x1 + 0.20;
|
el.x2 = el.x1 + 0.20;
|
||||||
|
|
|
||||||
|
|
@ -47,39 +47,69 @@ void GateMatePacker::disconnect_if_gnd(CellInfo *cell, IdString input)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CellInfo *GateMatePacker::move_ram_i(CellInfo *cell, IdString origPort, bool place)
|
std::pair<CellInfo *, CellInfo *> GateMatePacker::move_ram_i(CellInfo *cell, IdString origPort, bool place, Loc cpe_loc)
|
||||||
{
|
{
|
||||||
CellInfo *cpe_half = nullptr;
|
CellInfo *cpe_half = nullptr;
|
||||||
|
CellInfo *cpe_ramio = nullptr;
|
||||||
NetInfo *net = cell->getPort(origPort);
|
NetInfo *net = cell->getPort(origPort);
|
||||||
if (net) {
|
if (net) {
|
||||||
cpe_half = create_cell_ptr(id_CPE_HALF, ctx->idf("%s$%s_cpe_half", cell->name.c_str(ctx), origPort.c_str(ctx)));
|
cpe_ramio = create_cell_ptr(id_CPE_RAMI, ctx->idf("%s$%s_rami", cell->name.c_str(ctx), origPort.c_str(ctx)));
|
||||||
if (place) {
|
if (place) {
|
||||||
cell->constr_children.push_back(cpe_half);
|
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->cluster = cell->cluster;
|
||||||
cpe_half->constr_abs_z = false;
|
cpe_half->constr_abs_z = false;
|
||||||
cpe_half->constr_z = PLACE_DB_CONSTR + origPort.index;
|
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_half->params[id_C_RAM_I] = Property(1, 1);
|
|
||||||
|
|
||||||
NetInfo *ram_i = ctx->createNet(ctx->idf("%s$ram_i", cpe_half->name.c_str(ctx)));
|
cpe_ramio->params[id_C_RAM_I] = Property(1, 1);
|
||||||
cell->movePortTo(origPort, cpe_half, id_OUT);
|
|
||||||
|
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);
|
cell->connectPort(origPort, ram_i);
|
||||||
cpe_half->connectPort(id_RAM_I, ram_i);
|
cpe_ramio->connectPort(id_RAM_I, ram_i);
|
||||||
}
|
}
|
||||||
return cpe_half;
|
return std::make_pair(cpe_half, cpe_ramio);
|
||||||
}
|
}
|
||||||
|
|
||||||
CellInfo *GateMatePacker::move_ram_o(CellInfo *cell, IdString origPort, bool place)
|
std::pair<CellInfo *, CellInfo *> GateMatePacker::move_ram_o(CellInfo *cell, IdString origPort, bool place, Loc cpe_loc)
|
||||||
{
|
{
|
||||||
CellInfo *cpe_half = nullptr;
|
CellInfo *cpe_half = nullptr;
|
||||||
|
CellInfo *cpe_ramio = nullptr;
|
||||||
NetInfo *net = cell->getPort(origPort);
|
NetInfo *net = cell->getPort(origPort);
|
||||||
if (net) {
|
if (net) {
|
||||||
cpe_half = create_cell_ptr(id_CPE_HALF, ctx->idf("%s$%s_cpe_half", cell->name.c_str(ctx), origPort.c_str(ctx)));
|
cpe_ramio = create_cell_ptr(id_CPE_RAMO, ctx->idf("%s$%s_ramo", cell->name.c_str(ctx), origPort.c_str(ctx)));
|
||||||
if (place) {
|
if (place) {
|
||||||
cell->constr_children.push_back(cpe_half);
|
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->cluster = cell->cluster;
|
||||||
cpe_half->constr_abs_z = false;
|
cpe_half->constr_abs_z = false;
|
||||||
cpe_half->constr_z = PLACE_DB_CONSTR + origPort.index;
|
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")) {
|
if (net->name == ctx->id("$PACKER_GND")) {
|
||||||
cpe_half->params[id_INIT_L00] = Property(0b0000, 4);
|
cpe_half->params[id_INIT_L00] = Property(0b0000, 4);
|
||||||
|
|
@ -92,50 +122,47 @@ CellInfo *GateMatePacker::move_ram_o(CellInfo *cell, IdString origPort, bool pla
|
||||||
cell->movePortTo(origPort, cpe_half, id_IN1);
|
cell->movePortTo(origPort, cpe_half, id_IN1);
|
||||||
}
|
}
|
||||||
cpe_half->params[id_INIT_L10] = Property(0b1010, 4);
|
cpe_half->params[id_INIT_L10] = Property(0b1010, 4);
|
||||||
cpe_half->params[id_C_O] = Property(0b11, 2);
|
|
||||||
cpe_half->params[id_C_RAM_O] = Property(1, 1);
|
|
||||||
|
|
||||||
|
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)));
|
NetInfo *ram_o = ctx->createNet(ctx->idf("%s$ram_o", cpe_half->name.c_str(ctx)));
|
||||||
cell->connectPort(origPort, ram_o);
|
cell->connectPort(origPort, ram_o);
|
||||||
cpe_half->connectPort(id_RAM_O, 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 cpe_half;
|
return std::make_pair(cpe_half, cpe_ramio);
|
||||||
}
|
}
|
||||||
|
|
||||||
CellInfo *GateMatePacker::move_ram_i_fixed(CellInfo *cell, IdString origPort, Loc fixed)
|
std::pair<CellInfo *, CellInfo *> GateMatePacker::move_ram_io(CellInfo *cell, IdString iPort, IdString oPort,
|
||||||
|
bool place, Loc cpe_loc)
|
||||||
{
|
{
|
||||||
CellInfo *cpe = move_ram_i(cell, origPort, false);
|
|
||||||
if (cpe) {
|
|
||||||
BelId b = ctx->getBelByLocation(uarch->getRelativeConstraint(fixed, origPort));
|
|
||||||
ctx->bindBel(b, cpe, PlaceStrength::STRENGTH_FIXED);
|
|
||||||
}
|
|
||||||
return cpe;
|
|
||||||
}
|
|
||||||
|
|
||||||
CellInfo *GateMatePacker::move_ram_o_fixed(CellInfo *cell, IdString origPort, Loc fixed)
|
|
||||||
{
|
|
||||||
CellInfo *cpe = move_ram_o(cell, origPort, false);
|
|
||||||
if (cpe) {
|
|
||||||
BelId b = ctx->getBelByLocation(uarch->getRelativeConstraint(fixed, origPort));
|
|
||||||
ctx->bindBel(b, cpe, PlaceStrength::STRENGTH_FIXED);
|
|
||||||
}
|
|
||||||
return cpe;
|
|
||||||
}
|
|
||||||
|
|
||||||
CellInfo *GateMatePacker::move_ram_io(CellInfo *cell, IdString iPort, IdString oPort, bool place)
|
|
||||||
{
|
|
||||||
CellInfo *cpe_half = nullptr;
|
|
||||||
NetInfo *i_net = cell->getPort(iPort);
|
NetInfo *i_net = cell->getPort(iPort);
|
||||||
NetInfo *o_net = cell->getPort(oPort);
|
NetInfo *o_net = cell->getPort(oPort);
|
||||||
if (!i_net && !o_net)
|
if (!i_net && !o_net)
|
||||||
return cpe_half;
|
return std::make_pair(nullptr, nullptr);
|
||||||
|
|
||||||
cpe_half = create_cell_ptr(id_CPE_HALF, ctx->idf("%s$%s_cpe_half", cell->name.c_str(ctx), oPort.c_str(ctx)));
|
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) {
|
if (place) {
|
||||||
cell->constr_children.push_back(cpe_half);
|
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->cluster = cell->cluster;
|
||||||
cpe_half->constr_abs_z = false;
|
cpe_half->constr_abs_z = false;
|
||||||
cpe_half->constr_z = PLACE_DB_CONSTR + oPort.index;
|
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) {
|
||||||
|
|
@ -150,24 +177,41 @@ CellInfo *GateMatePacker::move_ram_io(CellInfo *cell, IdString iPort, IdString o
|
||||||
cell->movePortTo(oPort, cpe_half, id_IN1);
|
cell->movePortTo(oPort, cpe_half, id_IN1);
|
||||||
}
|
}
|
||||||
cpe_half->params[id_INIT_L10] = Property(0b1010, 4);
|
cpe_half->params[id_INIT_L10] = Property(0b1010, 4);
|
||||||
cpe_half->params[id_C_O] = Property(0b11, 2);
|
cpe_ramio->params[id_C_RAM_O] = Property(1, 1);
|
||||||
cpe_half->params[id_C_RAM_O] = Property(1, 1);
|
|
||||||
|
|
||||||
NetInfo *ram_o = ctx->createNet(ctx->idf("%s$ram_o", cpe_half->name.c_str(ctx)));
|
NetInfo *ram_o = ctx->createNet(ctx->idf("%s$ram_o", cpe_half->name.c_str(ctx)));
|
||||||
cell->connectPort(oPort, ram_o);
|
cell->connectPort(oPort, ram_o);
|
||||||
cpe_half->connectPort(id_RAM_O, 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) {
|
if (i_net) {
|
||||||
cpe_half->params[id_C_RAM_I] = Property(1, 1);
|
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)));
|
NetInfo *ram_i = ctx->createNet(ctx->idf("%s$ram_i", cpe_half->name.c_str(ctx)));
|
||||||
cell->movePortTo(iPort, cpe_half, id_OUT);
|
cell->movePortTo(iPort, cpe_ramio, id_OUT);
|
||||||
cell->connectPort(iPort, ram_i);
|
cell->connectPort(iPort, ram_i);
|
||||||
cpe_half->connectPort(id_RAM_I, ram_i);
|
cpe_ramio->connectPort(id_RAM_I, ram_i);
|
||||||
}
|
}
|
||||||
// TODO: set proper timing model, without this it detects combinational loops
|
return std::make_pair(cpe_half, cpe_ramio);
|
||||||
cpe_half->timing_index = ctx->get_cell_timing_idx(id_CPE_DFF);
|
}
|
||||||
return cpe_half;
|
|
||||||
|
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()
|
void GateMatePacker::pack_misc()
|
||||||
|
|
@ -258,6 +302,7 @@ void GateMateImpl::pack()
|
||||||
packer.pack_misc();
|
packer.pack_misc();
|
||||||
packer.pack_ram();
|
packer.pack_ram();
|
||||||
packer.pack_serdes();
|
packer.pack_serdes();
|
||||||
|
//packer.pack_mult();
|
||||||
packer.pack_addf();
|
packer.pack_addf();
|
||||||
packer.pack_cpe();
|
packer.pack_cpe();
|
||||||
packer.remove_constants();
|
packer.remove_constants();
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,26 @@
|
||||||
|
|
||||||
NEXTPNR_NAMESPACE_BEGIN
|
NEXTPNR_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
enum CPELut
|
||||||
|
{
|
||||||
|
LUT_ZERO = 0b0000,
|
||||||
|
LUT_NOR = 0b0001,
|
||||||
|
LUT_AND_INV_D1 = 0b0010,
|
||||||
|
LUT_INV_D1 = 0b0011,
|
||||||
|
LUT_AND_INV_D0 = 0b0100,
|
||||||
|
LUT_INV_D0 = 0b0101,
|
||||||
|
LUT_XOR = 0b0110,
|
||||||
|
LUT_NAND = 0b0111,
|
||||||
|
LUT_AND = 0b1000,
|
||||||
|
LUT_XNOR = 0b1001,
|
||||||
|
LUT_D0 = 0b1010,
|
||||||
|
LUT_NAND_INV_D0 = 0b1011,
|
||||||
|
LUT_D1 = 0b1100,
|
||||||
|
LUT_NAND_INV_D1 = 0b1101,
|
||||||
|
LUT_OR = 0b1110,
|
||||||
|
LUT_ONE = 0b1111
|
||||||
|
};
|
||||||
|
|
||||||
struct GateMatePacker
|
struct GateMatePacker
|
||||||
{
|
{
|
||||||
GateMatePacker(Context *ctx, GateMateImpl *uarch) : ctx(ctx), uarch(uarch) { h.init(ctx); };
|
GateMatePacker(Context *ctx, GateMateImpl *uarch) : ctx(ctx), uarch(uarch) { h.init(ctx); };
|
||||||
|
|
@ -37,6 +57,7 @@ struct GateMatePacker
|
||||||
void insert_pll_bufg();
|
void insert_pll_bufg();
|
||||||
void pack_pll();
|
void pack_pll();
|
||||||
void pack_misc();
|
void pack_misc();
|
||||||
|
void pack_mult();
|
||||||
void pack_constants();
|
void pack_constants();
|
||||||
void pack_ram();
|
void pack_ram();
|
||||||
void pack_serdes();
|
void pack_serdes();
|
||||||
|
|
@ -46,18 +67,22 @@ struct GateMatePacker
|
||||||
void remove_not_used();
|
void remove_not_used();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void dff_to_cpe(CellInfo *dff, CellInfo *cpe);
|
void dff_to_cpe(CellInfo *dff);
|
||||||
void insert_bufg(CellInfo *cell, IdString port);
|
void insert_bufg(CellInfo *cell, IdString port);
|
||||||
void disconnect_if_gnd(CellInfo *cell, IdString input);
|
void disconnect_if_gnd(CellInfo *cell, IdString input);
|
||||||
void pll_out(CellInfo *cell, IdString origPort, Loc fixed);
|
void pll_out(CellInfo *cell, IdString origPort, Loc fixed);
|
||||||
|
|
||||||
PllCfgRecord get_pll_settings(double f_ref, double f_core, int mode, int low_jitter, bool pdiv0_mux, bool feedback);
|
PllCfgRecord get_pll_settings(double f_ref, double f_core, int mode, int low_jitter, bool pdiv0_mux, bool feedback);
|
||||||
|
|
||||||
CellInfo *move_ram_i(CellInfo *cell, IdString origPort, bool place = true);
|
std::pair<CellInfo *, CellInfo *> move_ram_i(CellInfo *cell, IdString origPort, bool place = true,
|
||||||
CellInfo *move_ram_o(CellInfo *cell, IdString origPort, bool place = true);
|
Loc cpe_loc = Loc());
|
||||||
CellInfo *move_ram_i_fixed(CellInfo *cell, IdString origPort, Loc fixed);
|
std::pair<CellInfo *, CellInfo *> move_ram_o(CellInfo *cell, IdString origPort, bool place = true,
|
||||||
CellInfo *move_ram_o_fixed(CellInfo *cell, IdString origPort, Loc fixed);
|
Loc cpe_loc = Loc());
|
||||||
CellInfo *move_ram_io(CellInfo *cell, IdString iPort, IdString oPort, bool place = true);
|
std::pair<CellInfo *, CellInfo *> move_ram_io(CellInfo *cell, IdString iPort, IdString oPort, bool place = true,
|
||||||
|
Loc cpe_loc = Loc());
|
||||||
|
std::pair<CellInfo *, CellInfo *> move_ram_i_fixed(CellInfo *cell, IdString origPort, Loc fixed);
|
||||||
|
std::pair<CellInfo *, CellInfo *> move_ram_o_fixed(CellInfo *cell, IdString origPort, Loc fixed);
|
||||||
|
std::pair<CellInfo *, CellInfo *> move_ram_io_fixed(CellInfo *cell, IdString iPort, IdString oPort, Loc fixed);
|
||||||
uint8_t ram_ctrl_signal(CellInfo *cell, IdString port, bool alt);
|
uint8_t ram_ctrl_signal(CellInfo *cell, IdString port, bool alt);
|
||||||
uint8_t ram_clk_signal(CellInfo *cell, IdString port);
|
uint8_t ram_clk_signal(CellInfo *cell, IdString port);
|
||||||
bool is_gpio_valid_dff(CellInfo *dff);
|
bool is_gpio_valid_dff(CellInfo *dff);
|
||||||
|
|
|
||||||
|
|
@ -199,17 +199,20 @@ void GateMatePacker::pack_bufg()
|
||||||
if (user_glb) {
|
if (user_glb) {
|
||||||
ci.movePortTo(id_I, glbout[die], ctx->idf("USR_GLB%d", i));
|
ci.movePortTo(id_I, glbout[die], ctx->idf("USR_GLB%d", i));
|
||||||
move_ram_o_fixed(glbout[die], ctx->idf("USR_GLB%d", i), ctx->getBelLocation(glbout[die]->bel));
|
move_ram_o_fixed(glbout[die], ctx->idf("USR_GLB%d", i), ctx->getBelLocation(glbout[die]->bel));
|
||||||
glbout[die]->params[ctx->idf("USR_GLB%d", i)] = Property(Property::State::S1);
|
glbout[die]->params[ctx->idf("USR_GLB%d_EN", i)] = Property(Property::State::S1);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// SER_CLK
|
// SER_CLK
|
||||||
clkin[die]->params[ctx->idf("REF%d", i)] = Property(0b100, 3);
|
clkin[die]->params[ctx->idf("REF%d", i)] = Property(0b100, 3);
|
||||||
clkin[die]->params[ctx->idf("REF%d_INV", i)] = Property(Property::State::S0);
|
clkin[die]->params[ctx->idf("REF%d_INV", i)] = Property(Property::State::S0);
|
||||||
|
NetInfo *conn = ctx->createNet(ci.name);
|
||||||
|
clkin[die]->connectPort(ctx->idf("CLK_REF%d", i), conn);
|
||||||
|
glbout[die]->connectPort(ctx->idf("CLK_REF_OUT%d", i), conn);
|
||||||
}
|
}
|
||||||
|
|
||||||
ci.movePortTo(id_O, glbout[die], ctx->idf("GLB%d", i));
|
ci.movePortTo(id_O, glbout[die], ctx->idf("GLB%d", i));
|
||||||
glbout[die]->params[ctx->idf("GLB%d_EN", i)] = Property(Property::State::S1);
|
glbout[die]->params[ctx->idf("GLB%d_EN", i)] = Property(Property::State::S1);
|
||||||
glbout[die]->params[ctx->idf("GLB%d", i)] = Property(glb_mux, 3);
|
glbout[die]->params[ctx->idf("GLB%d_CFG", i)] = Property(glb_mux, 3);
|
||||||
packed_cells.emplace(ci.name);
|
packed_cells.emplace(ci.name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -222,10 +225,10 @@ void GateMatePacker::pack_bufg()
|
||||||
if (!global_signals.count(feedback_net)) {
|
if (!global_signals.count(feedback_net)) {
|
||||||
pll[i]->movePortTo(id_CLK_FEEDBACK, glbout[die], ctx->idf("USR_FB%d", i));
|
pll[i]->movePortTo(id_CLK_FEEDBACK, glbout[die], ctx->idf("USR_FB%d", i));
|
||||||
move_ram_o_fixed(glbout[die], ctx->idf("USR_FB%d", i), ctx->getBelLocation(glbout[die]->bel));
|
move_ram_o_fixed(glbout[die], ctx->idf("USR_FB%d", i), ctx->getBelLocation(glbout[die]->bel));
|
||||||
glbout[die]->params[ctx->idf("USR_FB%d", i)] = Property(Property::State::S1);
|
glbout[die]->params[ctx->idf("USR_FB%d_EN", i)] = Property(Property::State::S1);
|
||||||
} else {
|
} else {
|
||||||
int index = global_signals[feedback_net];
|
int index = global_signals[feedback_net];
|
||||||
glbout[die]->params[ctx->idf("FB%d", i)] = Property(index, 2);
|
glbout[die]->params[ctx->idf("FB%d_CFG", i)] = Property(index, 2);
|
||||||
pll[i]->disconnectPort(id_CLK_FEEDBACK);
|
pll[i]->disconnectPort(id_CLK_FEEDBACK);
|
||||||
}
|
}
|
||||||
NetInfo *conn =
|
NetInfo *conn =
|
||||||
|
|
|
||||||
|
|
@ -29,94 +29,102 @@ NEXTPNR_NAMESPACE_BEGIN
|
||||||
// Return true if a cell is a flipflop
|
// 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); }
|
inline bool is_dff(const BaseCtx *ctx, const CellInfo *cell) { return cell->type.in(id_CC_DFF, id_CC_DLT); }
|
||||||
|
|
||||||
void GateMatePacker::dff_to_cpe(CellInfo *dff, CellInfo *cpe)
|
void GateMatePacker::dff_to_cpe(CellInfo *dff)
|
||||||
{
|
{
|
||||||
bool invert;
|
bool invert;
|
||||||
bool is_latch = dff->type == id_CC_DLT;
|
bool is_latch = dff->type == id_CC_DLT;
|
||||||
if (is_latch) {
|
if (is_latch) {
|
||||||
NetInfo *g_net = cpe->getPort(id_G);
|
NetInfo *g_net = dff->getPort(id_G);
|
||||||
invert = bool_or_default(dff->params, id_G_INV, 0);
|
invert = bool_or_default(dff->params, id_G_INV, 0);
|
||||||
if (g_net) {
|
if (g_net) {
|
||||||
if (g_net->name == ctx->id("$PACKER_GND")) {
|
if (g_net->name == ctx->id("$PACKER_GND")) {
|
||||||
cpe->params[id_C_CPE_CLK] = Property(invert ? 0b11 : 0b00, 2);
|
dff->params[id_C_CPE_CLK] = Property(invert ? 0b11 : 0b00, 2);
|
||||||
cpe->disconnectPort(id_G);
|
dff->disconnectPort(id_G);
|
||||||
} else if (g_net->name == ctx->id("$PACKER_VCC")) {
|
} else if (g_net->name == ctx->id("$PACKER_VCC")) {
|
||||||
cpe->params[id_C_CPE_CLK] = Property(invert ? 0b00 : 0b11, 2);
|
dff->params[id_C_CPE_CLK] = Property(invert ? 0b00 : 0b11, 2);
|
||||||
cpe->disconnectPort(id_G);
|
dff->disconnectPort(id_G);
|
||||||
} else {
|
} else {
|
||||||
cpe->params[id_C_CPE_CLK] = Property(invert ? 0b01 : 0b10, 2);
|
dff->params[id_C_CPE_CLK] = Property(invert ? 0b01 : 0b10, 2);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
cpe->params[id_C_CPE_CLK] = Property(invert ? 0b11 : 0b00, 2);
|
dff->params[id_C_CPE_CLK] = Property(invert ? 0b11 : 0b00, 2);
|
||||||
}
|
}
|
||||||
dff->unsetParam(id_G_INV);
|
dff->unsetParam(id_G_INV);
|
||||||
cpe->renamePort(id_G, id_CLK);
|
dff->renamePort(id_G, id_CLK);
|
||||||
|
|
||||||
cpe->params[id_C_CPE_EN] = Property(0b11, 2);
|
dff->params[id_C_CPE_EN] = Property(0b11, 2);
|
||||||
cpe->params[id_C_L_D] = Property(0b1, 1);
|
dff->params[id_C_L_D] = Property(0b1, 1);
|
||||||
} else {
|
} else {
|
||||||
NetInfo *en_net = cpe->getPort(id_EN);
|
NetInfo *en_net = dff->getPort(id_EN);
|
||||||
bool invert = bool_or_default(dff->params, id_EN_INV, 0);
|
bool invert = bool_or_default(dff->params, id_EN_INV, 0);
|
||||||
if (en_net) {
|
if (en_net) {
|
||||||
if (en_net->name == ctx->id("$PACKER_GND")) {
|
if (en_net->name == ctx->id("$PACKER_GND")) {
|
||||||
cpe->params[id_C_CPE_EN] = Property(invert ? 0b11 : 0b00, 2);
|
dff->params[id_C_CPE_EN] = Property(invert ? 0b11 : 0b00, 2);
|
||||||
cpe->disconnectPort(id_EN);
|
dff->disconnectPort(id_EN);
|
||||||
} else if (en_net->name == ctx->id("$PACKER_VCC")) {
|
} else if (en_net->name == ctx->id("$PACKER_VCC")) {
|
||||||
cpe->params[id_C_CPE_EN] = Property(invert ? 0b00 : 0b11, 2);
|
dff->params[id_C_CPE_EN] = Property(invert ? 0b00 : 0b11, 2);
|
||||||
cpe->disconnectPort(id_EN);
|
dff->disconnectPort(id_EN);
|
||||||
} else {
|
} else {
|
||||||
cpe->params[id_C_CPE_EN] = Property(invert ? 0b01 : 0b10, 2);
|
dff->params[id_C_CPE_EN] = Property(invert ? 0b01 : 0b10, 2);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
cpe->params[id_C_CPE_EN] = Property(invert ? 0b11 : 0b00, 2);
|
dff->params[id_C_CPE_EN] = Property(invert ? 0b11 : 0b00, 2);
|
||||||
}
|
}
|
||||||
dff->unsetParam(id_EN_INV);
|
dff->unsetParam(id_EN_INV);
|
||||||
|
|
||||||
NetInfo *clk_net = cpe->getPort(id_CLK);
|
NetInfo *clk_net = dff->getPort(id_CLK);
|
||||||
invert = bool_or_default(dff->params, id_CLK_INV, 0);
|
invert = bool_or_default(dff->params, id_CLK_INV, 0);
|
||||||
if (clk_net) {
|
if (clk_net) {
|
||||||
if (clk_net->name == ctx->id("$PACKER_GND")) {
|
if (clk_net->name == ctx->id("$PACKER_GND")) {
|
||||||
cpe->params[id_C_CPE_CLK] = Property(invert ? 0b11 : 0b00, 2);
|
dff->params[id_C_CPE_CLK] = Property(invert ? 0b11 : 0b00, 2);
|
||||||
cpe->disconnectPort(id_CLK);
|
dff->disconnectPort(id_CLK);
|
||||||
} else if (clk_net->name == ctx->id("$PACKER_VCC")) {
|
} else if (clk_net->name == ctx->id("$PACKER_VCC")) {
|
||||||
cpe->params[id_C_CPE_CLK] = Property(invert ? 0b00 : 0b11, 2);
|
dff->params[id_C_CPE_CLK] = Property(invert ? 0b00 : 0b11, 2);
|
||||||
cpe->disconnectPort(id_CLK);
|
dff->disconnectPort(id_CLK);
|
||||||
} else {
|
} else {
|
||||||
cpe->params[id_C_CPE_CLK] = Property(invert ? 0b01 : 0b10, 2);
|
dff->params[id_C_CPE_CLK] = Property(invert ? 0b01 : 0b10, 2);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
cpe->params[id_C_CPE_CLK] = Property(invert ? 0b11 : 0b00, 2);
|
dff->params[id_C_CPE_CLK] = Property(invert ? 0b11 : 0b00, 2);
|
||||||
}
|
}
|
||||||
dff->unsetParam(id_CLK_INV);
|
dff->unsetParam(id_CLK_INV);
|
||||||
}
|
}
|
||||||
|
|
||||||
NetInfo *sr_net = cpe->getPort(id_SR);
|
NetInfo *sr_net = dff->getPort(id_SR);
|
||||||
invert = bool_or_default(dff->params, id_SR_INV, 0);
|
invert = bool_or_default(dff->params, id_SR_INV, 0);
|
||||||
bool sr_val = bool_or_default(dff->params, id_SR_VAL, 0);
|
bool sr_val = bool_or_default(dff->params, id_SR_VAL, 0);
|
||||||
if (sr_net) {
|
if (sr_net) {
|
||||||
if (sr_net->name.in(ctx->id("$PACKER_GND"), ctx->id("$PACKER_VCC"))) {
|
if (sr_net->name.in(ctx->id("$PACKER_GND"), ctx->id("$PACKER_VCC"))) {
|
||||||
bool sr_signal = sr_net->name == ctx->id("$PACKER_VCC");
|
bool sr_signal = sr_net->name == ctx->id("$PACKER_VCC");
|
||||||
if (sr_signal ^ invert)
|
if (sr_signal ^ invert) {
|
||||||
log_error("Currently unsupported DFF configuration for '%s'\n.", dff->name.c_str(ctx));
|
if (sr_val) {
|
||||||
cpe->params[id_C_CPE_RES] = Property(0b11, 2);
|
dff->params[id_C_CPE_RES] = Property(0b11, 2);
|
||||||
cpe->params[id_C_CPE_SET] = Property(0b11, 2);
|
dff->params[id_C_CPE_SET] = Property(0b00, 2);
|
||||||
cpe->disconnectPort(id_SR);
|
} else {
|
||||||
|
dff->params[id_C_CPE_RES] = Property(0b00, 2);
|
||||||
|
dff->params[id_C_CPE_SET] = Property(0b11, 2);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
dff->params[id_C_CPE_RES] = Property(0b11, 2);
|
||||||
|
dff->params[id_C_CPE_SET] = Property(0b11, 2);
|
||||||
|
}
|
||||||
|
dff->disconnectPort(id_SR);
|
||||||
} else {
|
} else {
|
||||||
if (sr_val) {
|
if (sr_val) {
|
||||||
cpe->params[id_C_CPE_RES] = Property(0b11, 2);
|
dff->params[id_C_CPE_RES] = Property(0b11, 2);
|
||||||
cpe->params[id_C_CPE_SET] = Property(invert ? 0b10 : 0b01, 2);
|
dff->params[id_C_CPE_SET] = Property(invert ? 0b10 : 0b01, 2);
|
||||||
if (is_latch)
|
if (is_latch)
|
||||||
cpe->renamePort(id_SR, id_EN);
|
dff->renamePort(id_SR, id_EN);
|
||||||
else
|
else
|
||||||
cpe->params[id_C_EN_SR] = Property(0b1, 1);
|
dff->params[id_C_EN_SR] = Property(0b1, 1);
|
||||||
} else {
|
} else {
|
||||||
cpe->params[id_C_CPE_RES] = Property(invert ? 0b10 : 0b01, 2);
|
dff->params[id_C_CPE_RES] = Property(invert ? 0b10 : 0b01, 2);
|
||||||
cpe->params[id_C_CPE_SET] = Property(0b11, 2);
|
dff->params[id_C_CPE_SET] = Property(0b11, 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
cpe->params[id_C_CPE_RES] = Property(0b11, 2);
|
dff->params[id_C_CPE_RES] = Property(0b11, 2);
|
||||||
cpe->params[id_C_CPE_SET] = Property(0b11, 2);
|
dff->params[id_C_CPE_SET] = Property(0b11, 2);
|
||||||
}
|
}
|
||||||
dff->unsetParam(id_SR_VAL);
|
dff->unsetParam(id_SR_VAL);
|
||||||
dff->unsetParam(id_SR_INV);
|
dff->unsetParam(id_SR_INV);
|
||||||
|
|
@ -124,15 +132,13 @@ void GateMatePacker::dff_to_cpe(CellInfo *dff, CellInfo *cpe)
|
||||||
if (dff->params.count(id_INIT) && dff->params[id_INIT].is_fully_def()) {
|
if (dff->params.count(id_INIT) && dff->params[id_INIT].is_fully_def()) {
|
||||||
bool init = bool_or_default(dff->params, id_INIT, 0);
|
bool init = bool_or_default(dff->params, id_INIT, 0);
|
||||||
if (init)
|
if (init)
|
||||||
cpe->params[id_FF_INIT] = Property(0b11, 2);
|
dff->params[id_FF_INIT] = Property(0b11, 2);
|
||||||
else
|
else
|
||||||
cpe->params[id_FF_INIT] = Property(0b10, 2);
|
dff->params[id_FF_INIT] = Property(0b10, 2);
|
||||||
dff->unsetParam(id_INIT);
|
dff->unsetParam(id_INIT);
|
||||||
} else {
|
} else {
|
||||||
dff->unsetParam(id_INIT);
|
dff->unsetParam(id_INIT);
|
||||||
}
|
}
|
||||||
cpe->timing_index = ctx->get_cell_timing_idx(id_CPE_DFF);
|
|
||||||
cpe->params[id_C_O] = Property(0b00, 2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GateMatePacker::pack_cpe()
|
void GateMatePacker::pack_cpe()
|
||||||
|
|
@ -151,11 +157,14 @@ void GateMatePacker::pack_cpe()
|
||||||
ci.renamePort(id_I3, id_IN4);
|
ci.renamePort(id_I3, id_IN4);
|
||||||
|
|
||||||
ci.renamePort(id_O, id_OUT);
|
ci.renamePort(id_O, id_OUT);
|
||||||
|
uarch->rename_param(&ci, id_INIT_L02, id_INIT_L00, 4);
|
||||||
ci.params[id_C_O] = Property(0b11, 2);
|
uarch->rename_param(&ci, id_INIT_L03, id_INIT_L01, 4);
|
||||||
ci.type = id_CPE_HALF_L;
|
uarch->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;
|
||||||
} else if (ci.type == id_CC_MX2) {
|
} else if (ci.type == id_CC_MX2) {
|
||||||
ci.params[id_C_O] = Property(0b11, 2);
|
|
||||||
ci.renamePort(id_D1, id_IN1);
|
ci.renamePort(id_D1, id_IN1);
|
||||||
NetInfo *sel = ci.getPort(id_S0);
|
NetInfo *sel = ci.getPort(id_S0);
|
||||||
ci.renamePort(id_S0, id_IN2);
|
ci.renamePort(id_S0, id_IN2);
|
||||||
|
|
@ -168,14 +177,13 @@ void GateMatePacker::pack_cpe()
|
||||||
ci.params[id_INIT_L01] = Property(0b0100, 4); // AND inv D0
|
ci.params[id_INIT_L01] = Property(0b0100, 4); // AND inv D0
|
||||||
ci.params[id_INIT_L10] = Property(0b1110, 4); // OR
|
ci.params[id_INIT_L10] = Property(0b1110, 4); // OR
|
||||||
ci.renamePort(id_Y, id_OUT);
|
ci.renamePort(id_Y, id_OUT);
|
||||||
ci.type = id_CPE_HALF;
|
ci.type = id_CPE_L2T4;
|
||||||
} else {
|
} else {
|
||||||
ci.renamePort(id_I0, id_IN1);
|
ci.renamePort(id_I0, id_IN1);
|
||||||
ci.renamePort(id_I1, id_IN2);
|
ci.renamePort(id_I1, id_IN2);
|
||||||
ci.renamePort(id_I2, id_IN3);
|
ci.renamePort(id_I2, id_IN3);
|
||||||
ci.renamePort(id_I3, id_IN4);
|
ci.renamePort(id_I3, id_IN4);
|
||||||
ci.renamePort(id_O, id_OUT);
|
ci.renamePort(id_O, id_OUT);
|
||||||
ci.params[id_C_O] = Property(0b11, 2);
|
|
||||||
if (ci.type.in(id_CC_LUT1, id_CC_LUT2)) {
|
if (ci.type.in(id_CC_LUT1, id_CC_LUT2)) {
|
||||||
uint8_t val = int_or_default(ci.params, id_INIT, 0);
|
uint8_t val = int_or_default(ci.params, id_INIT, 0);
|
||||||
if (ci.type == id_CC_LUT1)
|
if (ci.type == id_CC_LUT1)
|
||||||
|
|
@ -184,38 +192,40 @@ void GateMatePacker::pack_cpe()
|
||||||
ci.unsetParam(id_INIT);
|
ci.unsetParam(id_INIT);
|
||||||
ci.params[id_INIT_L10] = Property(0b1010, 4);
|
ci.params[id_INIT_L10] = Property(0b1010, 4);
|
||||||
}
|
}
|
||||||
ci.type = id_CPE_HALF;
|
ci.type = id_CPE_L2T4;
|
||||||
}
|
}
|
||||||
NetInfo *o = ci.getPort(id_OUT);
|
NetInfo *o = ci.getPort(id_OUT);
|
||||||
if (o) {
|
if (o) {
|
||||||
CellInfo *dff = net_only_drives(ctx, o, is_dff, id_D, true);
|
CellInfo *dff = net_only_drives(ctx, o, is_dff, id_D, true);
|
||||||
if (dff) {
|
if (dff) {
|
||||||
if (dff->type == id_CC_DLT) {
|
dff->cluster = ci.name;
|
||||||
dff->movePortTo(id_G, &ci, id_G);
|
dff->constr_abs_z = false;
|
||||||
} else {
|
dff->constr_z = +2;
|
||||||
dff->movePortTo(id_EN, &ci, id_EN);
|
ci.cluster = ci.name;
|
||||||
dff->movePortTo(id_CLK, &ci, id_CLK);
|
ci.constr_children.push_back(dff);
|
||||||
}
|
dff->renamePort(id_D, id_DIN);
|
||||||
dff->movePortTo(id_SR, &ci, id_SR);
|
dff->renamePort(id_Q, id_DOUT);
|
||||||
dff->disconnectPort(id_D);
|
dff_to_cpe(dff);
|
||||||
ci.disconnectPort(id_OUT);
|
dff->type = (dff->type == id_CC_DLT) ? id_CPE_LATCH : id_CPE_FF;
|
||||||
dff->movePortTo(id_Q, &ci, id_OUT);
|
|
||||||
dff_to_cpe(dff, &ci);
|
|
||||||
packed_cells.insert(dff->name);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto ci : l2t5_list) {
|
for (auto ci : l2t5_list) {
|
||||||
CellInfo *upper = create_cell_ptr(id_CPE_HALF_U, ctx->idf("%s$upper", ci->name.c_str(ctx)));
|
CellInfo *upper = create_cell_ptr(id_CPE_L2T4, ctx->idf("%s$upper", ci->name.c_str(ctx)));
|
||||||
upper->cluster = ci->name;
|
upper->cluster = ci->name;
|
||||||
upper->constr_abs_z = false;
|
upper->constr_abs_z = true;
|
||||||
upper->constr_z = -1;
|
upper->constr_z = CPE_LT_U_Z;
|
||||||
ci->cluster = ci->name;
|
|
||||||
ci->movePortTo(id_I4, upper, id_IN1);
|
ci->movePortTo(id_I4, upper, id_IN1);
|
||||||
upper->params[id_INIT_L00] = Property(0b1010, 4);
|
upper->params[id_INIT_L00] = Property(0b1010, 4);
|
||||||
upper->params[id_INIT_L10] = Property(0b1010, 4);
|
upper->params[id_INIT_L10] = Property(0b1010, 4);
|
||||||
ci->constr_children.push_back(upper);
|
ci->constr_children.push_back(upper);
|
||||||
|
|
||||||
|
NetInfo *ci_out_conn = ctx->createNet(ctx->idf("%s$combin", ci->name.c_str(ctx)));
|
||||||
|
upper->connectPort(id_OUT, ci_out_conn);
|
||||||
|
ci->ports[id_COMBIN].name = id_COMBIN;
|
||||||
|
ci->ports[id_COMBIN].type = PORT_IN;
|
||||||
|
ci->connectPort(id_COMBIN, ci_out_conn);
|
||||||
}
|
}
|
||||||
l2t5_list.clear();
|
l2t5_list.clear();
|
||||||
|
|
||||||
|
|
@ -255,10 +265,9 @@ void GateMatePacker::pack_cpe()
|
||||||
ci.params[id_INIT_L03] = Property(0b1100, 4); // IN8
|
ci.params[id_INIT_L03] = Property(0b1100, 4); // IN8
|
||||||
ci.params[id_INIT_L11] = Property(invert, 4); // Inversion bits
|
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(0b1100, 4); // Always D1
|
||||||
ci.params[id_C_O] = Property(0b11, 2);
|
ci.type = id_CPE_LT_L;
|
||||||
ci.type = id_CPE_HALF_L;
|
|
||||||
|
|
||||||
CellInfo *upper = create_cell_ptr(id_CPE_HALF_U, ctx->idf("%s$upper", ci.name.c_str(ctx)));
|
CellInfo *upper = create_cell_ptr(id_CPE_LT_U, ctx->idf("%s$upper", ci.name.c_str(ctx)));
|
||||||
upper->cluster = ci.name;
|
upper->cluster = ci.name;
|
||||||
upper->constr_abs_z = false;
|
upper->constr_abs_z = false;
|
||||||
upper->constr_z = -1;
|
upper->constr_z = -1;
|
||||||
|
|
@ -273,26 +282,43 @@ void GateMatePacker::pack_cpe()
|
||||||
}
|
}
|
||||||
mux_list.clear();
|
mux_list.clear();
|
||||||
|
|
||||||
|
std::vector<CellInfo *> dff_list;
|
||||||
for (auto &cell : ctx->cells) {
|
for (auto &cell : ctx->cells) {
|
||||||
CellInfo &ci = *cell.second;
|
CellInfo &ci = *cell.second;
|
||||||
if (!ci.type.in(id_CC_DFF, id_CC_DLT))
|
if (!ci.type.in(id_CC_DFF, id_CC_DLT))
|
||||||
continue;
|
continue;
|
||||||
ci.renamePort(id_Q, id_OUT);
|
dff_list.push_back(&ci);
|
||||||
|
}
|
||||||
|
for (auto &cell : dff_list) {
|
||||||
|
CellInfo &ci = *cell;
|
||||||
|
CellInfo *lt = create_cell_ptr(id_CPE_L2T4, ctx->idf("%s$lt", ci.name.c_str(ctx)));
|
||||||
|
lt->cluster = ci.name;
|
||||||
|
lt->constr_abs_z = false;
|
||||||
|
lt->constr_z = -2;
|
||||||
|
ci.cluster = ci.name;
|
||||||
|
ci.constr_children.push_back(lt);
|
||||||
|
ci.renamePort(id_Q, id_DOUT);
|
||||||
NetInfo *d_net = ci.getPort(id_D);
|
NetInfo *d_net = ci.getPort(id_D);
|
||||||
if (d_net->name == ctx->id("$PACKER_GND")) {
|
if (d_net->name == ctx->id("$PACKER_GND")) {
|
||||||
ci.params[id_INIT_L00] = Property(0b0000, 4);
|
lt->params[id_INIT_L00] = Property(0b0000, 4);
|
||||||
ci.disconnectPort(id_D);
|
ci.disconnectPort(id_D);
|
||||||
} else if (d_net->name == ctx->id("$PACKER_VCC")) {
|
} else if (d_net->name == ctx->id("$PACKER_VCC")) {
|
||||||
ci.params[id_INIT_L00] = Property(0b1111, 4);
|
lt->params[id_INIT_L00] = Property(0b1111, 4);
|
||||||
ci.disconnectPort(id_D);
|
ci.disconnectPort(id_D);
|
||||||
} else {
|
} else {
|
||||||
ci.params[id_INIT_L00] = Property(0b1010, 4);
|
lt->params[id_INIT_L00] = Property(0b1010, 4);
|
||||||
}
|
}
|
||||||
ci.params[id_INIT_L10] = Property(0b1010, 4);
|
lt->params[id_INIT_L10] = Property(0b1010, 4);
|
||||||
ci.renamePort(id_D, id_IN1);
|
ci.movePortTo(id_D, lt, id_IN1);
|
||||||
dff_to_cpe(&ci, &ci);
|
dff_to_cpe(&ci);
|
||||||
ci.type = id_CPE_HALF;
|
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);
|
||||||
|
ci.ports[id_DIN].name = id_DIN;
|
||||||
|
ci.ports[id_DIN].type = PORT_IN;
|
||||||
|
ci.connectPort(id_DIN, conn);
|
||||||
}
|
}
|
||||||
|
dff_list.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool is_addf_ci(NetInfo *net)
|
static bool is_addf_ci(NetInfo *net)
|
||||||
|
|
@ -385,23 +411,33 @@ void GateMatePacker::pack_addf()
|
||||||
CellInfo *root = grp.front();
|
CellInfo *root = grp.front();
|
||||||
root->cluster = root->name;
|
root->cluster = root->name;
|
||||||
|
|
||||||
CellInfo *ci_upper = create_cell_ptr(id_CPE_HALF_U, ctx->idf("%s$ci_upper", root->name.c_str(ctx)));
|
CellInfo *ci_upper = create_cell_ptr(id_CPE_DUMMY, ctx->idf("%s$ci_upper", root->name.c_str(ctx)));
|
||||||
root->constr_children.push_back(ci_upper);
|
root->constr_children.push_back(ci_upper);
|
||||||
ci_upper->cluster = root->name;
|
ci_upper->cluster = root->name;
|
||||||
ci_upper->constr_abs_z = false;
|
ci_upper->constr_abs_z = false;
|
||||||
ci_upper->constr_z = -1;
|
ci_upper->constr_z = -1;
|
||||||
ci_upper->constr_y = -1;
|
ci_upper->constr_y = -1;
|
||||||
|
|
||||||
CellInfo *ci_lower = create_cell_ptr(id_CPE_HALF_L, ctx->idf("%s$ci_lower", root->name.c_str(ctx)));
|
CellInfo *ci_lower = create_cell_ptr(id_CPE_L2T4, ctx->idf("%s$ci", root->name.c_str(ctx)));
|
||||||
root->constr_children.push_back(ci_lower);
|
root->constr_children.push_back(ci_lower);
|
||||||
ci_lower->cluster = root->name;
|
ci_lower->cluster = root->name;
|
||||||
ci_lower->constr_abs_z = false;
|
ci_lower->constr_abs_z = false;
|
||||||
ci_lower->constr_y = -1;
|
ci_lower->constr_y = -1;
|
||||||
ci_lower->params[id_C_O] = Property(0b11, 2);
|
ci_lower->params[id_INIT_L00] = Property(0b0000, 4); // zero
|
||||||
ci_lower->params[id_C_SELY1] = Property(1, 1);
|
|
||||||
ci_lower->params[id_C_CY1_I] = Property(1, 1);
|
|
||||||
ci_lower->params[id_INIT_L10] = Property(0b1010, 4); // D0
|
ci_lower->params[id_INIT_L10] = Property(0b1010, 4); // D0
|
||||||
|
|
||||||
|
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);
|
||||||
|
ci_cplines->params[id_C_CY1_I] = Property(1, 1);
|
||||||
|
root->constr_children.push_back(ci_cplines);
|
||||||
|
ci_cplines->cluster = root->name;
|
||||||
|
ci_cplines->constr_abs_z = true;
|
||||||
|
ci_cplines->constr_y = -1;
|
||||||
|
ci_cplines->constr_z = CPE_CPLINES_Z;
|
||||||
|
NetInfo *ci_out_conn = ctx->createNet(ctx->idf("%s$out", ci_lower->name.c_str(ctx)));
|
||||||
|
ci_lower->connectPort(id_OUT, ci_out_conn);
|
||||||
|
ci_cplines->connectPort(id_OUT1, ci_out_conn);
|
||||||
|
|
||||||
NetInfo *ci_net = root->getPort(id_CI);
|
NetInfo *ci_net = root->getPort(id_CI);
|
||||||
if (ci_net->name == ctx->id("$PACKER_GND")) {
|
if (ci_net->name == ctx->id("$PACKER_GND")) {
|
||||||
ci_lower->params[id_INIT_L00] = Property(0b0000, 4);
|
ci_lower->params[id_INIT_L00] = Property(0b0000, 4);
|
||||||
|
|
@ -414,8 +450,8 @@ void GateMatePacker::pack_addf()
|
||||||
ci_lower->params[id_INIT_L00] = Property(0b1010, 4); // IN5
|
ci_lower->params[id_INIT_L00] = Property(0b1010, 4); // IN5
|
||||||
}
|
}
|
||||||
|
|
||||||
NetInfo *ci_conn = ctx->createNet(ctx->idf("%s$ci", root->name.c_str(ctx)));
|
NetInfo *ci_conn = ctx->createNet(ctx->idf("%s$ci_net", root->name.c_str(ctx)));
|
||||||
ci_lower->connectPort(id_COUTY1, ci_conn);
|
ci_cplines->connectPort(id_COUTY1, ci_conn);
|
||||||
|
|
||||||
root->ports[id_CINY1].name = id_CINY1;
|
root->ports[id_CINY1].name = id_CINY1;
|
||||||
root->ports[id_CINY1].type = PORT_IN;
|
root->ports[id_CINY1].type = PORT_IN;
|
||||||
|
|
@ -464,10 +500,9 @@ void GateMatePacker::pack_addf()
|
||||||
cy->params[id_INIT_L20] = Property(0b0110, 4); // XOR
|
cy->params[id_INIT_L20] = Property(0b0110, 4); // XOR
|
||||||
}
|
}
|
||||||
cy->params[id_C_FUNCTION] = Property(merged ? C_ADDF2 : C_ADDF, 3);
|
cy->params[id_C_FUNCTION] = Property(merged ? C_ADDF2 : C_ADDF, 3);
|
||||||
cy->params[id_C_O] = Property(0b11, 2);
|
cy->type = id_CPE_LT_L;
|
||||||
cy->type = id_CPE_HALF_L;
|
|
||||||
|
|
||||||
CellInfo *upper = create_cell_ptr(id_CPE_HALF_U, ctx->idf("%s$upper", cy->name.c_str(ctx)));
|
CellInfo *upper = create_cell_ptr(id_CPE_LT_U, ctx->idf("%s$upper", cy->name.c_str(ctx)));
|
||||||
upper->cluster = root->name;
|
upper->cluster = root->name;
|
||||||
root->constr_children.push_back(upper);
|
root->constr_children.push_back(upper);
|
||||||
upper->constr_abs_z = false;
|
upper->constr_abs_z = false;
|
||||||
|
|
@ -475,7 +510,6 @@ void GateMatePacker::pack_addf()
|
||||||
upper->constr_z = -1;
|
upper->constr_z = -1;
|
||||||
if (merged) {
|
if (merged) {
|
||||||
cy->movePortTo(id_S, upper, id_OUT);
|
cy->movePortTo(id_S, upper, id_OUT);
|
||||||
upper->params[id_C_O] = Property(0b11, 2);
|
|
||||||
} else {
|
} else {
|
||||||
cy->renamePort(id_S, id_OUT);
|
cy->renamePort(id_S, id_OUT);
|
||||||
}
|
}
|
||||||
|
|
@ -509,26 +543,23 @@ void GateMatePacker::pack_addf()
|
||||||
if (i == grp.size() - 1) {
|
if (i == grp.size() - 1) {
|
||||||
if (!cy->getPort(id_CO))
|
if (!cy->getPort(id_CO))
|
||||||
break;
|
break;
|
||||||
CellInfo *co_upper = create_cell_ptr(id_CPE_HALF_U, ctx->idf("%s$co_upper", cy->name.c_str(ctx)));
|
CellInfo *co_upper = create_cell_ptr(id_CPE_DUMMY, ctx->idf("%s$co_upper", cy->name.c_str(ctx)));
|
||||||
co_upper->cluster = root->name;
|
co_upper->cluster = root->name;
|
||||||
root->constr_children.push_back(co_upper);
|
root->constr_children.push_back(co_upper);
|
||||||
co_upper->constr_abs_z = false;
|
co_upper->constr_abs_z = false;
|
||||||
co_upper->constr_z = -1;
|
co_upper->constr_z = -1;
|
||||||
co_upper->constr_y = +i + 1;
|
co_upper->constr_y = +i + 1;
|
||||||
CellInfo *co_lower = create_cell_ptr(id_CPE_HALF_L, ctx->idf("%s$co_lower", cy->name.c_str(ctx)));
|
CellInfo *co_lower = create_cell_ptr(id_CPE_L2T4, ctx->idf("%s$co", cy->name.c_str(ctx)));
|
||||||
co_lower->cluster = root->name;
|
co_lower->cluster = root->name;
|
||||||
root->constr_children.push_back(co_lower);
|
root->constr_children.push_back(co_lower);
|
||||||
co_lower->constr_abs_z = false;
|
co_lower->constr_abs_z = false;
|
||||||
co_lower->constr_y = +i + 1;
|
co_lower->constr_y = +i + 1;
|
||||||
co_lower->params[id_C_O] = Property(0b11, 2);
|
|
||||||
co_lower->params[id_C_FUNCTION] = Property(C_EN_CIN, 3);
|
co_lower->params[id_C_FUNCTION] = Property(C_EN_CIN, 3);
|
||||||
co_lower->params[id_INIT_L11] = Property(0b1100, 4);
|
co_lower->params[id_INIT_L10] = Property(0b1100, 4);
|
||||||
co_lower->params[id_INIT_L20] = Property(0b1100, 4);
|
co_lower->params[id_INIT_L20] = Property(0b1100, 4);
|
||||||
|
|
||||||
NetInfo *co_conn = ctx->createNet(ctx->idf("%s$co", cy->name.c_str(ctx)));
|
NetInfo *co_conn = ctx->createNet(ctx->idf("%s$co_net", cy->name.c_str(ctx)));
|
||||||
|
|
||||||
co_lower->ports[id_CINY1].name = id_CINY1;
|
|
||||||
co_lower->ports[id_CINY1].type = PORT_IN;
|
|
||||||
co_lower->connectPort(id_CINY1, co_conn);
|
co_lower->connectPort(id_CINY1, co_conn);
|
||||||
cy->ports[id_COUTY1].name = id_COUTY1;
|
cy->ports[id_COUTY1].name = id_COUTY1;
|
||||||
cy->ports[id_COUTY1].type = PORT_OUT;
|
cy->ports[id_COUTY1].type = PORT_OUT;
|
||||||
|
|
@ -543,7 +574,7 @@ void GateMatePacker::pack_addf()
|
||||||
for (auto &usr : co_net->users) {
|
for (auto &usr : co_net->users) {
|
||||||
if (usr.cell->type == id_CC_ADDF || usr.port == id_CI) {
|
if (usr.cell->type == id_CC_ADDF || usr.port == id_CI) {
|
||||||
usr.cell->disconnectPort(id_CI);
|
usr.cell->disconnectPort(id_CI);
|
||||||
NetInfo *co_conn = ctx->createNet(ctx->idf("%s$co", cy->name.c_str(ctx)));
|
NetInfo *co_conn = ctx->createNet(ctx->idf("%s$co_net", cy->name.c_str(ctx)));
|
||||||
cy->ports[id_COUTY1].name = id_COUTY1;
|
cy->ports[id_COUTY1].name = id_COUTY1;
|
||||||
cy->ports[id_COUTY1].type = PORT_OUT;
|
cy->ports[id_COUTY1].type = PORT_OUT;
|
||||||
cy->connectPort(id_COUTY1, co_conn);
|
cy->connectPort(id_COUTY1, co_conn);
|
||||||
|
|
@ -551,8 +582,7 @@ void GateMatePacker::pack_addf()
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
upper->params[id_C_O] = Property(0b10, 2);
|
cy->movePortTo(id_CO, upper, id_CPOUT);
|
||||||
cy->movePortTo(id_CO, upper, id_OUT);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -563,10 +593,10 @@ void GateMatePacker::pack_constants()
|
||||||
{
|
{
|
||||||
log_info("Packing constants..\n");
|
log_info("Packing constants..\n");
|
||||||
// Replace constants with LUTs
|
// Replace constants with LUTs
|
||||||
const dict<IdString, Property> vcc_params = {{id_INIT_L10, Property(0b1111, 4)}, {id_C_O, Property(0b11, 2)}};
|
const dict<IdString, Property> vcc_params = {{id_INIT_L10, Property(0b1111, 4)}};
|
||||||
const dict<IdString, Property> gnd_params = {{id_INIT_L10, Property(0b0000, 4)}, {id_C_O, Property(0b11, 2)}};
|
const dict<IdString, Property> gnd_params = {{id_INIT_L10, Property(0b0000, 4)}};
|
||||||
|
|
||||||
h.replace_constants(CellTypePort(id_CPE_HALF, id_OUT), CellTypePort(id_CPE_HALF, id_OUT), vcc_params, gnd_params);
|
h.replace_constants(CellTypePort(id_CPE_L2T4, id_OUT), CellTypePort(id_CPE_L2T4, id_OUT), vcc_params, gnd_params);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GateMatePacker::remove_constants()
|
void GateMatePacker::remove_constants()
|
||||||
|
|
|
||||||
|
|
@ -326,8 +326,8 @@ void GateMatePacker::pack_io_sel()
|
||||||
cells.push_back(&ci);
|
cells.push_back(&ci);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::array<CellInfo *, 9>> ddr(uarch->dies); // for each bank
|
std::vector<std::array<std::pair<CellInfo *, CellInfo *>, 9>> ddr(
|
||||||
|
uarch->dies, std::array<std::pair<CellInfo *, CellInfo *>, 9>{});
|
||||||
auto set_out_clk = [&](CellInfo *cell, CellInfo *target) -> bool {
|
auto set_out_clk = [&](CellInfo *cell, CellInfo *target) -> bool {
|
||||||
NetInfo *clk_net = cell->getPort(id_CLK);
|
NetInfo *clk_net = cell->getPort(id_CLK);
|
||||||
if (clk_net) {
|
if (clk_net) {
|
||||||
|
|
@ -477,21 +477,18 @@ void GateMatePacker::pack_io_sel()
|
||||||
oddr->movePortTo(id_D1, &ci, id_OUT1);
|
oddr->movePortTo(id_D1, &ci, id_OUT1);
|
||||||
const auto &pad = ctx->get_package_pin(ctx->id(loc));
|
const auto &pad = ctx->get_package_pin(ctx->id(loc));
|
||||||
int die = uarch->tile_extra_data(ci.bel.tile)->die;
|
int die = uarch->tile_extra_data(ci.bel.tile)->die;
|
||||||
CellInfo *cpe_half = ddr[die][pad->pad_bank];
|
auto [cpe_half, cpe_ramio] = ddr[die][pad->pad_bank];
|
||||||
if (cpe_half) {
|
if (cpe_half) {
|
||||||
if (cpe_half->getPort(id_IN1) != oddr->getPort(id_DDR))
|
if (cpe_half->getPort(id_IN1) != oddr->getPort(id_DDR))
|
||||||
log_error("DDR port use signal different than already occupied DDR source.\n");
|
log_error("DDR port use signal different than already occupied DDR source.\n");
|
||||||
ci.ports[id_DDR].name = id_DDR;
|
ci.ports[id_DDR].name = id_DDR;
|
||||||
ci.ports[id_DDR].type = PORT_IN;
|
ci.ports[id_DDR].type = PORT_IN;
|
||||||
ci.connectPort(id_DDR, cpe_half->getPort(id_RAM_O));
|
ci.connectPort(id_DDR, cpe_ramio->getPort(id_RAM_O));
|
||||||
} else {
|
} else {
|
||||||
oddr->movePortTo(id_DDR, &ci, id_DDR);
|
|
||||||
cpe_half = move_ram_o(&ci, id_DDR, false);
|
|
||||||
uarch->ddr_nets.insert(cpe_half->getPort(id_IN1)->name);
|
|
||||||
auto l = reinterpret_cast<const GateMatePadExtraDataPOD *>(pad->extra_data.get());
|
auto l = reinterpret_cast<const GateMatePadExtraDataPOD *>(pad->extra_data.get());
|
||||||
ctx->bindBel(ctx->getBelByLocation(Loc(l->x, l->y, l->z)), cpe_half,
|
oddr->movePortTo(id_DDR, &ci, id_DDR);
|
||||||
PlaceStrength::STRENGTH_FIXED);
|
ddr[die][pad->pad_bank] = move_ram_o(&ci, id_DDR, false, Loc(l->x, l->y, l->z));
|
||||||
ddr[die][pad->pad_bank] = cpe_half;
|
uarch->ddr_nets.insert(ddr[die][pad->pad_bank].first->getPort(id_IN1)->name);
|
||||||
}
|
}
|
||||||
use_custom_clock = set_out_clk(oddr, &ci);
|
use_custom_clock = set_out_clk(oddr, &ci);
|
||||||
bool invert = bool_or_default(oddr->params, id_CLK_INV, 0);
|
bool invert = bool_or_default(oddr->params, id_CLK_INV, 0);
|
||||||
|
|
@ -523,7 +520,7 @@ void GateMatePacker::pack_io_sel()
|
||||||
|
|
||||||
Loc root_loc = ctx->getBelLocation(ci.bel);
|
Loc root_loc = ctx->getBelLocation(ci.bel);
|
||||||
for (int i = 0; i < 4; i++) {
|
for (int i = 0; i < 4; i++) {
|
||||||
CellInfo *cpe = move_ram_o_fixed(&ci, ctx->idf("OUT%d", i + 1), root_loc);
|
CellInfo *cpe = move_ram_o_fixed(&ci, ctx->idf("OUT%d", i + 1), root_loc).first;
|
||||||
if (cpe && i == 2)
|
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(0b0101, 4); // Invert CPE out for output enable (OUT3)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,942 @@
|
||||||
|
/*
|
||||||
|
* nextpnr -- Next Generation Place and Route
|
||||||
|
*
|
||||||
|
* Copyright (C) 2024 The Project Peppercorn Authors.
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "idstring.h"
|
||||||
|
#include "log.h"
|
||||||
|
#include "nextpnr_assertions.h"
|
||||||
|
#include "nextpnr_namespaces.h"
|
||||||
|
#include "nextpnr_types.h"
|
||||||
|
#include "pack.h"
|
||||||
|
#include "property.h"
|
||||||
|
#include "uarch/gatemate/extra_data.h"
|
||||||
|
#include "uarch/gatemate/gatemate.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
#define HIMBAECHEL_CONSTIDS "uarch/gatemate/constids.inc"
|
||||||
|
#include "himbaechel_constids.h"
|
||||||
|
|
||||||
|
NEXTPNR_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
// Constant zero.
|
||||||
|
struct ZeroDriver
|
||||||
|
{
|
||||||
|
ZeroDriver() : lower{nullptr}, upper{nullptr} {}
|
||||||
|
ZeroDriver(CellInfo *lower, CellInfo *upper, IdString name);
|
||||||
|
|
||||||
|
CellInfo *lower;
|
||||||
|
CellInfo *upper;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Propagate A0 through OUT1 and A1 through OUT2; zero COUTX and POUTX.
|
||||||
|
struct APassThroughCell
|
||||||
|
{
|
||||||
|
APassThroughCell(CellInfo *lower, CellInfo *upper, CellInfo *comp, CellInfo *cplines, IdString name);
|
||||||
|
|
||||||
|
void clean_up_cell(Context *ctx, CellInfo *cell);
|
||||||
|
|
||||||
|
CellInfo *lower;
|
||||||
|
CellInfo *upper;
|
||||||
|
CellInfo *comp;
|
||||||
|
CellInfo *cplines;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Propagate B0 through POUTY1 and B1 through COUTY1
|
||||||
|
struct BPassThroughCell
|
||||||
|
{
|
||||||
|
BPassThroughCell() : lower{nullptr}, upper{nullptr}, cplines{nullptr} {}
|
||||||
|
BPassThroughCell(CellInfo *lower, CellInfo *upper, CellInfo *cplines, IdString name);
|
||||||
|
|
||||||
|
void clean_up_cell(Context *ctx, CellInfo *cell);
|
||||||
|
|
||||||
|
CellInfo *lower;
|
||||||
|
CellInfo *upper;
|
||||||
|
CellInfo *cplines;
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: Micko points out this is an L2T4 CPE_HALF
|
||||||
|
struct CarryGenCell
|
||||||
|
{
|
||||||
|
CarryGenCell() : lower{nullptr}, upper{nullptr}, comp{nullptr}, cplines{nullptr} {}
|
||||||
|
CarryGenCell(CellInfo *lower, CellInfo *upper, CellInfo *comp, CellInfo *cplines, IdString name, bool is_odd_x,
|
||||||
|
bool enable_cinx);
|
||||||
|
|
||||||
|
CellInfo *lower;
|
||||||
|
CellInfo *upper;
|
||||||
|
CellInfo *comp;
|
||||||
|
CellInfo *cplines;
|
||||||
|
};
|
||||||
|
|
||||||
|
// This prepares B bits for multiplication.
|
||||||
|
// CITE: CPE_MULTFab.pdf
|
||||||
|
struct MultfabCell
|
||||||
|
{
|
||||||
|
MultfabCell() : lower{nullptr}, upper{nullptr}, comp{nullptr}, cplines{nullptr} {}
|
||||||
|
MultfabCell(CellInfo *lower, CellInfo *upper, CellInfo *comp, CellInfo *cplines, IdString name, bool is_even_x,
|
||||||
|
bool enable_cinx);
|
||||||
|
|
||||||
|
CellInfo *lower;
|
||||||
|
CellInfo *upper;
|
||||||
|
CellInfo *comp;
|
||||||
|
CellInfo *cplines;
|
||||||
|
};
|
||||||
|
|
||||||
|
// CITE: CPE_ges_f-routing1.pdf for !is_even_x; CPE_ges_f-routing2 for is_even_x
|
||||||
|
struct FRoutingCell
|
||||||
|
{
|
||||||
|
FRoutingCell() : lower{nullptr}, upper{nullptr}, comp{nullptr}, cplines{nullptr} {}
|
||||||
|
FRoutingCell(CellInfo *lower, CellInfo *upper, CellInfo *comp, CellInfo *cplines, IdString name, bool is_even_x);
|
||||||
|
|
||||||
|
CellInfo *lower;
|
||||||
|
CellInfo *upper;
|
||||||
|
CellInfo *comp;
|
||||||
|
CellInfo *cplines;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Multiply two bits of A with two bits of B.
|
||||||
|
//
|
||||||
|
// CITE: CPE_MULT.pdf
|
||||||
|
struct MultCell
|
||||||
|
{
|
||||||
|
MultCell() : lower{nullptr}, upper{nullptr} {}
|
||||||
|
MultCell(CellInfo *lower, CellInfo *upper, IdString name, bool is_msb);
|
||||||
|
|
||||||
|
CellInfo *lower;
|
||||||
|
CellInfo *upper;
|
||||||
|
};
|
||||||
|
|
||||||
|
// CITE: CPE_ges_MSB-routing.pdf
|
||||||
|
struct MsbRoutingCell
|
||||||
|
{
|
||||||
|
MsbRoutingCell() : lower{nullptr}, upper{nullptr}, comp{nullptr}, cplines{nullptr} {}
|
||||||
|
MsbRoutingCell(CellInfo *lower, CellInfo *upper, CellInfo *comp, CellInfo *cplines, IdString name);
|
||||||
|
|
||||||
|
CellInfo *lower;
|
||||||
|
CellInfo *upper;
|
||||||
|
CellInfo *comp;
|
||||||
|
CellInfo *cplines;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct MultiplierColumn
|
||||||
|
{
|
||||||
|
BPassThroughCell b_passthru;
|
||||||
|
CarryGenCell carry;
|
||||||
|
MultfabCell multfab;
|
||||||
|
FRoutingCell f_route;
|
||||||
|
std::vector<MultCell> mults;
|
||||||
|
MsbRoutingCell msb_route;
|
||||||
|
};
|
||||||
|
|
||||||
|
// A GateMate multiplier is made up of columns of 2x2 multipliers.
|
||||||
|
struct Multiplier
|
||||||
|
{
|
||||||
|
ZeroDriver zero;
|
||||||
|
std::vector<APassThroughCell> a_passthrus;
|
||||||
|
std::vector<MultiplierColumn> cols;
|
||||||
|
|
||||||
|
size_t cpe_count() const
|
||||||
|
{
|
||||||
|
auto count = 1 /* (zero driver) */ + a_passthrus.size();
|
||||||
|
for (const auto &col : cols) {
|
||||||
|
count += 4 /* (b_passthru, carry, multfab, f_route) */ + col.mults.size() + 1 /* (msb_route) */;
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ZeroDriver::ZeroDriver(CellInfo *lower, CellInfo *upper, IdString name) : lower{lower}, upper{upper}
|
||||||
|
{
|
||||||
|
upper->params[id_INIT_L00] = Property(LUT_ZERO, 4); // (unused)
|
||||||
|
upper->params[id_INIT_L01] = Property(LUT_ZERO, 4); // (unused)
|
||||||
|
upper->params[id_INIT_L10] = Property(LUT_ZERO, 4); // (unused)
|
||||||
|
}
|
||||||
|
|
||||||
|
APassThroughCell::APassThroughCell(CellInfo *lower, CellInfo *upper, CellInfo *comp, CellInfo *cplines, IdString name)
|
||||||
|
: lower{lower}, upper{upper}, comp{comp}, cplines{cplines}
|
||||||
|
{
|
||||||
|
lower->params[id_INIT_L00] = Property(LUT_D0, 4); // IN5
|
||||||
|
lower->params[id_INIT_L01] = Property(LUT_ZERO, 4); // (unused)
|
||||||
|
lower->params[id_INIT_L10] = Property(LUT_D0, 4); // L02
|
||||||
|
|
||||||
|
comp->params[id_INIT_L30] = Property(LUT_ONE, 4); // zero -> COMP_OUT (L30 is inverted)
|
||||||
|
|
||||||
|
upper->params[id_INIT_L00] = Property(LUT_D0, 4); // IN1
|
||||||
|
upper->params[id_INIT_L01] = Property(LUT_ZERO, 4); // (unused)
|
||||||
|
upper->params[id_INIT_L10] = Property(LUT_D0, 4); // L00 -> COMB2OUT
|
||||||
|
|
||||||
|
cplines->params[id_C_SEL_C] = Property(1, 1); // COMP_OUT -> CX_VAL
|
||||||
|
cplines->params[id_C_SEL_P] = Property(1, 1); // COMP_OUT -> PX_VAL
|
||||||
|
cplines->params[id_C_CX_I] = Property(1, 1); // CX_VAL -> COUTX
|
||||||
|
cplines->params[id_C_PX_I] = Property(1, 1); // PX_VAL -> POUTX
|
||||||
|
}
|
||||||
|
|
||||||
|
void APassThroughCell::clean_up_cell(Context *ctx, CellInfo *cell)
|
||||||
|
{
|
||||||
|
auto *net = cell->ports.at(id_IN1).net;
|
||||||
|
|
||||||
|
NPNR_ASSERT(net != nullptr);
|
||||||
|
|
||||||
|
bool net_is_gnd = net->name == ctx->idf("$PACKER_GND");
|
||||||
|
bool net_is_vcc = net->name == ctx->idf("$PACKER_VCC");
|
||||||
|
if (net_is_gnd || net_is_vcc) {
|
||||||
|
cell->params[id_INIT_L00] = Property(LUT_ZERO, 4);
|
||||||
|
cell->params[id_INIT_L10] = Property(net_is_vcc ? LUT_ONE : LUT_ZERO, 4);
|
||||||
|
cell->disconnectPort(id_IN1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// B0 -> POUTY1; B1 -> COUTY1
|
||||||
|
BPassThroughCell::BPassThroughCell(CellInfo *lower, CellInfo *upper, CellInfo *cplines, IdString name)
|
||||||
|
: lower{lower}, upper{upper}, cplines{cplines}
|
||||||
|
{
|
||||||
|
lower->params[id_INIT_L00] = Property(LUT_D0, 4); // IN5
|
||||||
|
lower->params[id_INIT_L01] = Property(LUT_ZERO, 4); // (unused)
|
||||||
|
lower->params[id_INIT_L10] = Property(LUT_D0, 4); // L02
|
||||||
|
|
||||||
|
upper->params[id_INIT_L00] = Property(LUT_D0, 4); // IN1
|
||||||
|
upper->params[id_INIT_L01] = Property(LUT_ZERO, 4); // (unused)
|
||||||
|
upper->params[id_INIT_L10] = Property(LUT_D0, 4); // L00 -> COMB2OUT
|
||||||
|
|
||||||
|
cplines->params[id_C_SEL_C] = Property(0, 1); // COMB2OUT -> CY1_VAL
|
||||||
|
cplines->params[id_C_SEL_P] = Property(0, 1); // COMB1OUT -> PY1_VAL
|
||||||
|
cplines->params[id_C_SELY1] = Property(0, 1); // COMB1OUT -> PY1_VAL; COMB2OUT -> CY1_VAL
|
||||||
|
cplines->params[id_C_CY1_I] = Property(1, 1); // CY1_VAL -> COUTY1
|
||||||
|
cplines->params[id_C_PY1_I] = Property(1, 1); // PY1_VAL -> POUTY1
|
||||||
|
}
|
||||||
|
|
||||||
|
void BPassThroughCell::clean_up_cell(Context *ctx, CellInfo *cell)
|
||||||
|
{
|
||||||
|
auto *net = cell->ports.at(id_IN1).net;
|
||||||
|
|
||||||
|
NPNR_ASSERT(net != nullptr);
|
||||||
|
|
||||||
|
bool net_is_gnd = net->name == ctx->idf("$PACKER_GND");
|
||||||
|
bool net_is_vcc = net->name == ctx->idf("$PACKER_VCC");
|
||||||
|
if (net_is_gnd || net_is_vcc) {
|
||||||
|
cell->params[id_INIT_L00] = Property(LUT_ZERO, 4);
|
||||||
|
cell->params[id_INIT_L10] = Property(net_is_vcc ? LUT_ONE : LUT_ZERO, 4);
|
||||||
|
cell->disconnectPort(id_IN1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CarryGenCell::CarryGenCell(CellInfo *lower, CellInfo *upper, CellInfo *comp, CellInfo *cplines, IdString name,
|
||||||
|
bool is_odd_x, bool enable_cinx)
|
||||||
|
: lower{lower}, upper{upper}, comp{comp}, cplines{cplines}
|
||||||
|
{
|
||||||
|
lower->params[id_INIT_L00] = Property(LUT_D1, 4); // PINY1
|
||||||
|
lower->params[id_INIT_L01] = Property(LUT_ZERO, 4); // (overriden by CIN)
|
||||||
|
lower->params[id_INIT_L10] = Property(is_odd_x ? LUT_OR : LUT_ZERO, 4);
|
||||||
|
lower->params[id_INIT_L20] = Property(is_odd_x ? LUT_OR : LUT_ZERO, 4);
|
||||||
|
lower->params[id_C_FUNCTION] = Property(C_EN_CIN, 3);
|
||||||
|
lower->params[id_C_I3] = Property(1, 1); // PINY1 for L02
|
||||||
|
lower->params[id_C_HORIZ] = Property(0, 1); // CINY1 for CIN_ for L03
|
||||||
|
|
||||||
|
upper->params[id_INIT_L00] = Property(LUT_ZERO, 4); // (unused)
|
||||||
|
upper->params[id_INIT_L01] = Property(enable_cinx ? LUT_D1 : LUT_ZERO, 4); // CINX
|
||||||
|
upper->params[id_INIT_L10] = Property(LUT_D1, 4);
|
||||||
|
if (enable_cinx)
|
||||||
|
upper->params[id_C_I2] = Property(1, 1); // CINX for L01
|
||||||
|
|
||||||
|
comp->params[id_INIT_L30] = Property(LUT_INV_D0, 4); // OUT1 -> COMP_OUT
|
||||||
|
|
||||||
|
cplines->params[id_C_PY1_I] = Property(0, 1); // PINY1 -> POUTY1
|
||||||
|
cplines->params[id_C_CY1_I] = Property(0, 1); // CINY1 -> COUTY1
|
||||||
|
cplines->params[id_C_CY2_I] = Property(1, 1); // CY2_VAL -> COUTY2
|
||||||
|
cplines->params[id_C_SEL_C] = Property(1, 1); // COMP_OUT -> CY2_VAL
|
||||||
|
cplines->params[id_C_SELY2] = Property(0, 1); // COMP_OUT -> CY2_VAL
|
||||||
|
|
||||||
|
// upper->params[id_C_O1] = Property(0b11, 2); // COMB1OUT -> OUT1
|
||||||
|
}
|
||||||
|
|
||||||
|
MultfabCell::MultfabCell(CellInfo *lower, CellInfo *upper, CellInfo *comp, CellInfo *cplines, IdString name,
|
||||||
|
bool is_even_x, bool enable_cinx)
|
||||||
|
: lower{lower}, upper{upper}, comp{comp}, cplines{cplines}
|
||||||
|
{
|
||||||
|
// TODO: perhaps C_I[1234] could be pips?
|
||||||
|
|
||||||
|
lower->params[id_INIT_L00] = Property(LUT_D1, 4); // PINY1
|
||||||
|
// lower->params[id_INIT_L01] = Property(LUT_ZERO, 4); // (unused)
|
||||||
|
lower->params[id_INIT_L10] = Property(LUT_D0, 4); // L02
|
||||||
|
lower->params[id_INIT_L20] = Property(is_even_x ? LUT_AND_INV_D0 : LUT_OR, 4); // L10 AND L11 -> OUT1
|
||||||
|
lower->params[id_C_FUNCTION] = Property(C_ADDCIN, 3);
|
||||||
|
lower->params[id_C_HORIZ] = Property(0, 1); // CINY1 for CIN_ for L20
|
||||||
|
|
||||||
|
comp->params[id_INIT_L30] = Property(LUT_INV_D1, 4); // L10 -> COMP_OUT
|
||||||
|
|
||||||
|
upper->params[id_INIT_L00] = Property(LUT_D1, 4); // PINY1
|
||||||
|
upper->params[id_INIT_L01] = Property(enable_cinx ? LUT_D1 : LUT_ZERO, 4); // CINX
|
||||||
|
upper->params[id_INIT_L10] = Property(LUT_XOR, 4); // XOR
|
||||||
|
|
||||||
|
upper->params[id_C_I1] = Property(1, 1); // PINY1 for L00
|
||||||
|
if (enable_cinx)
|
||||||
|
upper->params[id_C_I2] = Property(1, 1); // CINX for L01
|
||||||
|
lower->params[id_C_I3] = Property(1, 1); // PINY1 for L02
|
||||||
|
// upper->params[id_C_FUNCTION] = Property(C_ADDCIN, 3);
|
||||||
|
|
||||||
|
cplines->params[id_C_SELX] = Property(1, 1); // inverted CINY2 -> CX_VAL
|
||||||
|
cplines->params[id_C_SEL_C] = Property(1, 1); // inverted CINY2 -> CX_VAL; COMP_OUT -> CY1_VAL
|
||||||
|
cplines->params[id_C_Y12] = Property(1, 1); // inverted CINY2 -> CX_VAL
|
||||||
|
cplines->params[id_C_CX_I] = Property(1, 1); // CX_VAL -> COUTX
|
||||||
|
cplines->params[id_C_CY1_I] = Property(1, 1); // CY1_VAL -> COUTY1
|
||||||
|
cplines->params[id_C_PY1_I] = Property(1, 1); // PY1_VAL -> POUTY1
|
||||||
|
cplines->params[id_C_SEL_P] = Property(0, 1); // OUT1 -> PY1_VAL
|
||||||
|
cplines->params[id_C_SELY1] = Property(0, 1); // COMP_OUT -> CY1_VAL; OUT1 -> PY1_VAL
|
||||||
|
|
||||||
|
// upper->params[id_C_O1] = Property(0b11, 2); // COMB1OUT -> OUT1
|
||||||
|
}
|
||||||
|
|
||||||
|
FRoutingCell::FRoutingCell(CellInfo *lower, CellInfo *upper, CellInfo *comp, CellInfo *cplines, IdString name,
|
||||||
|
bool is_even_x)
|
||||||
|
: lower{lower}, upper{upper}, comp{comp}, cplines{cplines}
|
||||||
|
{
|
||||||
|
// TODO: simplify AND with zero/OR with zero into something more sensical.
|
||||||
|
|
||||||
|
lower->params[id_INIT_L00] = Property(LUT_ZERO, 4); // (unused)
|
||||||
|
lower->params[id_INIT_L01] = Property(LUT_ZERO, 4); // (unused)
|
||||||
|
lower->params[id_INIT_L10] = Property(LUT_ZERO, 4);
|
||||||
|
lower->params[id_INIT_L20] = Property(LUT_D1, 4);
|
||||||
|
lower->params[id_C_FUNCTION] = Property(C_ADDCIN, 3);
|
||||||
|
lower->params[id_C_HORIZ] = Property(0, 1); // CINY1 for CIN_ for L20
|
||||||
|
|
||||||
|
comp->params[id_INIT_L30] = Property(is_even_x ? LUT_ONE : LUT_INV_D1, 4); // L10 -> COMP_OUT
|
||||||
|
|
||||||
|
upper->params[id_INIT_L00] = Property(LUT_D1, 4); // PINY1
|
||||||
|
// upper->params[id_INIT_L01] = Property(LUT_ONE, 4); // (unused)
|
||||||
|
upper->params[id_INIT_L10] = Property(LUT_D0, 4);
|
||||||
|
upper->params[id_C_I1] = Property(1, 1); // PINY1 for L00
|
||||||
|
// upper->params[id_C_FUNCTION] = Property(C_ADDCIN, 3);
|
||||||
|
|
||||||
|
cplines->params[id_C_SELX] = Property(1, 1);
|
||||||
|
cplines->params[id_C_SEL_C] = Property(1, 1);
|
||||||
|
cplines->params[id_C_Y12] = Property(1, 1);
|
||||||
|
cplines->params[id_C_CX_I] = Property(1, 1);
|
||||||
|
cplines->params[id_C_CY1_I] = Property(is_even_x, 1);
|
||||||
|
cplines->params[id_C_CY2_I] = Property(1, 1);
|
||||||
|
cplines->params[id_C_PY1_I] = Property(1, 1);
|
||||||
|
cplines->params[id_C_PY2_I] = Property(1, 1);
|
||||||
|
|
||||||
|
// upper->params[id_C_O1] = Property(0b11, 2); // COMB1OUT -> OUT1
|
||||||
|
// upper->params[id_C_O2] = Property(0b11, 2); // COMB2OUT -> OUT2
|
||||||
|
}
|
||||||
|
|
||||||
|
MultCell::MultCell(CellInfo *lower, CellInfo *upper, IdString name, bool is_msb) : lower{lower}, upper{upper}
|
||||||
|
{
|
||||||
|
lower->params[id_INIT_L02] = Property(LUT_AND, 4);
|
||||||
|
lower->params[id_INIT_L03] = Property(LUT_D1, 4); // PINX
|
||||||
|
lower->params[id_INIT_L11] = Property(LUT_XOR, 4);
|
||||||
|
// lower->params[id_INIT_L20] = Property(LUT_D1, 4); // L11
|
||||||
|
lower->params[id_C_FUNCTION] = Property(C_MULT, 3);
|
||||||
|
|
||||||
|
upper->params[id_INIT_L00] = Property(LUT_AND, 4);
|
||||||
|
upper->params[id_INIT_L01] = Property(LUT_D1, 4); // CINX
|
||||||
|
upper->params[id_INIT_L10] = Property(LUT_XOR, 4);
|
||||||
|
|
||||||
|
upper->params[id_C_I1] = Property(1, 1); // PINY1 for L00
|
||||||
|
upper->params[id_C_I2] = Property(1, 1); // CINX for L01
|
||||||
|
lower->params[id_C_I3] = Property(1, 1); // PINY1 for L02
|
||||||
|
lower->params[id_C_I4] = Property(1, 1); // PINX for L03
|
||||||
|
upper->params[id_C_FUNCTION] = Property(C_MULT, 3);
|
||||||
|
|
||||||
|
if (is_msb) {
|
||||||
|
lower->params[id_C_PY1_I] = Property(1, 1);
|
||||||
|
lower->params[id_C_C_P] = Property(1, 1);
|
||||||
|
} else {
|
||||||
|
lower->params[id_C_PY1_I] = Property(0, 1);
|
||||||
|
lower->params[id_C_C_P] = Property(0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// upper->params[id_C_O1] = Property(0b10, 2); // CP_OUT1 -> OUT1
|
||||||
|
// upper->params[id_C_O2] = Property(0b10, 2); // CP_OUT2 -> OUT2
|
||||||
|
}
|
||||||
|
|
||||||
|
MsbRoutingCell::MsbRoutingCell(CellInfo *lower, CellInfo *upper, CellInfo *comp, CellInfo *cplines, IdString name)
|
||||||
|
: lower{lower}, upper{upper}, comp{comp}, cplines{cplines}
|
||||||
|
{
|
||||||
|
// lower->params[id_INIT_L02] = Property(LUT_ZERO, 4); // (unused)
|
||||||
|
// lower->params[id_INIT_L03] = Property(LUT_ZERO, 4); // (unused)
|
||||||
|
// lower->params[id_INIT_L11] = Property(LUT_ZERO, 4); // (unused)
|
||||||
|
// lower->params[id_INIT_L20] = Property(LUT_ZERO, 4); // (unused)
|
||||||
|
|
||||||
|
comp->params[id_INIT_L30] = Property(LUT_ONE, 4); // zero -> COMP_OUT (L30 is inverted)
|
||||||
|
|
||||||
|
upper->params[id_INIT_L00] = Property(LUT_D1, 4); // PINY1
|
||||||
|
upper->params[id_INIT_L01] = Property(LUT_ZERO, 4); // (unused)
|
||||||
|
upper->params[id_INIT_L10] = Property(LUT_D0, 4); // L00 -> COMB2OUT
|
||||||
|
upper->params[id_C_I1] = Property(1, 1); // PINY1 for L00
|
||||||
|
|
||||||
|
cplines->params[id_C_SELX] = Property(1, 1); // COMB2OUT -> CX_VAL; PINY1 -> PX_VAL
|
||||||
|
cplines->params[id_C_SELY1] = Property(0, 1); // COMP_OUT -> PY1_VAL
|
||||||
|
cplines->params[id_C_SELY2] = Property(0, 1); // COMP_OUT -> PY2_VAL
|
||||||
|
cplines->params[id_C_SEL_P] = Property(1, 1); // PINY1 -> PX_VAL; COMP_OUT -> PY1_VAL; COMP_OUT -> PY2_VAL
|
||||||
|
cplines->params[id_C_CX_I] = Property(1, 1); // CX_VAL -> COUTX
|
||||||
|
cplines->params[id_C_PX_I] = Property(1, 1); // PX_VAL -> POUTX
|
||||||
|
cplines->params[id_C_PY1_I] = Property(1, 1); // PY1_VAL -> POUTY1
|
||||||
|
cplines->params[id_C_PY2_I] = Property(1, 1); // PY2_VAL -> POUTY2
|
||||||
|
|
||||||
|
// upper->params[id_C_O2] = Property(0b11, 2); // COMB2 -> OUT2
|
||||||
|
}
|
||||||
|
|
||||||
|
void GateMatePacker::pack_mult()
|
||||||
|
{
|
||||||
|
// note to self: use constr_children for recursive constraints
|
||||||
|
// fpga_generic.pas in p_r might have useful info
|
||||||
|
|
||||||
|
auto create_zero_driver = [&](IdString name) {
|
||||||
|
auto *zero_lower = create_cell_ptr(id_CPE_DUMMY, ctx->idf("%s$zero_lower", name.c_str(ctx)));
|
||||||
|
auto *zero_upper = create_cell_ptr(id_CPE_L2T4, ctx->idf("%s$zero", name.c_str(ctx)));
|
||||||
|
return ZeroDriver{zero_lower, zero_upper, name};
|
||||||
|
};
|
||||||
|
|
||||||
|
auto create_a_passthru = [&](IdString name) {
|
||||||
|
auto *a_passthru_lower = create_cell_ptr(id_CPE_L2T4, ctx->idf("%s$a_passthru_lower", name.c_str(ctx)));
|
||||||
|
auto *a_passthru_upper = create_cell_ptr(id_CPE_L2T4, ctx->idf("%s$a_passthru_upper", name.c_str(ctx)));
|
||||||
|
auto *a_passthru_comp = create_cell_ptr(id_CPE_COMP, ctx->idf("%s$a_passthru_comp", name.c_str(ctx)));
|
||||||
|
auto *a_passthru_lines = create_cell_ptr(id_CPE_CPLINES, ctx->idf("%s$a_passthru_cplines", name.c_str(ctx)));
|
||||||
|
|
||||||
|
NetInfo *comp_conn = ctx->createNet(ctx->idf("%s$a_passthru_comp$compout", name.c_str(ctx)));
|
||||||
|
a_passthru_comp->connectPort(id_COMPOUT, comp_conn);
|
||||||
|
a_passthru_lines->connectPort(id_COMPOUT, comp_conn);
|
||||||
|
|
||||||
|
return APassThroughCell{a_passthru_lower, a_passthru_upper, a_passthru_comp, a_passthru_lines, name};
|
||||||
|
};
|
||||||
|
|
||||||
|
auto create_mult_col = [&](IdString name, int a_width, bool is_even_x, bool carry_enable_cinx,
|
||||||
|
bool multfab_enable_cinx) {
|
||||||
|
// Ideally this would be the MultiplierColumn constructor, but we need create_cell_ptr here.
|
||||||
|
auto col = MultiplierColumn{};
|
||||||
|
|
||||||
|
{
|
||||||
|
auto *b_passthru_lower = create_cell_ptr(id_CPE_L2T4, ctx->idf("%s$b_passthru_lower", name.c_str(ctx)));
|
||||||
|
auto *b_passthru_upper = create_cell_ptr(id_CPE_L2T4, ctx->idf("%s$b_passthru_upper", name.c_str(ctx)));
|
||||||
|
auto *b_passthru_lines =
|
||||||
|
create_cell_ptr(id_CPE_CPLINES, ctx->idf("%s$b_passthru_cplines", name.c_str(ctx)));
|
||||||
|
|
||||||
|
NetInfo *comb1_conn = ctx->createNet(ctx->idf("%s$b_passthru$comb1", name.c_str(ctx)));
|
||||||
|
b_passthru_lower->connectPort(id_OUT, comb1_conn);
|
||||||
|
b_passthru_lines->connectPort(id_OUT1, comb1_conn);
|
||||||
|
|
||||||
|
NetInfo *comb2_conn = ctx->createNet(ctx->idf("%s$b_passthru$comb2", name.c_str(ctx)));
|
||||||
|
b_passthru_upper->connectPort(id_OUT, comb2_conn);
|
||||||
|
b_passthru_lines->connectPort(id_OUT2, comb2_conn);
|
||||||
|
|
||||||
|
col.b_passthru = BPassThroughCell{b_passthru_lower, b_passthru_upper, b_passthru_lines, name};
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto *carry_lower = create_cell_ptr(id_CPE_L2T4, ctx->idf("%s$carry_lower", name.c_str(ctx)));
|
||||||
|
auto *carry_upper = create_cell_ptr(id_CPE_L2T4, ctx->idf("%s$carry_upper", name.c_str(ctx)));
|
||||||
|
auto *carry_comp = create_cell_ptr(id_CPE_COMP, ctx->idf("%s$carry_comp", name.c_str(ctx)));
|
||||||
|
auto *carry_lines = create_cell_ptr(id_CPE_CPLINES, ctx->idf("%s$carry_lines", name.c_str(ctx)));
|
||||||
|
|
||||||
|
NetInfo *comp_in = ctx->createNet(ctx->idf("%s$carry$comp_in", name.c_str(ctx)));
|
||||||
|
carry_lower->connectPort(id_OUT, comp_in);
|
||||||
|
carry_comp->connectPort(id_COMB1, comp_in);
|
||||||
|
|
||||||
|
NetInfo *comp_out = ctx->createNet(ctx->idf("%s$carry$comp_out", name.c_str(ctx)));
|
||||||
|
carry_comp->connectPort(id_COMPOUT, comp_out);
|
||||||
|
carry_lines->connectPort(id_COMPOUT, comp_out);
|
||||||
|
|
||||||
|
col.carry = CarryGenCell{carry_lower, carry_upper, carry_comp, carry_lines,
|
||||||
|
name, !is_even_x, carry_enable_cinx};
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto *multfab_lower =
|
||||||
|
create_cell_ptr(id_CPE_L2T4, ctx->idf("%s$multf%c_lower", name.c_str(ctx), is_even_x ? 'a' : 'b'));
|
||||||
|
auto *multfab_upper =
|
||||||
|
create_cell_ptr(id_CPE_L2T4, ctx->idf("%s$multf%c_upper", name.c_str(ctx), is_even_x ? 'a' : 'b'));
|
||||||
|
auto *multfab_comp =
|
||||||
|
create_cell_ptr(id_CPE_COMP, ctx->idf("%s$multf%c_comp", name.c_str(ctx), is_even_x ? 'a' : 'b'));
|
||||||
|
auto *multfab_lines = create_cell_ptr(
|
||||||
|
id_CPE_CPLINES, ctx->idf("%s$multf%c_cplines", name.c_str(ctx), is_even_x ? 'a' : 'b'));
|
||||||
|
|
||||||
|
NetInfo *comb1_conn = ctx->createNet(ctx->idf("%s$multf%c$comb1", name.c_str(ctx), is_even_x ? 'a' : 'b'));
|
||||||
|
multfab_lower->connectPort(id_OUT, comb1_conn);
|
||||||
|
multfab_lines->connectPort(id_OUT1, comb1_conn);
|
||||||
|
|
||||||
|
NetInfo *comb2_conn = ctx->createNet(ctx->idf("%s$multf%c$comb2", name.c_str(ctx), is_even_x ? 'a' : 'b'));
|
||||||
|
multfab_upper->connectPort(id_OUT, comb2_conn);
|
||||||
|
multfab_comp->connectPort(id_COMB2, comb2_conn);
|
||||||
|
|
||||||
|
NetInfo *comp_out = ctx->createNet(ctx->idf("%s$multf%c$comp_out", name.c_str(ctx), is_even_x ? 'a' : 'b'));
|
||||||
|
multfab_comp->connectPort(id_COMPOUT, comp_out);
|
||||||
|
multfab_lines->connectPort(id_COMPOUT, comp_out);
|
||||||
|
|
||||||
|
col.multfab = MultfabCell{multfab_lower, multfab_upper, multfab_comp, multfab_lines,
|
||||||
|
name, is_even_x, multfab_enable_cinx};
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto *f_route_lower = create_cell_ptr(id_CPE_L2T4, ctx->idf("%s$f_route_lower", name.c_str(ctx)));
|
||||||
|
auto *f_route_upper = create_cell_ptr(id_CPE_L2T4, ctx->idf("%s$f_route_upper", name.c_str(ctx)));
|
||||||
|
auto *f_route_comp = create_cell_ptr(id_CPE_COMP, ctx->idf("%s$f_route_comp", name.c_str(ctx)));
|
||||||
|
auto *f_route_lines = create_cell_ptr(id_CPE_CPLINES, ctx->idf("%s$f_route_lines", name.c_str(ctx)));
|
||||||
|
|
||||||
|
NetInfo *comb1_conn = ctx->createNet(ctx->idf("%s$f_route$comb1", name.c_str(ctx)));
|
||||||
|
f_route_lower->connectPort(id_OUT, comb1_conn);
|
||||||
|
f_route_lines->connectPort(id_OUT1, comb1_conn);
|
||||||
|
|
||||||
|
NetInfo *comb2_conn = ctx->createNet(ctx->idf("%s$f_route$comb2", name.c_str(ctx)));
|
||||||
|
f_route_upper->connectPort(id_OUT, comb2_conn);
|
||||||
|
f_route_lines->connectPort(id_OUT2, comb2_conn);
|
||||||
|
if (!is_even_x) {
|
||||||
|
f_route_comp->connectPort(id_COMB2, comb2_conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
NetInfo *comp_out = ctx->createNet(ctx->idf("%s$f_route$comp_out", name.c_str(ctx)));
|
||||||
|
f_route_comp->connectPort(id_COMPOUT, comp_out);
|
||||||
|
f_route_lines->connectPort(id_COMPOUT, comp_out);
|
||||||
|
|
||||||
|
col.f_route = FRoutingCell{f_route_lower, f_route_upper, f_route_comp, f_route_lines, name, is_even_x};
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < (a_width / 2); i++) {
|
||||||
|
auto *mult_lower = create_cell_ptr(id_CPE_LT_L, ctx->idf("%s$row%d$mult_lower", name.c_str(ctx), i));
|
||||||
|
auto *mult_upper = create_cell_ptr(id_CPE_LT_U, ctx->idf("%s$row%d$mult_upper", name.c_str(ctx), i));
|
||||||
|
|
||||||
|
col.mults.push_back(MultCell{mult_lower, mult_upper, name, i == ((a_width / 2) - 1)});
|
||||||
|
uarch->multipliers.push_back(mult_lower);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto *msb_route_lower = create_cell_ptr(id_CPE_DUMMY, ctx->idf("%s$msb_route_lower", name.c_str(ctx)));
|
||||||
|
auto *msb_route_upper = create_cell_ptr(id_CPE_L2T4, ctx->idf("%s$msb_route", name.c_str(ctx)));
|
||||||
|
auto *msb_route_comp = create_cell_ptr(id_CPE_COMP, ctx->idf("%s$msb_route_comp", name.c_str(ctx)));
|
||||||
|
auto *msb_route_lines = create_cell_ptr(id_CPE_CPLINES, ctx->idf("%s$msb_route_lines", name.c_str(ctx)));
|
||||||
|
|
||||||
|
NetInfo *comp_conn = ctx->createNet(ctx->idf("%s$msb_route$compout", name.c_str(ctx)));
|
||||||
|
msb_route_comp->connectPort(id_COMPOUT, comp_conn);
|
||||||
|
msb_route_lines->connectPort(id_COMPOUT, comp_conn);
|
||||||
|
|
||||||
|
col.msb_route = MsbRoutingCell{msb_route_lower, msb_route_upper, msb_route_comp, msb_route_lines, name};
|
||||||
|
}
|
||||||
|
|
||||||
|
return col;
|
||||||
|
};
|
||||||
|
|
||||||
|
log_info("Packing multipliers...\n");
|
||||||
|
|
||||||
|
auto mults = std::vector<CellInfo *>{};
|
||||||
|
|
||||||
|
for (auto &cell : ctx->cells) {
|
||||||
|
CellInfo *ci = cell.second.get();
|
||||||
|
if (ci->type == id_CC_MULT)
|
||||||
|
mults.push_back(ci);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto *mult : mults) {
|
||||||
|
auto a_width = int_or_default(mult->params, id_A_WIDTH);
|
||||||
|
auto b_width = int_or_default(mult->params, id_B_WIDTH);
|
||||||
|
auto p_width = int_or_default(mult->params, id_P_WIDTH);
|
||||||
|
|
||||||
|
// Sign-extend odd A_WIDTH to even, because we're working with 2x2 multiplier cells.
|
||||||
|
while (a_width < p_width || a_width % 2 == 1) {
|
||||||
|
mult->copyPortTo(ctx->idf("A[%d]", a_width - 1), mult, ctx->idf("A[%d]", a_width));
|
||||||
|
a_width += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sign-extend odd B_WIDTH to even, because we're working with 2x2 multiplier cells.
|
||||||
|
while (b_width < p_width || b_width % 2 == 1) {
|
||||||
|
mult->copyPortTo(ctx->idf("B[%d]", b_width - 1), mult, ctx->idf("B[%d]", b_width));
|
||||||
|
b_width += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
log_info(" Configuring '%s' as a %d-bit * %d-bit = %d-bit multiplier.\n", mult->name.c_str(ctx), a_width,
|
||||||
|
b_width, p_width);
|
||||||
|
|
||||||
|
auto m = Multiplier{};
|
||||||
|
|
||||||
|
// Step 1: instantiate all the CPEs.
|
||||||
|
m.zero = create_zero_driver(ctx->idf("%s$col0", mult->name.c_str(ctx)));
|
||||||
|
for (int a = 0; a < a_width / 2; a++)
|
||||||
|
m.a_passthrus.push_back(create_a_passthru(ctx->idf("%s$col0$row%d", mult->name.c_str(ctx), a)));
|
||||||
|
for (int b = 0; b < b_width / 2; b++)
|
||||||
|
m.cols.push_back(create_mult_col(ctx->idf("%s$col%d", mult->name.c_str(ctx), b + 1), a_width, b % 2 == 0,
|
||||||
|
b == 2 /* ??? */, b > 0 /* ??? */));
|
||||||
|
|
||||||
|
// Step 2: constrain them together.
|
||||||
|
// We define (0, 0) to be the B passthrough cell of column 1.
|
||||||
|
// we also constrain it to proper Z location
|
||||||
|
auto *root = m.cols[0].b_passthru.upper;
|
||||||
|
root->cluster = root->name;
|
||||||
|
root->constr_abs_z = true;
|
||||||
|
root->constr_z = CPE_LT_U_Z;
|
||||||
|
|
||||||
|
auto constrain_cell = [&](CellInfo *cell, int x_offset, int y_offset, int z_offset) {
|
||||||
|
if (cell == root)
|
||||||
|
return;
|
||||||
|
root->constr_children.push_back(cell);
|
||||||
|
cell->cluster = root->name;
|
||||||
|
cell->constr_abs_z = true;
|
||||||
|
cell->constr_x = x_offset;
|
||||||
|
cell->constr_y = y_offset;
|
||||||
|
cell->constr_z = z_offset;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Constrain zero driver.
|
||||||
|
constrain_cell(m.zero.lower, -1, 3, CPE_LT_L_Z);
|
||||||
|
constrain_cell(m.zero.upper, -1, 3, CPE_LT_U_Z);
|
||||||
|
|
||||||
|
// Constrain A passthrough cells.
|
||||||
|
for (int a = 0; a < a_width / 2; a++) {
|
||||||
|
auto &a_passthru = m.a_passthrus.at(a);
|
||||||
|
constrain_cell(a_passthru.lower, -1, 4 + a, CPE_LT_L_Z);
|
||||||
|
constrain_cell(a_passthru.upper, -1, 4 + a, CPE_LT_U_Z);
|
||||||
|
constrain_cell(a_passthru.comp, -1, 4 + a, CPE_COMP_Z);
|
||||||
|
constrain_cell(a_passthru.cplines, -1, 4 + a, CPE_CPLINES_Z);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Constrain multiplier columns.
|
||||||
|
for (int b = 0; b < b_width / 2; b++) {
|
||||||
|
auto &col = m.cols.at(b);
|
||||||
|
constrain_cell(col.b_passthru.lower, b, b, CPE_LT_L_Z);
|
||||||
|
constrain_cell(col.b_passthru.upper, b, b, CPE_LT_U_Z);
|
||||||
|
constrain_cell(col.b_passthru.cplines, b, b, CPE_CPLINES_Z);
|
||||||
|
|
||||||
|
constrain_cell(col.carry.lower, b, b + 1, CPE_LT_L_Z);
|
||||||
|
constrain_cell(col.carry.upper, b, b + 1, CPE_LT_U_Z);
|
||||||
|
constrain_cell(col.carry.comp, b, b + 1, CPE_COMP_Z);
|
||||||
|
constrain_cell(col.carry.cplines, b, b + 1, CPE_CPLINES_Z);
|
||||||
|
|
||||||
|
constrain_cell(col.multfab.lower, b, b + 2, CPE_LT_L_Z);
|
||||||
|
constrain_cell(col.multfab.upper, b, b + 2, CPE_LT_U_Z);
|
||||||
|
constrain_cell(col.multfab.comp, b, b + 2, CPE_COMP_Z);
|
||||||
|
constrain_cell(col.multfab.cplines, b, b + 2, CPE_CPLINES_Z);
|
||||||
|
|
||||||
|
constrain_cell(col.f_route.lower, b, b + 3, CPE_LT_L_Z);
|
||||||
|
constrain_cell(col.f_route.upper, b, b + 3, CPE_LT_U_Z);
|
||||||
|
constrain_cell(col.f_route.comp, b, b + 3, CPE_COMP_Z);
|
||||||
|
constrain_cell(col.f_route.cplines, b, b + 3, CPE_CPLINES_Z);
|
||||||
|
|
||||||
|
for (size_t mult_idx = 0; mult_idx < col.mults.size(); mult_idx++) {
|
||||||
|
constrain_cell(col.mults[mult_idx].lower, b, b + 4 + mult_idx, CPE_LT_L_Z);
|
||||||
|
constrain_cell(col.mults[mult_idx].upper, b, b + 4 + mult_idx, CPE_LT_U_Z);
|
||||||
|
}
|
||||||
|
|
||||||
|
constrain_cell(col.msb_route.lower, b, b + 4 + col.mults.size(), CPE_LT_L_Z);
|
||||||
|
constrain_cell(col.msb_route.upper, b, b + 4 + col.mults.size(), CPE_LT_U_Z);
|
||||||
|
constrain_cell(col.msb_route.comp, b, b + 4 + col.mults.size(), CPE_COMP_Z);
|
||||||
|
constrain_cell(col.msb_route.cplines, b, b + 4 + col.mults.size(), CPE_CPLINES_Z);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 3: connect them.
|
||||||
|
|
||||||
|
// Zero driver.
|
||||||
|
auto *zero_net = ctx->createNet(ctx->idf("%s$out", m.zero.upper->name.c_str(ctx)));
|
||||||
|
m.zero.upper->connectPort(id_OUT, zero_net);
|
||||||
|
|
||||||
|
// A input.
|
||||||
|
for (size_t a = 0; a < m.a_passthrus.size(); a++) {
|
||||||
|
auto &a_passthru = m.a_passthrus.at(a);
|
||||||
|
|
||||||
|
// Connect A input passthrough cell.
|
||||||
|
mult->movePortTo(ctx->idf("A[%d]", 2 * a), a_passthru.lower, id_IN1);
|
||||||
|
mult->movePortTo(ctx->idf("A[%d]", 2 * a + 1), a_passthru.upper, id_IN1);
|
||||||
|
|
||||||
|
// Prepare A passthrough nets.
|
||||||
|
auto lower_name = a_passthru.lower->name;
|
||||||
|
auto upper_name = a_passthru.upper->name;
|
||||||
|
auto lower_net_name = a_passthru.lower->ports.at(id_IN1).net->name;
|
||||||
|
auto upper_net_name = a_passthru.upper->ports.at(id_IN1).net->name;
|
||||||
|
|
||||||
|
auto *lower_net = ctx->createNet(
|
||||||
|
ctx->idf("%s$%s$a%d_passthru", lower_name.c_str(ctx), lower_net_name.c_str(ctx), 2 * a));
|
||||||
|
a_passthru.lower->connectPort(id_OUT, lower_net);
|
||||||
|
|
||||||
|
auto *upper_net = ctx->createNet(
|
||||||
|
ctx->idf("%s$%s$a%d_passthru", upper_name.c_str(ctx), upper_net_name.c_str(ctx), 2 * a + 1));
|
||||||
|
a_passthru.upper->connectPort(id_OUT, upper_net);
|
||||||
|
|
||||||
|
// Inputs may be GND/VCC; if so, clean them up.
|
||||||
|
a_passthru.clean_up_cell(ctx, a_passthru.lower);
|
||||||
|
a_passthru.clean_up_cell(ctx, a_passthru.upper);
|
||||||
|
|
||||||
|
// Connect A passthrough outputs to multiplier inputs.
|
||||||
|
{
|
||||||
|
// Sum output connections.
|
||||||
|
auto &mult_row = m.cols.at(0).mults.at(a);
|
||||||
|
|
||||||
|
auto *so1_net = ctx->createNet(ctx->idf("%s$so1", upper_name.c_str(ctx)));
|
||||||
|
a_passthru.cplines->connectPort(id_COUTX, so1_net);
|
||||||
|
mult_row.lower->connectPort(id_CINX, so1_net);
|
||||||
|
|
||||||
|
auto *so2_net = ctx->createNet(ctx->idf("%s$so2", upper_name.c_str(ctx)));
|
||||||
|
a_passthru.cplines->connectPort(id_POUTX, so2_net);
|
||||||
|
mult_row.lower->connectPort(id_PINX, so2_net);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t b = 0; b < m.cols.size(); b++) {
|
||||||
|
auto &mult_row = m.cols.at(b).mults.at(a);
|
||||||
|
mult_row.lower->connectPort(id_IN1, lower_net);
|
||||||
|
mult_row.upper->connectPort(id_IN1, upper_net);
|
||||||
|
|
||||||
|
if (a == 0) {
|
||||||
|
mult_row.lower->connectPort(id_IN4, zero_net);
|
||||||
|
} else {
|
||||||
|
auto &mult_row_below = m.cols.at(b).mults.at(a - 1);
|
||||||
|
auto *a_net_below = mult_row_below.upper->ports.at(id_IN1).net;
|
||||||
|
mult_row.lower->connectPort(id_IN4, a_net_below);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// B input.
|
||||||
|
for (size_t b = 0; b < m.cols.size(); b++) {
|
||||||
|
auto &b_passthru = m.cols.at(b).b_passthru;
|
||||||
|
|
||||||
|
// Connect B input passthrough cell.
|
||||||
|
mult->movePortTo(ctx->idf("B[%d]", 2 * b), b_passthru.upper, id_IN1);
|
||||||
|
mult->movePortTo(ctx->idf("B[%d]", 2 * b + 1), b_passthru.lower, id_IN1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Intermediate multiplier connections.
|
||||||
|
for (size_t b = 0; b < m.cols.size(); b++) {
|
||||||
|
auto &b_passthru = m.cols.at(b).b_passthru;
|
||||||
|
auto &b_carry = m.cols.at(b).carry;
|
||||||
|
auto &b_multfab = m.cols.at(b).multfab;
|
||||||
|
auto &b_f_route = m.cols.at(b).f_route;
|
||||||
|
auto &b_msb_route = m.cols.at(b).msb_route;
|
||||||
|
|
||||||
|
auto lower_net_name = b_passthru.lower->ports.at(id_IN1).net->name;
|
||||||
|
auto upper_net_name = b_passthru.upper->ports.at(id_IN1).net->name;
|
||||||
|
|
||||||
|
// B Passthrough (POUTY1, COUTY1) -> Carry Gen (PINY1, CINY1)
|
||||||
|
{
|
||||||
|
auto lines_name = b_passthru.cplines->name;
|
||||||
|
|
||||||
|
auto *lower_net = ctx->createNet(
|
||||||
|
ctx->idf("%s$%s$b%d_passthru", lines_name.c_str(ctx), lower_net_name.c_str(ctx), 2 * b));
|
||||||
|
b_passthru.cplines->connectPort(id_POUTY1, lower_net);
|
||||||
|
b_carry.cplines->connectPort(id_PINY1, lower_net);
|
||||||
|
b_carry.lower->connectPort(id_PINY1, lower_net);
|
||||||
|
|
||||||
|
auto *upper_net = ctx->createNet(
|
||||||
|
ctx->idf("%s$%s$b%d_passthru", lines_name.c_str(ctx), upper_net_name.c_str(ctx), 2 * b + 1));
|
||||||
|
b_passthru.cplines->connectPort(id_COUTY1, upper_net);
|
||||||
|
b_carry.cplines->connectPort(id_CINY1, upper_net);
|
||||||
|
b_carry.lower->connectPort(id_CINY1, upper_net);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Carry Gen (POUTY1, COUTY1, COUTY2) -> MULTFab (PINY1, CINY1, CINY2)
|
||||||
|
{
|
||||||
|
auto lines_name = b_carry.cplines->name;
|
||||||
|
|
||||||
|
auto *lower_net = ctx->createNet(
|
||||||
|
ctx->idf("%s$%s$b%d_passthru", lines_name.c_str(ctx), lower_net_name.c_str(ctx), 2 * b));
|
||||||
|
b_carry.cplines->connectPort(id_POUTY1, lower_net);
|
||||||
|
b_multfab.cplines->connectPort(id_PINY1, lower_net);
|
||||||
|
b_multfab.lower->connectPort(id_PINY1, lower_net);
|
||||||
|
b_multfab.upper->connectPort(id_PINY1, lower_net);
|
||||||
|
|
||||||
|
auto *upper_net = ctx->createNet(
|
||||||
|
ctx->idf("%s$%s$b%d_passthru", lines_name.c_str(ctx), upper_net_name.c_str(ctx), 2 * b + 1));
|
||||||
|
b_carry.cplines->connectPort(id_COUTY1, upper_net);
|
||||||
|
b_multfab.cplines->connectPort(id_CINY1, upper_net);
|
||||||
|
b_multfab.lower->connectPort(id_CINY1, upper_net);
|
||||||
|
|
||||||
|
auto *ccs_net = ctx->createNet(ctx->idf("%s$ccs", lines_name.c_str(ctx)));
|
||||||
|
b_carry.cplines->connectPort(id_COUTY2, ccs_net);
|
||||||
|
b_multfab.cplines->connectPort(id_CINY2, ccs_net);
|
||||||
|
}
|
||||||
|
|
||||||
|
// MULTFab (POUTY1, COUTY1, COUTY2) -> FRoute (PINY1, CINY1, CINY2)
|
||||||
|
{
|
||||||
|
auto lines_name = b_multfab.cplines->name;
|
||||||
|
|
||||||
|
auto *lower_net = ctx->createNet(
|
||||||
|
ctx->idf("%s$%s$f%d", lines_name.c_str(ctx), lower_net_name.c_str(ctx), 2 * b + 1));
|
||||||
|
b_multfab.cplines->connectPort(id_POUTY1, lower_net);
|
||||||
|
b_f_route.cplines->connectPort(id_PINY1, lower_net);
|
||||||
|
b_f_route.upper->connectPort(id_PINY1, lower_net);
|
||||||
|
|
||||||
|
auto *upper_net =
|
||||||
|
ctx->createNet(ctx->idf("%s$%s$f%d", lines_name.c_str(ctx), upper_net_name.c_str(ctx), 2 * b));
|
||||||
|
b_multfab.cplines->connectPort(id_COUTY1, upper_net);
|
||||||
|
b_f_route.cplines->connectPort(id_CINY1, upper_net);
|
||||||
|
b_f_route.lower->connectPort(id_CINY1, upper_net);
|
||||||
|
|
||||||
|
auto *ccs_net = ctx->createNet(ctx->idf("%s$ccs", lines_name.c_str(ctx)));
|
||||||
|
b_multfab.cplines->connectPort(id_COUTY2, ccs_net);
|
||||||
|
b_f_route.cplines->connectPort(id_CINY2, ccs_net);
|
||||||
|
}
|
||||||
|
|
||||||
|
// MULTFab (COUTX) -> Carry Gen (CINX)
|
||||||
|
if (b + 1 < m.cols.size()) {
|
||||||
|
auto &b_carry_right = m.cols.at(b + 1).carry;
|
||||||
|
|
||||||
|
auto lines_name = b_multfab.cplines->name;
|
||||||
|
|
||||||
|
auto *cco_net = ctx->createNet(ctx->idf("%s$cco", lines_name.c_str(ctx)));
|
||||||
|
b_multfab.cplines->connectPort(id_COUTX, cco_net);
|
||||||
|
b_carry_right.cplines->connectPort(id_CINX, cco_net);
|
||||||
|
b_carry_right.upper->connectPort(id_CINX, cco_net);
|
||||||
|
}
|
||||||
|
|
||||||
|
// FRoute (POUTY1, POUTY2, COUTY1, COUTY2) -> C_MULT (PINY1, PINY2, CINY1, CINY2)
|
||||||
|
{
|
||||||
|
auto &b_mult = m.cols.at(b).mults.front();
|
||||||
|
|
||||||
|
auto lines_name = b_multfab.cplines->name;
|
||||||
|
|
||||||
|
auto *f_p1_net = ctx->createNet(
|
||||||
|
ctx->idf("%s$%s$f%d_p1", lines_name.c_str(ctx), lower_net_name.c_str(ctx), 2 * b));
|
||||||
|
b_f_route.cplines->connectPort(id_POUTY1, f_p1_net);
|
||||||
|
b_mult.lower->connectPort(id_PINY1, f_p1_net);
|
||||||
|
|
||||||
|
auto *f_p2_net = ctx->createNet(
|
||||||
|
ctx->idf("%s$%s$f%d_p2", lines_name.c_str(ctx), lower_net_name.c_str(ctx), 2 * b + 1));
|
||||||
|
b_f_route.cplines->connectPort(id_POUTY2, f_p2_net);
|
||||||
|
b_mult.lower->connectPort(id_PINY2, f_p2_net);
|
||||||
|
|
||||||
|
auto *f_c1_net = ctx->createNet(
|
||||||
|
ctx->idf("%s$%s$f%d_c1", lines_name.c_str(ctx), upper_net_name.c_str(ctx), 2 * b));
|
||||||
|
b_f_route.cplines->connectPort(id_COUTY1, f_c1_net);
|
||||||
|
b_mult.lower->connectPort(id_CINY1, f_c1_net);
|
||||||
|
|
||||||
|
auto *f_c2_net = ctx->createNet(
|
||||||
|
ctx->idf("%s$%s$f%d_c2", lines_name.c_str(ctx), upper_net_name.c_str(ctx), 2 * b + 1));
|
||||||
|
b_f_route.cplines->connectPort(id_COUTY2, f_c2_net);
|
||||||
|
b_mult.lower->connectPort(id_CINY2, f_c2_net);
|
||||||
|
}
|
||||||
|
|
||||||
|
// FRoute (COUTX) -> MULTFab (CINX)
|
||||||
|
if (b + 1 < m.cols.size()) {
|
||||||
|
auto &b_multfab_right = m.cols.at(b + 1).multfab;
|
||||||
|
|
||||||
|
auto lines_name = b_f_route.cplines->name;
|
||||||
|
|
||||||
|
auto *cco_net = ctx->createNet(ctx->idf("%s$cco", lines_name.c_str(ctx)));
|
||||||
|
b_f_route.cplines->connectPort(id_COUTX, cco_net);
|
||||||
|
b_multfab_right.cplines->connectPort(id_CINX, cco_net);
|
||||||
|
b_multfab_right.upper->connectPort(id_CINX, cco_net);
|
||||||
|
}
|
||||||
|
|
||||||
|
// C_MULT (POUTY1, POUTY2, COUTY1, COUTY2) -> C_MULT (PINY1, PINY2, CINY1, CINY2)
|
||||||
|
for (size_t row = 0; row < m.cols.at(b).mults.size() - 1; row++) {
|
||||||
|
auto &b_mult = m.cols.at(b).mults.at(row);
|
||||||
|
auto &b_mult_up = m.cols.at(b).mults.at(row + 1);
|
||||||
|
|
||||||
|
auto lines_name = b_mult.lower->name;
|
||||||
|
|
||||||
|
auto *lower_b_net =
|
||||||
|
ctx->createNet(ctx->idf("%s$%s$b%d", lines_name.c_str(ctx), lower_net_name.c_str(ctx), 2 * b));
|
||||||
|
b_mult.lower->connectPort(id_POUTY1, lower_b_net);
|
||||||
|
b_mult_up.lower->connectPort(id_PINY1, lower_b_net);
|
||||||
|
|
||||||
|
auto *upper_b_net = ctx->createNet(
|
||||||
|
ctx->idf("%s$%s$b%d", lines_name.c_str(ctx), upper_net_name.c_str(ctx), 2 * b + 1));
|
||||||
|
b_mult.lower->connectPort(id_POUTY2, upper_b_net);
|
||||||
|
b_mult_up.lower->connectPort(id_PINY2, upper_b_net);
|
||||||
|
|
||||||
|
auto *lower_co_net =
|
||||||
|
ctx->createNet(ctx->idf("%s$%s$co%d", lines_name.c_str(ctx), upper_net_name.c_str(ctx), 2 * b));
|
||||||
|
b_mult.lower->connectPort(id_COUTY1, lower_co_net);
|
||||||
|
b_mult_up.lower->connectPort(id_CINY1, lower_co_net);
|
||||||
|
|
||||||
|
auto *upper_co_net = ctx->createNet(
|
||||||
|
ctx->idf("%s$%s$co%d", lines_name.c_str(ctx), upper_net_name.c_str(ctx), 2 * b + 1));
|
||||||
|
b_mult.lower->connectPort(id_COUTY2, upper_co_net);
|
||||||
|
b_mult_up.lower->connectPort(id_CINY2, upper_co_net);
|
||||||
|
}
|
||||||
|
|
||||||
|
// C_MULT (POUTX, COUTX) -> C_MULT (PINX, CINX)
|
||||||
|
if (b + 1 < m.cols.size()) {
|
||||||
|
for (size_t row = 1; row < m.cols.at(b).mults.size(); row++) {
|
||||||
|
auto &b_mult = m.cols.at(b).mults.at(row);
|
||||||
|
auto &b_mult_right = m.cols.at(b + 1).mults.at(row - 1);
|
||||||
|
|
||||||
|
auto lines_name = b_mult.lower->name;
|
||||||
|
|
||||||
|
auto *so1_net = ctx->createNet(ctx->idf("%s$so1", lines_name.c_str(ctx)));
|
||||||
|
b_mult.lower->connectPort(id_POUTX, so1_net);
|
||||||
|
b_mult_right.lower->connectPort(id_PINX, so1_net);
|
||||||
|
|
||||||
|
auto *so2_net = ctx->createNet(ctx->idf("%s$so2", lines_name.c_str(ctx)));
|
||||||
|
b_mult.lower->connectPort(id_COUTX, so2_net);
|
||||||
|
b_mult_right.lower->connectPort(id_CINX, so2_net);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// C_MULT (POUTY1, POUTY2) -> MsbRouting (PINY1, PINY2)
|
||||||
|
{
|
||||||
|
auto &b_mult = m.cols.at(b).mults.back();
|
||||||
|
|
||||||
|
auto lines_name = b_mult.lower->name;
|
||||||
|
|
||||||
|
auto *lower_net =
|
||||||
|
ctx->createNet(ctx->idf("%s$%s$b%d", lines_name.c_str(ctx), lower_net_name.c_str(ctx), 2 * b));
|
||||||
|
b_mult.lower->connectPort(id_POUTY1, lower_net);
|
||||||
|
b_msb_route.cplines->connectPort(id_PINY1, lower_net);
|
||||||
|
b_msb_route.upper->connectPort(id_PINY1, lower_net);
|
||||||
|
|
||||||
|
auto *upper_net = ctx->createNet(
|
||||||
|
ctx->idf("%s$%s$b%d", lines_name.c_str(ctx), lower_net_name.c_str(ctx), 2 * b + 1));
|
||||||
|
b_mult.lower->connectPort(id_POUTY2, upper_net);
|
||||||
|
b_msb_route.cplines->connectPort(id_PINY2, upper_net);
|
||||||
|
}
|
||||||
|
|
||||||
|
// MsbRouting (POUTX, COUTX) -> C_MULT (PINX, CINX)
|
||||||
|
if (b + 1 < m.cols.size()) {
|
||||||
|
auto &b_mult_right = m.cols.at(b + 1).mults.back();
|
||||||
|
|
||||||
|
auto lines_name = b_msb_route.cplines->name;
|
||||||
|
|
||||||
|
auto *so1_net = ctx->createNet(ctx->idf("%s$so1", lines_name.c_str(ctx)));
|
||||||
|
b_msb_route.cplines->connectPort(id_POUTX, so1_net);
|
||||||
|
b_mult_right.lower->connectPort(id_PINX, so1_net);
|
||||||
|
|
||||||
|
auto *so2_net = ctx->createNet(ctx->idf("%s$so2", lines_name.c_str(ctx)));
|
||||||
|
b_msb_route.cplines->connectPort(id_COUTX, so2_net);
|
||||||
|
b_mult_right.lower->connectPort(id_CINX, so2_net);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// P output.
|
||||||
|
auto diagonal_p_width = std::min(b_width, p_width);
|
||||||
|
auto vertical_p_width = std::max(p_width - b_width, 0);
|
||||||
|
|
||||||
|
for (int p = 0; p < diagonal_p_width; p++) {
|
||||||
|
auto &mult_cell = m.cols[p / 2].mults[0];
|
||||||
|
auto *cpe_half = (p % 2 == 1) ? mult_cell.upper : mult_cell.lower;
|
||||||
|
|
||||||
|
mult->movePortTo(ctx->idf("P[%d]", p), cpe_half, id_CPOUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int p = 0; p < vertical_p_width; p++) {
|
||||||
|
auto &mult_cell = m.cols.back().mults[1 + (p / 2)];
|
||||||
|
auto *cpe_half = (p % 2 == 1) ? mult_cell.upper : mult_cell.lower;
|
||||||
|
|
||||||
|
mult->movePortTo(ctx->idf("P[%d]", p + diagonal_p_width), cpe_half, id_CPOUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up the multiplier.
|
||||||
|
for (size_t b = 0; b < m.cols.size(); b++) {
|
||||||
|
auto &b_passthru = m.cols.at(b).b_passthru;
|
||||||
|
|
||||||
|
// This may be GND/VCC.
|
||||||
|
b_passthru.clean_up_cell(ctx, b_passthru.lower);
|
||||||
|
b_passthru.clean_up_cell(ctx, b_passthru.upper);
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx->cells.erase(mult->name);
|
||||||
|
|
||||||
|
log_info(" Created %zu CPEs.\n", m.cpe_count());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NEXTPNR_NAMESPACE_END
|
||||||
|
|
@ -48,8 +48,8 @@ void GateMateImpl::route_clock()
|
||||||
auto clk_nets = std::vector<NetInfo *>{};
|
auto clk_nets = std::vector<NetInfo *>{};
|
||||||
auto reserved_wires = dict<WireId, IdString>{};
|
auto reserved_wires = dict<WireId, IdString>{};
|
||||||
|
|
||||||
auto feeds_clk_port = [](PortRef &port) {
|
auto feeds_clk_port = [&](PortRef &port) {
|
||||||
return port.cell->type.in(id_CPE_HALF, id_CPE_HALF_L, id_CPE_HALF_U) && port.port.in(id_CLK);
|
return (ctx->getBelBucketForCellType(port.cell->type) == id_CPE_FF) && port.port.in(id_CLK);
|
||||||
};
|
};
|
||||||
|
|
||||||
auto feeds_ddr_port = [&](NetInfo *net, PortRef &port) {
|
auto feeds_ddr_port = [&](NetInfo *net, PortRef &port) {
|
||||||
|
|
|
||||||
|
|
@ -34,3 +34,94 @@ TEST_F(GateMateTest, pack_constants)
|
||||||
packer.remove_constants();
|
packer.remove_constants();
|
||||||
ASSERT_EQ(ctx->cells.size(), 0LU);
|
ASSERT_EQ(ctx->cells.size(), 0LU);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LUT[1:0] Function
|
||||||
|
// ==============================
|
||||||
|
// 00 Constant 0
|
||||||
|
// 01 NOT A (inverts input)
|
||||||
|
// 10 A (passes input)
|
||||||
|
// 11 Constant 1
|
||||||
|
|
||||||
|
TEST_F(GateMateTest, remove_lut1_zero)
|
||||||
|
{
|
||||||
|
CellInfo *lut1 = create_cell_ptr(id_CC_LUT1, "lut");
|
||||||
|
lut1->params[id_INIT] = Property(0b00, 2);
|
||||||
|
|
||||||
|
CellInfo *obuf = create_cell_ptr(id_CC_OBUF, "obuf");
|
||||||
|
|
||||||
|
direct_connect(lut1, id_O, obuf, id_A);
|
||||||
|
|
||||||
|
ASSERT_EQ(ctx->cells.size(), 1LU);
|
||||||
|
ctx->uarch->pack();
|
||||||
|
ASSERT_EQ(ctx->cells.size(), 1LU);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(GateMateTest, remove_lut1_one)
|
||||||
|
{
|
||||||
|
CellInfo *lut1 = create_cell_ptr(id_CC_LUT1, "lut");
|
||||||
|
lut1->params[id_INIT] = Property(0b11, 2);
|
||||||
|
|
||||||
|
CellInfo *obuf = create_cell_ptr(id_CC_OBUF, "obuf");
|
||||||
|
|
||||||
|
direct_connect(lut1, id_O, obuf, id_A);
|
||||||
|
|
||||||
|
ASSERT_EQ(ctx->cells.size(), 2LU);
|
||||||
|
ctx->uarch->pack();
|
||||||
|
ASSERT_EQ(ctx->cells.size(), 1LU);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(GateMateTest, remove_lut1_pass)
|
||||||
|
{
|
||||||
|
CellInfo *lut1 = create_cell_ptr(id_CC_LUT1, "lut");
|
||||||
|
lut1->params[id_INIT] = Property(0b10, 2);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(GateMateTest, remove_lut1_inv)
|
||||||
|
{
|
||||||
|
CellInfo *lut1 = create_cell_ptr(id_CC_LUT1, "lut");
|
||||||
|
lut1->params[id_INIT] = Property(0b01, 2);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(GateMateTest, remove_lut1_not_driven)
|
||||||
|
{
|
||||||
|
CellInfo *lut1 = create_cell_ptr(id_CC_LUT1, "lut");
|
||||||
|
lut1->params[id_INIT] = Property(0b01, 2);
|
||||||
|
|
||||||
|
CellInfo *obuf = create_cell_ptr(id_CC_OBUF, "obuf");
|
||||||
|
CellInfo *ibuf = create_cell_ptr(id_CC_IBUF, "ibuf");
|
||||||
|
|
||||||
|
NetInfo *net_in = ctx->createNet(ctx->id("in"));
|
||||||
|
ibuf->connectPort(id_Y, net_in);
|
||||||
|
lut1->connectPort(id_I0, net_in);
|
||||||
|
obuf->connectPort(id_A, net_in);
|
||||||
|
|
||||||
|
ASSERT_EQ(ctx->cells.size(), 3LU);
|
||||||
|
ctx->uarch->pack();
|
||||||
|
// Expect IBUF -> CPE -> OBUF
|
||||||
|
// LUT1 removed as not used, but CPE for driving OBUF added
|
||||||
|
ASSERT_EQ(ctx->cells.size(), 3LU);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ void GateMateTest::SetUp()
|
||||||
{
|
{
|
||||||
init_share_dirname();
|
init_share_dirname();
|
||||||
chipArgs.device = "CCGM1A1";
|
chipArgs.device = "CCGM1A1";
|
||||||
|
chipArgs.options.emplace("allow-unconstrained", "");
|
||||||
ctx = new Context(chipArgs);
|
ctx = new Context(chipArgs);
|
||||||
ctx->uarch->init(ctx);
|
ctx->uarch->init(ctx);
|
||||||
ctx->late_init();
|
ctx->late_init();
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue