ip.tcl 16.6 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
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
232
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
279
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
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
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
namespace eval ::tapasco::ip {
  set stdcomps [dict create]

  # check if we're running inside Vivado
  if {[llength [info commands version]] > 0} {
    # source IP catalog VLNVs for the current Vivado version
    set cip [format "$::env(TAPASCO_HOME)/common/common_%s.tcl" [version -short]]
    if {! [file exists $cip]} {
      puts "Could not find $cip, Vivado [version -short] is not supported yet!"
      exit 1
    } {
      source $cip
    }
  } {
    puts "Skipping IP catalog."
  }

  # Automatically generate create proc for every known IP block.
  # Can be overridden below.
  foreach comp [dict keys $stdcomps] {
    namespace export create_${comp}
    set vlnv [dict get $stdcomps $comp "vlnv"]
    proc create_${comp} {name} {
      variable stdcomps
      set comp_name [regsub {^([^:]*::)*create_} [lindex [info level 0] 0] {}]
      set vlnv [dict get $stdcomps $comp_name "vlnv"]
      puts "Creating component $name ..."
      puts "  VLNV: $vlnv"
      return [create_bd_cell -type ip -vlnv $vlnv $name]
    }
  }

  namespace export get_vlnv

  # Returns the VLNV for a given abstract TaPaSCo name.
  proc get_vlnv {name} {
    variable stdcomps
    if {! [dict exists $stdcomps $name]} { error "VLNV for $name was not found in IP catalog!" }
    return [dict get $stdcomps $name vlnv]
  }

