common.tcl 33.4 KB
Newer Older
1
2
3
#
# Copyright (C) 2014 Jens Korinth, TU Darmstadt
#
4
# This file is part of Tapasco (TPC).
5
#
6
# Tapasco is free software: you can redistribute it and/or modify
7
8
9
10
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
11
# Tapasco is distributed in the hope that it will be useful,
12
13
14
15
16
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
17
# along with Tapasco.  If not, see <http://www.gnu.org/licenses/>.
18
19
20
21
22
#
# @file		common.tcl
# @brief	Common Vivado Tcl helper procs to create block designs.
# @authors	J. Korinth, TU Darmstadt (jk@esa.cs.tu-darmstadt.de)
#
23
namespace eval tapasco {
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
  namespace export createBinaryCounter
  namespace export createClockingWizard
  namespace export createConcat
  namespace export createConstant
  namespace export createDualDMA
  namespace export createIntCtrl
  namespace export createInterconnect
  namespace export createMIG
  namespace export createOLEDController
  namespace export createPCIeBridge
  namespace export createPCIeIntrCtrl
  namespace export createSlice
  namespace export createSystemCache
  namespace export createZynqBFM
  namespace export createZynqPS
  namespace export get_board_preset
  namespace export get_composition
  namespace export get_design_frequency
  namespace export get_design_period
  namespace export get_number_of_processors
Jens Korinth's avatar
Jens Korinth committed
44
  namespace export get_speed_grade
45
46
47
48
49
50
51
  namespace export get_wns_from_timing_report

  namespace export create_interconnect_tree

  # check if we're running inside Vivado
  if {[llength [info commands version]] > 0} {
    # source IP catalog VLNVs for the current Vivado version
52
    set cip [format "$::env(TAPASCO_HOME)/common/common_%s.tcl" [version -short]]
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
    if {! [file exists $cip]} {
      puts "Could not find $cip, Vivado [version -short] is not supported yet!"
      exit 1
    } {
      source $cip
    }
  } {
    puts "Skipping IP catalog."
  }

  # Instantiates an AXI4 Interconnect IP.
  # @param name Name of the instance.
  # @param no_slaves Number of AXI4 Slave interfaces.
  # @param no_masters Number of AXI4 Master interfaces.
  # @return bd_cell of the instance.
  proc createInterconnect {name no_slaves no_masters} {
    variable stdcomps
    puts "Creating AXI Interconnect $name with $no_slaves slaves and $no_masters masters..."
    puts "  VLNV: [dict get $stdcomps axi_ic vlnv]"

    set ic [create_bd_cell -type ip -vlnv [dict get $stdcomps axi_ic vlnv] $name]
    set props [list CONFIG.NUM_SI $no_slaves CONFIG.NUM_MI $no_masters]
    for {set i 0} {$i < $no_slaves} {incr i} {
      set ifname [format "CONFIG.S%02d_HAS_REGSLICE" $i]
      lappend props $ifname {4}
    }
    for {set i 0} {$i < $no_masters} {incr i} {
      set ifname [format "CONFIG.M%02d_HAS_REGSLICE" $i]
      lappend props $ifname {4}
    }
    set_property -dict $props $ic
    return $ic
  }

  # Instantiates a Zynq-7000 Processing System IP core.
  # @param name Name of the instance (default: ps7).
89
90
  # @param preset Name of board preset to apply (default: tapasco::get_board_preset).
  # @param freq_mhz FCLK_0 frequency in MHz (default: tapasco::get_design_frequency).
91
  # @return bd_cell of the instance.
92
  proc createZynqPS {{name ps7} {preset [tapasco::get_board_preset]} {freq_mhz [tapasco::get_design_frequency]}} {
93
94
95
96
97
    variable stdcomps
    puts "Creating Zynq-7000 series IP core ..."
    puts "  VLNV: [dict get $stdcomps ps vlnv]"
    puts "  Preset: $preset"
    puts "  FCLK0 : $freq_mhz"
98
99

    set ps [create_bd_cell -type ip -vlnv [dict get $stdcomps ps vlnv] $name]
100
    if {$preset != {} && $preset != ""} {
101
102
103
104
105
      set_property -dict [list CONFIG.preset $preset] $ps
      apply_bd_automation -rule xilinx.com:bd_rule:processing_system7 -config {make_external "FIXED_IO, DDR" apply_board_preset "1" Master "Disable" Slave "Disable" } $ps
    } {
      apply_bd_automation -rule xilinx.com:bd_rule:processing_system7 -config {make_external "FIXED_IO, DDR" apply_board_preset "0" Master "Disable" Slave "Disable" } $ps
    }
106
107
108
109
110
    return $ps
  }

