@@ -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