Merge branch 'master' of ssh.github.com:YosysHQ/nextpnr
diff --git a/ecp5/arch.cc b/ecp5/arch.cc
index a293668..8ba1af4 100644
--- a/ecp5/arch.cc
+++ b/ecp5/arch.cc
@@ -158,6 +158,26 @@
     }
 }
 
+std::string Arch::getFullChipName() const
+{
+    std::string name = getChipName();
+    name += "-";
+    switch (args.speed) {
+    case ArchArgs::SPEED_6:
+        name += "6";
+        break;
+    case ArchArgs::SPEED_7:
+        name += "7";
+        break;
+    case ArchArgs::SPEED_8:
+    case ArchArgs::SPEED_8_5G:
+        name += "8";
+        break;
+    }
+    name += args.package;
+    return name;
+}
+
 // -----------------------------------------------------------------------
 
 IdString Arch::archArgsToId(ArchArgs args) const
diff --git a/ecp5/arch.h b/ecp5/arch.h
index e85d9c4..a479abb 100644
--- a/ecp5/arch.h
+++ b/ecp5/arch.h
@@ -491,6 +491,7 @@
     Arch(ArchArgs args);
 
     std::string getChipName() const;
+    std::string getFullChipName() const;
 
     IdString archId() const { return id("ecp5"); }
     ArchArgs archArgs() const { return args; }
diff --git a/ecp5/bitstream.cc b/ecp5/bitstream.cc
index cac1186..f010d7d 100644
--- a/ecp5/bitstream.cc
+++ b/ecp5/bitstream.cc
@@ -600,6 +600,8 @@
         }
     }
 
+    cc.metadata.push_back("Part: " + ctx->getFullChipName());
+
     // Clear out DCU tieoffs in base config if DCU used
     for (auto &cell : ctx->cells) {
         CellInfo *ci = cell.second.get();
@@ -890,7 +892,35 @@
             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);
diff --git a/ecp5/globals.cc b/ecp5/globals.cc
index bc5c66d..da2ba8f 100644
--- a/ecp5/globals.cc
+++ b/ecp5/globals.cc
@@ -78,7 +78,18 @@
             }
             // 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 @@
     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 @@
     // 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;
     }