Skip to content

Commit fd2d4a8

Browse files
authoredNov 24, 2021
Merge pull request #863 from antmicro/pack_lutff
nexus: LUT and FF clustering
2 parents f5cc959 + 41accf8 commit fd2d4a8

File tree

2 files changed

+182
-0
lines changed

2 files changed

+182
-0
lines changed
 

‎nexus/main.cc

+11
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ po::options_description NexusCommandHandler::getArchOptions()
5151
specific.add_options()("fasm", po::value<std::string>(), "fasm file to write");
5252
specific.add_options()("pdc", po::value<std::string>(), "physical constraints file");
5353
specific.add_options()("no-post-place-opt", "disable post-place repacking (debugging use only)");
54+
specific.add_options()("no-pack-lutff", "disable packing (clustering) LUTs and FFs together");
55+
specific.add_options()("carry-lutff-ratio", po::value<float>(), "ratio of FFs to be added to carry-chain LUT clusters");
5456

5557
return specific;
5658
}
@@ -76,6 +78,15 @@ std::unique_ptr<Context> NexusCommandHandler::createContext(dict<std::string, Pr
7678
auto ctx = std::unique_ptr<Context>(new Context(chipArgs));
7779
if (vm.count("no-post-place-opt"))
7880
ctx->settings[ctx->id("no_post_place_opt")] = Property::State::S1;
81+
if (vm.count("no-pack-lutff"))
82+
ctx->settings[ctx->id("no_pack_lutff")] = Property::State::S1;
83+
if (vm.count("carry-lutff-ratio")) {
84+
float ratio = vm["carry-lutff-ratio"].as<float>();
85+
if (ratio < 0.0f || ratio > 1.0f) {
86+
log_error("Carry LUT+FF packing ration must be between 0.0 and 1.0");
87+
}
88+
ctx->settings[ctx->id("carry_lutff_ratio")] = ratio;
89+
}
7990
return ctx;
8091
}
8192

‎nexus/pack.cc

+171
Original file line numberDiff line numberDiff line change
@@ -2304,6 +2304,172 @@ struct NexusPacker
23042304
}
23052305
}
23062306

