450 lines
16 KiB
Tcl
450 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
|
|
# -- Symbolic link for file dose not support on NTFS
|
|
if {$::tcl_platform(platform) eq "windows" && [file isfile $eval_source]} {
|
|
if {[catch {
|
|
file link -hard $target_path $eval_source
|
|
} err]} {
|
|
error "Failed to create file hard link: $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." |