  # Instantiates a Zynq-7000 Processing System IP BFM simulation core.
  # @param name Name of the instance (default: ps7).
111
112
  # @param preset Name of board preset to apply (default: tapasco::get_board_preset).
  # @param freq_mhz FCLK_0 frequency in MHz (default: tapasco::get_design_frequency).
113
  # @return bd_cell of the instance.
114
  proc createZynqBFM {{name ps7} {preset [tapasco::get_board_preset]} {freq_mhz [tapasco::get_design_frequency]}} {
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
    variable stdcomps
    puts "Creating Zynq-7000 series BFM IP core ..."
    puts "  VLNV: [dict get $stdcomps ps_bfm vlnv]"
    puts "  Preset: $preset"
    puts "  FCLK0 : $freq_mhz"

    set paramlist [list \
        CONFIG.PCW_USE_M_AXI_GP0 {1} \
        CONFIG.PCW_USE_M_AXI_GP1 {1} \
        CONFIG.PCW_USE_S_AXI_HP0 {1} \
        CONFIG.PCW_USE_S_AXI_HP1 {1} \
        CONFIG.PCW_USE_S_AXI_HP2 {1} \
        CONFIG.PCW_USE_S_AXI_HP3 {1} \
        CONFIG.PCW_FCLK_CLK0_FREQ [expr $freq_mhz * 1000000] \
      ]

    set ps [create_bd_cell -type ip -vlnv [dict get $stdcomps ps_bfm vlnv] $name]
    set_property -dict $paramlist $ps
    return $ps
  }

  # Instantiates a XLConcat bit concatenation IP core.
  # @param name Name of the instance.
  # @param inputs Number of input wires.
  # @return bd_cell of the instance.
  proc createConcat {name inputs} {
    variable stdcomps
    puts "Creating xlconcat $name with $inputs ..."
    puts "  VLNV: [dict get $stdcomps xlconcat vlnv]"

    set xlconcat [create_bd_cell -type ip -vlnv [dict get $stdcomps xlconcat vlnv] $name]
    set_property -dict [list CONFIG.NUM_PORTS $inputs] $xlconcat
    return $xlconcat
  }

  # Instantiates a XLSlice bit slicing IP core.
  # @param name Name of the instance.
  # @param width Number of input wires.
  # @param bit Selected bit.
  # @return bd_cell of the instance.
  proc createSlice {name width bit} {
    variable stdcomps
    puts "Creating xlslice $name with $width-bit width and bit $bit selected ..."
    puts "  VLNV: [dict get $stdcomps xlslice vlnv]"

    set xlslice [create_bd_cell -type ip -vlnv [dict get $stdcomps xlslice vlnv] $name]
    set_property -dict [list CONFIG.DIN_WIDTH $width CONFIG.DIN_TO $bit CONFIG.DIN_FROM $bit CONFIG.DOUT_WIDTH 1] $xlslice
    return $xlslice
  }

  # Create a constant tie-off.
  # @param name Name of the instance.
  # @param width Number of input wires.
  # @param value Value of the constant.
  # @return bd_cell of the instance.
  proc createConstant {name width value} {
    variable stdcomps
    puts "Creating xlconstant $name with $width-bit width and value $value ..."
    puts "  VLNV: [dict get $stdcomps xlconst vlnv]"

    set xlconst [create_bd_cell -type ip -vlnv [dict get $stdcomps xlconst vlnv] $name]
    set_property -dict [list CONFIG.CONST_WIDTH $width CONFIG.CONST_VAL $value] $xlconst
    return $xlconst
  }

  # Instantiates an AXI4 Interrupt Controller IP core.
  # @param name Name of the instance (default: axi_intc).
  # @return bd_cell of the instance.
  proc createIntCtrl {{name axi_intc}} {
    variable stdcomps
    puts "Creating interrupt control $name ..."
    puts "  VLNV: [dict get $stdcomps axi_irqc vlnv]"

    set irqc [create_bd_cell -type ip -vlnv [dict get $stdcomps axi_irqc vlnv] $name]
    # activate edge-sensitive interrupts
    set_property -dict [list CONFIG.C_KIND_OF_INTR.VALUE_SRC USER] $irqc
    set_property -dict [list CONFIG.C_KIND_OF_INTR {0xFFFFFFFF}] $irqc
    # set_property -dict [list CONFIG.C_EN_CASCADE_MODE {1} CONFIG.C_CASCADE_MASTER {1}] $irqc
    return $irqc
  }

  # Instantiates binary counter IP core.
  # @param name Name of the instance.
  # @param output_width Bit width of the counter.
  # @return bd_cell of the instance.
  proc createBinaryCounter {name width} {
    variable stdcomps
    puts "Creating $width-bits binary counter ..."
    puts "  VLNV: [dict get $stdcomps bincnt vlnv]"

    set bincnt [create_bd_cell -type ip -vlnv [dict get $stdcomps bincnt vlnv] $name]
    # set bit-width
    set_property -dict [list CONFIG.Output_Width $width CONFIG.Restrict_Count {false}] $bincnt
    return $bincnt
  }

