@@ -34,6 +34,82 @@ using namespace DeviceResources;
34
34
using namespace LogicalNetlist ;
35
35
using namespace capnp ;
36
36
37
+ /* *
38
+ * @brief The FPGA interchange timing model includes three different corners (min, typ and max) for each of the two
39
+ * speed_models (slow and fast).
40
+ *
41
+ * Timing data can be found on PIPs, nodes, site pins and bel pins.
42
+ * This function retrieves the timing value based on the wanted speed model and the wanted corner.
43
+ *
44
+ * More information on the FPGA Interchange timing model can be found here:
45
+ * - https://github.com/chipsalliance/fpga-interchange-schema/blob/main/interchange/DeviceResources.capnp
46
+ */
47
+ static float get_corner_value (Device::CornerModel::Reader model, const char * speed_model, const char * value) {
48
+ bool slow_model = std::string (speed_model) == std::string (" slow" );
49
+ bool fast_model = std::string (speed_model) == std::string (" fast" );
50
+
51
+ bool min_corner = std::string (value) == std::string (" min" );
52
+ bool typ_corner = std::string (value) == std::string (" typ" );
53
+ bool max_corner = std::string (value) == std::string (" max" );
54
+
55
+ if (!slow_model && !fast_model) {
56
+ archfpga_throw (" " , __LINE__,
57
+ " Wrong speed model `%s`. Expected `slow` or `fast`\n " , speed_model);
58
+ }
59
+
60
+ if (!min_corner && !typ_corner && !max_corner) {
61
+ archfpga_throw (" " , __LINE__,
62
+ " Wrong corner model `%s`. Expected `min`, `typ` or `max`\n " , value);
63
+ }
64
+
65
+ bool has_fast = model.getFast ().hasFast ();
66
+ bool has_slow = model.getSlow ().hasSlow ();
67
+
68
+ if (slow_model && has_slow) {
69
+ auto half = model.getSlow ().getSlow ();
70
+ if (min_corner && half.getMin ().isMin ()) {
71
+ return half.getMin ().getMin ();
72
+ } else if (typ_corner && half.getTyp ().isTyp ()) {
73
+ return half.getTyp ().getTyp ();
74
+ } else if (max_corner && half.getMax ().isMax ()) {
75
+ return half.getMax ().getMax ();
76
+ } else {
77
+ if (half.getMin ().isMin ()) {
78
+ return half.getMin ().getMin ();
79
+ } else if (half.getTyp ().isTyp ()) {
80
+ return half.getTyp ().getTyp ();
81
+ } else if (half.getMax ().isMax ()) {
82
+ return half.getMax ().getMax ();
83
+ } else {
84
+ archfpga_throw (" " , __LINE__,
85
+ " Invalid speed model %s. No value found!\n " , speed_model);
86
+ }
87
+ }
88
+ } else if (fast_model && has_fast) {
89
+ auto half = model.getFast ().getFast ();
90
+ if (min_corner && half.getMin ().isMin ()) {
91
+ return half.getMin ().getMin ();
92
+ } else if (typ_corner && half.getTyp ().isTyp ()) {
93
+ return half.getTyp ().getTyp ();
94
+ } else if (max_corner && half.getMax ().isMax ()) {
95
+ return half.getMax ().getMax ();
96
+ } else {
97
+ if (half.getMin ().isMin ()) {
98
+ return half.getMin ().getMin ();
99
+ } else if (half.getTyp ().isTyp ()) {
100
+ return half.getTyp ().getTyp ();
101
+ } else if (half.getMax ().isMax ()) {
102
+ return half.getMax ().getMax ();
103
+ } else {
104
+ archfpga_throw (" " , __LINE__,
105
+ " Invalid speed model %s. No value found!\n " , speed_model);
106
+ }
107
+ }
108
+ }
109
+
110
+ return 0 .;
111
+ }
112
+
37
113
struct ArchReader {
38
114
public:
39
115
ArchReader (t_arch* arch, Device::Reader& arch_reader, const char * arch_file, std::vector<t_physical_tile_type>& phys_types, std::vector<t_logical_block_type>& logical_types)
@@ -47,6 +123,11 @@ struct ArchReader {
47
123
48
124
void read_arch () {
49
125
process_models ();
126
+ process_device ();
127
+
128
+ process_layout ();
129
+ process_switches ();
130
+ process_segments ();
50
131
}
51
132
52
133
private:
@@ -163,6 +244,234 @@ struct ArchReader {
163
244
}
164
245
}
165
246
}
247
+
248
+ // Layout Processing
249
+ void process_layout () {
250
+ auto strList = ar_.getStrList ();
251
+ auto tileList = ar_.getTileList ();
252
+ auto tileTypeList = ar_.getTileTypeList ();
253
+ t_grid_def grid_def;
254
+
255
+ grid_def.width = grid_def.height = 0 ;
256
+ for (auto tile : tileList) {
257
+ grid_def.width = std::max (grid_def.width , tile.getCol () + 1 );
258
+ grid_def.height = std::max (grid_def.height , tile.getRow () + 1 );
259
+ }
260
+
261
+ grid_def.grid_type = GridDefType::FIXED;
262
+ std::string name = std::string (ar_.getName ());
263
+
264
+ if (name == " auto" ) {
265
+ // At the moment, the interchange specifies fixed-layout only architectures,
266
+ // and allowing for auto-sizing could potentially be implemented later on
267
+ // to allow for experimentation on new architectures.
268
+ // For the time being the layout is restricted to be only fixed.
269
+ archfpga_throw (arch_file_, __LINE__,
270
+ " The name auto is reserved for auto-size layouts; please choose another name" );
271
+ }
272
+
273
+ grid_def.name = name;
274
+ for (auto tile : tileList) {
275
+ t_metadata_dict data;
276
+ std::string tile_prefix (strList[tile.getName ()].cStr ());
277
+ auto tileType = tileTypeList[tile.getType ()];
278
+ std::string tile_type (strList[tileType.getName ()].cStr ());
279
+
280
+ size_t pos = tile_prefix.find (tile_type);
281
+ if (pos != std::string::npos && pos == 0 )
282
+ tile_prefix.erase (pos, tile_type.length () + 1 );
283
+ data.add (arch_->strings .intern_string (vtr::string_view (" fasm_prefix" )),
284
+ arch_->strings .intern_string (vtr::string_view (tile_prefix.c_str ())));
285
+ t_grid_loc_def single (tile_type, 1 );
286
+ single.x .start_expr = tile.getCol ();
287
+ single.y .start_expr = tile.getRow ();
288
+ single.x .end_expr = single.x .start_expr + " + w - 1" ;
289
+ single.y .end_expr = single.y .start_expr + " + h - 1" ;
290
+ single.owned_meta = std::make_unique<t_metadata_dict>(data);
291
+ single.meta = single.owned_meta .get ();
292
+ grid_def.loc_defs .emplace_back (std::move (single));
293
+ }
294
+
295
+ arch_->grid_layouts .emplace_back (std::move (grid_def));
296
+ }
297
+
298
+ void process_device () {
299
+ /*
300
+ * The generic architecture data is not currently available in the interchange format
301
+ * therefore, for a very initial implementation, the values are taken from the ones
302
+ * used primarly in the Xilinx series7 devices, generated using SymbiFlow.
303
+ *
304
+ * As the interchange format develops further, with possibly more details, this function can
305
+ * become dynamic, allowing for different parameters for the different architectures.
306
+ *
307
+ * FIXME: This will require to be dynamically assigned, and a suitable representation added
308
+ * to the FPGA interchange device schema.
309
+ */
310
+ arch_->R_minW_nmos = 6065.520020 ;
311
+ arch_->R_minW_pmos = 18138.500000 ;
312
+ arch_->grid_logic_tile_area = 14813.392 ;
313
+ arch_->Chans .chan_x_dist .type = UNIFORM;
314
+ arch_->Chans .chan_x_dist .peak = 1 ;
315
+ arch_->Chans .chan_x_dist .width = 0 ;
316
+ arch_->Chans .chan_x_dist .xpeak = 0 ;
317
+ arch_->Chans .chan_x_dist .dc = 0 ;
318
+ arch_->Chans .chan_y_dist .type = UNIFORM;
319
+ arch_->Chans .chan_y_dist .peak = 1 ;
320
+ arch_->Chans .chan_y_dist .width = 0 ;
321
+ arch_->Chans .chan_y_dist .xpeak = 0 ;
322
+ arch_->Chans .chan_y_dist .dc = 0 ;
323
+ arch_->ipin_cblock_switch_name = std::string (" generic" );
324
+ arch_->SBType = WILTON;
325
+ arch_->Fs = 3 ;
326
+ default_fc_.specified = true ;
327
+ default_fc_.in_value_type = e_fc_value_type::FRACTIONAL;
328
+ default_fc_.in_value = 1.0 ;
329
+ default_fc_.out_value_type = e_fc_value_type::FRACTIONAL;
330
+ default_fc_.out_value = 1.0 ;
331
+ }
332
+
333
+ void process_switches () {
334
+ std::set<std::pair<bool , uint32_t >> pip_timing_models;
335
+ for (auto tile_type : ar_.getTileTypeList ()) {
336
+ for (auto pip : tile_type.getPips ()) {
337
+ pip_timing_models.insert (std::pair<bool , uint32_t >(pip.getBuffered21 (), pip.getTiming ()));
338
+ if (!pip.getDirectional ())
339
+ pip_timing_models.insert (std::pair<bool , uint32_t >(pip.getBuffered20 (), pip.getTiming ()));
340
+ }
341
+ }
342
+
343
+ auto timing_data = ar_.getPipTimings ();
344
+
345
+ std::vector<std::pair<bool , uint32_t >> pip_timing_models_list;
346
+ pip_timing_models_list.reserve (pip_timing_models.size ());
347
+
348
+ for (auto entry : pip_timing_models) {
349
+ pip_timing_models_list.push_back (entry);
350
+ }
351
+
352
+ auto num_switches = pip_timing_models.size () + 2 ;
353
+ std::string switch_name;
354
+
355
+ arch_->num_switches = num_switches;
356
+ auto * switches = arch_->Switches ;
357
+
358
+ if (num_switches > 0 ) {
359
+ switches = new t_arch_switch_inf[num_switches];
360
+ }
361
+
362
+ float R, Cin, Cint, Cout, Tdel;
363
+ for (int i = 0 ; i < (int )num_switches; ++i) {
364
+ t_arch_switch_inf& as = switches[i];
365
+
366
+ R = Cin = Cint = Cout = Tdel = 0.0 ;
367
+ SwitchType type;
368
+
369
+ if (i == 0 ) {
370
+ switch_name = " short" ;
371
+ type = SwitchType::SHORT;
372
+ R = 0.0 ;
373
+ } else if (i == 1 ) {
374
+ switch_name = " generic" ;
375
+ type = SwitchType::MUX;
376
+ R = 0.0 ;
377
+ } else {
378
+ auto entry = pip_timing_models_list[i - 2 ];
379
+ auto model = timing_data[entry.second ];
380
+ std::stringstream name;
381
+ std::string mux_type_string = entry.first ? " mux_" : " passGate_" ;
382
+ name << mux_type_string;
383
+
384
+ // FIXME: allow to dynamically choose different speed models and corners
385
+ R = get_corner_value (model.getOutputResistance (), " slow" , " min" );
386
+ name << " R" << std::scientific << R;
387
+
388
+ Cin = get_corner_value (model.getInputCapacitance (), " slow" , " min" );
389
+ name << " Cin" << std::scientific << Cin;
390
+
391
+ Cout = get_corner_value (model.getOutputCapacitance (), " slow" , " min" );
392
+ name << " Cout" << std::scientific << Cout;
393
+
394
+ if (entry.first ) {
395
+ Cint = get_corner_value (model.getInternalCapacitance (), " slow" , " min" );
396
+ name << " Cinternal" << std::scientific << Cint;
397
+ }
398
+
399
+ Tdel = get_corner_value (model.getInternalDelay (), " slow" , " min" );
400
+ name << " Tdel" << std::scientific << Tdel;
401
+
402
+ switch_name = name.str ();
403
+ type = entry.first ? SwitchType::MUX : SwitchType::PASS_GATE;
404
+ }
405
+
406
+ /* Should never happen */
407
+ if (switch_name == std::string (VPR_DELAYLESS_SWITCH_NAME)) {
408
+ archfpga_throw (arch_file_, __LINE__,
409
+ " Switch name '%s' is a reserved name for VPR internal usage!" , switch_name.c_str ());
410
+ }
411
+
412
+ as.name = vtr::strdup (switch_name.c_str ());
413
+ as.set_type (type);
414
+ as.mux_trans_size = as.type () == SwitchType::MUX ? 1 : 0 ;
415
+
416
+ as.R = R;
417
+ as.Cin = Cin;
418
+ as.Cout = Cout;
419
+ as.Cinternal = Cint;
420
+ as.set_Tdel (t_arch_switch_inf::UNDEFINED_FANIN, Tdel);
421
+
422
+ if (as.type () == SwitchType::SHORT || as.type () == SwitchType::PASS_GATE) {
423
+ as.buf_size_type = BufferSize::ABSOLUTE;
424
+ as.buf_size = 0 ;
425
+ as.power_buffer_type = POWER_BUFFER_TYPE_ABSOLUTE_SIZE;
426
+ as.power_buffer_size = 0 .;
427
+ } else {
428
+ as.buf_size_type = BufferSize::AUTO;
429
+ as.buf_size = 0 .;
430
+ as.power_buffer_type = POWER_BUFFER_TYPE_AUTO;
431
+ }
432
+ }
433
+ }
434
+
435
+ void process_segments () {
436
+ auto strList = ar_.getStrList ();
437
+
438
+ // Segment names will be taken from wires connected to pips
439
+ // They are good representation for nodes
440
+ std::set<uint32_t > wire_names;
441
+ for (auto tile_type : ar_.getTileTypeList ()) {
442
+ auto wires = tile_type.getWires ();
443
+ for (auto pip : tile_type.getPips ()) {
444
+ wire_names.insert (wires[pip.getWire0 ()]);
445
+ wire_names.insert (wires[pip.getWire1 ()]);
446
+ }
447
+ }
448
+ int num_seg = wire_names.size ();
449
+ arch_->Segments .resize (num_seg);
450
+ uint32_t index = 0 ;
451
+ for (auto i : wire_names) {
452
+ // Use default values as we will populate rr_graph with correct values
453
+ // This segments are just declaration of future use
454
+ arch_->Segments [index].name = std::string (strList[i]);
455
+ arch_->Segments [index].length = 1 ;
456
+ arch_->Segments [index].frequency = 1 ;
457
+ arch_->Segments [index].Rmetal = 0 ;
458
+ arch_->Segments [index].Cmetal = 0 ;
459
+ arch_->Segments [index].parallel_axis = BOTH_AXIS;
460
+
461
+ // TODO: Only bi-directional segments are created, but it the interchange format
462
+ // has directionality information on PIPs, which may be used to infer the
463
+ // segments' directonality.
464
+ arch_->Segments [index].directionality = BI_DIRECTIONAL;
465
+ arch_->Segments [index].arch_wire_switch = 1 ;
466
+ arch_->Segments [index].arch_opin_switch = 1 ;
467
+ arch_->Segments [index].cb .resize (1 );
468
+ arch_->Segments [index].cb [0 ] = true ;
469
+ arch_->Segments [index].sb .resize (2 );
470
+ arch_->Segments [index].sb [0 ] = true ;
471
+ arch_->Segments [index].sb [1 ] = true ;
472
+ ++index;
473
+ }
474
+ }
166
475
};
167
476
168
477
void FPGAInterchangeReadArch (const char * FPGAInterchangeDeviceFile,
0 commit comments