gatemate: cleanup of PLL and BUFG (#1562)

* Check SER_CLK more

* Use connectPorts

* move rewire code

* Move data structures

* move placement decision for later

* cleanups

* find working layout

* clangformat

* Inverted input on ODDR

* Fix some tests

* Copy clocks for multi die

* cleanup

* reporting

* bugfix

* handle PLL special inputs

* Fix user globals

* Proper DDR per bank and cleanup

* Add extra data for die regions and create them

* Better forced_die implementation

* Copy region to newly generated cells, and update when constrained

* Update PLL error messages

* Add TODO comment
This commit is contained in:
Miodrag Milanović 2025-09-30 13:00:02 +02:00 committed by GitHub
parent 125df9952c
commit abb52f81c2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 533 additions and 173 deletions

View File

@ -94,6 +94,11 @@ struct GateMateCCFReader
count[i]--;
}
uarch->available_pads.erase(ctx->id(value));
// Erase aliases as well
if (value == "SER_CLK")
uarch->available_pads.erase(ctx->id("SER_CLK_N"));
if (value == "SER_CLK_N")
uarch->available_pads.erase(ctx->id("SER_CLK"));
} else if (name == "SCHMITT_TRIGGER" || name == "PULLUP" || name == "PULLDOWN" || name == "KEEPER" ||
name == "FF_IBF" || name == "FF_OBF" || name == "LVDS_BOOST" || name == "LVDS_RTERM") {
if (value == "1")

View File

@ -122,6 +122,21 @@ CellInfo *GateMatePacker::create_cell_ptr(IdString type, IdString name)
add_port(id_CLOCK2, PORT_IN);
add_port(id_CLOCK3, PORT_IN);
add_port(id_CLOCK4, PORT_IN);
} else if (type.in(id_CPE_IBUF)) {
add_port(id_Y, PORT_OUT);
add_port(id_I, PORT_IN);
} else if (type.in(id_PLL)) {
add_port(id_CLK_REF, PORT_IN);
add_port(id_USR_CLK_REF, PORT_IN);
add_port(id_USR_SEL_A_B, PORT_IN);
add_port(id_CLK_FEEDBACK, PORT_IN);
add_port(id_CLK0, PORT_OUT);
add_port(id_CLK90, PORT_OUT);
add_port(id_CLK180, PORT_OUT);
add_port(id_CLK270, PORT_OUT);
add_port(id_USR_PLL_LOCKED, PORT_OUT);
add_port(id_USR_PLL_LOCKED_STDY, PORT_OUT);
add_port(id_USR_LOCKED_STDY_RST, PORT_IN);
} else {
log_error("Trying to create unknown cell type %s\n", type.c_str(ctx));
}

View File

@ -2468,6 +2468,12 @@ X(MULT_INVERT)
X(RAM_HALF)
X(RAM_HALF_DUMMY)
// Dies
X(1A)
X(1B)
X(2A)
X(2B)
// Timing
X(timing_ADDF2x_IN5_8_comb2)
X(timing_CIN_C_CINI)

View File

@ -69,6 +69,16 @@ NPNR_PACKED_STRUCT(struct GateMateTimingExtraDataPOD {
NPNR_PACKED_STRUCT(struct GateMateSpeedGradeExtraDataPOD { RelSlice<GateMateTimingExtraDataPOD> timings; });
NPNR_PACKED_STRUCT(struct GateMateDieRegionPOD {
int32_t name;
uint16_t x1;
uint16_t y1;
uint16_t x2;
uint16_t y2;
});
NPNR_PACKED_STRUCT(struct GateMateChipExtraDataPOD { RelSlice<GateMateDieRegionPOD> dies; });
enum MuxFlags
{
MUX_INVERT = 1,

View File

@ -150,6 +150,27 @@ void GateMateImpl::init(Context *ctx)
ram_signal_clk.emplace(ctx->idf("DOB[%d]", i + num * 20), num + 2);
}
}
const GateMateChipExtraDataPOD *extra =
reinterpret_cast<const GateMateChipExtraDataPOD *>(ctx->chip_info->extra_data.get());
const ArchArgs &args = ctx->args;
std::string die_name;
if (args.options.count("force_die"))
die_name = args.options.at("force_die");
bool found = false;
int index = 0;
for (auto &die : extra->dies) {
IdString name(die.name);
die_to_index[name] = index++;
ctx->createRectangularRegion(name, die.x1, die.y1, die.x2, die.y2);
if (die_name == name.c_str(ctx)) {
found = true;
forced_die = name;
}
}
if (!die_name.empty() && !found)
log_error("Unable to select forced die '%s'.\n", die_name.c_str());
}
bool GateMateImpl::isBelLocationValid(BelId bel, bool explain_invalid) const
@ -159,10 +180,6 @@ bool GateMateImpl::isBelLocationValid(BelId bel, bool explain_invalid) const
return true;
}
// TODO: remove when placemente per die is better handled
if (cell->belStrength != PlaceStrength::STRENGTH_FIXED && tile_extra_data(bel.tile)->die != preferred_die)
return false;
if (getBelBucketForBel(bel) == id_CPE_FF) {
Loc loc = ctx->getBelLocation(bel);
const CellInfo *adj_half = ctx->getBoundBelCell(

View File

@ -90,6 +90,10 @@ struct GateMateImpl : HimbaechelAPI
std::vector<bool> used_cpes;
int fpga_mode;
int timing_mode;
std::map<const NetInfo *, int> global_signals;
std::vector<CellInfo *> clkin;
std::vector<CellInfo *> glbout;
std::vector<CellInfo *> pll;
private:
bool getChildPlacement(const BaseClusterInfo *cluster, Loc root_loc,
@ -122,6 +126,8 @@ struct GateMateImpl : HimbaechelAPI
std::map<BelId, std::map<IdString, const GateMateBelPinConstraintPOD *>> pin_to_constr;
std::map<IdString, const GateMateTimingExtraDataPOD *> timing;
dict<IdString, int> ram_signal_clk;
IdString forced_die;
dict<IdString, int> die_to_index;
};
NEXTPNR_NAMESPACE_END

View File

@ -161,6 +161,40 @@ class SpeedGradeExtraData(BBAStruct):
def serialise(self, context: str, bba: BBAWriter):
bba.slice(f"{context}_timings", len(self.timings))
@dataclass
class DieRegion(BBAStruct):
name: IdString
x1: int = 0
y1: int = 0
x2: int = 0
y2: int = 0
def serialise_lists(self, context: str, bba: BBAWriter):
pass
def serialise(self, context: str, bba: BBAWriter):
bba.u32(self.name.index)
bba.u16(self.x1)
bba.u16(self.y1)
bba.u16(self.x2)
bba.u16(self.y2)
@dataclass
class ChipExtraData(BBAStruct):
dies: list[DieRegion] = field(default_factory = list)
def add_die(self, name: IdString, x1: int, y1: int, x2:int, y2:int):
item = DieRegion(name,x1,y1,x2,y2)
self.dies.append(item)
def serialise_lists(self, context: str, bba: BBAWriter):
self.dies.sort(key=lambda p: p.name.index)
bba.label(f"{context}_dies")
for i, t in enumerate(self.dies):
t.serialise(f"{context}_die{i}", bba)
pass
def serialise(self, context: str, bba: BBAWriter):
bba.slice(f"{context}_dies", len(self.dies))
def convert_timing(tim):
return TimingValue(tim.rise.min, tim.rise.max, tim.fall.min, tim.fall.max)
@ -222,6 +256,10 @@ def main():
print("==============================================================================")
os._exit(-1)
ch.extra_data = ChipExtraData()
for d in sorted(dev.dies):
ch.extra_data.add_die(ch.strs.id(dev.dies[d].name), dev.dies[d].offset_x, dev.dies[d].offset_y, dev.dies[d].offset_x + die.num_cols() - 1, dev.dies[d].offset_y + die.num_rows()-1)
new_wires = dict()
wire_delay = dict()
for _,nodes in dev.get_connections():

View File

@ -98,7 +98,7 @@ void GateMatePacker::disconnect_not_used()
}
}
void GateMatePacker::copy_constraint(NetInfo *in_net, NetInfo *out_net)
void GateMatePacker::copy_constraint(const NetInfo *in_net, NetInfo *out_net)
{
if (!in_net || !out_net)
return;
@ -385,12 +385,14 @@ void GateMateImpl::pack()
parse_ccf(args.options.at("ccf"));
}
if (forced_die != IdString())
preferred_die = die_to_index[forced_die];
GateMatePacker packer(ctx, this);
packer.pack_constants();
packer.cleanup();
packer.pack_io();
packer.insert_clocking();
packer.sort_bufg();
packer.pack_pll();
packer.pack_bufg();
packer.pack_io_sel(); // merge in FF and DDR
@ -400,8 +402,15 @@ void GateMateImpl::pack()
packer.pack_mult();
packer.pack_addf();
packer.pack_cpe();
packer.copy_clocks();
packer.remove_constants();
packer.remove_clocking();
if (forced_die != IdString()) {
for (auto &cell : ctx->cells) {
if (cell.second->belStrength != PlaceStrength::STRENGTH_FIXED)
ctx->constrainCellToRegion(cell.second->name, forced_die);
}
}
}
void GateMateImpl::repack()
@ -409,6 +418,8 @@ void GateMateImpl::repack()
GateMatePacker packer(ctx, this);
packer.repack_ram();
packer.repack_cpe();
packer.reassign_clocks();
packer.remove_clocking();
}
NEXTPNR_NAMESPACE_END

