gatemate: support multiple clock distribution strategies

This commit is contained in:
Miodrag Milanovic 2025-10-10 15:35:27 +02:00
parent 5d45520bb2
commit cbf3969dd0
7 changed files with 213 additions and 15 deletions

View File

@ -921,8 +921,10 @@ class HeAPPlacer
y0 = std::max(y0, r.y0);
x1 = std::min(x1, r.x1);
y1 = std::min(y1, r.y1);
if (x0 > x1) std::swap(x0, x1);
if (y0 > y1) std::swap(y0, y1);
if (x0 > x1)
std::swap(x0, x1);
if (y0 > y1)
std::swap(y0, y1);
}
// Pick a random X and Y location within our search radius / search box

View File

@ -125,6 +125,11 @@ CellInfo *GateMatePacker::create_cell_ptr(IdString type, IdString name)
} else if (type.in(id_CPE_IBUF)) {
add_port(id_Y, PORT_OUT);
add_port(id_I, PORT_IN);
} else if (type.in(id_CPE_IOBUF)) {
add_port(id_Y, PORT_OUT);
add_port(id_T, PORT_IN);
add_port(id_A, PORT_IN);
add_port(id_IO, PORT_INOUT);
} else if (type.in(id_PLL)) {
add_port(id_CLK_REF, PORT_IN);
add_port(id_USR_CLK_REF, PORT_IN);

View File

@ -91,9 +91,12 @@ struct GateMateImpl : HimbaechelAPI
int fpga_mode;
int timing_mode;
std::map<const NetInfo *, int> global_signals;
dict<std::pair<IdString, int>, NetInfo *> global_mapping;
dict<std::pair<IdString, int>, IdString> global_clk_mapping;
std::vector<CellInfo *> clkin;
std::vector<CellInfo *> glbout;
std::vector<CellInfo *> pll;
pool<IdString> ignore;
private:
bool getChildPlacement(const BaseClusterInfo *cluster, Loc root_loc,

View File

@ -404,10 +404,32 @@ void GateMateImpl::pack()
parse_ccf(args.options.at("ccf"));
}
MultiDieStrategy strategy;
if (args.options.count("multi")) {
std::string val = args.options.at("multi");
if (val == "mirror") {
strategy = MultiDieStrategy::CLOCK_MIRROR;
log_info("Multidie mode: CLOCK MIRROR\n");
} else if (val == "clk1") {
strategy = MultiDieStrategy::REUSE_CLK1;
log_info("Multidie mode: REUSE CLK1\n");
} else {
log_error("Unknown value for 'multi' option. Allowed values are 'mirror' and 'clk1'.\n");
}
} else {
strategy = MultiDieStrategy::CLOCK_MIRROR;
if (dies != 1)
log_warning("Multi die clock placement strategy set to 'mirror'.\n");
}
if (forced_die != IdString())
preferred_die = die_to_index[forced_die];
if (strategy == MultiDieStrategy::REUSE_CLK1)
preferred_die = 0;
GateMatePacker packer(ctx, this);
packer.set_strategy(strategy);
packer.pack_constants();
packer.cleanup();
packer.pack_io();

View File

@ -44,6 +44,12 @@ enum CPELut
LUT_ONE = 0b1111
};
enum MultiDieStrategy
{
CLOCK_MIRROR,
REUSE_CLK1,
};
struct GateMatePacker
{
GateMatePacker(Context *ctx, GateMateImpl *uarch) : ctx(ctx), uarch(uarch) { h.init(ctx); };
@ -72,6 +78,8 @@ struct GateMatePacker
void reassign_clocks();
void copy_clocks();
void set_strategy(MultiDieStrategy strategy) { this->strategy = strategy; }
private:
void rename_param(CellInfo *cell, IdString name, IdString new_name, int width);
void dff_to_cpe(CellInfo *dff);
@ -88,6 +96,9 @@ struct GateMatePacker
void move_connections(NetInfo *from_net, NetInfo *to_net);
void remap_ram_half(CellInfo *half, CellInfo *cell, int num);
void strategy_mirror();
void strategy_clk1();
PllCfgRecord get_pll_settings(double f_ref, double f_core, int mode, int low_jitter, bool pdiv0_mux, bool feedback);
std::pair<CellInfo *, CellInfo *> move_ram_i(CellInfo *cell, IdString origPort, bool place = true,
@ -121,6 +132,7 @@ struct GateMatePacker
NetInfo *net_SER_CLK;
int count;
std::map<IdString, int> count_per_type;
MultiDieStrategy strategy;
};
NEXTPNR_NAMESPACE_END

View File

@ -682,7 +682,70 @@ void GateMatePacker::copy_clocks()
{
if (uarch->dies == 1)
return;
log_info("Copy clocks..\n");
switch (strategy) {
case MultiDieStrategy::REUSE_CLK1:
strategy_clk1();
break;
case MultiDieStrategy::CLOCK_MIRROR:
strategy_mirror();
break;
}
}
void GateMatePacker::strategy_clk1()
{
log_info("Reuse CLK1 for clock distribution..\n");
NetInfo *net = uarch->glbout[0]->getPort(id_GLB0);
NetInfo *new_clk1 = ctx->createNet(ctx->id("$clk1$pin"));
for (int new_die = 0; new_die < uarch->dies; new_die++) {
CellInfo *iosel = create_cell_ptr(id_IOSEL, ctx->idf("$iosel_clk1$die%d", new_die));
iosel->setParam(id_DELAY_IBF, Property(1, 16));
iosel->setParam(id_INPUT_ENABLE, Property(1, 1));
if (new_die == 0) {
// On die 0 it should be output as well
iosel->setParam(id_DELAY_OBF, Property(1, 16));
iosel->setParam(id_OE_ENABLE, Property(1, 1));
iosel->setParam(id_OUT_SIGNAL, Property(1, 1));
iosel->setParam(id_SLEW, Property(1, 1));
}
BelId bel = ctx->getBelByLocation(uarch->locations[std::make_pair(ctx->id("IO_SB_A7"), new_die)]);
ctx->bindBel(bel, iosel, PlaceStrength::STRENGTH_FIXED);
CellInfo *gpio = create_cell_ptr((new_die ? id_CPE_IBUF : id_CPE_IOBUF), ctx->idf("$clk1$die%d", new_die));
Loc loc = ctx->getBelLocation(bel);
ctx->bindBel(ctx->getBelByLocation({loc.x, loc.y, 0}), gpio, PlaceStrength::STRENGTH_FIXED);
uarch->clkin[new_die]->connectPort(id_CLK1, new_clk1);
uarch->clkin[new_die]->params[ctx->id("REF1")] = Property(1, 3);
uarch->glbout[new_die]->params[ctx->id("GLB1_EN")] = Property(Property::State::S1);
uarch->glbout[new_die]->params[ctx->id("GLB1_CFG")] = Property(0, 3);
uarch->clkin[new_die]->connectPorts(ctx->id("CLK_REF1"), uarch->glbout[new_die], ctx->id("CLK_REF_OUT1"));
gpio->connectPorts(id_Y, iosel, id_GPIO_IN);
if (new_die == 0) {
iosel->connectPort(id_OUT1, ctx->getNetByAlias(uarch->global_signals.begin()->first->name));
CellInfo *cpe = move_ram_o_fixed(iosel, id_OUT1, loc).first;
uarch->ignore.emplace(cpe->name);
iosel->connectPorts(id_GPIO_OUT, gpio, id_A);
iosel->connectPorts(id_GPIO_EN, gpio, id_T);
gpio->connectPort(id_IO, new_clk1);
} else
gpio->connectPort(id_I, new_clk1);
NetInfo *new_signal = ctx->createNet(ctx->idf("%s$die%d", net->name.c_str(ctx), new_die));
uarch->glbout[new_die]->connectPort(ctx->id("GLB1"), new_signal);
copy_constraint(net, new_signal);
uarch->global_mapping.emplace(std::make_pair(net->name, new_die), new_signal);
uarch->global_clk_mapping.emplace(std::make_pair(id_CLOCK1, new_die), id_CLOCK2);
}
}
void GateMatePacker::strategy_mirror()
{
log_info("Mirror clocks..\n");
// Save first CLKIN inputs
std::vector<CellInfo *> clk_iosel(4, nullptr);
@ -793,8 +856,52 @@ void GateMatePacker::copy_clocks()
ctx->idf("CLK_REF_OUT%d", i));
}
}
for (int i = 0; i < 4; i++) {
NetInfo *net = uarch->glbout[0]->getPort(ctx->idf("GLB%d", i));
if (net) {
if (new_die != 0) {
NetInfo *new_signal = ctx->createNet(ctx->idf("%s$die%d", net->name.c_str(ctx), new_die));
uarch->glbout[new_die]->connectPort(ctx->idf("GLB%d", i), new_signal);
copy_constraint(net, new_signal);
uarch->global_mapping.emplace(std::make_pair(net->name, new_die), new_signal);
} else {
uarch->global_mapping.emplace(std::make_pair(net->name, new_die), net);
}
}
}
}
}
static int clk_config_val(IdString name)
{
switch (name.index) {
case id_CLOCK1.index:
return 0b00100011;
case id_CLOCK2.index:
return 0b00110011;
case id_CLOCK3.index:
return 0b00000011;
case id_CLOCK4.index:
return 0b00010011;
}
return 0;
}
static int ioclk_config_val(IdString name)
{
switch (name.index) {
case id_CLOCK1.index:
return 0;
case id_CLOCK2.index:
return 1;
case id_CLOCK3.index:
return 2;
case id_CLOCK4.index:
return 3;
}
return 0;
}
void GateMatePacker::reassign_clocks()
{
if (uarch->dies == 1)
@ -805,22 +912,55 @@ void GateMatePacker::reassign_clocks()
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);
}
if (uarch->global_mapping.count(std::make_pair(net->name, cell_die))) {
NetInfo *new_net = uarch->global_mapping.at(std::make_pair(net->name, cell_die));
if (uarch->ignore.count(user.cell->name))
continue;
if (new_net == net)
continue;
user.cell->disconnectPort(user.port);
user.cell->connectPort(user.port, new_bufg[cell_die][index]);
if (user.port.in(id_CLOCK1, id_CLOCK2, id_CLOCK3, id_CLOCK4) &&
uarch->global_clk_mapping.count(std::make_pair(user.port, cell_die))) {
IdString newPort = uarch->global_clk_mapping.at(std::make_pair(user.port, cell_die));
if (!user.cell->ports.count(newPort))
user.cell->addInput(newPort);
user.cell->connectPort(newPort, new_net);
if (user.cell->type == id_RAM) {
int a0_clk = int_or_default(user.cell->params, id_RAM_cfg_forward_a0_clk, 0);
int a1_clk = int_or_default(user.cell->params, id_RAM_cfg_forward_a1_clk, 0);
int b0_clk = int_or_default(user.cell->params, id_RAM_cfg_forward_b0_clk, 0);
int b1_clk = int_or_default(user.cell->params, id_RAM_cfg_forward_b1_clk, 0);
if (a0_clk == clk_config_val(user.port))
user.cell->params[id_RAM_cfg_forward_a0_clk] = Property(clk_config_val(newPort), 8);
if (a1_clk == clk_config_val(user.port))
user.cell->params[id_RAM_cfg_forward_a1_clk] = Property(clk_config_val(newPort), 8);
if (b0_clk == clk_config_val(user.port))
user.cell->params[id_RAM_cfg_forward_b0_clk] = Property(clk_config_val(newPort), 8);
if (b1_clk == clk_config_val(user.port))
user.cell->params[id_RAM_cfg_forward_b1_clk] = Property(clk_config_val(newPort), 8);
}
if (user.cell->type == id_IOSEL) {
int in_clk = int_or_default(user.cell->params, id_IN_CLOCK, 0);
int out_clk = int_or_default(user.cell->params, id_OUT_CLOCK, 0);
if (in_clk == ioclk_config_val(user.port))
user.cell->params[id_IN_CLOCK] = Property(ioclk_config_val(newPort), 2);
if (out_clk == ioclk_config_val(user.port))
user.cell->params[id_OUT_CLOCK] = Property(ioclk_config_val(newPort), 2);
}
} else
user.cell->connectPort(user.port, new_net);
count++;
} else {
log_error("Global signal '%s' is not available in die %d.\n", net->name.c_str(ctx), cell_die);
}
}
if (count)

