From 0333c937a7ad2f1fe9accadb03c705b678dec6f5 Mon Sep 17 00:00:00 2001 From: Ricardo Barbedo Date: Mon, 13 Jun 2016 22:05:47 +0200 Subject: [PATCH] Initial commit --- .gitattributes | 2 + README.md | 31 + init.tcl | 7 + scripts/git_wrapper.tcl | 50 + scripts/write_project_tcl_git.tcl | 1928 +++++++++++++++++++++++++++++ 5 files changed, 2018 insertions(+) create mode 100644 .gitattributes create mode 100644 README.md create mode 100644 init.tcl create mode 100644 scripts/git_wrapper.tcl create mode 100644 scripts/write_project_tcl_git.tcl diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..dd9973b --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +write_project_tcl_git.tcl eol=lf +*.tcl eol=crlf \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..22deaf1 --- /dev/null +++ b/README.md @@ -0,0 +1,31 @@ +# vivado-git + +Trying to make Vivado more git-friendly on Windows. + +### Requirements + +[Git for Windows.](https://git-scm.com/download/win) + +### Installation + +Append/replace/add `init.tcl` and the `scripts` directory to `%APPDATA%\Roaming\Xilinx\Vivado`. + +### How it works + +Vivado is a pain in the ass to source control decently, so these scripts provide: + + - A modified `write_project_tcl_git` script to generate a project generator script without absolute paths. + + - A git wrapper that will regenerate the project script and add it before commiting. + +### Workflow + +When first starting with a project, create it at a folder like `C:/.../PROJECT_NAME/work`. All the untracked files will be under this directory. + +Place your source files anywhere you want in your project folder. + +Then go to your project directory using the Tcl Console with `cd C:/.../PROJECT_NAME` before adding or committing you files. + +When you are done, just add your files and `git commit` your project. A `PROJECT_NAME.tcl` script will be created in your `PROJECT_NAME` folder and added to your commit. + +When reopening the project, make sure to do it by using `Tools -> Run Tcl Script...`. The Tcl Console will change the directory to your project folder automatically. \ No newline at end of file diff --git a/init.tcl b/init.tcl new file mode 100644 index 0000000..63f15a1 --- /dev/null +++ b/init.tcl @@ -0,0 +1,7 @@ +set init_dir [file dirname [info script]] + +source $init_dir/scripts/write_project_tcl_git.tcl +namespace import ::custom::write_project_tcl_git + +source $init_dir/scripts/git_wrapper.tcl +namespace import ::git_wrapper::git \ No newline at end of file diff --git a/scripts/git_wrapper.tcl b/scripts/git_wrapper.tcl new file mode 100644 index 0000000..32e6f7f --- /dev/null +++ b/scripts/git_wrapper.tcl @@ -0,0 +1,50 @@ +################################################################################ +# +# This file provides a basic wrapper to use git directly from the tcl console in +# Vivado. +# It requires the write_project_tcl_git.tcl script to work properly. +# Unversioned files will be put in the work/ folder +# +# Ricardo Barbedo +# +################################################################################ + +namespace eval ::git_wrapper { + namespace export git + namespace import ::custom::write_project_tcl_git + namespace import ::current_project + namespace import ::common::get_property + + proc git {args} { + set command [lindex $args 0] + + switch $command { + "init" {git_init {*}$args} + "commit" {git_commit {*}$args} + "default" {exec git {*}$args} + } + } + + proc git_init {args} { + # Generate gitignore file + set file [open ".gitignore" "w"] + puts $file "work/*" + close $file + + # Initialize the repo + exec git {*}$args + } + + proc git_commit {args} { + # Get project name + set proj_file [current_project].tcl + + # Generate project and add it + write_project_tcl_git -no_copy_sources -force $proj_file + puts $proj_file + exec git add $proj_file + + # Now commit everything + exec git {*}$args + } +} \ No newline at end of file diff --git a/scripts/write_project_tcl_git.tcl b/scripts/write_project_tcl_git.tcl new file mode 100644 index 0000000..e91e132 --- /dev/null +++ b/scripts/write_project_tcl_git.tcl @@ -0,0 +1,1928 @@ +#################################################################################### +# +# write_project_tcl.tcl (write a Vivado project tcl script for re-creating project) +# +# Script created on 02/08/2013 by Raj Klair (Xilinx, Inc.) +# +# 2014.2 - v2.0 (rev 4) +# * do not return value from main proc +# * fixed bug with relative file path calculation (break from loop while comparing +# directory elements of file paths for file to make relative to o/p script dir) +# 2014.1 - v2.0 (rev 3) +# * make source file paths relative to script output directory +# +# 2013.4 - +# 2013.3 - +# 2013.2 - v1.0 (rev 2) +# * no change +# +# 2013.1 - v1.0 (rev 1) +# * initial version +# +#################################################################################### +# +# Modified version to better support revision control. +# Can be called as write_project_tcl_git from the tcl console in Vivado. +# +# Differences: +# +# 1. The project directory is now relative to the scripts location. +# Project directory was relative to the tcl console current directory. +# +# 2. When recreating a project, the generated files will be put in a "work" directory +# under the project directory. If the work exists already, it will be rewritten. +# +# 3. After recreating a project, the tcl console will change directory to the project directory. +# +# 4. No mention to the creation time of the project script, so that it is not different +# every time it is generated. +# +#################################################################################### +package require Vivado 1.2014.1 + +namespace eval ::custom { + namespace export write_project_tcl_git +} + +namespace eval ::custom { +proc write_project_tcl_git {args} { + # Summary: + # Export Tcl script for re-creating the current project + + # Argument Usage: + # [-paths_relative_to = Script output directory path]: Override the reference directory variable for source file relative paths + # [-target_proj_dir = Current project directory path]: Directory where the project needs to be restored + # [-force]: Overwrite existing tcl script file + # [-all_properties]: Write all properties (default & non-default) for the project object(s) + # [-no_copy_sources]: Do not import sources even if they were local in the original project + # [-absolute_path]: Make all file paths absolute wrt the original project directory + # [-dump_project_info]: Write object values + # file: Name of the tcl script file to generate + + # Return Value: + # true (0) if success, false (1) otherwise + + # Categories: xilinxtclstore, projutils + + # reset global variables + variable a_global_vars + + reset_global_vars + + # process options + for {set i 0} {$i < [llength $args]} {incr i} { + set option [string trim [lindex $args $i]] + switch -regexp -- $option { + "-paths_relative_to" { incr i;set a_global_vars(s_relative_to) [file normalize [lindex $args $i]] } + "-target_proj_dir" { incr i;set a_global_vars(s_target_proj_dir) [lindex $args $i] } + "-force" { set a_global_vars(b_arg_force) 1 } + "-all_properties" { set a_global_vars(b_arg_all_props) 1 } + "-no_copy_sources" { set a_global_vars(b_arg_no_copy_srcs) 1 } + "-absolute_path" { set a_global_vars(b_absolute_path) 1 } + "-dump_project_info" { set a_global_vars(b_arg_dump_proj_info) 1 } + default { + # is incorrect switch specified? + if { [regexp {^-} $option] } { + send_msg_id Vivado-projutils-001 ERROR "Unknown option '$option', please type 'write_project_tcl -help' for usage info.\n" + return + } + set a_global_vars(script_file) $option + } + } + } + + # script file is a must + if { [string equal $a_global_vars(script_file) ""] } { + send_msg_id Vivado-projutils-002 ERROR "Missing value for option 'file', please type 'write_project_tcl -help' for usage info.\n" + return + } + + # should not be a directory + if { [file isdirectory $a_global_vars(script_file)] } { + send_msg_id Vivado-projutils-003 ERROR "The specified filename is a directory ($a_global_vars(script_file)), please type 'write_project_tcl -help' for usage info.\n" + return + } + + # check extension + if { [file extension $a_global_vars(script_file)] != ".tcl" } { + set a_global_vars(script_file) $a_global_vars(script_file).tcl + } + set a_global_vars(script_file) [file normalize $a_global_vars(script_file)] + + # error if file directory path does not exist + set file_path [file dirname $a_global_vars(script_file)] + if { ! [file exists $file_path] } { + set script_filename [file tail $a_global_vars(script_file)] + send_msg_id Vivado-projutils-013 ERROR "Directory in which file ${script_filename} is to be written does not exist \[$a_global_vars(script_file)\]\n" + return + } + + # recommend -force if file exists + if { [file exists $a_global_vars(script_file)] && !$a_global_vars(b_arg_force) } { + send_msg_id Vivado-projutils-004 ERROR "Tcl Script '$a_global_vars(script_file)' already exist. Use -force option to overwrite.\n" + return + } + + # set script file directory path + set a_global_vars(s_path_to_script_dir) [file normalize $file_path] + + # now write + if {[write_project_tcl_script]} { + return + } +} +} + +namespace eval ::custom { +# +# write_project_tcl tcl script argument & file handle vars +# +variable a_global_vars +variable l_script_data [list] +variable l_local_files [list] +variable l_remote_files [list] +variable b_project_board_set 0 + +# set file types to filter +variable l_filetype_filter [list] + +# Setup filter for non-user-settable filetypes +set l_filetype_filter [list "ip" "ipx" "embedded design sources" "elf" "coefficient files" "configuration files" \ + "block diagrams" "block designs" "dsp design sources" "text" \ + "design checkpoint" "waveform configuration file"] +# ip file extension types +variable l_valid_ip_extns [list] +set l_valid_ip_extns [list ".xci" ".bd" ".slx"] + +# set fileset types +variable a_fileset_types +set a_fileset_types { + {{DesignSrcs} {srcset}} + {{BlockSrcs} {blockset}} + {{Constrs} {constrset}} + {{SimulationSrcs} {simset}} +} + +proc reset_global_vars {} { + # Summary: initializes global namespace vars + # This helper command is used to reset the variables used in the script. + # Argument Usage: + # none + # Return Value: + # None + + variable a_global_vars + + set a_global_vars(s_relative_to) {.} + set a_global_vars(s_path_to_script_dir) "" + set a_global_vars(s_target_proj_dir) "" + set a_global_vars(b_arg_force) 0 + set a_global_vars(b_arg_no_copy_srcs) 0 + set a_global_vars(b_absolute_path) 0 + set a_global_vars(b_arg_all_props) 0 + set a_global_vars(b_arg_dump_proj_info) 0 + set a_global_vars(b_local_sources) 0 + set a_global_vars(curr_time) [clock format [clock seconds]] + set a_global_vars(fh) 0 + set a_global_vars(dp_fh) 0 + set a_global_vars(def_val_fh) 0 + set a_global_vars(script_file) "" + + set l_script_data [list] + set l_local_files [list] + set l_remote_files [list] +} + +proc write_project_tcl_script {} { + # Summary: write project script + # This helper command is used to script help. + # Argument Usage: + # none + # Return Value: + # true (0) if success, false (1) otherwise + + variable a_global_vars + variable l_script_data + variable l_remote_files + variable l_local_files + + set l_script_data [list] + set l_local_files [list] + set l_remote_files [list] + + # get the project name + set tcl_obj [current_project] + set proj_name [file tail [get_property name $tcl_obj]] + set proj_dir [get_property directory $tcl_obj] + set part_name [get_property part $tcl_obj] + + # output file script handle + set file $a_global_vars(script_file) + if {[catch {open $file w} a_global_vars(fh)]} { + send_msg_id Vivado-projutils-005 ERROR "failed to open file for write ($file)\n" + return 1 + } + + # dump project in canonical form + if { $a_global_vars(b_arg_dump_proj_info) } { + set dump_file [file normalize [file join $a_global_vars(s_path_to_script_dir) ${proj_name}_dump.txt]] + if {[catch {open $dump_file w} a_global_vars(dp_fh)]} { + send_msg_id Vivado-projutils-006 ERROR "failed to open file for write ($dump_file)\n" + return 1 + } + + # default value output file script handle + set def_val_file [file normalize [file join $a_global_vars(s_path_to_script_dir) ${proj_name}_def_val.txt]] + if {[catch {open $def_val_file w} a_global_vars(def_val_fh)]} { + send_msg_id Vivado-projutils-007 ERROR "failed to open file for write ($file)\n" + return 1 + } + } + + # explicitly update the compile order for current source/simset, if following conditions are met + if { {All} == [get_property source_mgmt_mode [current_project]] && + {0} == [get_property is_readonly [current_project]] && + {RTL} == [get_property design_mode [current_fileset]] } { + + + # re-parse source fileset compile order for the current top + if {[llength [get_files -compile_order sources -used_in synthesis]] > 1} { + update_compile_order -fileset [current_fileset] -quiet + } + + # re-parse simlulation fileset compile order for the current top + if {[llength [get_files -compile_order sources -used_in simulation]] > 1} { + update_compile_order -fileset [current_fileset -simset] -quiet + } + } + + # writer helpers + wr_create_project $proj_dir $proj_name $part_name + wr_project_properties $proj_dir $proj_name + wr_filesets $proj_dir $proj_name + wr_runs $proj_dir $proj_name + wr_proj_info $proj_name + + # write header + write_header $proj_dir $proj_name $file + + # write script data + foreach line $l_script_data { + puts $a_global_vars(fh) $line + } + + close $a_global_vars(fh) + + if { $a_global_vars(b_arg_dump_proj_info) } { + close $a_global_vars(def_val_fh) + close $a_global_vars(dp_fh) + } + + set script_filename [file tail $file] + set out_dir [file dirname [file normalize $file]] + send_msg_id Vivado-projutils-008 INFO "Tcl script '$script_filename' generated in output directory '$out_dir'\n\n" + + if { $a_global_vars(b_absolute_path) } { + send_msg_id Vivado-projutils-016 INFO "Please note that the -absolute_path switch was specified, hence the project source files will be referenced using\n\ + absolute path only, in the generated script. As such, the generated script will only work in the same filesystem where those absolute paths are accessible." + } else { + if { "." != $a_global_vars(s_relative_to) } { + send_msg_id Vivado-projutils-017 INFO "Please note that the -paths_relative_to switch was specified, hence the project source files will be referenced\n\ + wrt the path that was specified with this switch. The 'origin_dir' variable is set to this path in the generated script." + } else { + send_msg_id Vivado-projutils-015 INFO "Please note that by default, the file path for the project source files were set wrt the 'origin_dir' variable in the\n\ + generated script. When this script is executed from the output directory, these source files will be referenced wrt this 'origin_dir' path value.\n\ + In case this script was later physically moved to a different directory, the 'origin_dir' value MUST be set manually in the script with the path\n\ + relative to the new output directory to make sure that the source files are referenced correctly from the original project. You can also set the\n\ + 'origin_dir' automatically by setting the 'origin_dir_loc' variable in the tcl shell before sourcing this generated script. The 'origin_dir_loc'\n\ + variable should be set to the path relative to the new output directory. Alternatively, if you are sourcing the script from the Vivado command line,\n\ + then set the origin dir using '-tclargs --origin_dir '. For example, 'vivado -mode tcl -source $script_filename -tclargs --origin_dir \"..\"\n" + } + } + + if { $a_global_vars(b_local_sources) } { + print_local_file_msg "warning" + } else { + print_local_file_msg "info" + } + + reset_global_vars + + return 0 +} + +proc wr_create_project { proj_dir name part_name } { + # Summary: write create project command + # This helper command is used to script help. + # Argument Usage: + # proj_dir: project directory path + # name: project name + # Return Value: + # none + + variable a_global_vars + variable l_script_data + + lappend l_script_data "# Set the reference directory for source file relative paths (by default the value is script directory path)" + lappend l_script_data "set origin_dir \[file dirname \[info script\]\]" + lappend l_script_data "" + set var_name "origin_dir_loc" + lappend l_script_data "# Use origin directory path location variable, if specified in the tcl shell" + lappend l_script_data "if \{ \[info exists ::$var_name\] \} \{" + lappend l_script_data " set origin_dir \$::$var_name" + lappend l_script_data "\}" + + lappend l_script_data "" + + lappend l_script_data "variable script_file" + lappend l_script_data "set script_file \"[file tail $a_global_vars(script_file)]\"\n" + lappend l_script_data "# Help information for this script" + lappend l_script_data "proc help \{\} \{" + lappend l_script_data " variable script_file" + lappend l_script_data " puts \"\\nDescription:\"" + lappend l_script_data " puts \"Recreate a Vivado project from this script. The created project will be\"" + lappend l_script_data " puts \"functionally equivalent to the original project for which this script was\"" + lappend l_script_data " puts \"generated. The script contains commands for creating a project, filesets,\"" + lappend l_script_data " puts \"runs, adding/importing sources and setting properties on various objects.\\n\"" + lappend l_script_data " puts \"Syntax:\"" + lappend l_script_data " puts \"\$script_file\"" + lappend l_script_data " puts \"\$script_file -tclargs \\\[--origin_dir \\\]\"" + lappend l_script_data " puts \"\$script_file -tclargs \\\[--help\\\]\\n\"" + lappend l_script_data " puts \"Usage:\"" + lappend l_script_data " puts \"Name Description\"" + lappend l_script_data " puts \"-------------------------------------------------------------------------\"" + lappend l_script_data " puts \"\\\[--origin_dir \\\] Determine source file paths wrt this path. Default\"" + lappend l_script_data " puts \" origin_dir path value is \\\".\\\", otherwise, the value\"" + lappend l_script_data " puts \" that was set with the \\\"-paths_relative_to\\\" switch\"" + lappend l_script_data " puts \" when this script was generated.\\n\"" + lappend l_script_data " puts \"\\\[--help\\\] Print help information for this script\"" + lappend l_script_data " puts \"-------------------------------------------------------------------------\\n\"" + lappend l_script_data " exit 0" + lappend l_script_data "\}\n" + lappend l_script_data "if \{ \$::argc > 0 \} \{" + lappend l_script_data " for \{set i 0\} \{\$i < \[llength \$::argc\]\} \{incr i\} \{" + lappend l_script_data " set option \[string trim \[lindex \$::argv \$i\]\]" + lappend l_script_data " switch -regexp -- \$option \{" + lappend l_script_data " \"--origin_dir\" \{ incr i; set origin_dir \[lindex \$::argv \$i\] \}" + lappend l_script_data " \"--help\" \{ help \}" + lappend l_script_data " default \{" + lappend l_script_data " if \{ \[regexp \{^-\} \$option\] \} \{" + lappend l_script_data " puts \"ERROR: Unknown option '\$option' specified, please type '\$script_file -tclargs --help' for usage info.\\n\"" + lappend l_script_data " return 1" + lappend l_script_data " \}" + lappend l_script_data " \}" + lappend l_script_data " \}" + lappend l_script_data " \}" + lappend l_script_data "\}\n" + + lappend l_script_data "# Set the directory path for the original project from where this script was exported" + if { $a_global_vars(b_absolute_path) } { + lappend l_script_data "set orig_proj_dir \"$proj_dir\"" + } else { + set rel_file_path "[get_relative_file_path_for_source $proj_dir [get_script_execution_dir]]" + set path "\[file normalize \"\$origin_dir/$rel_file_path\"\]" + lappend l_script_data "set orig_proj_dir \"$path\"" + } + lappend l_script_data "" + + # create project + lappend l_script_data "# Create project" + + set tcl_cmd "" + # set target project directory path if specified. If not, create project dir in current dir. + set target_dir $a_global_vars(s_target_proj_dir) + if { {} == $target_dir } { + set tcl_cmd "create_project $name \$origin_dir/work -part $part_name -quiet -force" + } else { + # is specified target proj dir == current dir? + set cwd [file normalize [string map {\\ /} [pwd]]] + set dir [file normalize [string map {\\ /} $target_dir]] + if { [string equal $cwd $dir] } { + set tcl_cmd "create_project $name -part $part_name" + } else { + set tcl_cmd "create_project $name \"$target_dir\" -part $part_name" + } + } + + if { [get_property managed_ip [current_project]] } { + set tcl_cmd "$tcl_cmd -ip" + } + lappend l_script_data $tcl_cmd + + if { $a_global_vars(b_arg_dump_proj_info) } { + puts $a_global_vars(dp_fh) "project_name=$name" + } + + lappend l_script_data "" + lappend l_script_data "# Set the directory path for the new project" + lappend l_script_data "set proj_dir \[get_property directory \[current_project\]\]" + + lappend l_script_data "" + lappend l_script_data "# Reconstruct message rules" + + set msg_control_rules [ debug::get_msg_control_rules -as_tcl ] + if { [string length $msg_control_rules] > 0 } { + lappend l_script_data "${msg_control_rules}" + } else { + lappend l_script_data "# None" + } + lappend l_script_data "" +} + +proc wr_project_properties { proj_dir proj_name } { + # Summary: write project properties + # This helper command is used to script help. + # Argument Usage: + # proj_name: project name + # Return Value: + # None + + variable l_script_data + variable b_project_board_set + + # write project properties + set tcl_obj [current_project] + set get_what "get_projects" + + lappend l_script_data "# Set project properties" + lappend l_script_data "set obj \[$get_what $tcl_obj\]" + + # is project "board_part" set already? + if { [string length [get_property "board_part" $tcl_obj]] > 0 } { + set b_project_board_set 1 + } + + write_props $proj_dir $proj_name $get_what $tcl_obj "project" +} + +proc wr_filesets { proj_dir proj_name } { + # Summary: write fileset object properties + # This helper command is used to script help. + # Argument Usage: + # proj_name: project name + # Return Value: + # None + + variable a_fileset_types + + # write fileset data + foreach {fs_data} $a_fileset_types { + set filesets [get_filesets -filter FILESET_TYPE==[lindex $fs_data 0]] + write_specified_fileset $proj_dir $proj_name $filesets + } +} + +proc write_specified_fileset { proj_dir proj_name filesets } { + # Summary: write fileset properties and sources + # This helper command is used to script help. + # Argument Usage: + # proj_name: project name + # filesets: list of filesets + # Return Value: + # None + + variable a_global_vars + variable l_script_data + variable a_fileset_types + + # write filesets + set type "file" + foreach tcl_obj $filesets { + + # Is this a IP block fileset for a proxy IP that is owned by another composite file? + # If so, we don't want to write it out as an independent file. The parent will take care of it. + if { [is_proxy_ip_fileset $tcl_obj] } { + continue + } + + set fs_type [get_property fileset_type [get_filesets $tcl_obj]] + + # is this a IP block fileset? if yes, do not create block fileset, but create for a pure HDL based fileset (no IP's) + if { [is_ip_fileset $tcl_obj] } { + # do not create block fileset + } else { + lappend l_script_data "# Create '$tcl_obj' fileset (if not found)" + lappend l_script_data "if \{\[string equal \[get_filesets -quiet $tcl_obj\] \"\"\]\} \{" + + set fs_sw_type [get_fileset_type_switch $fs_type] + lappend l_script_data " create_fileset $fs_sw_type $tcl_obj" + lappend l_script_data "\}\n" + } + + set get_what_fs "get_filesets" + + # set IP REPO PATHS (if any) for filesets of type "DesignSrcs" or "BlockSrcs" + if { (({DesignSrcs} == $fs_type) || ({BlockSrcs} == $fs_type)) } { + if { ({RTL} == [get_property design_mode [get_filesets $tcl_obj]]) } { + set repo_paths [get_ip_repo_paths $tcl_obj] + if { [llength $repo_paths] > 0 } { + lappend l_script_data "# Set IP repository paths" + lappend l_script_data "set obj \[get_filesets $tcl_obj\]" + set path_list [list] + foreach path $repo_paths { + if { $a_global_vars(b_absolute_path) } { + lappend path_list $path + } else { + set rel_file_path "[get_relative_file_path_for_source $path [get_script_execution_dir]]" + set path "\[file normalize \"\$origin_dir/$rel_file_path\"\]" + lappend path_list $path + } + } + set repo_path_str [join $path_list " "] + lappend l_script_data "set_property \"ip_repo_paths\" \"${repo_path_str}\" \$obj" + lappend l_script_data "" + lappend l_script_data "# Rebuild user ip_repo's index before adding any source files" + lappend l_script_data "update_ip_catalog -rebuild" + lappend l_script_data "" + } + } + } + + # is this a IP block fileset? if yes, then set the current srcset object (IP's will be added to current source fileset) + if { [is_ip_fileset $tcl_obj] } { + set srcset [current_fileset -srcset] + lappend l_script_data "# Set '$srcset' fileset object" + lappend l_script_data "set obj \[$get_what_fs $srcset\]" + } else { + lappend l_script_data "# Set '$tcl_obj' fileset object" + lappend l_script_data "set obj \[$get_what_fs $tcl_obj\]" + } + if { {Constrs} == $fs_type } { + lappend l_script_data "" + write_constrs $proj_dir $proj_name $tcl_obj $type + } else { + write_files $proj_dir $proj_name $tcl_obj $type + } + + # is this a IP block fileset? if yes, do not write block fileset properties (block fileset doesnot exist in new project) + if { [is_ip_fileset $tcl_obj] } { + # do not write ip fileset properties + } else { + lappend l_script_data "# Set '$tcl_obj' fileset properties" + lappend l_script_data "set obj \[$get_what_fs $tcl_obj\]" + write_props $proj_dir $proj_name $get_what_fs $tcl_obj "fileset" + } + } +} + +proc wr_runs { proj_dir proj_name } { + # Summary: write runs and properties + # This helper command is used to script help. + # Argument Usage: + # proj_name: project name + # Return Value: + # None + + variable l_script_data + + # write runs (synthesis, Implementation) + set runs [get_runs -filter {IS_SYNTHESIS == 1}] + write_specified_run $proj_dir $proj_name $runs + + if { {RTL} == [get_property design_mode [current_fileset]] } { + lappend l_script_data "# set the current synth run" + lappend l_script_data "current_run -synthesis \[get_runs [current_run -synthesis]\]\n" + } + + set runs [get_runs -filter {IS_IMPLEMENTATION == 1}] + write_specified_run $proj_dir $proj_name $runs + + lappend l_script_data "# set the current impl run" + lappend l_script_data "current_run -implementation \[get_runs [current_run -implementation]\]" + lappend l_script_data "" + lappend l_script_data "# Change current directory to project folder" + lappend l_script_data "cd \[file dirname \[info script\]\]" +} + +proc wr_proj_info { proj_name } { + # Summary: write generated project status message + # This helper command is used to script help. + # Argument Usage: + # proj_name: project name + # Return Value: + # None + + variable l_script_data + + lappend l_script_data "\nputs \"INFO: Project created:$proj_name\"" +} + +proc write_header { proj_dir proj_name file } { + # Summary: write script header + # This helper command is used to script help. + # Argument Usage: + # Return Value: + # None + + variable a_global_vars + variable l_local_files + variable l_remote_files + + set version_txt [split [version] "\n"] + set version [lindex $version_txt 0] + set copyright [lindex $version_txt 2] + set product [lindex [split $version " "] 0] + set version_id [join [lrange $version 1 end] " "] + + set tcl_file [file tail $file] + puts $a_global_vars(fh) "#\n# $product (TM) $version_id" + puts $a_global_vars(fh) "#\n# $tcl_file: Tcl script for re-creating project '$proj_name'\n#" + puts $a_global_vars(fh) "# $copyright" + puts $a_global_vars(fh) "#\n# This file contains the $product Tcl commands for re-creating the project to the state*" + puts $a_global_vars(fh) "# when this script was generated. In order to re-create the project, please source this" + puts $a_global_vars(fh) "# file in the $product Tcl Shell." + puts $a_global_vars(fh) "#" + puts $a_global_vars(fh) "# * Note that the runs in the created project will be configured the same way as the" + puts $a_global_vars(fh) "# original project, however they will not be launched automatically. To regenerate the" + puts $a_global_vars(fh) "# run results please launch the synthesis/implementation runs as needed.\n#" + puts $a_global_vars(fh) "#*****************************************************************************************" + puts $a_global_vars(fh) "# NOTE: In order to use this script for source control purposes, please make sure that the" + puts $a_global_vars(fh) "# following files are added to the source control system:-" + puts $a_global_vars(fh) "#" + puts $a_global_vars(fh) "# 1. This project restoration tcl script (${tcl_file}) that was generated." + puts $a_global_vars(fh) "#" + puts $a_global_vars(fh) "# 2. The following source(s) files that were local or imported into the original project." + puts $a_global_vars(fh) "# (Please see the '\$orig_proj_dir' and '\$origin_dir' variable setting below at the start of the script)\n#" + + if {[llength $l_local_files] == 0} { + puts $a_global_vars(fh) "# " + } else { + foreach line $l_local_files { + puts $a_global_vars(fh) "# $line" + } + } + puts $a_global_vars(fh) "#" + puts $a_global_vars(fh) "# 3. The following remote source files that were added to the original project:-\n#" + if {[llength $l_remote_files] == 0} { + puts $a_global_vars(fh) "# " + } else { + foreach line $l_remote_files { + puts $a_global_vars(fh) "# $line" + } + } + puts $a_global_vars(fh) "#" + puts $a_global_vars(fh) "#*****************************************************************************************\n" +} + +proc print_local_file_msg { msg_type } { + # Summary: print warning on finding local sources + # This helper command is used to script help. + # Argument Usage: + # Return Value: + # None + + puts "" + if { [string equal $msg_type "warning"] } { + send_msg_id Vivado-projutils-010 WARNING "Found source(s) that were local or imported into the project. If this project is being source controlled, then\n\ + please ensure that the project source(s) are also part of this source controlled data. The list of these local source(s) can be found in the generated script\n\ + under the header section." + } else { + send_msg_id Vivado-projutils-011 INFO "If this project is being source controlled, then please ensure that the project source(s) are also part of this source\n\ + controlled data. The list of these local source(s) can be found in the generated script under the header section." + } + puts "" +} + +proc get_ip_repo_paths { tcl_obj } { + # Summary: + # Iterate over the fileset properties and get the ip_repo_paths (if set) + # Argument Usage: + # tcl_obj : fileset + # Return Value: + # List of repo paths + + set repo_path_list [list] + foreach path [get_property ip_repo_paths [get_filesets $tcl_obj]] { + lappend repo_path_list $path + } + return $repo_path_list +} + +proc is_deprecated { prop } { + # Summary: filter deprecated properties + # Argument Usage: + # Return Value: + # true (1) if found, false (1) otherwise + + set prop [string toupper $prop] + if { $prop == "BOARD" } { + return 1 + } + return 0 +} + +proc filter { prop val { file {} } } { + # Summary: filter special properties + # This helper command is used to script help. + # Argument Usage: + # Return Value: + # true (1) if found, false (1) otherwise + + variable l_filetype_filter + variable l_valid_ip_extns + + set prop [string toupper $prop] + if { [expr { $prop == "BOARD" } || \ + { $prop == "IS_HD" } || \ + { $prop == "IS_PARTIAL_RECONFIG" } || \ + { $prop == "ADD_STEP" }]} { + return 1 + } + + if { [string equal type "project"] } { + if { [expr { $prop == "DIRECTORY" }] } { + return 1 + } + } + + # error reported if file_type is set + # e.g ERROR: [Vivado 12-563] The file type 'IP' is not user settable. + set val [string tolower $val] + if { [string equal $prop "FILE_TYPE"] } { + if { [lsearch $l_filetype_filter $val] != -1 } { + return 1 + } + } + + # filter readonly is_managed property for ip + if { [string equal $prop "IS_MANAGED"] } { + if { [lsearch -exact $l_valid_ip_extns [string tolower [file extension $file]]] >= 0 } { + return 1 + } + } + + # filter ip_repo_paths (ip_repo_paths is set before adding sources) + if { [string equal -nocase $prop {ip_repo_paths}] } { + return 1 + } + + return 0 +} + +proc is_local_to_project { file } { + # Summary: check if file is local to the project directory structure + # This helper command is used to script help. + # Argument Usage: + # Return Value: + # true (1), if file is local to the project (inside project directory structure) + # false (0), if file is outside the project directory structure + + set dir [get_property directory [current_project]] + set proj_comps [split [string trim [file normalize [string map {\\ /} $dir]]] "/"] + set file_comps [split [string trim [file normalize [string map {\\ /} $file]]] "/"] + set is_local 1 + for {set i 1} {$i < [llength $proj_comps]} {incr i} { + if { [lindex $proj_comps $i] != [lindex $file_comps $i] } { + set is_local 0;break + } + } + + return $is_local +} + +proc is_ip_readonly_prop { name } { + # Summary: Return true if dealing with following IP properties that are not settable for an IP in read-only state + # Argument Usage: + # name: property name + # Return Value: + # true if success, false otherwise + + if { [regexp -nocase {synth_checkpoint_mode} $name] || + [regexp -nocase {is_locked} $name] || + [regexp -nocase {generate_synth_checkpoint} $name] } { + return true + } + + return false +} + +proc write_properties { prop_info_list get_what tcl_obj } { + # Summary: write object properties + # This helper command is used to script help. + # Argument Usage: + # Return Value: + # None + + variable a_global_vars + variable l_script_data + + if {[llength $prop_info_list] > 0} { + set b_add_closing_brace 0 + foreach x $prop_info_list { + set elem [split $x "#"] + set name [lindex $elem 0] + set value [lindex $elem 1] + if { [regexp "more options" $name] } { + set cmd_str "set_property -name {$name} -value {$value} -objects" + } elseif { ([is_ip_readonly_prop $name]) && ([string equal $get_what "get_files"]) } { + set cmd_str "if \{ !\[get_property \"is_locked\" \$file_obj\] \} \{" + lappend l_script_data "$cmd_str" + set cmd_str " set_property \"$name\" \"$value\"" + set b_add_closing_brace 1 + } else { + set cmd_str "set_property \"$name\" \"$value\"" + } + if { [string equal $get_what "get_files"] } { + lappend l_script_data "$cmd_str \$file_obj" + if { $b_add_closing_brace } { + lappend l_script_data "\}" + set b_add_closing_brace 0 + } + } else { + # comment "is_readonly" project property + if { [string equal $get_what "get_projects"] && [string equal "$name" "is_readonly"] } { + if { ! $a_global_vars(b_arg_all_props) } { + send_msg_id Vivado-projutils-012 INFO "The current project is in 'read_only' state. The generated script will create a writable project." + } + continue + } + lappend l_script_data "$cmd_str \$obj" + } + } + } + lappend l_script_data "" +} + +proc write_props { proj_dir proj_name get_what tcl_obj type } { + # Summary: write first class object properties + # This helper command is used to script help. + # Argument Usage: + # Return Value: + # none + + variable a_global_vars + variable l_script_data + variable b_project_board_set + + set obj_name [get_property name [$get_what $tcl_obj]] + set read_only_props [rdi::get_attr_specs -class [get_property class $tcl_obj] -filter {is_readonly}] + set prop_info_list [list] + set properties [list_property [$get_what $tcl_obj]] + + foreach prop $properties { + if { [is_deprecated $prop] } { continue } + + # skip read-only properties + if { [lsearch $read_only_props $prop] != -1 } { continue } + + set prop_type "unknown" + if { [string equal $type "run"] } { + if { [regexp "STEPS" $prop] } { + # skip step properties + } else { + set attr_names [rdi::get_attr_specs -class [get_property class [get_runs $tcl_obj] ]] + set prop_type [get_property type [lindex $attr_names [lsearch $attr_names $prop]]] + } + } else { + set attr_spec [rdi::get_attr_specs -quiet $prop -object [$get_what $tcl_obj]] + if { {} == $attr_spec } { + set prop_lower [string tolower $prop] + set attr_spec [rdi::get_attr_specs -quiet $prop_lower -object [$get_what $tcl_obj]] + } + set prop_type [get_property type $attr_spec] + } + set def_val [list_property_value -default $prop $tcl_obj] + set dump_prop_name [string tolower ${obj_name}_${type}_$prop] + set cur_val [get_property $prop $tcl_obj] + + # filter special properties + if { [filter $prop $cur_val] } { continue } + + # do not set "runs" or "project" part, if "board_part" is set + if { ([string equal $type "project"] || [string equal $type "run"]) && + [string equal -nocase $prop "part"] && + $b_project_board_set } { + continue + } + + # do not set "fileset" target_part, if "board_part" is set + if { [string equal $type "fileset"] && + [string equal -nocase $prop "target_part"] && + $b_project_board_set } { + continue + } + + # re-align values + set cur_val [get_target_bool_val $def_val $cur_val] + + set prop_entry "[string tolower $prop]#[get_property $prop [$get_what $tcl_obj]]" + + # fix paths wrt the original project dir + if {([string equal -nocase $prop "top_file"]) && ($cur_val != "") } { + set file $cur_val + + set srcs_dir "${proj_name}.srcs" + set file_dirs [split [string trim [file normalize [string map {\\ /} $file]]] "/"] + set src_file [join [lrange $file_dirs [lsearch -exact $file_dirs "$srcs_dir"] end] "/"] + + if { [is_local_to_project $file] } { + set proj_file_path "\$proj_dir/$src_file" + } else { + set proj_file_path "[get_relative_file_path_for_source $src_file [get_script_execution_dir]]" + } + set prop_entry "[string tolower $prop]#$proj_file_path" + + } elseif {([string equal -nocase $prop "target_constrs_file"] || + [string equal -nocase $prop "target_ucf"]) && + ($cur_val != "") } { + + set file $cur_val + set fs_name $tcl_obj + + set path_dirs [split [string trim [file normalize [string map {\\ /} $file]]] "/"] + set src_file [join [lrange $path_dirs [lsearch -exact $path_dirs "$fs_name"] end] "/"] + set file_object [lindex [get_files -of_objects [get_filesets $fs_name] [list $file]] 0] + set file_props [list_property $file_object] + + if { [lsearch $file_props "IMPORTED_FROM"] != -1 } { + if { $a_global_vars(b_arg_no_copy_srcs) } { + set proj_file_path "\$orig_proj_dir/${proj_name}.srcs/$src_file" + } else { + set proj_file_path "\$proj_dir/${proj_name}.srcs/$src_file" + } + } else { + # is file new inside project? + if { [is_local_to_project $file] } { + # is file inside fileset dir? + if { [regexp "^${fs_name}/" $src_file] } { + set proj_file_path "\$orig_proj_dir/${proj_name}.srcs/$src_file" + } else { + set file_no_quotes [string trim $file "\""] + set rel_file_path [get_relative_file_path_for_source $file_no_quotes [get_script_execution_dir]] + set proj_file_path "\[file normalize \"\$origin_dir/$rel_file_path\"\]" + #set proj_file_path "$file" + } + } else { + if { $a_global_vars(b_absolute_path) } { + set proj_file_path "$file" + } else { + set file_no_quotes [string trim $file "\""] + set rel_file_path [get_relative_file_path_for_source $file_no_quotes [get_script_execution_dir]] + set proj_file_path "\[file normalize \"\$origin_dir/$rel_file_path\"\]" + } + } + } + + set prop_entry "[string tolower $prop]#$proj_file_path" + } + + + # re-align compiled_library_dir + if { [string equal -nocase $prop "compxlib.compiled_library_dir"] || + [string equal -nocase $prop "compxlib.modelsim_compiled_library_dir"] || + [string equal -nocase $prop "compxlib.questa_compiled_library_dir"] || + [string equal -nocase $prop "compxlib.ies_compiled_library_dir"] || + [string equal -nocase $prop "compxlib.vcs_compiled_library_dir"] || + [string equal -nocase $prop "compxlib.riviera_compiled_library_dir"] || + [string equal -nocase $prop "compxlib.activehdl_compiled_library_dir"] } { + set compile_lib_dir_path $cur_val + set cache_dir "${proj_name}.cache" + set path_dirs [split [string trim [file normalize [string map {\\ /} $cur_val]]] "/"] + if {[lsearch -exact $path_dirs "$cache_dir"] > 0} { + set dir_path [join [lrange $path_dirs [lsearch -exact $path_dirs "$cache_dir"] end] "/"] + set compile_lib_dir_path "\$proj_dir/$dir_path" + } + set prop_entry "[string tolower $prop]#$compile_lib_dir_path" + } + + # process run step tcl pre/post properties + if { [string equal $type "run"] } { + if { [regexp "STEPS" $prop] } { + if { [regexp "TCL.PRE" $prop] || [regexp "TCL.POST" $prop] } { + if { ($cur_val != "") } { + set file $cur_val + + set srcs_dir "${proj_name}.srcs" + set file_dirs [split [string trim [file normalize [string map {\\ /} $file]]] "/"] + set src_file [join [lrange $file_dirs [lsearch -exact $file_dirs "$srcs_dir"] end] "/"] + + set tcl_file_path {} + if { [is_local_to_project $file] } { + set tcl_file_path "\$proj_dir/$src_file" + } else { + if { $a_global_vars(b_absolute_path) } { + set tcl_file_path "$file" + } else { + set rel_file_path "[get_relative_file_path_for_source $src_file [get_script_execution_dir]]" + set tcl_file_path "\[file normalize \"\$origin_dir/$rel_file_path\"\]" + } + } + set prop_entry "[string tolower $prop]#$tcl_file_path" + } + } + } + } + + if { $a_global_vars(b_arg_all_props) } { + lappend prop_info_list $prop_entry + } else { + if { $def_val != $cur_val } { + lappend prop_info_list $prop_entry + } + } + + if { $a_global_vars(b_arg_dump_proj_info) } { + if { ([string equal -nocase $prop "top_file"] || + [string equal -nocase $prop "target_constrs_file"] || + [string equal -nocase $prop "target_ucf"] ) && [string equal $type "fileset"] } { + + # fix path + set file $cur_val + + set srcs_dir "${proj_name}.srcs" + set file_dirs [split [string trim [file normalize [string map {\\ /} $file]]] "/"] + set src_file [join [lrange $file_dirs [lsearch -exact $file_dirs "$srcs_dir"] end] "/"] + set cur_val "\$PSRCDIR/$src_file" + } + puts $a_global_vars(def_val_fh) "$prop:($prop_type) DEFAULT_VALUE ($def_val)==CURRENT_VALUE ($cur_val)" + puts $a_global_vars(dp_fh) "${dump_prop_name}=$cur_val" + } + } + + if { {fileset} == $type } { + set fs_type [get_property fileset_type [get_filesets $tcl_obj]] + if { {SimulationSrcs} == $fs_type } { + if { ![get_property is_readonly [current_project]] } { + add_simulator_props $get_what $tcl_obj prop_info_list + } + } + } + + # write properties now + write_properties $prop_info_list $get_what $tcl_obj + +} + +proc add_simulator_props { get_what tcl_obj prop_info_list_arg } { + # Summary: write file and file properties + # This helper command is used to script help. + # Argument Usage: + # Return Value: + # none + upvar $prop_info_list_arg prop_info_list + + set target_simulator [get_property target_simulator [current_project]] + set simulators [get_simulators] + foreach simulator [get_simulators] { + if { $target_simulator == $simulator } { continue } + set_property target_simulator $simulator [current_project] + set prefix [string tolower [lindex [split $simulator {.}] 0]] + write_simulator_props $prefix $get_what $tcl_obj prop_info_list + } + set_property target_simulator $target_simulator [current_project] +} + +proc write_simulator_props { prefix get_what tcl_obj prop_info_list_arg } { + # Summary: write non-default simulator properties + # Argument Usage: + # Return Value: + # none + + upvar $prop_info_list_arg prop_info_list + variable a_global_vars + variable l_script_data + + set read_only_props [rdi::get_attr_specs -class [get_property class $tcl_obj] -filter {is_readonly}] + foreach prop [list_property [$get_what $tcl_obj]] { + if { [lsearch $read_only_props $prop] != -1 } { continue } + if { [is_deprecated_property $prop] } { continue } + set sim_prefix [string tolower [lindex [split $prop {.}] 0]] + if { $prefix != $sim_prefix } { continue } + + set attr_spec [rdi::get_attr_specs -quiet $prop -object [$get_what $tcl_obj]] + if { {} == $attr_spec } { + set prop_lower [string tolower $prop] + set attr_spec [rdi::get_attr_specs -quiet $prop_lower -object [$get_what $tcl_obj]] + } + set prop_type [get_property type $attr_spec] + set def_val [list_property_value -default $prop $tcl_obj] + set cur_val [get_property $prop $tcl_obj] + set cur_val [get_target_bool_val $def_val $cur_val] + set prop_entry "[string tolower $prop]#[get_property $prop [$get_what $tcl_obj]]" + if { $def_val != $cur_val } { + lappend prop_info_list $prop_entry + } + } +} + +proc is_deprecated_property { property } { + # Summary: filter old properties + # Argument Usage: + # Return Value: + + set property [string tolower $property] + + if { [string equal $property "runtime"] || + [string equal $property "unit_under_test"] || + [string equal $property "xelab.snapshot"] || + [string equal $property "xelab.debug_level"] || + [string equal $property "xelab.relax"] || + [string equal $property "xelab.mt_level"] || + [string equal $property "xelab.load_glbl"] || + [string equal $property "xelab.rangecheck"] || + [string equal $property "xelab.sdf_delay"] || + [string equal $property "xelab.unifast"] || + [string equal $property "xelab.nosort"] || + [string equal $property "xelab.more_options"] || + [string equal $property "xsim.view"] || + [string equal $property "xsim.wdb"] || + [string equal $property "xsim.saif"] || + [string equal $property "xsim.more_options"] || + [string equal $property "modelsim.custom_do"] || + [string equal $property "modelsim.custom_udo"] || + [string equal $property "modelsim.vhdl_syntax"] || + [string equal $property "modelsim.use_explicit_decl"] || + [string equal $property "modelsim.log_all_signals"] || + [string equal $property "modelsim.sdf_delay"] || + [string equal $property "modelsim.saif"] || + [string equal $property "modelsim.incremental"] || + [string equal $property "modelsim.unifast"] || + [string equal $property "modelsim.64bit"] || + [string equal $property "modelsim.vsim_more_options"] || + [string equal $property "modelsim.vlog_more_options"] || + [string equal $property "modelsim.vcom_more_options"] } { + return true + } + return false +} + +proc write_files { proj_dir proj_name tcl_obj type } { + # Summary: write file and file properties + # This helper command is used to script help. + # Argument Usage: + # Return Value: + # none + + variable a_global_vars + variable l_script_data + + set l_local_file_list [list] + set l_remote_file_list [list] + + # return if empty fileset + if {[llength [get_files -quiet -of_objects [get_filesets $tcl_obj]]] == 0 } { + lappend l_script_data "# Empty (no sources present)\n" + return + } + + set fs_name [get_filesets $tcl_obj] + + set import_coln [list] + set add_file_coln [list] + + foreach file [get_files -norecurse -of_objects [get_filesets $tcl_obj]] { + if { [file extension $file] == ".xcix" } { continue } + set path_dirs [split [string trim [file normalize [string map {\\ /} $file]]] "/"] + set begin [lsearch -exact $path_dirs "$proj_name.srcs"] + set src_file [join [lrange $path_dirs $begin+1 end] "/"] + + # fetch first object + set file_object [lindex [get_files -of_objects [get_filesets $fs_name] [list $file]] 0] + set file_props [list_property $file_object] + + if { [lsearch $file_props "IMPORTED_FROM"] != -1 } { + + # import files + set imported_path [get_property "imported_from" $file] + set rel_file_path [get_relative_file_path_for_source $file [get_script_execution_dir]] + set proj_file_path "\$origin_dir/$rel_file_path" + + set file "\"[file normalize $proj_dir/${proj_name}.srcs/$src_file]\"" + + if { $a_global_vars(b_arg_no_copy_srcs) } { + # add to the local collection + lappend l_remote_file_list $file + if { $a_global_vars(b_absolute_path) } { + lappend add_file_coln "$file" + } else { + lappend add_file_coln "\"\[file normalize \"$proj_file_path\"\]\"" + } + } else { + # add to the import collection + lappend l_local_file_list $file + if { $a_global_vars(b_absolute_path) } { + lappend import_coln "$file" + } else { + lappend import_coln "\"\[file normalize \"$proj_file_path\"\]\"" + } + } + + } else { + set file "\"$file\"" + + # is local? add to local project, add to collection and then import this collection by default unless -no_copy_sources is specified + if { [is_local_to_project $file] } { + if { $a_global_vars(b_arg_dump_proj_info) } { + set src_file "\$PSRCDIR/$src_file" + } + + # add to the import collection + set file_no_quotes [string trim $file "\""] + set org_file_path "\$origin_dir/[get_relative_file_path_for_source $file_no_quotes [get_script_execution_dir]]" + lappend import_coln "\"\[file normalize \"$org_file_path\"\]\"" + lappend l_local_file_list $file + } else { + lappend l_remote_file_list $file + } + + # add file to collection + if { $a_global_vars(b_arg_no_copy_srcs) && (!$a_global_vars(b_absolute_path))} { + set file_no_quotes [string trim $file "\""] + set rel_file_path [get_relative_file_path_for_source $file_no_quotes [get_script_execution_dir]] + set file1 "\"\[file normalize \"\$origin_dir/$rel_file_path\"\]\"" + lappend add_file_coln "$file1" + } else { + lappend add_file_coln "$file" + } + + # set flag that local sources were found and print warning at the end + if { !$a_global_vars(b_local_sources) } { + set a_global_vars(b_local_sources) 1 + } + } + } + + if {[llength $add_file_coln]>0} { + lappend l_script_data "set files \[list \\" + foreach file $add_file_coln { + if { $a_global_vars(b_absolute_path) } { + lappend l_script_data " $file\\" + } else { + if { $a_global_vars(b_arg_no_copy_srcs) } { + lappend l_script_data " $file\\" + } else { + set file_no_quotes [string trim $file "\""] + set rel_file_path [get_relative_file_path_for_source $file_no_quotes [get_script_execution_dir]] + lappend l_script_data " \"\[file normalize \"\$origin_dir/$rel_file_path\"\]\"\\" + } + } + } + lappend l_script_data "\]" + lappend l_script_data "add_files -norecurse -fileset \$obj \$files" + lappend l_script_data "" + } + + # now import local files if -no_copy_sources is not specified + if { ! $a_global_vars(b_arg_no_copy_srcs)} { + if { [llength $import_coln] > 0 } { + lappend l_script_data "# Import local files from the original project" + lappend l_script_data "set files \[list \\" + foreach ifile $import_coln { + lappend l_script_data " $ifile\\" + } + lappend l_script_data "\]" + # is this a IP block fileset? if yes, import files into current source fileset + if { [is_ip_fileset $tcl_obj] } { + lappend l_script_data "set imported_files \[import_files -fileset [current_fileset -srcset] \$files\]" + } else { + lappend l_script_data "set imported_files \[import_files -fileset $tcl_obj \$files\]" + } + lappend l_script_data "" + } + } + + # write fileset file properties for remote files (added sources) + write_fileset_file_properties $tcl_obj $fs_name $proj_dir $l_remote_file_list "remote" + + # write fileset file properties for local files (imported sources) + write_fileset_file_properties $tcl_obj $fs_name $proj_dir $l_local_file_list "local" + +} + +proc write_constrs { proj_dir proj_name tcl_obj type } { + # Summary: write constrs fileset files and properties + # Argument Usage: + # Return Value: + # none + + variable a_global_vars + variable l_script_data + + set fs_name [get_filesets $tcl_obj] + + # return if empty fileset + if {[llength [get_files -quiet -of_objects [get_filesets $tcl_obj]]] == 0 } { + lappend l_script_data "# Empty (no sources present)\n" + return + } + + foreach file [get_files -norecurse -of_objects [get_filesets $tcl_obj]] { + lappend l_script_data "# Add/Import constrs file and set constrs file properties" + set constrs_file {} + set file_category {} + set path_dirs [split [string trim [file normalize [string map {\\ /} $file]]] "/"] + set begin [lsearch -exact $path_dirs "$proj_name.srcs"] + set src_file [join [lrange $path_dirs $begin+1 end] "/"] + set file_object [lindex [get_files -of_objects [get_filesets $fs_name] [list $file]] 0] + set file_props [list_property $file_object] + + # constrs sources imported? + if { [lsearch $file_props "IMPORTED_FROM"] != -1 } { + set imported_path [get_property "imported_from" $file] + set rel_file_path [get_relative_file_path_for_source $file [get_script_execution_dir]] + set proj_file_path "\$origin_dir/$rel_file_path" + set file "\"[file normalize $proj_dir/${proj_name}.srcs/$src_file]\"" + # donot copy imported constrs in new project? set it as remote file in new project. + if { $a_global_vars(b_arg_no_copy_srcs) } { + set constrs_file $file + set file_category "remote" + if { $a_global_vars(b_absolute_path) } { + add_constrs_file "$file" + } else { + set str "\"\[file normalize \"$proj_file_path\"\]\"" + add_constrs_file $str + } + } else { + # copy imported constrs in new project. Set it as local file in new project. + set constrs_file $file + set file_category "local" + if { $a_global_vars(b_absolute_path) } { + import_constrs_file $tcl_obj "$file" + } else { + set str "\"\[file normalize \"$proj_file_path\"\]\"" + import_constrs_file $tcl_obj $str + } + } + } else { + # constrs sources were added, so check if these are local or added from remote location + set file "\"$file\"" + set constrs_file $file + + # is added constrs local to the project? import it in the new project and set it as local in the new project + if { [is_local_to_project $file] } { + # file is added from within project, so set it as local in the new project + set file_category "local" + + if { $a_global_vars(b_arg_dump_proj_info) } { + set src_file "\$PSRCDIR/$src_file" + } + set file_no_quotes [string trim $file "\""] + set org_file_path "\$origin_dir/[get_relative_file_path_for_source $file_no_quotes [get_script_execution_dir]]" + set str "\"\[file normalize \"$org_file_path\"\]\"" + if { $a_global_vars(b_arg_no_copy_srcs)} { + add_constrs_file "$str" + } else { + import_constrs_file $tcl_obj $str + } + } else { + # file is added from remote location, so set it as remote in the new project + set file_category "remote" + + # find relative file path of the added constrs if no_copy in the new project + if { $a_global_vars(b_arg_no_copy_srcs) && (!$a_global_vars(b_absolute_path))} { + set file_no_quotes [string trim $file "\""] + set rel_file_path [get_relative_file_path_for_source $file_no_quotes [get_script_execution_dir]] + set file_1 "\"\[file normalize \"\$origin_dir/$rel_file_path\"\]\"" + add_constrs_file "$file_1" + } else { + add_constrs_file "$file" + } + } + + # set flag that local sources were found and print warning at the end + if { !$a_global_vars(b_local_sources) } { + set a_global_vars(b_local_sources) 1 + } + } + write_constrs_fileset_file_properties $tcl_obj $fs_name $proj_dir $constrs_file $file_category + } +} + +proc add_constrs_file { file_str } { + # Summary: add constrs file + # This helper command is used to script help. + # Argument Usage: + # Return Value: + # none + + variable a_global_vars + variable l_script_data + + if { $a_global_vars(b_absolute_path) } { + lappend l_script_data "set file $file_str" + } else { + if { $a_global_vars(b_arg_no_copy_srcs) } { + lappend l_script_data "set file $file_str" + } else { + set file_no_quotes [string trim $file_str "\""] + set rel_file_path [get_relative_file_path_for_source $file_no_quotes [get_script_execution_dir]] + lappend l_script_data "set file \"\[file normalize \"\$origin_dir/$rel_file_path\"\]\"" + } + } + lappend l_script_data "set file_added \[add_files -norecurse -fileset \$obj \$file\]" +} + +proc import_constrs_file { tcl_obj file_str } { + # Summary: import constrs file + # This helper command is used to script help. + # Argument Usage: + # Return Value: + # none + + variable a_global_vars + variable l_script_data + + # now import local files if -no_copy_sources is not specified + if { ! $a_global_vars(b_arg_no_copy_srcs)} { + lappend l_script_data "set file $file_str" + lappend l_script_data "set file_imported \[import_files -fileset $tcl_obj \$file\]" + } +} + +proc write_constrs_fileset_file_properties { tcl_obj fs_name proj_dir file file_category } { + # Summary: write constrs fileset file properties + # This helper command is used to script help. + # Argument Usage: + # Return Value: + # none + + variable a_global_vars + variable l_script_data + variable l_local_files + variable l_remote_files + + set file_prop_count 0 + + # collect local/remote files for the header section + if { [string equal $file_category "local"] } { + lappend l_local_files $file + } elseif { [string equal $file_category "remote"] } { + lappend l_remote_files $file + } + + set file [string trim $file "\""] + + # fix file path for local files + if { [string equal $file_category "local"] } { + set path_dirs [split [string trim [file normalize [string map {\\ /} $file]]] "/"] + set src_file [join [lrange $path_dirs end-1 end] "/"] + set src_file [string trimleft $src_file "/"] + set src_file [string trimleft $src_file "\\"] + set file $src_file + } + + set file_object "" + if { [string equal $file_category "local"] } { + set file_object [lindex [get_files -of_objects [get_filesets $fs_name] [list "*$file"]] 0] + } elseif { [string equal $file_category "remote"] } { + set file_object [lindex [get_files -of_objects [get_filesets $fs_name] [list $file]] 0] + } + + # get the constrs file properties + set file_props [list_property $file_object] + set prop_info_list [list] + set prop_count 0 + foreach file_prop $file_props { + set is_readonly [get_property is_readonly [rdi::get_attr_specs $file_prop -object $file_object]] + if { [string equal $is_readonly "1"] } { + continue + } + set prop_type [get_property type [rdi::get_attr_specs $file_prop -object $file_object]] + set def_val [list_property_value -default $file_prop $file_object] + set cur_val [get_property $file_prop $file_object] + + # filter special properties + if { [filter $file_prop $cur_val $file] } { continue } + + # re-align values + set cur_val [get_target_bool_val $def_val $cur_val] + + set dump_prop_name [string tolower ${fs_name}_file_${file_prop}] + set prop_entry "" + if { [string equal $file_category "local"] } { + set prop_entry "[string tolower $file_prop]#[get_property $file_prop $file_object]" + } elseif { [string equal $file_category "remote"] } { + set prop_value_entry [get_property $file_prop $file_object] + set prop_entry "[string tolower $file_prop]#$prop_value_entry" + } + # include all properties? + if { $a_global_vars(b_arg_all_props) } { + lappend prop_info_list $prop_entry + incr prop_count + } else { + # include only non-default (default behavior) + if { $def_val != $cur_val } { + lappend prop_info_list $prop_entry + incr prop_count + } + } + + if { $a_global_vars(b_arg_dump_proj_info) } { + puts $a_global_vars(def_val_fh) "[file tail $file]=$file_prop ($prop_type) :DEFAULT_VALUE ($def_val)==CURRENT_VALUE ($cur_val)" + puts $a_global_vars(dp_fh) "$dump_prop_name=$cur_val" + } + } + + # write properties now + if { $prop_count>0 } { + if { {remote} == $file_category } { + if { $a_global_vars(b_absolute_path) } { + lappend l_script_data "set file \"$file\"" + } else { + lappend l_script_data "set file \"\$origin_dir/[get_relative_file_path_for_source $file [get_script_execution_dir]]\"" + lappend l_script_data "set file \[file normalize \$file\]" + } + } else { + lappend l_script_data "set file \"$file\"" + } + + lappend l_script_data "set file_obj \[get_files -of_objects \[get_filesets $tcl_obj\] \[list \"*\$file\"\]\]" + set get_what "get_files" + write_properties $prop_info_list $get_what $tcl_obj + incr file_prop_count + } + + if { $file_prop_count == 0 } { + lappend l_script_data "# None" + } +} + +proc write_specified_run { proj_dir proj_name runs } { + # Summary: write the specified run information + # This helper command is used to script help. + # Argument Usage: + # Return Value: + # none + + variable a_global_vars + variable l_script_data + + set get_what "get_runs" + foreach tcl_obj $runs { + # is block fileset based run that contains IP? donot create OOC run + if { [is_ip_run $tcl_obj] } { + continue + } + # fetch run attributes + set part [get_property part [$get_what $tcl_obj]] + set parent_run [get_property parent [$get_what $tcl_obj]] + set src_set [get_property srcset [$get_what $tcl_obj]] + set constrs_set [get_property constrset [$get_what $tcl_obj]] + set strategy [get_property strategy [$get_what $tcl_obj]] + set parent_run_str "" + if { $parent_run != "" } { + set parent_run_str " -parent_run $parent_run" + } + + set def_flow_type_val [list_property_value -default flow [$get_what $tcl_obj]] + set cur_flow_type_val [get_property flow [$get_what $tcl_obj]] + set def_strat_type_val [list_property_value -default strategy [$get_what $tcl_obj]] + set cur_strat_type_val [get_property strategy [$get_what $tcl_obj]] + + lappend l_script_data "# Create '$tcl_obj' run (if not found)" + lappend l_script_data "if \{\[string equal \[get_runs -quiet $tcl_obj\] \"\"\]\} \{" + set cmd_str " create_run -name $tcl_obj -part $part -flow {$cur_flow_type_val} -strategy \"$cur_strat_type_val\"" + lappend l_script_data "$cmd_str -constrset $constrs_set$parent_run_str" + lappend l_script_data "\} else \{" + lappend l_script_data " set_property strategy \"$cur_strat_type_val\" \[get_runs $tcl_obj\]" + lappend l_script_data " set_property flow \"$cur_flow_type_val\" \[get_runs $tcl_obj\]" + lappend l_script_data "\}" + + lappend l_script_data "set obj \[$get_what $tcl_obj\]" + write_props $proj_dir $proj_name $get_what $tcl_obj "run" + } +} + +proc get_fileset_type_switch { fileset_type } { + # Summary: Return the fileset type switch for a given fileset + # Argument Usage: + # Return Value: + # Fileset type switch name + + variable a_fileset_types + + set fs_switch "" + foreach {fs_data} $a_fileset_types { + set fs_type [lindex $fs_data 0] + if { [string equal -nocase $fileset_type $fs_type] } { + set fs_switch [lindex $fs_data 1] + set fs_switch "-$fs_switch" + break + } + } + return $fs_switch +} + +proc get_target_bool_val { def_val cur_val } { + # Summary: Resolve current boolean property value wrt its default value + # Argument Usage: + # Return Value: + # Resolved boolean value + + set target_val $cur_val + + if { [string equal $def_val "false"] && [string equal $cur_val "0"] } { set target_val "false" } \ + elseif { [string equal $def_val "true"] && [string equal $cur_val "1"] } { set target_val "true" } \ + elseif { [string equal $def_val "false"] && [string equal $cur_val "1"] } { set target_val "true" } \ + elseif { [string equal $def_val "true"] && [string equal $cur_val "0"] } { set target_val "false" } \ + elseif { [string equal $def_val "{}"] && [string equal $cur_val ""] } { set target_val "{}" } + + return $target_val +} + +proc write_fileset_file_properties { tcl_obj fs_name proj_dir l_file_list file_category } { + # Summary: + # Write fileset file properties for local and remote files + # Argument Usage: + # tcl_obj: object to inspect + # fs_name: fileset name + # l_file_list: list of files (local or remote) + # file_category: file catwgory (local or remote) + # Return Value: + # none + + variable a_global_vars + variable l_script_data + variable l_local_files + variable l_remote_files + + # is this a IP block fileset? if yes, set current source fileset + if { [is_ip_fileset $tcl_obj] } { + lappend l_script_data "# Set '[current_fileset -srcset]' fileset file properties for $file_category files" + } else { + lappend l_script_data "# Set '$tcl_obj' fileset file properties for $file_category files" + } + set file_prop_count 0 + + # collect local/remote files + foreach file $l_file_list { + if { [string equal $file_category "local"] } { + lappend l_local_files $file + } elseif { [string equal $file_category "remote"] } { + lappend l_remote_files $file + } else {} + } + + foreach file $l_file_list { + set file [string trim $file "\""] + + # fix file path for local files + if { [string equal $file_category "local"] } { + set path_dirs [split [string trim [file normalize [string map {\\ /} $file]]] "/"] + set src_file [join [lrange $path_dirs end-1 end] "/"] + set src_file [string trimleft $src_file "/"] + set src_file [string trimleft $src_file "\\"] + set file $src_file + } + + set file_object "" + if { [string equal $file_category "local"] } { + set file_object [lindex [get_files -of_objects [get_filesets $fs_name] [list "*$file"]] 0] + } elseif { [string equal $file_category "remote"] } { + set file_object [lindex [get_files -of_objects [get_filesets $fs_name] [list $file]] 0] + } + + set file_props [list_property $file_object] + set prop_info_list [list] + set prop_count 0 + + foreach file_prop $file_props { + set is_readonly [get_property is_readonly [rdi::get_attr_specs $file_prop -object $file_object]] + if { [string equal $is_readonly "1"] } { + continue + } + + set prop_type [get_property type [rdi::get_attr_specs $file_prop -object $file_object]] + set def_val [list_property_value -default $file_prop $file_object] + set cur_val [get_property $file_prop $file_object] + + # filter special properties + if { [filter $file_prop $cur_val $file] } { continue } + + # re-align values + set cur_val [get_target_bool_val $def_val $cur_val] + + set dump_prop_name [string tolower ${fs_name}_file_${file_prop}] + set prop_entry "" + if { [string equal $file_category "local"] } { + set prop_entry "[string tolower $file_prop]#[get_property $file_prop $file_object]" + } elseif { [string equal $file_category "remote"] } { + set prop_value_entry [get_property $file_prop $file_object] + set prop_entry "[string tolower $file_prop]#$prop_value_entry" + } else {} + + if { $a_global_vars(b_arg_all_props) } { + lappend prop_info_list $prop_entry + incr prop_count + } else { + if { $def_val != $cur_val } { + lappend prop_info_list $prop_entry + incr prop_count + } + } + + if { $a_global_vars(b_arg_dump_proj_info) } { + puts $a_global_vars(def_val_fh) "[file tail $file]=$file_prop ($prop_type) :DEFAULT_VALUE ($def_val)==CURRENT_VALUE ($cur_val)" + puts $a_global_vars(dp_fh) "$dump_prop_name=$cur_val" + } + } + + # write properties now + if { $prop_count>0 } { + if { {remote} == $file_category } { + if { $a_global_vars(b_absolute_path) } { + lappend l_script_data "set file \"$file\"" + } else { + lappend l_script_data "set file \"\$origin_dir/[get_relative_file_path_for_source $file [get_script_execution_dir]]\"" + lappend l_script_data "set file \[file normalize \$file\]" + } + } else { + lappend l_script_data "set file \"$file\"" + } + # is this a IP block fileset? if yes, get files from current source fileset + if { [is_ip_fileset $tcl_obj] } { + lappend l_script_data "set file_obj \[get_files -of_objects \[get_filesets [current_fileset -srcset]\] \[list \"*\$file\"\]\]" + } else { + lappend l_script_data "set file_obj \[get_files -of_objects \[get_filesets $tcl_obj\] \[list \"*\$file\"\]\]" + } + set get_what "get_files" + write_properties $prop_info_list $get_what $tcl_obj + incr file_prop_count + } + } + + if { $file_prop_count == 0 } { + lappend l_script_data "# None" + } + lappend l_script_data "" +} + +proc get_script_execution_dir { } { + # Summary: Return script directory path from where the script will be executed + # Argument Usage: + # none + # Return Value: + # Path to the script direc + + variable a_global_vars + + # default: return script directory path + set scr_exe_dir $a_global_vars(s_path_to_script_dir) + + # is -path_to_relative specified and the path exists? return this dir + set rel_to_dir $a_global_vars(s_relative_to) + if { ("." != $rel_to_dir) } { + set rel_to_dir [file normalize $rel_to_dir] + if { [file exists $rel_to_dir] } { + set scr_exe_dir $rel_to_dir + } + } + return $scr_exe_dir +} + +# TODO: This is the same as xcs_get_relative_file_path for simulators, see common/utils.tcl +# Remember to add the 'source .../common/utils.tcl' in the write_project_tcl proc to load the common file +proc get_relative_file_path_for_source { file_path_to_convert relative_to } { + # Summary: Get the relative path wrt to path specified + # Argument Usage: + # file_path_to_convert: input file to make relative to specfied path + # Return Value: + # Relative path wrt the path specified + + variable a_xport_sim_vars + + # make sure we are dealing with a valid relative_to directory. If regular file or is not a directory, get directory + if { [file isfile $relative_to] || ![file isdirectory $relative_to] } { + set relative_to [file dirname $relative_to] + } + + set cwd [file normalize [pwd]] + + if { [file pathtype $file_path_to_convert] eq "relative" } { + # is relative_to path same as cwd?, just return this path, no further processing required + if { [string equal $relative_to $cwd] } { + return $file_path_to_convert + } + # the specified path is "relative" but something else, so make it absolute wrt current working dir + set file_path_to_convert [file join $cwd $file_path_to_convert] + } + + # is relative_to "relative"? convert to absolute as well wrt cwd + if { [file pathtype $relative_to] eq "relative" } { + set relative_to [file join $cwd $relative_to] + } + + # normalize + set file_path_to_convert [file normalize $file_path_to_convert] + set relative_to [file normalize $relative_to] + + set file_path $file_path_to_convert + set file_comps [file split $file_path] + set relative_to_comps [file split $relative_to] + + set found_match false + set index 0 + + set fc_comps_len [llength $file_comps] + set rt_comps_len [llength $relative_to_comps] + + # compare each dir element of file_to_convert and relative_to, set the flag and + # get the final index till these sub-dirs matched. Break if limit reaches. + while { [lindex $file_comps $index] == [lindex $relative_to_comps $index] } { + if { !$found_match } { set found_match true } + incr index + if { ($index == $fc_comps_len) || ($index == $rt_comps_len) } { + break; + } + } + + # any common dirs found? convert path to relative + if { $found_match } { + set parent_dir_path "" + set rel_index $index + # keep traversing the relative_to dirs and build "../" levels + while { [lindex $relative_to_comps $rel_index] != "" } { + set parent_dir_path "../$parent_dir_path" + incr rel_index + } + + # + # at this point we have parent_dir_path setup with exact number of sub-dirs to go up + # + + # now build up part of path which is relative to matched part + set rel_path "" + set rel_index $index + + while { [lindex $file_comps $rel_index] != "" } { + set comps [lindex $file_comps $rel_index] + if { $rel_path == "" } { + # first dir + set rel_path $comps + } else { + # append remaining dirs + set rel_path "${rel_path}/$comps" + } + incr rel_index + } + + # prepend parent dirs, this is the complete resolved path now + set resolved_path "${parent_dir_path}${rel_path}" + + return $resolved_path + } + + # no common dirs found, just return the normalized path + return $file_path +} + +proc is_ip_fileset { fileset } { + # Summary: Find IP's if any from the specified fileset and return true if 'generate_synth_checkpoint' is set to 1 + # Argument Usage: + # fileset: fileset name + # Return Value: + # true (1) if success, false (0) otherwise + + # make sure fileset is block fileset type + if { {BlockSrcs} != [get_property fileset_type [get_filesets $fileset]] } { + return false + } + + set ip_filter "FILE_TYPE == \"IP\" || FILE_TYPE==\"Block Designs\"" + set ips [get_files -all -quiet -of_objects [get_filesets $fileset] -filter $ip_filter] + set b_found false + foreach ip $ips { + if { [get_property generate_synth_checkpoint [lindex [get_files -all $ip] 0]] } { + set b_found true + break + } + } + + if { $b_found } { + return true + } + return false +} + +proc is_proxy_ip_fileset { fileset } { + # Summary: Determine if the fileset is an OOC run for a proxy IP that has a parent composite + # Argument Usage: + # fileset: fileset name + # Return Value: + # true (1) if the fileset contains an IP at its root with a parent composite, false (0) otherwise + + # make sure fileset is block fileset type + if { {BlockSrcs} != [get_property fileset_type [get_filesets $fileset]] } { + return false + } + + set ip_with_parent_filter "FILE_TYPE == IP && PARENT_COMPOSITE_FILE != \"\"" + if {[llength [get_files -norecurse -quiet -of_objects [get_filesets $fileset] -filter $ip_with_parent_filter]] == 1} { + return true + } + + return false +} + + +proc is_ip_run { run } { + # Summary: Find IP's if any from the fileset linked with the block fileset run + # Argument Usage: + # run: run name + # Return Value: + # true (1) if success, false (0) otherwise + + set fileset [get_property srcset [get_runs $run]] + return [is_ip_fileset $fileset] +} +}