View File

@ -68,6 +68,8 @@ struct GateMatePacker
void cleanup();
void repack_cpe();
void repack_ram();
void reassign_clocks();
void copy_clocks();
private:
void rename_param(CellInfo *cell, IdString name, IdString new_name, int width);
@ -75,6 +77,7 @@ struct GateMatePacker
void dff_update_params();
void disconnect_if_gnd(CellInfo *cell, IdString input);
void pll_out(CellInfo *cell, IdString origPort, Loc fixed);
void rewire_ram_o(CellInfo *first, IdString port, CellInfo *second);
void disconnect_not_used();
void optimize_lut();
@ -104,12 +107,9 @@ struct GateMatePacker
CellInfo *create_cell_ptr(IdString type, IdString name);
void flush_cells();
void pack_ram_cell(CellInfo &ci, CellInfo *cell, bool is_split);
void copy_constraint(NetInfo *in_net, NetInfo *out_net);
void copy_constraint(const NetInfo *in_net, NetInfo *out_net);
pool<IdString> packed_cells;
std::map<NetInfo *, int> global_signals;
std::vector<CellInfo *> clkin;
std::vector<CellInfo *> glbout;
Context *ctx;
GateMateImpl *uarch;
@ -117,6 +117,7 @@ struct GateMatePacker
HimbaechelHelpers h;
NetInfo *net_PACKER_VCC;
NetInfo *net_PACKER_GND;
NetInfo *net_SER_CLK;
int count;
std::map<IdString, int> count_per_type;
};

View File

@ -47,10 +47,10 @@ uint8_t GateMatePacker::ram_ctrl_signal(CellInfo *cell, IdString port, bool alt)
uint8_t GateMatePacker::ram_clk_signal(CellInfo *cell, IdString port)
{
NetInfo *clk_net = cell->getPort(port);
if (!global_signals.count(clk_net)) {
if (!uarch->global_signals.count(clk_net)) {
return 0b00000000;
} else {
int index = global_signals[clk_net];
int index = uarch->global_signals[clk_net];
uint8_t val = 0;
switch (index) {
case 0:
@ -348,6 +348,7 @@ void GateMatePacker::pack_ram()
cell->constr_y = +8;
cell->constr_z = RAM_HALF_L_Z;
cell->cluster = ci.cluster;
cell->region = ci.region;
cell->params[id_RAM_cfg_ecc_enable] = Property(b_ecc_en << 1 | a_ecc_en, 2);
cell->params[id_RAM_cfg_sram_mode] = Property(ram_mode << 1 | split, 2);
}
@ -395,7 +396,7 @@ void GateMatePacker::pack_ram()
ci.renamePort(ctx->idf("F_ALMOST_EMPTY_OFFSET[%d]", i), ctx->idf("WEA[%d]", i));
// WEA[34:20] = F_ALMOST_FULL_OFFSET
ci.disconnectPort(ctx->idf("WEA[%d]", 20 + i));
ci.renamePort(ctx->idf("F_ALMOST_FULL_OFFSET[%d]", i), ctx->idf("WEA[%d]", 20 + i));
ci.renamePort(ctx->idf("F_ALMOST_FULL_OFFSET[%d]", i), ctx->idf("WEA[%d]", 20 + i));
}
}
}

View File

