{#
Bitstream Template - Bitstream Generation and File Management

This template handles bitstream generation and creates additional output files
like MCS, debug probes, and checkpoints.

Context variables expected:
- device: Device information (vendor_id, device_id)
- board: Board information (name)
- project: Project settings (name, dir)
- header: TCL header comment
#}
{{ header_comment }}

# allow builds to complete even if a few user pins stay unconstrained
set_property BITSTREAM.CONFIG.UNCONSTRAINED_IO  ALLOW [current_design]
set_property UNCONSTRAINED_IO                   ALLOW [current_design]

puts "Generating bitstream..."
if {[catch {open_run impl_1} err]} {
    puts "Fatal: failed to open impl_1: $err"; exit 1
}

# -------------------------------------------------------------------------
# Live console summaries
# -------------------------------------------------------------------------

# Helper: extract "<label> | used | total" from a utilisation report line
proc ::summarise_util {label rptstr} {
    if {[regexp "\\Q$label\\E\\s+\\|\\s+(\\d+)\\s+\\|\\s+(\\d+)" $rptstr -> used tot]} {
        return "$label $used / $tot"
    }
    return "$label n/a"
}

# ---------------- utilisation -----------------
set u_str [report_utilization -return_string -quiet]
set lut    [::summarise_util "CLB LUTs"          $u_str]
set ff     [::summarise_util "CLB Registers"     $u_str]
set bram   [::summarise_util "Block RAM Tile"    $u_str]
set dsp    [::summarise_util "DSPs"              $u_str]

puts "UTIL  →  $lut,  $ff,  $bram,  $dsp"

# -------------------------------------------------------------------------
# Resource-compatibility guard for pcileech 35T / 75T boards
# -------------------------------------------------------------------------
#
#  • If you target the 35T card the check is obvious: we must not exceed
#    its device limits.
#  • If you target the 75T/100T card we still want to know whether the exact
#    RTL build would *fit* on a 35T (handy for mass-build SKUs). Warn if
#    it does not, but continue the build.
#
#  Device caps come from Xilinx DS180 (Artix-7) - rounded down a little
#  to leave head-room for routed nets.

array set CAP_LUTs  {35t 20800  75t 46560}
array set CAP_REGs  {35t 41600  75t 93120}
array set CAP_BRAMS {35t  50    75t   135}
array set CAP_DSPS  {35t  90    75t   180}

# Determine which part we *are* compiling
set part    [get_property PART [current_project]]
set partkey [expr {
    [string match -nocase *35t* $part] ? "35t" : \
    ([string match -nocase *75t* $part] ? "75t" : \
    ([string match -nocase *100t* $part] ? "100t" : "unknown"))
}]
# Unknown parts will be evaluated against actual device totals from reports.

# Parse utilisation once (we already have $u_str from the live summary)
if {![info exists u_str]} { set u_str [report_utilization -return_string -quiet] }

proc parse_field {field txt} {
    regexp "\\Q$field\\E\\s+\\|\\s+(\\d+)" $txt -> val
    return $val
}
proc parse_used_total {field txt} {
    if {[regexp "\\Q$field\\E\\s+\\|\\s+(\\d+)\\s+\\|\\s+(\\d+)" $txt -> u t]} {
        return [list $u $t]
    }
    return [list 0 0]
}

set usedLUT [parse_field "CLB LUTs"       $u_str]
set usedREG [parse_field "CLB Registers"  $u_str]
set usedBR  [parse_field "Block RAM Tile" $u_str]
set usedDSP [parse_field "DSPs"           $u_str]

# ---- check against 35T limits first (smallest card) ---------------
set resource_warns 0
foreach {capName used arr} {
    LUTs        $usedLUT CAP_LUTs
    Registers   $usedREG CAP_REGs
    BRAMs       $usedBR  CAP_BRAMS
    DSPs        $usedDSP CAP_DSPS
} {
    set max35 [set ${arr}(35t)]
    if {$used > $max35} {
        puts "WARNING: design needs $used $capName but a 35T card only has $max35. (won't fit 35T)"
        incr resource_warns
    }
}

# ---- also check we don't overrun the *current* target device -------
# Use the totals reported by Vivado so this works for 35T/75T/100T and others.
foreach {label used} {
    "CLB LUTs"       $usedLUT
    "CLB Registers"  $usedREG
    "Block RAM Tile" $usedBR
    "DSPs"           $usedDSP
} {
    lassign [parse_used_total $label $u_str] u tot
    if {$tot > 0 && $used > $tot} {
        puts "WARNING: design exceeds $label capacity of target part $part ($used > $tot)."
        incr resource_warns
    }
}

if {$resource_warns == 0} {
    puts "COMPAT→  resource checks passed (35T envelope + target totals) ✔"
} else {
    puts "COMPAT→  resource check warnings: $resource_warns"
}

# ---------------- timing ----------------------
report_timing_summary -delay_type max -file timing_summary.rpt
set slack [get_property SLACK [report_timing_summary]]
puts [format "TIMING→  Worst Slack: %.3f ns" $slack]

# ---------------- DRC -------------------------
set drc_str [report_drc -return_string -quiet]
set err_cnt [regexp -all -inline -nocase {ERROR:} $drc_str]
set warn_cnt [regexp -all -inline -nocase {WARNING:} $drc_str]
puts "DRC   →  [llength $err_cnt] ERR, [llength $warn_cnt] WARN"

# --- design rule check --------------------------------------------------
# Use the previously captured DRC string to avoid re-running and ensure robust matching
if {[regexp -nocase {ERROR:} $drc_str]} {
    puts "Fatal: DRC errors present"; exit 1
}

# --- timing -------------------------------------------------------------
report_timing_summary -delay_type max -file timing_summary.rpt
set t_slack [get_property SLACK [report_timing_summary]]
if {$t_slack < 0.0} {
    puts "Fatal: negative timing slack ($t_slack ns)"; exit 1
}

# --- utilisation guard (≥80 % CLB LUTs) ---------------------------------
# Warn (do not fail) if LUT utilisation exceeds 80% to keep builds reliable
set u_str [report_utilization -return_string -quiet]
set used 0
set avail 0
if {[regexp {CLB LUTs\s+\|\s+(\d+)\s+\|\s+(\d+)} $u_str -> used avail]} {
    if {$avail > 0 && $used > 0.80 * $avail} {
        puts "WARNING: LUT utilisation high $used/$avail (>80%)"
    }
}

write_bitstream -force pcileech_top.bit

# Generate hex file for programming
write_cfgmem -format mcs -size 16 -interface SPIx4 -loadbit "up 0x0 pcileech_top.bit" -force pcileech_top.mcs
write_cfgmem -format hex -size 16 -interface SPIx4 -loadbit "up 0x0 pcileech_top.bit" -force pcileech_top.hex

# Validate bitstream file was created
if {![file exists "pcileech_top.bit"]} {
    puts "Fatal: pcileech_top.bit was not generated"
    exit 1
}

# Validate hex file was created
if {![file exists "pcileech_top.hex"]} {
    puts "Fatal: pcileech_top.hex was not generated"
    exit 1
}

puts "Bitstream generation completed successfully!"
puts "Output files:"
puts "  - pcileech_top.bit (bitstream)"
puts "  - pcileech_top.hex (hex format)"
puts "  - pcileech_top.mcs (MCS format)"