2307+
FFControlSet gather_ff_settings(CellInfo* cell) {
2308+
NPNR_ASSERT(cell->type == id_OXIDE_FF);
2309+
2310+
FFControlSet ctrlset;
2311+
ctrlset.async = str_or_default(cell->params, id_SRMODE, "LSR_OVER_CE") == "ASYNC";
2312+
ctrlset.regddr_en = is_enabled(cell, id_REGDDR);
2313+
ctrlset.gsr_en = is_enabled(cell, id_GSR);
2314+
ctrlset.clkmux = ctx->id(str_or_default(cell->params, id_CLKMUX, "CLK")).index;
2315+
ctrlset.cemux = ctx->id(str_or_default(cell->params, id_CEMUX, "CE")).index;
2316+
ctrlset.lsrmux = ctx->id(str_or_default(cell->params, id_LSRMUX, "LSR")).index;
2317+
ctrlset.clk = get_net_or_empty(cell, id_CLK);
2318+
ctrlset.ce = get_net_or_empty(cell, id_CE);
2319+
ctrlset.lsr = get_net_or_empty(cell, id_LSR);
2320+
2321+
return ctrlset;
2322+
}
2323+
2324+
void pack_lutffs () {
2325+
log_info("Inferring LUT+FF pairs...\n");
2326+
2327+
float carry_ratio = 1.0f;
2328+
if (ctx->settings.find(ctx->id("carry_lutff_ratio")) != ctx->settings.end()) {
2329+
carry_ratio = ctx->setting<float>("carry_lutff_ratio");
2330+
}
2331+
2332+
// FF control settings/signals are slice-wide. The dict below is used
2333+
// to track settings of FFs glued to clusters which may span more than
2334+
// one slice (eg. carry-chains). For now it is assumed that all FFs
2335+
// in one cluster share the same settings and control signals.
2336+
dict<IdString, FFControlSet> cluster_ffinfo;
2337+
2338+
size_t num_comb = 0;
2339+
size_t num_ff = 0;
2340+
size_t num_pair = 0;
2341+
size_t num_glue = 0;
2342+
2343+
for (auto &cell : ctx->cells) {
2344+
CellInfo *ff = cell.second.get();
2345+
if (ff->type != id_OXIDE_FF) {
2346+
continue;
2347+
}
2348+
2349+
num_ff++;
2350+
2351+
// Get input net
2352+
// At the packing stage all inputs go to M
2353+
NetInfo *di = get_net_or_empty(ff, id_M);
2354+
if (di == nullptr || di->driver.cell == nullptr) {
2355+
continue;
2356+
}
2357+
2358+
// Skip if there are multiple sinks on that net
2359+
if (di->users.size() != 1) {
2360+
continue;
2361+
}
2362+
2363+
// Check if the driver is a LUT and the direct connection is from F
2364+
CellInfo* lut = di->driver.cell;
2365+
if (lut->type != id_OXIDE_COMB) {
2366+
continue;
2367+
}
2368+
if (di->driver.port != id_F &&
2369+
di->driver.port != id_OFX)
2370+
{
2371+
continue;
2372+
}
2373+
2374+
// The FF must not use M and DI at the same time
2375+
if (get_net_or_empty(ff, id_DI)) {
2376+
continue;
2377+
}
2378+
2379+
// The LUT must be in LOGIC/CARRY mode
2380+
if (str_or_default(lut->params, id_MODE, "LOGIC") != "LOGIC" &&
2381+
str_or_default(lut->params, id_MODE, "LOGIC") != "CCU2") {
2382+
continue;
2383+
}
2384+
2385+
// The FF cannot be in another cluster
2386+
if (ff->cluster != ClusterId()) {
2387+
continue;
2388+
}
2389+
2390+
// Get FF settings
2391+
auto ffinfo = gather_ff_settings(ff);
2392+
2393+
// A free LUT, create a new cluster
2394+
if (lut->cluster == ClusterId()) {
2395+
2396+
lut->cluster = lut->name;
2397+
lut->constr_children.push_back(ff);
2398+
2399+
ff->cluster = lut->name;
2400+
ff->constr_x = 0;
2401+
ff->constr_y = 0;
2402+
ff->constr_z = 2;
2403+
ff->constr_abs_z = false;
2404+
2405+
num_pair++;
2406+
}
2407+
// Attach the FF to the existing cluster of the LUT
2408+
else {
2409+
2410+
// Check if the FF settings match those of others in this
2411+
// cluster. If not then reject this FF.
2412+
//
2413+
// This is a greedy approach - the first attached FF will
2414+
// enforce its settings on all following candidates. A better
2415+
// approach would be to first form groups of matching FFs for
2416+
// a cluster and then attach only the largest group to it.
2417+
if (cluster_ffinfo.count(lut->cluster)) {
2418+
if (ffinfo != cluster_ffinfo.at(lut->cluster)) {
2419+
continue;
2420+
}
2421+
}
2422+
2423+
// No order not to make too large carry clusters pack only the
2424+
// given fraction of FFs there.
2425+
if(str_or_default(lut->params, id_MODE, "LOGIC") == "CCU2") {
2426+
float r = (float)(ctx->rng() % 1000) * 1e-3f;
2427+
if (r > carry_ratio) {
2428+
continue;
2429+
}
2430+
}
2431+
2432+
// Get the cluster root
2433+
CellInfo* root = ctx->cells.at(lut->cluster).get();
2434+
2435+
// Constrain the FF relative to the LUT
2436+
ff->cluster = root->cluster;
2437+
ff->constr_x = lut->constr_x;
2438+
ff->constr_y = lut->constr_y;
2439+
ff->constr_z = lut->constr_z + 2;
2440+
ff->constr_abs_z = lut->constr_abs_z;
2441+
root->constr_children.push_back(ff);
2442+
2443+
num_glue++;
2444+
}
2445+
2446+
// Reconnect M to DI
2447+
rename_port(ctx, ff, id_M, id_DI);
2448+
ff->params[id_SEL] = std::string("DL");
2449+
2450+
// Store FF settings of the cluster
2451+
if (!cluster_ffinfo.count(lut->cluster)) {
2452+
cluster_ffinfo.emplace(lut->cluster, ffinfo);
2453+
}
2454+
}
2455+
2456+
// Count OXIDE_COMB, OXIDE_FF are already counted
2457+
for (auto &cell : ctx->cells) {
2458+
CellInfo *ff = cell.second.get();
2459+
if (ff->type == id_OXIDE_COMB) {
2460+
num_comb++;
2461+
}
2462+
}
2463+
2464+
// Print statistics
2465+
log_info(" Created %zu LUT+FF pairs and extended %zu clusters using total %zu FFs and %zu LUTs\n",
2466+
num_pair,
2467+
num_glue,
2468+
num_ff,
2469+
num_comb
2470+
);
2471+
}
2472+
23072473
explicit NexusPacker(Context *ctx) : ctx(ctx) {}
23082474

23092475
void operator()()
@@ -2323,6 +2489,11 @@ struct NexusPacker
23232489
pack_luts();
23242490
pack_ip();
23252491
handle_iologic();
2492+
2493+
if (!bool_or_default(ctx->settings, ctx->id("no_pack_lutff"))) {
2494+
pack_lutffs();
2495+
}
2496+
23262497
promote_globals();
23272498
place_globals();
23282499
generate_constraints();

0 commit comments

Comments
 (0)
Please sign in to comment.