@ -92,65 +92,99 @@ static int glb_mux_mapping[] = {
void GateMatePacker::pack_bufg()
{
log_info("Packing BUFGs..\n");
CellInfo *bufg[4] = {nullptr};
CellInfo *pll[4] = {nullptr};
sort_bufg();
auto update_bufg_port = [&](CellInfo *cell, int port_num, int pll_num) {
log_info("Packing BUFGs..\n");
auto update_bufg_port = [&](std::vector<CellInfo *> &bufg, CellInfo *cell, int port_num, int pll_num) {
CellInfo *b = net_only_drives(ctx, cell->getPort(ctx->idf("CLK%d", 90 * port_num)), is_bufg, id_I, false);
if (b) {
if (bufg[port_num] == nullptr) {
bufg[port_num] = b;
return true;
} else {
if (bufg[pll_num] == nullptr) {
bufg[pll_num] = b;
return true;
} else {
log_error("Unable to place BUFG for PLL.\n");
return false;
}
}
}
return true;
};
for (auto &cell : ctx->cells) {
CellInfo &ci = *cell.second;
if (!ci.type.in(id_PLL))
unsigned max_plls = 4; // * uarch->dies;
// Index vector for permutation
std::vector<unsigned> indexes(max_plls);
for (unsigned i = 0; i < max_plls; ++i)
indexes[i] = i;
std::vector<CellInfo *> bufg(max_plls, nullptr);
std::vector<CellInfo *> pll(max_plls, nullptr);
pool<IdString> used_bufg;
bool valid = true;
do {
valid = true;
std::vector<std::vector<CellInfo *>> tmp_bufg(uarch->dies, std::vector<CellInfo *>(4, nullptr));
for (unsigned i = 0; i < max_plls; ++i) {
if (indexes[i] < uarch->pll.size()) {
for (int j = 0; j < 4; j++)
valid &= update_bufg_port(tmp_bufg[i >> 2], uarch->pll[indexes[i]], j, i & 3);
}
}
if (valid) {
for (unsigned i = 0; i < max_plls; ++i) {
bufg[i] = tmp_bufg[i >> 2][i & 3];
if (bufg[i])
used_bufg.insert(bufg[i]->name);
if (indexes[i] < uarch->pll.size())
pll[i] = uarch->pll[indexes[i]];
}
break;
}
} while (std::next_permutation(indexes.begin(), indexes.end()));
if (!valid)
log_error("Unable to place PLLs and BUFGs\n");
for (unsigned i = 0; i < max_plls; ++i) {
int die = i >> 2;
if (!pll[i])
continue;
int index = ci.constr_z - 2;
pll[index] = &ci;
for (int j = 0; j < 4; j++)
update_bufg_port(pll[index], j, index);
CellInfo &ci = *pll[i];
ci.cluster = ci.name;
ci.constr_abs_z = true;
ci.constr_z = 2 + (i & 3); // Position to a proper Z location
Loc fixed_loc = uarch->locations[std::make_pair(ctx->idf("PLL%d", i & 3), die)];
BelId pll_bel = ctx->getBelByLocation(fixed_loc);
ctx->bindBel(pll_bel, &ci, PlaceStrength::STRENGTH_FIXED);
pll_out(&ci, id_CLK0, fixed_loc);
pll_out(&ci, id_CLK90, fixed_loc);
pll_out(&ci, id_CLK180, fixed_loc);
pll_out(&ci, id_CLK270, fixed_loc);
move_ram_i_fixed(&ci, id_USR_PLL_LOCKED, fixed_loc);
move_ram_i_fixed(&ci, id_USR_PLL_LOCKED_STDY, fixed_loc);
move_ram_o_fixed(&ci, id_USR_LOCKED_STDY_RST, fixed_loc);
move_ram_o_fixed(&ci, id_USR_CLK_REF, fixed_loc);
move_ram_o_fixed(&ci, id_USR_SEL_A_B, fixed_loc);
}
for (auto &cell : ctx->cells) {
CellInfo &ci = *cell.second;
if (!ci.type.in(id_CC_BUFG))
continue;
if (used_bufg.count(cell.second->name))
continue;
NetInfo *in_net = ci.getPort(id_I);
int die = uarch->preferred_die;
if (in_net) {
if (in_net->driver.cell) {
if (ctx->getBelBucketForCellType(in_net->driver.cell->type) == id_IOSEL) {
auto pad_info = uarch->bel_to_pad[in_net->driver.cell->bel];
if (pad_info->flags) {
int index = pad_info->flags - 1;
die = uarch->tile_extra_data(in_net->driver.cell->bel.tile)->die;
if (!clkin[die]->getPort(ctx->idf("CLK%d", index))) {
clkin[die]->connectPort(ctx->idf("CLK%d", index), in_net->driver.cell->getPort(id_Y));
}
}
}
} else {
// SER_CLK
clkin[die]->connectPort(id_SER_CLK, in_net);
}
copy_constraint(in_net, ci.getPort(id_O));
if ((in_net->driver.cell && ctx->getBelBucketForCellType(in_net->driver.cell->type) != id_PLL) ||
!in_net->driver.cell) {
for (int i = 0; i < 4; i++) {
if (bufg[i] == nullptr) {
for (unsigned i = 0; i < max_plls; ++i) {
if (bufg[i] == nullptr && pll[i] == nullptr) { // PLL must not be used
bufg[i] = &ci;
break;
}
@ -159,24 +193,29 @@ void GateMatePacker::pack_bufg()
}
}
for (int i = 0; i < 4; i++) {
if (bufg[i]) {
CellInfo &ci = *bufg[i];
global_signals.emplace(ci.getPort(id_O), i);
for (unsigned j = 0; j < max_plls; ++j) {
if (bufg[j]) {
CellInfo &ci = *bufg[j];
int i = j & 3;
int die = j >> 2;
uarch->global_signals.emplace(ci.getPort(id_O), j);
int glb_mux = 0;
NetInfo *in_net = ci.getPort(id_I);
int die = uarch->preferred_die;
copy_constraint(in_net, ci.getPort(id_O));
if (in_net->driver.cell) {
bool user_glb = true;
if (ctx->getBelBucketForCellType(in_net->driver.cell->type) == id_IOSEL) {
auto pad_info = uarch->bel_to_pad[in_net->driver.cell->bel];
if (pad_info->flags) {
die = uarch->tile_extra_data(in_net->driver.cell->bel.tile)->die;
clkin[die]->params[ctx->idf("REF%d", i)] = Property(pad_info->flags - 1, 3);
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);
uarch->clkin[die]->params[ctx->idf("REF%d", i)] = Property(pad_info->flags - 1, 3);
uarch->clkin[die]->params[ctx->idf("REF%d_INV", i)] = Property(Property::State::S0);
uarch->clkin[die]->connectPorts(ctx->idf("CLK_REF%d", i), uarch->glbout[die],
ctx->idf("CLK_REF_OUT%d", i));
int index = pad_info->flags - 1;
if (!uarch->clkin[die]->getPort(ctx->idf("CLK%d", index))) {
uarch->clkin[die]->connectPort(ctx->idf("CLK%d", index),
in_net->driver.cell->getPort(id_Y));
}
user_glb = false;
}
}
@ -195,49 +234,71 @@ void GateMatePacker::pack_bufg()
else
log_error("Uknown connecton on BUFG to PLL.\n");
glb_mux = glb_mux_mapping[i * 16 + pll_index * 4 + pll_out];
ci.movePortTo(id_I, glbout[die], ctx->idf("%s_%d", in_net->driver.port.c_str(ctx), pll_index));
ci.movePortTo(id_I, uarch->glbout[die],
ctx->idf("%s_%d", in_net->driver.port.c_str(ctx), pll_index));
user_glb = false;
}
if (user_glb) {
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));
glbout[die]->params[ctx->idf("USR_GLB%d_EN", i)] = Property(Property::State::S1);
ci.movePortTo(id_I, uarch->glbout[die], ctx->idf("USR_GLB%d", i));
move_ram_o_fixed(uarch->glbout[die], ctx->idf("USR_GLB%d", i),
ctx->getBelLocation(uarch->glbout[die]->bel));
uarch->glbout[die]->params[ctx->idf("USR_GLB%d_EN", i)] = Property(Property::State::S1);
}
} else {
// SER_CLK
clkin[die]->params[ctx->idf("REF%d", i)] = Property(0b100, 3);
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);
uarch->clkin[die]->connectPort(id_SER_CLK, in_net);
uarch->clkin[die]->params[ctx->idf("REF%d", i)] = Property(0b100, 3);
uarch->clkin[die]->params[ctx->idf("REF%d_INV", i)] = Property(Property::State::S0);
uarch->clkin[die]->connectPorts(ctx->idf("CLK_REF%d", i), uarch->glbout[die],
ctx->idf("CLK_REF_OUT%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_CFG", i)] = Property(glb_mux, 3);
ci.movePortTo(id_O, uarch->glbout[die], ctx->idf("GLB%d", i));
uarch->glbout[die]->params[ctx->idf("GLB%d_EN", i)] = Property(Property::State::S1);
uarch->glbout[die]->params[ctx->idf("GLB%d_CFG", i)] = Property(glb_mux, 3);
packed_cells.emplace(ci.name);
}
}
for (int i = 0; i < 4; i++) {
if (pll[i]) {
NetInfo *feedback_net = pll[i]->getPort(id_CLK_FEEDBACK);
int die = uarch->tile_extra_data(pll[i]->bel.tile)->die;
if (feedback_net) {
if (!global_signals.count(feedback_net)) {
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));
glbout[die]->params[ctx->idf("USR_FB%d_EN", i)] = Property(Property::State::S1);
} else {
int index = global_signals[feedback_net];
glbout[die]->params[ctx->idf("FB%d_CFG", i)] = Property(index, 2);
pll[i]->disconnectPort(id_CLK_FEEDBACK);
}
NetInfo *conn =
ctx->createNet(ctx->idf("%s_%s", glbout[die]->name.c_str(ctx), feedback_net->name.c_str(ctx)));
pll[i]->connectPort(id_CLK_FEEDBACK, conn);
glbout[die]->connectPort(ctx->idf("CLK_FB%d", i), conn);
for (auto &cell : uarch->pll) {
CellInfo &ci = *cell;
int i = ci.constr_z - 2;
NetInfo *clk = ci.getPort(id_CLK_REF);
int die = uarch->tile_extra_data(ci.bel.tile)->die;
if (clk) {
if (clk->driver.cell) {
auto pad_info = uarch->bel_to_pad[clk->driver.cell->bel];
uarch->clkin[die]->params[ctx->idf("REF%d", i)] = Property(pad_info->flags - 1, 3);
uarch->clkin[die]->params[ctx->idf("REF%d_INV", i)] = Property(Property::State::S0);
int index = pad_info->flags - 1;
if (!uarch->clkin[die]->getPort(ctx->idf("CLK%d", index)))
ci.movePortTo(id_CLK_REF, uarch->clkin[die], ctx->idf("CLK%d", index));
else
ci.disconnectPort(id_CLK_REF);
} else {
// SER_CLK
uarch->clkin[die]->params[ctx->idf("REF%d", i)] = Property(0b100, 3);
uarch->clkin[die]->params[ctx->idf("REF%d_INV", i)] = Property(Property::State::S0);
ci.movePortTo(id_CLK_REF, uarch->clkin[die], id_SER_CLK);
}
uarch->clkin[die]->connectPorts(ctx->idf("CLK_REF%d", i), &ci, id_CLK_REF);
}
NetInfo *feedback_net = ci.getPort(id_CLK_FEEDBACK);
if (feedback_net) {
if (!uarch->global_signals.count(feedback_net)) {
ci.movePortTo(id_CLK_FEEDBACK, uarch->glbout[die], ctx->idf("USR_FB%d", i));
move_ram_o_fixed(uarch->glbout[die], ctx->idf("USR_FB%d", i),
ctx->getBelLocation(uarch->glbout[die]->bel));
uarch->glbout[die]->params[ctx->idf("USR_FB%d_EN", i)] = Property(Property::State::S1);
} else {
int index = uarch->global_signals[feedback_net];
if ((index >> 2) != die)
log_error("TODO: Feedback signal from another die.\n");
uarch->glbout[die]->params[ctx->idf("FB%d_CFG", i)] = Property(index, 2);
ci.disconnectPort(id_CLK_FEEDBACK);
}
ci.connectPorts(id_CLK_FEEDBACK, uarch->glbout[die], ctx->idf("CLK_FB%d", i));
}
}
@ -268,13 +329,13 @@ void GateMatePacker::insert_clocking()
log_info("Insert clocking cells..\n");
for (int i = 0; i < uarch->dies; i++) {
Loc fixed_loc = uarch->locations[std::make_pair(id_CLKIN, i)];
clkin.push_back(create_cell_ptr(id_CLKIN, ctx->idf("CLKIN%d", i)));
uarch->clkin.push_back(create_cell_ptr(id_CLKIN, ctx->idf("CLKIN%d", i)));
BelId clkin_bel = ctx->getBelByLocation(fixed_loc);
ctx->bindBel(clkin_bel, clkin.back(), PlaceStrength::STRENGTH_FIXED);
glbout.push_back(create_cell_ptr(id_GLBOUT, ctx->idf("GLBOUT%d", i)));
ctx->bindBel(clkin_bel, uarch->clkin.back(), PlaceStrength::STRENGTH_FIXED);
uarch->glbout.push_back(create_cell_ptr(id_GLBOUT, ctx->idf("GLBOUT%d", i)));
fixed_loc = uarch->locations[std::make_pair(id_GLBOUT, i)];
BelId glbout_bel = ctx->getBelByLocation(fixed_loc);
ctx->bindBel(glbout_bel, glbout.back(), PlaceStrength::STRENGTH_FIXED);
ctx->bindBel(glbout_bel, uarch->glbout.back(), PlaceStrength::STRENGTH_FIXED);
}
}
@ -298,8 +359,8 @@ void GateMatePacker::remove_clocking()
}
}
};
remove_unused_cells(clkin);
remove_unused_cells(glbout);
remove_unused_cells(uarch->clkin);
remove_unused_cells(uarch->glbout);
flush_cells();
}
@ -317,7 +378,6 @@ static const char *timing_mode_to_str(int mode)
void GateMatePacker::pack_pll()
{
std::vector<int> pll_index(uarch->dies);
log_info("Packing PLLs..\n");
for (auto &cell : ctx->cells) {
CellInfo &ci = *cell.second;
@ -329,39 +389,18 @@ void GateMatePacker::pack_pll()
disconnect_if_gnd(&ci, id_CLK_FEEDBACK);
disconnect_if_gnd(&ci, id_USR_LOCKED_STDY_RST);
int die = uarch->preferred_die;
NetInfo *clk = ci.getPort(id_CLK_REF);
if (clk && clk->driver.cell) { // Only for GPIO pins
if (ctx->getBelBucketForCellType(clk->driver.cell->type) == id_CC_BUFG) {
clk = clk->driver.cell->getPort(id_I);
}
if (ctx->getBelBucketForCellType(clk->driver.cell->type) == id_IOSEL) {
auto pad_info = uarch->bel_to_pad[clk->driver.cell->bel];
if (pad_info->flags != 0) {
die = uarch->tile_extra_data(clk->driver.cell->bel.tile)->die;
}
}
}
if (pll_index[die] >= 4)
if (uarch->pll.size() >= (uarch->dies * 4U))
log_error("Used more than available PLLs.\n");
if (ci.getPort(id_CLK_REF) == nullptr && ci.getPort(id_USR_CLK_REF) == nullptr)
log_error("At least one reference clock (CLK_REF or USR_CLK_REF) must be set.\n");
log_error("At least one reference clock (CLK_REF or USR_CLK_REF) must be set for cell '%s'.\n",
ci.name.c_str(ctx));
if (ci.getPort(id_CLK_REF) != nullptr && ci.getPort(id_USR_CLK_REF) != nullptr)
log_error("CLK_REF and USR_CLK_REF are not allowed to be set in same time.\n");
log_error("CLK_REF and USR_CLK_REF are not allowed to be set in same time for cell '%s'.\n",
ci.name.c_str(ctx));
ci.cluster = ci.name;
ci.constr_abs_z = true;
ci.constr_z = 2 + pll_index[die]; // Position to a proper Z location
Loc fixed_loc = uarch->locations[std::make_pair(ctx->idf("PLL%d", pll_index[die]), die)];
BelId pll_bel = ctx->getBelByLocation(fixed_loc);
ctx->bindBel(pll_bel, &ci, PlaceStrength::STRENGTH_FIXED);
clk = ci.getPort(id_CLK_REF);
NetInfo *clk = ci.getPort(id_CLK_REF);
delay_t period = ctx->getDelayFromNS(1.0e9 / ctx->setting<float>("target_freq"));
if (clk) {
if (clk->driver.cell) {
@ -372,45 +411,36 @@ void GateMatePacker::pack_pll()
clk = in;
}
if (ctx->getBelBucketForCellType(clk->driver.cell->type) != id_IOSEL)
log_error("CLK_REF must be driven with GPIO pin.\n");
log_error("CLK_REF must be driven with GPIO pin for cell '%s'.\n", ci.name.c_str(ctx));
auto pad_info = uarch->bel_to_pad[clk->driver.cell->bel];
if (pad_info->flags == 0)
log_error("CLK_REF must be driven with CLK dedicated pin.\n");
clkin[die]->params[ctx->idf("REF%d", pll_index[die])] = Property(pad_info->flags - 1, 3);
clkin[die]->params[ctx->idf("REF%d_INV", pll_index[die])] = Property(Property::State::S0);
ci.movePortTo(id_CLK_REF, clkin[die], ctx->idf("CLK%d", pad_info->flags - 1));
log_error("CLK_REF must be driven with CLK dedicated pin for cell '%s'.\n", ci.name.c_str(ctx));
} else {
// SER_CLK
clkin[die]->params[ctx->idf("REF%d", pll_index[die])] = Property(0b100, 3);
clkin[die]->params[ctx->idf("REF%d_INV", pll_index[die])] = Property(Property::State::S0);
ci.movePortTo(id_CLK_REF, clkin[die], id_SER_CLK);
if (clk != net_SER_CLK)
log_error("CLK_REF connected to uknown pin for cell '%s'.\n", ci.name.c_str(ctx));
}
if (clk->clkconstr)
period = clk->clkconstr->period.minDelay();
NetInfo *conn = ctx->createNet(ctx->idf("%s_CLK_REF", ci.name.c_str(ctx)));
clkin[die]->connectPort(ctx->idf("CLK_REF%d", pll_index[die]), conn);
ci.connectPort(id_CLK_REF, conn);
}
clk = ci.getPort(id_USR_CLK_REF);
if (clk) {
move_ram_o_fixed(&ci, id_USR_CLK_REF, fixed_loc);
ci.params[ctx->id("USR_CLK_REF")] = Property(0b1, 1);
if (clk->driver.cell) {
if (ctx->getBelBucketForCellType(clk->driver.cell->type) == id_CC_BUFG) {
NetInfo *in = clk->driver.cell->getPort(id_I);
ci.disconnectPort(id_USR_CLK_REF);
ci.connectPort(id_USR_CLK_REF, in);
clk = in;
}
}
if (clk->clkconstr)
period = clk->clkconstr->period.minDelay();
}
if (ci.getPort(id_CLK_REF_OUT))
log_error("Output CLK_REF_OUT cannot be used if PLL is used.\n");
pll_out(&ci, id_CLK0, fixed_loc);
pll_out(&ci, id_CLK90, fixed_loc);
pll_out(&ci, id_CLK180, fixed_loc);
pll_out(&ci, id_CLK270, fixed_loc);
move_ram_i_fixed(&ci, id_USR_PLL_LOCKED, fixed_loc);
move_ram_i_fixed(&ci, id_USR_PLL_LOCKED_STDY, fixed_loc);
move_ram_o_fixed(&ci, id_USR_LOCKED_STDY_RST, fixed_loc);
log_error("Output CLK_REF_OUT cannot be used if PLL '%s' is used.\n", ci.name.c_str(ctx));
double out_clk_max = 0;
int clk270_doub = 0;
@ -450,18 +480,18 @@ void GateMatePacker::pack_pll()
double ref_clk = double_or_default(ci.params, id_REF_CLK, 0.0);
if (ref_clk <= 0 || ref_clk > 125)
log_error("REF_CLK parameter is out of range (0,125.00].\n");
log_error("REF_CLK parameter is out of range (0,125.00] for '%s'.\n", ci.name.c_str(ctx));
double out_clk = double_or_default(ci.params, id_OUT_CLK, 0.0);
if (out_clk <= 0 || out_clk > max_freq)
log_error("OUT_CLK parameter is out of range (0,%.2lf].\n", max_freq);
log_error("OUT_CLK parameter is out of range (0,%.2lf] for '%s'.\n", max_freq, ci.name.c_str(ctx));
if ((ci_const < 1) || (ci_const > 31)) {
log_warning("CI const out of range. Set to default CI = 2\n");
log_warning("CI const out of range. Set to default CI = 2 for '%s'\n", ci.name.c_str(ctx));
ci_const = 2;
}
if ((cp_const < 1) || (cp_const > 31)) {
log_warning("CP const out of range. Set to default CP = 4\n");
log_warning("CP const out of range. Set to default CP = 4 for '%s'\n", ci.name.c_str(ctx));
cp_const = 4;
}
// PLL_cfg_val_800_1400 PLL values from 11.08.2021
@ -578,7 +608,6 @@ void GateMatePacker::pack_pll()
ci.disconnectPort(id_USR_SEL_A_B);
} else {
ci.params[ctx->id("USR_SET")] = Property(0b1, 1);
move_ram_o_fixed(&ci, id_USR_SEL_A_B, fixed_loc);
}
ci.params[ctx->id("LOCK_REQ")] = Property(0b1, 1);
ci.unsetParam(id_PLL_CFG_A);
@ -615,7 +644,187 @@ void GateMatePacker::pack_pll()
ci.type = id_PLL;
pll_index[die]++;
uarch->pll.push_back(&ci);
}
}
void GateMatePacker::rewire_ram_o(CellInfo *first, IdString port, CellInfo *second)
{
NetInfo *net = first->getPort(port);
if (net && net->driver.cell) {
net = net->driver.cell->getPort(id_I);
if (net && net->driver.cell) {
uint8_t val = int_or_default(net->driver.cell->params, id_INIT_L00, 0);
switch (val) {
case LUT_ZERO:
net = net_PACKER_GND;
break;
case LUT_ONE:
net = net_PACKER_VCC;
break;
case LUT_D0:
net = net->driver.cell->getPort(id_IN1);
break;
default:
log_error("Unsupported config, rewire from '%s' port '%s'\n", first->name.c_str(ctx), port.c_str(ctx));
break;
}
second->connectPort(port, net);
} else {
log_error("Missing cell, rewire from '%s' port '%s'\n", first->name.c_str(ctx), port.c_str(ctx));
}
} else {
log_error("Missing cell, rewire from '%s' port '%s'\n", first->name.c_str(ctx), port.c_str(ctx));
}
}
void GateMatePacker::copy_clocks()
{
if (uarch->dies == 1)
return;
log_info("Copy clocks..\n");
// Save first CLKIN inputs
std::vector<CellInfo *> clk_iosel(4, nullptr);
bool use_ser_clk = false;
for (int i = 0; i < 4; i++) {
NetInfo *in_net = uarch->clkin[0]->getPort(ctx->idf("CLK%d", i));
if (in_net) {
if (in_net->driver.cell)
clk_iosel[i] = in_net->driver.cell;
else
use_ser_clk = true;
}
uarch->clkin[0]->disconnectPort(ctx->idf("CLK%d", i));
}
for (int new_die = 0; new_die < uarch->dies; new_die++) {
// Reconnect CLKIN and create appropriate GPIO and IOSEL cells
for (int i = 0; i < 4; i++) {
if (clk_iosel[i]) {
CellInfo *iosel = clk_iosel[i];
auto pad_info = uarch->bel_to_pad[iosel->bel];
Loc l = uarch->locations[std::make_pair(IdString(pad_info->package_pin), new_die)];
CellInfo *iosel_new = ctx->getBoundBelCell(ctx->getBelByLocation(l));
if (!iosel_new) {
iosel_new = create_cell_ptr(iosel->type, ctx->idf("%s$die%d", iosel->name.c_str(ctx), new_die));
iosel_new->params = iosel->params;
ctx->bindBel(ctx->getBelByLocation(l), iosel_new, PlaceStrength::STRENGTH_FIXED);
CellInfo *gpio = iosel->getPort(id_GPIO_IN)->driver.cell;
CellInfo *gpio_new =
create_cell_ptr(gpio->type, ctx->idf("%s$die%d", gpio->name.c_str(ctx), new_die));
gpio_new->params = gpio->params;
ctx->bindBel(ctx->getBelByLocation({l.x, l.y, 0}), gpio_new, PlaceStrength::STRENGTH_FIXED);
// Duplicate input connection
gpio_new->connectPort(id_I, gpio->getPort(id_I));
// Connect IOSEL and CPE_IBUF
gpio_new->connectPorts(id_Y, iosel_new, id_GPIO_IN);
}
if (iosel_new->getPort(id_IN1))
uarch->clkin[new_die]->connectPort(ctx->idf("CLK%d", i), iosel_new->getPort(id_IN1));
else
iosel_new->connectPorts(id_IN1, uarch->clkin[new_die], ctx->idf("CLK%d", i));
}
}
if (use_ser_clk)
uarch->clkin[new_die]->connectPort(id_SER_CLK, net_SER_CLK);
if (new_die != 0) {
// Copy configuration from first die to other dies
uarch->clkin[new_die]->params = uarch->clkin[0]->params;
uarch->glbout[new_die]->params = uarch->glbout[0]->params;
// Copy PLLs
for (int i = 0; i < 4; i++) {
Loc fixed_loc = uarch->locations[std::make_pair(ctx->idf("PLL%d", i), 0)];
BelId pll_bel = ctx->getBelByLocation(fixed_loc);
CellInfo *pll = ctx->getBoundBelCell(pll_bel);
if (pll) {
// Create new PLL
CellInfo *pll_new = create_cell_ptr(pll->type, ctx->idf("%s$die%d", pll->name.c_str(ctx), new_die));
pll_new->params = pll->params;
// Bind to new location
Loc new_loc = uarch->locations[std::make_pair(ctx->idf("PLL%d", i), new_die)];
BelId bel = ctx->getBelByLocation(new_loc);
ctx->bindBel(bel, pll_new, PlaceStrength::STRENGTH_FIXED);
if (pll->getPort(id_CLK_REF))
uarch->clkin[new_die]->connectPorts(ctx->idf("CLK_REF%d", i), pll_new, id_CLK_REF);
if (pll->getPort(id_CLK0))
pll_new->connectPorts(id_CLK0, uarch->glbout[new_die], ctx->idf("CLK0_%d", i));
if (pll->getPort(id_CLK90))
pll_new->connectPorts(id_CLK90, uarch->glbout[new_die], ctx->idf("CLK90_%d", i));
if (pll->getPort(id_CLK180))
pll_new->connectPorts(id_CLK180, uarch->glbout[new_die], ctx->idf("CLK180_%d", i));
if (pll->getPort(id_CLK270))
pll_new->connectPorts(id_CLK270, uarch->glbout[new_die], ctx->idf("CLK270_%d", i));
if (pll->getPort(id_USR_LOCKED_STDY_RST))
rewire_ram_o(pll, id_USR_LOCKED_STDY_RST, pll_new);
if (pll->getPort(id_USR_CLK_REF))
rewire_ram_o(pll, id_USR_CLK_REF, pll_new);
if (pll->getPort(id_USR_SEL_A_B))
rewire_ram_o(pll, id_USR_SEL_A_B, pll_new);
move_ram_o_fixed(pll_new, id_USR_LOCKED_STDY_RST, new_loc);
move_ram_o_fixed(pll_new, id_USR_CLK_REF, new_loc);
move_ram_o_fixed(pll_new, id_USR_SEL_A_B, new_loc);
// TODO: AND outputs of all USR_LOCKED_STDY_RST and use that signal to drive logic
}
}
// Copy GLBOUT inputs
for (int i = 0; i < 4; i++) {
Loc new_loc = uarch->locations[std::make_pair(id_GLBOUT, new_die)];
// Plain copy of user signals
NetInfo *net = uarch->glbout[0]->getPort(ctx->idf("USR_GLB%d", i));
if (net)
rewire_ram_o(uarch->glbout[0], ctx->idf("USR_GLB%d", i), uarch->glbout[new_die]);
net = uarch->glbout[0]->getPort(ctx->idf("USR_FB%d", i));
if (net)
rewire_ram_o(uarch->glbout[0], ctx->idf("USR_FB%d", i), uarch->glbout[new_die]);
move_ram_o_fixed(uarch->glbout[new_die], ctx->idf("USR_GLB%d", i), new_loc);
move_ram_o_fixed(uarch->glbout[new_die], ctx->idf("USR_FB%d", i), new_loc);
if (uarch->glbout[0]->getPort(ctx->idf("CLK_REF_OUT%d", i)))
uarch->clkin[new_die]->connectPorts(ctx->idf("CLK_REF%d", i), uarch->glbout[new_die],
ctx->idf("CLK_REF_OUT%d", i));
}
}
}
}
void GateMatePacker::reassign_clocks()
{
if (uarch->dies == 1)
return;
log_info("Reassign clocks..\n");
std::vector<std::vector<NetInfo *>> new_bufg(uarch->dies, std::vector<NetInfo *>(4));
for (auto &glob : uarch->global_signals) {
const NetInfo *net = glob.first;
int index = glob.second;
int drv_die = uarch->tile_extra_data(net->driver.cell->bel.tile)->die;
auto users = net->users; // make a copy
int count = 0;
for (auto &user : users) {
int cell_die = uarch->tile_extra_data(user.cell->bel.tile)->die;
if (cell_die != drv_die) {
if (!new_bufg[cell_die][index]) {
NetInfo *new_signal = ctx->createNet(ctx->idf("%s$die%d", net->name.c_str(ctx), cell_die));
new_bufg[cell_die][index] = new_signal;
uarch->glbout[cell_die]->connectPort(ctx->idf("GLB%d", index), new_signal);
copy_constraint(net, new_signal);
}
user.cell->disconnectPort(user.port);
user.cell->connectPort(user.port, new_bufg[cell_die][index]);
count++;
}
}
if (count)
log_info(" reassign %d net '%s' users\n", count, net->name.c_str(ctx));
}
}

