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 { # 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 file link -symbolic $target_path $eval_source _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 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 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 [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."