  # Instantiates Dual DMA core.
  # @param name Name of the instance.
  proc createDualDMA {name} {
    variable stdcomps
    puts "Creating dual DMA core ..."
    puts "  VLNV: [dict get $stdcomps dualdma vlnv]"

    set dd [create_bd_cell -type ip -vlnv [dict get $stdcomps dualdma vlnv] $name]
    set_property -dict [list \
      CONFIG.C_M32_AXI_BURST_LEN {64} \
      CONFIG.C_M32_AXI_DATA_WIDTH {512} \
      CONFIG.C_M64_AXI_BURST_LEN {128} \
      CONFIG.C_M64_AXI_DATA_WIDTH {256} \
      CONFIG.DATA_FIFO_DEPTH {16} \
      CONFIG.M32_IS_ASYNC {1} \
      CONFIG.M32_READ_MAX_REQ {8} \
      CONFIG.M32_WRITE_MAX_REQ {8} \
      CONFIG.M64_READ_MAX_REQ {8} \
      CONFIG.M64_WRITE_MAX_REQ {8} \
    ] $dd
    # read XDC file
232
    #set folder [format "%s/common/ip/dual_dma_1.0" $::env(TAPASCO_HOME)]
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
    # [get_property IP_DIR [get_ips [get_property CONFIG.Component_Name $dd]]]
    #set xdc ${folder}/dual.xdc
    #read_xdc -cells "*[get_property NAME $dd]" $xdc
    return $dd
  }

  # Instantiates a PCIe interrupt controller.
  # @param name Name of the instance.
  proc createPCIeIntrCtrl {name} {
    variable stdcomps
    puts "Creating PCIe Interrupt Controller ..."
    puts "  VLNV: [dict get $stdcomps pcie_intr_ctrl vlnv]"

    set ic [create_bd_cell -type ip -vlnv [dict get $stdcomps pcie_intr_ctrl vlnv] $name]
    set_property -dict [list \
      CONFIG.IRQ_DELAY {10} \
      CONFIG.IRQ_RECAP {NORMAL} \
      CONFIG.IRQ_TIMEOUT {1000} \
      CONFIG.IRQ_RECAP_CHECK {"0"} \
    ] $ic
    return $ic
  }

  # Instantiates a performance counter controller for the zedboard OLED display.
  # @param name Name of the instance.
  proc createOLEDController {name} {
    variable stdcomps
    set composition [get_composition]
    set pecount 0
    dict for {k v} $composition {
      set c [dict get $composition $k count]
      set pecount [expr "$pecount + $c"]
    }
    set oled_freq "10"
    set width [expr 128 / max(1, ($pecount / 32))]
    set cw [expr "log($width) / log(2)"]
    set p [expr round($oled_freq) * 1000]

    puts "Creating OLED Controller ..."
    puts "  VLNV       : [dict get $stdcomps oled_ctrl vlnv]"
    puts "  C_DELAY_1MS: $p"
    puts "  C_COLS     : $width"
    puts "  C_COUNTER_N: $pecount"
    puts "  C_COUNTER_W: $cw"

    set oc [create_bd_cell -type ip -vlnv [dict get $stdcomps oled_ctrl vlnv] $name]
279
    set xdc "$::env(TAPASCO_HOME)/common/ip/oled_pc/constraints/oled.xdc"
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
    read_xdc $xdc
    set_property PROCESSING_ORDER LATE [get_files $xdc]

    set_property -dict [list \
      "CONFIG.C_DELAY_1MS" "$p" \
      "CONFIG.C_COUNTER_N" "$pecount" \
      "CONFIG.C_COUNTER_W" [expr "round(log($width) / log(2))"] \
      "CONFIG.C_COLS" "$width" \
    ] $oc
    puts "  OLED controller frequency: $oled_freq MHz => C_DELAY_1MS = [expr round($oled_freq * 1000)]"
    return $oc
  }

  # Instantiates a processor reset generator.
  # @param name Name of the instance.
  proc createResetGen {name} {
    variable stdcomps
    puts "Creating Reset Generator ..."
    puts "  VLNV: [dict get $stdcomps rst_gen vlnv]"

    set inst [create_bd_cell -type ip -vlnv [dict get $stdcomps rst_gen vlnv] $name]
    return $inst
  }

  # Instantiates an AXI BFM core.
  # @param name Name of the instance.
  proc createAxiBFM {name} {
    variable stdcomps
    puts "Creating AXI BFM core ..."
    puts "  VLNV: [dict get $stdcomps axi_bfm vlnv]"

    set inst [create_bd_cell -type ip -vlnv [dict get $stdcomps axi_bfm vlnv] $name]
    return $inst
  }

  # Instantiates an AXI-MM full to lite stripper (assumes no bursts etc.).
  # @param name Name of the instance.
  proc createMmToLite {name} {
    variable stdcomps
    puts "Creating AXI full to lite stripper ..."
    puts "  VLNV: [dict get $stdcomps mm_to_lite vlnv]"

    set inst [create_bd_cell -type ip -vlnv [dict get $stdcomps mm_to_lite vlnv] $name]
    set_property -dict [list CONFIG.C_S_AXI_ID_WIDTH {32}] $inst
    return $inst
  }

