sim.tcl.template 16.1 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
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
#
# source common procs
@HEADER@

# script parameters:
set mode "@MODE@"
set no_inst @NO_INST@
@BASE_ADDRESSES@

# check Vivado version
if {[version -short] < "2014.3"} {
  puts "Vivado version >= 2014.3 required, exiting."
  exit 1
}
# check COMPILED_SIMLIB env var
if {![info exists ::env(COMPILED_SIMLIB)]} {
  puts "Missing environment variable 'COMPILED_SIMLIB' - point to directory containing Vivado Simlib compiled for ModelSim (see UG900)."
  exit 1
}
# check if DPI server lib env var is set
if {$mode == "sim" && ![info exists ::env(RCU_DPI_SERVER_LIB)]} {
  puts "Could not find DPI server lib, please set environment variable 'RCU_DPI_SERVER_LIB'."
  exit 1
}
# check no_inst: currently only 32 instances possible
if {$no_inst > 32} {
  puts "Currently only 32 instances of the target IP are supported."
  exit 1
}

# add target IP to dictionary
dict set stdcomps target_ip vlnv "@VENDOR@:@LIBRARY@:@TOP@:@VERSION@"

proc checkInstanceCount {no_inst {ex "target_ip_0"}} {
  set example [get_bd_cells $ex]
  set mc [llength [get_bd_intf_pins -of $example -filter { MODE == "Master" && CONFIG.PROTOCOL =~ "AXI*" }]]
  set sc [llength [get_bd_intf_pins -of $example -filter { MODE == "Slave" && CONFIG.PROTOCOL =~ "AXI*" }]]
  if {[expr "$mc * $no_inst"] > 320} {
    puts "ERROR: Configuration requires connection of $max_conns M-AXI interfaces; at the moment only 320 are supported."
    exit 1
  }
  if {[expr "$sc * $no_inst"] > 240} {
    puts "ERROR: Configuration requires connection of $max_conns S-AXI interfaces; at the moment only 240 are supported."
    exit 1
  }
}

proc createInstances {{no_inst 1}} {
  global stdcomps
  global base_addr
  puts "Creating $no_inst instances of target IP core ..."
  puts "  VLNV: [dict get $stdcomps target_ip vlnv]"

  set insts [list]
  set id_width [format "%.0f" [expr "ceil(log($no_inst) / log(2))"]]
  puts "  id_width: $id_width bit"

  for {set i 0} {$i < $no_inst} {incr i} {
    set inst [create_bd_cell -type ip -vlnv [dict get $stdcomps target_ip vlnv] "target_ip_$i"]
    set_property -dict [list CONFIG.C_M_AXI_GMEM32_ID_WIDTH {1} CONFIG.C_M_AXI_GMEM32_DATA_WIDTH {32} CONFIG.C_M_AXI_GMEM32_ENABLE_USER_PORTS {false}] $inst 
    lappend insts $inst

    foreach master [get_bd_intf_pins -of_objects $inst -filter {MODE=="Master" && CONFIG.PROTOCOL=~"AXI*"}] {
      puts $master
      # set the base address
      set mname [get_property NAME $master]
      if {[dict exists $base_addr $mname]} {
        set base [dict get $base_addr $mname BASE]
        puts $base
        set_property -dict [list CONFIG.C_${mname}_TARGET_ADDR $base] [get_bd_cells target_ip_$i]
      }
    } 
  }
  return $insts
}

