From 04be9a71f961feec668dd94343a6f3bb41d31927 Mon Sep 17 00:00:00 2001 From: David Shah Date: Sat, 31 Aug 2019 10:45:12 +0100 Subject: [PATCH] ecp5: Add support for clock gating with DCCA Signed-off-by: David Shah --- ecp5/bitstream.cc | 30 ++++++++++++++- ecp5/globals.cc | 96 ++++++++++++++++++++++++++++------------------- 2 files changed, 87 insertions(+), 39 deletions(-) diff --git a/ecp5/bitstream.cc b/ecp5/bitstream.cc index c5a04a8d..f010d7dd 100644 --- a/ecp5/bitstream.cc +++ b/ecp5/bitstream.cc @@ -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{"BMID_0H", "BMID_0V"})); + tg.tiles.push_back(ctx->getTileByTypeAndLocation(loc.y, loc.x + 1, + std::set{"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); diff --git a/ecp5/globals.cc b/ecp5/globals.cc index bc5c66df..da2ba8f0 100644 --- a/ecp5/globals.cc +++ b/ecp5/globals.cc @@ -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 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 glbnet = std::unique_ptr(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 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 glbnet = std::unique_ptr(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 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(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(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; }