  # Instantiates an AXI System Cache.
  # @param name Name of the instance.
  proc createSystemCache {name {num_ports 3} {size 262144} {num_sets 2}} {
    variable stdcomps
    puts "Creating AXI System Cache ..."
    puts "  VLNV: [dict get $stdcomps system_cache vlnv]"
    puts "  ports: $num_ports"
    puts "  size: $size B"
    puts "  number sets: $num_sets"

    set inst [create_bd_cell -type ip -vlnv [dict get $stdcomps system_cache vlnv] $name]
    set_property -dict [list \
      CONFIG.C_CACHE_SIZE $size \
      CONFIG.C_M_AXI_THREAD_ID_WIDTH {6} \
      CONFIG.C_NUM_GENERIC_PORTS $num_ports \
      CONFIG.C_NUM_OPTIMIZED_PORTS {0} \
      CONFIG.C_NUM_SETS $num_sets \
    ] $inst
    return $inst
  }

  # Instantiates a TPC status core.
  # @param name Name of the instance.
  # @param ids  List of kernel IDs.
351
  proc createTapascoStatus {name {ids {}}} {
352
353
    variable stdcomps
    puts "Creating TPC Status ..."
354
    puts "  VLNV: [dict get $stdcomps tapasco_status vlnv]"
355
356
357
358
359
360
361
    puts "  IDs : $ids"

    # check if ids are given, otherwise fetch automatically
    if {[llength $ids] > 0} {
      set c $ids
    } {
      set c [list]
362
      set composition [tapasco::get_composition]
363
364
365
366
367
368
369
370
371
372
      set no_kinds [llength [dict keys $composition]]
      for {set i 0} {$i < $no_kinds} {incr i} {
        set no_inst [dict get $composition $i count]
        for {set j 0} {$j < $no_inst} {incr j} {
          lappend c [dict get $composition $i id]
        }
      }
    }

    # create the IP core
373
    set inst [create_bd_cell -type ip -vlnv [dict get $stdcomps tapasco_status vlnv] $name]
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
    # make properties list
    set props [list]
    set slot 0
    foreach i $c {
      puts "  slot #$slot = $i"
      if {$slot < 128} {
        lappend props "[format CONFIG.C_SLOT_KERNEL_ID_%d [expr $slot + 1]]" "$i"
      }
      incr slot
    }
    lappend props "CONFIG.C_INTC_COUNT" "[expr [llength $c] > 96 ? 4 : ([llength $c] > 64 ? 3 : ([llength $c] > 32 ? 2 : 1))]"
    puts "  properties: $props"
    set_property -dict $props $inst
    return $inst
  }

  # Instantiates an AXI protocol converter.
  # @param name Name of the instance.
  # @param from Protocol on slave side (default: AXI4LITE)
  # @param to   Protocol on master side (default: AXI4)
  proc createProtocolConverter {name {from "AXI4LITE"} {to "AXI4"}} {
    variable stdcomps
    puts "Creating AXI Protocol converter $name $from -> $to ..."
    set vlnv [dict get $stdcomps proto_conv vlnv]
    puts "  VLNV: $vlnv"

    set inst [create_bd_cell -type ip -vlnv $vlnv $name]
    set_property -dict [list CONFIG.MI_PROTOCOL $to CONFIG.SI_PROTOCOL $from] $inst
    return $inst
  }

  # Instantiates a MIG core.
  # @param name Name of the instance.
  # @return block design cell.
  proc createMIG {name} {
    variable stdcomps
    puts "Creating MIG cores $name ..."
    set vlnv [dict get $stdcomps mig_core vlnv]
    puts "  VLNV: $vlnv"

    set inst [create_bd_cell -type ip -vlnv $vlnv $name]
    return $inst
  }

  # Instantiates a PCIe 3.0 AXI bridge core.
  # @param name Name of the instance.
  # @return block design cell.
  proc createPCIeBridge {name} {
    variable stdcomps
    puts "Creating PCIe Bridge core $name ..."
    set vlnv [dict get $stdcomps axi_pcie3_0 vlnv]
    puts "  VLNV: $vlnv"

    set inst [create_bd_cell -type ip -vlnv $vlnv $name]
    return $inst
  }

  # Instantiates a clocking wizard.
  # @param name Name of the instance.
  # @return block design cell.
  proc createClockingWizard {name} {
    variable stdcomps
    puts "Creating Clocking Wizard $name ..."
    set vlnv [dict get $stdcomps clk_wiz]
    puts "  VLNV: $vlnv"

    set inst [create_bd_cell -type ip -vlnv $vlnv $name]
    return $inst
  }

  # Returns the interface pin groups for all AXI MM interfaces on cell.
  # @param cell the object whose interfaces shall be returned
  # @parma mode filters interfaces by mode (default: Master)
  # @return list of interface pins
  proc get_aximm_interfaces {cell {mode "Master"}} {
    return [get_bd_intf_pins -of_objects $cell -filter "VLNV =~ xilinx.com:interface:aximm_rtl:* && MODE == $mode"]
  }

  # Returns the current generation mode selected by the user.
  # Default: "sim"
  proc get_generate_mode {} {
Jens Korinth's avatar
Jens Korinth committed
455
    return "bit"
456
457
458
459
460
  }