proc createMemInterconnects {{no_inst 1} {ex "target_ip_0"} {no_masters 1}} {
  set example [get_bd_cells $ex]
  set masters [get_bd_intf_pins -of_objects $example -filter { MODE == "Master" && CONFIG.PROTOCOL =~ "AXI*" }]
  set ic_m [expr "[llength $masters] * $no_inst"]
  set ic_count [expr "$ic_m % 16 > 0 ? $ic_m / 16 + 1 : $ic_m / 16"]
  set ret [list]

  puts "Creating interconnect hierarchy toward memory ..."
  if {$masters == ""} {
    puts "  No memory interconnects necessary."
  } {
    puts "  $no_inst instances, each [llength $masters] masters"
    puts "  => requires $ic_count interconnects"
  
    set left $ic_m
    for {set i 0} {$i < $ic_count} {incr i} {
      set no_slaves [expr "min($left, 16)"]
      set ic [createInterconnect "axi_mem_ic_$i" $no_slaves $no_masters]
      incr left -$no_slaves
      lappend ret $ic
    }
  
    set top_ic [createInterconnect "axi_mem_top_ic" $ic_count 1]
  
    # connect them
    set i 0
    foreach ic $ret {
      set p [format "axi_mem_top_ic/S%02d_AXI" $i]
      puts "p = $p"
      connect_bd_intf_net [get_bd_intf_pins -of_objects $ic -filter { NAME == "M00_AXI" }] -boundary_type upper [get_bd_intf_pins $p] 
      incr i
    }
  }
  return $ret
}

proc createPeriphInterconnects {{no_inst 1} {ex "target_ip_0"} {no_slaves 1}} {
  set example [get_bd_cells $ex]
  set slaves [get_bd_intf_pins -of $example -filter { MODE == "Slave" && CONFIG.PROTOCOL =~ "AXI*" }]
  set ic_m [expr "[llength $slaves] * $no_inst"]
  set ic_count [expr "$ic_m % 16 > 0 ? $ic_m / 16 + 1 : $ic_m / 16"]

  puts "Creating interconnect hierarchy toward peripherals ..."
  puts "  $no_inst instances, each [llength $slaves] slaves"
  puts "  => requires $ic_count interconnects"

  set ret [list]
  set left $ic_m
  for {set i 0} {$i < $ic_count} {incr i} {
    set no_masters [expr "min($left, 16)"]
    set ic [createInterconnect "axi_periph_ic_$i" $no_slaves $no_masters]
    incr left -$no_masters
    lappend ret $ic
  }

  set top_ic [createInterconnect "axi_periph_top_ic" 1 [expr "$ic_count + 1"]]

  # connect them
  set i 0
  foreach ic $ret {
    set p [format "axi_periph_top_ic/M%02d_AXI" $i]
    puts "p = $p"
    connect_bd_intf_net [get_bd_intf_pins $p] -boundary_type upper [get_bd_intf_pins -of_objects $ic -filter { NAME == "S00_AXI" }]
    incr i
  }

  return $ret
}

proc connectPeriph {periph_ics ips} {
  puts "Connecting PS to peripherals ..."
  set pic 0
  set ic [lindex $periph_ics $pic]
  set conn 0
  foreach ip $ips {
    # connect target IP slaves
    set slaves [get_bd_intf_pins -of $ip -filter { MODE == "Slave" && CONFIG.PROTOCOL =~ "AXI*" }]
    foreach slave $slaves {
      set m_name [format "axi_periph_ic_$pic/M%02d_AXI" $conn]
      connect_bd_intf_net [get_bd_intf_pins $m_name] -boundary_type upper $slave
      incr conn
    }
    if {$conn == 16} { incr pic; set ic [lindex $periph_ics $pic]; set conn 0 }
  }
}

proc connectMem {mem_ics ips} {
  puts "Connecting peripherals to memory ..."
  set pic 0
  if {[llength $mem_ics] == 0} {
    puts "No masters found, not connecting memory port."
  } {
    set ic [lindex $mem_ics $pic]
    set conn 0
    foreach ip $ips {
      # connect target IP masters
      set masters [get_bd_intf_pins -of $ip -filter { MODE == "Master" && CONFIG.PROTOCOL =~ "AXI*" }]
      puts "  found [llength $masters] masters"
      foreach master $masters {
        set s_name [format "axi_mem_ic_$pic/S%02d_AXI" $conn]
	puts "  connecting $master to $s_name"
        connect_bd_intf_net $master -boundary_type upper [get_bd_intf_pins $s_name]
        incr conn
      }
      if {$conn == 16} { incr pic; set ic [lindex $mem_ics $pic]; set conn 0 }
    }
  }
}

