@@ -2304,6 +2304,172 @@ struct NexusPacker
2304
2304
}
2305
2305
}
2306
2306
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
+
2307
2473
explicit NexusPacker (Context *ctx) : ctx(ctx) {}
2308
2474
2309
2475
void operator ()()
@@ -2323,6 +2489,11 @@ struct NexusPacker
2323
2489
pack_luts ();
2324
2490
pack_ip ();
2325
2491
handle_iologic ();
2492
+
2493
+ if (!bool_or_default (ctx->settings , ctx->id (" no_pack_lutff" ))) {
2494
+ pack_lutffs ();
2495
+ }
2496
+
2326
2497
promote_globals ();
2327
2498
place_globals ();
2328
2499
generate_constraints ();
0 commit comments