  # 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 create_bin_cnt {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 an AXI4 Interrupt Controller IP core.
  # @param name Name of the instance (default: axi_intc).
  # @return bd_cell of the instance.
  proc create_axi_irqc {{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 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 create_axi_ic {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).
  # @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).
  # @return bd_cell of the instance.
  proc create_ps {{name ps7} {preset [::tapasco::get_board_preset]} {freq_mhz [::tapasco::get_design_frequency]}} {
    variable stdcomps
    puts "Creating Zynq-7000 series IP core ..."
    puts "  VLNV: [dict get $stdcomps ps vlnv]"
    puts "  Preset: $preset"
    puts "  FCLK0 : $freq_mhz"

    set ps [create_bd_cell -type ip -vlnv [dict get $stdcomps ps vlnv] $name]
    if {$preset != {} && $preset != ""} {
      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
    }
    return $ps
  }

  # Instantiates a Zynq-7000 Processing System IP BFM simulation core.
  # @param name Name of the instance (default: ps7).
  # @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).
  # @return bd_cell of the instance.
  proc create_ps_bfm {{name ps7} {preset [::tapasco::get_board_preset]} {freq_mhz [::tapasco::get_design_frequency]}} {
    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 create_xlconcat {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 create_xlslice {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 create_constant {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 Dual DMA core.
  # @param name Name of the instance.
  proc create_dualdma {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
    #set folder [format "%s/common/ip/dual_dma_1.0" $::env(TAPASCO_HOME)]
    # [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 performance counter controller for the zedboard OLED display.
  # @param name Name of the instance.
  proc create_oled_ctrl {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]
    set xdc "$::env(TAPASCO_HOME)/common/ip/oled_pc/constraints/oled.xdc"
    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 an AXI-MM full to lite stripper (assumes no bursts etc.).
  # @param name Name of the instance.
  proc create_mm_to_lite {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 create_axi_cache {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 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 create_proto_conv {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 an AXI datawidth converter.
  # @param name Name of the instance.
  # @param from Data width on slave side (default: 256)
  # @param to   Data width on master side (default: 64)
  proc create_dwidth_conv {name {from "256"} {to ""}} {
    variable stdcomps
    puts "Creating AXI Datawidth converter $name $from -> $to ..."
    set vlnv [dict get $stdcomps dwidth_conv vlnv]
    puts "  VLNV: $vlnv"

    set inst [create_bd_cell -type ip -vlnv $vlnv $name]
    if {$to != ""} {
      set_property -dict [list CCONFIG.SI_DATA_WIDTH $from CONFIG.MI_DATA_WIDTH $to] $inst
    }
    return $inst
  }

  # Instantiates a System ILA core for AXI debugging.
  # @param name Name of the instance
  # @param ports Number of ports (optional, default: 1)
  # @param depth Data depth (optional, default: 1024)
  # @param stages Input pipeline stages (optional, default: 0)
  # @return block design cell (or error)
  proc create_system_ila {name {ports 1} {depth 1024} {stages 0}} {
    variable stdcomps
    puts "Creating System ILA $name ..."
    set vlnv [dict get $stdcomps system_ila vlnv]
    puts "  VLNV: $vlnv"
    set inst [create_bd_cell -type ip -vlnv $vlnv $name]
    set_property -dict [list \
      CONFIG.C_NUM_MONITOR_SLOTS $ports \
      CONFIG.C_DATA_DEPTH $depth \
      CONFIG.C_INPUT_PIPE_STAGES $stages \
    ] $inst
    return $inst
  }

  # Instantiates a TaPaSCo status core.
  # @param name Name of the instance.
  # @param ids  List of kernel IDs.
  proc create_tapasco_status {name {ids {}}} {
    variable stdcomps
    source -notrace "$::env(TAPASCO_HOME)/common/json_write.tcl"
    package require json::write

    set json [make_status_config_json]
    set json_file "[file normalize [pwd]]/tapasco_status.json"
    puts "Creating TPC Status ..."
    puts "  VLNV: [dict get $stdcomps tapasco_status vlnv]"
    puts "  IDs : $ids"
    puts "  Status: $json_file $json"
    if {[catch {open $json_file "w"} f]} {
      error "could not open file $json_file!"
    } else {
      puts -nonewline $f $json
      close $f
    }

    # generate core
    set old_pwd [pwd]
    cd $::env(TAPASCO_HOME)/common/ip/tapasco_status 
    if {[catch {exec -ignorestderr which sbt} sbt]} {
      error "could not find sbt"
    } else {
      puts "  SBT: $sbt"
    }
    if {[catch {exec -ignorestderr $sbt "run $::env(TAPASCO_HOME)/tapasco-status-cache $json_file" | tee ${json_file}.log >@stdout 2>@1}]} {
      puts stderr "Building TaPaSCO status core failed, see ${json_file}.log:"
      puts stderr [read [open ${json_file}.log r]]
      error "Could not build status core."
    }
    cd $old_pwd

    # parse log and add custom IP path to IP_REPO_PATHS
    set log [read [open ${json_file}.log r]]
    set ip_path [regsub {.*Finished, IP Core is located in ([^ \n\t]*).*} $log {\1}]
    puts "  Path to custom IP: $ip_path"
    set ip_repo_paths [get_property IP_REPO_PATHS [current_project]]
    lappend ip_repo_paths $ip_path
    set_property IP_REPO_PATHS $ip_repo_paths [current_project]
    update_ip_catalog

    # create the IP core
    return [create_bd_cell -type ip -vlnv [dict get $stdcomps tapasco_status vlnv] $name]
  }

  # Generate JSON configuration for the status core.
  proc make_status_config_json {} {
    set addr [platform::get_address_map [platform::get_pe_base_address]]
    set slots [list]
    set slot_id 0
    foreach intf [dict keys $addr] {
      switch [dict get $addr $intf "kind"] {
        "register" {
          set kind [format "%d" [regsub {.*target_ip_([0-9][0-9]).*} $intf {\1}]]
          set kid [dict get [::tapasco::get_composition] $kind id]
          lappend slots [json::write object "Type" [json::write string "Kernel"] "SlotId" $slot_id "Kernel" $kid]
          incr slot_id
        }
        "memory" {
          lappend slots [json::write object "Type" [json::write string "Memory"] "SlotId" $slot_id "Bytes" [format "%d" [dict get $addr $intf "range"]]]
          incr slot_id
        }
        "master" {}
        default { error "invalid kind: [dict get $addr $intf kind]" }
      }
    }

    set regex {([0-9][0-9][0-9][0-9]).([0-9][0-9]*)}
    set no_pes [llength [arch::get_processing_elements]]
    set no_intc [expr "$no_pes > 96 ? 4 : ($no_pes > 64 ? 3 : ($no_pes > 32 ? 2 : 1))"]
    set ts [clock seconds]

    return [json::write object \
      "Composition" [json::write array {*}$slots] \
      "Timestamp" [expr "$ts - ($ts \% 86400)"] \
      "Interrupt Controllers" $no_intc \
      "Versions" [json::write array \
        [json::write object "Software" [json::write string "Vivado"] "Year" [regsub $regex [version -short] {\1}] "Release" [regsub $regex [version -short] {\2}]] \
        [json::write object "Software" [json::write string "TaPaSCo"] "Year" [regsub $regex [::tapasco::get_tapasco_version] {\1}] "Release" [regsub $regex [::tapasco::get_tapasco_version] {\2}]] \
      ] \
      "Clocks" [json::write array \
        [json::write object "Domain" [json::write string "Host"] "Frequency" [::tapasco::get_host_frequency]] \
        [json::write object "Domain" [json::write string "Design"] "Frequency" [::tapasco::get_design_frequency]] \
        [json::write object "Domain" [json::write string "Memory"] "Frequency" [::tapasco::get_mem_frequency]] \
      ] \
      "Capabilities" [json::write object "Capabilities 0" [::tapasco::get_capabilities_flags]] \
    ]
  }
}