  # Returns the desired design clock frequency (in MHz) selected by the user.
  # Default: 250
  proc get_design_frequency {} {
461
    global tapasco_freq
462
    return $tapasco_freq
463
464
465
466
467
468
469
470
471
472
473
  }

  # Returns the desired design clock period (in ns) selected by the user.
  # Default: 4
  proc get_design_period {} {
    return [expr "1000 / [get_design_frequency]"]
  }

  # Returns the board preset selected by the user.
  # Default: ZC706
  proc get_board_preset {} {
474
475
    global tapasco_board_preset
    if {[info exists tapasco_board_preset]} {return $tapasco_board_preset} {return {}}
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
  }

  # Returns an array of lists consisting of VLNV and instance count of kernels in
  # the current composition.
  proc get_composition {} {
    global kernels
    return $kernels
  }

  # Returns a list of configured features for the Platform.
  proc get_platform_features {} {
    global platformfeatures
    if {[info exists platformfeatures]} { return [dict keys $platformfeatures] } { return [dict] }
  }

  # Returns a dictionary with the configuration of given Platform feature.
  proc get_platform_feature {feature} {
    global platformfeatures
Jens Korinth's avatar
Jens Korinth committed
494
495
496
497
498
    if {[info exists platformfeatures] && [dict exists $platformfeatures $feature]} {
      return [dict get $platformfeatures $feature]
    } else {
      return [dict create]
    }
499
500
501
502
503
504
505
506
  }

  # Returns true, if given feature is configured and enabled.
  proc is_platform_feature_enabled {feature} {
    global platformfeatures
    if {[info exists platformfeatures]} {
      if {[dict exists $platformfeatures $feature]} {
        if {[dict get $platformfeatures $feature "enabled"] == "true"} {
Jens Korinth's avatar
Jens Korinth committed
507
508
          return true
        }
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
      }
    }
    return false
  }

  # Returns a list of configured features for the Architecture.
  proc get_architecture_features {} {
    global architecturefeatures
    if {[info exists architecturefeatures]} { return [dict keys $architectureFeatures] } { return [dict create] }
  }

  # Returns a dictionary with the configuration of given Architecture feature.
  proc get_architecture_feature {feature} {
    global architecturefeatures
    if {[info exists architecturefeatures]} { return [dict get $architecturefeatures $feature] } { return [dict] }
  }

  # Returns true, if given feature is configured and enabled.
  proc is_architecture_feature_enabled {feature} {
    global architecturefeatures
    if {[info exists architecturefeatures]} {
      if {[dict exists $architecturefeatures $feature]} {
        if {[dict get $architecturefeatures $feature "enabled"] == "true"} {
    return true
  }
      }
    }
    return false
  }

  proc create_debug_core {clk nets {depth 4096} {stages 0} {name "u_ila_0"}} {
    puts "Creating an ILA debug core ..."
    puts "  data depth      : $depth"
    puts "  pipeline stages : $stages"
    puts "  clock           : $clk"
    puts "  number of probes: [llength $nets]"
    set dc [::create_debug_core $name ila]
    set_property C_DATA_DEPTH $depth $dc
    set_property C_TRIGIN_EN false $dc
    set_property C_TRIGOUT_EN false $dc
    set_property C_INPUT_PIPE_STAGES $stages $dc
    set_property ALL_PROBE_SAME_MU true $dc
    set_property ALL_PROBE_SAME_MU_CNT 1 $dc
    set_property C_EN_STRG_QUAL 0 $dc
    set_property C_ADV_TRIGGER 0 $dc
    set_property ALL_PROBE_SAME_MU true $dc
    # connect clock
    set_property port_width 1 [get_debug_ports $dc/clk]
    connect_debug_port u_ila_0/clk [get_nets $clk]
    set i 0
    foreach n $nets {
      puts "  current nl: $n"
      if {$i > 0} { create_debug_port $dc probe }
      set_property port_width [llength $n] [get_debug_ports $dc/probe$i]
      connect_debug_port $dc/probe$i $n
      incr i
    }
    set xdc_file "[get_property DIRECTORY [current_project]]/debug.xdc"
    puts "  xdc_file = $xdc_file"
    close [ open $xdc_file w ]
    add_files -fileset [current_fileset -constrset] $xdc_file
    set_property target_constrs_file $xdc_file [current_fileset -constrset]
    save_constraints -force
    return $dc
  }

