diff --git a/himbaechel/uarch/gatemate/gatemate.cc b/himbaechel/uarch/gatemate/gatemate.cc index 4d96ffc2..3bcba45a 100644 --- a/himbaechel/uarch/gatemate/gatemate.cc +++ b/himbaechel/uarch/gatemate/gatemate.cc @@ -49,6 +49,7 @@ po::options_description GateMateImpl::getUArchOptions() specific.add_options()("clk-cp", "use CP lines for CLK and EN"); specific.add_options()("no-cpe-cp", "do not use CP lines pass through CPE"); specific.add_options()("no-bridges", "do not use CPE in bridge mode"); + specific.add_options()("auto-part", "use automatic hypergraph-based partitioning for multi-die designs"); return specific; } @@ -121,6 +122,7 @@ void GateMateImpl::init_database(Arch *arch) use_cp_for_clk = args.options.count("clk-cp") == 1; use_cp_for_cpe = args.options.count("no-cpe-cp") == 0; use_bridges = args.options.count("no-bridges") == 0; + auto_part = args.options.count("auto-part") == 1; } void GateMateImpl::init(Context *ctx) diff --git a/himbaechel/uarch/gatemate/gatemate.h b/himbaechel/uarch/gatemate/gatemate.h index 746e5bf8..7e36b62d 100644 --- a/himbaechel/uarch/gatemate/gatemate.h +++ b/himbaechel/uarch/gatemate/gatemate.h @@ -142,6 +142,8 @@ struct GateMateImpl : HimbaechelAPI void get_setuphold_from_tmg_db(IdString id_setup, IdString id_hold, DelayPair &setup, DelayPair &hold) const; void get_setuphold_from_tmg_db(IdString id_setuphold, DelayPair &setup, DelayPair &hold) const; + void partition_design(); + struct GateMateCellInfo { // slice info @@ -158,6 +160,7 @@ struct GateMateImpl : HimbaechelAPI bool use_cp_for_clk; bool use_cp_for_cpe; bool use_bridges; + bool auto_part; }; NEXTPNR_NAMESPACE_END diff --git a/himbaechel/uarch/gatemate/pack.cc b/himbaechel/uarch/gatemate/pack.cc index 11167ae2..3d9fd0a6 100644 --- a/himbaechel/uarch/gatemate/pack.cc +++ b/himbaechel/uarch/gatemate/pack.cc @@ -651,6 +651,11 @@ void GateMateImpl::pack() packer.copy_clocks(); packer.remove_constants(); packer.remove_double_constrained(); + + if (auto_part) { + partition_design(); + } + if (forced_die != IdString()) { for (auto &cell : ctx->cells) { if (cell.second->belStrength != PlaceStrength::STRENGTH_FIXED) diff --git a/himbaechel/uarch/gatemate/partition.cc b/himbaechel/uarch/gatemate/partition.cc index 4dacecf8..2d1d29db 100644 --- a/himbaechel/uarch/gatemate/partition.cc +++ b/himbaechel/uarch/gatemate/partition.cc @@ -699,13 +699,13 @@ int partition_recursive(Context *ctx, Hypergraph &g, const std::vector 0) + if (!n.fixed && !n.edges.empty()) ++non_fixed_nodes; FMPartitioner fm(ctx, g, partitions); fm.init(); if (ctx->verbose) log_info("enter level=%d, N=%d, A=%f\n", level, non_fixed_nodes, fm.total_area()); - if (non_fixed_nodes <= 200) { + if (non_fixed_nodes <= 20) { // Final level in the hierarchy // Initial random partioning as our seed fm.random_part(); @@ -749,6 +749,103 @@ int partition_recursive(Context *ctx, Hypergraph &g, const std::vectortype == id_CC_BRAM_20K) { + return 100.0f; + } else if (ci->type == id_CC_BRAM_40K) { + return 200.0f; + } + return 1.0f; +} + +template void recursive_visit_children(const CellInfo *ci, Tfunc func) { + for (auto child : ci->constr_children) { + func(child); + recursive_visit_children(child, func); + } +} + } // namespace +void GateMateImpl::partition_design() +{ + if (dies != 2) { + log_error("Partitioning is currently only supported for the CCGM1A2 device.\n"); + } + log_info("Partitioning design across dies...\n"); + // Build the hypergraph + dict cell2node; + Hypergraph g; + double total_area = 0; + for (auto &cell : ctx->cells) { + CellInfo *ci = cell.second.get(); + if (ci->cluster != ClusterId() && ci->name != ci->cluster) + continue; // not cluster root + + int node_idx = g.nodes.size(); + cell2node[ci->name] = node_idx; + g.nodes.emplace_back(); + auto &n = g.nodes.back(); + n.area = get_cell_area(ctx, ci); + + recursive_visit_children(ci, [&](CellInfo *child) { + n.area += get_cell_area(ctx, child); + cell2node[child->name] = node_idx; + }); + + total_area += n.area; + if (ci->bel != BelId()) { + // constrained bels + auto tile_data = tile_extra_data(ci->bel.tile); + n.fixed = true; + n.partition = tile_data->die; + } + } + // Import edges + for (auto &net : ctx->nets) { + NetInfo *ni = net.second.get(); + if (!ni->driver.cell || ni->driver.cell->type.in(id_CC_BUFG, id_CC_PLL, id_CC_PLL_ADV)) + continue; // undriven or global nets + int edge_idx = int(g.edges.size()); + g.edges.emplace_back(); + auto &e = g.edges.back(); + auto import_port = [&](const PortRef &pr) { + auto node_idx = cell2node.at(pr.cell->name); + e.nodes.push_back(node_idx); + g.nodes.at(node_idx).edges.push_back(edge_idx); + }; + import_port(ni->driver); + for (auto &usr : ni->users) + import_port(usr); + } + // Run the partitioner + std::vector partitions; + for (int i = 0; i < dies; i++) { + // TODO: better partition constraints (35000 for a target per-die utilisation of ~75% max) + partitions.emplace_back(); + partitions.back().min_nodes = 0; + partitions.back().max_nodes = std::max(35000, total_area * 0.6); + } + + int cost = partition_recursive(ctx, g, partitions, 0); + log_info("Hypergraph partitioning complete, final cost: %d\n", cost); + + std::vector partition_area(dies); + for (const auto &c2n : cell2node) { + CellInfo *ci = ctx->cells.at(c2n.first).get(); + if (ci->cluster != ClusterId() && ci->cluster != ci->name) + continue; // not cluster root + auto &n = g.nodes.at(c2n.second); + partition_area.at(n.partition) += n.area; + if (!n.fixed) { + ctx->constrainCellToRegion(c2n.first, index_to_die.at(n.partition)); + } + } + for (int i = 0; i < dies; i++) { + log_info(" die %s area: %f\n", index_to_die.at(i).c_str(ctx), partition_area.at(i)); + } +} + NEXTPNR_NAMESPACE_END