Files
embedded-autobuild/platform/xilinx/util.tcl
T
Gatsby Van caa636018b refactor: make paths relative to current working directory
- Allows configuration files to be placed in separate folders, with paths in config files now relative to the config file's location.
- Added `cd /d "%~dp0"` at the beginning of the `.bat` template to ensure paths are based on the script's location, enhancing portability and consistency.
2024-11-06 00:32:04 +08:00

451 lines
16 KiB
Tcl

package require json
# Helper procedure for formatted messages
proc _info {message} {
puts [format "INFO: %s" $message]
}
proc _warn {message} {
puts [format "WARN: %s" $message]
}
proc _error {message details} {
error [format "ERROR: %s. Details: %s" $message $details]
}
# Helper procedure to create hardware platform
proc _prepare_platform {hw_file platform_name is_vitis} {
if {![file exists $hw_file]} {
_error "Hardware design file does not exist" $hw_file
}
_info "Creating hardware platform: $platform_name"
if {$is_vitis} {
platform create -name $platform_name -hw $hw_file
_info "Platform '$platform_name' created successfully for Vitis."
} else {
sdk createhw -name $platform_name -hwspec $hw_file
_info "Hardware platform '$platform_name' created successfully for Xilinx SDK."
}
}
# Helper procedure for BSP configuration
proc _configure_bsp {bsp_name bsp_config platform_name is_vitis} {
set cpu [dict get $bsp_config target_cpu]
set os [dict get $bsp_config os]
# Create Domain/BSP
_info "Creating BSP: $bsp_name for CPU: $cpu and OS: $os"
if {$is_vitis} {
platform active $platform_name
domain create -name $bsp_name -os $os -proc $cpu
domain active $bsp_name
_info "BSP '$bsp_name' created for Vitis."
} else {
sdk createbsp -name $bsp_name -hwproject $platform_name -proc $cpu -os $os
_info "BSP '$bsp_name' created for Xilinx SDK."
}
# Apply BSP library configurations if they exist
if {[dict exists $bsp_config libs]} {
_info "Configuring BSP libraries..."
dict for {lib_name lib_info} [dict get $bsp_config libs] {
if {$is_vitis} {
bsp setlib -name $lib_name -ver [dict get $lib_info version]
} else {
setlib -bsp $bsp_name -lib $lib_name -ver [dict get $lib_info version]
}
_info "Applied library '$lib_name' version '[dict get $lib_info version]' for BSP '$bsp_name'."
if {[dict exists $lib_info config]} {
dict for {param value} [dict get $lib_info config] {
if {$is_vitis} {
bsp config $param $value
} else {
configbsp -bsp $bsp_name $param $value
}
_info "Configured library '$lib_name': $param = $value"
}
}
}
}
# Apply BSP configurations if they exist
if {[dict exists $bsp_config config]} {
_info "Configuring BSP options..."
dict for {param value} [dict get $bsp_config config] {
if {$is_vitis} {
bsp config $param $value
} else {
configbsp -bsp $bsp_name $param $value
}
_info "Applied BSP configuration: $param = $value for $bsp_name"
}
}
if {$is_vitis} {
bsp regenerate
platform generate -domains "$bsp_name"
} else {
regenbsp -bsp $bsp_name
projects -build -type bsp -name $bsp_name
}
_info "BSP '$bsp_name' successfully configured and regenerated."
return $bsp_name
}
# Helper procedure to handle source files: link or copy based on LINK_SRCS
proc _handle_sources {app_name sources} {
# Check environment variable LINK_SRCS
set link_srcs "y"
if {[info exists ::env(LINK_SRCS)]} {
set link_srcs $::env(LINK_SRCS)
}
foreach source_file $sources {
# Expand wildcards
set expanded_sources [glob -nocomplain -directory $::root $source_file]
if {[llength $expanded_sources] == 0} {
_error "Source file not found" "No file or directory matches $source_file"
}
# Hanle each expanded source
foreach eval_source $expanded_sources {
set relative_source [string range $eval_source [expr [string length $::root] + 1] end]
set target_path [file join $::ws $app_name $relative_source]
# Ensure the target directory exists
set target_dir [file dirname $target_path]
if {![file exists $target_dir]} {
file mkdir $target_dir
_info "Created directory: $target_dir"
}
# Delete existing symbolic link or file if it exists
if {[file exists $target_path]} {
file delete -force $target_path
_warn "Deleted existing link or file: $target_path"
}
# Handle source based on the value of LINK_SRCS
switch $link_srcs {
"y" {
# Create symbolic link
# -- TCL cannont create symbolic link for files on Windows
# -- Fallback to use mklink command
if {$::tcl_platform(platform) eq "windows" && ![file isdirectory $eval_source]} {
if {[catch {
exec cmd /c mklink [file nativename $target_path] [file nativename $eval_source]
} err]} {
error "Failed to create file symbolic link using mklink: $err"
}
} else {
if {[catch {
file link -symbolic $target_path $eval_source
} err]} {
error "Failed to create symbolic link: $err"
}
}
_info "Created symbolic link: $target_path -> $eval_source"
}
"n" {
# Copy the source file
file copy $eval_source $target_path
_info "Copied source file: $eval_source to $target_path"
}
default {
_error "Unknown value for LINK_SRCS: ${link_srcs}."
}
}
}
}
}
proc _update_app_mem_layout {app_name mem_layout} {
# Update heap and stack sizes in linker script if specified
set lscript_path "$::ws/$app_name/src/lscript.ld"
if {[file exists $lscript_path]} {
set file_content {}
set file_id [open $lscript_path r]
while {[gets $file_id line] >= 0} {
if {[regexp {^_HEAP_SIZE =} $line] && [dict exists $mem_layout heap_size]} {
set heap_size [dict get $mem_layout heap_size]
if {$heap_size ne ""} {
set line "_HEAP_SIZE = DEFINED(_HEAP_SIZE) ? _HEAP_SIZE : $heap_size;"
_info "Updating _HEAP_SIZE in linker script to $heap_size"
} else {
_warn "Empty `heap_size` config, _HEAP_SIZE not updated."
}
} elseif {[regexp {^_STACK_SIZE =} $line] && [dict exists $mem_layout stack_size]} {
set stack_size [dict get $mem_layout stack_size]
if {$stack_size ne ""} {
set line "_STACK_SIZE = DEFINED(_STACK_SIZE) ? _STACK_SIZE : $stack_size;"
_info "Updating _STACK_SIZE in linker script to $stack_size"
} else {
_warn "Empty `stack_size` config, _STACK_SIZE not updated."
}
}
lappend file_content $line
}
close $file_id
set file_id [open $lscript_path w]
puts $file_id [join $file_content \n]
close $file_id
} else {
_warn "Linker script $lscript_path not found for application: $app_name"
}
}
proc _update_app_build_config {app_name build_type build_config is_vitis} {
set build_types {"debug" "release"}
if {[lsearch $build_types $build_type] == -1} {
_error "Invalid config type: $build_type" "available types: $build_types"
}
set cmd [list]
if {$is_vitis} {
lappend cmd app config -name
} else {
lappend cmd sdk configapp -app
}
_info "Setting build configuration for '$app_name' as $build_type."
eval $cmd $app_name -set build-config $build_type
dict for {param value} $build_config {
switch -exact -- $param {
"include-path" -
"library-search-path" {
foreach path $value {
set path_str [format "\"\${workspace_loc:/%s/%s}\"" $app_name $path]
eval $cmd $app_name $param [list $path_str]
_info "Append $param: $path for $app_name."
}
}
"define-compiler-symbols" -
"undef-compiler-symbols" -
"libraries" {
foreach item $value {
eval $cmd $app_name $param $item
_info "Append parameter $param: $item for $app_name."
}
}
"assembler-flags" -
"compiler-misc" -
"compiler-optimization" -
"linker-misc" -
"linker-script" {
if {$value ne ""} {
eval $cmd $app_name -set $param $value
_info "Set $param: $value for $app_name."
}
}
default {
_error "Unsupported app build-config option" $param
}
}
}
}
# Helper procedure for application configuration
proc _create_app {app_name app_config platform_name is_vitis} {
set cpu [dict get $app_config target_cpu]
set os [dict get $app_config os]
set sources [dict get $app_config sources]
set bsp_name "${app_name}_bsp"
set lang [dict get $app_config language]
if { $lang ne "C" && $lang ne "C++" } {
_error "Unsupport language" "language should be C or C++"
}
if { [dict exists $app_config template] } {
set template [dict get $app_config template]
} elseif { !$is_vitis } {
set template "Empty Application"
} elseif { $lang eq "C++" } {
set template "Empty Application (C++)"
} else {
set template "Empty Application(C)"
}
# Configure BSP/Domain for application
if {[dict exists $app_config bsp] && [dict get $app_config bsp] ne ""} {
set bsp_name [dict get $app_config bsp]
if {![dict exists $::config bsps]} {
_error "BSP configuration missing" "The 'bsps' section is not found in the configuration."
} elseif {![dict exists [dict get $::config bsps] $bsp_name]} {
_error "BSP configuration missing" "No configuration found for BSP '$bsp_name' in the 'bsps' section."
} else {
_info "Configuring BSP '$bsp_name' for application '$app_name'."
}
} else {
set bsp_config [dict create]
set bsp_config [dict set bsp_config target_cpu $cpu]
set bsp_config [dict set bsp_config os $os]
if {[dict exists $app_config bsp_lib]} {
set bsp_lib [dict get $app_config bsp_lib]
set bsp_config [dict merge $bsp_config [dict create libs $bsp_lib]]
}
if {[dict exists $app_config bsp_config]} {
set bsp_cfg [dict get $app_config bsp_config]
set bsp_config [dict merge $bsp_config [dict create config $bsp_cfg]]
}
_info "Creating BSP $bsp_name for Application $app_name ..."
_configure_bsp $bsp_name $bsp_config $platform_name $is_vitis
}
# Create application
_info "Creating application: $app_name"
if {$is_vitis} {
app create -name $app_name \
-platform $platform_name \
-domain $bsp_name \
-proc $cpu \
-os $os \
-lang $lang \
-sysproj $::project_name \
-template "$template"
_info "Application '$app_name' created for Vitis."
} else {
sdk createapp -name $app_name \
-hwproject $platform_name \
-bsp $bsp_name \
-proc $cpu \
-os $os \
-lang $lang \
-app "$template"
_info "Application '$app_name' created for Xilinx SDK."
}
# Handle source files if present
if {[llength $sources] > 0} {
_info "Handling sources for application: $app_name"
_handle_sources $app_name $sources
} else {
_warn "No source files specified for application: $app_name"
}
# Configure application if additional configs exist
_info "Configuring application: $app_name"
if {[dict exists $app_config build_config]} {
dict for {build_type build_config} [dict get $app_config build_config] {
_update_app_build_config $app_name $build_type $build_config $is_vitis
}
}
# Update memory layout in linker script if specified
_info "Updating memory layout for application: $app_name"
if {[dict exists $app_config mem_layout]} {
set mem_layout [dict get $app_config mem_layout]
_update_app_mem_layout $app_name $mem_layout
}
}
# Apply FSBL patches if specified
# TODO:
proc _apply_fsbl_patches {patches} {
_info "Applying FSBL patches."
dict for {target_file patch_file} $patches {
set target_path "$::ws/fsbl/src/$target_file"
set patch_path "$::root/$patch_file"
file copy -force $patch_path $target_path
if {[file exists $patch_path]} {
_info "Patched $target_file with $patch_file"
} else {
_warn "FSBL patch file $patch_path not found, new file $patch_file "
}
}
}
# Main procedures
proc create_project {} {
cd $::ws
setws $::ws
_info "Workspace set to $::ws."
set is_vitis [expr {[string first ".hdf" $::hw_file] == -1}]
set platform_name "${::project_name}_platform"
# Prepare hardware platform
_prepare_platform $::hw_file $platform_name $is_vitis
# Process each bsp
if {[dict exists $::config bsps]} {
dict for {bsp_name bsp_config} [dict get $::config bsps] {
_configure_bsp $bsp_name $bsp_config $platform_name $is_vitis
}
}
# Process each application
dict for {app_name app_config} [dict get $::config apps] {
_create_app $app_name $app_config $platform_name $is_vitis
}
}
proc build_project {} {
setws $::ws
set is_vitis [expr {[string first ".hdf" $::hw_file] == -1}]
if {$is_vitis} {
# paltform file can be deleted by clean operation ahead
platform active ${::project_name}_platform
platform generate
}
# Determine build type from environment
set build_type "debug"
if {[info exists ::env(RELEASE)] && $::env(RELEASE) == "y"} {
set build_type "release"
}
_info "Build type: $build_type"
dict for {app_name app_config} [dict get $::config apps] {
_info "Building application: $app_name"
if {$is_vitis} {
app config -name $app_name -set build-config $build_type
app build -name $app_name
_info "Application '$app_name' built for Vitis."
} else {
sdk configapp -app $app_name -set build-config $build_type
projects -build -type app -name $app_name
_info "Application '$app_name' built for Xilinx SDK."
}
}
}
proc clean_project {} {
setws $::ws
_info "Cleaning project in workspace: $::ws."
set is_vitis [expr {[string first ".hdf" $::hw_file] == -1}]
dict for {app_name app_config} [dict get $::config apps] {
_info "Cleaning application: $app_name"
if {$is_vitis} {
app clean -name $app_name
} else {
projects -clean -type app -name $app_name
}
_info "Application '$app_name' cleaned."
}
}
# Main execution
set function [lindex $argv 0]
set config_file [lindex $argv 1]
set ws [file normalize [lindex $argv 2]]
_info "Working at $ws"
# Load and parse configuration
set config_file_id [open $config_file r]
set config_data [read $config_file_id]
close $config_file_id
set root [pwd]
set config [json::json2dict $config_data]
set project_name [dict get $config project_name]
set hw_file [file normalize [file join $root [dict get $config hw_design]]]
_info "Starting process: $function"
_info "Project name: $project_name"
_info "Hardware design file: $hw_file"
# Execute requested function
$function
_info "Process $function completed."