  # Creates a tree of AXI interconnects to accomodate n connections.
  # @param name Name of the group cell
  # @param n Number of connnections (outside)
  # @param masters if true, will create n master connections, otherwise slaves
  proc create_interconnect_tree {name n {masters true}} {
    puts "Creating AXI Interconnect tree $name for $n [expr $masters ? {"masters"} : {"slaves"}]"
    puts "  tree depth: [expr int(ceil(log($n) / log(16)))]"
    puts "  instance : [current_bd_instance .]"

    # create group
    set instance [current_bd_instance .]
    set group [create_bd_cell -type hier $name]
    current_bd_instance $group

    # create hierarchical ports: clocks, resets (interconnect + peripherals)
    set m_aclk [create_bd_pin -type "clk" -dir "I" "m_aclk"]
    set m_ic_arstn [create_bd_pin -type "rst" -dir "I" "m_interconnect_aresetn"]
    set m_p_arstn [create_bd_pin -type "rst" -dir "I" "m_peripheral_aresetn"]
    set s_aclk [create_bd_pin -type "clk" -dir "I" "s_aclk"]
    set s_ic_arstn [create_bd_pin -type "rst" -dir "I" "s_interconnect_aresetn"]
    set s_p_arstn [create_bd_pin -type "rst" -dir "I" "s_peripheral_aresetn"]

    set ic_n 0
    set ics [list]
    set ns [list]
    set totalOut $n

    puts "  totalOut = $totalOut"
    if {$masters} {
      # all interconnects except the outermost slaves are driven by the master clock
      set main_aclk $m_aclk
      set main_p_arstn $m_p_arstn
      set main_ic_arstn $m_ic_arstn
      # the slave clock is only used for the last stage
      set scnd_aclk $s_aclk
      set scnd_p_arstn $s_p_arstn
      set scnd_ic_arstn $s_ic_arstn
    } {
      # all interconnects except the outermost masters are driven by the slave clock
      set main_aclk $s_aclk
      set main_p_arstn $s_p_arstn
      set main_ic_arstn $s_ic_arstn
      # the master clock is only used for the last stage
      set scnd_aclk $m_aclk
      set scnd_p_arstn $m_p_arstn
      set scnd_ic_arstn $m_ic_arstn
    }

    # special case: bypass (not necessary; only for performance, Tcl is slow)
    if {$totalOut == 1} {
      puts "  building 1-on-1 bypass"
      set bic [createInterconnect "bic" 1 1]
      set m [create_bd_intf_pin -mode Master -vlnv "xilinx.com:interface:aximm_rtl:1.0" "M000_AXI"]
      set s [create_bd_intf_pin -mode Slave -vlnv "xilinx.com:interface:aximm_rtl:1.0" "S000_AXI"]
      connect_bd_intf_net $s [get_bd_intf_pins -filter {VLNV == "xilinx.com:interface:aximm_rtl:1.0" && MODE == "Slave"} -of_objects $bic]
      connect_bd_intf_net [get_bd_intf_pins -filter {VLNV == "xilinx.com:interface:aximm_rtl:1.0" && MODE == "Master"} -of_objects $bic] $m

      connect_bd_net $m_aclk [get_bd_pins -filter {NAME =~ "M*_ACLK"} -of_objects $bic]
      connect_bd_net $s_aclk [get_bd_pins -filter {NAME =~ "S*_ACLK"} -of_objects $bic]
      connect_bd_net $m_p_arstn [get_bd_pins -filter {NAME =~ "M*_ARESETN"} -of_objects $bic]
      connect_bd_net $s_p_arstn [get_bd_pins -filter {NAME =~ "S*_ARESETN"} -of_objects $bic]
      connect_bd_net $main_aclk [get_bd_pins -filter {NAME == "ACLK"} -of_objects $bic]
      connect_bd_net $main_ic_arstn [get_bd_pins -filter {NAME == "ARESETN"} -of_objects $bic]
      current_bd_instance $instance
      return $group
    }

    # pre-compute ports at each stage
    while {$n != 1} {
      lappend ns $n
      set n [expr "int(ceil($n / 16.0))"]
    }
    if {!$masters} { set ns [lreverse $ns] }
    puts "  ports at each stage: $ns"

    # keep track of the interconnects at each stage
    set stage [list]

    # loop over nest levels
    foreach n $ns {
      puts "  generating stage $n ($ns) ..."
      set nports $n
      set n [expr "int(ceil($n / 16.0))"]
      set curr_ics [list]
      #puts "n = $n"
      for {set i 0} {$i < $n} {incr i} {
        set rest_ports [expr "$nports - $i * 16"] 
        set rest_ports [expr "min($rest_ports, 16)"]
        set nic [createInterconnect [format "ic_%03d" $ic_n] [expr "$masters ? $rest_ports : 1"] [expr "$masters ? 1 : $rest_ports"]]
        incr ic_n
        lappend curr_ics $nic
      }

      # on first level only: connect slaves to outside
      if {[llength $ics] == 0} {
        set pidx 0
        set ss [lsort [get_bd_intf_pins -filter {VLNV == "xilinx.com:interface:aximm_rtl:1.0" && MODE == "Slave"} -of_objects $curr_ics]]
        foreach s $ss {
          lappend ics [create_bd_intf_pin -mode Slave -vlnv "xilinx.com:interface:aximm_rtl:1.0" [format "S%03d_AXI" $pidx]]
          incr pidx
        }
        set ms $ics
      } {
        # in between: connect masters from previous level to slaves of current level
        set ms [lsort [get_bd_intf_pins -filter {VLNV == "xilinx.com:interface:aximm_rtl:1.0" && MODE == "Master"} -of_objects $ics]]
      }

      # masters/slaves from previous level
      set ss [lsort [get_bd_intf_pins -filter {VLNV == "xilinx.com:interface:aximm_rtl:1.0" && MODE == "Slave"} -of_objects $curr_ics]]
      set idx 0
      foreach m $ms {
        if {$masters} { 
          connect_bd_intf_net $m [lindex $ss $idx]
        } {
          connect_bd_intf_net [lindex $ss $idx] $m
        }
        incr idx
      }

      # on last level only: connect master port to outside
      if {[expr "($masters && $n == 1) || (!$masters &&  $nports == $totalOut)"]} {
        # connect outputs
        set ms [lsort [get_bd_intf_pins -filter {VLNV == "xilinx.com:interface:aximm_rtl:1.0" && MODE == "Master"} -of_objects $curr_ics]]
        set pidx 0
        foreach m $ms {
          set port [create_bd_intf_pin -mode Master -vlnv "xilinx.com:interface:aximm_rtl:1.0" [format "M%03d_AXI" $pidx]]
          connect_bd_intf_net $m $port
          incr pidx
        }
        # activate deep packet mode FIFO
        set_property -dict [list CONFIG.M00_HAS_DATA_FIFO {2}] $curr_ics
      }
      set ics $curr_ics
      # record current stage
      lappend stage $curr_ics
    }

    # connect stage-0 slave clks to outer slave clock
    if {$masters} {
      # last stage is "right-most" stage
      set main_range [lrange $stage 1 end]
      set last_stage [lindex $stage 0]
    } {
      # last stage is "left-most" stage
      set main_range [lrange $stage 0 end-1]
      set last_stage [lrange $stage end end]
    }
    # bulk connect clocks and resets of all interconnects except on the last stage to main clock
    foreach icl $main_range {
      puts "  current stage list: $icl"
      # connect all clocks to master clock
      connect_bd_net $main_aclk [get_bd_pins -filter {TYPE == "clk" && DIR == "I"} -of_objects $icl]
      # connect all m/s resets to master peripheral reset
      connect_bd_net $main_p_arstn [get_bd_pins -filter {TYPE == "rst" && DIR == "I" && NAME != "ARESETN"} -of_objects $icl]
    }
    # last stage requires separate connections
    puts "  last stage: $last_stage"
    if {$masters} {
      # connect all non-slave clocks to main clock, and only slave clocks to secondary clock
      connect_bd_net $main_aclk [get_bd_pins -filter {TYPE == "clk" && DIR == "I" && NAME !~ "S*_ACLK"} -of_objects $last_stage]
      connect_bd_net $scnd_aclk [get_bd_pins -filter {TYPE == "clk" && DIR == "I" && NAME =~ "S*_ACLK"} -of_objects $last_stage]
    } {
      # connect all non-master clocks to main clock, and only master clocks to secondary clock
      connect_bd_net $main_aclk [get_bd_pins -filter {TYPE == "clk" && DIR == "I" && NAME !~ "M*_ACLK"} -of_objects $last_stage]
      connect_bd_net $scnd_aclk [get_bd_pins -filter {TYPE == "clk" && DIR == "I" && NAME =~ "M*_ACLK"} -of_objects $last_stage]
    }
    # now connect all resets
    connect_bd_net $main_ic_arstn [get_bd_pins -filter {TYPE == "rst" && DIR == "I" && NAME == "ARESETN"} -of_objects [get_bd_cells "$group/*"]]
    connect_bd_net $m_p_arstn [get_bd_pins -filter {TYPE == "rst" && DIR == "I" && NAME =~ "M*_ARESETN"} -of_objects $last_stage]
    connect_bd_net $s_p_arstn [get_bd_pins -filter {TYPE == "rst" && DIR == "I" && NAME =~ "S*_ARESETN"} -of_objects $last_stage]

    current_bd_instance $instance
    return $group
  }