proc connectInterrupts {ips xlconcat intc} {
  puts "Connecting interrupts ..."
  set i 0
  foreach ip $ips {
    foreach pin [get_bd_pins -of $ip -filter { TYPE == intr }] {
      set inpin "In$i"
      connect_bd_net $pin [get_bd_pins -of $xlconcat -filter "NAME == $inpin"]
      incr i
    }
  }
  connect_bd_net [get_bd_pins -of $xlconcat -filter "NAME == dout"] [get_bd_pins -of $intc -filter { NAME == "intr" }]
}

proc connectClock {ps} {
  global mode
  puts "Connecting clocks ..."

  set clk_inputs [get_bd_pins -of_objects [get_bd_cells] -filter { TYPE == "clk" && DIR == "I" }]
  switch $mode {
    "sim" {
      puts "  simulation mode, creating external port to drive PS_CLK" 
      set clk_port [create_bd_port -dir "I" -type CLK clk]
      connect_bd_net $clk_port [get_bd_pins -of_objects $ps -filter { NAME == "PS_CLK" }]
      connect_bd_net [get_bd_pins -of_objects $ps -filter { NAME == "FCLK_CLK0" }] $clk_inputs
    }
    "bit" {
      puts "  bitstream mode"
      connect_bd_net [get_bd_pins -of_objects $ps -filter { NAME == "FCLK_CLK0" }] $clk_inputs
    }
    default {
      puts "Don't know how to connect clock for mode '$mode'."
      exit 1
    }
  }
}

proc connectReset {ps rst_gen} {
  global mode
  global stdcomps
  puts "Connecting resets ..."

  set ic_vlnv [dict get $stdcomps axi_ic vlnv]
  set intc_vlnv [dict get $stdcomps axi_irqc vlnv]
  set ics [get_bd_cells -filter "VLNV == $ic_vlnv"]
  set ic_resets [get_bd_pins -of_objects $ics -filter { TYPE == "rst" && NAME == "ARESETN" }]
  set periph_resets [get_bd_pins -of_objects $ics -filter { TYPE == "rst" && NAME != "ARESETN" && DIR == "I" }]
  lappend periph_resets [get_bd_pins -of_objects [get_bd_cells -filter "NAME =~ target_ip* || VLNV == $intc_vlnv"] -filter { TYPE == "rst" }]
  puts $periph_resets

  switch $mode {
    "sim" {
      puts "  simulation mode, creating active low external port to drive PS and rst_gen resets"
      set rst_port [create_bd_port -dir "I" -type RST rst]
      set ext_resets [get_bd_pins -of_objects $rst_gen -filter { NAME == "ext_reset_in" }]
      lappend ext_resets [get_bd_pins -of_objects $ps -filter { NAME == "PS_SRSTB" || NAME == "PS_PORB" }]
      connect_bd_net $rst_port $ext_resets

      connect_bd_net [get_bd_pins -of_objects $rst_gen -filter { NAME == "interconnect_aresetn" }] $ic_resets
      connect_bd_net [get_bd_pins -of_objects $rst_gen -filter { NAME == "peripheral_aresetn" }] $periph_resets
    }
    "bit" {
      connect_bd_net [get_bd_pins -of_objects $ps -filter { NAME == "FCLK_RESET0_N" }] [get_bd_pins -of_objects $rst_gen -filter { NAME == "ext_reset_in" }]
      set ic_resets [get_bd_pins -of_objects [get_bd_cells -filter { NAME =~ "axi_mem_*" || NAME =~ "axi_periph_*" }] -filter { TYPE == "rst"  && NAME == "ARESETN"}]
      connect_bd_net [get_bd_pins -of_objects $rst_gen -filter { NAME == "interconnect_aresetn" }] $ic_resets
      set p_resets [get_bd_pins -of_objects [get_bd_cells -filter { NAME =~ "axi_mem_*" || NAME =~ "axi_periph_*" }] -filter { TYPE == "rst"  && NAME != "ARESETN"}]
      lappend p_resets [get_bd_pins -of_objects [get_bd_cells -filter { NAME =~ "target_ip*" || NAME =~ "axi_intc" }] -filter { TYPE == "rst" }]
      connect_bd_net [get_bd_pins -of_objects $rst_gen -filter { NAME == "peripheral_aresetn" }] $p_resets
    }
    default {
      puts "Don't know how to connect reset for mode '$mode'."
      exit 1
    }
  }
}

