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:
Miodrag Milanović 2025-07-07 10:14:48 +02:00 committed by GitHub
parent 1d4b0eeac4
commit 84d8e1abe7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 1868 additions and 299 deletions

View File

@ -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);

View File

@ -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

View File

@ -81,12 +81,12 @@ struct BitstreamBackend
return invert; return invert;
} }
void update_cpe_lt(CellInfo *cell, IdString port, IdString init, dict<IdString, Property> &params) void update_cpe_lt(CellInfo *cell, IdString port, IdString init, dict<IdString, Property> &params, 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> &params) void update_cpe_mux(CellInfo *cell, IdString port, IdString param, int bit, dict<IdString, Property> &params)
{ {
// 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: {

View File

@ -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));
} }

View File

@ -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)

View File

@ -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,

View File

@ -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>();
} }

View File

@ -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;

View File

@ -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)

View File

@ -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;

View File

@ -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();

View File

@ -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);

View File

@ -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 =

View File

@ -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()

View File

@ -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)
} }

View File

@ -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

View File

@ -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) {

View File

@ -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);
}

View File

@ -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();