From b5aa176c6b351f132f8c6e439e5b31cea5db0d22 Mon Sep 17 00:00:00 2001 From: Jeroen Hegeman <jeroen.hegeman@cern.ch> Date: Fri, 19 Apr 2024 12:37:43 +0200 Subject: [PATCH 1/5] Make boolean check less restrictive --- scripts/vivado_utils.tcl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/vivado_utils.tcl b/scripts/vivado_utils.tcl index 230d8b0..2703cb9 100644 --- a/scripts/vivado_utils.tcl +++ b/scripts/vivado_utils.tcl @@ -91,7 +91,7 @@ proc vivado_utils::vivado_create_ip {ip_name \ set_property -dict $module_properties [get_ips $module_name] } - if { $include_example_design == true } { + if { $include_example_design } { # Create the corresponding example design/project. open_example_project -force -dir $dir_name_base -in_process [get_ips $module_name] } -- GitLab From d4469b7b197e4a3b2ef2f94267c3aebdadf04500 Mon Sep 17 00:00:00 2001 From: Jeroen Hegeman <jeroen.hegeman@cern.ch> Date: Fri, 19 Apr 2024 12:39:45 +0200 Subject: [PATCH 2/5] Remove spurious whitespace from tcl script --- scripts/vivado_create_ips.tcl | 26 +++++++++++++------------- scripts/vivado_utils.tcl | 12 ++++++------ 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/scripts/vivado_create_ips.tcl b/scripts/vivado_create_ips.tcl index dfdcc7c..67738da 100644 --- a/scripts/vivado_create_ips.tcl +++ b/scripts/vivado_create_ips.tcl @@ -59,7 +59,7 @@ set parameters { set usage "- A script to (re)generate Xilinx IP cores from stored parameters" -if { [catch {array set options [cmdline::getoptions ::argv $parameters $usage]}] } { +if {[catch {array set options [cmdline::getoptions ::argv $parameters $usage]}]} { puts [cmdline::usage $parameters $usage] exit 1 } @@ -89,22 +89,22 @@ set target_board $options(target-board) set user_ip_repo [file normalize $options(user-ip-repo)] # Perform some basic checks on the user-IP repo path. -if { $user_ip_repo ne "" } { - if { ! [file exists $user_ip_repo] } { +if {$user_ip_repo ne ""} { + if {! [file exists $user_ip_repo]} { error "Path '$user_ip_repo' does not exist" } - if { ! [file isdirectory $user_ip_repo] } { + if {! [file isdirectory $user_ip_repo]} { error "Path '$user_ip_repo' is not a directory" } } puts "$sep_line" -if { $user_ip_repo ne "" } { +if {$user_ip_repo ne ""} { puts "Including user-IP repository '$user_ip_repo'" } set s_or_not "" set tmp [llength $ip_scripts] -if { $tmp > 1 || $tmp == 0 } { +if {$tmp > 1 || $tmp == 0} { set s_or_not "s" } puts "Found [llength $ip_scripts] matching IP core creation script$s_or_not" @@ -140,22 +140,22 @@ foreach {script_name} $ip_scripts { # Have Vivado run the script and generate the IP core. set args "" - if { [string length $target_part] != 0 } { + if {[string length $target_part] != 0} { set args [concat $args "-target-part \"$target_part\""] } - if { [string length $target_board] != 0 } { + if {[string length $target_board] != 0} { set args [concat $args "-target-board \"$target_board\""] } - if { [string length $user_ip_repo] != 0 } { + if {[string length $user_ip_repo] != 0} { set args [concat $args "-user-ip-repo \"$user_ip_repo\""] } set full_args "" - if { [string length $args] != 0 } { + if {[string length $args] != 0} { set full_args "-tclargs $args" } set full_cmd [concat $vivado_cmd $script_name $full_args] set status [catch {exec {*}$full_cmd} err] - if { $status != 0 } { + if {$status != 0} { puts "A problem occurred:" set tmp {} foreach i [split $err "\n"] { @@ -182,7 +182,7 @@ foreach {script_name} $ip_scripts { # under 'imports') and copy these as well. set ex_file_name "imports" set created_ex_dir [glob_recursive $dir_name $ex_file_name d] - if { [llength $created_ex_dir] > 0 } { + if {[llength $created_ex_dir] > 0} { file rename $created_ex_dir [file join $target_dir_name "example_imports"] } } @@ -191,7 +191,7 @@ foreach {script_name} $ip_scripts { file delete -force -- $dir_name } -if { $status == 0 } { +if {$status == 0} { puts "Done" } else { puts "Failed" diff --git a/scripts/vivado_utils.tcl b/scripts/vivado_utils.tcl index 2703cb9..4dee45b 100644 --- a/scripts/vivado_utils.tcl +++ b/scripts/vivado_utils.tcl @@ -26,7 +26,7 @@ proc vivado_utils::run_vivado_create_ip {ip_name \ [list user-ip-repo.arg "" "Path to an optional user-IP repository"] ] - if { [catch {array set options [cmdline::getoptions argv $parameters]}] } { + if {[catch {array set options [cmdline::getoptions argv $parameters]}]} { puts [cmdline::usage $parameters] exit 1 } @@ -65,13 +65,13 @@ proc vivado_utils::vivado_create_ip {ip_name \ set project_name ${module_name}_ip create_project -force $project_name $dir_name - if { $part ne "" } { + if {$part ne ""} { set_property PART $part [current_project] } - if { $board ne "" } { + if {$board ne ""} { set_property BOARD_PART $board [current_project] } - if { $user_ip_repo_path ne "" } { + if {$user_ip_repo_path ne ""} { set ip_repo_path_ori [get_property ip_repo_paths [current_fileset]] set_property ip_repo_paths "$ip_repo_path_ori $user_ip_repo_path" [current_fileset] update_ip_catalog -rebuild @@ -87,11 +87,11 @@ proc vivado_utils::vivado_create_ip {ip_name \ -module_name $module_name # Apply the IP settings. - if { [dict size $module_properties] != 0 } { + if {[dict size $module_properties] != 0} { set_property -dict $module_properties [get_ips $module_name] } - if { $include_example_design } { + if {$include_example_design} { # Create the corresponding example design/project. open_example_project -force -dir $dir_name_base -in_process [get_ips $module_name] } -- GitLab From d29d7fc616bd627c1d73b42db3ebaa05e1fb0afd Mon Sep 17 00:00:00 2001 From: jhegeman <jeroen.hegeman@cern.ch> Date: Wed, 29 May 2024 11:59:02 +0200 Subject: [PATCH 3/5] Make the IP core recreation script work on Windows too Windows suffers from path name length restrictions. The script now works better under such conditions. The speed of the script has also been improved. --- scripts/pkgIndex.tcl | 2 + scripts/tcl_utils.tcl | 49 +++++ scripts/vivado_create_ips.tcl | 332 ++++++++++++++++++++-------------- scripts/vivado_utils.tcl | 44 +++-- 4 files changed, 281 insertions(+), 146 deletions(-) create mode 100644 scripts/tcl_utils.tcl diff --git a/scripts/pkgIndex.tcl b/scripts/pkgIndex.tcl index 0848997..2874e76 100644 --- a/scripts/pkgIndex.tcl +++ b/scripts/pkgIndex.tcl @@ -8,4 +8,6 @@ # script is sourced, the variable $dir must contain the # full path name of this file's directory. +package ifneeded repo_utils 0.1 [list source [file join $dir repo_utils.tcl]] +package ifneeded tcl_utils 0.1 [list source [file join $dir tcl_utils.tcl]] package ifneeded vivado_utils 0.1 [list source [file join $dir vivado_utils.tcl]] diff --git a/scripts/tcl_utils.tcl b/scripts/tcl_utils.tcl new file mode 100644 index 0000000..abe8743 --- /dev/null +++ b/scripts/tcl_utils.tcl @@ -0,0 +1,49 @@ +######################################################################## + +package require Tcl 8.5 + +namespace eval tcl_utils { + set version 0.1 +} + +package provide tcl_utils $tcl_utils::version + +######################################################################## + +proc tcl_utils::map {lambda list} { + variable result {} + foreach item $list { + lappend result [apply $lambda $item] + } + return $result +} + +######################################################################## + +# Crude, but seems to be sufficient. +proc tcl_utils::get_os {} { + variable os [lindex $::tcl_platform(platform) 0] + return $os +} + +######################################################################## + +proc tcl_utils::os_is_windows {} { + set is_win [expr {[get_os] == "windows"}] + return $is_win +} + +######################################################################## + +proc tcl_utils::source_with_args {file_name {source_args {}} {cmd_line_args {}}} { + variable argv $::argv + variable argc $::argc + set ::argv $cmd_line_args + set ::argc [llength $cmd_line_args] + variable code [catch {uplevel [list source $source_args $file_name]} return] + set ::argv $argv + set ::argc $argc + return -code $code $return +} + +######################################################################## diff --git a/scripts/vivado_create_ips.tcl b/scripts/vivado_create_ips.tcl index 67738da..ad4b034 100644 --- a/scripts/vivado_create_ips.tcl +++ b/scripts/vivado_create_ips.tcl @@ -16,21 +16,21 @@ # vivado -mode batch -notrace -nolog -nojou -quiet -source scripts/vivado_create_ips.tcl -tclargs -target-part xcku15p-ffva1760-2-e ######################################################################## -package require cmdline +package require Tcl 8.5 -set script_dir [file dirname [file normalize [info script]]] +variable script_dir [file dirname [file normalize [info script]]] set env(TCLLIBPATH) [list $script_dir] lappend ::auto_path $script_dir +package require cmdline +package require tcl_utils + ######################################################################## # The IP core generation scripts are are found by name using the # following pattern. set script_name_pattern_base "vivado_create_ip_" -# This is how we want to call Vivado. -set vivado_cmd {vivado -mode batch -notrace -nolog -nojou -quiet -source} - set sep_line [string repeat "-" 60] ######################################################################## @@ -38,8 +38,11 @@ set sep_line [string repeat "-" 60] proc glob_recursive {{dir .} {filespec *} {types {b c f l p s}}} { set files [glob -nocomplain -types $types -dir $dir -- $filespec] foreach x [glob -nocomplain -types {d} -dir $dir -- *] { - set files [concat $files \ - [glob_recursive [file join [pwd] $x] $filespec $types]] + # We don't follow links for directories. + if {[file type [file normalize $x]] != "link"} { + set files [concat $files \ + [glob_recursive [file join [pwd] $x] $filespec $types]] + } } set filelist {} foreach x $files { @@ -50,154 +53,221 @@ proc glob_recursive {{dir .} {filespec *} {types {b c f l p s}}} { ######################################################################## -set parameters { - {target-part.arg "" "The FPGA to target"} - {target-board.arg "" "The evaluation board to target"} - {ip-name-pattern.arg ".*" "Regular expression describing which IP core(s) to generate"} - {user-ip-repo.arg "" "Path to an optional user-IP repository to include"} +# This would work in TCL 8.6. Vivado 2022.2 seems to provide 8.5. +# proc tempdir {template} { +# close [file tempfile path $template] +# file delete $path +# file mkdir $path +# return $path +# } + +proc tempdir {{template ""}} { + set tmp [pwd] + if {[file exists /tmp]} { + set tmp /tmp + } + catch {set tmp $::env(TRASH_FOLDER)} + catch {set tmp $::env(TMP)} + catch {set tmp $::env(TEMP)} + set suffix [pid] + if [string length $template] { + set suffix ${template}_[pid] + } + set dir_name [file join $tmp $suffix] } -set usage "- A script to (re)generate Xilinx IP cores from stored parameters" +######################################################################## -if {[catch {array set options [cmdline::getoptions ::argv $parameters $usage]}]} { - puts [cmdline::usage $parameters $usage] - exit 1 -} +namespace eval ns { + set parameters { + {target-part.arg "" "The FPGA to target"} + {target-board.arg "" "The evaluation board to target"} + {ip-name-pattern.arg ".*" "Regular expression describing which IP core(s) to generate"} + {user-ip-repo.arg "" "Path to an optional user-IP repository to include"} + } -# Find all the Vivado IP core creation scripts. -set ip_name_pattern $options(ip-name-pattern) -set script_name_pattern "${script_name_pattern_base}*\.tcl" -set ip_scripts_tmp [glob_recursive . $script_name_pattern] -# NOTE: There is a bit of fiddling here with the IP name pattern. We -# want to make sure to apply the filtering only to the IP name part of -# the file names (while still filtering a list of file names). This is -# not super efficient. -set ip_scripts {} -foreach file_name $ip_scripts_tmp { - regexp "${script_name_pattern_base}(.*)\.tcl$" $file_name dummy ip_name - if {[regexp $ip_name_pattern $ip_name]} { - lappend ip_scripts $file_name + set usage "- A script to (re)generate Xilinx IP cores from stored parameters" + + if {[catch {array set options [cmdline::getoptions ::argv $parameters $usage]}]} { + puts [cmdline::usage $parameters $usage] + exit 1 + } + + # Find all the Vivado IP core creation scripts. + set ip_name_pattern $options(ip-name-pattern) + + set script_name_pattern "${script_name_pattern_base}*\.tcl" + set ip_scripts_tmp [glob_recursive . $script_name_pattern] + # NOTE: There is a bit of fiddling here with the IP name pattern. We + # want to make sure to apply the filtering only to the IP name part of + # the file names (while still filtering a list of file names). This is + # not super efficient. + set ip_scripts {} + foreach file_name $ip_scripts_tmp { + regexp "${script_name_pattern_base}(.*)\.tcl$" $file_name dummy ip_name + if {[regexp $ip_name_pattern $ip_name]} { + lappend ip_scripts $file_name + } } -} -# Sort and remove duplicates (which may arise from symlinks). -set ip_scripts [lsort -unique $ip_scripts] + # Sort and remove duplicates (which may arise from symlinks). + set ip_scripts [lsort -unique $ip_scripts] -# Get the values of all options. -set target_part $options(target-part) -set target_board $options(target-board) -set user_ip_repo [file normalize $options(user-ip-repo)] + # Get the values of all options. + set target_part $options(target-part) + set target_board $options(target-board) + set user_ip_repo [file normalize $options(user-ip-repo)] -# Perform some basic checks on the user-IP repo path. -if {$user_ip_repo ne ""} { - if {! [file exists $user_ip_repo]} { - error "Path '$user_ip_repo' does not exist" + # Perform some basic checks on the user-IP repo path. + if {$user_ip_repo ne ""} { + if {! [file exists $user_ip_repo]} { + error "Path '$user_ip_repo' does not exist" + } + if {! [file isdirectory $user_ip_repo]} { + error "Path '$user_ip_repo' is not a directory" + } + } + + puts "$sep_line" + if {$user_ip_repo ne ""} { + puts "Including user-IP repository '$user_ip_repo'" } - if {! [file isdirectory $user_ip_repo]} { - error "Path '$user_ip_repo' is not a directory" + set s_or_not "" + set tmp [llength $ip_scripts] + if {$tmp > 1 || $tmp == 0} { + set s_or_not "s" } -} + puts "Found [llength $ip_scripts] matching IP core creation script$s_or_not" + # foreach item $ip_scripts { + # puts $item + # } + puts "$sep_line" -puts "$sep_line" -if {$user_ip_repo ne ""} { - puts "Including user-IP repository '$user_ip_repo'" -} -set s_or_not "" -set tmp [llength $ip_scripts] -if {$tmp > 1 || $tmp == 0} { - set s_or_not "s" -} -puts "Found [llength $ip_scripts] matching IP core creation script$s_or_not" -# foreach item $ip_scripts { -# puts $item -# } -puts "$sep_line" + # Now process all found scripts. + set status 0 + foreach {script_name} $ip_scripts { -# Now process all found scripts. -set status 0 -foreach {script_name} $ip_scripts { + # Derive the Vivado project name from the script name. (NOTE: This + # relies on a naming convention.) + set vivado_project_name [file rootname [file tail $script_name]] - # Derive the Vivado project name from the script name. (NOTE: This - # relies on a naming convention.) - set vivado_project_name [file rootname [file tail $script_name]] + # Derive the IP core name from the vivado project name. + set ip_name [regsub ***=$script_name_pattern_base $vivado_project_name ""] - # Derive the IP core name from the vivado project name. - set ip_name [regsub ***=$script_name_pattern_base $vivado_project_name ""] + # Derive a more-or-less descriptive name for progress + # reporting. (Useful in case there are multiple IP creation + # scripts with the same (IP) name.) + regsub ***=[pwd] $script_name "" tmp + set chunks [file split [file dirname $tmp]] + # NOTE: The following is a bit ad hoc. It removes path + # separators. + set unwanteds [list \\ /] + set chunks_clean $chunks + foreach unwanted $unwanteds { + set chunks_clean [lsearch -all -inline -not -exact $chunks_clean $unwanted] + } + puts "Processing \"[join $chunks_clean :]:$ip_name\"" + puts "$sep_line" - # Derive a descriptive 'parent' name. (Useful in case there are - # multiple IP creation scripts with the same (IP) name. - set grandparent_name [file tail [file dirname [file dirname [file dirname [file dirname $script_name]]]]] - set parent_name [file tail [file dirname [file dirname [file dirname $script_name]]]] - puts "Processing \"$grandparent_name:$parent_name:$ip_name\"" - puts "$sep_line" + # Derive the expected Vivado project (temporary) directory name + # from the project name. + set tmp_dir_name [tempdir] + set dir_name [file join $tmp_dir_name $vivado_project_name] - # Derive the expected Vivado project directory name from the - # project name. - set dir_name $vivado_project_name + # Remove any possible left-over Vivado project directory. + file delete -force -- $dir_name + # And create a clean directory. + file mkdir $dir_name - # Remove any possible left-over Vivado project directory. - file delete -force -- $dir_name + # This default case will work on Linux. + set dir_name_short $dir_name + # For OSs that suffer from path length limitations, however, we + # need a trick. + if {[tcl_utils::os_is_windows]} { + set win_virtual_drive Z: + set dir_name_short ${win_virtual_drive}/ + if {[catch {exec subst $win_virtual_drive $dir_name}]} { + puts "Virtual drive $win_virtual_drive is not available.\ + Please unmap that drive\ + ('subst $win_virtual_drive /d')\ + for this script to work." + break + } + } - # Have Vivado run the script and generate the IP core. - set args "" - if {[string length $target_part] != 0} { - set args [concat $args "-target-part \"$target_part\""] - } - if {[string length $target_board] != 0} { - set args [concat $args "-target-board \"$target_board\""] - } - if {[string length $user_ip_repo] != 0} { - set args [concat $args "-user-ip-repo \"$user_ip_repo\""] - } - set full_args "" - if {[string length $args] != 0} { - set full_args "-tclargs $args" - } - set full_cmd [concat $vivado_cmd $script_name $full_args] - set status [catch {exec {*}$full_cmd} err] - if {$status != 0} { - puts "A problem occurred:" - set tmp {} - foreach i [split $err "\n"] { - append tmp " !!! $i\n" + # Have Vivado run the script and generate the IP core. + set args "-work-dir \"$dir_name_short\"" + if {[string length $target_part] != 0} { + set args [concat $args "-target-part \"$target_part\""] + } + if {[string length $target_board] != 0} { + set args [concat $args "-target-board \"$target_board\""] + } + if {[string length $user_ip_repo] != 0} { + set args [concat $args "-user-ip-repo \"$user_ip_repo\""] + } + # NOTE: Calling Vivado here as a separate process is not very + # efficient. So we source the script in a separate namespace + # instead. This should prevent the script from affecting our + # local variables. This is a bit more involved, but it + # definitely speeds things up. + variable err + variable status [catch { + namespace eval sub_ns { + # NOTE: The '-quiet' flag to the 'source' command is + # Vivado-specific. + set ::ns::status [tcl_utils::source_with_args $::ns::script_name {-quiet} $::ns::args]] + } + } err] + if {$status != 0} { + puts "A problem occurred:" + set tmp {} + foreach i [split $err "\n"] { + append tmp " !!! $i\n" + } + puts -nonewline $tmp + break + } else { + + set target_base_name [file dirname $script_name] + set target_sub_name $ip_name + set target_dir_name [file join $target_base_name $target_sub_name] + file delete -force $target_dir_name + file mkdir $target_dir_name + + # Find the produced IP core name and move the produced IP core + # to where it should go. (I.e. to where the original script + # lives.) + set ip_file_name "$ip_name.xci" + set created_ip_file [lindex [glob_recursive $dir_name_short $ip_file_name] 0] + file rename $created_ip_file $target_dir_name + + # Find the produced example design files (or at least the ones + # under 'imports') and copy these as well. + set ex_file_name "imports" + set created_ex_dir [glob_recursive $dir_name_short $ex_file_name d] + if {[llength $created_ex_dir] > 0} { + file rename $created_ex_dir [file join $target_dir_name "example_imports"] + } } - puts -nonewline $tmp - break - } else { - set target_base_name [file dirname $script_name] - set target_sub_name $ip_name - set target_dir_name [file join $target_base_name $target_sub_name] - file delete -force $target_dir_name - file mkdir $target_dir_name - - # Find the produced IP core name and move the produced IP core - # to where it should go. (I.e. to where the original script - # lives.) - set ip_file_name "$ip_name.xci" - set created_ip_file [lindex [glob_recursive $dir_name $ip_file_name] 0] - file rename $created_ip_file $target_dir_name - - # Find the produced example design files (or at least the ones - # under 'imports') and copy these as well. - set ex_file_name "imports" - set created_ex_dir [glob_recursive $dir_name $ex_file_name d] - if {[llength $created_ex_dir] > 0} { - file rename $created_ex_dir [file join $target_dir_name "example_imports"] + # Remove the Vivado project directory. + file delete -force -- $dir_name_short + + # Unmap the virtual drive, if we mapped it. + if {[tcl_utils::os_is_windows]} { + exec subst $win_virtual_drive /d } } - # Remove the Vivado project directory. - file delete -force -- $dir_name -} + if {$status == 0} { + puts "Done" + } else { + puts "Failed" + } + puts "$sep_line" -if {$status == 0} { - puts "Done" -} else { - puts "Failed" + exit $status } -puts "$sep_line" - -exit $status ######################################################################## diff --git a/scripts/vivado_utils.tcl b/scripts/vivado_utils.tcl index 4dee45b..5322880 100644 --- a/scripts/vivado_utils.tcl +++ b/scripts/vivado_utils.tcl @@ -1,5 +1,7 @@ ######################################################################## +package require Tcl 8.5 + namespace eval vivado_utils { set version 0.1 } @@ -20,32 +22,38 @@ proc vivado_utils::run_vivado_create_ip {ip_name \ default_part \ default_board \ argv} { - set parameters [list \ - [list target-part.arg $default_part "The FPGA to target"] \ - [list target-board.arg $default_board "The development board to target"] \ - [list user-ip-repo.arg "" "Path to an optional user-IP repository"] - ] - + variable parameters [list \ + [list target-part.arg $default_part "The FPGA to target"] \ + [list target-board.arg $default_board "The development board to target"] \ + [list user-ip-repo.arg "" "Path to an optional user-IP repository"] \ + [list work-dir.arg "" "Path to the (temporary) working directory to use"] + ] + + variable options if {[catch {array set options [cmdline::getoptions argv $parameters]}]} { puts [cmdline::usage $parameters] exit 1 } # The part to target. - set part $options(target-part) + variable part $options(target-part) # The board to target. - set board $options(target-board) + variable board $options(target-board) # An optional user IP repository. - set user_ip_repo_path $options(user-ip-repo) + variable user_ip_repo_path $options(user-ip-repo) + + # Working directory path. + variable work_dir $options(work-dir) vivado_utils::vivado_create_ip \ $ip_name $ip_vendor $ip_library $ip_version \ $module_name $module_properties \ $include_example_design \ $part $board \ - $user_ip_repo_path + $user_ip_repo_path \ + $work_dir } ######################################################################## @@ -59,10 +67,16 @@ proc vivado_utils::vivado_create_ip {ip_name \ {include_example_design false} \ {part ""} \ {board ""} \ - {user_ip_repo_path ""}} { - set dir_name_base vivado_create_ip_$module_name - set dir_name [file join $dir_name_base ${module_name}_ip] - set project_name ${module_name}_ip + {user_ip_repo_path ""} \ + {work_dir ""}} { + variable dir_name_base + if {[string length $work_dir]} { + set dir_name_base $work_dir + } else { + set dir_name_base [pwd] + } + variable dir_name [file join $dir_name_base ${module_name}_ip] + variable project_name ${module_name}_ip create_project -force $project_name $dir_name if {$part ne ""} { @@ -72,7 +86,7 @@ proc vivado_utils::vivado_create_ip {ip_name \ set_property BOARD_PART $board [current_project] } if {$user_ip_repo_path ne ""} { - set ip_repo_path_ori [get_property ip_repo_paths [current_fileset]] + variable ip_repo_path_ori [get_property ip_repo_paths [current_fileset]] set_property ip_repo_paths "$ip_repo_path_ori $user_ip_repo_path" [current_fileset] update_ip_catalog -rebuild } -- GitLab From 6eba352a5fb7b736b705f15908403935d4d150ea Mon Sep 17 00:00:00 2001 From: jhegeman <jeroen.hegeman@cern.ch> Date: Wed, 29 May 2024 12:00:32 +0200 Subject: [PATCH 4/5] Update IP core creation instructions in README --- README.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 5bce461..4d2a680 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ cores are (re)created using Vivado Tcl scripts. After cloning this repo, please run the following to generate all IP cores on Linux, or use the corresponding .bat file on Windows. +### On Linux The simplest way is to simply call the IP core creation script from inside the cloned repository: @@ -18,13 +19,13 @@ pushd tclink popd ``` -For the impatient, the above can be sped up (albeit at the cost of -some loss of efficiency) when GNU parallel is available: +### On Windows + +Open a ```cmd``` window in the ```tclink``` repository +directory, and then enter the following command. ``` -pushd tclink -parallel ./scripts/vivado_create_ips.sh ::: $(find . -iname '*vivado_create_ip_*.tcl' | sed -s 's/.*vivado_create_ip_\(.*\)\.tcl/\1/g') -popd +call <path-to-vivado-installation>\bin\vivado.bat -notrace -mode batch -source .\scripts\vivado_create_ips.tcl ``` ## TClink concept -- GitLab From cb4d9daf1b2817eb7813d66890c702d5579c882b Mon Sep 17 00:00:00 2001 From: jhegeman <jeroen.hegeman@cern.ch> Date: Thu, 21 Nov 2024 11:46:49 +0100 Subject: [PATCH 5/5] Improve Windows support Most notably: - Improve robustness related to the unmapping of the temporary drive in case of errors. - Implement dedicated '--include-ips' and '--exclude-ips' options because Windows users cannot specify complex regexs on the command line. (Special characters tend to get mangled.) --- scripts/vivado_create_ips.sh | 21 +++++++++++--- scripts/vivado_create_ips.tcl | 52 +++++++++++++++++++++++++---------- 2 files changed, 55 insertions(+), 18 deletions(-) diff --git a/scripts/vivado_create_ips.sh b/scripts/vivado_create_ips.sh index c55046d..dad61c2 100755 --- a/scripts/vivado_create_ips.sh +++ b/scripts/vivado_create_ips.sh @@ -1,7 +1,7 @@ #!/bin/bash # Disable wildcard expansion because of the possibility of a '.*' as -# ip-name-pattern. +# IP include/exclude patterns. set -o noglob args=() @@ -24,12 +24,25 @@ done TCL_ARGS="" +if [ ! -z "${args[0]}" ] && [ ! -z "${ip_include_pattern}" ]; then + echo "Cannot specify both -include-ips and a free IP selection argument" + exit 1 +fi + if [ ! -z "${args[0]}" ]; then - TCL_ARGS="-ip-name-pattern ${args[0]}" + TCL_ARGS="-include-ips ${args[0]}" +fi + +if [ ! -z "${include_ips}" ]; then + TCL_ARGS="$TCL_ARGS -include-ips ${include_ips}" +fi + +if [ ! -z "${exclude_ips}" ]; then + TCL_ARGS="$TCL_ARGS -exclude-ips ${exclude_ips}" fi -if [ ! -z "${args[1]}" ]; then - TCL_ARGS="$TCL_ARGS -target-part ${args[1]}" +if [ ! -z "${target_part}" ]; then + TCL_ARGS="$TCL_ARGS -target-part ${target_part}" fi if [ ! -z "${user_ip_repo}" ]; then diff --git a/scripts/vivado_create_ips.tcl b/scripts/vivado_create_ips.tcl index ad4b034..190d4da 100644 --- a/scripts/vivado_create_ips.tcl +++ b/scripts/vivado_create_ips.tcl @@ -82,7 +82,8 @@ namespace eval ns { set parameters { {target-part.arg "" "The FPGA to target"} {target-board.arg "" "The evaluation board to target"} - {ip-name-pattern.arg ".*" "Regular expression describing which IP core(s) to generate"} + {include-ips.arg ".*" "Regular expression describing which IP core(s) to generate"} + {exclude-ips.arg "" "Regular expression describing which IP core(s) not to generate"} {user-ip-repo.arg "" "Path to an optional user-IP repository to include"} } @@ -94,19 +95,23 @@ namespace eval ns { } # Find all the Vivado IP core creation scripts. - set ip_name_pattern $options(ip-name-pattern) + set ip_include_pattern $options(include-ips) + set ip_exclude_pattern $options(exclude-ips) set script_name_pattern "${script_name_pattern_base}*\.tcl" set ip_scripts_tmp [glob_recursive . $script_name_pattern] - # NOTE: There is a bit of fiddling here with the IP name pattern. We - # want to make sure to apply the filtering only to the IP name part of - # the file names (while still filtering a list of file names). This is - # not super efficient. + # NOTE: There is a bit of fiddling here with the IP name + # patterns. We want to make sure to apply the filtering only to + # the IP name part of the file names (while still filtering a list + # of file names). This is not super efficient. set ip_scripts {} + set need_exclude_filter [expr {$ip_exclude_pattern eq ""}] foreach file_name $ip_scripts_tmp { regexp "${script_name_pattern_base}(.*)\.tcl$" $file_name dummy ip_name - if {[regexp $ip_name_pattern $ip_name]} { - lappend ip_scripts $file_name + if {[regexp $ip_include_pattern $ip_name]} { + if {$need_exclude_filter || ![regexp $ip_exclude_pattern $ip_name]} { + lappend ip_scripts $file_name + } } } @@ -141,7 +146,21 @@ namespace eval ns { # foreach item $ip_scripts { # puts $item # } - puts "$sep_line" + # puts "$sep_line" + + # Suppress some of the cluttering Vivado output. + set_msg_config -string "Refreshing IP repositories" -suppress + set_msg_config -string "Loaded user IP repository" -suppress + set_msg_config -string "Loaded Vivado IP repository" -suppress + set_msg_config -string "Using compiled simulation libraries for IPs" -suppress + set_msg_config -string "Exporting simulation files" -suppress + set_msg_config -string "Script generated" -suppress + set_msg_config -string "Pre-compiled simulation library path" -suppress + set_msg_config -string "Using boost library" -suppress + set_msg_config -regexp -string {.*Generating.*target.*} -suppress + if {![tcl_utils::os_is_windows]} { + set_msg_config -string "The Windows operating system has path length limitations" -suppress + } # Now process all found scripts. set status 0 @@ -166,8 +185,8 @@ namespace eval ns { foreach unwanted $unwanteds { set chunks_clean [lsearch -all -inline -not -exact $chunks_clean $unwanted] } - puts "Processing \"[join $chunks_clean :]:$ip_name\"" puts "$sep_line" + puts "Processing \"[join $chunks_clean :]:$ip_name\"" # Derive the expected Vivado project (temporary) directory name # from the project name. @@ -214,9 +233,9 @@ namespace eval ns { variable err variable status [catch { namespace eval sub_ns { - # NOTE: The '-quiet' flag to the 'source' command is + # NOTE: The '-notrace' flag to the 'source' command is # Vivado-specific. - set ::ns::status [tcl_utils::source_with_args $::ns::script_name {-quiet} $::ns::args]] + set ::ns::status [tcl_utils::source_with_args $::ns::script_name {-notrace} $::ns::args] } } err] if {$status != 0} { @@ -226,9 +245,7 @@ namespace eval ns { append tmp " !!! $i\n" } puts -nonewline $tmp - break } else { - set target_base_name [file dirname $script_name] set target_sub_name $ip_name set target_dir_name [file join $target_base_name $target_sub_name] @@ -258,8 +275,15 @@ namespace eval ns { if {[tcl_utils::os_is_windows]} { exec subst $win_virtual_drive /d } + + # If a problem occurred, there is no need to continue with the + # other IPs. + if {$status != 0} { + break + } } + puts "$sep_line" if {$status == 0} { puts "Done" } else { -- GitLab