forked from gatsby/embedded-autobuild
397 lines
14 KiB
Tcl
397 lines
14 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
|
||
} 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 {
|
||
set source_path [file normalize $::root/$source_file]
|
||
set target_path [file join $::ws $app_name [file tail $source_path]]
|
||
|
||
# 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
|
||
file link -symbolic $target_path $source_path
|
||
_info "Created symbolic link: $target_path -> $source_path"
|
||
}
|
||
"n" {
|
||
# Copy the source file
|
||
file copy $source_path $target_path
|
||
_info "Copied source file: $source_path 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" {
|
||
foreach path $value {
|
||
eval $cmd $app_name include-path "../$path"
|
||
_info "Append included path: $path for $app_name."
|
||
}
|
||
}
|
||
"define-compiler-symbols" -
|
||
"undef-compiler-symbols" -
|
||
"libraries" -
|
||
"library-search-path" {
|
||
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 lang [dict get $app_config language]
|
||
set sources [dict get $app_config sources]
|
||
set bsp_name "${os}_${cpu}"
|
||
|
||
# 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]
|
||
} 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]]
|
||
}
|
||
_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 "Empty Application($lang)"
|
||
_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 "Empty Application"
|
||
_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
|
||
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 [file dirname [file normalize $config_file]]
|
||
set config [json::json2dict $config_data]
|
||
set project_name [dict get $config project_name]
|
||
set hw_file [file normalize [file join [file dirname $config_file] [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." |