View File

@ -173,6 +173,7 @@ void GateMatePacker::pack_cpe()
auto merge_dff = [&](CellInfo &ci, CellInfo *dff) {
dff->cluster = ci.name;
dff->region = ci.region;
dff->constr_abs_z = false;
dff->constr_z = +2;
ci.cluster = ci.name;
@ -253,6 +254,7 @@ void GateMatePacker::pack_cpe()
CellInfo *lower = create_cell_ptr(id_CPE_L2T4, ctx->idf("%s$lower", ci.name.c_str(ctx)));
ci.constr_children.push_back(lower);
lower->cluster = ci.name;
lower->region = ci.region;
lower->constr_abs_z = true;
lower->constr_z = CPE_LT_L_Z;
lower->params[id_INIT_L20] = Property(LUT_D0, 4);
@ -280,6 +282,7 @@ void GateMatePacker::pack_cpe()
for (auto ci : l2t5_list) {
CellInfo *upper = create_cell_ptr(id_CPE_L2T4, ctx->idf("%s$upper", ci->name.c_str(ctx)));
upper->cluster = ci->name;
upper->region = ci->region;
upper->constr_abs_z = true;
upper->constr_z = CPE_LT_U_Z;
ci->movePortTo(id_I4, upper, id_IN1);
@ -334,6 +337,7 @@ void GateMatePacker::pack_cpe()
CellInfo *upper = create_cell_ptr(id_CPE_LT_U, ctx->idf("%s$upper", ci.name.c_str(ctx)));
upper->cluster = ci.name;
upper->region = ci.region;
upper->constr_abs_z = false;
upper->constr_z = -1;
upper->params[id_INIT_L10] = Property(select, 4); // Selection bits
@ -365,6 +369,7 @@ void GateMatePacker::pack_cpe()
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->region = ci.region;
lt->constr_abs_z = false;
lt->constr_z = -2;
ci.cluster = ci.name;
@ -515,6 +520,7 @@ void GateMatePacker::pack_addf()
CellInfo *dff = net_only_drives(ctx, o, is_dff, id_D, true);
if (dff && are_ffs_compatible(dff, other)) {
dff->cluster = cell->cluster;
dff->region = cell->region;
dff->constr_abs_z = false;
dff->constr_z = +2;
cell->constr_children.push_back(dff);
@ -534,6 +540,7 @@ void GateMatePacker::pack_addf()
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);
ci_upper->cluster = root->name;
ci_upper->region = root->region;
ci_upper->constr_abs_z = false;
ci_upper->constr_z = -1;
ci_upper->constr_y = -1;
@ -541,6 +548,7 @@ void GateMatePacker::pack_addf()
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);
ci_lower->cluster = root->name;
ci_lower->region = root->region;
ci_lower->constr_abs_z = false;
ci_lower->constr_y = -1;
ci_lower->params[id_INIT_L00] = Property(LUT_ZERO, 4);
@ -551,6 +559,7 @@ void GateMatePacker::pack_addf()
ci_cplines->params[id_C_CY1_I] = Property(1, 1);
root->constr_children.push_back(ci_cplines);
ci_cplines->cluster = root->name;
ci_cplines->region = root->region;
ci_cplines->constr_abs_z = true;
ci_cplines->constr_y = -1;
ci_cplines->constr_z = CPE_CPLINES_Z;
@ -580,6 +589,7 @@ void GateMatePacker::pack_addf()
CellInfo *cy = grp.at(i);
if (i != 0) {
cy->cluster = root->name;
cy->region = root->region;
root->constr_children.push_back(cy);
cy->constr_abs_z = false;
cy->constr_y = +i;
@ -602,6 +612,7 @@ void GateMatePacker::pack_addf()
CellInfo *upper = create_cell_ptr(id_CPE_LT_U, ctx->idf("%s$upper", cy->name.c_str(ctx)));
upper->cluster = root->name;
upper->region = root->region;
root->constr_children.push_back(upper);
upper->constr_abs_z = false;
upper->constr_y = +i;
@ -625,12 +636,14 @@ void GateMatePacker::pack_addf()
break;
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->region = root->region;
root->constr_children.push_back(co_upper);
co_upper->constr_abs_z = false;
co_upper->constr_z = -1;
co_upper->constr_y = +i + 1;
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->region = root->region;
root->constr_children.push_back(co_lower);
co_lower->constr_abs_z = false;
co_lower->constr_y = +i + 1;
@ -678,6 +691,7 @@ void GateMatePacker::pack_constants()
h.replace_constants(CellTypePort(id_CPE_L2T4, id_OUT), CellTypePort(id_CPE_L2T4, id_OUT), vcc_params, gnd_params);
net_PACKER_VCC = ctx->nets.at(ctx->id("$PACKER_VCC")).get();
net_PACKER_GND = ctx->nets.at(ctx->id("$PACKER_GND")).get();
net_SER_CLK = nullptr;
}
void GateMatePacker::remove_constants()
@ -719,6 +733,7 @@ std::pair<CellInfo *, CellInfo *> GateMatePacker::move_ram_i(CellInfo *cell, IdS
if (place) {
cell->constr_children.push_back(cpe_ramio);
cpe_ramio->cluster = cell->cluster;
cpe_ramio->region = cell->region;
cpe_ramio->constr_abs_z = false;
cpe_ramio->constr_z = PLACE_DB_CONSTR + origPort.index;
} else {
@ -730,6 +745,7 @@ std::pair<CellInfo *, CellInfo *> GateMatePacker::move_ram_i(CellInfo *cell, IdS
if (place) {
cpe_ramio->constr_children.push_back(cpe_half);
cpe_half->cluster = cell->cluster;
cpe_half->region = cell->region;
cpe_half->constr_abs_z = false;
cpe_half->constr_z = -4;
} else {
@ -757,6 +773,7 @@ std::pair<CellInfo *, CellInfo *> GateMatePacker::move_ram_o(CellInfo *cell, IdS
if (place) {
cell->constr_children.push_back(cpe_ramio);
cpe_ramio->cluster = cell->cluster;
cpe_ramio->region = cell->region;
cpe_ramio->constr_abs_z = false;
cpe_ramio->constr_z = PLACE_DB_CONSTR + origPort.index;
} else {
@ -767,6 +784,7 @@ std::pair<CellInfo *, CellInfo *> GateMatePacker::move_ram_o(CellInfo *cell, IdS
if (place) {
cpe_ramio->constr_children.push_back(cpe_half);
cpe_half->cluster = cell->cluster;
cpe_half->region = cell->region;
cpe_half->constr_abs_z = false;
cpe_half->constr_z = -4;
} else {
@ -815,6 +833,7 @@ std::pair<CellInfo *, CellInfo *> GateMatePacker::move_ram_io(CellInfo *cell, Id
if (place) {
cell->constr_children.push_back(cpe_ramio);
cpe_ramio->cluster = cell->cluster;
cpe_ramio->region = cell->region;
cpe_ramio->constr_abs_z = false;
cpe_ramio->constr_z = PLACE_DB_CONSTR + oPort.index;
} else {
@ -826,6 +845,7 @@ std::pair<CellInfo *, CellInfo *> GateMatePacker::move_ram_io(CellInfo *cell, Id
if (place) {
cpe_ramio->constr_children.push_back(cpe_half);
cpe_half->cluster = cell->cluster;
cpe_half->region = cell->region;
cpe_half->constr_abs_z = false;
cpe_half->constr_z = -4;
} else {

View File

@ -142,6 +142,7 @@ void GateMatePacker::pack_io()
s.cell->disconnectPort(s.port);
s.cell->connectPort(s.port, ser_clk);
}
net_SER_CLK = ser_clk;
ci.disconnectPort(id_I);
packed_cells.emplace(ci.name);
continue;
@ -302,7 +303,6 @@ void GateMatePacker::pack_io()
uarch->available_pads.erase(id);
loc = id.c_str(ctx);
}
ci.params[id_LOC] = Property(loc);
BelId bel;
if (uarch->locations.count(std::make_pair(ctx->id(loc), uarch->preferred_die)))
@ -370,12 +370,12 @@ void GateMatePacker::pack_io_sel()
} else if (clk_net == net_PACKER_VCC) {
cell->disconnectPort(id_CLK);
} else {
if (!global_signals.count(clk_net)) {
if (!uarch->global_signals.count(clk_net)) {
cell->movePortTo(id_CLK, target, id_OUT4);
target->params[id_SEL_OUT_CLOCK] = Property(Property::State::S1);
return true;
} else {
int index = global_signals[clk_net];
int index = uarch->global_signals[clk_net];
cell->movePortTo(id_CLK, target, ctx->idf("CLOCK%d", index + 1));
target->params[id_OUT_CLOCK] = Property(index, 2);
}
@ -391,11 +391,11 @@ void GateMatePacker::pack_io_sel()
} else if (clk_net == net_PACKER_VCC) {
cell->disconnectPort(id_CLK);
} else {
if (!global_signals.count(clk_net)) {
if (!uarch->global_signals.count(clk_net)) {
cell->movePortTo(id_CLK, target, id_OUT4);
target->params[id_SEL_IN_CLOCK] = Property(Property::State::S1);
} else {
int index = global_signals[clk_net];
int index = uarch->global_signals[clk_net];
cell->movePortTo(id_CLK, target, ctx->idf("CLOCK%d", index + 1));
target->params[id_IN_CLOCK] = Property(index, 2);
}
@ -406,7 +406,7 @@ void GateMatePacker::pack_io_sel()
auto merge_ibf = [&](NetInfo *di_net, CellInfo &ci, bool use_custom_clock) -> bool {
CellInfo *dff = (*di_net->users.begin()).cell;
if (is_gpio_valid_dff(dff)) {
if (!global_signals.count(ci.getPort(id_CLK)) && use_custom_clock) {
if (!uarch->global_signals.count(ci.getPort(id_CLK)) && use_custom_clock) {
log_warning("Found DFF %s cell, but not enough CLK signals.\n", dff->name.c_str(ctx));
return false;
}
@ -431,7 +431,7 @@ void GateMatePacker::pack_io_sel()
auto merge_iddr = [&](NetInfo *di_net, CellInfo &ci, bool use_custom_clock) -> bool {
CellInfo *iddr = (*di_net->users.begin()).cell;
if (!global_signals.count(ci.getPort(id_CLK)) && use_custom_clock) {
if (!uarch->global_signals.count(ci.getPort(id_CLK)) && use_custom_clock) {
log_warning("Found IDDR %s cell, but not enough CLK signals.\n", iddr->name.c_str(ctx));
return false;
}
@ -454,6 +454,9 @@ void GateMatePacker::pack_io_sel()
return true;
};
// Invert CPE out for output enable (OUT3)
bool is_inverted[4] = {false, false, true, false};
for (auto &cell : cells) {
CellInfo &ci = *cell;
bool ff_obf = bool_or_default(ci.params, id_FF_OBF, 0);
@ -467,8 +470,6 @@ void GateMatePacker::pack_io_sel()
}
ci.cluster = ci.name;
std::string loc = str_or_default(ci.params, id_LOC, "UNPLACED");
ci.unsetParam(id_LOC);
NetInfo *do_net = ci.getPort(id_A);
bool use_custom_clock = false;
@ -503,6 +504,16 @@ void GateMatePacker::pack_io_sel()
}
}
bool oddr_merged = false;
if (do_net->driver.cell && do_net->driver.cell->type == id_CC_LUT1 && do_net->users.entries() == 1) {
NetInfo *net = do_net->driver.cell->getPort(id_I0);
if (net->driver.cell && net->driver.cell->type == id_CC_ODDR && net->users.entries() == 1) {
do_net = net;
packed_cells.insert(net->driver.cell->name);
// Inverting both input is equal to inverter at output
is_inverted[0] = true;
is_inverted[1] = true;
}
}
if (do_net->driver.cell && do_net->driver.cell->type == id_CC_ODDR && do_net->users.entries() == 1) {
CellInfo *oddr = do_net->driver.cell;
ci.params[id_OUT1_FF] = Property(Property::State::S1);
@ -512,7 +523,7 @@ void GateMatePacker::pack_io_sel()
ci.disconnectPort(id_A);
oddr->movePortTo(id_D0, &ci, id_OUT2);
oddr->movePortTo(id_D1, &ci, id_OUT1);
const auto &pad = ctx->get_package_pin(ctx->id(loc));
const auto &pad = uarch->bel_to_pad[ci.bel];
int die = uarch->tile_extra_data(ci.bel.tile)->die;
auto [cpe_half, cpe_ramio] = ddr[die][pad->pad_bank];
if (cpe_half) {
@ -558,8 +569,8 @@ void GateMatePacker::pack_io_sel()
Loc root_loc = ctx->getBelLocation(ci.bel);
for (int i = 0; i < 4; i++) {
CellInfo *cpe = move_ram_o_fixed(&ci, ctx->idf("OUT%d", i + 1), root_loc).first;
if (cpe && i == 2)
cpe->params[id_INIT_L10] = Property(LUT_INV_D0, 4); // Invert CPE out for output enable (OUT3)
if (cpe && is_inverted[i])
cpe->params[id_INIT_L10] = Property(LUT_INV_D0, 4);
}
}
flush_cells();

View File

@ -573,6 +573,7 @@ void GateMatePacker::pack_mult()
// we also constrain it to proper Z location
auto *root = m.cols[0].b_passthru.upper;
root->cluster = root->name;
root->region = mult->region;
root->constr_abs_z = true;
root->constr_z = CPE_LT_U_Z;
@ -581,6 +582,7 @@ void GateMatePacker::pack_mult()
return;
root->constr_children.push_back(cell);
cell->cluster = root->name;
cell->region = root->region;
cell->constr_abs_z = true;
cell->constr_x = x_offset;
cell->constr_y = y_offset;

View File

@ -49,11 +49,11 @@ TEST_F(GateMateTest, remove_lut1_zero)
CellInfo *obuf = create_cell_ptr(id_CC_OBUF, "obuf");
lut1->connectPorts(id_O, obuf, id_A);
direct_connect(lut1, id_O, obuf, id_A);
ASSERT_EQ(ctx->cells.size(), 2LU);
ctx->uarch->pack();
ASSERT_EQ(ctx->cells.size(), 1LU);
ASSERT_EQ(ctx->cells.size(), 4LU);
}
TEST_F(GateMateTest, remove_lut1_one)
@ -63,11 +63,11 @@ TEST_F(GateMateTest, remove_lut1_one)
CellInfo *obuf = create_cell_ptr(id_CC_OBUF, "obuf");
lut1->connectPorts(id_O, obuf, id_A);
direct_connect(lut1, id_O, obuf, id_A);
ASSERT_EQ(ctx->cells.size(), 2LU);
ctx->uarch->pack();
ASSERT_EQ(ctx->cells.size(), 1LU);
ASSERT_EQ(ctx->cells.size(), 4LU);
}
TEST_F(GateMateTest, remove_lut1_pass)
@ -78,14 +78,14 @@ TEST_F(GateMateTest, remove_lut1_pass)
CellInfo *obuf = create_cell_ptr(id_CC_OBUF, "obuf");
CellInfo *ibuf = create_cell_ptr(id_CC_IBUF, "ibuf");
ibuf->connectPorts(id_Y, lut1, id_I0);
lut1->connectPorts(id_O, obuf, id_A);
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(), 4LU);
ASSERT_EQ(ctx->cells.size(), 8LU);
}
TEST_F(GateMateTest, remove_lut1_inv)
@ -96,14 +96,14 @@ TEST_F(GateMateTest, remove_lut1_inv)
CellInfo *obuf = create_cell_ptr(id_CC_OBUF, "obuf");
CellInfo *ibuf = create_cell_ptr(id_CC_IBUF, "ibuf");
ibuf->connectPorts(id_Y, lut1, id_I0);
lut1->connectPorts(id_O, obuf, id_A);
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(), 5LU);
ASSERT_EQ(ctx->cells.size(), 9LU);
}
TEST_F(GateMateTest, remove_lut1_not_driven)
@ -123,5 +123,5 @@ TEST_F(GateMateTest, remove_lut1_not_driven)
ctx->uarch->pack();
// Expect IBUF -> CPE -> OBUF
// LUT1 removed as not used, but CPE for driving OBUF added
ASSERT_EQ(ctx->cells.size(), 4LU);
ASSERT_EQ(ctx->cells.size(), 8LU);
}

View File

@ -201,4 +201,11 @@ CellInfo *GateMateTest::create_cell_ptr(IdString type, std::string name)
return cell;
}
void GateMateTest::direct_connect(CellInfo *o_cell, IdString o_port, CellInfo *i_cell, IdString i_port)
{
NetInfo *net = ctx->createNet(ctx->idf("%s_%s", o_cell->name.c_str(ctx), o_port.c_str(ctx)));
o_cell->connectPort(o_port, net);
i_cell->connectPort(i_port, net);
}
NEXTPNR_NAMESPACE_END

View File

@ -34,6 +34,7 @@ class GateMateTest : public ::testing::Test
virtual void TearDown() override;
CellInfo *create_cell_ptr(IdString type, std::string name);
void direct_connect(CellInfo *o_cell, IdString o_port, CellInfo *i_cell, IdString i_port);
ArchArgs chipArgs;
Context *ctx;