  # Creates a subsystem with clock and reset generation for a list of clocks.
  # Consists of clocking wizard + reset generators with single ext. reset in.
  # @param freqs list of name frequency (MHz) pairs, e.g., [list design 100 memory 250]
  # @param name Name of the subsystem group
  # @return Subsystem group
  proc create_subsystem_clocks_and_resets {freqs {name ClockResets}} {
    puts "Creating clock and reset subsystem ..."
    puts "  frequencies: $freqs"
    set instance [current_bd_instance .]
    set group [create_bd_cell -type hier $name]
    current_bd_instance $group

    set reset_in [create_bd_pin -dir I -type rst "reset_in"]
    set clk [createClockingWizard "clk_wiz"]
    set_property -dict [list CONFIG.USE_LOCKED {false} CONFIG.USE_RESET {false} CONFIG.NUM_OUT_CLKS [expr "[llength $freqs] / 2"]] $clk
    set clk_mode "sys_diff_clock"

    if {[catch {set_property CONFIG.CLK_IN1_BOARD_INTERFACE {sys_diff_clock} $clk}]} {
      puts "  sys_diff_clock is not supported, trying sys_clock instead"
      set clk_mode "sys_clock"
    }
    # check if external port already exists, re-use
    if {[catch [get_bd_ports "/$clk_mode"]]} {
      # connect existing top-level port
      connect_bd_net [get_bd_ports "/$clk_mode"] [get_bd_pins -filter {TYPE == clk && DIR == I} -of_objects $clk]
      # use PLL primitive for all but the first subsystem (MMCMs are limited)
      set_property -dict [list CONFIG.PRIMITIVE {PLL} CONFIG.USE_MIN_POWER {true}] $clk
    } {
      # apply board automation to create top-level port
      if {$clk_mode == "sys_diff_clock"} {
        set cport [get_bd_intf_pins -of_objects $clk]
      } {
        set cport [get_bd_pins -filter {DIR == I} -of_objects $clk]
      }
      puts "  clk: $clk, cport: $cport"
785
786
787
788
789
790
791
792
793
794
      if {$cport != {}} {
        # apply board automation
        apply_bd_automation -rule xilinx.com:bd_rule:board -config "Board_Interface $clk_mode" $cport
        puts "board automation worked, moving on"
      } {
        # last resort: try to call platform::create_clock_port
        set clk_mode "sys_clk"
        set cport [platform::create_clock_port $clk_mode]
        connect_bd_net $cport [get_bd_pins -filter {TYPE == clk && DIR == I} -of_objects $clk]
      }
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
    }

    for {set i 0; set clkn 1} {$i < [llength $freqs]} {incr i 2} {
      set name [lindex $freqs $i]
      set freq [lindex $freqs [expr $i + 1]]
      #set clkn [expr "$i / 2 + 1"]
      puts "  instantiating clock: $name @ $freq MHz"
      for {set j 0} {$j < $i} {incr j 2} {
        if {[lindex $freqs [expr $j + 1]] == $freq} {
          puts "    $name is same frequency as [lindex $freqs $j], re-using"
          break
        }
      }
      # create ports
      set port [create_bd_pin -dir O -type clk ${name}_aclk]
      set p_rst [create_bd_pin -dir O -type rst "${name}_peripheral_aresetn"]
      set i_rst [create_bd_pin -dir O -type rst "${name}_interconnect_aresetn"]

      if {[expr "$j < $i"]} {
        # simply re-wire sources
        foreach p [list "aclk" "interconnect_aresetn" "peripheral_aresetn"] dst [list $port $i_rst $p_rst] {
          puts "  j = $j,  [lindex $freqs $j]_${p}"
          set src [get_bd_pins -filter {DIR == O} -of_objects [get_bd_nets -boundary_type lower -of_objects [get_bd_pins "[lindex $freqs $j]_${p}"]]]
          connect_bd_net $src $dst
        }
      } {
        set_property -dict [list CONFIG.CLKOUT${clkn}_USED {true} CONFIG.CLKOUT${clkn}_REQUESTED_OUT_FREQ $freq] $clk
        set clkp [get_bd_pins "$clk/clk_out${clkn}"]
        set rstgen [createResetGen "${name}_rst_gen"]
        connect_bd_net $clkp $port
        connect_bd_net $reset_in [get_bd_pins "$rstgen/ext_reset_in"]
        connect_bd_net $clkp [get_bd_pins "$rstgen/slowest_sync_clk"]
        connect_bd_net [get_bd_pins "$rstgen/peripheral_aresetn"] $p_rst
        connect_bd_net [get_bd_pins "$rstgen/interconnect_aresetn"] $i_rst
        incr clkn
      }
    }

    current_bd_instance $instance
    return $group
  }