View File

@ -376,7 +376,14 @@ void GateMatePacker::pack_io_sel()
return true;
} else {
int index = uarch->global_signals[clk_net];
cell->movePortTo(id_CLK, target, ctx->idf("CLOCK%d", index + 1));
NetInfo *net = target->getPort(ctx->idf("CLOCK%d", index + 1));
if (net) {
if (net != clk_net)
log_error("Not able to connected different CLK signal to cell '%s'.\n",
cell->name.c_str(ctx));
} else {
cell->movePortTo(id_CLK, target, ctx->idf("CLOCK%d", index + 1));
}
target->params[id_OUT_CLOCK] = Property(index, 2);
}
}
@ -396,7 +403,14 @@ void GateMatePacker::pack_io_sel()
target->params[id_SEL_IN_CLOCK] = Property(Property::State::S1);
} else {
int index = uarch->global_signals[clk_net];
cell->movePortTo(id_CLK, target, ctx->idf("CLOCK%d", index + 1));
NetInfo *net = target->getPort(ctx->idf("CLOCK%d", index + 1));
if (net) {
if (net != clk_net)
log_error("Not able to connected different CLK signal to cell '%s'.\n",
cell->name.c_str(ctx));
} else {
cell->movePortTo(id_CLK, target, ctx->idf("CLOCK%d", index + 1));
}
target->params[id_IN_CLOCK] = Property(index, 2);
}
}