proc createMemMaster {ps} {
  global stdcomps
  set axi_bfm_vlnv [dict get $stdcomps axi_bfm vlnv]
  puts "Creating AXI BFM Master to connect to the ACP port for memory access ..."
  puts "  VLNV: $axi_bfm_vlnv"
  set axi_bfm [create_bd_cell -type ip -vlnv $axi_bfm_vlnv axi_bfm_mem_master]
  set axi_bfm_ic [createInterconnect "axi_bfm_ic" 1 1]
  set acp_port [get_bd_intf_pins -of_objects $ps -filter {NAME =~ "*HP1"}]

  set_property -dict [list CONFIG.C_DISABLE_RESET_VALUE_CHECKS {1} CONFIG.C_FUNCTION_LEVEL_INFO {0} CONFIG.C_CHANNEL_LEVEL_INFO {0} CONFIG.C_PROTOCOL_SELECTION {0}] $axi_bfm


  connect_bd_intf_net [get_bd_intf_pins -of_objects $axi_bfm_ic -filter { MODE == "Master" }] -boundary_type upper $acp_port
  connect_bd_intf_net [get_bd_intf_pins -of_objects $axi_bfm -filter { MODE == "Master" }] -boundary_type upper [get_bd_intf_pins -of_objects $axi_bfm_ic -filter { MODE == "Slave" }]
  #connect_bd_intf_net [get_bd_intf_pins -of_objects $axi_bfm -filter { MODE == "Master"}] -boundary_type upper $acp_port
  return $axi_bfm
}


# source files in output/
create_project @PROJECT_NAME@ [pwd]/@PROJECT_NAME@ -part @PART@ -force
set_property board_part {@BOARD_PART@} [current_project]
set_property IP_REPO_PATHS {@REPO_DIR@} [current_fileset]
update_ip_catalog
update_ip_catalog -add_ip {@CORE_ZIP@} -repo_path {@REPO_DIR@}

# ModelSim
set_property target_simulator ModelSim [current_project]
# compile simlib for ModelSim
# compile_simlib -language all -simulator modelsim -simulator_exec_path {/opt/cad/mentor/modelsim/latest/modeltech/bin} -library all -family all
set_property compxlib.compiled_library_dir "$::env(COMPILED_SIMLIB)" [current_project]
set_property -name {modelsim.simulate.vlog.more_options} -value {-sv} -objects [current_fileset -simset]
set_property -name {modelsim.simulate.vsim.more_options} -value {-c -keepstdout} -objects [current_fileset -simset]
set_property -name {modelsim.simulate.runtime} -value {-all} -objects [current_fileset -simset]
set_property -name {modelsim.simulate.log_all_signals} -value {true} -objects [current_fileset -simset]


# create design
startgroup
create_bd_design -quiet "system"

if {$mode == "bit"} { set ps [createZynqPS "ps7"] } { set ps [createZynqBFM "ps7"] }

# create instances of target IP
set insts [createInstances $no_inst]
checkInstanceCount $no_inst
set mem_ics [createMemInterconnects $no_inst]
if {[llength $mem_ics] > 0} {
  connect_bd_intf_net [get_bd_intf_pins "axi_mem_top_ic/M00_AXI"] -boundary_type upper [get_bd_intf_pins "ps7/S_AXI_HP0"]
}
set periph_ics [createPeriphInterconnects $no_inst]
connect_bd_intf_net [get_bd_intf_pins "ps7/M_AXI_GP0"] -boundary_type upper [get_bd_intf_pins "axi_periph_top_ic/S00_AXI"]