  set plugins [dict create]

  proc register_plugin {call when} {
    variable plugins
    puts "Registering new plugin call '$call' to be called at event '$when' ..."
    dict lappend plugins $when $call
  }

  proc get_plugins {when} {
    variable plugins
    if {[dict exists $plugins $when]} { return [dict get $plugins $when] } { return [list] }
  }

  proc call_plugins {when args} {
    variable plugins
    if {[dict exists $plugins $when]} {
      foreach cb [dict get $plugins $when] {
        set args [eval $cb $args]
      }
    }
    return $args
  }

  # Returns the number of processors in the system.
  proc get_number_of_processors {} {
862
863
864
    global tapasco_jobs
    if {![info exists tapasco_jobs] && ![catch {open "/proc/cpuinfo"} f]} {
      set tapasco_jobs [regexp -all -line {^processor\s} [read $f]]
865
      close $f
866
      if {$tapasco_jobs <= 0} { set tapasco_jobs 1 }
867
    }
868
    return $tapasco_jobs
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
  }

  # Parses a Vivado timing report and report the worst negative slack (WNS) value.
  # @param reportfile Filename of timing report
  proc get_wns_from_timing_report {reportfile} {
    set f [open $reportfile r]
    set tr [read $f]
    close $f
    if {[regexp {\s*WNS[^\n]*\n[^\n]*\n\s*(-?\d+\.\d+)} $tr line wns] > 0} {
      return $wns
    } {
      return 0
    }
  }

  # Returns the speed grade of the FPGA part in the current design.
  proc get_speed_grade {} {
    return [get_property SPEED [get_parts [get_property PART [current_project]]]]
  }
}