#
#*******************************************************************************************************************************************
#
# script for performing the common tasks that completer scripts require
#
#   originally written to support the rapid development of both generic designs as well as the Unified Embedded Design
#
#    see Doxygen generated documentation
#
#
#<copyright-disclaimer-start>
#  **************************************************************************************************************
#  * © 2023 Advanced Micro Devices, Inc. All rights reserved.                                                   *
#  * DISCLAIMER                                                                                                 *
#  * The information contained herein is for informational purposes only, and is subject to change              *
#  * without notice. While every precaution has been taken in the preparation of this document, it              *
#  * may contain technical inaccuracies, omissions and typographical errors, and AMD is under no                *
#  * obligation to update or otherwise correct this information.  Advanced Micro Devices, Inc. makes            *
#  * no representations or warranties with respect to the accuracy or completeness of the contents of           *
#  * this document, and assumes no liability of any kind, including the implied warranties of noninfringement,  *
#  * merchantability or fitness for particular purposes, with respect to the operation or use of AMD            *
#  * hardware, software or other products described herein.  No license, including implied or                   *
#  * arising by estoppel, to any intellectual property rights is granted by this document.  Terms and           *
#  * limitations applicable to the purchase or use of AMD’s products are as set forth in a signed agreement     *
#  * between the parties or in AMD's Standard Terms and Conditions of Sale. GD-18                               *
#  *                                                                                                            *
#  **************************************************************************************************************
#<copyright-disclaimer-end>
#
# History:
#    2024.1    - RS - 2024/08/14 - added make external command in LEDsLinearAdd proc and corrected the output size for VCK190 and ZCU104 to 4. 
#    2024.1    - RS - 2024/08/05 - merged helper and completer_ completer_helper files
#    2023.2    - WK - 2024/02/12 - fixed MPSoC entry in LEDsLinearAdd
#    2023.2    - RS - 2023/11/06 - modified the GPIO width to 8 for mpsoc and versal and tied the Linear LEDs ip to vio 
#    2023.1    - LR - 2023/09/23 - cleaned up ACAP & NoC property access, proc's processorConfigure & versalNocAdd. Modified LEDsLinearAdd & to be compatible with LEDs_rosetta naming in xdc constraint files
#    2023.1    - WK - 2023/05/21 - cleaned up initial checks, added support for FPGA/Lang classes to pull from common rather than CustEdIP, added FPGA/Lang specific support
#    2023.1    - WK - 2023/05/15 - fixed numerous small errors in MPSoC code for GPIOs
#    2022.2    - WK - 2022/12/06 - forced definition of trainingPath as there was a bit of circular logic that was causing some problems
#    2021.2    - LR - 2022/05/05 - Removed adding external LED Linear ports for Versal, as UED design starting throwing errors, suspect changes to this file made in last month - 2021.2
#    2021.2    - LR - 2022/03/15 - Updated LEDsLinearAdd, xTrigPSenable, and ILAconnect for Versal and 2021.2 support
#    2021.2    - LR - 2022/03/15 - Added versalNocAdd for Versal support
#    2021.2    - LR - 2022/03/15 - Added "@@@ completer_helper - " to messages to make them easier to find and which tcl file is producing them when debugging, please do not remove
#    2021.2    - WK - 2022/03/01 - removed ES support as the boards themselves have been "recalled" and relaced with production boards
#    2021.2    - LR - 2022/01/21 - Added vck190es & VMK180es support
#    2021.2    - WK - 2022/01/20 - updated for Doxygen compatibility
#    2021.1    - WK - 2021/08/28 - cleaned up sourcesAdd
#    2021.1    - NK - 2021/07/11 - Added CIPS 3.0 support based on 2021 release
#    2020.2    - WK - 2021/05/04 - removed unnecessary environment variables prior to 2021 releases
#    2020.2    - WK - 2020/11/02 - added support for ACAP VCK190, VMK180; minor enhancment for blockDesignWrap
#    2020.1    - AM - 2020/07/16 - Fixed sourcing failure due to wrong variable check custEdIP instead of CustEdIP - line 144
#    2020.1    - WK - 2020/06/05 - migrated use, loadedProcs, and make to helper script (as helper script loaded when this script is opened)
#    2019.2    - WK - 2020/01/22 - fixed designExport
#    2019.2    - WK - 2020/01/14 - moved "USE" to script; tests for helper script being loaded and if not, loads it
#    2019.2    - WK - 2019/11/21 - added BRAMadd
#    2019.1    - LR - 2019/11/12 - added additional terms for zeroing the MPSoC
#    2019.1    - WK - 2019/10/28 - added support for environment variables, removed extra /training layer as it is now defined by $trainingPath
#    2019.1    - WK - 2019/06/09 - fixed make All (issue with dealing with arguments in the list)
#    2019.1    - WK - 2019/06/11 - updated LEDsLinearAdd for ZCU102
#    2019.1    - WK - 2019/05/06 - added support for ZCU111 and ZCU102. minor code cleanup.
#    2019.1    - OB,NK,WK - 2019/03/?? - documenting and general cleanup
#    2018.3    - LR - 2019/02/06 - fixed Xparernt cell gneration and unused interrupt input
#    2018.3    - WK - 2019/01/22 - cleaned up problems associated with MicroBlaze processor selection
#    2018.3    - AM - 2019/01/16 - Added Linux paths
#    2018.1a   - WK - 2018/05/02 - added RFSoC support in USE
#    2018.1    - LR - 2018/04/16 - Fixed blockDesignWrap for proper operation
#    2017.3    - WK - 2017/10/16 - "make" now works with comma separated list of arguments within parenthesis constraintFilesAdd(file1,file2,...); got constraintFilesAdd working
#    2017.3    - WK - 2017/09/14 - deprecation of "makeTo" and "makeToEndOfStep"
#    2016.3    - WK - 2017/01/13 - addition of new procs, further testing of existing procs, includsion of UED similar capabilities
#    initial   - WK - 2016/11/10 - based on many other completer scripts
#
#*******************************************************************************************************************************************
#

set completer_helper_loaded 0;      # have a defineable variable to check for success or failure of this load
set suppressLogErrors       1;
#
# identify the OS that we are currently running in...
variable hostOS [lindex $tcl_platform(os) 0];

# if the badEnv variable has not been created by the script that called  completer_helper, create it and issue a warning
if {![info exists badEnv]} {
   set badEnv 0;
   puts "@@@  completer_helper - WARNING! badEnv variable wasn't set by the calling script to  completer_helper - now set in  completer_helper";
   #set trace [strace]
   #set thisLevel [info level]
   # since trace is still sitting on the stack we have to go up two instead of one
   #set upTwoLevels [expr $thisLevel - 2]
   #set thisLevelName [lindex $trace $upTwoLevels]
   #puts "@@@  completer_helper - This likely means that an absolute path was used in the calling script ($thisLevelName) which is a bad practice!";
}

# is verbose mode defined?
if {![info exists verbose]} {
   variable verbose 0;        # just define the verbose variable and keep it disabled
}

# variables commonly used by CustEd procs
variable language              undefined;
variable platform              undefined;
variable lastStep              notStarted;
variable processor             undefined;
variable activePeripheralList  "";
variable debugLog              0;
variable usingQEMU             0;
variable myLocation            [file normalize [info script]];
variable suppressLogErrors     0;

# *********************************** DEBUG ***********************************
# here's the issue: we need to determine if the helper scripts are available 
# from either the training directory which is where the students would use it 
# from or the repository which is where developers building the student starting
# points would draw it from. We've been using a fairly convoluted logic process
# to determine this and we'll replace it 7/13/22 with a much simpler approach which
# uses the existing buildingStartingPoint variable which will be present and set to 1
# if this was called from the starting point building flow and will not be present 
# for the student. Based on this, we'' set the value of custEdIP so that the script
# can load what is needed
#
puts "@@@  completer_helper - checking environment to see if this is a builder use or a student use";

# if buildingStartingPoint is defined and set to 1, then we know that we're building from the repository and the CustEd location variable needs to reflect that
# otherwise the student is using this script and we should grab stuff from the training directory
if {[info exists buildingStartingPoint]} {
   puts "@@@  completer_helper - Developer mode - pull scripts from repository";
   # make sure the repository is defined, if not, then bail
   # NOTE: by definition the REPOSITORY variable points to the useable aspect (i.e. \Xilinx_SVN\trunk)
   if {[catch {variable repo $::env(REPOSITORY)} emsg]} {
      puts stderr "ERROR!!! REPOSITORY environment variable not defined - therefore this cannot be a startingPoint build!"
      set badEnv 1;
   } else {
      # looks like a startingPoint build so we can use the $REPOSITORY as the custEdIP directory and not worry about using trainingPath stuff as it may not have been copied yet
      set custEdIP $repo/custEdIP;
      regsub -all {\\} $custEdIP / custEdIP;
      puts "@@@  completer_helper - building starting point operational mode";
   }
} else {
   puts "@@@  completer_helper - Student mode - pull scripts from TRAINING_PATH ($trainingPath)";
	   
	# make sure the training path exists, if it doesn't, then bail!
	if {[catch {set trainingPath $::env(TRAINING_PATH)} emsg]} {
		puts stderr "ERROR!!! TRAINING_PATH environment variable not defined!"
		set badEnv 1;
	} else {
		puts "@@@  completer_helper - trainingPath variable set - checking for drive specifiers to see if this is a windows or linux situation";
		# if we were called from an FPGA or language script then FPGA will be set to '1' otherwise it will not exist
		if {![info exists FPGA]} {
			set FPGA 0
		}
		if {$FPGA} {
			set custEdIP $trainingPath/common
			regsub -all {\\} $custEdIP / custEdIP;
		} else {
			set custEdIP $trainingPath;
			append custEdIP / CustEdIP;
			regsub -all {\\} $custEdIP / custEdIP;

			puts "@@@  completer_helper - custEdIP - checking if a drive was specified";
			puts "@@@  completer_helper - checking $custEdIP now...";
			set driveDesignatorPresent [string first \: $custEdIP];
			if {$driveDesignatorPresent > -1} {
				puts "@@@  completer_helper - custEdIP - drive specified - stripping";
				set custEdIP [string range $custEdIP 2 [string length $custEdIP]]; # strip the drive: from the path
			#puts "@@@  completer_helper - custEdIP - custEdIP now set to $custEdIP";
			} else {
				puts "@@@  completer_helper - custEdIP - no drive specification found, running with $custEdIP";
			}
		}
		regsub -all {\\} $trainingPath / trainingPath;
	}
}

#
# if everything is OK, then continue, otherwise throw an error here and quit
if {$badEnv} {
   #puts -nonewline "Hit Enter to exit ==> "
   #flush stdout
   #gets stdin
   puts stderr "Environment not properly configured! Exiting in...";
   for {set i 10} {$i > 0} {incr i -1} {
      puts stderr $i;
      after 1000;
   }
   exit 1
}

# build a manifest of each proc that is successfully loaded
if {![info exists loadedProcs]} {
   puts "@@@  completer_helper - loadedProcs variable not defined - defining it in  completer_helper";
   variable loadedProcs {};
}

#
# load the additional helper script files
set scriptLocation $custEdIP;
if {$FPGA} {set scriptLocation $trainingPath/common; }
set scriptFiles {helper_logging.tcl helper_strings.tcl helper_files.tcl helper_lists.tcl};
foreach scriptFile $scriptFiles {
	puts "@@@  completer_helper - attempting to source $scriptFile";
	if {[file exist $scriptLocation/$scriptFile]} {
		puts "   loading from specified script location in starting point: $scriptLocation/$scriptFile";
		source $scriptLocation/$scriptFile;
	} elseif {[info exist buildingStartingPoint]} {
		puts "   loading from repository: $repo/$scriptFile"
		source $repo/$scriptFile;
	} else {
		puts stderr " completer_helper: failed when attempting to load $scriptFile!";
		after 5000;
		# exit 1;
	}
}
puts "@@@  completer_helper - done sourcing helper scripts";



### Variable Definitions ###
variable helperErrorList {};        # all errors get tossed into this list. The list can then be explored or cleared

##
# proc:  helperErrorsClear
# descr: clears the internal list of errors generated by the helper scripts
# @remark internal error errors clear erase remove delete
#
proc helperErrorsClear {} {
   variable helperErrorList;
   set helperErrorList {};
}
#lappend loadedProcs {helperErrorsClear "clears the internal list of errors generated by the helper scripts"};
##
# proc:  helperErrors
# descr: returns an unsorted, unfiltered copy of the list of errors
# @remark dump error errors get internal
# @return an unsorted, unfiltered copy of the list of errors
#
proc helperErrors {} {
   variable helperErrorList;
   return $helperErrorList;
}
#lappend loadedProcs {helperErrors "returns an unsorted, unfiltered copy of the list of errors"};
##
# proc:  helperErrorsCount
# descr: get the number of errors in the list
# @remark dump error errors get internal
# @return number of errors in the internal error list
#
proc helperErrorsCount {} {
   variable helperErrorList;
   return [llength $helperErrorList];
}
#lappend loadedProcs { helperErrorsCount "get the number of errors in the list"};
##
# proc:  helperErrorAdd
# descr: adds the argument to the internal error list
# @remark add error errors append
# @param errMsg
# @return none
#
proc helperErrorAdd { errMsg } {
   variable helperErrorList;
   set helperErrorList [lappend helperErrorList $errMsg]
   # errorMsg $errMsg;         # and add it to the log file - not needed as helperErrorAdd is called by errorMsg
}
#lappend loadedProcs { helperErrorsAdd "adds the argument to the internal error list" };