# connect AXI infrastructure
connectPeriph $periph_ics $insts
connectMem $mem_ics $insts

set xlconcat [createConcat "xlconcat" $no_inst]
set irqc [createIntCtrl]
set periph_top_ic [get_bd_cells axi_periph_top_ic]
set ms [lsort [get_bd_intf_pins -of $periph_top_ic -filter { MODE == "Master" }]]
set last_periph_master [lindex $ms [expr "[llength $ms] - 1"]]
connect_bd_intf_net $last_periph_master -boundary_type upper [get_bd_intf_pins -of $irqc -filter { MODE == "Slave" }]
connect_bd_net [get_bd_pins -of $irqc -filter { NAME == "irq" }] [get_bd_pins -of $ps -filter { NAME == "IRQ_F2P" }]
connectInterrupts $insts $xlconcat $irqc

if {$mode == "sim"} {
  createMemMaster $ps
}

# create processor reset generator, and connect clocks & resets
set rst_gen [create_bd_cell -type ip -vlnv [dict get $stdcomps rst_gen vlnv] rst_gen]
connectClock $ps
connectReset $ps $rst_gen
endgroup


# validate the design
assign_bd_address
validate_bd_design
save_bd_design


# create wrapper
make_wrapper -files [get_files [pwd]/$mode/@PROJECT_NAME@.srcs/sources_1/bd/system/system.bd] -top
add_files -norecurse [pwd]/$mode/@PROJECT_NAME@.srcs/sources_1/bd/system/hdl/system_wrapper.v
update_compile_order -fileset sources_1

# perform some action on the design
switch $mode {
  "sim" {
    # prepare ModelSim simulation
    update_compile_order -fileset sim_1
    set_property SOURCE_SET sources_1 [get_filesets sim_1]
    import_files -fileset sim_1 -norecurse @TEST_API_VH@
    import_files -fileset sim_1 -norecurse @TEST_HARNESS_VH@
    import_files -fileset sim_1 -norecurse @TEST_BENCH_V@
    import_files -quiet -fileset sim_1 -norecurse @PRELOAD_FILES@
    update_compile_order -fileset sim_1
    # Disabling source management mode.  This is to allow the top design properties to be set without GUI intervention.
    set_property source_mgmt_mode None [current_project]
    set_property top tb [get_filesets sim_1]
    # Re-enabling previously disabled source management mode.
    set_property source_mgmt_mode All [current_project]
    update_compile_order -fileset sim_1

    # generate simulation scripts
    launch_simulation -scripts_only
    # patch scripts: console mode only, use DPI
    [exec sed -i {s+bin_path/vsim+bin_path/vsim -c -keepstdout -sv_lib \$RCU_SV_SERVER_LIB+} [pwd]/sim/sim.sim/sim_1/behav/simulate.sh]
    [exec sed -i {s+^vsim+vsim -sv_lib $::env(RCU_DPI_SERVER_LIB)+} [pwd]/sim/sim.sim/sim_1/behav/tb_simulate.do]
  }
  "bit" {
    # generate bitstream from given design and report utilization / timing closure
    generate_target all [get_files system.bd]
    set synth_run [create_run -flow {Vivado Synthesis 2014} synth]
    set_property strategy Flow_PerfOptimized_High $synth_run
    create_run impl -parent_run synth -flow {Vivado Implementation 2014} -strategy Performance_Explore
    reset_run synth
    launch_runs impl -to_step write_bitstream
    wait_on_run impl
    open_run impl
    report_timing_summary -file timing.txt -warn_on_violation
    report_utilization -file utilization.txt
    if {[get_property PROGRESS [get_runs impl]] != "100%"} {
      error "ERROR: impl failed!"
    }
    write_bitstream -force sim.bit
  }
  default {
    puts "Don't know what to do for mode '$mode'."
  }
}