ecp5: Add support for clock gating with DCCA

Signed-off-by: David Shah <dave@ds0.me>
This commit is contained in:
David Shah 2019-08-31 10:45:12 +01:00
parent cce5cb65c1
commit 04be9a71f9
2 changed files with 87 additions and 39 deletions

View File

@ -892,7 +892,35 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex
if (datamux_mddr != "PADDO")
cc.tiles[pic_tile].add_enum(pio + ".DATAMUX_MDDR", datamux_mddr);
} else if (ci->type == ctx->id("DCCA")) {
// Nothing to do
const NetInfo *cen = get_net_or_empty(ci, ctx->id("CE"));
if (cen != nullptr) {
std::string belname = ctx->locInfo(bel)->bel_data[bel.index].name.get();
Loc loc = ctx->getBelLocation(bel);
TileGroup tg;
switch (belname[0]) {
case 'B':
tg.tiles.push_back(
ctx->getTileByTypeAndLocation(loc.y, loc.x, std::set<std::string>{"BMID_0H", "BMID_0V"}));
tg.tiles.push_back(ctx->getTileByTypeAndLocation(loc.y, loc.x + 1,
std::set<std::string>{"BMID_2", "BMID_2V"}));
break;
case 'T':
tg.tiles.push_back(ctx->getTileByTypeAndLocation(loc.y, loc.x, "TMID_0"));
tg.tiles.push_back(ctx->getTileByTypeAndLocation(loc.y, loc.x + 1, "TMID_1"));
break;
case 'L':
tg.tiles.push_back(ctx->getTileByTypeAndLocation(loc.y, loc.x, "LMID_0"));
break;
case 'R':
tg.tiles.push_back(ctx->getTileByTypeAndLocation(loc.y, loc.x, "RMID_0"));
break;
default:
NPNR_ASSERT_FALSE("bad DCC for gating");
break;
}
tg.config.add_enum(std::string("DCC_") + belname[0] + belname.substr(4) + ".MODE", "DCCA");
cc.tilegroups.push_back(tg);
}
} else if (ci->type == ctx->id("DP16KD")) {
TileGroup tg;
Loc loc = ctx->getBelLocation(ci->bel);

View File

@ -78,7 +78,18 @@ class Ecp5GlobalRouter
}
// log_info("clkcount %s: %d\n", ni->name.c_str(ctx),clockCount[ni->name]);
}
// DCCAs must always drive globals
std::vector<NetInfo *> clocks;
for (auto &cell : sorted(ctx->cells)) {
CellInfo *ci = cell.second;
if (ci->type == id_DCCA) {
NetInfo *glb = ci->ports.at(id_CLKO).net;
if (glb != nullptr) {
clocks.push_back(glb);
clockCount.erase(glb->name);
}
}
}
while (clocks.size() < 16) {
auto max = std::max_element(clockCount.begin(), clockCount.end(),
[](const decltype(clockCount)::value_type &a,
@ -355,10 +366,14 @@ class Ecp5GlobalRouter
void place_dcc(CellInfo *dcc)
{
BelId best_bel;
bool using_ce = get_net_or_empty(dcc, ctx->id("CE")) != nullptr;
wirelen_t best_wirelen = 9999999;
for (auto bel : ctx->getBels()) {
if (ctx->getBelType(bel) == id_DCCA && ctx->checkBelAvail(bel)) {
if (ctx->isValidBelForCell(dcc, bel)) {
std::string belname = ctx->locInfo(bel)->bel_data[bel.index].name.get();
if (belname.at(0) == 'D' && using_ce)
continue; // don't allow DCCs with CE at center
ctx->bindBel(bel, dcc, STRENGTH_LOCKED);
wirelen_t wirelen = get_dcc_wirelen(dcc);
if (wirelen < best_wirelen) {
@ -376,46 +391,51 @@ class Ecp5GlobalRouter
// Insert a DCC into a net to promote it to a global
NetInfo *insert_dcc(NetInfo *net)
{
auto dcc = create_ecp5_cell(ctx, id_DCCA, "$gbuf$" + net->name.str(ctx));
std::unique_ptr<NetInfo> glbnet = std::unique_ptr<NetInfo>(new NetInfo);
glbnet->name = ctx->id("$glbnet$" + net->name.str(ctx));
glbnet->driver.cell = dcc.get();
glbnet->driver.port = id_CLKO;
glbnet->attrs[ctx->id("ECP5_IS_GLOBAL")] = 1;
dcc->ports[id_CLKO].net = glbnet.get();
std::vector<PortRef> keep_users;
for (auto user : net->users) {
if (user.port == id_CLKFB) {
keep_users.push_back(user);
} else if (net->driver.cell->type == id_EXTREFB && user.cell->type == id_DCUA) {
keep_users.push_back(user);
} else {
glbnet->users.push_back(user);
user.cell->ports.at(user.port).net = glbnet.get();
NetInfo *glbptr = nullptr;
CellInfo *dccptr = nullptr;
if (net->driver.cell != nullptr && net->driver.cell->type == id_DCCA) {
// Already have a DCC (such as clock gating)
glbptr = net;
dccptr = net->driver.cell;
} else {
auto dcc = create_ecp5_cell(ctx, id_DCCA, "$gbuf$" + net->name.str(ctx));
std::unique_ptr<NetInfo> glbnet = std::unique_ptr<NetInfo>(new NetInfo);
glbnet->name = ctx->id("$glbnet$" + net->name.str(ctx));
glbnet->driver.cell = dcc.get();
glbnet->driver.port = id_CLKO;
dcc->ports[id_CLKO].net = glbnet.get();
std::vector<PortRef> keep_users;
for (auto user : net->users) {
if (user.port == id_CLKFB) {
keep_users.push_back(user);
} else if (net->driver.cell->type == id_EXTREFB && user.cell->type == id_DCUA) {
keep_users.push_back(user);
} else {
glbnet->users.push_back(user);
user.cell->ports.at(user.port).net = glbnet.get();
}
}
net->users = keep_users;
dcc->ports[id_CLKI].net = net;
PortRef clki_pr;
clki_pr.port = id_CLKI;
clki_pr.cell = dcc.get();
net->users.push_back(clki_pr);
if (net->clkconstr) {
glbnet->clkconstr = std::unique_ptr<ClockConstraint>(new ClockConstraint());
glbnet->clkconstr->low = net->clkconstr->low;
glbnet->clkconstr->high = net->clkconstr->high;
glbnet->clkconstr->period = net->clkconstr->period;
}
glbptr = glbnet.get();
ctx->nets[glbnet->name] = std::move(glbnet);
dccptr = dcc.get();
ctx->cells[dcc->name] = std::move(dcc);
}
net->users = keep_users;
dcc->ports[id_CLKI].net = net;
PortRef clki_pr;
clki_pr.port = id_CLKI;
clki_pr.cell = dcc.get();
net->users.push_back(clki_pr);
place_dcc(dcc.get());
if (net->clkconstr) {
glbnet->clkconstr = std::unique_ptr<ClockConstraint>(new ClockConstraint());
glbnet->clkconstr->low = net->clkconstr->low;
glbnet->clkconstr->high = net->clkconstr->high;
glbnet->clkconstr->period = net->clkconstr->period;
}
ctx->cells[dcc->name] = std::move(dcc);
NetInfo *glbptr = glbnet.get();
ctx->nets[glbnet->name] = std::move(glbnet);
glbptr->attrs[ctx->id("ECP5_IS_GLOBAL")] = 1;
if (str_or_default(dccptr->attrs, ctx->id("BEL"), "") == "")
place_dcc(dccptr);
return glbptr;
}