##
# Status - not fully validated, used in  completer_helper
# proc:  nextTerminator
# descr: comma separated list within the parenthesis
# @remark <list of searchable terms>
# @param str  (comma separated list)
# @return integer location of next terminator in the passed string (comma or closing parenthesis)
#
proc nextTerminator {str} {
   set nextCommaPos    [string first , $str];  # arguments are delimited by commas within the parenthesis
   set closingParenPos [string first ) $str]

   # if there is no comma or closing parenthesis, then we have a problem!
   if {$nextCommaPos < 0 && $closingParenPos < 0} {
      puts "@@@  completer_helper - missing closing parenthesis!"
   } else {

      # is there a comma?
      if {$nextCommaPos > -1} {
         #set rtnStr [string range $str 0 $nextCommaPos]
       return $nextCommaPos
      } else {
         # no commas remain therefore this must close with a parenthesis
        #set rtnStr [string range 0 $closingParenPos]
       return $closingParenPos
      }
   }
   return -1;
}
#lappend loadedProcs {nextTerminator "returns integer location of next terminator in the passed string (comma or closing parenthesis)" };
##
# proc:  use
# descr: proc for doing wide range of configurable items
# @remark <list of searchable terms>
# @param thing
#
proc use { thing } {
   variable processor;
   variable hdfName;
   variable language;
   variable platform;
   variable userIO;
   variable ACAPactivePeripheralList;
   variable NOCactiveConfigurationList;
   variable MPSoCactivePeripheralList;
   variable APSoCactivePeripheralList;
   variable activePeripheralList;
   variable activeConfigurationList;
   variable usingQEMU;
   variable verbose;

   # if the variable is not yet in use, initialize it
   if {[info exists processor] == 0} { set processor undefined }
   if {[info exists hdfName] == 0}   { set hdfName   undefined }
   if {[info exists language] == 0}  { set language  undefined }
   if {[info exists platform] == 0}  { set platform  undefined }
   if {[info exists userIO] == 0}    { set userIO    base }
   if {[info exists usingQEMU] == 0} { set usingQEMU 0}

   if {$verbose} { puts "@@@  completer_helper - setting environment to use $thing"; }

   # what kind of platform is being used? Determine the hdf name and type of processor
   if { [strsame $thing VCK190] } {
      set platform VCK190;
      set processor A72;
      if { ![strsame $processor MicroBlaze] } {

         set processor psv_cortexa72_0;                                 # validate - this is a guess

         if {[info exists ACAPactivePeripheralList]} {
           set activePeripheralList $ACAPactivePeripheralList;
         } else {
           puts "@@@  completer_helper - Variable ACAPactivePeripheralList must be defined and filled with user defined peripherals.";
           puts "@@@  completer_helper - In order to keep this script running, this variable will be defined, but not filled with any peripherals";
           set activePeripheralList {};
         }

         if {[info exists NOCactiveConfigurationList]} {
           set activeConfigurationList $NOCactiveConfigurationList;
         } else {
           puts "@@@  completer_helper - Variable NOCactiveConfigurationList must be defined and filled with user defined NOC configuration.";
           puts "@@@  completer_helper - In order to keep this script running, this variable will be defined, but not filled with any peripherals";
           set activeConfigurationList {};
         }
      } else {
         # processor is a MicroBlaze
      }
      if {$verbose} { puts "@@@  completer_helper - platform: $thing; using $processor" }

   } elseif { [strsame $thing VMK180] } {
      puts "@@@  completer_helper - !!! Warning - settings still in validation!!! - VMK180 not officially supported, please select VCK190 or VCK190es";
      set platform VMK180;
      set processor A72;
      if { ![strsame $processor MicroBlaze] } {

         set processor psv_cortexa72_0;                                 # validate - this is a guess

         if {[info exists ACAPactivePeripheralList]} {
           set activePeripheralList $ACAPactivePeripheralList;
         } else {
           puts "@@@  completer_helper - Variable ACAPactivePeripheralList must be defined and filled with user defined peripherals.";
           puts "@@@  completer_helper - In order to keep this script running, this variable will be defined, but not filled with any peripherals";
           set activePeripheralList {};
         }

         if {[info exists NOCactiveConfigurationList]} {
           set activeConfigurationList $NOCactiveConfigurationList;
         } else {
           puts "@@@  completer_helper - Variable NOCactiveConfigurationList must be defined and filled with user defined NOC configuration.";
           puts "@@@  completer_helper - In order to keep this script running, this variable will be defined, but not filled with any peripherals";
           set activeConfigurationList {};
         }
      } else {
         # processor is a MicroBlaze
      }
      if {$verbose} { puts "@@@  completer_helper - platform: $thing; using $processor" }

   } elseif { [strsame $thing ZCU102] } {
      puts "@@@  completer_helper - !!! Warning - ZCU102 not a generally supported board - do you mean ZCU104??? !!!"
      set platform ZCU102
      set processor A53
      puts "@@@  completer_helper - Processor is $processor"
      if { ![strsame $processor MicroBlaze] } {
         if {$verbose} { puts "@@@  completer_helper - platform: ZCU102; using A53" }
         set processor psu_cortexa53_0

         if {[info exists MPSoCactivePeripheralList]} {
           set activePeripheralList $MPSoCactivePeripheralList
         } else {
           puts "@@@  completer_helper - Variable MPSoCactivePeripheralList must be defined and filled with user defined peripherals."
           puts "@@@  completer_helper - In order to keep this script running, this variable will be defined, but not filled with any peripherals"
           set activePeripheralList {}
         }
      } else {
         # processor is a MicroBlaze
         if {$verbose} { puts "@@@  completer_helper - platform: ZCU102; using uB" }
      }
   } elseif { [strsame $thing ZCU104] } {
      set platform ZCU104
      set processor A53
      puts "@@@  completer_helper - Processor is $processor"
      if { ![strsame $processor MicroBlaze] } {
         if {$verbose} { puts "@@@  completer_helper - platform: ZCU104; using A53" }
         set processor psu_cortexa53_0

         if {[info exists MPSoCactivePeripheralList]} {
           set activePeripheralList $MPSoCactivePeripheralList
         } else {
           puts "@@@  completer_helper - Variable MPSoCactivePeripheralList must be defined and filled with user defined peripherals."
           puts "@@@  completer_helper - In order to keep this script running, this variable will be defined, but not filled with any peripherals"
           set activePeripheralList {}
         }
      } else {
         # processor is a MicroBlaze
         if {$verbose} { puts "@@@  completer_helper - platform: ZCU104; using uB" }
      }
   } elseif { [strsame $thing ZC702] } {
      #warningMsg "helper.use: ZC702 is a deprecated board for CustEd classes - consider if this is the proper target"
      set platform  ZC702
      if { ![strsame $processor "MicroBlaze"]} {
         if {$verbose} { puts "@@@  completer_helper - platform: ZC702; using A9" }
         set processor ps7_cortexa9_0

         if {[info exists APSoCactivePeripheralList]} {
           set activePeripheralList $APSoCactivePeripheralList
         } else {
           puts "@@@  completer_helper - Variable APSoCactivePeripheralList must be defined and filled with user defined peripherals."
           puts "@@@  completer_helper - In order to keep this script running, this variable will be defined, but not filled with any peripherals"
           set activePeripheralList {}
         }
      } else {
         # processor is a MicroBlaze
         if {$verbose} { puts "@@@  completer_helper - platform: ZC702; using uB" }
      }
   } elseif { [strsame $thing "Zed"] } {
      #warningMsg "helper.use: Zed is a deprecated board for CustEd classes - consider if this is the proper target"
      set platform  Zed
      if { ![strsame $processor "MicroBlaze"] } {
        if {$verbose} { puts "@@@  completer_helper - platform: Zed; using A9" }
         set processor ps7_cortexa9_0

        # assign peripheral list
       if {[info exists APSoCactivePeripheralList]} {
          set activePeripheralList $APSoCactivePeripheralList
        } else {
          puts "@@@  completer_helper - Variable APSoCactivePeripheralList must be defined and filled with user defined peripherals."
          puts "@@@  completer_helper - In order to keep this script running, this variable will be defined, but not filled with any peripherals"
         set activePeripheralList {}
       }
     } else {
        # processor is a microblaze
       if {$verbose} { puts "@@@  completer_helper - platform: Zed; using uB" }
     }
   } elseif { [strsame $thing KC705] } {
       set processor microblaze_0
       set platform  KC705
       puts "@@@  completer_helper - !!! Deprecated board! (KC705) !!!"
   } elseif { [strsame $thing ZCU111] } {
      set processor zynq_ultra_ps_e_0
      set platform ZCU111;
      if {[info exists MPSoCactivePeripheralList]} {
         puts "@@@  completer_helper - setting the peripheral list for this device"
         set activePeripheralList $MPSoCactivePeripheralList
      }
   } elseif {[strsame $thing "RFSoC"] } {
      set processor zynq_ultra_ps_e_0
      set platform ZCU111;
      if {[info exists MPSoCactivePeripheralList]} {
         puts "@@@  completer_helper - setting the peripheral list for this device"
         set activePeripheralList $MPSoCactivePeripheralList
      }
   }  elseif { [strsame $thing "KC705"] } {
        set platform KC705
        #warningMsg "helper.use: KC705 is a deprecated board for CustEd classes - consider if this is the proper target"
   } elseif { [strsame $thing "KCU105"] } {
        set platform KCU105
        #warningMsg "helper.use: KCU105 is a deprecated board for CustEd classes - consider if this is the proper target"
   #} elseif { [strsame $thing "KC7xx"] } {
    #   set platform KC7xx
   } elseif {[strsame $thing "vhdl"] } {
        set language vhdl
   } elseif {[strsame $thing "verilog"] } {
        set language verilog
   } elseif {[strsame $thing "netlist"] } {
        set language netlist
   } elseif { [strsame $thing "base"] } {
      set userIO base
   } elseif { [strsame $thing "FMC-CE"] } {
      set userIO FMC-CE
      puts "@@@  completer_helper - FMC-CE has been deprecated!"
   } elseif {[strsame $thing "A9"] } {
      set processor ps7_cortexa9_0
   } elseif {[strsame $thing "ps7_cortexa9_0"] } {
      set processor ps7_cortexa9_0
   } elseif {[strsame $thing "APU"] } {
      set processor A53
   } elseif {[strsame $thing "A53"] } {
      set processor A53
   } elseif {[strsame $thing "RPU"] } {
      set processor R5
   } elseif {[strsame $thing "R5"] } {
      set processor R5
   } elseif {[strsame $thing "PMU"] } {
      set processor MicroBlaze
   } elseif {[strsame $thing "MicroBlaze"] } {
      set processor MicroBlaze
   } elseif {[strsame $thing "microblaze_0"] } {
      set processor MicroBlaze
   } elseif {[strsame $thing "uB"] } {
      set processor MicroBlaze
   } elseif {[strsame $thing "QEMU"] } {
      set usingQEMU 1
   } else {
      puts "@@@  completer_helper - Unknown use item! $thing"
      return
   }
}
#lappend loadedProcs {use "proc for doing wide range of configurable items"};
#
# make stopAfterStep
#    requires a list of list named "stepList"
#       structure is as follows: each list within stepList is a list of procs to be called
#    example of a lab which is comprised of 3 steps
#
# Example of how to create the stepList in the calling proc:
# set stepList {{step1_instruction1 step1_instruction2 step1_instruction3}\
#               {step2_instruction1}\
#               {step3_instruction1 step3_instruction2 step3_instruction3 step3_instruction4 step3_instruction5 step3_instruction6}
#              }
#
# makeStep only builds the specified step (unlike make which builds upto and including that step)
##
# proc:  makeStep
# descr: runs the procs described in the global stepList for only the specified step
# @remark make step build
# @param stepToDo
#
proc makeStep {stepToDo} {
   variable stepList;
   variable verbose;
   if { $verbose } { infoMsg "helper.makeStep $stepToDo" }

   # subtract one as the list begins at 0 and users will start at 1
   set stepToDo [expr $stepToDo - 1]

   # is it a legal step?
   set nSteps [llength $stepList]
   if {[llength $stepList] >= $stepToDo} {
      # extract the instruction list from the stepList
      set theseInstructions [lindex $stepList $stepToDo]
      if {$verbose} { infoMsg "iterating step $stepToDo which consists of the following instructions: $theseInstructions"; }

      # loop through the instructions included in this step
      for {set j 0} {$j < [llength $theseInstructions]} {incr j} {
         set hasArguments 0;
         set argList {};
         set thisInstruction [lindex $theseInstructions $j];
         infoMsg "running this instruction: $thisInstruction"
         # separate out the arguments if present
         if {[strContains $thisInstruction (]} {
            if {$verbose} { infoMsg "instruction has arguments"; }
            set hasArguments 1

            # extract just the instruction
            set instructionEndPos [string first ( $thisInstruction]
            set instruction [string range $thisInstruction 0 [expr $instructionEndPos - 1]]
            if {$verbose} { infoMsg "the instruction itself is just $instruction"; }
            # loop until closing parenthesis is found
            set remainingArgList [string range $thisInstruction [expr $instructionEndPos + 1] [string length $thisInstruction]]
            set thisInstruction $instruction
            while {[string length $remainingArgList] > 0} {
               set next [nextTerminator $remainingArgList]
               set thisArg [string range $remainingArgList 0 [expr $next - 1]]
               if {$verbose} { infoMsg "just extracted the following argument: $thisArg"; }
               lappend argList $thisArg;
               append thisInstruction " " $thisArg
               if {$verbose} { infoMsg "building the string to run: $thisInstruction"; }
               set remainingArgList [string range $remainingArgList [expr $next + 1] [string length $remainingArgList]]
               puts "@@@  completer_helper - Remaining arguments: $remainingArgList";
            }
         } else {
            if {$verbose} { infoMsg "instruction does not have arguments"; }
         }

         # debug: may need to handle instructions with arguments differently...
         if {$verbose} { infoMsg "Attempting to launch the following instruction: $thisInstruction"; }
         if {$hasArguments} {
            set cmd "";
            if {$verbose} { puts "@@@  completer_helper - attempting to eval the command and it's argument(s): $thisInstruction"; };
            for {set argNum 0} {$argNum < [llength $argList]} {incr argNum} {
               set thisVar [lindex $argList $argNum];
               set thisVar [string range $thisVar 1 [string length $thisVar]]; # get rid of the $ in front of the variable name
               puts "@@@  completer_helper - creating variable $thisVar";
               append cmd "variable $thisVar; ";
            }
            append cmd $thisInstruction;
            if {$verbose} { puts "@@@  completer_helper - going to eval command and args: $cmd"; }
            [eval $cmd];
         } else {
            if {$verbose} { puts "@@@  completer_helper - running: $thisInstruction"; }
            $thisInstruction;
         }

         if {$verbose} { infoMsg "done running the instruction, on to the next..."; }
      }
   } else {
      infoMsg "Invalid step number: $stepToDo"
   }

  markLastStep makeStep;
}
#lappend loadedProcs { makeStep "runs the procs specified in the global variable stepList only for the step specified in the argument" };
##
# proc:  make
# descr: uses list of lists to carry step and instruction information. If an instruction requires arguments it is enclosed by comma separated parenthesis
# @remark make build run proc
# @param stopAfterStep
#
proc make {stopAfterStep} {
   variable verbose;
   if { $verbose } { infoMsg "helper.make will stop after step: $stopAfterStep" }
   variable stepList;

   if {![logIsOpen]} {
      if {$verbose} { puts "@@@  completer_helper - *** opening log file! ***"; }
      logOpen "helper.log"; # open the log file if it is not already open for recording any issues...
   }

   if {[strsame $stopAfterStep all]} {
      set stopAtStepN [llength $stepList]
      if {$verbose} {
         infoMsg "All detected. Changed to stop after step number $stopAtStepN. This includes all instructions within each step."
      }
   } else {
      # what is the number of the step to stop after?
      set stopAtStepN [extractIntegerFromString $stopAfterStep]
      if {$verbose} { infoMsg "stopping after $stopAtStepN";}
   }

   # is stopAfterStep within the total number of available taskLists?
   if {[llength $stepList] >= $stopAtStepN} {
      # process all tasks for all the lists below and including this stepList (that is, loop through all selected steps)
      for {set i 0} {$i < $stopAtStepN} {incr i} {

         set humanStep [expr $i + 1];
         if {$verbose} { puts "@@@  completer_helper - calling makeStep for step #$humanStep"; }
         makeStep $humanStep;

         # # extract the instruction list from the stepList
         # set theseInstructions [lindex $stepList $i]
         # if {$verbose} { infoMsg "iterating step [expr $i + 1] which consists of the following instructions: $theseInstructions"; }

         # # loop through the instructions included in this step
         # for {set j 0} {$j < [llength $theseInstructions]} {incr j} {
            # set hasArguments 0;
            # set thisInstruction [lindex $theseInstructions $j];
            # infoMsg "running this instruction: Step [expr $i+1].[expr $j+1]:$thisInstruction";
            # set commandAndArgumentString "";

            # # separate out the arguments if present
            # if {[strContains $thisInstruction (]} {
               # set hasArguments 1;
               # if {$verbose} { puts "@@@  completer_helper - argument(s) found!"; };
               # # extract just the instruction
               # set instructionEndPos [string first ( $thisInstruction];   # locate where the opening parenthesis is
               # set instruction [string range $thisInstruction 0 [expr $instructionEndPos - 1]]; # strip out the instruction
               # append commandAndArgumentString $instruction " {"; # start building the special command and argument string
               # if {$verbose} { infoMsg "the instruction itself is just $instruction and the under-construction cmd and arg is $commandAndArgumentString"; }
               # # loop until closing parenthesis is found
               # set remainingArgList [string range $thisInstruction [expr $instructionEndPos + 1] [string length $thisInstruction]]; # get the argument(s) and closing parenthesis
               # while {[string length $remainingArgList] > 0} {
                  # set next [nextTerminator $remainingArgList];     # nextTerminator return the location of either the next comma, or closing parenthisis
                  # set thisArg [string range $remainingArgList 0 [expr $next - 1]];
                  # infoMsg "checking if $thisArg is literal or a variable";
                  # if {[strStartsWith $thisArg {$}]} {
                     # # treat the value of $thisArg as the new argument
                     # #set thisArg [indirect $thisArg];      # this seems to have been obsoleted.
                     # set thisArg *;                         # we need to tell the receiving function that this was processed as a string instead of passing the actual thing itself
                     # append thisArg [join $thisArg];           # this converts the argument into a simple string. if we do this, then whatever function gets this will need to ensure that it handles it properly

                     # puts "@@@  completer_helper - make - thisArg is $thisArg";;

                     # # # expand the argument (i.e. do an indirect - what does this variable hold?)
                     # # infoMsg "it's a variable that needs to be expanded: "
                     # # warningMsg "thisArg = $thisArg"
                     # # set indirectedValue [subst $thisArg]
                     # # warningMsg "subst thisArg = $indirectedValue]"
                     # # infoMsg ">>> [strStripFirstAndLast [subst $thisArg]]"
                     # # # variable [strStripFirstAndLast [subst $thisArg]]
                     # # variable $indirectedValue;
                     # # set thisArg [strStripFirstAndLast [subst $thisArg]]
                     # # infoMsg "value of $thisArg is $value"
                  # } else {
                     # # no expansion needed
                     # #set thisArg $thisArg
                  # }
                  # if {$verbose} { infoMsg "just extracted the following argument: $thisArg"; }
                  # append commandAndArgumentString " " $thisArg; # append this argument to the string under construction
                  # if {$verbose} { infoMsg "building the string to run: $thisInstruction"; }
                  # set remainingArgList [string range $remainingArgList [expr $next + 1] [string length $remainingArgList]]
                # }
               # append commandAndArgumentString " }";     # close the list
            # }
            # # debug: may need to handle instructions with arguments differently...
            # if {$verbose} { infoMsg "Attempting to launch the following instruction: $thisInstruction"; }
            # if {$verbose} { infoMsg "Attempting to launch the following command and argument: $commandAndArgumentString"; }
            # if {$hasArguments} {
               # eval $commandAndArgumentString
            # } else {
               # $thisInstruction
            # }
            # if {$verbose} {
               # # see if there are more instruction to process...
               # if {$j < [expr [llength $theseInstructions] - 1]} {
                  # infoMsg "done running this instruction, on to the next...";
               # } else {
                  # if {$i < [expr $stopAtStepN - 1]} {
                     # infoMsg "no more instructions in this step, on to the next step...";
                  # } else {
                     # infoMsg "no more steps. moving to complete...";
                  # }
               # }
            # };      # end of debug block to determine what instructions and steps come next.
         # };         # end of instruction processing
         doneWithStep [expr $i + 1]
      }
      boxedMsg "Done with \"Make\"";
    } else {
       infoMsg "Specify the level to which you want the lab re-built to:";
       infoMsg "   n | Sn | Stepn - builds to the end of Step n (<[llength $stepList])"
       infoMsg "   All - builds all steps"
    }
    if {[logIsOpen]} { logClose; };  # close the log file if it is not already open for recording any issues...
}
#lappend loadedProcs { makeStep "runs the procs specified in the global variable stepList only for the step specified in the argument" };
##
# proc:  doneWithStep
# descr: graphic reminder that the section of the script has completed
# @remark <list of searchable terms>
# @param n - step number is argument
#
proc doneWithStep { n } {
   print "**************************";
   print "*  Done Running Step $n *";
   print "**************************";
}
#lappend loadedProcs { doneWithStep "internal call used by make and makeStep to indicate completion of a command list"; }
##
# proc:  pause
# descr: prompts user to press a key to continue. proc returns after key-press
# @remark wait, user, input, pause
#
proc pause {} {
    # puts -nonewline message "Hit Enter to continue ==> "
    puts -nonewline "Hit Enter to continue ==> "
    flush stdout
    gets stdin
}
#lappend loadedProcs { pause "prompts user to press a key to continue. proc returns after key-press" }
##
# proc:  sleep
# descr: sleep for N seconds
# @remark wait, user, sleep
#
proc sleep {N} {
    after [expr {int($N * 1000)}]
}
#lappend loadedProcs { sleep "delay for N seconds" };
#


##
# proc:   getPathToJava
# descr:  identifying the newest version of JRE assuming that java was installed into one of the two default directories
#         optionally uses environment variable: javaPreferred. this allows user to specify specific version and saves search time
# notes:  Linux system do not (yet) perform a directory search. Windows will search only in Program Files and Program Files (x86)
# @remark   java, jre, jdk, path, windows, linux, javaPreferred
# @return single path to the latest version of java OR value of environment variable: javaPreferred, if defined
#
proc getPathToJava {} {
   variable hostOS;

   # check to see if there is a user prefered version of java
   if {[catch {set manualPathToJava $::env(javaPreferred)} result]} {
      puts "@@@  completer_helper - the javaPreferred environment variable is absent, so a search of the system will be done to find the latest version of java.";
   } else {
      puts "@@@  completer_helper - the user prefers: $manualPathToJava";
      regsub -all {\\} $manualPathToJava / manualPathToJava;
      if {[fileExists $manualPathToJava]} {
         puts "@@@  completer_helper - and the file exists, so we're sending it's path back...";
         return $manualPathToJava;
      } else {
         puts "@@@  completer_helper - $manualPathToJava was selected through the environment variable javaPreferred, but it does not exist! Will perform the search for a Java executable.";
      }
   }

   # if we've gotten here, then the manual path was not specified. We now have to figure out where to start
   # looking as this is going to be OS dependent
   # if { $hostOS != "Windows" } { return "/usr/bin/java" }

   set allJavaDirs {};
   set maxVal "";
   set maxLength 0;
   if {$hostOS == "Windows"} {
      set javaDirs86 [glob -directory "c:/Program Files (x86)/Java" -type d -nocomplain *];
      set javaDirs   [glob -directory "c:/Program Files/Java" -type d -nocomplain *];
      append allJavaDirs $javaDirs " " $javaDirs86;
   } else {
      puts "@@@  completer_helper - todo:  completer_helper.getPathToJava - Linux search for java";
      #set vitisJava $::env(VITIS_PATH)/tps/lnx64;
      #set javaVitis [glob -directory $vitisJava -type d -nocomplain *];
      #set javaEtc [glob -directory "/etc" -type d -nocomplain *];
      #append allJavaDirs $javaVitis " " $javaEtc;
   }

   # allJavaDirs will contain a mix of jres and jdks. we need to look at both...
   set cleanListOfJREs {};
   set cleanListOfJDKs {};
   foreach dir $allJavaDirs {
      set dirList [hierarchyToList $dir];
      set dirName [lindex $dirList [expr [llength $dirList] - 1]];

      set type [string range $dirName 0 2];
      if {[strsame $type jre]} { lappend cleanListOfJREs $dir; };
      if {[strsame $type jdk]} { lappend cleanListOfJDKs $dir; };
   }

   # were any Java directories found? If not, we can't continue!
   set nothingFound true;
   if {[llength $cleanListOfJDKs] > 0} { set nothingFound false; };
   if {[llength $cleanListOfJREs] > 0} { set nothingFound false; };

   if { $nothingFound } {
     errorMsg " completer_helper:getPathToJava:No Java installation found! Cannot continue!";
     errorMsg " completer_helper:getPathToJava:Please install Java JRE/JDK in its default location!";
     errorMsg " completer_helper:getPathToJava:go to Java.com and download the free tool";
     return "";
   };

   # if some directories were found, then we can continue...

   # JDKs are numbered as follows jdk-<release>.<major>.<minor>
   if {[llength $cleanListOfJDKs] > 0} {
      set maxJDKpath "";
      set maxJDKversion "";
      foreach dir $cleanListOfJDKs {

         # isolate just the directory name from the full path
         set firstIndex [string first jdk- $dir];
         set version    [string range $dir $firstIndex [string length $dir]];

         # compare this trimmedVersion to the previous one - if it is greater, then update both the maxTrimmed and maxJDKpath
         if {[string compare $version $maxJDKversion] > 0 } {
            set maxJDKversion $version;
            set maxJDKpath    $dir;
         }
      }

      # convert the JDK into a class code
      set firstDashLoc  [string first "-" $maxJDKversion 0];
      set firstDotLoc   [string first "." $maxJDKversion [expr $firstDashLoc + 1]];
      set jdkMajor [string range $maxJDKversion [expr $firstDashLoc + 1] [expr $firstDotLoc - 1]];
      switch $jdkMajor {
         "14" { set jdkCode 58; }
         "13" { set jdkCode 57; }
         "12" { set jdkCode 56; }
         "11" { set jdkCode 55; }
         "10" { set jdkCode 54; }
         "9"  { set jdkCode 53; }
         "8"  { set jdkCode 52; }
         "7"  { set jdkCode 51; }
         "6"  { set jdkCode 50; }
         "5"  { set jdkCode 49; }
         default {
           puts "@@@  completer_helper - Could not find JDK code for version $jdkMajor";
           set jdkCode 59;           # probably not found because it is newer
         }
      }
   } else {
      set jdkCode 0;
   }

   # JREs are numbered as follows jre<release>.<major>.<minor>_<#>
   if {[llength $cleanListOfJREs] > 0} {
      set maxJREpath "";
      set maxJREversion "";
      foreach dir $cleanListOfJREs {
         # isolate just the directory name from the full path
         set firstIndex [string first jre1 $dir];
         set version    [string range $dir $firstIndex [string length $dir]];

         # compare this trimmedVersion to the previous one - if it is greater, then update both the maxTrimmed and maxPath
         if {[string compare $version $maxJREversion] > 0 } {
            set maxJREversion $version;
            set maxJREpath    $dir;
         }
      }

      # convert the JRE into a class code
      set firstDotLoc  [expr [string first "." $maxJREversion 0] + 0];
      set secondDotLoc [string first "." $maxJREversion [expr $firstDotLoc + 1]];
      set jreMajor     [string range $maxJREversion [expr $firstDotLoc + 1] [expr $secondDotLoc - 1]];
      switch $jreMajor {
         "14" { set jreCode 58; }
         "13" { set jreCode 57; }
         "12" { set jreCode 56; }
         "11" { set jreCode 55; }
         "10" { set jreCode 54; }
         "9"  { set jreCode 53; }
         "8"  { set jreCode 52; }
         "7"  { set jreCode 51; }
         "6"  { set jreCode 50; }
         "5"  { set jreCode 49; }
         default {
           puts "@@@  completer_helper - Could not find JRE code for version $jreMajor";
           set jreCode 59;           # probably not found because it is newer
         }
      }
   } else {
      set jreCode 0;
   }

   # compare class codes
   if {$jdkCode > $jreCode} {
      puts "@@@  completer_helper - Using java at $maxJDKpath/bin";
      set useJavaAt "$maxJDKpath/bin/java";
   } else {
      set useJavaAt  "$maxJREpath/bin/java";
   }

   # is it windows or linux?
   if {$hostOS == "Windows"} {
      append useJavaAt ".exe";
   }

   return $useJavaAt;
}
#lappend loadedProcs {getPathToJava "uses environment variable javaPreferred as the path - if absent, it attempts to find the newest version of Java." }


##
# proc:  runDedScript
# descr: proc for launching the directed editor
# @remark  directed, editor, ded, edit
# @param path_to_source
# @param path_to_script
# todo: return a value indicating if the edit was successful or not
#
proc runDedScript {path_to_source path_to_script} {
   variable java
   variable tools
   set java [getPathToJava]
   set arguments ""
   append arguments $path_to_source "," $path_to_script
   regsub -all {' '} $arguments ',' arguments
   infoMsg $arguments
   exec $java -jar $tools/directedEditor.jar $path_to_source $path_to_script
}
##
# proc:  runDedScriptExtra
# descr:
# @remark <list of searchable terms>
# @param path_to_source
# @param path_to_script
# @param path_to_destination
#
proc runDedScriptExtra {path_to_source path_to_script path_to_destination} {
   variable java
   variable tools
   set java [getPathToJava]
   set arguments ""
   append arguments $tools/directedEditor.jar "," $path_to_source "," $path_to_script
   regsub -all {' '} $arguments ',' arguments
   infoMsg $arguments
   exec $java -jar $tools/directedEditor.jar $path_to_source $path_to_destination
}
##
# proc:  runDedScriptExtra
# descr:
# @remark <list of searchable terms>
# @param commaSeparatedArgList
#
proc runDedScript {commaSeparatedArgList} {
   variable java
   variable tools
   set java [getPathToJava]
   #set arguments ""
   #append arguments $tools/directedEditor.jar $commaSeparatedArgList
   infoMsg $commaSeparatedArgList
   exec $java -jar $tools/directedEditor.jar $commaSeparatedArgList
}
# assumes toolName contains full path
# warning: this can be pretty picky with quotes in the argument list
##
# proc:  runJava
# descr:
# @remark <list of searchable terms>
# @param toolName
# @param arguments
#
proc runJava {toolName arguments} {
   variable verbose
   set verbose 1
   set java [getPathToJava]
   # iterate through the arguments list
   if {$verbose} {
      puts "@@@  completer_helper - listing passed arguments...$arguments"
      puts "@@@  completer_helper - now individually: "
   }
   set argumentString ""
   set argCount 0
   foreach argument $arguments {
      if {$verbose} { puts "@@@  completer_helper - $argCount: $argument" }
      append argumentString $argument
      incr argCount
      if {$argCount < [llength $arguments]} {
         append argumentString ","
      }
   }
   if {$verbose} {
      puts "@@@  completer_helper - argument string is $argumentString"
      puts "@@@  completer_helper - java location: $java"
      puts "@@@  completer_helper - tool name with path: $toolName"
   }
   puts "@@@  completer_helper - getting ready to run the tool"
   # catch any errors to avoid breaking the calling routine
   if {[catch {exec $java -jar $toolName $argumentString} resultText]} {
      puts "@@@  completer_helper - failed execution: $::errorInfo"
   } else {
      puts "@@@  completer_helper - successful execution - application returned $resultText"
   }
}
#########################################################################
# proc for launching the choicesGUI
#########################################################################
##
# proc:  runChoicesGUI
# descr:
# @remark <list of searchable terms>
# @param path_to_source
# @param argList
#
proc runChoicesGUI {path_to_source argList} {
   variable java
   set java [getPathToJava]
   exec $java -jar $path_to_source $argList
}
#########################################################################
# proc for fixing slashes from Windows to Linux
#########################################################################
##
# proc:  fixSlashes
# descr:
# @remark <list of searchable terms>
# @param path
#
proc fixSlashes {path} {

   # replace below with the following and verify
   regsub -all {\\} $path / path

   # set len [string length $path]
   # for {set i 0} {$i < $len} {incr i} {
      # set c [string index $path $i]
      # if {$c == "\\"} {
         # set path [string replace $path $i $i "/"]
      # }
   # }
   return $path
}

##
# proc:  unfixSlashes
# descr: fixes slashes from Linux to Windows
# @remark <list of searchable terms>
# @param path
#
proc unfixSlashes {path} {

   # replace below with the following and verify
   regsub -all / $path {\\} path

   return $path
}

##
# proc:  invertLogic
# descr: inverts the passed logic value
# @remark invert binary
# @param x - string representing the logical value
# @return "yes"  if "no" is passed and visa-versa
#           "1"    if "0"  is passed and visa-versa
#           "true" if "false" is passed and visa-versa
#
proc invertLogic {x} {
   if {[strsame $x "yes"]} {
      return "no"
   } elseif {[strsame $x "no"]} {
      return "yes"
   } elseif {$x != 0} {
      return 1
   } elseif {$x == 0} {
      return 1
   } elseif {[strsame $x "true"]} {
      return "false"
   } elseif {[strsame $x "false"]} {
      return "true"
   } else {
      return "?"
   }
}
##
# proc:  logicValue
# descr: returns 1 or 0 based on x
# @remark binary equivalent true false yes no
# @param x - string to test (case insensitive)
# @return a 1 when x is yes, true, or 1; 0 otherwise
#
proc logicValue {x} {
   if {[strsame $x "yes"]} {
      return 1
   } elseif {[strsame $x "true"]} {
      return 1
   } elseif {[strsame $x "1"]} {
      return 1
   }
   return 0
}
##
# proc:  markLastStep
# descr: marks (remembers) the step that was just completed (internal only)
# @remark <list of searchable terms>
# @param lastStepName
#
proc markLastStep { lastStepName } {
   variable lastStep
   set lastStep $lastStepName
}
##
# proc:  getLastStep
# descr: returns the last successfully executed step (internal use only)
# @remark <list of searchable terms>
# @return last set step value
#
proc getLastStep {} { variable lastStep; return $lastStep }
##
# proc:  getLanguage
# descr: returns the selected language (set by "use")
# @remark discover language retrieve
# @return string represting the selected language as set by "use" proc
#
proc getLanguage {} { variable language; return $language }

##
# proc:  boxedMsg
# descr: dumps message to the log file and terminal surrounded by asterisks
# history:
#    centered - 11/04/2016 WK
#    todo: - add wrap
# @remark pretty box message display
# @param x - message to display
#
proc boxedMsg { x } {
   set minWidth 50

   # how wide is the message?
   # future - adjust for cr/lfs in the msg (wrap)
   set xWidth [string length $x]
   # 5 for the leading 2 *s, 2 for the trailing *s, 1 for the each space btwn * and msg
   set totalWidth [expr $xWidth + 2 + 2 + 2]

   # ensure that there is a minimum width
   if {$totalWidth < $minWidth} { set totalWidth $minWidth }

   # build the top 2 lines (blank line and all asterisks)
   print ""
   set allAsterisks [repeat * $totalWidth]
   print "\t$allAsterisks"

   # 3rd line is asterisks at front and back of line
   set blankedLine ""
   append blankedLine "**" [repeatChar " " [expr $totalWidth - 4]] "**"
   print "\t$blankedLine"

   # 4th line contains the message
   # if smaller than minWidth, then center in the fields
   # first half is totalWidth/2 - "** " - half of the msg width
   set firstHalfBuffer  [expr $totalWidth / 2 - 3 - $xWidth / 2]
   # second half is what ever is left over to account for rounding: including "**" and "**" and whole word
   set secondHalfBuffer [expr $totalWidth - $firstHalfBuffer - $xWidth - 6]
   set msgLine ""
   append msgLine "\t** " [repeatChar " " $firstHalfBuffer] $x [repeatChar " " $secondHalfBuffer] " **"
   print "$msgLine"

   # finish up with what we started with
   print "\t$blankedLine"
   print "\t$allAsterisks"
   print ""
}
#lappend loadedProcs {xxx "9"};


##
# proc:  latestVersion
# descr: identifies the newest version of the named IP
# @remark version, IP, newest
# @param IPname
# @return the newest version of that IP
#
proc latestVersion { IPname } {
   # find the package type
   # set packageTypes {iclg }  -- not used
   set lastPos [strLastIndex $IPname :]; # strip off everything beyond the third colon (as this contains the version info)
   set IPnameNoVer [string range $IPname 0 $lastPos]

   set listOfAllIP [lsort [get_ipdefs]]
   foreach pieceOfIP $listOfAllIP {
      set lastPos [strLastIndex $pieceOfIP :]; # strip off everything beyond the third colon (as this contains the version info)
      set pieceOfIPnoVer [string range $pieceOfIP 0 $lastPos];
	  # puts "\comparing $IPnameNoVer against $pieceOfIPnoVer ==> $matches"	  
      if {[string compare $pieceOfIPnoVer $IPnameNoVer] == 0} {
        return $pieceOfIP
      }
   }
   puts stderr "Could not find an entry in the ipdefs for $IPname";
   return $IPname;  # if we can't find a match, just return the string that was passed in
}
##
# proc:  latestBoardVersion
# descr: identifies the newest version of the named board
# @remark version, board, newest
# @param boardName
# @return the newest version of the specified board
#
proc latestBoardVersion { boardName } {
   variable verbose;
   # sometimes there is a "part x:" string in there which really doesn't belong. If the word "part" is in there, get rid of it.
   set partLoc [expr [string first part $boardName] - 1];   # get to the beginning of "part"
   if {$partLoc != -1} {
      set colonLoc [expr $partLoc + 7];    # "part" + $ + ":" = 6
      set tempBoardName [string range $boardName 0 $partLoc];
      append tempBoardName [string range $boardName $colonLoc [string length $boardName]];
      set boardName $tempBoardName;
   }

   # remove the version information
   set lastPos [expr [strLastIndex $boardName :] - 1]; # strip off everything beyond the third colon (as this contains the version info)
   set boardNameWoVer [string range $boardName 0 $lastPos];

   set listOfAllBoards [get_boards];
   foreach board $listOfAllBoards {
      set lastPos [expr [strLastIndex $board :] - 1]; # strip off everything beyond the third colon (as this contains the version info)
      set boardWoVer [string range $board 0 $lastPos];
      #puts "@@@  completer_helper - comparing: $boardWoVer to $boardNameWoVer";
      if {[string compare $boardWoVer $boardNameWoVer] == 0} {
         return $board;
      }
   }
   if {$verbose} { puts "@@@  completer_helper - latestBoardVersion: could not update the board version $boardName - may not be supported"; }
}

##
# proc:  supportedPart
# descr: checks if the specified part is supported. If not, and it is an es part, then check if a non-es part is supported
# @remark version, part, device, newest, es, non-es
# @param partName
# @return the newest version of the specified part
#
proc supportedPart { partName } {
   # is this part in the list
   set listOfAllParts [get_parts];
   if {[lsearch $listOfAllParts $partName] != -1} {
      return $partName;
   } else {
      # was it not found because it was an es part?
      set esLoc [string last -e-S $partName];
      if { $esLoc != -1} {
         # this is NOT an es part - something else is wrong
         puts "@@@  completer_helper - $partName *NOT* found in the list of parts for this tool installation. The could be due to either a bad part number or the parts library containing this part may not have been loaded.";
         return "part not found: $partName";
      } else {
         # it is an ES part, strip the ES and look up the part
         set newPartName [string range $partName 0 $esLoc];
         if {[lsearch $listOfAllParts $newPartName] != -1} {
            # yes, the no-ES version of the part was found, return it
            return $newPartName;
         } else {
            # no, something else happened
            puts "@@@  completer_helper - $partName *NOT* found in the list of parts for this tool installation. The could be due to either a bad part number or the parts library containing this part may not have been loaded.";
            return "part not found: $partName";
         }
      }
   }
}

##
# proc:  closestPart
# descr:
# proc for identifying the closest part number
# pass in a partial part name and this function will return the first match of this part
# matches to core part minus speed grade, temp grade, es, etc.
# this is useful when a specific part is not required, rather only a member of a family
# and size
# todo: add wildcards
# @remark <list of searchable terms>
# @param partNumber
#
proc closestPart { partNumber } {
   # before we go through the lengthly process of searching, is this part already in the list?
   set partList [get_parts]
   set exactResult [lsearch $partList $partNumber]
   if {$exactResult > -1} {
      return $partNumber;
   }

   # list of known packages
   set packages {clg iclg sclg ifbg isbg sbg fbg fbv ifbv iffg iffv fbg ffg ffv cl rf rb ffvb ffvc ffvd sfva sfvc}

   # strip off the package id
   foreach thisPkg $packages {
      # set pkgPos [strLastIndex $partNumber $packages]; # look for this package in the part
      # if pkgPos > -1 means that it's found
      set pkgPos [strLastIndex $partNumber $thisPkg]
      if {$pkgPos > -1} {
       set partialPart [substr $partNumber 0 $pkgPos]
         append partialPart "*"
         # now find the first partial match...
         set fullPartPosition [lsearch $partList $partialPart]
         set fullPart [lindex $partList $fullPartPosition]
         return $fullPart
      }
   }
   return "???"
}
#lappend loadedProcs {xxx "10"};


##
# proc:  callingProcName
# descr:
# @remark <list of searchable terms>
# @return the name of the proc this proc is called from
#
proc callingProcName {} {
   set trace [strace]
   set thisLevel [info level]
   # since trace is still sitting on the stack we have to go up two instead of one
   set upTwoLevels [expr $thisLevel - 2]
   set thisLevelName [lindex $trace $upTwoLevels]
   return $thisLevelName
}

##
# proc:  dumpStack
# descr:
# @remark <list of searchable terms>
#
proc dumpStack {} {
    set trace [strace]
    puts "@@@  completer_helper - Trace:"
    foreach t $trace {
        puts "@@@  completer_helper - * ${t}"
    }
}

##
# proc:  strace
# descr:
# @remark <list of searchable terms>
#
proc strace {} {
    set ret {}
    set r [catch {expr [info level] - 1} l]
    if {$r} { return {""} }
    while {$l > -1} {
        incr l -1
        lappend ret [info level $l]
    }
    return $ret
}

##
# proc:  getScriptLocation
# descr:
# @remark <list of searchable terms>
#
proc getScriptLocation {} {
   variable myLocation
   return [file dirname $myLocation]
}



##
# proc:  msSleep
# descr: requires Tcl 8.4
# @remark sleep delay wait milli second
# @param ms
#
proc msSleep { ms } {
     after $ms;
 }

##
# proc:  randName
# descr: creates a random string of length
# @remark <list of searchable terms>
# @param len
# @return returns a string of random letters and number of the specified len
#
proc randName {len} {
   set retStr ""
   for {set i 0} {$i<$len} {incr i} {
      set value [expr int(rand()*127)]
      set char [format %c $value]
      if {(($value >= 48) && ($value <=  57) && ($i > 0)) ||
          (($value >= 65) && ($value <=  90)) ||
           (($value >= 97) && ($value <= 122)) } {
          # this is a legal symbol and should be appended to the return string
         append retStr $char
      } else {
        # this is an illegal symbol and should be skipped
        incr i -1;
      }
   }
   return $retStr;
 }


##
# proc:  find7z
# descr:
# @remark zip compress 7zip 7z locate find
# @return the path to 7z including the executable name or dumps an error on failure
#
proc find7z {} {
   # point to the 7zip tool which may be called if there is unzipping to be done

   # because it takes so @#$% long to search (which also kicks up a lot of stuff on the screen) we can short-circuit
   # this search by starting in the directory pointed to by 7zPath. If it's not found, then we can fall back to the old search
   if {[info exists ::env(7zPath)]} {
      set 7zPath $::env(7zPath);                # since the environment variable exists, assume that it's ok and assign it to a local variable
      regsub -all {\\} $7zPath / 7zPath;        # make sure the hierarchy separators work for Tcl
      set zipToolExe $7zPath/7z.exe;            # generate the path to the tool
      if {[file exist $zipToolExe]} {
         return $zipToolExe;
      } else {
         errorMsg "find7z - could not locate 7z tool at the given environment variable path $7zPath";
         return "";
      }
   }

   # no environment variable, so we need to do it the hard way...
   set zipTool [findFiles {c:/Program Files (x86)} 7z.exe 1];      # assumes default location
   if {[llength $zipTool] == 0} {
      set zipTool [findFiles {c:/Program Files} 7z.exe 1];         # assumes the other default location
   }

   # was anything found?
   if {[llength $zipTool] == 1} {
      set zipToolExe [lindex $zipTool 0];
      return $zipToolExe;
   } else {
      errorMsg "find7z - could not locate 7 zip utility in the default installation site"
   }

   return "";
}

##
# proc:  zipIt
# descr: uses find7z to locate the zipTool/zip tool then attempts to zip srcDirName
#   into a zip file given by destFileName.
# @remark <list of searchable terms>
# @param srcDirName
# @param destFileName
#
proc zipIt {srcDirName destFileName} {
   # point to the 7zip tool which may be called if there is unzipping to be done
   set zipTool [find7z]

   # confirm that the source is valid
   if {[isDirectory $srcDirName]} {
      exec $zipTool a -tzip $destFileName $srcDirName;           # zip it!
   } else {
      warningMsg "zipIt - source directory not found: $srcDirName"
   }
}
# same a zipIt, but with a flat directory structure
proc zipItFlat {srcDirName destFileName} {
   # point to the 7zip tool which may be called if there is unzipping to be done
   set zipTool [find7z]

   # confirm that the source is valid
   if {[isDirectory $srcDirName]} {
      exec $zipTool a -tzip $destFileName $srcDirName/*;           # zip it!
   } else {
      warningMsg "zipIt - source directory not found: $srcDirName"
   }
}
#lappend loadedProcs {xxx "12"};

##
# proc:  numberOfCPUs
# descr: stolen from: https://stackoverflow.com/questions/29482303/how-to-find-the-number-of-cpus-in-tcl
# @remark <list of searchable terms>
# @return the number of processors available in this environment regardless of os
#
proc numberOfCPUs {} {
    # Windows puts it in an environment variable
    global tcl_platform env
    if {$tcl_platform(platform) eq "windows"} {
        return $env(NUMBER_OF_PROCESSORS)
    }

    # Check for sysctl (OSX, BSD)
    set sysctl [auto_execok "sysctl"]
    if {[llength $sysctl]} {
        if {![catch {exec {*}$sysctl -n "hw.ncpu"} cores]} {
            return $cores
        }
    }

    # Assume Linux, which has /proc/cpuinfo, but be careful
    if {![catch {open "/proc/cpuinfo"} f]} {
        set cores [regexp -all -line {^processor\s} [read $f]]
        close $f
        if {$cores > 0} {
            return $cores
        }
    }

    # No idea what the actual number of cores is; exhausted all our options
    # Fall back to returning 1; there must be at least that because we're running on it!
    return 1
}
lappend loadedProcs {numberOfCPUs "Returns the number of available CPUs in this environment"};
##
# proc:  militaryMonthName
# descr:
# @remark calendar month name abbreviated
# @param monthNumber (1-12)
# @return military style month name (capitalized three letter month name)
#
proc militaryMonthName {monthNumber} {
   switch $monthNumber {
      1  { return JAN }
      2  { return FEB }
      3  { return MAR }
      4  { return APR }
      5  { return MAY }
      6  { return JUN }
      7  { return JUL }
      8  { return AUG }
      9  { return SEP }
      10 { return OCT }
      11 { return NOV }
      12 { return DEC }
      default { return ???; }
   }
}
lappend loadedProcs {militaryMonthName "Returns the miliarty 3 letter month name"};
##
# proc:  getScriptName
# descr: returns the name of the script that is currently running
# @remark  script name tcl
# @return name of currently running script
#
proc getScriptName {} {
   set fullScriptPath [info script];
   set onlyScriptName [getLastHierarchy $fullScriptPath];
   return $onlyScriptName
}
lappend loadedProcs {getScriptName "Returns the name of the script that is currently running"};


# return
##
# proc:  matchStop
# descr: finds the position of the character where two strings stop matching. Requires that comparison strings be the same length
# todo: remove same length requirement
# todo: make verbosity either debug or remove
# @remark  script name tcl
# @param a - first string
# @param b - second string
# @return position of first mismatch between the two strings, -1 if strings match
#
proc matchStop {a b} {
   # strings have to be the same length
   if {[string length $a] == [string length $b]} {
      for {set i 0} {$i < [string length $a]} {incr i} {
         set aChar [string index $a $i];
         set bChar [string index $b $i];
         puts "@@@  completer_helper - comparing $aChar to $bChar";
         if {$aChar != $bChar} {
            puts "@@@  completer_helper - The first difference between the two input strings occurs in position $i where the character from the first argument is $aChar vs. $bChar from the second argument.";
            return $i;
         } else {
            puts "@@@  completer_helper - this character matches!";
         }
      }
      return -1;  # indicates that no difference were found
   } else {
      puts "@@@  completer_helper - This proc only works when the two input strings are the same length.";
   }
}

###################################################### Executable Code #######################################################

# Note: does not display if run in quiet mode
variable helper_loaded 1;


if {![info exists loadedProcs]} {
   puts "@@@  completer_helper - loadedProcs variable not defined - will define it in completer_helper";
   variable loadedProcs {};
}

puts "@@@ completer_helper - checking if the TRAINING_PATH environment variable is set";
if {![info exists trainingPath]} {
   puts "@@@  completer_helper - trainingPath variable not defined - looking for env variable to set it with";
   if {![info exists ::env(TRAINING_PATH)]} {
      puts "!!! TRAINING_PATH environment variable doen't exist! Can't continue!";
	  set badEnv 1;;
   } else {
      puts "@@@      TRAINING_PATH found and is set to $::env(TRAINING_PATH)"
      set trainingPath $::env(TRAINING_PATH);
	  regsub -all {\\} $trainingPath / trainingPath
	  # the training directory really should have been created by the time we get here - is this code needed?
	  if {! [file isdirectory $trainingPath]} {
	     file mkdir $trainingPath;
	  } 
   }
} else {
   puts "@@@  completer_helper - TRAINING_PATH environment variable defined. Continuing with the loading of completer_helper.";
}

 

# if the debug variable is defined, it will usually have been defined in  completer_helper
if {![info exists debug]} {
   puts "@@@  completer_helper - debug variable not defined - will define it in completer_helper";
   variable debug 0;
}

set custEdIP $trainingPath/CustEdIP;
set tools    $trainingPath/tools;

if {$badEnv} {
    puts -nonewline "Hit Enter to exit ==> "
    flush stdout
    gets stdin
    exit 1
}

#
# data set to turn everything in the Zynq device off (except clock and reset which may be needed by the microBlaze)
variable ZynqAllOff {CONFIG.PCW_USE_M_AXI_GP0                     0
                     CONFIG.PCW_USE_M_AXI_GP1                     0
                     CONFIG.PCW_USE_S_AXI_GP0                     0
                     CONFIG.PCW_USE_S_AXI_GP1                     0
                     CONFIG.PCW_USE_S_AXI_ACP                     0
                     CONFIG.PCW_USE_S_AXI_HP0                     0
                     CONFIG.PCW_USE_S_AXI_HP1                     0
                     CONFIG.PCW_USE_S_AXI_HP2                     0
                     CONFIG.PCW_USE_S_AXI_HP3                     0
                     CONFIG.PCW_EN_CLK0_PORT                      0
                     CONFIG.PCW_EN_CLK1_PORT                      0
                     CONFIG.PCW_EN_CLK2_PORT                      0
                     CONFIG.PCW_EN_CLK3_PORT                      0
                     CONFIG.PCW_EN_RST0_PORT                      0
                     CONFIG.PCW_EN_RST1_PORT                      0
                     CONFIG.PCW_EN_RST2_PORT                      0
                     CONFIG.PCW_EN_RST3_PORT                      0
                     CONFIG.PCW_QSPI_PERIPHERAL_ENABLE            0
                     CONFIG.PCW_NAND_PERIPHERAL_ENABLE            0
                     CONFIG.PCW_NOR_PERIPHERAL_ENABLE             0
                     CONFIG.PCW_ENET0_PERIPHERAL_ENABLE           0
                     CONFIG.PCW_ENET1_PERIPHERAL_ENABLE           0
                     CONFIG.PCW_SD0_PERIPHERAL_ENABLE             0
                     CONFIG.PCW_SD1_PERIPHERAL_ENABLE             0
                     CONFIG.PCW_USB0_PERIPHERAL_ENABLE            0
                     CONFIG.PCW_USB1_PERIPHERAL_ENABLE            0
                     CONFIG.PCW_UART0_PERIPHERAL_ENABLE           0
                     CONFIG.PCW_UART1_PERIPHERAL_ENABLE           0
                     CONFIG.PCW_SPI0_PERIPHERAL_ENABLE            0
                     CONFIG.PCW_SPI1_PERIPHERAL_ENABLE            0
                     CONFIG.PCW_CAN0_PERIPHERAL_ENABLE            0
                     CONFIG.PCW_CAN1_PERIPHERAL_ENABLE            0
                     CONFIG.PCW_WDT_PERIPHERAL_ENABLE             0
                     CONFIG.PCW_TTC0_PERIPHERAL_ENABLE            0
                     CONFIG.PCW_TTC1_PERIPHERAL_ENABLE            0
                     CONFIG.PCW_USB0_PERIPHERAL_ENABLE            0
                     CONFIG.PCW_USB1_PERIPHERAL_ENABLE            0
                     CONFIG.PCW_I2C0_PERIPHERAL_ENABLE            0
                     CONFIG.PCW_I2C1_PERIPHERAL_ENABLE            0
                     CONFIG.PCW_GPIO_MIO_GPIO_ENABLE              0
                     CONFIG.PCW_GPIO_EMIO_GPIO_ENABLE             0
                    }
#
# data set for the MPSoC ZUS+ device - turns off all of the options
variable MPSoCallOff { CONFIG.PSU__DISPLAYPORT__PERIPHERAL__ENABLE  0
                       CONFIG.PSU__ENET1__PERIPHERAL__ENABLE        0
                       CONFIG.PSU__ENET2__PERIPHERAL__ENABLE        0
                       CONFIG.PSU__ENET3__PERIPHERAL__ENABLE        0
                       CONFIG.PSU__GPIO_EMIO__PERIPHERAL__ENABLE    0
                       CONFIG.PSU__GPIO0_MIO__PERIPHERAL__ENABLE    0
                       CONFIG.PSU__GPIO1_MIO__PERIPHERAL__ENABLE    0
                       CONFIG.PSU__GPIO2_MIO__PERIPHERAL__ENABLE    0
                       CONFIG.PSU__CAN0__PERIPHERAL__ENABLE         0
                       CONFIG.PSU__CAN1__PERIPHERAL__ENABLE         0
                       CONFIG.PSU__I2C0__PERIPHERAL__ENABLE         0
                       CONFIG.PSU__I2C1__PERIPHERAL__ENABLE         0
                       CONFIG.PSU__PCIE__PERIPHERAL__ENABLE         0
                       CONFIG.PSU__PJTAG__PERIPHERAL__ENABLE        0
                       CONFIG.PSU__PMU__PERIPHERAL__ENABLE          0
                       CONFIG.PSU__QSPI__PERIPHERAL__ENABLE         0
                       CONFIG.PSU__SATA__PERIPHERAL__ENABLE         0
                       CONFIG.PSU__SD0__PERIPHERAL__ENABLE          0
                       CONFIG.PSU__SD1__PERIPHERAL__ENABLE          0
                       CONFIG.PSU__SPI0__PERIPHERAL__ENABLE         0
                       CONFIG.PSU__SPI1__PERIPHERAL__ENABLE         0
                       CONFIG.PSU__SWDT0__PERIPHERAL__ENABLE        0
                       CONFIG.PSU__SWDT1__PERIPHERAL__ENABLE        0
                       CONFIG.PSU__TTC0__PERIPHERAL__ENABLE         0
                       CONFIG.PSU__TTC1__PERIPHERAL__ENABLE         0
                       CONFIG.PSU__TTC2__PERIPHERAL__ENABLE         0
                       CONFIG.PSU__TTC3__PERIPHERAL__ENABLE         0
                       CONFIG.PSU__UART0__PERIPHERAL__ENABLE        0
                       CONFIG.PSU__UART1__PERIPHERAL__ENABLE        0
                       CONFIG.PSU__USB0__PERIPHERAL__ENABLE         0
                       CONFIG.PSU__USB1__PERIPHERAL__ENABLE         0
                       CONFIG.PSU__FPGA_PL0_ENABLE                  0
                       CONFIG.PSU__USE__IRQ0                        0
                       CONFIG.PSU__USE__M_AXI_GP0                   0
                       CONFIG.PSU__USE__M_AXI_GP1                   0
                       CONFIG.PSU__USE__M_AXI_GP2                   0
                       CONFIG.PSU__USE__S_AXI_GP0                   0
                       CONFIG.PSU__USE__S_AXI_GP1                   0
                       CONFIG.PSU__USE__S_AXI_GP2                   0
                       CONFIG.PSU__USE__S_AXI_GP3                   0
                       CONFIG.PSU__USE__S_AXI_GP4                   0
                       CONFIG.PSU__USE__S_AXI_GP5                   0
                       CONFIG.PSU__USE__S_AXI_GP6                   0
                       CONFIG.PSU__USE__FABRIC__RST                 0
               }

#
# data set for the Versal ACAP device - turns off all of the options
variable ACAPallOff { PS_PCIE_RESET_ENABLE                  0
                      PS_USE_M_AXI_GP0                      0
                      PS_USE_S_AXI_GP0                      0
                      PS_USE_M_AXI_GP2                      0
                      PS_USE_S_AXI_GP2                      0
                      PS_USE_S_AXI_GP4                      0
                      PS_USE_S_AXI_ACP                      0
                      PS_USE_S_AXI_ACE                      0
                      PS_USE_BSCAN_USER1                    0 
                      PMC_SMAP_PERIPHERAL_ENABLE            0
                      PMC_SMAP_PERIPHERAL_IO                0
                      PS_NUM_FABRIC_RESETS                  0
                      PMC_QSPI_PERIPHERAL_ENABLE            0
                      PMC_OSPI_PERIPHERAL_ENABLE            0
                      PMC_I2CPMC_PERIPHERAL_ENABLE          0
                      PMC_SD0_PERIPHERAL_ENABLE             0
                      PMC_SD1_PERIPHERAL_ENABLE             0
                      PMC_GPIO_EMIO_PERIPHERAL_ENABLE       0
                      PS_CAN0_PERIPHERAL_ENABLE             0
                      PS_CAN1_PERIPHERAL_ENABLE             0
                      PS_ENET0_PERIPHERAL_ENABLE            0
                      PS_USE_ENET0_PTP                      0
                      PS_ENET1_PERIPHERAL_ENABLE            0
                      PS_USE_ENET1_PTP                      0
                      PS_USE_FIFO_ENET0                     0
                      PS_USE_FIFO_ENET1                     0
                      PS_GEM_TSU_ENABLE                     0
                      PS_SPI0_PERIPHERAL_ENABLE             0
                      PS_SPI1_PERIPHERAL_ENABLE             0
                      PS_TTC0_PERIPHERAL_ENABLE             1
                      PS_TTC1_PERIPHERAL_ENABLE             1
                      PS_TTC2_PERIPHERAL_ENABLE             0
                      PS_TTC3_PERIPHERAL_ENABLE             0
                      PS_UART0_PERIPHERAL_ENABLE            0
                      PS_UART1_PERIPHERAL_ENABLE            0
                      PS_USB3_PERIPHERAL_ENABLE             0
                      PS_WWDT0_PERIPHERAL_ENABLE            0
                      PS_WWDT1_PERIPHERAL_ENABLE            0
                      PS_TRACE_PERIPHERAL_ENABLE            0
                      PS_I2C0_PERIPHERAL_ENABLE             0
                      PS_I2C1_PERIPHERAL_ENABLE             0
                      PS_ADMA_CH0_ENABLE                    0
                      PS_ADMA_CH1_ENABLE                    0
                      PS_ADMA_CH2_ENABLE                    0
                      PS_ADMA_CH3_ENABLE                    0
                      PS_ADMA_CH4_ENABLE                    0
                      PS_ADMA_CH5_ENABLE                    0
                      PS_ADMA_CH6_ENABLE                    0
                      PS_ADMA_CH7_ENABLE                    0
                      PS_USE_PS_NOC_CCI                     0
                      PS_USE_PS_NOC_NCI_0                   0
                      PS_USE_PS_NOC_NCI_1                   0
                      PS_USE_NOC_PS_NCI_0                   0
                      PS_USE_NOC_PS_NCI_1                   0
                      PS_USE_NOC_PS_CCI_0                   0
                      PS_USE_NOC_PS_CCI_1                   0
                      PS_USE_PS_NOC_RPU_0                   0
                      PMC_USE_NOC_PMC_AXI0                  0
                      PMC_USE_PMC_NOC_AXI0                  0
                      PS_USE_PS_NOC_CCI                     0
                      PS_USE_PS_NOC_NCI_1                   0
                      PS_USE_NOC_PS_NCI_0                   0
                      PS_USE_NOC_PS_NCI_1                   0
                      PS_USE_NOC_PS_CCI_0                   0
                      PS_USE_NOC_PS_CCI_1                   0
                      PS_USE_PS_NOC_RPU_0                   0
                      PS_USE_PROC_EVENT_BUS                 0
                      PS_USE_APU_INTERRUPT                  0
                      PS_USE_RPU_EVENT                      0
                      PS_USE_RPU_INTERRUPT                  0
                      PS_USE_IRQ_0                          1
                      PS_USE_IRQ_1                          1
                      PS_USE_IRQ_2                          0
                      PS_USE_IRQ_3                          0
                      PS_USE_IRQ_4                          0
                      PS_USE_IRQ_5                          0
                      PS_USE_IRQ_6                          0
                      PS_USE_IRQ_7                          0
                      PS_USE_IRQ_8                          0
                      PS_USE_IRQ_9                          0
                      PS_USE_IRQ_10                         0
                      PS_USE_IRQ_11                         0
                      PS_USE_IRQ_12                         0
                      PS_USE_IRQ_13                         0
                      PS_USE_IRQ_14                         0
                      PS_USE_IRQ_15                         0
                      PS_USE_PSPL_IRQ_LPD                   0
                      PS_USE_PSPL_IRQ_FPD                   0
                      PS_USE_PSPL_IRQ_PMC                   0
                      PS_TRACE_PERIPHERAL_ENABLE            0
                      PS_USE_PS_NOC_PCI_0                   0
                      PS_USE_PS_NOC_PCI_1                   0
                      PS_USE_NOC_PS_PCI_0                   0
                      PS_USE_STM                            0
                      PS_FTM_CTI_IN_0                       0
                      PS_FTM_CTI_IN_1                       0
                      PS_FTM_CTI_IN_2                       0
                      PS_FTM_CTI_IN_3                       0
                      PS_FTM_CTI_OUT_0                      0
                      PS_FTM_CTI_OUT_1                      0
                      PS_FTM_CTI_OUT_2                      0
                      PS_FTM_CTI_OUT_3                      0
                      PS_USE_TRACE_ATB                      0
                      CPM_PCIE0_MODES                       None
                      CPM_PCIE1_MODES                       None
                      PS_USE_PMCPL_CLK0                     0
                      PS_USE_PMCPL_CLK1                     0
                      PS_USE_PMCPL_CLK2                     0
                      PS_USE_PMCPL_CLK3                     0
                      PS_USE_PMCPL_IRO_CLK                  0
                      PMC_HSM0_CLOCK_ENABLE                 0
                      PMC_HSM1_CLOCK_ENABLE                 0
                      PS_USE_CAPTURE                        0
                     }
lappend loadedProcs ZynqAllOff MPSoCallOff ACAPallOff

#
# ********** Create the New Project
#

##
# proc:  projectCreate
# descr:
# @remark <list of searchable terms>
#
proc projectCreate {} {
   # get the globally defined variables
   variable tcName;
   variable labName;
   variable language;
   variable verbose;
   variable platform;
   variable processor;
   variable demoOrLab;
   variable trainingPath;
   variable projName;
   variable debug;

   if {$verbose} { puts "@@@  completer_helper - completer_helper.projectCreate"; }

   # close the project if one is open
   if { [catch { set nProjects [llength [get_projects -quiet -verbose *]]} fid] } {
      puts stderr "error caught!"
      puts $fid
   } else {
      if {$nProjects > 0} {
        if {$verbose} { puts "@@@  completer_helper - project is open and will try to close it" }
           close_project
      } else {
       #if {$verbose} { puts "@@@  completer_helper - no projects to close. Continuing with creation of new project" }
     }
   }

   # check if both a language and platform has been selected
   if {$debug} { puts "@@@  completer_helper - checking if the language and platform has been selected"; }
   set isLangNotSelected [strsame $language "undefined"];
   set isPlatNotSelected [strsame $platform "undefined"];

   set isverilog    [string compare -nocase $language "verilog"];
   set isvhdl       [string compare -nocase $language "vhdl"];

   if {$debug} { puts "@@@  completer_helper - checking for platform/processor selection"; }
   set isVCK190          [strsame $platform VCK190];
   set isZed             [strsame $platform ZED];
   set isZC702           [strsame $platform ZC702];
   set isUBlaze          [strsame $processor MicroBlaze]; #   -- processor not carried into this proc
   set isZCU102          [strsame $platform ZCU102];
   set isZCU104          [strsame $platform ZCU104];
   set isZCU111          [strsame $platform ZCU111];

   # obsoleted boards
   set isKCU105          [strsame $platform KCU105];
   set isKC705           [strsame $platform KC705];
   set isKC7xx           [strsame $platform KC7xx];

   # ensure that the language has been selected
   if {$isLangNotSelected} {
      puts "@@@  completer_helper - Please type: use VHDL | Verilog";
      puts "@@@  completer_helper -    then rerun the projectCreate";
   } elseif {$isPlatNotSelected} {
      puts "@@@  completer_helper - Please type: use VCK190 | ZCU102 | ZCU111 | ZC702 | Zed | ZCU104 -- note: other boards exist, but have been deprecated";
      puts "@@@  completer_helper -    then rerun the projectCreate";
   } else {
     if {$isVCK190} {
         create_project -force $labName $trainingPath/$tcName/$demoOrLab -part [supportedPart xcvc1902-vsva2197-2MP-e-S];
         set_property board_part [latestBoardVersion xilinx.com:vck190:part0:2.2] [current_project];
     } elseif {$isZed} {
         create_project -force $labName $trainingPath/$tcName/$demoOrLab -part xc7z020clg484-1
         set_property board_part [latestBoardVersion avnet.com:zedboard:part0:1.4] [current_project]
     } elseif {$isZC702} {
         create_project -force $labName $trainingPath/$tcName/$demoOrLab -part xc7z020clg484-1
         set_property board_part [latestBoardVersion xilinx.com:zc702:part0:1.4] [current_project]
     } elseif {$isZCU102} {
         create_project -force $labName $trainingPath/$tcName/$demoOrLab -part xczu9eg-ffvb1156-2-e
         set_property board_part [latestBoardVersion xilinx.com:zcu102:part0:3.3] [current_project]
     } elseif {$isZCU104} {
         create_project -force $labName $trainingPath/$tcName/$demoOrLab -part xczu7ev-ffvc1156-2-e
         set_property board_part [latestBoardVersion xilinx.com:zcu104:part0:1.1] [current_project]
    } elseif {$isZCU111} {
         create_project -force $labName $trainingPath/$tcName/$demoOrLab -part xczu25dr-ffve1156-1-i
         set_property board_part [latestBoardVersion xilinx.com:zcu111:part0:1.2] [current_project]
         set_property target_language VHDL [current_project]
     } elseif {$isKCU105} {
         # create_project -force $projName $trainingPath/$tcName/$demoOrLab/$platform/$language -part xcku040-ffva1156-2-e
		 create_project -force $projName $trainingPath/$tcName/$demoOrLab -part xcku040-ffva1156-2-e
         set_property board_part [latestBoardVersion xilinx.com:kcu105:part0:1.5] [current_project]
    } elseif {$isKC705} {
         create_project -force $projName $trainingPath/$tcName/$demoOrLab/$platform/$language -part xc7k325tffg900-2
       set_property board_part [latestBoardVersion xilinx.com:kc705:part0:1.6] [current_project]
    } elseif {$isKC7xx} {
         create_project -force $projName $trainingPath/$tcName/$demoOrLab/$platform/$language -part xc7k70tfbg484-2
    }

    # with the project now open, set the default language
    set_property target_language $language [current_project]
   }

   markLastStep projectCreate
}
lappend loadedProcs {projectCreate "creates a project based on the globally defined variables"};

##
# proc:  blockDesignCreate
# descr:
# @remark <list of searchable terms>
#
proc blockDesignCreate {} {
   variable verbose
   if {$verbose} { puts "@@@  completer_helper - completer_helper.blockDesignCreate"; }
   variable blockDesignName;

   # create Block Design - test to see if "blkDsgn" exists and skip if it does
   # note: this only tests to see if a block design exists - it doesn't test for the specific block design name
   set blkDsgns [get_bd_designs -quiet];

   # if the blockDesignName doesn't exist, create it
   if {[lsearch -exact $blkDsgns $blockDesignName] == -1} {
      create_bd_design $blockDesignName;
      update_compile_order -fileset sources_1;
   } else {
      puts "@@@  completer_helper - $blockDesignName already exists! Cannot create.";
   }

   markLastStep blockDesignCreate;
}
lappend loadedProcs {blockDesignCreate "creates a block design based on the globally defined variables"};
#
# *********** save the block design
#
##
# proc:  blockDesignSave
# descr:
# @remark <list of searchable terms>
#
proc blockDesignSave {} {
   variable verbose
   if {$verbose} { puts "@@@  completer_helper - completer_helper.blockDesignSave"; }
   save_bd_design
   markLastStep save_bd_design;
}
lappend loadedProcs {blockDesignSave "saves the current block design"};
##
# proc: moduleCreateFromHDL
# descr: creates an IP block from an HDL file and adds it to the canvas
# @remark HDL block module create make
# @param hdlFileName - name of file to convert to an IP module (typically with path and extension)
# @param moduleName  - name of the module
# @return - true if successful, false if not (typically file not found)
#
proc moduleCreateFromHDL {hdlFileName moduleName} {
   variable tcName
   variable verbose
   if {$verbose} { puts "@@@  completer_helper - completer_helper.moduleCreateFromHDL"; }
   #update_compile_order -fileset sources_1
   if {[fileExists $hdlFileName]} {
      # extract IPname from the file name
      set onlyFileName [getLastHierarchy $hdlFileName]
      set IPname       [stripExtension $onlyFileName]
      add_files -norecurse $hdlFileName
      update_compile_order -fileset sources_1
      create_bd_cell -type module -reference $IPname $moduleName
      return true
   }
   return false
}
lappend loadedProcs {moduleCreateFromHDL "turns an HDL file into an IP block"};

##
# proc: librarySourcesAdd(libSrcList)
# @param libSrcList - list of source names and which library they go in
#      { { src lib } { src lib } ... }
# add_files -norecurse C:/training/VHDL_basicTestbench/support/uart_baud_gen.vhd
# set_property library newLib [get_files  C:/training/VHDL_basicTestbench/support/uart_baud_gen.vhd]
#
proc libSourcesAdd {libSrcList} {
   variable trainingPath;
   variable verbose ;
   if {$verbose} { puts "@@@  completer_helper - completer_helper.sourcesAdd $libSrcList"; }
   variable language;
   variable tcName;

   # set selected language
   set isVHDL    [strsame $language vhdl];
   set isVerilog [strsame $language verilog];
   
   # load all the files from the source list from the support directory unless a full path is specified
   foreach fileName $libSrcList {

     # each element in the list consists of 2 parts - the source file name and where it goes
	 # split into separate pieces for processing
	 set sourceLocation [subst [lindex $fileName 0]];
	 set libraryName [lindex $fileName 1];
	 
      # is there a full path provided? - Does not make corrections for langauage - assumes user knows what he/she's doing
     set hierarchyList [hierarchyToList $sourceLocation]
     if {[llength $hierarchyList] > 1} {        # a hierarchy has been presented so use it instead of the support directory
        set useThisFile $sourceLocation;
     } else {
        # no, so assume that we are pulling from the support directory
        set    fullFileName "";
        append fullFileName $trainingPath/$tcName/support/ $sourceLocation;
        #if there isn't an extension, then add one based on the selected language
         set isVHDLsource    [strEndsWith $sourceLocation .vhd];
         set isVerilogSource [strEndsWith $sourceLocation .v];
         set isTextSource    [strEndsWith $sourceLocation .txt];

        if {$isVHDLsource == 0 && $isVerilogSource == 0 && $isTextSource == 0} {
           if {$isVHDL == 1}    { append fullFileName .vhd }
           if {$isVerilog == 1} { append fullFileName .v }
        }

        # this line copies the file to the local working directory keeping the original file unchanged
        set useThisFile $fullFileName;
      }
	  
	  # we identified the file, let's add it
      if {[file exist $useThisFile]} { 
         import_files -norecurse -force $useThisFile;
		 # only the file name is required for the get_files
		 set fileNameOnly [lindex $hierarchyList [expr [llength $hierarchyList] - 1]];
		 puts "completer_helper.libSourcesAdd: adding $fileNameOnly to library $libraryName"
		 set_property SOURCE_SET sources_1 [get_filesets sim_1];
		 add_files -fileset sim_1 -norecurse -scan_for_includes $useThisFile;
		 import_files -fileset sim_1 -norecurse $useThisFile;
		 set_property library $libraryName [get_files  $fileNameOnly]; # assign this file to a library
		 update_compile_order -fileset sim_1;
		 # set_property used_in_synthesis false [get_files  /home/amd/training/Project_Flow/lab/KCU105/vhdl/uart_led.srcs/sources_1/imports/design_files/tb_fifo_pkg.vhd]
      } else {
         puts "@@@  completer_helper - completer_helper:libSourcesAdd - could not find $useThisFile and therefore cannot add it to the project";
      }
   }
}
lappend loadedProcs {libSourcesAdd "adds the list of sources to the project"};
##
# proc:  sourcesAdd
# descr: add source files
#   source files in list may include extensions or not
#   if no extensions are found, then the language is used to identify what the extension should be and this extension is appended to the file name
# @remark <list of searchable terms>
# @param sourceList   if the elements in the list don't have the hierarchy path, then the $tcName/support directory is assumed
#
proc sourcesAdd { sourceList } {
	variable trainingPath;
	variable verbose ;
	if {$verbose} { puts "@@@  completer_helper - completer_helper.sourcesAdd $sourceList"; }
	variable language;
	variable tcName;
	
	# is there anything to process?
	if {[llength sourceList] == 0} {
		puts "\tno arguments provided, returning from sourcesAdd"; 
		return;
	}

	# set selected language
	set isVHDL    [strsame $language vhdl];
	set isVerilog [strsame $language verilog];

	# load all the files from the source list from the support directory unless a full path is specified
	foreach fileName $sourceList {
		puts "completer_helper.sourcesAdd: adding $fileName"
		# is there a full path provided? - Does not make corrections for langauage - assumes user knows what he/she's doing
		set hierarchyList [hierarchyToList $fileName]
		if {[llength $hierarchyList] > 1} {        # a hierarchy has been presented so use it instead of the support directory
			set useThisFile $fileName;
			if {[string last . useThisFile] == -1} {
				if {$isVHDL == 1} { 
					append useThisFile .vhd 
				} elseif {$isVerilog == 1} { append useThisFile .v }			
			}
		} else {
			# no, so assume that we are pulling from the support directory
			set    fullFileName "";
			append fullFileName $trainingPath/$tcName/support/ $fileName;
			#if there isn't an extension, then add one based on the selected language
			set isVHDLsource    [strEndsWith $fileName .vhd];
			set isVerilogSource [strEndsWith $fileName .v];
			set isTextSource    [strEndsWith $fileName .txt];

			if {$isVHDLsource == 0 && $isVerilogSource == 0 && $isTextSource == 0} {
				if {$isVHDL == 1}    { append fullFileName .vhd }
				if {$isVerilog == 1} { append fullFileName .v }
			}

			# this line copies the file to the local working directory keeping the original file unchanged
			set useThisFile $fullFileName;
		}
		if {[file exist $useThisFile]} {
			import_files -norecurse $useThisFile;
		} else {
			puts "@@@  completer_helper - completer_helper:sourcesAdd - could not find $useThisFile and therefore cannot add it to the project";
		}
	}
}
lappend loadedProcs {sourcesAdd "adds the list of sources to the project"};
##
# proc:  simSourcesAdd
# descr: simulation source files in list may include extensions or not
#   if no extensions are found, then the language is used to identify what the extension should be and this extension is appended to the file name
# @remark <list of searchable terms>
# @param sourceList - list of sources to add to the simulation library
#
proc simSourcesAdd { sourceList } {
    variable trainingPath;
    variable verbose ;
    if {$verbose} { puts "@@@  completer_helper - completer_helper.simSourcesAdd $sourceList"; }
    variable language;
    variable tcName;

    # set selected language
    set isVHDL    [strsame $language vhdl];
    set isVerilog [strsame $language verilog];

    # load all the files from the source list from the support directory unless a full path is specified
    foreach fileName $sourceList {
	    puts "completer_helper.simSourcesAdd: adding $fileName"
        # is there a full path provided? - Does not make corrections for langauage - assumes user knows what he/she's doing
        set hierarchyList [hierarchyToList $fileName]
        if {[llength $hierarchyList] > 1} {        # a hierarchy has been presented so use it instead of the support directory
            set useThisFile $fileName;
        } else {
            # no, so assume that we are pulling from the support directory
            set    fullFileName "";
            append fullFileName $trainingPath/$tcName/support/ $fileName;
            #if there isn't an extension, then add one based on the selected language
            set isVHDLsource    [strEndsWith $fileName .vhd];
            set isVerilogSource [strEndsWith $fileName .v];
            set isTextSource    [strEndsWith $fileName .txt];

            if {$isVHDLsource == 0 && $isVerilogSource == 0 && $isTextSource == 0} {
                if {$isVHDL == 1}    { append fullFileName .vhd }
                if {$isVerilog == 1} { append fullFileName .v }
            }

            # this line copies the file to the local working directory keeping the original file unchanged
            set useThisFile $fullFileName;
        }
		
		# if the file exists, then add it to the simulation set
        if {[file exist $useThisFile]} {
		    set_property SOURCE_SET sources_1 [get_filesets sim_1]
		    add_files -fileset sim_1 -norecurse $useThisFile;
		    update_compile_order -fileset sim_1;
        } else {
            puts "@@@  completer_helper - completer_helper:simSourcesAdd - could not find $useThisFile and therefore cannot add it to the project";
        }
    }
}
lappend loadedProcs {simSourcesAdd "adds simulation files"};

##
# proc:  constraintFilesDefaultAdd
# descr:
# @remark <list of searchable terms>
#
proc constraintFilesDefaultAdd {} {
   variable tcName
   variable trainingPathproces
   variable trainingPath
   variable processor
   variable platform
   variable verbose
   if {$verbose} { puts "@@@  completer_helper - completer_helper.constraintFilesDefaultAdd"}

   # default constraint files
   set isZed        [strsame $platform ZED]
   set isZC702      [strsame $platform ZC702]
   set isZCU104     [strsame $platform ZCU104]
   set isVCK190     [strsame $platform VCK190];
   set isACAP       [expr $isVCK190];

   # Add the XDC files
   if {$isACAP} {
      add_files -fileset constrs_1 -norecurse $trainingPath/CustEdIP/VCK190_base.xdc
   } elseif {$isZCU104} {
      add_files -fileset constrs_1 -norecurse $trainingPath/CustEdIP/ZCU104_base.xdc
   } elseif {$isZC702} {
     #add_files -fileset constrs_1 -norecurse $trainingPath/training/$tcName/support/ZC702_base.xdc
     add_files -fileset constrs_1 -norecurse $trainingPath/CustEdIP/ZC702_base.xdc
   } elseif {$isZed} {
     #add_files -fileset constrs_1 -norecurse $trainingPath/training/$tcName/support/Zed_base.xdc
     add_files -fileset constrs_1 -norecurse $trainingPath/CustEdIP/ZED_base.xdc
   }

   # if it's a microblaze, then we have to connect the sys_diff_clk
   set isUBlaze [strsame $processor MicroBlaze]

   if {$isUBlaze} {
     if {$isZC702} {
       set clkConstraintFile $trainingPath/$tcName/support/ZC702_sys_clk.xdc
       if {[fileExists $clkConstraintFile]} {
         add_files -fileset constrs_1 -norecurse $clkConstraintFile
       }
     } elseif {$isZed == 0} {
       set clkConstraintFile $trainingPath/$tcName/support/zed_sys_clk.xdc
       if {[fileExists $clkConstraintFile]} {
         add_files -fileset constrs_1 -norecurse $clkConstraintFile
       }
     }
   } else {
      # it is not a MicroBlaze processor, no need to do anything.
     # puts "@@@  completer_helper - ***** Unsupported platform! $platform in constraintFilesAdd"
   }

   markLastStep constraintFilesDefaultAdd;
}
lappend loadedProcs {constraintFilesDefaultAdd "adds the default (_base.xdc) to the project"};
##
# proc:  constraintFilesAdd
# descr:
# @remark <list of searchable terms>
# @param constraintFileList  if the elements in the list don't have the hierarchy path, then the $tcName/support directory is assumed
#
proc constraintFilesAdd { constraintFileList } {
    variable tcName
    variable trainingPath
    variable verbose
    if {$verbose} { puts "@@@  completer_helper - completer_helper.constraintFilesAdd - $constraintFileList"}

    # if a list is provided, use it and ignore the embedded defaults
    if {[llength $constraintFileList] > 0 } {
       foreach fileName $constraintFileList {
         # is this a full path or just the name - if it's just the name assume that it's coming from the support directory
         # is there a full path provided? - Does not make corrections for langauage - assumes user knows what he/she's doing
         set hierarchyList [hierarchyToList $fileName]
         if {[llength $hierarchyList] > 1} {        # a hierarchy has been presented so use it instead of the support directory
            set fullFileName $fileName
         } else {
            # just the file name - assume it's coming from the source directory
            set fullFileName ""
            append fullFileName $trainingPath/$tcName/support/ $fileName
         }
       regsub -all {\\} $fullFileName / fullFileName
         import_files -fileset constrs_1 -norecurse $fullFileName
       }
    } else {
       puts "@@@  completer_helper - Expected a {list} of constraint files!"
    }

   markLastStep constraintFilesAdd;
}
lappend loadedProcs {constraintFilesAdd "adds a list of files as constraints"};
##
# proc:  ipFilesAdd
# descr:
# @remark <list of searchable terms>
#
proc ipFilesAdd {} {
 variable tcName
 variable platform
 variable demoOrLab
 variable language
 variable verbose
 variable trainingPath

 if {$verbose == 1} { puts "@@@  completer_helper - adding IP files"}
 #Adds the ip files based on board choosen
 if {$platform == "KC705"} {
  import_files -norecurse $trainingPath/$tcName/support/clk_core.xci
 } elseif {$platform == "KCU105"} {
  import_files -norecurse $trainingPath/$tcName/support/clk_core.xci
  import_files -norecurse $trainingPath/$tcName/support/char_fifo.xci
 }
markLastStep ipFilesAdded
}
lappend loadedProcs {ipFilesAdds "add IP files as IP"};
##
# proc:  ipFilesAdd
# @param IPsourceLoc - location of the directory containing the necessary xci files
# descr:
# @remark <list of searchable terms>
#
proc ipFilesAdd { IPsourceLoc } {
   variable tcName
   variable platform
   variable demoOrLab
   variable language
   variable verbose
   variable trainingPath

   if {$verbose == 1} { puts "@@@  completer_helper - adding IP files"; }
   #Adds the ip files based on board choosen
   if {$platform == "KC705"} {
     import_files -norecurse $IPsourceLoc/clk_core.xci;
   } elseif {$platform == "KCU105"} {
     import_files -norecurse $IPsourceLoc/clk_core.xci;
     import_files -norecurse $IPsourceLoc/char_fifo.xci;
   }
   markLastStep ipFilesAdded;
}
lappend loadedProcs {ipFilesAdd "add IP files as IP"};

#
##   ***OBSOLETE???***
# proc:  copySourcestoTraining
# descr: Copies SVN sources to training directory
# @remark <list of searchable terms>
#
proc copySourcestoTraining {} {
   variable tcName
   variable trainingPath

   file copy /media/sf_trunk/FPGA/TopicClusters/$tcName $trainingPath
}

proc designValidate {} {
   variable verbose;
   if {$verbose} { puts "completer_ completer_helper:designValidate" }
   set results [validate_bd_design -force];

   if {[llength $results] == 0} {
      puts "validation successful!"
   } else {
      puts "validation returning issues!";
      return $results;
   }
}

#
# ***** Processor/Processing System
##
# proc:  applyZynqProcessorPreset
# descr:
# @remark <list of searchable terms>
#
proc applyZynqProcessorPreset {} {
   variable processor
   variable platform
   variable verbose

   if {$verbose} { puts "@@@  completer_helper - in completer_helper.applyZynqProcessorPreset"; }

   # what is it? makes comparisons below easier
   set isZed    [strcmp $platform Zed]
   set isZC702  [strcmp $platform ZC702]
   set isZCU102 [strcmp $platform ZCU102]
   set isZCU104 [strcmp $platform ZCU104]
   set isUBlaze [strcmp $processor MicroBlaze]

   if {$isUBlaze == 0} {
      # If using ublaze, no need to do anything with presets.
   } else {
      if {$isZCU104 == 0} {
      # todo: probably wrong - validate
         set_property -dict [list CONFIG.preset {ZCU104}] [get_bd_cells processing_system7_0]
      } elseif {$isZC702 == 0} {
         set_property -dict [list CONFIG.preset {ZC702}] [get_bd_cells processing_system7_0]
      } elseif {$isZed ==0} {
         set_property -dict [list CONFIG.preset {ZedBoard}] [get_bd_cells processing_system7_0]
      } else {
         puts "@@@  completer_helper - ****** Zynq MP Needs to be implemented"
      }
   }

   markLastStep applyZynqProcessorPreset;
}
lappend loadedProcs {applyZynqProcessorPreset "apply the Zynq-7000 processor preset - warning this will reset all settings for the PS"};
#
# ********** Processor/Processing System
##
# proc:  processorAdd
# descr:
# @remark <list of searchable terms>
#
proc processorAdd {} {
   variable platform;
   variable trainingPath;
   variable processor;
   variable tcName;
   variable suppressPreClean;
   variable verbose;
   if {$verbose} { puts "@@@  completer_helper - in completer_helper.processorAdd - adding processor: $processor" }

   # clear the processors if they exist - suppressPreClean allows for microblaze plus other processor
   if {[info exist suppressPreClean]} {
      # the variable WAS defined, now we need to figure out what to do...
      if {!$suppressPreClean} {
         puts "@@@  completer_helper - Removing processors and starting fresh";
         set processors [get_bd_cells -quiet {micro* proc* zynq_ultra* noInterrupts PS_access versal_cips*}];
         delete_bd_objs -quiet $processors;
      } else {
         # we need to suppress the preClean so don't erase anything and fall through to the next part
         puts "@@@  completer_helper - Leaving existing processors alone...";
      }
   } else {
      # suppressPreClean was NOT defined therefore treat it as false - this means "clean-it-up!"
      puts "@@@  completer_helper - suppressPreClean doesn't exists, so we're removing processors and starting fresh";
      set processors [get_bd_cells -quiet {micro* proc* zynq_ultra* noInterrupts PS_access versal_cips*}]
      delete_bd_objs -quiet $processors
   }

   # PS (if part supports it)
   set isVCK190 [strsame $platform VCK190];
   set isACAP   [expr $isVCK190];
   set isZed    [strsame $platform Zed];
   set isZC702  [strsame $platform ZC702];
   set isZ7000  [expr $isZed || $isZC702];
   set isZCU102 [strsame $platform ZCU102];
   set isZCU104 [strsame $platform ZCU104];
   set isMPSoC  [expr $isZCU102 || $isZCU104];
   set isZCU111 [strsame $platform ZCU111];
   set isRFSoC  $isZCU111;
   set isUBlaze [strsame $processor MicroBlaze];

   if {$isUBlaze} {
      if {$verbose} { puts "@@@  completer_helper - adding the MicroBlaze";}
      create_bd_cell -type ip -vlnv [latestVersion xilinx.com:ip:microblaze:10.0] microblaze_0;

      # when the uB is running in a Zynq-7000 device, we have to use the special IP to access the rx, tx, and hp0 ports of the PS
      # [2018.1] mcheck if ip_repo_paths contains CustEdIP - if not, add it to the repository
      # todo: make sure this ADDs it to the repository not replacing anything that is already there
      if {$isVCK190} {
        puts "@@@  completer_helper - ACAP devices offer access to the RX/TX without needing the transparent PS";
      } elseif {$isZCU104} {
        # transparent PS not needed
        puts "@@@  completer_helper - MPSoC devices offer access to the RX/TX without needing the transparent PS";
      } else {
       # get the transparent PS as it is needed for rx/tx and clock
       # set availableIPs [get_ipdefs]
       # set targetIPname XparentPS:1.0
       # if {![containedIn $targetIPname $availableIPs]} {
       #    set_property  ip_repo_paths  $trainingPath/CustEdIP/XparentPS [current_project]
       #    update_ip_catalog
       # }
         # todo: this is what it should be: create_bd_cell -type ip -vlnv [latestVersion xilinx.com:user:xparentPS:1.0] PS_access
         #create_bd_cell -type ip -vlnv [latestVersion xilinx.com:user:XarentPS:1.0] PS_access
         #create_bd_cell -type ip -vlnv xilinx.com:user:$targetIPname PS_access
         # set_property -dict [list CONFIG.CLK_100MHz_EN {true} CONFIG.Rx_EN {true} CONFIG.Tx_EN {true} CONFIG.CLK_reset_EN {true} CONFIG.S_AXI_HP0_EN {true}] [get_bd_cells PS_access]
         ##set_property range 512M [get_bd_addr_segs {microblaze_0/Data/SEG_PS_access_reg0}]
      }

      # Use the clock off the PS to drive the uB
      if {$isACAP} {
         connect_bd_net [get_bd_pins versal_cips_0/pl0_ref_clk] [get_bd_pins microblaze_0/Clk];
         connect_bd_net [get_bd_pins microblaze_0/Reset] [get_bd_pins rst_versal_cips_0_333M/mb_reset];
      } elseif {$isMPSoC || $isRFSoC} {
         connect_bd_net [get_bd_pins zynq_ultra_ps_e_0/pl_clk0] [get_bd_pins microblaze_0/Clk];
         connect_bd_net [get_bd_pins microblaze_0/Reset] [get_bd_pins rst_ps8_0_100M/peripheral_reset];
      }
   } else {
      # if not specifically targeting the uB, the PS will be instantiated
      if {$isVCK190} {
         if {$verbose} { puts "@@@  completer_helper - is a VCK190"; }
         create_bd_cell -type ip -vlnv [latestVersion xilinx.com:ip:versal_cips:2.0] versal_cips_0
      } elseif {$isZed || $isZC702} {                 # add in the Zynq7000 PS
         if {$verbose} { puts "@@@  completer_helper - is a Zed or ZC702 - adding the PS" }
         create_bd_cell -type ip -vlnv [latestVersion xilinx.com:ip:processing_system7:5.5] processing_system7_0
      } elseif {$isZCU102 || $isZCU104} {                          # add in the US+ PS
         if {$verbose} { puts "@@@  completer_helper - is a ZCU102/4 - adding the PS"}
         create_bd_cell -type ip -vlnv [latestVersion xilinx.com:ip:zynq_ultra_ps_e:3.0] zynq_ultra_ps_e_0
      } elseif {$isZCU111} {
         if {$verbose} { puts "@@@  completer_helper - is an RFSoC device - adding the PS"}
         create_bd_cell -type ip -vlnv [latestVersion xilinx.com:ip:zynq_ultra_ps_e:3.0] zynq_ultra_ps_e_0
      }
   }

   regenerate_bd_layout
   save_bd_design

   markLastStep processorAdd
}
lappend loadedProcs {processorAdd "adds the specified processor to the block design"};
#
# ********** processorConfigure
#
# - ensures that processor is configured for the board and includes an M_AXI_GP0
#
##
# proc:  processorConfigure
# descr:
# @remark <list of searchable terms>
#
proc processorConfigure {} {
   variable platform;
   variable processor;
   variable ZynqAllOff;
   variable MPSoCallOff;
   variable ACAPallOff;
   variable activePeripheralList;
   variable verbose

   if {$verbose} { puts "@@@  completer_helper - in completer_helper.processorConfigure"; }

   variable suppressInterrupts
   if (![info exists suppressInterrupts]) { set suppressInterrupts 0; }

   # what is it? makes comparisons below easier
   set isVCK190   [strsame $platform VCK190];
   set isACAP    [expr $isVCK190];
   set isZed     [strsame $platform Zed];
   set isZC702   [strsame $platform ZC702];
   set isZCU102  [strsame $platform ZCU102];
   set isZCU104  [strsame $platform ZCU104];
   set isZCU111  [strsame $platform ZCU111] ;
   set isZ7000   [expr $isZed || $isZC702];
   set isMPSoC   [expr $isZCU102 || $isZCU104 || $isZCU111];
   set isUBlaze  [strsame $processor MicroBlaze];
   set isA53     [strsame $processor A53];
   set isR5      [strsame $processor R5];
   set isA72     [strsame $processor A72];
   set isR5F     [strsame $processor R5F];

   # clear the PS's configuration
   if {$isACAP} {
      set targetDevice versal_cips_0;
      set list $ACAPallOff;
   } elseif {$isZ7000} {
      set targetDevice processing_system7_0;
      set list $ZynqAllOff;
   } elseif {$isMPSoC} {
      set targetDevice zynq_ultra_ps_e_0;
      set list $MPSoCallOff;
   } elseif {$isUBlaze} {
      # no action required
   } else {
      boxedMsg "undefined processor!";
      return;
   }

   # clear the PS's configuration
   if {$verbose} { puts "@@@  completer_helper - completer_helper.processorConfigure - clearing all options in PS"; }
#    if {$isACAP} {
#       set nConfigItems [llength $list]
#       for {set index 0} {$index < $nConfigItems} {incr index 2} {
#          set itemNameIndex  $index
#          set itemValueIndex [expr $index + 1 ]
#          set itemName       [lindex $list $itemNameIndex ]
#          set itemValue      [lindex $list $itemValueIndex]
#          if {$verbose} { puts "@@@  completer_helper - setting $itemName to $itemValue on $targetDevice"; }
#          set_property $itemName $itemValue [get_bd_cells $targetDevice]
#       }
#    }

   if {$isMPSoC || $isZ7000} {
      set nConfigItems [llength $list]
      for {set index 0} {$index < $nConfigItems} {incr index 2} {
         set itemNameIndex  $index
         set itemValueIndex [expr $index + 1 ]
         set itemName       [lindex $list $itemNameIndex ]
         set itemValue      [lindex $list $itemValueIndex]
         if {$verbose} { puts "@@@  completer_helper - setting $itemName to $itemValue on $targetDevice"; }
         set_property $itemName $itemValue [get_bd_cells $targetDevice]
      }
   }

   # is there a microblaze in this design?
   if {$isUBlaze} {
      # ignore the PS in this device as it is being managed by the PS_access IP

      if ($suppressInterrupts) {
         if {$verbose} { puts "@@@  completer_helper - configuring uB without interrupts"; }
         apply_bd_automation -rule xilinx.com:bd_rule:microblaze -config {preset "Microcontroller" local_mem "32KB" ecc "None" cache "None" debug_module "Debug Only" axi_periph "Enabled" axi_intc "0" clk "/PS_access/CLK_100MHz (100 MHz)" }  [get_bd_cells microblaze_0]
      } else {
         if {$verbose} { puts "@@@  completer_helper - configuring uB with interrupts"; }
         apply_bd_automation -rule xilinx.com:bd_rule:microblaze -config {preset "Microcontroller" local_mem "32KB" ecc "None" cache "None" debug_module "Debug Only" axi_periph "Enabled" axi_intc "1" clk "/PS_access/CLK_100MHz (100 MHz)" }  [get_bd_cells microblaze_0]
         puts "@@@  completer_helper - Grounding unused pin on Concat block to Intertupt controller";
         create_bd_cell -type ip -vlnv [latestVersion xilinx.com:ip:xlconstant:1.1] UNUSEDintr_gnd
         set_property -dict [list CONFIG.CONST_VAL {0}] [get_bd_cells UNUSEDintr_gnd]
         connect_bd_net [get_bd_pins microblaze_0_xlconcat/In1] [get_bd_pins UNUSEDintr_gnd/dout];
      }
      connect_bd_net [get_bd_pins PS_access/CLK_reset_n] [get_bd_pins rst_PS_access_100M/ext_reset_in]

     # connect the uB design to the HP0 for data access to DDR
     apply_bd_automation -rule xilinx.com:bd_rule:axi4 -config {Master "/microblaze_0 (Periph)" intc_ip "/microblaze_0_axi_periph" Clk_xbar "Auto" Clk_master "Auto" Clk_slave "Auto" }  [get_bd_intf_pins PS_access/S_AXI_HP0]

   } else {
      if {$verbose} { puts "@@@  completer_helper -    configuring $platform ($targetDevice) PS with preset"; }
      if {$verbose} { puts "@@@  completer_helper -    this is done to properly configure connections and settings for the DDR, QSPI, and SD card"; }
      # set the board specific preset
      if {$isACAP} {
         apply_bd_automation -rule xilinx.com:bd_rule:cips -config { board_preset {Yes} boot_config {Custom} configure_noc {Add new AXI NoC} debug_config {JTAG} design_flow {Full System} mc_type {None} num_mc_ddr {None} num_mc_lpddr {None} pl_clocks {None} pl_resets {None}}  [get_bd_cells versal_cips_0]
      } elseif {$isZCU104 || $isZCU102} {
         apply_bd_automation -rule xilinx.com:bd_rule:zynq_ultra_ps_e -config {apply_board_preset "1" }  [get_bd_cells zynq_ultra_ps_e_0];
      } elseif {$isZC702} {
         set_property -dict [list CONFIG.preset {ZC702}] [get_bd_cells processing_system7_0]
         apply_bd_automation -rule xilinx.com:bd_rule:processing_system7 -config {make_external "FIXED_IO, DDR" Master "Disable" Slave "Disable" }  [get_bd_cells processing_system7_0]
      } elseif {$isZed} {
         set_property -dict [list CONFIG.preset {ZedBoard}] [get_bd_cells processing_system7_0]
         apply_bd_automation -rule xilinx.com:bd_rule:processing_system7 -config {make_external "FIXED_IO, DDR" Master "Disable" Slave "Disable" }  [get_bd_cells processing_system7_0]
      } else {
         if {$verbose} { puts "@@@  completer_helper - no presets for $platform" }
      }

      # add in user's specific configuration
      if {$verbose} { puts "@@@  completer_helper -    Add in user's specific configuration"; }
      set nConfigItems [llength $activePeripheralList]
      if {$nConfigItems == 0} {
         if {$verbose} { puts "@@@  completer_helper - activePeripheralList is empty!"; }
      }
      

      # iterate through the activePeripheralList and set the property
#       if {$isACAP} {
#          set nConfigItems [llength $list]
#          for {set index 0} {$index < $nConfigItems} {incr index 2} {
#             set itemNameIndex  $index
#             set itemValueIndex [expr $index + 1 ]
#             set itemName       [lindex $activePeripheralList $itemNameIndex ]
#             set itemValue      [lindex $activePeripheralList $itemValueIndex]
#             if {$verbose} { puts "@@@  completer_helper - configuring PS's option: $itemName to $itemValue on $targetDevice"; }
#             set_property $itemName $itemValue [get_bd_cells $targetDevice]
#          }
#      }
   
      if {$isMPSoC || $isZ7000} {
         for {set index 0} {$index < $nConfigItems} {incr index 2} {
            set itemNameIndex $index
            set itemValueIndex [expr $index + 1 ]
            set itemName       [lindex $activePeripheralList $itemNameIndex ]
            set itemValue      [lindex $activePeripheralList $itemValueIndex]
            if {$verbose} { puts "@@@  completer_helper - configuring PS's ($targetDevice) option: $itemName to value: $itemValue"; }
            set_property $itemName $itemValue [get_bd_cells $targetDevice]
         }
      }
   }

   save_bd_design
#
   markLastStep processorConfigure
}
lappend loadedProcs {processorConfigure "configures the PS based on the settings in the AP/MPSoC list"};
#
# ********** versalNocAdd
#
# Adds NOC with DRAM Enabled to Versal ACAP that was instantiated with processorAdd
# ONLY VERSAL DEVICES ARE SUPPORTED
#
##
# proc:  versalNocAdd
# descr:
# @remark <list of searchable terms>
#
proc versalNocAdd {} {
   variable platform;
   variable processor;
   variable activeConfigurationList;
   variable verbose
   
   # what is it? makes comparisons below easier
   set isVCK190   [strsame $platform VCK190];
   set isACAP    [expr $isVCK190];
   set isZed     [strsame $platform Zed];
   set isZC702   [strsame $platform ZC702];
   set isZCU102  [strsame $platform ZCU102];
   set isZCU104  [strsame $platform ZCU104];
   set isZCU111  [strsame $platform ZCU111] ;
   set isZ7000   [expr $isZed || $isZC702];
   set isMPSoC   [expr $isZCU102 || $isZCU104 || $isZCU111];
   set isUBlaze  [strsame $processor MicroBlaze];
   set isA53     [strsame $processor A53];
   set isR5      [strsame $processor R5];
   set isA72     [strsame $processor A72];
   set isR5F     [strsame $processor R5F];

   if {$isACAP} {
      # Set platform target names.
      set procTargetDevice versal_cips_0;
      set nocTargetDevice axi_noc_0;
   
      if {$verbose} { puts "@@@  completer_helper - in completer_helper.versalNocAdd"; }
      # Configure the ACAP
      apply_bd_automation -rule xilinx.com:bd_rule:cips -config { board_preset {Yes} boot_config {Custom} configure_noc {Add new AXI NoC} debug_config {JTAG} design_flow {Full System} mc_type {DDR} num_mc_ddr {1} num_mc_lpddr {None} pl_clocks {1} pl_resets {1}}  [get_bd_cells $procTargetDevice]
   
      # add in user's specific configuration
      if {$verbose} { puts "@@@  completer_helper -    Add in user's specific NOC configuration"; }
      set nConfigItems [llength $activeConfigurationList]
      if {$nConfigItems == 0} {
         if {$verbose} { puts "@@@  completer_helper - NOCactiveConfigurationList is empty!"; }
      }
   
      # iterate through the activeConfigurationList and set the NOC's properties
      for {set index 0} {$index < $nConfigItems} {incr index 2} {
         set itemNameIndex $index
         set itemValueIndex [expr $index + 1 ]
         set itemName       [lindex $activeConfigurationList $itemNameIndex ]
         set itemValue      [lindex $activeConfigurationList $itemValueIndex]
         if {$verbose} { puts "@@@  completer_helper - configuring NOC ($nocTargetDevice) option: $itemName to value: $itemValue"; }
         set_property $itemName $itemValue [get_bd_cells $nocTargetDevice]
      }

   } elseif {$isZ7000} {
      if {$verbose} { puts "@@@  completer_helper - NOC configuration not supported or required in $platform"; }
   } elseif {$isMPSoC} {
      if {$verbose} { puts "@@@  completer_helper - NOC configuration not supported or required in $platform"; }
   } elseif {$isUBlaze} {
      if {$verbose} { puts "@@@  completer_helper - NOC configuration not supported or required in $platform"; }
   } else {
      if {$verbose} { puts "@@@  completer_helper - NOC configuration not supported or required for undefined processor"; }
   }

   markLastStep versalNocAdd
}
lappend loadedProcs {versalNocAdd "Adds NOC with DRAM Enabled to Versal ACAP"};

#
# *** processorDefaultConnect
#
##
# proc:  processorDefaultConnect
# descr:
# @remark <list of searchable terms>
#
proc processorDefaultConnect {} {
   variable platform
   variable processor
   variable verbose
   variable ZynqAllOff
   variable MPSoCallOff
   variable activePeripheralsList

   if {$verbose} { puts "@@@  completer_helper - in completer_helper.processorDefaultConnect"; }

   # what is it? makes comparisons below easier
   set isZed    [strcmp $platform Zed]
   set isZC702  [strcmp $platform ZC702]
   set isZCU102 [strcmp $platform ZCU102]
   set isUBlaze [strcmp $processor MicroBlaze]

   if { $isUBlaze == 0 } {
      # if the transparent PS peripheral is available, then get rid of the clock wizard and use the XparentPS instead
      set XparentPSpresent [get_bd_cells -quiet PS_access]
      if {[llength $XparentPSpresent]} {
         # clock connection
         disconnect_bd_net /microblaze_0_Clk [get_bd_pins clk_wiz_1/clk_out1]
         connect_bd_net [get_bd_pins PS_access/CLK_100MHz] [get_bd_pins microblaze_0/Clk]
         # and the locked nReset signals
         delete_bd_objs [get_bd_nets clk_wiz_1_locked]
         connect_bd_net [get_bd_pins PS_access/CLK_reset_n] [get_bd_pins rst_clk_wiz_1_100M/dcm_locked]
         # remove the IP and port
         delete_bd_objs [get_bd_intf_nets CLK_IN1_D_1] [get_bd_intf_ports CLK_IN1_D]
         delete_bd_objs [get_bd_nets noReset_dout] [get_bd_cells clk_wiz_1] [get_bd_cells noReset]
         # remove the rtl reset port and associated connection with the clk_wiz_1
         delete_bd_objs [get_bd_nets reset_rtl_1] [get_bd_ports reset_rtl]
         # connect the ext_reset_in to the XParentPS
         connect_bd_net [get_bd_pins rst_clk_wiz_1_100M/ext_reset_in] [get_bd_pins PS_access/CLK_reset_n]
      }
   }

   set PS7inUse [get_bd_cells -quiet processing_system7*]
   if {[llength $PS7inUse] > 0} {
      if { $isZed == 0 || $isZC702 == 0} {
         apply_bd_automation -rule xilinx.com:bd_rule:processing_system7 -config {make_external "FIXED_IO, DDR" apply_board_preset "0" Master "Disable" Slave "Disable" }  [get_bd_cells processing_system7_0]
      }
   }

   markLastStep processorDefaultConnect
}
lappend loadedProcs {processorDefaultConnect "Makes default connections for the PS - includes clock wizards, reset blocks, etc."};
##
# proc:  VIOadd
# descr: adds and configures a VIO for this design
#        requires that a clock source exist before making this call - in the case of the uB, the clock should be from the PS_access IP
# @remark <list of searchable terms>
#
proc VIOadd {} {
   variable platform;
   variable processor;
   variable tcName;
   variable verbose;
   if {$verbose} { puts "@@@  completer_helper - completer_helper.VIOadd"; }

   # remove the VIO if it already exists
   set VIOs [get_bd_cells -quiet {vio_fmcce LCD_*} ];
   delete_bd_objs -quiet $VIOs;

   # determine the platform
   set isZed    [strsame $platform Zed];
   set isZC702  [strsame $platform ZC702];
   set isZ7000  [expr $isZed || $isZC702];
   set isZCU102 [strsame $platform ZCU102];
   set isZCU104 [strsame $platform ZCU104];
   set isMPSoC  [expr $isZCU104 || $isZCU102];
   set isZCU111 [strsame $platform ZCU111];
   set isRFSoC  $isZCU111;
   set isVCK190 [strsame $platform VCK190];
   set isACAP   [expr $isVCK190];

   # what kind of PS?
   if {$isACAP} {
      set PStype     [strsame $processor psv_cortexa72_0];
   } elseif {$isMPSoC || $isRFSoC} {
      # is an MPSoC or RFSoC
      set PStype     [strsame $processor psu_cortexa53_0];
   } elseif {$isZ7000} {
      # is a Z7000
      set PStype     [strsame $processor ps7_cortexa9_0];
   } else {
      puts stderr "VIOadd - unknown platform: $platform";
      return;
   }

   set isUBlaze [strsame $processor microblaze]

   # instantiate and configure the VIO
   set vioName vio_fmcce;

   # ACAP devices require the AXI streaming version of the VIO
   if {$isACAP} {
      create_bd_cell -type ip -vlnv [latestVersion xilinx.com:ip:axis_vio:1.0] $vioName;
   } else {
      create_bd_cell -type ip -vlnv [latestVersion xilinx.com:ip:vio:3.0] $vioName;
   }

   # configure the VIO's inputs for the max configuration
   set VIO_nInputs  3;
   set VIO_nOutputs 3;
   set_property -dict [list CONFIG.C_NUM_PROBE_IN $VIO_nInputs  CONFIG.C_NUM_PROBE_OUT $VIO_nOutputs] [get_bd_cells $vioName];
   set_property -dict [list CONFIG.C_PROBE_IN0_WIDTH {7}] [get_bd_cells $vioName];     # LCD
   set_property -dict [list CONFIG.C_PROBE_IN1_WIDTH {8}] [get_bd_cells $vioName];     # LEDs_linear
   set_property -dict [list CONFIG.C_PROBE_IN2_WIDTH {5}] [get_bd_cells $vioName];     # LEDs_rosetta

   # set the VIO's outputs based on the board specific capabilities
   set nRosettaLEDs    5;  # max capability
   set nLinearLEDs     8;
   set nRosettaButtons 5;
   set nLinearSwitches 8;

   if {$isVCK190} {
      set nRosettaLEDs    4;
      set nLinearSwitches 4;
      set nRosettaButtons 2;
      set nLinearLEDs     8;
   } elseif {$isZCU104} {
      set nRosettaLEDs    4;
      set nLinearSwitches 4;
      set nRosettaButtons 4;
   }

   # set the widths for the inputs and outputs
   set_property -dict [list CONFIG.C_PROBE_IN1_WIDTH  $nLinearLEDs]     [get_bd_cells $vioName];    # LEDs in the linear configuration
   set_property -dict [list CONFIG.C_PROBE_IN2_WIDTH  $nRosettaLEDs]    [get_bd_cells $vioName];    # LEDs in the Rosetta
   set_property -dict [list CONFIG.C_PROBE_OUT0_WIDTH {1}]              [get_bd_cells $vioName];    # LCD data catcher next datum request
   set_property -dict [list CONFIG.C_PROBE_OUT1_WIDTH $nRosettaButtons] [get_bd_cells $vioName];    # Buttons_Rosetta
   set_property -dict [list CONFIG.C_PROBE_OUT2_WIDTH $nLinearSwitches] [get_bd_cells $vioName];    # Switches_Linear
   # the LCD display is deprecated and therefore we're just leaving it alone for now.

   # connect the clock
   if {$isUBlaze} {
     #connect_bd_net [get_bd_pins vio_fmcce/clk] [get_bd_pins clk_wiz_1/clk_out1];
     apply_bd_automation -rule xilinx.com:bd_rule:clkrst -config {Clk "/PS_access/CLK_100MHz (100 MHz)" }  [get_bd_pins vio_fmcce/clk]
   } elseif {$isACAP} {
      connect_bd_net [get_bd_pins vio_fmcce/clk] [get_bd_pins versal_cips_0/pl0_ref_clk];
   } elseif {$isMPSoC || $isRFSoC} {
      connect_bd_net [get_bd_pins vio_fmcce/clk] [get_bd_pins zynq_ultra_ps_e_0/pl_clk0];
   } else {
     # Z7000 board
     connect_bd_net [get_bd_pins vio_fmcce/clk] [get_bd_pins processing_system7_0/FCLK_CLK0];
   }

   # tie off the LCDs as they are no longer being used, let the LED outputs hang open
   create_bd_cell -type ip -vlnv [latestVersion xilinx.com:ip:xlconstant:1.1] LCD_const_data;
   set_property -dict [list CONFIG.CONST_WIDTH {7} CONFIG.CONST_VAL {0}] [get_bd_cells LCD_const_data];
   connect_bd_net [get_bd_pins LCD_const_data/dout] [get_bd_pins vio_fmcce/probe_in0];

   # the remainingVIO connections will be made by their respective GPIO elements
}
lappend loadedProcs {vioAdd "adds the VIO for the virtualized I/O settings"};
##
#
# proc:  LEDsLinearAdd
# descr: adds GPIO supporting 8 bit linear LEDs
# @remark <list of searchable terms>
#
proc LEDsLinearAdd { } {
   variable platform;
   variable processor;
   variable tcName;
   variable verbose;
   if {$verbose} { puts "@@@  completer_helper - completer_helper.LEDsLinearAdd (Adding GPIO for Linear LED and connecting to PS)";  }

   # connect to the appropriate processor
   set isVCK190  [strsame $platform VCK190];
   set isACAP    [expr $isVCK190];
   set isUBlaze  [strsame $processor MicroBlaze];
   set isZed     [strsame $platform ZED];
   set isZC702   [strsame $platform ZC702];
   set isZ7000   [expr $isZed || $isZC702];
   set isZCU102  [strsame $platform ZCU102];
   set isZCU104  [strsame $platform ZCU104];
   set isZCU111  [strsame $platform ZCU111];
   set isMPSoC   [expr $isZCU102 || $isZCU104];
   set isRFSoC   $isZCU111;

   set ipCore "xilinx.com:ip:axi_gpio";

   # Set GPIO port based on LED configuration being used, either linear or rosetta
   set ipName GPIO_LEDs_linear;
   set ipPort GPIO_LEDs_linear;

   # remove the previous configuration of the GPIO as we will regenerate it in a moment
   set objects [get_bd_cells -quiet $ipName ];
   delete_bd_objs -quiet $objects;
   set objects [get_bd_ports -quiet $ipPort ];
   delete_bd_objs -quiet $objects;

   # add GPIO Linear LEDs, configure, and connect
   create_bd_cell -type ip -vlnv [latestVersion xilinx.com:ip:axi_gpio:2.0] $ipName;

   # based on the board type, set the size of the output
   if {$isACAP} { #this doesn't exist on the VCK190 board
      set_property -dict [list CONFIG.C_GPIO_WIDTH {4} CONFIG.C_ALL_OUTPUTS {1}] [get_bd_cells $ipName];
   } elseif {$isMPSoC || $isRFSoC} {
      set_property -dict [list CONFIG.C_GPIO_WIDTH {4} CONFIG.C_ALL_OUTPUTS {1} CONFIG.C_ALL_INPUTS {0} CONFIG.GPIO_BOARD_INTERFACE {Custom} CONFIG.C_ALL_OUTPUTS {1}] [get_bd_cells $ipName];
   } elseif {$isZC702} {
      set_property -dict [list CONFIG.C_GPIO_WIDTH {8} CONFIG.C_ALL_OUTPUTS {1}] [get_bd_cells $ipName]
   } elseif {$isZed} {
      set_property -dict [list CONFIG.C_GPIO_WIDTH {8} CONFIG.C_ALL_OUTPUTS {1}] [get_bd_cells $ipName]
   } else {
      puts "@@@  completer_helper - Unsupported platform in LEDsLinearAdd: $platform --- aborting this funciton";
      return;
   }

   # make AXI connection
   if {$isACAP} {
      apply_bd_automation -rule xilinx.com:bd_rule:axi4 -config { Clk_master {/versal_cips_0/fpd_cci_noc_axi0_clk (824 MHz)} Clk_slave {Auto} Clk_xbar {Auto} Master {/versal_cips_0/FPD_CCI_NOC_0} Slave {/$ipName/S_AXI} ddr_seg {Auto} intc_ip {/axi_noc_0} master_apm {0}}  [get_bd_intf_pins $ipName/S_AXI];
   } elseif {$isMPSoC || $isRFSoC} {
	   # make ONLY the AXI connection
	   puts "lookie! it's an MPSoC or RFSoC board!"
	  if {$isZCU104} {
	     puts "ZCU104 boards don't have the LEDs for this device, therefore it will only be implemented via the VIO";
	     # this makes the connection between the GPIO and the AXI interconnect
	     # apply_bd_automation -rule xilinx.com:bd_rule:axi4 -config { Clk_master {/zynq_ultra_ps_e_0/pl_clk0 (100 MHz)} Clk_slave {Auto} Clk_xbar {/zynq_ultra_ps_e_0/pl_clk0 (100 MHz)} Master {/zynq_ultra_ps_e_0/M_AXI_HPM0_FPD} Slave {/$ipName/S_AXI} ddr_seg {Auto} intc_ip {/ps8_0_axi_periph} master_apm {0}}  [get_bd_intf_pins $ipName/S_AXI];
         apply_bd_automation -rule xilinx.com:bd_rule:axi4 -config { Clk_master {Auto} Clk_slave {Auto} Clk_xbar {Auto} Master {/zynq_ultra_ps_e_0/M_AXI_HPM0_LPD} Slave {/GPIO_LEDs_linear/S_AXI} ddr_seg {Auto} intc_ip {New AXI Interconnect} master_apm {0}}  [get_bd_intf_pins GPIO_LEDs_linear/S_AXI];
	  } else {
	     puts "whatever board you're trying to use hasn't been validated yet.";
	  }
   } elseif {$isZ7000} {
       # for Z7000
       apply_bd_automation -rule xilinx.com:bd_rule:axi4 -config { Clk_master {Auto} Clk_slave {Auto} Clk_xbar {Auto} Master {/processing_system7_0/M_AXI_GP0} Slave {/$ipName/S_AXI} ddr_seg {Auto} intc_ip {New AXI Interconnect} master_apm {0}}  [get_bd_intf_pins $ipName/S_AXI];
   } else {
      # at the moment, the uB is not supported
      # todo: add in uB support
      puts "@@@  completer_helper - Unsupported platform in LEDsLinearAdd: $platform - should never reach!";
   }

 #  if {$isUBlaze} {
 #     apply_bd_automation -rule xilinx.com:bd_rule:axi4 -config {Master "/microblaze_0 (Periph)" Clk "Auto" }  [get_bd_intf_pins $ipName/S_AXI]
 #  } elseif {[strsame $processor A53] || [strContains $processor a53]} {
 #     puts "@@@  completer_helper - attaching to the A53"
 #     apply_bd_automation -rule xilinx.com:bd_rule:axi4 -config { Clk_master {Auto} Clk_slave {Auto} Clk_xbar {Auto} Master {/zynq_ultra_ps_e_0/M_AXI_HPM0_LPD} Slave {/$ipName/S_AXI} intc_ip {New AXI Interconnect} master_apm {0}}  [get_bd_intf_pins $ipName/S_AXI]
 #     # this was the old one for the ZCU102
 #     #apply_bd_automation -rule xilinx.com:bd_rule:axi4 -config { Clk_master {/zynq_ultra_ps_e_0/pl_clk0 (96 MHz)} Clk_slave {Auto} Clk_xbar {/zynq_ultra_ps_e_0/pl_clk0 (96 MHz)} Master {/zynq_ultra_ps_e_0/M_AXI_HPM0_LPD} Slave {/$ipName/S_AXI} intc_ip {/ps8_0_axi_periph} master_apm {0}}  [get_bd_intf_pins $ipName/S_AXI]
 #  } elseif {[strContains $processor A9]} {
 #     puts "@@@  completer_helper - Zynq7000"
 #     apply_bd_automation -rule xilinx.com:bd_rule:axi4 -config {Master "/processing_system7_0/M_AXI_GP0" Clk "Auto" }  [get_bd_intf_pins $ipName/S_AXI]
 #  } else {
 #     puts "@@@  completer_helper - unidentified processor: $processor"
 #  }

   # Connect LEDs to GPIO as external outputs
   # First, boards with 8 LEDs and external pins named LEDs_linear
   if {$isZ7000 || $isZCU102 || $isZCU111} {
       create_bd_port -dir O -from 7 -to 0 $ipPort
       connect_bd_net [get_bd_pins /$ipName/LEDs_linear] [get_bd_ports $ipPort];
   } elseif {$isVCK190} { #VCK190 doesn't support these LEDs, so we tie this to the vio instead. 
      puts "The VCK190 needs the connections to the outside updated";
      # 4 LEDs
      #create_bd_port -dir O -from 3 -to 0 $ipPort
      #connect_bd_net [get_bd_pins /$ipName/gpio_io_o] [get_bd_ports $ipPort]
      #connect_bd_net [get_bd_pins $ipName/gpio_io_o] [get_bd_pins vio_fmcce/probe_in1];
      make_bd_pins_external  [get_bd_pins $ipName/gpio_io_o];
      set_property name LEDs_linear [get_bd_ports gpio_io_o_0]
   } elseif {$isZCU104} {
      # there aren't any physical LEDs for the GPIO to tie to, so tie they output of the GPIO to the VIO input
	  #if {[llength [get_bd_pins vio_fmcce/probe_in1]] > 0} {
		  #connect_bd_net [get_bd_pins $ipName/gpio_io_o] [get_bd_pins vio_fmcce/probe_in1];
		  make_bd_pins_external  [get_bd_pins $ipName/gpio_io_o];
      		set_property name LEDs_linear [get_bd_ports gpio_io_o_0]
	  #} else {
	   #  puts "LEDsLinearAdd - WARNING! - There aren't any phiical LEDs  to use for this peripheral, so normally we tie it to the input of the VIO; however, no VIO was found!"
	  #}
   } else {
      # at the moment, the uB is not supported
      # todo: add in uB support
      puts "@@@  completer_helper - Unsupported platform in LEDsLinear: $platform";
   }

   # For Versal, add external reset button connection - why is this here? it doesn't have anything to do with the GPIO! commenting out in hopes that when tested, it will cause an error and we can find the right place for this
   #if {$isVCK190} {
   #    apply_bd_automation -rule xilinx.com:bd_rule:board -config { Manual_Source {Auto}}  [get_bd_pins rst_versal_cips_0_333M/ext_reset_in]
   #}

   markLastStep LEDsLinearAdd;
}
lappend loadedProcs {LEDsLinearAdd "adds the GPIO for controlling the linear LEDs"};
#
# *** Buttons Rosetta
#
##
# proc:  buttonsRosettaAdd
# descr:
# @remark <list of searchable terms>
#
proc buttonsRosettaAdd { } {
   variable platform
   variable processor
   variable tcName
   variable verbose
   if {$verbose} { puts "@@@  completer_helper - completer_helper.buttonsRosettaAdd"; }

   set isVCK190  [strsame $platform VCK190];
   set isACAP    [expr $isVCK190];
   set isUBlaze  [strsame $processor MicroBlaze];
   set isZed     [strsame $platform ZED];
   set isZC702   [strsame $platform ZC702];
   set isZ7000   [expr $isZed || $isZC702];
   set isZCU102  [strsame $platform ZCU102];
   set isZCU104  [strsame $platform ZCU104];
   set isZCU111  [strsame $platform ZCU111];
   set isMPSoC   [expr $isZCU102 || $isZCU104];
   set isRFSoC   $isZCU111;
   set ipName "GPIO_buttons_rosetta"
   set ipPort buttons_rosetta

   # remove the device if it already exists
   set objects [get_bd_cells -quiet {$ipName GPIO_buttons_rosetta Buttons_ORed ground_3_bits adjust_Rosetta_button_width} ]
   delete_bd_objs -quiet $objects
   set objects [get_bd_ports -quiet $ipPort ]
   delete_bd_objs -quiet $objects

   # add GPIO Rosetta buttons and configure
   create_bd_cell -type ip -vlnv [latestVersion xilinx.com:ip:axi_gpio:2.0] $ipName
   if {$isVCK190} {
      set_property -dict [list CONFIG.C_GPIO_WIDTH {2} CONFIG.C_ALL_INPUTS {1}] [get_bd_cells $ipName];
   } elseif {$isZCU104} {
      # 4 button rosetta configuration
     set_property -dict [list CONFIG.C_GPIO_WIDTH {4} CONFIG.C_ALL_INPUTS {1}] [get_bd_cells $ipName];
   } else {
      # full 5 button rosetta configuration
      set_property -dict [list CONFIG.C_GPIO_WIDTH {5} CONFIG.C_ALL_INPUTS {1}] [get_bd_cells $ipName];
   }

   # connect to the appropriate processor
   set isUBlaze [strsame $processor Microblaze];
   if {$isUBlaze} {
      if {$verbose} { puts "@@@  completer_helper - running automation for $ipName - slave AXI connection in MicroBlaze context" }
      apply_bd_automation -rule xilinx.com:bd_rule:axi4 -config {Master "/microblaze_0 (Periph)" Clk "Auto" }  [get_bd_intf_pins $ipName/S_AXI]
      #apply_bd_automation -rule xilinx.com:bd_rule:axi4 -config {Master "/microblaze_0 (Periph)" Clk "Auto" }  [get_bd_intf_pins GPIO_buttons_rosetta/S_AXI]
   } elseif {$isVCK190} {
      apply_bd_automation -rule xilinx.com:bd_rule:axi4 -config { Clk_master {/versal_cips_0/fpd_cci_noc_axi0_clk (824 MHz)} Clk_slave {Auto} Clk_xbar {Auto} Master {/versal_cips_0/FPD_CCI_NOC_0} Slave {/GPIO_buttons_rosetta/S_AXI} ddr_seg {Auto} intc_ip {/axi_noc_0} master_apm {0}}  [get_bd_intf_pins GPIO_buttons_rosetta/S_AXI]
   } elseif {$isZCU104} {
      if {$verbose} { puts "@@@  completer_helper - running automation for $ipName - slave AXI connection in PS context" }
      apply_bd_automation -rule xilinx.com:bd_rule:axi4 -config { Clk_master {/zynq_ultra_ps_e_0/pl_clk0 (96 MHz)} Clk_slave {/zynq_ultra_ps_e_0/pl_clk0 (96 MHz)} Clk_xbar {/zynq_ultra_ps_e_0/pl_clk0 (96 MHz)} Master {/zynq_ultra_ps_e_0/M_AXI_HPM0_FPD} Slave {/GPIO_buttons_rosetta/S_AXI} intc_ip {/ps8_0_axi_periph} master_apm {0}}  [get_bd_intf_pins GPIO_buttons_rosetta/S_AXI]
   } else {
      if {$verbose} { puts "@@@  completer_helper - running automation for $ipName - slave AXI connection in PS context" }
      apply_bd_automation -rule xilinx.com:bd_rule:axi4 -config { Clk_master {/processing_system7_0/FCLK_CLK0 (100 MHz)} Clk_slave {Auto} Clk_xbar {/processing_system7_0/FCLK_CLK0 (100 MHz)} Master {/processing_system7_0/M_AXI_GP0} Slave {/GPIO_buttons_rosetta/S_AXI} ddr_seg {Auto} intc_ip {/ps7_0_axi_periph} master_apm {0}}  [get_bd_intf_pins GPIO_buttons_rosetta/S_AXI]
      apply_bd_automation -rule xilinx.com:bd_rule:axi4 -config {Master "/processing_system7_0/M_AXI_GP0" Clk "Auto" }  [get_bd_intf_pins $ipName/S_AXI]
   }

   # Connect signals to board wherever possible - ZC702 only has north and south, Zed has all 5 buttons available
   if {$isVCK190} {
      # since input is available from the VIO and the board, an OR is required
      create_bd_cell -type ip -vlnv [latestVersion xilinx.com:ip:util_vector_logic:2.0] Buttons_ORed
      set_property -dict [list CONFIG.C_OPERATION {or} CONFIG.LOGO_FILE {data/sym_orgate.png} CONFIG.C_SIZE {2}] [get_bd_cells Buttons_ORed]

      # create a board port and connect to the concatonation block and then on to the OR block
      create_bd_port -dir I -from 1 -to 0 $ipPort
      connect_bd_net [get_bd_ports buttons_rosetta] [get_bd_pins Buttons_ORed/Op1]
      connect_bd_net [get_bd_pins vio_fmcce/probe_out1] [get_bd_pins Buttons_ORed/Op2]

      # connect the output of the OR to the GPIO
      connect_bd_net [get_bd_pins $ipName/gpio_io_i] [get_bd_pins Buttons_ORed/Res];
   } elseif {$isZCU104} {
      # since input is available from the VIO and the board, an OR is required
      create_bd_cell -type ip -vlnv [latestVersion xilinx.com:ip:util_vector_logic:2.0] Buttons_ORed
      set_property -dict [list CONFIG.C_OPERATION {or} CONFIG.LOGO_FILE {data/sym_orgate.png} CONFIG.C_SIZE {4}] [get_bd_cells Buttons_ORed]

      # create a board port and connect to the concatonation block and then on to the OR block
      create_bd_port -dir I -from 3 -to 0 $ipPort
      connect_bd_net [get_bd_ports buttons_rosetta] [get_bd_pins Buttons_ORed/Op1]
      connect_bd_net [get_bd_pins vio_fmcce/probe_out1] [get_bd_pins Buttons_ORed/Op2]

      # connect the output of the OR to the GPIO
      connect_bd_net [get_bd_pins $ipName/gpio_io_i] [get_bd_pins Buttons_ORed/Res];

   } elseif {$isZC702} {
      # since input is available from the VIO and the board, an OR is required
      create_bd_cell -type ip -vlnv [latestVersion xilinx.com:ip:util_vector_logic:2.0] Buttons_ORed
      set_property -dict [list CONFIG.C_OPERATION {or} CONFIG.LOGO_FILE {data/sym_orgate.png} CONFIG.C_SIZE {5}] [get_bd_cells Buttons_ORed]

      # since this board lacks a number of buttons,  the existing buttons have to be buffered to properly match the input of the OR gate
      create_bd_cell -type ip -vlnv [latestVersion xilinx.com:ip:xlconcat:2.1] adjust_Rosetta_button_width
      create_bd_cell -type ip -vlnv [latestVersion xilinx.com:ip:xlconstant:1.1] ground_3_bits
      set_property -dict [list CONFIG.CONST_WIDTH {3} CONFIG.CONST_VAL {0}] [get_bd_cells ground_3_bits]

      # create a board port and connect to the concatonation block and then on to the OR block
      create_bd_port -dir I -from 1 -to 0 $ipPort
      connect_bd_net [get_bd_pins /adjust_Rosetta_button_width/In0] [get_bd_ports $ipPort]
      connect_bd_net [get_bd_pins ground_3_bits/dout] [get_bd_pins adjust_Rosetta_button_width/In1]
      connect_bd_net [get_bd_pins adjust_Rosetta_button_width/dout] [get_bd_pins Buttons_ORed/Op1]

      # connect the VIO to the other input to the OR logic cell
      connect_bd_net [get_bd_pins vio_fmcce/probe_out1] [get_bd_pins Buttons_ORed/Op2]

      # connect the output of the OR to the GPIO
      connect_bd_net [get_bd_pins $ipName/gpio_io_i] [get_bd_pins Buttons_ORed/Res]

   } elseif {$isZed} {
      # since input is available from the VIO and the board, an OR is required
      create_bd_cell -type ip -vlnv [latestVersion xilinx.com:ip:util_vector_logic:2.0] Buttons_ORed
      set_property -dict [list CONFIG.C_OPERATION {or} CONFIG.LOGO_FILE {data/sym_orgate.png} CONFIG.C_SIZE {5}] [get_bd_cells Buttons_ORed]

      # create a board port and connect to the concatonation block and then on to the OR block
      create_bd_port -dir I -from 4 -to 0 $ipPort
      connect_bd_net [get_bd_pins $ipPort] [get_bd_pins Buttons_ORed/Op1]

      # connect the VIO to the OR logic cell
      connect_bd_net [get_bd_pins vio_fmcce/probe_out1] [get_bd_pins Buttons_ORed/Op2]

      # connect the output of the OR to the GPIO
      connect_bd_net [get_bd_pins $ipName/gpio_io_i] [get_bd_pins Buttons_ORed/Res]

   } else {
      # future - if a board completely lacks these buttons then it can be routed directly to the VIO and GPIO
   }
}
lappend loadedProcs {buttonsRosettaAdd "adds the GPIO as an input device for the rosettas buttons"};
#
# *** LEDs Rosetta
#
##
# proc:  LEDsRosettaAdd
# descr: assembles the drive to the LEDs Rosetta. The Rosetta can be driven from the GPIO's output port and monitored from the VIO's probe_in2 port
# @remark <list of searchable terms>
#
proc LEDsRosettaAdd { } {
   variable platform
   variable processor
   variable tcName
   variable verbose
   if {$verbose} { puts "@@@  completer_helper - completer_helper.LEDsRosettaAdd"; }

   set isVCK190  [strsame $platform VCK190];
   set isACAP    [expr $isVCK190];
   set isUBlaze  [strsame $processor MicroBlaze];
   set isZed     [strsame $platform ZED];
   set isZC702   [strsame $platform ZC702];
   set isZ7000   [expr $isZed || $isZC702];
   set isZCU102  [strsame $platform ZCU102];
   set isZCU104  [strsame $platform ZCU104];
   set isZCU111  [strsame $platform ZCU111];
   set isMPSoC   [expr $isZCU102 || $isZCU104];
   set isRFSoC   $isZCU111;
   set ipName GPIO_LEDs_rosetta;
   set ipPort LEDs_rosetta;

   # remove the device if it already exists
   set objects [get_bd_cells -quiet $ipName ]
   delete_bd_objs -quiet $objects
   set objects [get_bd_ports -quiet $ipPort ]
   delete_bd_objs -quiet $objects

   # add GPIO Rosetta LEDs and configure
   create_bd_cell -type ip -vlnv [latestVersion xilinx.com:ip:axi_gpio:2.0] $ipName;
   if {$isZCU104 || $isVCK190} {
      # only has 4 elements in the rosetta
      set_property -dict [list CONFIG.C_GPIO_WIDTH {4} CONFIG.C_ALL_OUTPUTS {1}] [get_bd_cells $ipName];
   } else {
      # boards with full 5 element rosettas
      set_property -dict [list CONFIG.C_GPIO_WIDTH {5} CONFIG.C_ALL_OUTPUTS {1}] [get_bd_cells $ipName];
   }

   # connect to the appropraite processor
   set isUBlaze [strsame $processor MicroBlaze];
   if {$isUBlaze} {
      apply_bd_automation -rule xilinx.com:bd_rule:axi4 -config {Master "/microblaze_0 (Periph)" Clk "Auto" }  [get_bd_intf_pins $ipName/S_AXI];
   } elseif {$isVCK190} {
      apply_bd_automation -rule xilinx.com:bd_rule:axi4 -config { Clk_master {/versal_cips_0/fpd_cci_noc_axi0_clk (824 MHz)} Clk_slave {Auto} Clk_xbar {Auto} Master {/versal_cips_0/FPD_CCI_NOC_0} Slave {/GPIO_LEDs_rosetta/S_AXI} ddr_seg {Auto} intc_ip {/axi_noc_0} master_apm {0}}  [get_bd_intf_pins GPIO_LEDs_rosetta/S_AXI];
   } elseif {$isZCU104} {
      apply_bd_automation -rule xilinx.com:bd_rule:axi4 -config { Clk_master {/zynq_ultra_ps_e_0/pl_clk0 (96 MHz)} Clk_slave {/zynq_ultra_ps_e_0/pl_clk0 (96 MHz)} Clk_xbar {/zynq_ultra_ps_e_0/pl_clk0 (96 MHz)} Master {/zynq_ultra_ps_e_0/M_AXI_HPM0_FPD} Slave {/GPIO_LEDs_rosetta/S_AXI} intc_ip {/ps8_0_axi_periph} master_apm {0}}  [get_bd_intf_pins GPIO_LEDs_rosetta/S_AXI]
   } else {
      # Z7000
      apply_bd_automation -rule xilinx.com:bd_rule:axi4 -config { Clk_master {/processing_system7_0/FCLK_CLK0 (100 MHz)} Clk_slave {Auto} Clk_xbar {/processing_system7_0/FCLK_CLK0 (100 MHz)} Master {/processing_system7_0/M_AXI_GP0} Slave {/GPIO_LEDs_rosetta/S_AXI} ddr_seg {Auto} intc_ip {/ps7_0_axi_periph} master_apm {0}}  [get_bd_intf_pins GPIO_LEDs_rosetta/S_AXI]
   }

   #
   #  Connect signals to board wherever possible - both the ZC702 and Zed boards completely lack the rosetta LEDs therefore there is no port and connects directly to the VIO
   if {$isVCK190} {
      make_bd_pins_external  [get_bd_pins $ipName/gpio_io_o];   # connect to the output pins
      set_property name LEDs_rosetta [get_bd_ports gpio_io_o_0];          # change the name to be compliant with the IO naming convetion for this design
      connect_bd_net [get_bd_pins vio_fmcce/probe_in2] [get_bd_pins $ipName/gpio_io_o];   # tie to the VIO
   } elseif {$isZCU104} {
      make_bd_pins_external  [get_bd_pins $ipName/gpio_io_o];   # connect to the output pins
      set_property name LEDs_rosetta [get_bd_ports gpio_io_o_0];          # change the name to be compliant with the IO naming convetion for this design
      connect_bd_net [get_bd_pins vio_fmcce/probe_in2] [get_bd_pins $ipName/gpio_io_o];   # tie to the VIO
   } elseif {$isZC702} {
      connect_bd_net [get_bd_pins vio_fmcce/probe_in2] [get_bd_pins $ipName/gpio_io_o]
   } elseif {$isZed} {
      connect_bd_net [get_bd_pins vio_fmcce/probe_in2] [get_bd_pins $ipName/gpio_io_o]
   }
}
lappend loadedProcs {LEDsRosettaAdd "adds the GPIO as an output device for the rosettas LEDs"};
#
# *** Switches Linear
#
##
# proc:  switchesLinearAdd
# descr:
# @remark <list of searchable terms>
#
proc switchesLinearAdd { } {
   variable platform;
   variable processor;
   variable tcName;
   variable verbose;
   if {$verbose} { puts "@@@  completer_helper - completer_helper.switchesLinearAdd"; }

   set isVCK190  [strsame $platform VCK190];
   set isACAP    [expr $isVCK190];
   set isUBlaze  [strsame $processor MicroBlaze];
   set isZed     [strsame $platform ZED];
   set isZC702   [strsame $platform ZC702];
   set isZ7000   [expr $isZed || $isZC702];
   set isZCU102  [strsame $platform ZCU102];
   set isZCU104  [strsame $platform ZCU104];
   set isZCU111  [strsame $platform ZCU111];
   set isMPSoC   [expr $isZCU102 || $isZCU104];
   set isRFSoC   $isZCU111;
   set ipName GPIO_switches_linear;
   set ipPort switches_linear;

   # remove the device if it already exists
   set objects [get_bd_cells -quiet {*switches*} ];
   delete_bd_objs -quiet $objects;
   set objects [get_bd_ports -quiet $ipPort ];
   delete_bd_objs -quiet $objects;

   # add GPIO Linear Switches and configure
   create_bd_cell -type ip -vlnv [latestVersion xilinx.com:ip:axi_gpio:2.0] $ipName;
   if {$isZCU104 || $isACAP} {
      # only has four user DIP switches
      set_property -dict [list CONFIG.C_GPIO_WIDTH {4} CONFIG.C_ALL_INPUTS {1}] [get_bd_cells $ipName];
   } else {
      set_property -dict [list CONFIG.C_GPIO_WIDTH {8} CONFIG.C_ALL_INPUTS {1}] [get_bd_cells $ipName];
   }

   # connect to the appropraite processor using the connection automation
   set isUBlaze [strsame $processor MicroBlaze]
   if {$isUBlaze} {
      apply_bd_automation -rule xilinx.com:bd_rule:axi4 -config {Master "/microblaze_0 (Periph)" Clk "Auto" }  [get_bd_intf_pins $ipName/S_AXI]
   } elseif {$isACAP} {
      apply_bd_automation -rule xilinx.com:bd_rule:axi4 -config { Clk_master {/versal_cips_0/fpd_cci_noc_axi0_clk (824 MHz)} Clk_slave {Auto} Clk_xbar {Auto} Master {/versal_cips_0/FPD_CCI_NOC_0} Slave {/GPIO_switches_linear/S_AXI} ddr_seg {Auto} intc_ip {/axi_noc_0} master_apm {0}}  [get_bd_intf_pins GPIO_switches_linear/S_AXI];
   } elseif {$isZ7000} {
      #apply_bd_automation -rule xilinx.com:bd_rule:board -config { Board_Interface {gpio_sw ( DIP switches ) } Manual_Source {Auto}}  [get_bd_intf_pins GPIO_switches_linear/GPIO]
      apply_bd_automation -rule xilinx.com:bd_rule:axi4 -config { Clk_master {Auto} Clk_slave {Auto} Clk_xbar {Auto} Master {/processing_system7_0/M_AXI_GP0} Slave {/GPIO_switches_linear/S_AXI} ddr_seg {Auto} intc_ip {New AXI Interconnect} master_apm {0}}  [get_bd_intf_pins GPIO_switches_linear/S_AXI]
     #apply_bd_automation -rule xilinx.com:bd_rule:axi4 -config { Clk_master {Auto} Clk_slave {Auto} Clk_xbar {Auto} Master {/processing_system7_0/M_AXI_GP0} Slave {/GPIO_switches_linear/S_AXI} ddr_seg {Auto} intc_ip {New AXI Interconnect} master_apm {0}}  [get_bd_intf_pins GPIO_switches_linear/S_AXI]
   } elseif {$isMPSoC} {
      apply_bd_automation -rule xilinx.com:bd_rule:axi4 -config { Clk_master {Auto} Clk_slave {Auto} Clk_xbar {Auto} Master {/zynq_ultra_ps_e_0/M_AXI_HPM0_FPD} Slave {/GPIO_switches_linear/S_AXI} intc_ip {New AXI Interconnect} master_apm {0}}  [get_bd_intf_pins GPIO_switches_linear/S_AXI]
   } else {
      puts "@@@  completer_helper - completer_helper: switchesLinearAdd: Undefined platform: $platform";
   }

   #
   # Connect signals to board wherever possible - ZC702 lacks the linear switches; Zed boards has the full set of 8 switches
   puts "@@@  completer_helper - Now attempting to connect the GPIO to the linear switches...";
   if {$isVCK190} {
      create_bd_port -dir I -from 3 -to 0 $ipPort; # 4 dip switches are avilable

      # create an or gate so that the input to the GPIO can be from the VIO or DIP switches
      create_bd_cell -type ip -vlnv [latestVersion xilinx.com:ip:util_vector_logic:2.0] Switches_ORed
      set_property -dict [list CONFIG.C_OPERATION {or} CONFIG.LOGO_FILE {data/sym_orgate.png}] [get_bd_cells Switches_ORed]
      set_property -dict [list CONFIG.C_SIZE {4}] [get_bd_cells Switches_ORed]

      connect_bd_net [get_bd_ports switches_linear] [get_bd_pins Switches_ORed/Op1];                # connect from the input port to the OR gate
      connect_bd_net [get_bd_pins Switches_ORed/Op2] [get_bd_pins vio_fmcce/probe_out2];            # connect to the VIO
      connect_bd_net [get_bd_pins Switches_ORed/Res] [get_bd_pins $ipName/gpio_io_i];               # connect from the OR gate to the GPIO
   } elseif {$isZCU104} {
      # 4 dip switches are avilable
      create_bd_port -dir I -from 3 -to 0 $ipPort;

      # create an or gate so that the input to the GPIO can be from the VIO or DIP switches
      create_bd_cell -type ip -vlnv [latestVersion xilinx.com:ip:util_vector_logic:2.0] Switches_ORed;
      set_property -dict [list CONFIG.C_OPERATION {or} CONFIG.LOGO_FILE {data/sym_orgate.png}] [get_bd_cells Switches_ORed];
      set_property -dict [list CONFIG.C_SIZE {4}] [get_bd_cells Switches_ORed];

      connect_bd_net [get_bd_ports switches_linear] [get_bd_pins Switches_ORed/Op1];                # connect from the input port to the OR gate
      connect_bd_net [get_bd_pins Switches_ORed/Op2] [get_bd_pins vio_fmcce/probe_out2];            # connect to the VIO
      connect_bd_net [get_bd_pins Switches_ORed/Res] [get_bd_pins $ipName/gpio_io_i];               # connect from the OR gate to the GPIO

   } elseif {$isZC702} {
      # no board connection
      puts "@@@  completer_helper - ZC702 does not have any linear switches, therefore, no ports will be created and the GPIO will be connected directly to the VIO";
      connect_bd_net [get_bd_pins vio_fmcce/probe_out2] [get_bd_pins $ipName/gpio_io_i];

   } elseif {$isZed} {
      puts "@@@  completer_helper - Zed board has full complement of switches and will be combined via an OR with the VIO's inputs";
      create_bd_port -dir I -from 7 -to 0 $ipPort;
      create_bd_cell -type ip -vlnv [latestVersion xilinx.com:ip:util_vector_logic:2.0] Switches_ORed;
      set_property -dict [list CONFIG.C_OPERATION {or} CONFIG.LOGO_FILE {data/sym_orgate.png}] [get_bd_cells Switches_ORed];

     # connect the OR gate to the port and to the VIO (which already have been instantiated)
     connect_bd_net [get_bd_ports switches_linear] [get_bd_pins Switches_ORed/Op1];
     connect_bd_net [get_bd_pins vio_fmcce/probe_out2] [get_bd_pins Switches_ORed/Op2];

     # connect the OR to the input of the GPIO
     connect_bd_net [get_bd_pins Switches_ORed/Res] [get_bd_pins $ipName/gpio_io_i];
   }
}
lappend loadedProcs {switchesLinearAdd "adds the GPIO as an input device for the linear switches"};
#
# *** UARTaddConditional
#   adds the UART only if the processor is a MicroBlaze
##
# proc:  UARTaddConditional
# descr:
# @remark <list of searchable terms>
#
proc UARTaddConditional {} {
   variable verbose
   if {$verbose} { puts "@@@  completer_helper - completer_helper.UARTaddConditional"; }
   variable processor
   if {[strsame $processor MicroBlaze]} {
      UARTadd
   } else {
      puts "@@@  completer_helper - Not adding UART as there are UARTs in the PS";
   }
}
lappend loadedProcs {UARTaddConditional "adds a PL UART to the block design"};
#
# *** UART
##
# proc:  UARTadd
# descr:
# @remark <list of searchable terms>
#
proc UARTadd {} {
   variable platform
   variable processor
   variable tcName
   variable verbose
   variable suppressUARTinterrupt;     # there might be interrupts in the system, but the UART should connect to one
   variable suppressInterrupts;        # no interrupts in the system therefore the UART shouldn't generate one
   if {![info exists suppressUARTinterrupt]} { set suppressUARTinterrupt 0; }
   if {![info exists suppressInterrupts]}    { set suppressInterrupts 0; }
   variable UARTdebug;                 # generates an ILA for monitoring the Rx/Tx lines of the UART
   if {![info exists UARTdebug]}    { set UARTdebug 0; }

   set ipName UART

   if {$verbose} { puts "@@@  completer_helper - completer_helper.UARTadd"; }

   # remove the device if it already exists
   set objects [get_bd_cells -quiet *UART*]
   delete_bd_objs -quiet $objects
   delete_bd_objs [get_bd_nets -quiet {*UART* *Rx*}]

   if {[strsame $processor MicroBlaze]} {
      # add the UART itself
      create_bd_cell -type ip -vlnv [latestVersion xilinx.com:ip:axi_uartlite:2.0] $ipName
      set_property -dict [list CONFIG.C_BAUDRATE {115200}] [get_bd_cells $ipName]
      if {$suppressUARTinterrupt || $suppressInterrupts} {
         puts "@@@  completer_helper - Microblaze processor found - adding UART without interrupt";
         # UART interrupt pin left hanging, input to interrupt controller is tied to ground - but only if there are system interrupts
         if {!$suppressInterrupts} {
           # LR moved the constant creation to processorConfig
           # create_bd_cell -type ip -vlnv [latestVersion xilinx.com:ip:xlconstant:1.1] UARTintr_gnd
           # set_property -dict [list CONFIG.CONST_VAL {0}] [get_bd_cells UARTintr_gnd]
            connect_bd_net [get_bd_pins microblaze_0_xlconcat/In0] [get_bd_pins UARTintr_gnd/dout];
         }
      } else {
         puts "@@@  completer_helper - Microblaze processor found - adding UART with interrupt";
         connect_bd_net [get_bd_pins microblaze_0_xlconcat/In0] [get_bd_pins UART/interrupt];   # Added by LR 2/24/2017
      }

      # if in debug mode, add an ILA to the Rx/Tx
      if {$UARTdebug} {
         if {$verbose} { puts "@@@  completer_helper - completer_helper.UARTadd - adding debug ILA for the UART"; }

         # add and configure the System ILA for the UART
         create_bd_cell -type ip -vlnv [latestVersion xilinx.com:ip:system_ila:1.1] UART_ILA
         set_property -dict [list CONFIG.C_BRAM_CNT {7.5} CONFIG.C_DATA_DEPTH {131072} CONFIG.C_NUM_OF_PROBES {2} CONFIG.C_ADV_TRIGGER {true} CONFIG.C_MON_TYPE {NATIVE}] [get_bd_cells UART_ILA]

         # remove the interface connection and replace with individual wires
         connect_bd_net [get_bd_pins PS_access/Rx] [get_bd_pins UART/rx]
         connect_bd_net [get_bd_pins UART_ILA/probe0] [get_bd_pins PS_access/Rx]
         connect_bd_net [get_bd_pins PS_access/Tx] [get_bd_pins UART/tx]
         connect_bd_net [get_bd_pins UART_ILA/probe1] [get_bd_pins UART/tx]

         # connect the clock
         apply_bd_automation -rule xilinx.com:bd_rule:clkrst -config {Clk "/PS_access/CLK_100MHz (100 MHz)" }  [get_bd_pins UART_ILA/clk]
      } else {
         connect_bd_intf_net [get_bd_intf_pins PS_access/UART_pins] [get_bd_intf_pins UART/UART]
      }

      # finally connect the UART's clock
      apply_bd_automation -rule xilinx.com:bd_rule:axi4 -config {Master "/microblaze_0 (Periph)" intc_ip "/microblaze_0_axi_periph" Clk_xbar "Auto" Clk_master "Auto" Clk_slave "Auto" }  [get_bd_intf_pins UART/S_AXI]
   }
}
lappend loadedProcs {UARTadd "adds the UART dependent on platform"};
#
# /**
#  * proc: BRAMadd
#  * descr: adds and connects BRAMadd
#  * note:  support only for MPSoC at this time...
#  * @remark: BRAM
#  */
proc BRAMadd {} {
   variable platform;
   variable verbose;
   if {$verbose} { puts "@@@  completer_helper - completer_helper.BRAMadd"; }

   # figure out what we're dealing with...
   set isVCK190  [strsame $platform VCK190];
   set isACAP    [expr $isVCK190];
   set isUBlaze  [strsame $processor MicroBlaze];
   set isZed     [strsame $platform ZED];
   set isZC702   [strsame $platform ZC702];
   set isZ7000   [expr $isZed || $isZC702];
   set isZCU102  [strsame $platform ZCU102];
   set isZCU104  [strsame $platform ZCU104];
   set isZCU111  [strsame $platform ZCU111];
   set isMPSoC   [expr $isZCU102 || $isZCU104];
   set isRFSoC   $isZCU111;

   # remove any traces of existing BRAMs so we can make a clean start
   set objects [get_bd_cells -quiet *BRAM*];
   delete_bd_objs -quiet $objects;

   # add the BRAM controller and configure it as a single port first...
   create_bd_cell -type ip -vlnv [latestVersion xilinx.com:ip:axi_bram_ctrl:4.1] BRAMctrl;
   set_property -dict [list CONFIG.SINGLE_PORT_BRAM {1}] [get_bd_cells BRAMctrl];

   # connect the BRAM to the controller
   apply_bd_automation -rule xilinx.com:bd_rule:bram_cntlr -config {BRAM "Auto" }  [get_bd_intf_pins BRAMctrl/BRAM_PORTA];

   # connect the BRAM controller to the AXI interconnect
   if {$isACAP} {
      apply_bd_automation -rule xilinx.com:bd_rule:axi4 -config { Clk_master {/versal_cips_0/fpd_cci_noc_axi0_clk (824 MHz)} Clk_slave {Auto} Clk_xbar {Auto} Master {/versal_cips_0/FPD_CCI_NOC_0} Slave {/BRAMctrl/S_AXI} ddr_seg {Auto} intc_ip {/axi_noc_0} master_apm {0}}  [get_bd_intf_pins BRAMctrl/S_AXI]
   } elseif {$isMPSoC || $isRFSoC} {
      apply_bd_automation -rule xilinx.com:bd_rule:axi4 -config { Clk_master {/zynq_ultra_ps_e_0/pl_clk0 (100 MHz)} Clk_slave {Auto} Clk_xbar {/zynq_ultra_ps_e_0/pl_clk0 (100 MHz)} Master {/zynq_ultra_ps_e_0/M_AXI_HPM0_FPD} Slave {/BRAMctrl/S_AXI} intc_ip {/ps8_0_axi_periph} master_apm {0}}  [get_bd_intf_pins BRAMctrl/S_AXI];
   }

   markLastStep BRAMadd;
}
lappend loadedProcs {BRAMadd "adds the BRAM controller and BRAMs to the block design"};
#
# *** DDR Memory Controller (MIG) - not tested!
#
##
# proc:  MIGadd
# descr:
# @remark <list of searchable terms>
#
proc MIGadd { } {
   variable verbose;
   if {$verbose} { puts "@@@  completer_helper - completer_helper.MIGadd"; }
   variable blockDesignName;

   # add MIG only if there is a MicroBlaze processor
   if {[strsame $processor MicroBlaze]} {
      # remove default clocking wizard
      # need to remove clock port as well because board automation for MIG automatically adds another one
      # maybe in the future this will get fixed
      delete_bd_objs [get_bd_intf_nets sys_diff_clock_1] [get_bd_nets clk_wiz_1_locked] [get_bd_cells clk_wiz_1]
      delete_bd_objs [get_bd_nets reset_1]
      delete_bd_objs [get_bd_intf_ports sys_diff_clock]

      # Add MIG using board interface
      create_bd_cell -type ip -vlnv [latestVersion xilinx.com:ip:mig_7series:4.0] mig_7series_0
      apply_board_connection -board_interface "ddr3_sdram" -ip_intf "mig_7series_0/mig_ddr_interface" -diagram $blockDesignName

      # Connect reset
      connect_bd_net [get_bd_ports reset] [get_bd_pins mig_7series_0/sys_rst]
      connect_bd_net [get_bd_pins mig_7series_0/ui_clk_sync_rst] [get_bd_pins rst_clk_wiz_1_100M/ext_reset_in]

      # Connect clock signals
      connect_bd_net -net [get_bd_nets microblaze_0_Clk] [get_bd_pins mig_7series_0/ui_clk]
      connect_bd_net [get_bd_pins mig_7series_0/mmcm_locked] [get_bd_pins rst_clk_wiz_1_100M/dcm_locked]
      connect_bd_net -net [get_bd_nets rst_clk_wiz_1_100M_peripheral_aresetn] [get_bd_pins mig_7series_0/aresetn] [get_bd_pins rst_clk_wiz_1_100M/peripheral_aresetn]

      # Re-customize uB to support DDR connection
      set_property -dict [list CONFIG.C_USE_ICACHE {1} CONFIG.C_USE_DCACHE {1}] [get_bd_cells microblaze_0]
      apply_bd_automation -rule xilinx.com:bd_rule:axi4 -config {Master "/microblaze_0 (Cached)" Clk "Auto" }  [get_bd_intf_pins mig_7series_0/S_AXI]
   }
   markLastStep MIGadd;
}
lappend loadedProcs {MIGadd "adds the PL MIG to the block design"};
#
# *** regenerate board layout
#
##
# proc:  reGenLayout
# descr:
# @remark <list of searchable terms>
#
proc reGenLayout {} {
  variable verbose
  if {$verbose} { puts "@@@  completer_helper - completer_helper.reGenLayout" }
  regenerate_bd_layout
  save_bd_design

  markLastStep reGenLayout
}
lappend loadedProcs {reGenLayout "regenerate the layout"};
#
# ********** processorBlockAutomationRun
#
# - connects processor to I/O
#
##
# proc:  processorBlockAutomationRun
# descr:
# @remark <list of searchable terms>
#
proc processorBlockAutomationRun {} {
   variable processor;
   variable platform;
   variable verbose   ;
   if {$verbose} { puts "@@@  completer_helper - in processorBlockAutomationRun with processor set to: $processor"; }

   # what is it? makes comparisons below easier
   set isVCK190  [strsame $platform VCK190];
   set isACAP    [expr $isVCK190];
   set isUBlaze  [strsame $processor MicroBlaze];
   set isZed     [strsame $platform ZED];
   set isZC702   [strsame $platform ZC702];
   set isZ7000   [expr $isZed || $isZC702];
   set isZCU102  [strsame $platform ZCU102];
   set isZCU104  [strsame $platform ZCU104];
   set isZCU111  [strsame $platform ZCU111];
   set isMPSoC   [expr $isZCU102 || $isZCU104];
   set isRFSoC   $isZCU111;
   set isUBlaze [strsame $processor MicroBlaze];
   set isA72    [strsame $processor A72];
   set isA53    [strsame $processor A53];
   set isR5     [strsame $processor R5];

   if {$isUBlaze} {
      puts "@@@  completer_helper - no automation needs to be run on a microblaze only configuration";
      #**LR** (2018.3)
      puts "@@@  completer_helper - Set PS DDR RAM access to 512MB Starting at 0x20000000";
      set_property range 512M [get_bd_addr_segs {microblaze_0/Data/SEG_PS_access_reg0}];
      set_property offset 0x20000000 [get_bd_addr_segs {microblaze_0/Data/SEG_PS_access_reg0}]    ;
   } elseif {$isACAP} {
      puts "@@@  completer_helper - running automation for an ACAP device";
      # automation set to add a DDR4 memory controller, one PL clock and reset, no PCIe, and one AXI NoC for getting to the DDRMC
      #apply_bd_automation -rule xilinx.com:bd_rule:versal_cips -config { configure_noc {Add new AXI NoC} num_ddr {1} pcie0_lane_width {None} pcie0_mode {None} pcie0_port_type {Endpoint Device} pcie1_lane_width {None} pcie1_mode {None} pcie1_port_type {Endpoint Device} pl_clocks {1} pl_resets {1}}  [get_bd_cells versal_cips_0];
      apply_bd_automation -rule xilinx.com:bd_rule:cips -config { board_preset {Yes} boot_config {Custom} configure_noc {Add new AXI NoC} debug_config {JTAG} design_flow {Full System} mc_type {DDR} num_mc {1} pl_clocks {1} pl_resets {1}}  [get_bd_cells versal_cips_0];

     # apply_bd_automation -rule xilinx.com:bd_rule:cips -config { board_preset {Yes} boot_config {Custom} configure_noc {Add new AXI NoC} debug_config {JTAG} design_flow {Full System} mc_type {DDR} num_mc {1} pl_clocks {1} pl_resets {1}}  [get_bd_cells versal_cips_0]
     # tie in the clocks
     set_property -dict [list CONFIG.MC_BOARD_INTRF_EN {true} CONFIG.MC0_CONFIG_NUM {config17} CONFIG.MC1_CONFIG_NUM {config17} CONFIG.MC2_CONFIG_NUM {config17} CONFIG.MC3_CONFIG_NUM {config17} CONFIG.CH0_DDR4_0_BOARD_INTERFACE {ddr4_dimm1} CONFIG.sys_clk0_BOARD_INTERFACE {ddr4_dimm1_sma_clk} CONFIG.MC_INPUT_FREQUENCY0 {200.000} CONFIG.MC_INPUTCLK0_PERIOD {5000} CONFIG.MC_MEMORY_DEVICETYPE {UDIMMs} CONFIG.MC_MEMORY_SPEEDGRADE {DDR4-3200AA(22-22-22)} CONFIG.MC_TRCD {13750} CONFIG.MC_TRP {13750} CONFIG.MC_DDR4_2T {Disable} CONFIG.MC_CASLATENCY {22} CONFIG.MC_TRC {45750} CONFIG.MC_TRPMIN {13750} CONFIG.MC_CONFIG_NUM {config17} CONFIG.MC_F1_TRCD {13750} CONFIG.MC_F1_TRCDMIN {13750} CONFIG.MC_F1_LPDDR4_MR1 {0x000} CONFIG.MC_F1_LPDDR4_MR2 {0x000} CONFIG.MC_F1_LPDDR4_MR3 {0x000} CONFIG.MC_F1_LPDDR4_MR11 {0x000} CONFIG.MC_F1_LPDDR4_MR13 {0x000} CONFIG.MC_F1_LPDDR4_MR22 {0x000}] [get_bd_cells axi_noc_0];
     set_property -dict [list CONFIG.NUM_MI {1}] [get_bd_cells axi_noc_0];
     set_property -dict [list CONFIG.ASSOCIATED_BUSIF {S03_AXI:S02_AXI:S00_AXI:M00_AXI:S01_AXI:S04_AXI:S05_AXI}] [get_bd_pins /axi_noc_0/aclk0];
     set_property -dict [list CONFIG.ASSOCIATED_BUSIF {}] [get_bd_pins /axi_noc_0/aclk1];
     set_property -dict [list CONFIG.ASSOCIATED_BUSIF {}] [get_bd_pins /axi_noc_0/aclk2];
     set_property -dict [list CONFIG.ASSOCIATED_BUSIF {}] [get_bd_pins /axi_noc_0/aclk3];
     set_property -dict [list CONFIG.ASSOCIATED_BUSIF {}] [get_bd_pins /axi_noc_0/aclk4];
     set_property -dict [list CONFIG.ASSOCIATED_BUSIF {}] [get_bd_pins /axi_noc_0/aclk5];
   } elseif {$isZ7000} {
      puts "@@@  completer_helper - running automation on a Zynq-7000";
      apply_bd_automation -rule xilinx.com:bd_rule:processing_system7 -config {make_external "FIXED_IO, DDR" apply_board_preset "0" Master "Disable" Slave "Disable" }  [get_bd_cells processing_system7_0]
   } elseif {$isMPSoC} {
      # no action at this point because this appears to be taking place with the processorConfigure proc
   } else {
      puts "@@@  completer_helper - Undefined or unsupported processor for blockAutomation";
   }

   markLastStep processorBlockAutomationRun
}
lappend loadedProcs {processorBlockAutomationRun "run block automation on the PS"};
##
# proc:  blockDesignWrap
# descr:
# @remark <list of searchable terms>
# @history:
#    2023-02-06 - WK - removed excess "append _wrapper"
#
proc blockDesignWrap {} {
   variable tcName;
   variable trainingPath;
   variable labName;
   variable language;
   variable blockDesignName;
   variable demoOrLab;
   variable verbose;
   if {$verbose} { puts "@@@  completer_helper - completer_helper.blockDesignWrap" };

   save_bd_design;
   close_bd_design [get_bd_designs $blockDesignName];

   # Reset block diagram output products and re-generate
   reset_target all [get_files  $trainingPath/$tcName/$demoOrLab/$labName.srcs/sources_1/bd/$blockDesignName/$blockDesignName.bd];
   generate_target all [get_files  $trainingPath/$tcName/$demoOrLab/$labName.srcs/sources_1/bd/$blockDesignName/$blockDesignName.bd];
   export_ip_user_files -of_objects [get_files $trainingPath/$tcName/$demoOrLab/$labName.srcs/sources_1/bd/$blockDesignName/$blockDesignName.bd] -no_script -sync -force -quiet;
   create_ip_run [get_files -of_objects [get_fileset sources_1] $trainingPath/$tcName/$demoOrLab/$labName.srcs/sources_1/bd/$blockDesignName/$blockDesignName.bd];

   # is this really necessary? will we ever get this far without having defined the language?
   if {[strsame $language undefined]} {
      puts "@@@  completer_helper - Please select VHDL or Verilog with the \"use\" proc"
      return
   }

   set fullPath ""
   append fullPath $trainingPath/$tcName/$demoOrLab/$labName.srcs/sources_1/bd/$blockDesignName/$blockDesignName.bd;
   make_wrapper -files [get_files $fullPath] -top;

   set fullPath ""
   if {[string compare -nocase [getLanguage] verilog] == 0} {
      append fullPath $trainingPath/$tcName/$demoOrLab/$tcName.srcs/sources_1/bd/$blockDesignName/hdl/$blockDesignName\_wrapper.v;
   } else {
      append fullPath $trainingPath/$tcName/$demoOrLab/$labName.srcs/sources_1/bd/$blockDesignName/hdl/$blockDesignName\_wrapper.vhd;
   }
   add_files -norecurse $fullPath;
   update_compile_order -fileset sources_1;
   update_compile_order -fileset sim_1;

   markLastStep blockDesignWrap
}
lappend loadedProcs {blockDesignWrap "creates an HDL wrapper for the block design"};
##
# proc:  simulationRun
# descr:
# @remark <list of searchable terms>
#
proc simulationRun {} {
   variable verbose
   if {$verbose} { puts "@@@  completer_helper - completer_helper.simulationRun"; }

   launch_simulation;

   markLastStep simulationRun;
}
lappend loadedProcs {simulationRun "runs the simulation"};

##
# proc: simulationClose
proc simulationClose {} {
   variable verbose
   if {$verbose} { puts "@@@  completer_helper - completer_helper.simulationClose"; }
   close_sim;
}
##
# proc:  synthesisRun
# descr:
# @remark <list of searchable terms>
#
proc synthesisRun {} {
   variable verbose
   if {$verbose} { puts "@@@  completer_helper.synthesisRun - starting"; }

   set start [clock seconds];

   reset_run synth_1
   launch_runs synth_1 -jobs 4
   if {$verbose} { puts "@@@  completer_helper.synthesisRun - waiting for synthesis to complete"; }
   wait_on_run synth_1
   open_run synth_1 -name synth_1

   set finish [clock seconds];

   set duration [expr $finish - $start];
   if {$verbose} { puts "@@@  completer_helper.synthesisRun - \tsynthesis took $duration seconds to complete"; }
   return $duration;
   # markLastStep synthesisRun
}
lappend loadedProcs {synthesisRun "runs synthesis"};
##
# proc:  implementationRun
# descr:
# @remark <list of searchable terms>
#
proc implementationRun {} {
   variable verbose
   if {$verbose} { puts "@@@  completer_helper.implementationRun"; }

   set start [clock seconds];

   #reset_run synth_1
   launch_runs impl_1 -jobs [numberOfCPUs]
   if {$verbose} { puts "@@@  completer_helper.implementationRun - waiting for implementation to complete"; }
   wait_on_run impl_1
   open_run impl_1

   set finish [clock seconds];

   set duration [expr $finish - $start];
   if {$verbose} {
      if {$duration > 120} {
         set hours [expr int($duration / 3600)];
         set minutes [expr int(($duration - ($hours * 3600))/60)];
         if {$minutes < 10} { set sMinutes 0; append sMinutes $minutes; } else { set sMinutes $minutes}
         set seconds [expr int($duration - ($hours * 3600) - ($minutes * 60))];
         if {$seconds < 10} { set sSeconds 0; append sSeconds $seconds; } else { set sSeconds $seconds}
         puts "@@@ completer_helper.implementationRun - implementation took $hours:$sMinutes:$sSeconds to complete";
      } else {
         puts "@@@  completer_helper.implementationRun - implementation took $duration seconds to complete";
      }
   }
   return $duration;
#   markLastStep implementationRun
}
lappend loadedProcs {implenentationRun "run the design through implementation"};
##
# proc: imageGeneration
#
proc imageGeneration {} {
    variable verbose;
    if {$verbose} { puts "@@@ NoCddr_completer.tcl:hardwareFinalization" };
	variable trainingPath;
	
	# strangely enough, this seems to re-run implementation. Can we just run this here instead of running implementation first?
	launch_runs impl_1 -to_step write_device_image -jobs 16;
	
	# xport the xsa file
	write_hw_platform -fixed -include_bit -force -file $trainingPath/NoCddr/lab/NoCddr.xsa
}
lappend loadedProcs {imageGeneration "generate a device image"};
##
# proc:  bitstreamRun
# descr:
# @remark <list of searchable terms>
#
proc bitstreamRun {} {
    variable verbose
    if {$verbose} { puts "@@@  completer_helper.bitstreamRun"; }

    set start [clock seconds];

    # test if there is already a bitstream
    set status [get_property status [current_run]]
    set implementationCompleteMessage "@@@  completer_helper.bitstream - Runroute_design Complete!"
    set bitstreamCompleteMessage      "@@@  completer_helper.bitstream - write_bitstream Complete!"
    set bitstreamErrorMessage         "@@@  completer_helper.bitstream - write_bitstream ERROR"

    if {[strsame $status $bitstreamErrorMessage]} {
       errorMsg "@@@  completer_helper.bitstream - Error writing bitstream"
    }

	# how many CPUs are available?
	set nCPUs [numberOfCPUs];

    if {![strsame $status $bitstreamCompleteMessage]} {
        reset_run impl_1 -prev_step 
        launch_runs impl_1 -to_step write_bitstream -jobs $nCPUs
        if {$verbose} { infoMsg "@@@  completer_helper.bitstream - waiting for bitstream generation to complete"; }
        wait_on_run impl_1
    } else {
        infoMsg "@@@  completer_helper.bitstream - Bitstream has already been run!"
    }
    set finish [clock seconds];

   set duration [expr $finish - $start];
    if {$verbose} { puts "@@@  completer_helper.bitstream - bitstream generation took $duration seconds to complete"; }
    return $duration;
}
lappend loadedProcs {bitstreamRun "@@@  completer_helper.bitstream - builds the bitstream"};
##
# proc:  hardwareManagerOpen
# descr: current limitations: only works for ZC702 and Zed boards due to part selection
# @remark <list of searchable terms>
#
proc hardwareManagerOpen {} {
   variable tcName
   variable trainingPath
   variable blockDesignName
   variable verbose
   if {$verbose} { puts "@@@  completer_helper - completer_helper.hardwareManagerOpen"; }

   open_hw;             # open the hardware manager
   connect_hw_server;   #
   open_hw_target;      #
   set_property PROGRAM.FILE {$trainingPath/$tcName/lab/$tcName.runs/impl_1/$blockDesignName.bit} [get_hw_devices xc7z020_1]
   set_property PROBES.FILE {$trainingPath/$tcName/lab/$tcName.runs/impl_1/$blockDesignName.ltx} [get_hw_devices xc7z020_1]
   set_property FULL_PROBES.FILE {$trainingPath/$tcName/lab/$tcName.runs/impl_1/$blockDesignName.ltx} [get_hw_devices xc7z020_1]
   current_hw_device [get_hw_devices xc7z020_1]
   refresh_hw_device [lindex [get_hw_devices xc7z020_1] 0]
   display_hw_ila_data [ get_hw_ila_data hw_ila_data_1 -of_objects [get_hw_ilas -of_objects [get_hw_devices xc7z020_1] -filter {CELL_NAME=~"embd_dsgn_i/ila_0"}]]

   markLastStep hardwareManagerOpen
}
lappend loadedProcs {hardwareManagerOpen "open the hardware manager"};

##
# proc:  designExport (local to project)
# descr: exports ths hardware and bitstream to $trainingPath/$tcName/lab/$exportName$platform.xsa
# @remark <list of searchable terms>
# @param exportName - exportName is the basis of the exported file
proc designExport {exportName} {
   variable tcName
   variable trainingPath
   variable blockDesignName
   variable platform
   variable verbose
   if {$verbose} { puts "@@@  completer_helper - completer_helper.designExport"; }

   # support for Vitis IDE
   set xsaFileName $trainingPath/$tcName/lab/
   append xsaFileName $exportName [string tolower $platform] .xsa

   write_hw_platform -fixed -force -include_bit -file $xsaFileName

   markLastStep designExport
}
##
# proc:  designExport (local to project)
# descr: like designExport, but doesn't add the platform name
# @remark <list of searchable terms>
#
proc designExport_noPlatform {} {
   variable tcName
   variable trainingPath
   variable blockDesignName
   variable verbose
   if {$verbose} { puts "@@@  completer_helper - completer_helper.designExport_noPlatform"; }

   # support for Vitis IDE
   set xsaFileName $trainingPath/$tcName/lab/$blockDesignName\_wrapper.xsa
   write_hw_platform -fixed -force -include_bit -file $xsaFileName

   markLastStep designExport
}
lappend loadedProcs {designExport "export the design for software development"};

##
# proc:  VitisLaunch
# descr: WARNING - no tcl proc exists to launch this tool from within Vivado as there was with SDK
# @remark <list of searchable terms>
#
proc VitisLaunch {} {
#   variable tcName
#   variable trainingPath
#   variable blockDesignName
#   variable verbose
    if {$verbose} { puts "@@@  completer_helper - in VitisLaunch - not implemented"; }
#
#   set hdfPath ""
#   append hdfPath $trainingPath/$tcName/lab/$tcName.sdk/$blockDesignName _wrapper.hdf
#   launch_sdk -workspace $trainingPath/$tcName/lab/$tcName.sdk -hwspec $hdfPath
#
#   markLastStep SDKlaunch
}
#
# ***** close vivado project
#
##
# proc:  VivadoCloseProject
# descr:
# @remark <list of searchable terms>
#
proc VivadoCloseProject {} {
   variable verbose
   if {$verbose} { puts "@@@  completer_helper - in VivadoCloseProject"; }

   close_project;

   markLastStep VivadoCloseProject
}
lappend loadedProcs {VivadoCloseProject "closes the open Vivado project"};

##
# proc:  VivadoClose
# descr:
# @remark <list of searchable terms>
#
proc VivadoClose {} { exit; }
lappend loadedProcs {VivadoClose "close the Vivado tool"};

##
# proc:  buildStartingPoint
# descr: assumes that make contains the buildStartingPoint state
# @remark <list of searchable terms>
#
#proc buildStartingPoint {} { make buildStartingPoint }
#lappend loadedProcs {buildStartingPoint "launch the starting point from make"};

proc indirect { var } {
   set variableName [string range $var 1 [string length $var]]
   puts "@@@  completer_helper - variable name is $variableName"
   variable $variableName;           # the variable referenced by var is not scoped inside this proc unless forces. This should make it available.
   set thisArg [format [subst $var]];        # now that it is available, get the value
   puts "@@@  completer_helper - value of thisArg = $thisArg"
   return $thisArg
}
lappend loadedProcs {indirect "not ready for prime time"};

#
# *** Clear everything from the canvas
#
##
# proc:  clearAll
# descr:
# @remark <list of searchable terms>
#
proc clearAll {} {
   delete_bd_objs [get_bd_nets *]
   delete_bd_objs [get_bd_intf_ports *]
   delete_bd_objs [get_bd_ports *]
   delete_bd_objs [get_bd_cells *]
}
lappend loadedProcs {clearAll "removes all objects in the canvas"};

set completer_helper_loaded 1;

if ($debug) {
   puts "@@@  completer_helper - Done with load of completer_ completer_helper";
}
