{#
Device Setup Template - PCIe Device Configuration
Variables:
- header: TCL header comment
- board: Target board name
- vendor_id: PCI vendor ID
- device_id: PCI device ID
- class_code: PCI class code
- fpga_part: FPGA part number
- pcie_config: PCIe IP core configuration
#}
{{ header }}

# Set up build environment
set project_name "pcileech_firmware"
set project_dir "./vivado_project"
set output_dir "."

# Create project directory
file mkdir $project_dir

puts "Creating Vivado project for {{ board }}..."
puts "Device: {{ vendor_id }}:{{ device_id }} (Class: {{ class_code }})"

# Create project with correct FPGA part
create_project $project_name $project_dir -part {{ fpga_part }} -force

# Set project properties
set_property target_language Verilog [current_project]
set_property simulator_language Mixed [current_project]
set_property default_lib xil_defaultlib [current_project]

{{ generate_tcl_header_comment("PCIe IP Core Configuration") }}
puts "Creating PCIe IP core for device {{ vendor_id }}:{{ device_id }}..."
puts "FPGA Part: {{ fpga_part }}"
puts "Board: {{ board }}"

{{ pcie_config }}

{{ generate_tcl_header_comment("Source File Management") }}
puts "Adding source files..."

# Add all SystemVerilog files
set sv_files [glob -nocomplain *.sv]
if {[llength $sv_files] > 0} {
    puts "Found [llength $sv_files] SystemVerilog files"
    add_files -norecurse $sv_files
    set_property file_type SystemVerilog [get_files *.sv]
    foreach sv_file $sv_files {
        puts "  - $sv_file"
    }
}

# Add all Verilog files
set v_files [glob -nocomplain *.v]
if {[llength $v_files] > 0} {
    puts "Found [llength $v_files] Verilog files"
    add_files -norecurse $v_files
    foreach v_file $v_files {
        puts "  - $v_file"
    }
}

# Add all constraint files
set xdc_files [glob -nocomplain *.xdc]
if {[llength $xdc_files] > 0} {
    puts "Found [llength $xdc_files] constraint files"
    add_files -fileset constrs_1 -norecurse $xdc_files
    foreach xdc_file $xdc_files {
        puts "  - $xdc_file"
    }
}

# Set top module
set top_module ""
if {[file exists "pcileech_top.sv"]} {
    set top_module "pcileech_top"
} elseif {[file exists "pcileech_tlps128_bar_controller.sv"]} {
    set top_module "pcileech_tlps128_bar_controller"
} else {
    set top_files [glob -nocomplain "*top*.sv"]
    if {[llength $top_files] > 0} {
        set top_file [lindex $top_files 0]
        set top_module [file rootname [file tail $top_file]]
    } else {
        puts "ERROR: No suitable top module found!"
        exit 1
    }
}

if {$top_module != ""} {
    set_property top $top_module [current_fileset]
    puts "Set top module: $top_module"
} else {
    puts "ERROR: Failed to determine top module"
    exit 1
}

{{ generate_tcl_header_comment("Device-Specific Timing Constraints") }}
puts "Adding device-specific timing constraints..."
set timing_constraints {
    # Clock constraints
    create_clock -period 10.000 -name sys_clk [get_ports clk]
    
    # Input delay constraints for actual input ports
    set_input_delay -clock sys_clk 2.000 [get_ports reset_n]
    catch {set_input_delay -clock sys_clk 2.000 [get_ports pcie_rx_data*]}
    catch {set_input_delay -clock sys_clk 2.000 [get_ports pcie_rx_valid]}
    catch {set_input_delay -clock sys_clk 2.000 [get_ports cfg_ext_read_received]}
    catch {set_input_delay -clock sys_clk 2.000 [get_ports cfg_ext_write_received]}
    catch {set_input_delay -clock sys_clk 2.000 [get_ports cfg_ext_register_number*]}
    catch {set_input_delay -clock sys_clk 2.000 [get_ports cfg_ext_function_number*]}
    catch {set_input_delay -clock sys_clk 2.000 [get_ports cfg_ext_write_data*]}
    catch {set_input_delay -clock sys_clk 2.000 [get_ports cfg_ext_write_byte_enable*]}
    catch {set_input_delay -clock sys_clk 2.000 [get_ports msix_interrupt_ack]}
    
    # Output delay constraints - only apply to ports that exist
    # Use catch to handle missing ports gracefully
    catch {set_output_delay -clock sys_clk 2.000 [get_ports pcie_tx_data*]}
    catch {set_output_delay -clock sys_clk 2.000 [get_ports pcie_tx_valid]}
    catch {set_output_delay -clock sys_clk 2.000 [get_ports cfg_ext_read_data*]}
    catch {set_output_delay -clock sys_clk 1.000 [get_ports cfg_ext_read_data_valid]}
    catch {set_output_delay -clock sys_clk 2.000 [get_ports msix_interrupt]}
    catch {set_output_delay -clock sys_clk 2.000 [get_ports msix_vector*]}
    catch {set_output_delay -clock sys_clk 2.000 [get_ports debug_status*]}
    catch {set_output_delay -clock sys_clk 2.000 [get_ports device_ready]}

    # Device-specific constraints for {{ vendor_id }}:{{ device_id }}
    # Board-specific pin assignments for {{ board }}
    # WARNING: These pin assignments are examples and MUST be updated for your specific FPGA/package
    
    # Clock and Reset pins
    set_property PACKAGE_PIN E3 [get_ports clk]
    set_property IOSTANDARD LVCMOS33 [get_ports clk]
    set_property PACKAGE_PIN C12 [get_ports reset_n]
    set_property IOSTANDARD LVCMOS33 [get_ports reset_n]
    
    # Note: Additional pin assignments for all other ports should be added here
    # based on your specific FPGA part and package. See constraints.j2 template
    # for a complete example of all port assignments.
    #
    # For production use, you MUST:
    # 1. Update all PACKAGE_PIN assignments for your target FPGA
    # 2. Verify IOSTANDARD values match your board design
    # 3. Add any additional timing constraints required by your design
}

# Write timing constraints to file
set constraints_file "./device_constraints.xdc"
set fp [open $constraints_file w]
puts $fp $timing_constraints
close $fp
add_files -fileset constrs_1 -norecurse $constraints_file

{{ generate_tcl_header_comment("Synthesis & Implementation") }}
puts "Configuring synthesis settings..."
set_property strategy "Vivado Synthesis Defaults" [get_runs synth_1]
set_property steps.synth_design.args.directive "AreaOptimized_high" [get_runs synth_1]

puts "Starting synthesis..."
reset_run synth_1
launch_runs synth_1 -jobs 8
wait_on_run synth_1

if {[get_property PROGRESS [get_runs synth_1]] != "100%"} {
    puts "ERROR: Synthesis failed!"
    exit 1
}

puts "Synthesis completed successfully"
report_utilization -file utilization_synth.rpt

puts "Configuring implementation settings..."
set_property strategy "Performance_Explore" [get_runs impl_1]

puts "Starting implementation..."
launch_runs impl_1 -jobs 8
wait_on_run impl_1

if {[get_property PROGRESS [get_runs impl_1]] != "100%"} {
    puts "ERROR: Implementation failed!"
    exit 1
}

puts "Implementation completed successfully"

{{ generate_tcl_header_comment("Report Generation & Bitstream") }}
puts "Generating reports..."
open_run impl_1
report_timing_summary -file timing_summary.rpt
report_utilization -file utilization_impl.rpt
report_power -file power_analysis.rpt
report_drc -file drc_report.rpt

puts "Generating bitstream..."
launch_runs impl_1 -to_step write_bitstream -jobs 8
wait_on_run impl_1

# Check bitstream generation
set bitstream_file "$project_dir/$project_name.runs/impl_1/[get_property top [current_fileset]].bit"
if {[file exists $bitstream_file]} {
    set output_bit "pcileech_{{ vendor_id }}_{{ device_id }}_{{ board }}.bit"
    file copy -force $bitstream_file $output_bit
    puts "SUCCESS: Bitstream generated successfully!"
    puts "Output file: $output_bit"

    # Generate additional files
    write_cfgmem -format mcs -size 16 -interface SPIx4 \
        -loadbit "up 0x0 $output_bit" \
        -file "pcileech_{{ vendor_id }}_{{ device_id }}_{{ board }}.mcs"

    if {[llength [get_debug_cores]] > 0} {
        write_debug_probes -file "pcileech_{{ vendor_id }}_{{ device_id }}_{{ board }}.ltx"
    }

    write_checkpoint -force "pcileech_{{ vendor_id }}_{{ device_id }}_{{ board }}.dcp"

    puts "Generated files:"
    puts "  - Bitstream: pcileech_{{ vendor_id }}_{{ device_id }}_{{ board }}.bit"
    puts "  - MCS file: pcileech_{{ vendor_id }}_{{ device_id }}_{{ board }}.mcs"
    puts "  - Checkpoint: pcileech_{{ vendor_id }}_{{ device_id }}_{{ board }}.dcp"
    puts "  - Reports: *.rpt"
} else {
    puts "ERROR: Bitstream generation failed!"
    exit 1
}

puts "Build completed successfully!"
close_project