找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

1092

积分

0

好友

135

主题
发表于 16 小时前 | 查看: 0| 回复: 0

本文系统解析一套面向 Xilinx FPGA 的专业化时序分析 Tcl 脚本体系,覆盖从 BRAM/URAM 寄存器使用合规性、DSP 嵌入式寄存器(MREG/PREG)配置验证、低深度 SRL 与组合 LUT 检查,到跨 SLR 时序路径分析、CDC 报告、高扇出网络识别等完整链路。所有模块均基于 Vivado 原生 Tcl API 构建,支持 JSON/YAML 配置驱动、多阶段 DCP(综合/布局/布线)适配,并内置 Yosys 流程回退机制,适用于大规模异构 FPGA 设计的工程化时序质量管控。

该套脚本并非通用教程,而是真实工业级项目中沉淀的可复用分析框架,其设计逻辑紧密贴合 计算机基础 中的数字电路时序建模原理,同时在资源调度与路径分析层面体现了对 算法/数据结构(如过滤、排序、树状遍历)的深度运用。


tcl/modules/bram_uram_analysis.tcl

该模块聚焦于 Block RAM(BRAM)与 Ultra RAM(URAM)单元中嵌入式输出寄存器(DOA_REG/DOB_REG)的启用状态检查,识别未按最佳实践使用寄存器的潜在时序瓶颈点。

#########################################################################################
## BRAM and URAM analysis functions
#########################################################################################
namespace eval timing_analysis {
  # 获取没有使用嵌入式输出寄存器的BRAM并生成报告
  proc get_no_reg_bram {sn used_bram {en 1}} {
    if {$en==1} {
      puts "Start section $sn: get_no_reg_bram get bram without using embedded registers"
      set used_bram_a [filter $used_bram "CASCADE_ORDER_A==NONE && CASCADE_ORDER_B==NONE"]
      set used_bram_a_violated {}
      if {[llength $used_bram_a]>0} {
        foreach i_used_bram_a $used_bram_a {
          set douta [get_pins -of $i_used_bram_a -filter "REF_PIN_NAME=~DOADO
  • "]           if {"1" in [get_property IS_CONNECTED $douta]} {             if {[get_property DOA_REG $i_used_bram_a]==0} {               lappend used_bram_a_violated $i_used_bram_a             }           }           set doutb [get_pins -of $i_used_bram_a -filter "REF_PIN_NAME=~DOBDO
  • "]           if {"1" in [get_property IS_CONNECTED $doutb]} {             if {[get_property DOB_REG $i_used_bram_a]==0} {               lappend used_bram_a_violated $i_used_bram_a             }           }         }         set used_bram_a_violated [lsort -unique $used_bram_a_violated]       }       set used_fifo_violated [filter $used_bram "CASCADE_ORDER==NONE && REGISTER_MODE==UNREGISTERED"]       set used_cas_bram_a_violated  [filter $used_bram "CASCADE_ORDER_A==LAST && CASCADE_ORDER_B==NONE && DOA_REG==0"]       set used_cas_bram_b_violated  [filter $used_bram "CASCADE_ORDER_B==LAST && CASCADE_ORDER_A==NONE && DOB_REG==0"]       set used_cas_bram_ab_violated [filter $used_bram "CASCADE_ORDER_B==LAST && CASCADE_ORDER_A==LAST && (DOB_REG==0 || DOA_REG==0)"]       set used_cas_fifo_violated    [filter $used_bram "CASCADE_ORDER==LAST && REGISTER_MODE==UNREGISTERED"]       set used_bram_violated {}       set used_bram_violated [concat $used_bram_a_violated $used_fifo_violated $used_cas_bram_a_violated \       $used_cas_bram_b_violated $used_cas_bram_ab_violated $used_cas_fifo_violated]       set used_bram_violated [lsort -unique $used_bram_violated]       if {[llength $used_bram_violated]>0} {         show_objects $used_bram_violated -name ${sn}_bram_violated         set fn ${sn}_bram_violated         report_critical_cells $fn $used_bram_violated       }       puts "Complete section $sn: get_no_reg_bram"     }   }   # 获取没有使用嵌入式输出寄存器的URAM并生成报告   proc get_no_reg_uram {sn used_uram {en 1}} {     if {$en==1} {       puts "Start section $sn: get_no_reg_uram get uram without using embedded registers"       set used_uram_a [filter $used_uram "CASCADE_ORDER_A==NONE && CASCADE_ORDER_B==NONE"]       set used_uram_a_violated {}       if {[llength $used_uram_a]>0} {         foreach i_used_uram_a $used_uram_a {           set douta [get_pins -of $i_used_uram_a -filter "REF_PIN_NAME=~DOA
  • "]           if {"1" in [get_property IS_CONNECTED $douta]} {             if {[get_property DOA_REG $i_used_uram_a]==0} {               lappend used_uram_a_violated $i_used_uram_a             }           }           set doutb [get_pins -of $i_used_uram_a -filter "REF_PIN_NAME=~DOB
  • "]           if {"1" in [get_property IS_CONNECTED $doutb]} {             if {[get_property DOB_REG $i_used_uram_a]==0} {               lappend used_uram_a_violated $i_used_uram_a             }           }         }         set used_uram_a_violated [lsort -unique $used_uram_a_violated]       }       set used_fifo_violated [filter $used_uram "CASCADE_ORDER==NONE && REGISTER_MODE==UNREGISTERED"]       set used_cas_uram_a_violated  [filter $used_uram "CASCADE_ORDER_A==LAST && CASCADE_ORDER_B==NONE && DOA_REG==0"]       set used_cas_uram_b_violated  [filter $used_uram "CASCADE_ORDER_B==LAST && CASCADE_ORDER_A==NONE && DOB_REG==0"]       set used_cas_uram_ab_violated [filter $used_uram "CASCADE_ORDER_B==LAST && CASCADE_ORDER_A==LAST && (DOB_REG==0 || DOA_REG==0)"]       set used_cas_fifo_violated    [filter $used_uram "CASCADE_ORDER==LAST && REGISTER_MODE==UNREGISTERED"]       set used_uram_violated {}       set used_uram_violated [concat $used_uram_a_violated $used_fifo_violated $used_cas_uram_a_violated \       $used_cas_uram_b_violated $used_cas_uram_ab_violated $used_cas_fifo_violated]       set used_uram_violated [lsort -unique $used_uram_violated]       if {[llength $used_uram_violated]>0} {         show_objects $used_uram_violated -name ${sn}_uram_violated         set fn ${sn}_uram_violated         report_critical_cells $fn $used_uram_violated       }       puts "Complete section $sn: get_no_reg_uram"     }   } }

  • tcl/modules/cell_analysis.tcl

    该模块针对特定原语单元进行精细化扫描,包括移位寄存器(SRL)、组合 LUT、MUXFX 多路复用器及锁存器(Latch),旨在发现非典型或易被忽略的设计模式。

    #########################################################################################
    ## Cell analysis functions
    #########################################################################################
    namespace eval timing_analysis {
      # 获取深度较小的移位寄存器并生成报告
      proc get_lower_depth_srl {sn lower_srl {en 1}} {
        if {$en==1} {
          puts "Start section $sn: get_lower_depth_srl"
          set lower_srl_1 [filter $lower_srl "SRL_MODE < 4"]
          set lower_srl_2 [filter $lower_srl "SRL_MODE >= 4 && SRL_MODE < 8"]
          set lower_srl_3 [filter $lower_srl "SRL_MODE >= 8 && SRL_MODE < 16"]
          if {[llength $lower_srl_1]>0} {
            show_objects $lower_srl_1 -name ${sn}_srl1_analysis
            report_critical_cells ${sn}_srl1_analysis $lower_srl_1
          }
          if {[llength $lower_srl_2]>0} {
            show_objects $lower_srl_2 -name ${sn}_srl2_analysis
            report_critical_cells ${sn}_srl2_analysis $lower_srl_2
          }
          if {[llength $lower_srl_3]>0} {
            show_objects $lower_srl_3 -name ${sn}_srl3_analysis
            report_critical_cells ${sn}_srl3_analysis $lower_srl_3
          }
          puts "Complete section $sn: get_lower_depth_srl"
        }
      }
    
      # 获取组合LUT并生成报告
      proc get_combined_lut {sn combined_lut {en 1}} {
        if {$en==1} {
          puts "Start section $sn: get_combined_lut"
          if {[llength $combined_lut]>0} {
            show_objects $combined_lut -name ${sn}_combined_lut_analysis
            set fn ${sn}_combined_lut_analysis
            report_critical_cells $fn $combined_lut
          }
          puts "Complete section $sn: get_combined_lut"
        }
      }
    
      # 获取MUXFX单元并生成报告
      proc get_muxfx {sn muxfx_cells {en 1}} {
        if {$en==1} {
          puts "Start section $sn: get_muxfx"
          if {[llength $muxfx_cells]>0} {
            show_objects $muxfx_cells -name ${sn}_muxfx_analysis
            set fn ${sn}_muxfx_analysis
            report_critical_cells $fn $muxfx_cells
          }
          puts "Complete section $sn: get_muxfx"
        }
      }
    
      # 获取锁存器单元并生成报告
      proc get_latch {sn latch_cells {en 1}} {
        if {$en==1} {
          puts "Start section $sn: get_latch"
          if {[llength $latch_cells]>0} {
            show_objects $latch_cells -name ${sn}_latch_analysis
            set fn ${sn}_latch_analysis
            report_critical_cells $fn $latch_cells
          }
          puts "Complete section $sn: get_latch"
        }
      }
    }

    tcl/modules/common_utils.tcl

    提供基础工具函数,包括 FPGA 器件型号标准化(剥离 es 工程样片标识)、序列号生成、以及关键时序基准值查表(timing_table),支撑后续分析模块的参数化运行。

    #########################################################################################
    ## Utility functions for timing analysis
    #########################################################################################
    namespace eval timing_analysis {
      # 移除FPGA器件型号中的工程样片(es)标志
      proc remove_es {x_part} {
        set x_part_seg [split $x_part "-"]
        set x_part_seg_last [lindex $x_part_seg end]
        if {[regexp {es.*} $x_part_seg_last]==1} {
          set x_part_seg_update [lrange $x_part_seg 0 end-1]
          set x_part [join $x_part_seg_update "-"]
        }
        return $x_part
      }
    
      # 根据索引和阶段生成序列号
      proc get_sn {index phase} {
        if {$index<10} {
          set sn "S0$index"
        } else {
          set sn "S$index"
        }
        return ${sn}_${phase}
      }
    
      # 根据索引和准确性标志获取时序基准值
      proc get_timing_base {index is_accurate} {
        array set timing_table {
          {1,-1} 0.575
          {1,-2} 0.500
          {1,-3} 0.425
          {2,-1} 0.490
          {2,-2} 0.425
          {2,-3} 0.360
          {3,-1} 0.350
          {3,-2} 0.300
          {3,-3} 0.250
          {3,-1LV} 0.490
          {3,-2LV} 0.425
          {3,-3LV} 0.360
        }
        if {[array exists timing_table] && [array names timing_table $index] ne ""} {
          set delay_i $timing_table($index)
        } else {
          set delay_i 0.5
        }
        if {$is_accurate==1} {
          set delay $delay_i
        } else {
          set delay 0.5
        }
        return $delay
      }
    }

    tcl/modules/config_loader.tcl

    实现轻量级 JSON/YAML 配置文件加载器,不依赖外部库,采用正则预处理 + 递归下降解析,支持嵌套对象、数组、布尔值、null 及基本类型转换,是整套脚本可配置化的基石。

    package provide config_loader 1.0
    
    namespace eval ::config_loader {
        namespace export load_config
    }
    
    proc ::config_loader::load_config {file_path} {
        if {![file exists $file_path]} {
            error "Config file not found: $file_path"
        }
    
        set ext [file extension $file_path]
        set fp [open $file_path r]
        set content [read $fp]
        close $fp
    
        if {$ext eq ".json"} {
            return [parse_json $content]
        } elseif {$ext eq ".yaml" || $ext eq ".yml"} {
            return [parse_yaml $content]
        } else {
            error "Unsupported config format: $ext"
        }
    }
    
    # -------------------------------------------------------------------------
    # Simple JSON Parser
    # -------------------------------------------------------------------------
    proc ::config_loader::parse_json {json} {
        # Basic JSON to Dict converter
    
        # Pre-process: remove newlines to simplify
        set json [string map {\n " " \r " " \t " "} $json]
    
        # Tokenize
        # Matches: strings (simple), numbers, booleans, null, or structural chars
        # Regex captures:
        # 1. Quoted string: "[^"]*" (non-escaped quotes only for simplicity)
        # 2. Numbers/Keywords: [-0-9.a-z]+
        # 3. Structure: [\{\}\[\],:]
        # Note: This regex assumes no escaped quotes inside strings. 
        # For "vivado_bin": "vivado", it works perfectly.
        set tokens [regexp -all -inline -- {(?:"[^"]*"|[-0-9.]+|true|false|null|[\{\}\[\],:])} $json]
    
        set index 0
        return [parse_json_value tokens index]
    }
    
    proc ::config_loader::parse_json_value {tokensName indexName} {
        upvar $tokensName tokens
        upvar $indexName index
    
        if {$index >= [llength $tokens]} { return "" }
    
        set token [lindex $tokens $index]
        incr index
    
        if {$token eq "\{"} {
            # Object -> Dict
            set res [dict create]
    
            # Check for empty object
            if {$index < [llength $tokens] && [lindex $tokens $index] eq "\}"} { 
                incr index
                return $res 
            }
    
            while {$index < [llength $tokens]} {
                set key [parse_json_value tokens index]
    
                # Expect colon
                if {$index < [llength $tokens] && [lindex $tokens $index] eq ":"} { 
                    incr index 
                }
    
                set val [parse_json_value tokens index]
                dict set res $key $val
    
                if {$index >= [llength $tokens]} { break }
                set next [lindex $tokens $index]
                incr index
    
                if {$next eq "\}"} { break }
            }
            return $res
        } elseif {$token eq "\["} {
            # Array -> List
            set res [list]
    
            if {$index < [llength $tokens] && [lindex $tokens $index] eq "\]"} { 
                incr index
                return $res 
            }
    
            while {$index < [llength $tokens]} {
                lappend res [parse_json_value tokens index]
    
                if {$index >= [llength $tokens]} { break }
                set next [lindex $tokens $index]
                incr index
    
                if {$next eq "\]"} { break }
            }
            return $res
        } else {
            # Atomic value
            # Strip quotes if string
            if {[string match "\"*\"" $token]} {
                return [string range $token 1 end-1]
            }
            if {$token eq "true"} { return 1 }
            if {$token eq "false"} { return 0 }
            if {$token eq "null"} { return "" }
            return $token
        }
    }
    
    # -------------------------------------------------------------------------
    # Simple YAML Parser
    # -------------------------------------------------------------------------
    proc ::config_loader::parse_yaml {content} {
        # Supports a subset of YAML:
        # - Key: Value
        # - Nested dictionaries (indentation based)
        # - Inline lists [a, b]
        # - Comments #
        set lines [split $content "\n"]
        return [parse_yaml_lines lines 0]
    }
    
    proc ::config_loader::parse_yaml_lines {linesVar minIndent} {
        upvar $linesVar lines
        set result [dict create]
    
        while {[llength $lines] > 0} {
            set line [lindex $lines 0]
    
            # Check if empty or comment
            if {[regexp {^\s*(#|$)} $line]} {
                set lines [lrange $lines 1 end]
                continue
            }
    
            # Check indent
            regexp {^(\s*)} $line match spaces
            set indent [string length $spaces]
    
            if {$indent < $minIndent} {
                # End of block
                return $result
            }
    
            # Parse Key: Value
            if {[regexp {^\s*([^:]+):\s*(.*)$} $line match key val]} {
                set key [string trim $key]
                set val [string trim $val]
                set lines [lrange $lines 1 end]
    
                # Clean up value
                if {$val eq ""} {
                    # Block start? Check next line
                    if {[llength $lines] > 0} {
                        set next_line [lindex $lines 0]
                        regexp {^(\s*)} $next_line match next_spaces
                        set next_indent [string length $next_spaces]
    
                        if {$next_indent > $indent} {
                            # Recursively parse block
                            set val [parse_yaml_lines lines $next_indent]
                        }
                    }
                } else {
                    # Handle types
                    set val [parse_yaml_val $val]
                }
    
                dict set result $key $val
            } else {
                # Skip malformed lines
                set lines [lrange $lines 1 end]
            }
        }
        return $result
    }
    
    proc ::config_loader::parse_yaml_val {val} {
        # Remove quotes
        if {[string match "\"*\"" $val] || [string match "'*'" $val]} {
            return [string range $val 1 end-1]
        }
        # Arrays [a, b]
        if {[string match "\[*\]" $val]} {
            set inner [string range $val 1 end-1]
            set listVal [list]
            foreach item [split $inner ","] {
                lappend listVal [parse_yaml_val [string trim $item]]
            }
            return $listVal
        }
        # Booleans
        if {$val eq "true"} { return 1 }
        if {$val eq "false"} { return 0 }
        return $val
    }

    tcl/modules/csv_reporters.tcl

    统一 CSV 报告生成器,输出结构化数据供下游分析或人工审查,涵盖关键单元属性、时序路径详情、BEL 映射关系等核心维度。

    #########################################################################################
    ## Report generation functions
    #########################################################################################
    namespace eval timing_analysis {
      # 生成关键单元的CSV报告
      proc report_critical_cells {fn mycells} {
        set fid [open ${fn}.csv w]
        puts $fid "#\n# File created on [clock format [clock seconds]] \n#\n"
        puts $fid "CellName, RefName, ClockName, ClockRate"
        set myf "%.2f"
        foreach i_mycells $mycells {
          set ref_name [get_property REF_NAME [get_cells $i_mycells]]
          set clk_pin  [get_pins -of [get_cells $i_mycells] -filter "NAME =~ *CLK"]
          set clk_name [get_clocks -of $clk_pin -quiet]
          if {[llength $clk_name]==0} {
            set myclk "Review"
            set clk_rate "Review"
          } else {
            set myclk    [lindex $clk_name 0]
            set clk_rate [format $myf [expr 1000.0/[get_property PERIOD $myclk]]]
          }
          puts $fid "$i_mycells, $ref_name, $myclk, $clk_rate"
        }
        close $fid
        puts "CSV file $fn has been created."
      }
    
      # 生成单元格信息的CSV报告
      proc report_cells {fn mycells} {
        set fid [open ${fn}.csv w]
        puts $fid "#\n# File created on [clock format [clock seconds]] \n#\n"
        puts $fid "CellName, RefName, Parent"
        foreach i_mycells $mycells {
          set ref_name [get_property REF_NAME [get_cells $i_mycells]]
          set parent   [get_property PARENT   [get_cells $i_mycells]]
          if {[llength $parent]==0} {set parent "TOP"}
          puts $fid "$i_mycells, $ref_name, $parent"
        }
        close $fid
        puts "CSV file $fn has been created."
      }
    
      # 生成详细的时序路径CSV报告
      proc create_timing_report {fn paths} {
        set fid [open ${fn}.csv w]
        puts $fid "#\n# File created on [clock format [clock seconds]] \n#\n"
        puts $fid "PathType, StartCell, EndCell, StartPoint, EndPoint, StartClock, EndClock, Requirement, Slack, LogicLevel, Lut, PathDelay, LogicDelay, LogicDelay%, NetDelay, NetDelay%, Skew, Uncertainty, Inter-SLRCompensation"
        set myf "%.2f"
        foreach paths_i $paths {
          set delay_type  [get_property DELAY_TYPE       $paths_i]
          switch -exact -- $delay_type {
            "max" {set path_type "setup"}
            "min" {set path_type "hold"}
            default {set path_type $delay_type}
          }
          set start_point [get_property STARTPOINT_PIN   $paths_i]
          set start_cell  [get_property REF_NAME [get_cells -of [get_pins $start_point]]]
          set end_point   [get_property ENDPOINT_PIN     $paths_i]
          set end_cell    [get_property REF_NAME [get_cells -of [get_pins $end_point]]]
          set start_clk   [get_property STARTPOINT_CLOCK $paths_i]
          if {[llength $start_clk] == 0} {set start_clk No}
          set end_clk [get_property ENDPOINT_CLOCK $paths_i]
          if {[llength $end_clk] == 0} {set end_clk No}
          set req [get_property REQUIREMENT $paths_i]
          if {[llength $req] == 0} {
            set req inf
            set slack inf
          } else {
            set req [format $myf $req]
            set slack [get_property SLACK $paths_i]
            if {[llength $slack] > 0} {set slack [format $myf $slack]}
          }
          set logic_level [get_property LOGIC_LEVELS $paths_i]
          set num_luts    [llength [get_cells -filter {REF_NAME =~ LUT*} -of $paths_i -quiet]]
          set path_delay  [format $myf [get_property DATAPATH_DELAY $paths_i]]
          set logic_delay [format $myf [get_property DATAPATH_LOGIC_DELAY $paths_i]]
          set net_delay   [format $myf [get_property DATAPATH_NET_DELAY $paths_i]]
          if {$path_delay == 0 || $logic_delay == 0} {
            set logic_delay_percent 0.0%
          } else {
            set logic_delay_percent [format "%.0f%%" [expr double($logic_delay)/double($path_delay)*100]]
          }
          if {$path_delay == 0 || $net_delay == 0} {
            set net_delay_percent 0.0%
          } else {
            set net_delay_percent [format "%.0f%%" [expr double($net_delay)/double($path_delay)*100]]
          }
          set skew [get_property SKEW $paths_i]
          if {[llength $skew] == 0} {
            set skew inf
          } else {
            set skew [format $myf $skew]
          }
          set uncertainty [get_property UNCERTAINTY $paths_i]
          if {[llength $uncertainty] == 0} {
            set uncertainty inf
          } else {
            set uncertainty [format $myf $uncertainty]
          }
          set compensation [get_property INTER_SLR_COMPENSATION $paths_i]
          if {[llength $compensation]==0} {set compensation "Empty"}
          puts $fid "$path_type, $start_cell, $end_cell, $start_point, $end_point, $start_clk, $end_clk, $req, $slack, $logic_level, $num_luts, $path_delay, $logic_delay, $logic_delay_percent, $net_delay, $net_delay_percent, $skew, $uncertainty, $compensation"
        }
        close $fid
        puts "CSV file $fn has been created."
      }
    
      # 生成单元和BEL对应关系的CSV报告
      proc report_cell_bel {fn cell bel} {
        set fid [open ${fn}.csv w]
        puts $fid "#\n# File created on [clock format [clock seconds]] \n#\n"
        puts $fid "CellName, BelName"
        foreach i_cell $cell i_bel $bel {
          puts $fid "$i_cell, $i_bel"
        }
        close $fid
        puts "Cells and their bels are recorded"
      }
    }

    tcl/modules/design_analysis.tcl

    面向顶层设计质量的综合性分析模块,覆盖时钟特性(频率、偏斜、根节点)、拥塞、CDC、QoR、方法学检查等,是评估设计健康度的关键入口。

    #########################################################################################
    ## Design and Clock analysis functions
    #########################################################################################
    namespace eval timing_analysis {
      # 生成时钟特性报告
      proc report_clock_characteristics {sn {en 1}} {
        if {$en==1} {
          set myf "%.2f"
          set max_paths 10
          set fn ${sn}_clock_characteristics_analysis
          set fid [open ${fn}.csv w]
          puts $fid "#\n# File created on [clock format [clock seconds]] \n#\n"
          puts $fid "Name, Driver, Freq, Period, Skew(Setup), Skew(Hold), ClockRoot, CrossingSLRs, FlatPinCnt, HighPriority, UserClkRoot"
          set gclk_net [get_nets -hier -filter "TYPE==GLOBAL_CLOCK" -top_net_of_hierarchical_group]
          if {[llength $gclk_net]>0} {
            foreach i_gclk_net $gclk_net {
              set i_gclk [get_clocks -of $i_gclk_net -quiet]
              if {[llength $i_gclk]>0} {
                set i_period [get_property PERIOD $i_gclk]
                set i_freq [format $myf [expr 1000.0/$i_period]]
                set i_clock_root [get_property CLOCK_ROOT $i_gclk_net]
                if {[llength $i_clock_root]==0} {set i_clock_root "Unknown"}
                set i_crossing_slrs [get_property CROSSING_SLRS $i_gclk_net]
                if {[llength $i_crossing_slrs]==0} {set i_crossing_slrs "Unknown"}
                set i_flat_pin_cnt [get_property FLAT_PIN_COUNT $i_gclk_net]
                if {[llength $i_flat_pin_cnt]==0} {set i_flat_pin_cnt "Unknown"}
                set i_cell [get_cells -of [get_pins -of $i_gclk_net -leaf -filter "DIRECTION==OUT"]]
                set i_driver [get_property REF_NAME $i_cell]
                set i_high_priority [get_property HIGH_PRIORITY $i_gclk_net]
                if {[llength $i_high_priority]==0} {set i_high_priority "Unknown"}
                set i_user_clock_root [get_property USER_CLOCK_ROOT $i_gclk_net]
                if {[llength $i_user_clock_root]==0} {set i_user_clock_root "Unknown"}
    
                set i_paths_setup [get_timing_paths -from $i_gclk -to $i_gclk -setup -max_paths $max_paths -nworst 1]
                set setup_skew_abs [list]
                if {[llength $i_paths_setup]>0} {
                  set setup_skew_val [get_property SKEW $i_paths_setup]
                  foreach i_setup_skew_val $setup_skew_val {
                    lappend setup_skew_abs [expr abs($i_setup_skew_val)]
                  }
                  set max_setup_skew [lindex [lsort -decreasing $setup_skew_abs] 0]
                  set i_paths_hold [get_timing_paths -from $i_gclk -to $i_gclk -hold -max_paths $max_paths -nworst 1]
                  set hold_skew_abs [list]
                  set hold_skew_val [get_property SKEW $i_paths_hold]
                  foreach i_hold_skew_val $hold_skew_val {
                    lappend hold_skew_abs [expr abs($i_hold_skew_val)]
                  }
                  set max_hold_skew [lindex [lsort -decreasing $hold_skew_abs] 0]
                  puts $fid "$i_gclk, $i_driver, $i_freq, $i_period, $max_setup_skew, $max_hold_skew, $i_clock_root, $i_crossing_slrs, $i_flat_pin_cnt, $i_high_priority, $i_user_clock_root"
                }
              }
            }
            close $fid
          }
          puts "Complete section $sn: report_clock_characteristics"
        }
      }
    
      # 生成时钟网络报告
      proc my_clock_networks {sn {en 1}} {
        if {$en==1} {
          puts "Start section $sn: report_clock_networks"
          set fn ${sn}_clock_networks_analysis
          report_clock_networks -name $fn -file ${fn}.rpt
          puts "Complete section $sn: report_clock_networks"
        }
      }
    
      # 生成时钟交互报告
      proc my_clock_interaction {sn {en 1}} {
        if {$en==1} {
          puts "Start section $sn: report_clock_interaction"
          set fn ${sn}_clock_interaction_analysis
          report_clock_interaction -name $fn -file ${fn}.rpt
          puts "Complete section $sn: report_clock_interaction"
        }
      }
    
      # 报告设计的拥塞程度
      proc report_congestion_level {sn {en 1}} {
        if {$en==1} {
          puts "Start section $sn: report_congestion_level"
          set fn ${sn}_congestion_analysis
          report_design_analysis -analysis_type {Congestion Complexity} -name $fn -file ${fn}.rpt
          puts "Complete section $sn: report_congestion_level"
        }
      }
    
      # 生成设计方法学报告
      proc my_report_methodology {sn {en 1} } {
        if {$en==1} {
          puts "Start section $sn: report_methodology"
          set fn ${sn}_methodology_analysis
          report_methodology -file ${fn}.rpt -name $fn
          puts "Complete section $sn: report_methodology"
        }
      }
    
      # 生成failfast报告
      proc report_failfast {sn slrs dcp_type {en 1} } {
        set syn_dcp {1 2}
        if {$en==1} {
          puts "Start section $sn: report_failfast"
          set fn ${sn}_failfast_analysis
          if {$slrs==1} {
            xilinx::designutils::report_failfast -detailed_reports impl -file ${fn}.rpt
          } else {
            if {$dcp_type in $syn_dcp} {
              xilinx::designutils::report_failfast -detailed_reports impl -file ${fn}.rpt
            } else {
              xilinx::designutils::report_failfast -by_slr -detailed_reports impl -file ${fn}.rpt
            }
          }
          puts "Complete section $sn: report_failfast"
        }
      }
    
      # 生成资源利用率报告
      proc my_report_utilization {sn {en 1}} {
        if {$en==1} {
          puts "Start section $sn: report_utilization"
          set fn ${sn}_util_analysis
          report_utilization -name $fn -file ${fn}.rpt
          puts "Complete section $sn: report_utilization"
        }
      }
    
      # 生成CDC(时钟域交叉)报告
      proc my_report_cdc {sn {en 1}} {
        if {$en==1} {
          puts "Start section $sn: report_cdc"
          set fn ${sn}_cdc_analysis
          report_cdc -no_header -severity {Critical} -name $fn -file ${fn}.rpt
          puts "Complete section $sn: report_cdc"
        }
      }
    
      # 生成例外情况的总结报告
      proc my_report_exceptions {sn {en 1}} {
        if {$en==1} {
          puts "Start section $sn: report_exceptions"
          set fn ${sn}_exceptions_analysis
          report_exceptions -summary -name ${fn} -file ${fn}.rpt
          puts "Complete section $sn: report_exceptions"
        }
      }
    
      # 生成高扇出网络报告
      proc high_fanout_nets {sn slrs dcp_type {en 1}} {
        set syn_dcp {1 2}
        if {$en==1} {
          puts "Start section $sn: report_high_fanout_nets"
          set fn ${sn}_high_fanout_nets_analysis
          if {$slrs==1} {
            report_high_fanout_nets -name ${fn} -file ${fn}.rpt
          } else {
            if {$dcp_type in $syn_dcp} {
              report_high_fanout_nets -name ${fn} -file ${fn}.rpt
            } else {
              report_high_fanout_nets -slr -name ${fn} -file ${fn}.rpt
            }
          }
          puts "Complete section $sn: report_high_fanout_nets"
        }
      }
    
      # 生成RAM利用率报告
      proc my_ram_util {sn {en 1}} {
        if {$en==1} {
          puts "Start section $sn: report_ram_utilization"
          set fn ${sn}_ram_util
          report_ram_utilization -file ${fn}.rpt
        }
      }
    }

    tcl/modules/dsp_analysis.tcl

    专用于 DSP 单元的寄存器使用合规性检查,重点识别 MREG(乘法器寄存器)与 PREG(累加器寄存器)未启用但存在乘法操作的场景,避免因流水线断裂导致的性能损失。

    #########################################################################################
    ## DSP analysis functions
    #########################################################################################
    namespace eval timing_analysis {
      # 获取没有使用MREG寄存器的DSP并生成报告
      proc get_no_mreg_dsp {sn used_dsp {en 1}} {
        if {$en==1} {
          puts "Start section $sn: get_no_mreg_dsp get dsp without using embedded MREG"
          set used_dsp_0 [filter $used_dsp "AREG==0 && BREG==0 && MREG==0"]
          set used_dsp_1 [filter $used_dsp "AREG==1 && BREG==1 && MREG==1"]
          set used_dsp_violated {}
          if {[llength $used_dsp_0]>0} {
            foreach i_used_dsp $used_dsp_0 {
              set d [get_pins -of $i_used_dsp -filter "REF_PIN_NAME=~D
  • "]           if {"1" in [get_property IS_CONNECTED $d]} {             if {[get_property USE_MULT $i_used_dsp]!="NONE"} {               lappend used_dsp_violated $i_used_dsp             }           }         }       }       if {[llength $used_dsp_1]>0} {         foreach i_used_dsp $used_dsp_1 {           if {[get_property USE_MULT $i_used_dsp]=="MULTIPLY"} {             if {[get_property MREG $i_used_dsp]==0} {               lappend used_dsp_violated $i_used_dsp             }           }         }       }       set used_dsp_violated [lsort -unique $used_dsp_violated]       if {[llength $used_dsp_violated]>0} {         show_objects $used_dsp_violated -name ${sn}_dsp_mreg_violated         set fn ${sn}_dsp_mreg_violated         report_critical_cells $fn $used_dsp_violated       }       puts "Complete section $sn: get_no_mreg_dsp"     }   }   # 获取没有使用PREG寄存器的DSP并生成报告   proc get_no_preg_dsp {sn used_dsp {en 1}} {     if {$en==1} {       puts "Start section $sn: get_no_preg_dsp get dsp without using embedded PREG"       set used_dsp_violated [filter $used_dsp "PREG==0 && USE_MULT==MULTIPLY"]       if {[llength $used_dsp_violated]>0} {         show_objects $used_dsp_violated -name ${sn}_dsp_preg_violated         set fn ${sn}_dsp_preg_violated         report_critical_cells $fn $used_dsp_violated       }       puts "Complete section $sn: get_no_preg_dsp"     }   } }

  • tcl/modules/params_utils.tcl

    参数管理中枢,定义了三类运行模式(用户自定义/默认/完整),并校验 vivado_version(正则 20[12][0-9][.][1-4])、dcp_type(1–6)、mode(1–3)等关键输入的合法性,确保脚本在受控前提下执行。

    package provide params_utils 1.0
    
    namespace eval ::params_utils {
    
        proc set_defaults {} {
            uplevel 1 {
                #########################################################################################
                # 设置主要参数
                #########################################################################################
                if {![info exists vivado_version]} { set vivado_version 2022.2 }     ;# Vivado版本号
                if {![info exists dcp_type]}       { set dcp_type       5 }          ;# DCP类型
                if {![info exists dcp_is_open]}    { set dcp_is_open    0 }          ;# DCP文件状态
                if {![info exists dcp_name]}       { set dcp_name       *routed.dcp };# DCP文件名模式
                if {![info exists mode]}           { set mode           1 }          ;# 运行模式
                if {![info exists is_accurate]}    { set is_accurate    1 }          ;# 时序计算精度
                if {![info exists max_paths]}      { set max_paths      100 }        ;# 最大路径数
    
                #########################################################################################
                # 检查Vivado版本是否合法
                # 使用正则表达式验证版本格式是否为dddd.d,其中d为数字,如2022.2
                if {[regexp {20[12][0-9][.][1-4]$} $vivado_version]==0} {
                    puts "Vivado version is not correct"
                    return -code 1
                }
    
                #########################################################################################
                # 用户自定义参数设置(模式1)
                # 以下参数控制各时序分析功能是否启用(1=启用, 0=禁用)
                if {![info exists user_check_timing]}                 { set user_check_timing                 1 }
                if {![info exists user_timing_summary]}              { set user_timing_summary              1 }
                if {![info exists user_neg_slack_timing]}            { set user_neg_slack_timing            1 }
                if {![info exists user_block2block]}                 { set user_block2block                 1 }
                if {![info exists user_high_logic_level]}           { set user_high_logic_level           1 }
                if {![info exists user_crossing_slrs]}              { set user_crossing_slrs              0 }
                if {![info exists user_get_cells_crossing_slrs]}    { set user_get_cells_crossing_slrs    0 }
                if {![info exists user_check_bram]}                 { set user_check_bram                 1 }
                if {![info exists user_check_uram]}                 { set user_check_uram                 1 }
                if {![info exists user_check_dsp_mreg]}             { set user_check_dsp_mreg             1 }
                if {![info exists user_check_dsp_preg]}             { set user_check_dsp_preg             1 }
                if {![info exists user_check_lower_srl]}            { set user_check_lower_srl            1 }
                if {![info exists user_check_combined_lut]}         { set user_check_combined_lut         1 }
                if {![info exists user_check_muxfx]}                { set user_check_muxfx                1 }
                if {![info exists user_check_latch]}                { set user_check_latch                1 }
                if {![info exists user_clock_characteristics]}      { set user_clock_characteristics      1 }
                if {![info exists user_clock_networks]}             { set user_clock_networks             1 }
                if {![info exists user_cdc]}                       { set user_cdc                       1 }
                if {![info exists user_clock_interaction]}           { set user_clock_interaction           1 }
                if {![info exists user_congestion]}                 { set user_congestion                 1 }
                if {![info exists user_exceptions]}                 { set user_exceptions                 1 }
                if {![info exists user_util]}                       { set user_util                       1 }
                if {![info exists user_methodology]}                { set user_methodology                1 }
                if {![info exists user_qor]}                         { set user_qor                         0 }
                if {![info exists user_failfast]}                   { set user_failfast                   0 }
                if {![info exists user_high_fanout_nets]}          { set user_high_fanout_nets          1 }
                if {![info exists user_ram_util]}                  { set user_ram_util                  1 }
    
                # 默认参数设置(模式2)
                # 以下参数为默认模式下各时序分析功能的启用状态(1=启用, 0=禁用)
                if {![info exists default_check_timing]}            { set default_check_timing            1 }
                if {![info exists default_timing_summary]}         { set default_timing_summary         1 }
                if {![info exists default_neg_slack_timing]}       { set default_neg_slack_timing       1 }
                if {![info exists default_block2block]}            { set default_block2block            1 }
                if {![info exists default_high_logic_level]}       { set default_high_logic_level       0 }
                if {![info exists default_crossing_slrs]}          { set default_crossing_slrs          1 }
                if {![info exists default_get_cells_crossing_slrs]}{ set default_get_cells_crossing_slrs 0 }
                if {![info exists default_check_bram]}             { set default_check_bram             1 }
                if {![info exists default_check_uram]}             { set default_check_uram             1 }
                if {![info exists default_check_dsp_mreg]}         { set default_check_dsp_mreg         1 }
                if {![info exists default_check_dsp_preg]}         { set default_check_dsp_preg         1 }
                if {![info exists default_check_lower_srl]}        { set default_check_lower_srl        0 }
                if {![info exists default_check_combined_lut]}     { set default_check_combined_lut     0 }
                if {![info exists default_check_muxfx]}           { set default_check_muxfx           0 }
                if {![info exists default_check_latch]}           { set default_check_latch           0 }
                if {![info exists default_clock_characteristics]}  { set default_clock_characteristics  0 }
                if {![info exists default_clock_networks]}        { set default_clock_networks        1 }
                if {![info exists default_cdc]}                    { set default_cdc                    0 }
                if {![info exists default_clock_interaction]}      { set default_clock_interaction      1 }
                if {![info exists default_congestion]}             { set default_congestion             0 }
                if {![info exists default_exceptions]}             { set default_exceptions             0 }
                if {![info exists default_util]}                    { set default_util                   0 }
                if {![info exists default_methodology]}            { set default_methodology            1 }
                if {![info exists default_qor]}                    { set default_qor                    0 }
                if {![info exists default_failfast]}              { set default_failfast               0 }
                if {![info exists default_high_fanout_nets]}       { set default_high_fanout_nets       0 }
                if {![info exists default_ram_util]}              { set default_ram_util               1 }
            }
        }
    
        proc check_dcp_and_mode {} {
            uplevel 1 {
                #########################################################################################
                # 检查dcp_type和mode参数是否在合理范围内
                #########################################################################################
                # 检查dcp_type是否在1-6范围内
                if {[regexp {^[1-6]$} $dcp_type] == 0} {
                    puts "dcp_type的取值范围应在1到6之间."
                    return -code 1
                } else {
                    puts "dcp_type变量取值合理,继续执行."
                }
    
                # 检查mode是否在1-3范围内
                if {[regexp {^[1-3]$} $mode] == 0} {
                    puts "mode的取值范围应在1到3之间."
                    return -code 1
                } else {
                    puts "mode变量取值合理,继续执行."
                }
    
                # 根据dcp_type设置当前阶段名称
                switch -exact -- $dcp_type {
                    1 {set phase "Syn"}       ;# 综合阶段
                    2 {set phase "Opt"}       ;# 优化阶段
                    3 {set phase "Place"}     ;# 布局阶段
                    4 {set phase "PhyPlace"}  ;# 物理优化后(布局后)阶段
                    5 {set phase "Route"}     ;# 布线阶段
                    6 {set phase "PhyRoute"}  ;# 物理优化后(布线后)阶段
                    default {set phase "X"}   ;# 未知阶段
                }
    
                #########################################################################################
                # 根据运行模式确定具体的检查项
                #########################################################################################
                switch -exact $mode {
                    1 { ;# 模式1: 用户定义模式
                        set en_check_timing            $user_check_timing            ;# 时序检查启用标志
                        set en_timing_summary          $user_timing_summary          ;# 时序摘要报告启用标志
                        set en_neg_slack_timing        $user_neg_slack_timing        ;# 负时序裕度路径报告启用标志
                        set en_block2block             $user_block2block             ;# 块间路径报告启用标志
                        set en_high_logic_level        $user_high_logic_level        ;# 高逻辑级数路径报告启用标志
                        set en_crossing_slrs           $user_crossing_slrs           ;# 跨SLR路径报告启用标志
                        set en_get_cells_crossing_slrs $user_get_cells_crossing_slrs ;# 获取跨SLR单元启用标志
                        set en_check_bram              $user_check_bram              ;# BRAM检查启用标志
                        set en_check_uram              $user_check_uram              ;# URAM检查启用标志
                        set en_check_dsp_mreg          $user_check_dsp_mreg          ;# DSP MREG检查启用标志
                        set en_check_dsp_preg          $user_check_dsp_preg          ;# DSP PREG检查启用标志
                        set en_check_lower_srl         $user_check_lower_srl         ;# 低深度SRL检查启用标志
                        set en_check_combined_lut      $user_check_combined_lut      ;# 组合LUT检查启用标志
                        set en_check_muxfx             $user_check_muxfx             ;# MUXFX检查启用标志
                        set en_check_latch             $user_check_latch             ;# 锁存器检查启用标志
                        set en_clock_characteristics   $user_clock_characteristics   ;# 时钟特性报告启用标志
                        set en_clock_networks          $user_clock_networks          ;# 时钟网络报告启用标志
                        set en_cdc                     $user_cdc                     ;# 时钟域交叉(CDC)报告启用标志
                        set en_clock_interaction       $user_clock_interaction       ;# 时钟交互报告启用标志
                        set en_congestion              $user_congestion              ;# 拥塞报告启用标志
                        set en_exceptions              $user_exceptions              ;# 时序例外报告启用标志
                        set en_util                    $user_util                    ;# 资源利用率报告启用标志
                        set en_methodology             $user_methodology             ;# 设计方法学报告启用标志
                        set en_qor                     $user_qor                     ;# QOR报告启用标志
                        set en_failfast                $user_failfast                ;# 快速失败分析启用标志
                        set en_high_fanout_nets        $user_high_fanout_nets        ;# 高扇出网络报告启用标志
                        set en_ram_util                $user_ram_util                ;# RAM利用率报告启用标志
                    }
                    2 { ;# 模式2: 默认模式
                        set en_check_timing             $default_check_timing             ;# 时序检查启用标志
                        set en_timing_summary           $default_timing_summary           ;# 时序摘要报告启用标志
                        set en_neg_slack_timing         $default_neg_slack_timing         ;# 负时序裕度路径报告启用标志
                        set en_block2block              $default_block2block              ;# 块间路径报告启用标志
                        set en_high_logic_level         $default_high_logic_level         ;# 高逻辑级数路径报告启用标志
                        set en_crossing_slrs            $default_crossing_slrs            ;# 跨SLR路径报告启用标志
                        set en_get_cells_crossing_slrs  $default_get_cells_crossing_slrs  ;# 获取跨SLR单元启用标志
                        set en_check_bram               $default_check_bram               ;# BRAM检查启用标志
                        set en_check_uram               $default_check_uram               ;# URAM检查启用标志
                        set en_check_dsp_mreg           $default_check_dsp_mreg           ;# DSP MREG检查启用标志
                        set en_check_dsp_preg           $default_check_dsp_preg           ;# DSP PREG检查启用标标
                        set en_check_lower_srl          $default_check_lower_srl          ;# 低深度SRL检查启用标志
                        set en_check_combined_lut       $default_check_combined_lut       ;# 组合LUT检查启用标志
                        set en_check_muxfx              $default_check_muxfx              ;# MUXFX检查启用标志
                        set en_check_latch              $default_check_latch              ;# 锁存器检查启用标志
                        set en_clock_characteristics    $default_clock_characteristics    ;# 时钟特性报告启用标志
                        set en_clock_networks           $default_clock_networks           ;# 时钟网络报告启用标志
                        set en_cdc                      $default_cdc                      ;# 时钟域交叉(CDC)报告启用标志
                        set en_clock_interaction        $default_clock_interaction        ;# 时钟交互报告启用标志
                        set en_congestion               $default_congestion               ;# 拥塞报告启用标志
                        set en_exceptions               $default_exceptions               ;# 时序例外报告启用标志
                        set en_util                     $default_util                     ;# 资源利用率报告启用标志
                        set en_methodology              $default_methodology              ;# 设计方法学报告启用标志
                        set en_qor                      $default_qor                      ;# QOR报告启用标志
                        set en_failfast                 $default_failfast                 ;# 快速失败分析启用标志
                        set en_high_fanout_nets         $default_high_fanout_nets         ;# 高扇出网络报告启用标志
                        set en_ram_util                 $default_ram_util                 ;# RAM利用率报告启用标志
                    }
                    3 { ;# 模式3: 完整模式
                        set en_check_timing             1 ;# 启用时序检查
                        set en_timing_summary           1 ;# 启用时序摘要报告
                        set en_neg_slack_timing         1 ;# 启用负时序裕度路径报告
                        set en_block2block              1 ;# 启用块间路径报告
                        set en_high_logic_level         1 ;# 启用高逻辑级数路径报告
                        set en_crossing_slrs            1 ;# 启用跨SLR路径报告
                        set en_get_cells_crossing_slrs  1 ;# 启用获取跨SLR单元
                        set en_check_bram               1 ;# 启用BRAM检查
                        set en_check_uram               1 ;# 启用URAM检查
                        set en_check_dsp_mreg           1 ;# 启用DSP MREG检查
                        set en_check_dsp_preg           1 ;# 启用DSP PREG检查
                        set en_check_lower_srl          1 ;# 启用低深度SRL检查
                        set en_check_combined_lut       1 ;# 启用组合LUT检查
                        set en_check_muxfx              1 ;# 启用MUXFX检查
                        set en_check_latch              1 ;# 启用锁存器检查
                        set en_clock_characteristics    1 ;# 启用时钟特性报告
                        set en_clock_networks           1 ;# 启用时钟网络报告
                        set en_cdc                      1 ;# 启用时钟域交叉(CDC)报告
                        set en_clock_interaction        1 ;# 启用时钟交互报告
                        set en_congestion               1 ;# 启用拥塞报告
                        set en_exceptions               1 ;# 启用时序例外报告
                        set en_util                     1 ;# 启用资源利用率报告
                        set en_methodology              1 ;# 启用设计方法学报告
                        set en_qor                      1 ;# 启用QOR报告
                        set en_failfast                 1 ;# 启用快速失败分析
                        set en_high_fanout_nets         1 ;# 启用高扇出网络报告
                        set en_ram_util                 1 ;# 启用RAM利用率报告
                    }
                    default {
                        puts "Invalid mode, should be 1, 2 or 3"
                        return -code 1
                    }
                }
            }
        }
    }

    tcl/modules/report_utils.tcl

    封装 report_failfast 命令的健壮调用逻辑,自动探测本地 XilinxTclStore 存在性,若缺失则触发 git clone 下载,确保脚本在无预装环境下的可运行性。

    package provide report_utils 1.0
    
    proc ::xilinx::designutils::report_failfast {args} {
      if {[info commands ::tclapp::xilinx::designutils::report_failfast] eq ""} {
        if {[info exists ::script_dir]} {
          set project_root [file normalize [file join $::script_dir ..]]
          set candidate_paths [list \
            [file normalize [file join $project_root XilinxTclStore/tclapp/xilinx/designutils]] \
          ]
          foreach local_designutils_path $candidate_paths {
            if {[file exists $local_designutils_path]} {
              if {[lsearch -exact $::auto_path $local_designutils_path] == -1} {
                lappend ::auto_path $local_designutils_path
              }
              catch {package require ::tclapp::xilinx::designutils}
              break
            }
          }
          if {[info commands ::tclapp::xilinx::designutils::report_failfast] eq ""} {
            # Use repo_utils to clone if needed
            ::repo_utils::ensure_tcl_store $project_root
    
            set clone_root [file normalize [file join $project_root XilinxTclStore]]
            set designutils_path [file normalize [file join $clone_root tclapp/xilinx/designutils]]
            if {[file exists $designutils_path]} {
              if {[lsearch -exact $::auto_path $designutils_path] == -1} {
                lappend ::auto_path $designutils_path
              }
              catch {package require ::tclapp::xilinx::designutils}
            }
          }
        }
      }
      if {[info commands ::tclapp::xilinx::designutils::report_failfast] ne ""} {
        tailcall ::tclapp::xilinx::designutils::report_failfast {*}$args
      }
      puts "report_failfast is disabled or designutils package is not available"
      return -code ok
    }

    tcl/modules/repo_utils.tcl

    提供 XilinxTclStore 仓库的按需克隆能力,通过 git clone --depth 1 实现轻量拉取,是 report_utils 模块的底层依赖。

    package provide repo_utils 1.0
    
    namespace eval ::repo_utils {
        variable script_dir [file dirname [file normalize [info script]]]
    
        # Clone XilinxTclStore if needed
        proc ensure_tcl_store {project_root} {
            set clone_root [file normalize [file join $project_root XilinxTclStore]]
    
            # Check if directory exists and is not empty
            set need_clone 0
            if {![file exists $clone_root]} {
                set need_clone 1
            } else {
                # Check if empty
                if {[llength [glob -nocomplain -directory $clone_root *]] == 0} {
                    set need_clone 1
                    # If empty directory exists, we might need to remove it for git clone to work easily, 
                    # or just clone into it (git allows cloning into empty dir).
                }
            }
    
            if {$need_clone} {
                set git_bin git
                if {[info exists ::env(GIT_BIN)] && $::env(GIT_BIN) ne ""} {
                    set git_bin $::env(GIT_BIN)
                }
                puts "Cloning XilinxTclStore to $clone_root..."
                if {[catch {exec $git_bin clone --depth 1 https://github.com/Xilinx/XilinxTclStore.git $clone_root} err]} {
                    puts "Warning: failed to clone XilinxTclStore: $err"
                    return 0
                } else {
                    puts "Cloned XilinxTclStore into $clone_root"
                    return 1
                }
            }
            return 1
        }
    }

    tcl/modules/slr_analysis.tcl

    专为多 SLR(Super Logic Region)架构设计,分析跨 SLR 网络的发送/接收单元及其 BEL 映射,识别未使用 Laguna 寄存器的跨域路径,并生成高逻辑级数(LL)与高扇出(FO)路径报告。

    #########################################################################################
    ## SLR analysis functions
    #########################################################################################
    namespace eval timing_analysis {
      # 生成单元和BEL对应关系的CSV报告
      # 参数: fn - 文件名前缀,用于生成最终的CSV文件名
      #       cell - 单元列表,包含需要报告的单元
      #       bel - 对应的BEL列表,与cell列表一一对应
      # 返回值: 无,直接生成CSV文件
      # 功能: 将每个单元及其对应的BEL名称写入CSV文件,便于分析单元在FPGA中的物理位置
      proc report_cell_bel {fn cell bel} {
        set fid [open ${fn}.csv w]
        puts $fid "#\n# File created on [clock format [clock seconds]] \n#\n"
        puts $fid "CellName, BelName"
        foreach i_cell $cell i_bel $bel {
          puts $fid "$i_cell, $i_bel"
        }
        close $fid
        puts "Cells and their bels are recorded"
      }
    
      # 获取没有使用Laguna寄存器的单元并生成报告
      # 参数: sn - 序列号,用于标识报告文件和日志信息
      #       xnets - 网络列表,包含跨SLR的网络
      #       en - 使能标志,1表示生成报告,0表示跳过
      # 返回值: 无,直接生成报告文件
      # 功能: 分析跨SLR网络的发送和接收单元,筛选出没有使用Laguna寄存器的单元,并生成详细报告
      proc get_cells_not_use_laguna {sn xnets {en 1}} {
        if {$en==1} {
          puts "Get cells without using laguna registers"
          set tx_cells [get_cells -of [get_pins -of [get_nets $xnets] -leaf -filter "DIRECTION==OUT"]]
          set rx_cells [get_cells -of [get_pins -of [get_nets $xnets] -leaf -filter "DIRECTION==IN"]]
          set tx_bels  [get_bels -of $tx_cells]
          set rx_bels  [get_bels -of $rx_cells]
          set tx_nlaguna_bels [filter $tx_bels "NAME !~ LAGUNA*"]
          set tx_laguna_bels  [filter $tx_bels "NAME =~ LAGUNA*"]
          set rx_nlaguna_bels [filter $rx_bels "NAME !~ LAGUNA*"]
          set rx_laguna_bels  [filter $rx_bels "NAME =~ LAGUNA*"]
          if {[llength $tx_cells]>0} {
            set fn ${sn}_tx_crossing_slr_analysis
            show_objects $tx_cells -name $fn
            report_cell_bel $fn $tx_cells $tx_bels
          }
          if {[llength $rx_cells]>0} {
            set fn ${sn}_rx_crossing_slr_analysis
            show_objects $rx_cells -name $fn
            report_cell_bel $fn $rx_cells $rx_bels
          }
          puts "Complete: get cells and their bels crossing SLRs"
        }
      }
    
      # 报告跨SLR的时序路径
      # 参数: sn - 序列号,用于标识报告文件和日志信息
      #       dcp_type - DCP类型,3/4表示布局DCP,5/6表示布线DCP
      #       en_get_cells_crossing_slrs - 是否获取跨SLR单元,1表示获取,0表示不获取
      #       en - 使能标志,1表示生成报告,0表示跳过
      # 返回值: 无,直接生成跨SLR时序路径报告
      # 功能: 根据DCP类型获取跨SLR网络,分析通过这些网络的时序路径,筛选出逻辑级别过高或扇出过大的路径,并生成详细报告
      proc report_paths_crossing_slrs {sn dcp_type en_get_cells_crossing_slrs {en 1}} {
        set place_dcp {3 4}
        set route_dcp {5 6}
        if {$en==1} {
          if {$dcp_type in $place_dcp} {
            set xneta [xilinx::designutils::get_inter_slr_nets]
            set xnets [filter $xneta "TYPE != GLOBAL_CLOCK"]
          } elseif {$dcp_type in $route_dcp} {
            set xnets [xilinx::designutils::get_sll_nets]
          } else {
            puts "The nets crossing SLRs cannot be obtained under this DCP"
            return 
          }
          set xnets_len [llength $xnets]
          if {$xnets_len>0} {
            set paths [get_timing_paths -nworst 1 -max $xnets_len -through $xnets \
            -filter {INTER_SLR_COMPENSATION != ""}]
            get_cells_not_use_laguna $sn $xnets $en_get_cells_crossing_slrs
          }
          if {[llength $paths]>0} {
            set paths_ll_1 [filter $paths "LOGIC_LEVELS > 0"]
            set paths_fo_1 [filter $paths "MAX_FANOUT > 1"]
            if {[llength $paths_ll_1]} {
              set fn ${sn}_paths_crossing_slr_LL_violation
              report_timing -of $paths_ll_1 -name $fn
              create_timing_report $fn $paths_ll_1
            }
            if {[llength $paths_fo_1]} {
              set fn ${sn}_paths_crossing_slr_FO_violation
              report_timing -of $paths_fo_1 -name $fn
              create_timing_report $fn $paths_fo_1
            }
          }
          puts "Complete section $sn: report_paths_crossing_slrs"
        }
      }
    }

    tcl/modules/slr_utils.tcl

    提供 get_inter_slr_netsget_sll_nets 两个核心命令,前者用于布局阶段(post_place)提取跨 SLR 网络,后者用于布线阶段(post_route)提取 SLL(SLR Link Layer)节点关联网络,是跨域分析的数据源头。

    package provide slr_utils 1.0
    
    namespace eval ::xilinx::designutils {}
    
    proc ::xilinx::designutils::lshift {inputlist} {
      upvar $inputlist argv
      set arg [lindex $argv 0]
      set argv [lrange $argv 1 end]
      return $arg
    }
    
    proc ::xilinx::designutils::get_inter_slr_nets {args} {
      set error 0
      set help 0
      set from {}
      set to {}
      while {[llength $args]} {
        set name [::xilinx::designutils::lshift args]
        switch -regexp -- $name {
          -from -
          {^-f(r(om?)?)?$} {
            set from [string toupper [::xilinx::designutils::lshift args]]
          }
          -to -
          {^-to?$} {
            set to [string toupper [::xilinx::designutils::lshift args]]
          }
          -usage -
          {^-u(s(a(ge?)?)?)?$} {
            set help 1
          }
          default {
            if {[string match "-*" $name]} {
              incr error
            } else {
              incr error
            }
          }
        }
      }
      if ($help) {
        return {}
      }
      if {($from == {} && $to != {}) || ($from != {} && $to == {})} {
        incr error
      } elseif {$from != {} && $to != {}} {
        set SLRs [get_slrs -quiet]
        if {[lsearch $SLRs $from] == -1} {
          incr error
        }
        if {[lsearch $SLRs $to] == -1} {
          incr error
        }
        if {($error == 0) && ($from == $to)} {
          incr error
        }
      }
      if {$error} {
        error "get_inter_slr_nets: invalid arguments"
      }
      set inter_slr_nets [list]
      if {($from == {}) && ($to == {})} {
        catch {unset ar}
        foreach SLR [get_slrs -quiet] {
          set cells [get_cells -quiet -of [get_sites -quiet -of $SLR]]
          set nets [get_nets -quiet -of $cells -filter {(TYPE != POWER) && (TYPE != GROUND) && (ROUTE_STATUS != INTRASITE)}]
          set props [lsort -uniq [get_property -quiet PARENT $nets]]
          foreach prop $props { lappend ar($prop) $SLR }
        }
        set slrs [list]
        foreach net [array names ar] {
          if {[llength $ar($net)] > 1} {
            lappend slrs $net
          }
        }
        set inter_slr_nets [get_nets -quiet $slrs]
        return $inter_slr_nets
      }
      catch {unset ar}
      set fromSlr $from
      set toSlr $to
      set from_cells [get_cells -quiet -of [get_sites -quiet -of [get_slrs -quiet $fromSlr]]]
      set from_nets [get_nets -quiet -of $from_cells -filter {(TYPE != POWER) && (TYPE != GROUND) && (ROUTE_STATUS != INTRASITE)}]
      set from_props [lsort -uniq [get_property -quiet parent $from_nets]]
      foreach prop $from_props { lappend ar($prop) $fromSlr }
      set to_cells [get_cells -quiet -of [get_sites -quiet -of [get_slrs -quiet $toSlr]]]
      set to_nets [get_nets -quiet -of $to_cells -filter {(TYPE != POWER) && (TYPE != GROUND) && (ROUTE_STATUS != INTRASITE)}]
      set to_props [lsort -uniq [get_property -quiet parent $to_nets]]
      foreach prop $to_props { lappend ar($prop) $toSlr }
      set slrs [list]
      foreach net [array names ar] {
        if {[llength $ar($net)] > 1} {
          lappend slrs $net
        }
      }
      set inter_slr_nets [get_nets -quiet $slrs]
      return $inter_slr_nets
    }
    
    proc ::xilinx::designutils::get_sll_nets {} {
      set sll_nodes [get_nodes -quiet -of_objects [get_tiles -quiet -of_objects [get_slrs -quiet ]] -filter {IS_CROSSING_SLRS}]
      if {[llength $sll_nodes] == 0} {
        return {}
      }
      return [get_nets -quiet -of $sll_nodes]
    }

    tcl/modules/timing_checks.tcl

    封装标准 Vivado 时序检查命令,包括 check_timingreport_timing_summaryget_timing_paths(负裕量路径)、report_block2blockreport_high_logic_level,构成时序收敛闭环的核心环节。

    #########################################################################################
    ## Timing analysis functions
    #########################################################################################
    namespace eval timing_analysis {
      # 执行时序检查并生成报告
      proc my_check_timing {sn {en 1}} {
        if {$en==1} {
          puts "Start section $sn: check_timing"
          check_timing -name ${sn}_check_timing_analysis \
          -file ${sn}_check_timing_analysis.rpt -verbose
          puts "Complete section $sn: check_timing"
        }
      }
    
      # 根据DCP类型生成时序摘要报告
      proc my_timing_summary {sn dcp_type {en 1}} {
        set syn_dcp {1 2}
        if {$en==1} {
          puts "Start section $sn: report_timing_summary"
          if {$dcp_type in $syn_dcp} {
            report_timing_summary -no_check_timing -no_header -setup -max_paths 50 -nworst 1 -unique_pins \
            -name ${sn}_timing_summary_analysis -file ${sn}_timing_summary_analysis.rpt
          } else {
            report_timing_summary -no_check_timing -no_header -max_paths 50 -nworst 1 -unique_pins \
            -name ${sn}_timing_summary_analysis -file ${sn}_timing_summary_analysis.rpt
          }
          puts "Complete section $sn: report_timing_summary"
        }
      }
    
      # 生成负时序裕量路径报告
      proc my_neg_slack_timing_report {sn dcp_type {en 1}} {
        set syn_dcp {1 2}
        if {$en==1} {
          puts "Start section $sn: report_timing: 100 negative slack paths"
          if {$dcp_type in $syn_dcp} {
            set paths [get_timing_paths -setup -max_paths 100 -nworst 1 -unique_pins \
            -slack_lesser_than 0 -quiet]
          } else {
            set paths [get_timing_paths -delay_type min_max -max_paths 100 -nworst 1 \
            -unique_pins -slack_lesser_than 0 -quiet]
          }
          if {[llength $paths]>0} {
            set fn ${sn}_neg_slack_path_violation
            report_timing -of $paths -name $fn
            create_timing_report $fn $paths
            puts "Complete section $sn: report_timing: 100 negative slack paths"
          } else {
            puts "There is no any paths with negative slack"
          }
        }
      }
    
      # 生成块到块的时序路径报告
      proc report_block2block {sn block {en 1}} {
        if {$en==1} {
          puts "Start section $sn: report_block2block: 100 paths from blocks to blocks"
          set fn ${sn}_b2b_violation
          set paths_block2block [get_timing_paths -from $block -to $block -max_paths 100 -quiet]
          if {[llength $paths_block2block]>0} {
            report_timing -of $paths_block2block -name $fn
            create_timing_report $fn $paths_block2block
            puts "Complete section $sn: report_block2block: 100 paths from blocks to blocks"
          } else {
            puts "There is not any path from blocks to blocks"
          }
        }
      }
    
      # 报告在给定时钟下逻辑级别过高的路径
      proc report_high_logic_level {sn clock_name delay {en 1}} {
        if {$en==1} {
          puts "Start section $sn: report_high_logic_level: 10 paths with higher logic level under given clock"
          set clock_period [get_property PERIOD $clock_name]
          set clock_freq   [format "%.2f" [expr 1000.0/$clock_period]]
          set max_ll [expr int(floor(double($clock_period)/double($delay)))]
          set paths [get_timing_paths -max_paths 10 -filter "GROUP==$clock_name && LOGIC_LEVELS>$max_ll" -quiet]
          if {[llength $paths]>0} {
            set fn ${sn}_${clock_name}_${clock_freq}_LL_${max_ll}
            report_timing -of $paths -name $fn
            create_timing_report $fn $paths
          }
          puts "Complete section $sn: report_high_logic_level: $clock_name $clock_freq $max_ll 10 paths"
        }
      }
    }

    tcl/modules/yosys_flow.tcl

    提供 Yosys 合成流程集成能力,当 Vivado 不可用时,自动读取 Verilog 文件、生成 .ys 脚本、调用 yosys 并捕获关键路径延迟,实现跨工具链的时序初步评估。

    package provide yosys_flow 1.0
    
    source [file join [file dirname [info script]] "repo_utils.tcl"]
    
    namespace eval ::yosys_flow {
    
        proc run_flow {script_dir} {
            # Clone XilinxTclStore if needed (using repo_utils)
            set project_root [file normalize [file join $script_dir ..]]
            ::repo_utils::ensure_tcl_store $project_root
    
            # Access configuration variables from the caller's scope
            uplevel 1 {
                # 记录项目根目录,以便处理相对路径
                set project_dir [pwd]
    
                # 提前创建并进入 Report 目录,确保所有输出都在此目录下
                file mkdir Report
                cd ./Report
    
                if {![info exists yosys_bin]} {set yosys_bin yosys}
                if {![info exists yosys_script]} {set yosys_script ""}
    
                if {$yosys_script eq ""} {
                    # Try to auto-generate Yosys script if verilog files and top module are provided
                    if {[info exists yosys_verilog_files] && [info exists yosys_top_module] && [llength $yosys_verilog_files] > 0 && $yosys_top_module ne ""} {
                        set yosys_script "auto_generated.ys"
                        set filelist_name "auto_generated.f"
                        set yfid [open $yosys_script w]
                        set ffid [open $filelist_name w]
    
                        # 检查输入是否为 filelist 文件 (.f, .list, .flist)
                        set raw_files $yosys_verilog_files
                        if {[llength $raw_files] == 1} {
                            set fpath [lindex $raw_files 0]
                            if {[file pathtype $fpath] eq "relative"} { set fpath_abs [file join $project_dir $fpath] } else { set fpath_abs $fpath }
                            if {[file exists $fpath_abs]} {
                                set ext [file extension $fpath_abs]
                                if {$ext eq ".f" || $ext eq ".list" || $ext eq ".flist"} {
                                    set raw_files [list]
                                    if {[catch {open $fpath_abs r} ifid] == 0} {
                                        while {[gets $ifid line] >= 0} {
                                            set line [string trim $line]
                                            # 忽略空行和注释
                                            if {$line ne "" && ![string match "#*" $line] && ![string match "//*" $line]} {
                                                lappend raw_files $line
                                            }
                                        }
                                        close $ifid
                                    }
                                }
                            }
                        }
    
                        foreach vfile $raw_files {
                            # 处理 Verilog 文件路径:如果是相对路径,加上 project_dir
                            if {[file pathtype $vfile] eq "relative"} {
                                set vfile_abs [file join $project_dir $vfile]
                            } else {
                                set vfile_abs $vfile
                            }
                            puts $yfid "read_verilog $vfile_abs"
                            puts $ffid "$vfile_abs"
                        }
                        close $ffid
                        puts "Auto-generated filelist: [file join [pwd] $filelist_name]"
    
                        if {![info exists yosys_target]} {
                            set yosys_target "generic"
                        }
                        switch -- $yosys_target {
                            xilinx {
                                set synth_cmd "synth_xilinx -top $yosys_top_module"
                                if {[info exists yosys_device]} {
                                    set dev [string tolower $yosys_device]
                                    if {[string match "*ku5p*" $dev]} {
                                        append synth_cmd " -family xcup"
                                    } elseif {[string match "*vu9p*" $dev]} {
                                        append synth_cmd " -family xcup"
                                    } elseif {[string match "*k7325t*" $dev]} {
                                        append synth_cmd " -family xc7"
                                    } elseif {[string match "*zynq7020*" $dev]} {
                                        append synth_cmd " -family xc7"
                                    }
                                }
                                puts $yfid $synth_cmd
                            }
                            altera {
                                puts $yfid "synth_intel -top $yosys_top_module"
                            }
                            default {
                                puts $yfid "synth -top $yosys_top_module"
                            }
                        }
    
                        if {[info exists yosys_liberty_file] && $yosys_liberty_file ne ""} {
                            # 处理 Liberty 文件路径
                            if {[file pathtype $yosys_liberty_file] eq "relative"} {
                                set lib_abs [file join $project_dir $yosys_liberty_file]
                            } else {
                                set lib_abs $yosys_liberty_file
                            }
                            puts $yfid "dfflibmap -liberty $lib_abs"
                            puts $yfid "abc -liberty $lib_abs"
                            puts $yfid "stat -liberty $lib_abs"
                        } else {
                            puts $yfid "stat"
                        }
                        close $yfid
                        puts "Auto-generated Yosys script: [file join [pwd] $yosys_script]"
                    } else {
                        puts "yosys_script is not set; please set it to a Yosys .ys file, or provide yosys_verilog_files and yosys_top_module to auto-generate."
                        return -code 1
                    }
                } else {
                    # 如果用户提供了脚本,且是相对路径,需要修正为绝对路径(因为我们已经 cd 到了 Report)
                    if {[file pathtype $yosys_script] eq "relative"} {
                        set yosys_script [file join $project_dir $yosys_script]
                    }
                }
    
                set yosys_cmd [list $yosys_bin -s $yosys_script]
                if {[catch {set yosys_output [eval exec $yosys_cmd]} err]} {
                    set fid [open yosys_timing.log w]
                    puts $fid $err
                    close $fid
                    puts "Error when running Yosys; see Report/yosys_timing.log"
                    set yosys_output ""
                }
                set fid [open yosys_timing.log w]
                puts $fid $yosys_output
                close $fid
                set worst_line ""
                foreach line [split $yosys_output "\n"] {
                    if {[string match "*Longest path*" $line]} {
                        set worst_line $line
                    }
                }
                if {$worst_line ne ""} {
                    set sfid [open yosys_timing_summary.txt w]
                    puts $sfid $worst_line
                    close $sfid
                }
            }
        }
    }

    tcl/test/test_loader.tcl

    配置加载器的单元测试脚本,验证 JSON/YAML 解析器能否正确读取 config.jsonconfig.yaml,并提取关键字段如 vivado_versionsteps.user_check_timing

    
    set script_dir [file dirname [file normalize [info script]]]
    source [file join [file dirname $script_dir] "modules/config_loader.tcl"]
    
    set root_dir [file dirname [file dirname $script_dir]]
    set json_file [file join $root_dir "config/config.json"]
    if {![file exists $json_file]} {
        set json_file [file join $root_dir "config.json"]
    }
    set yaml_file [file join $root_dir "config/config.yaml"]
    if {![file exists $yaml_file]} {
        set yaml_file [file join $root_dir "config.yaml"]
    }
    
    puts "Testing JSON Loader..."
    if {[catch {
        set cfg [::config_loader::load_config $json_file]
        puts "JSON Loaded Successfully:"
        puts "Content: $cfg"
        puts "  vivado_version: [dict get $cfg vivado_version]"
        puts "  steps.user_check_timing: [dict get $cfg steps user_check_timing]"
    } err]} {
        puts "JSON Failed: $err"
    }
    
    puts "\nTesting YAML Loader..."
    if {[catch {
        set cfg [::config_loader::load_config $yaml_file]
        puts "YAML Loaded Successfully:"
        puts "  vivado_version: [dict get $cfg vivado_version]"
        puts "  steps.user_check_timing: [dict get $cfg steps user_check_timing]"
    } err]} {
        puts "YAML Failed: $err"
    }

    tcl/test/test_yosys_gen.tcl

    Yosys 脚本生成测试,演示如何通过 Tcl 变量(yosys_verilog_files, yosys_top_module)驱动 yosys_flow 模块,生成可执行的 .ys 合成脚本。

    set yosys_bin "yosys"
    
    # 方式 1: 直接提供 Verilog 文件列表
    set yosys_verilog_files [list "test/test.v"]
    
    # 方式 2: 提供一个文件列表文件 (filelist)
    # set yosys_verilog_files [list "src/filelist.f"]
    
    set yosys_top_module "top"
    
    set yosys_liberty_file ""
    if {[info exists ::env(YOSYS_LIB)] && $::env(YOSYS_LIB) ne ""} {
      set yosys_liberty_file $::env(YOSYS_LIB)
    }
    
    set yosys_target "generic"
    if {[info exists ::env(YOSYS_TARGET)] && $::env(YOSYS_TARGET) ne ""} {
      set yosys_target $::env(YOSYS_TARGET)
    }
    
    set yosys_device ""
    if {[info exists ::env(YOSYS_DEVICE)] && $::env(YOSYS_DEVICE) ne ""} {
      set yosys_device $::env(YOSYS_DEVICE)
    }
    
    set script_dir [file normalize [file dirname [info script]]]
    source [file join $script_dir "../timing_analysis.tcl"]

    tcl/test/verify_modules.tcl

    模块加载完整性验证脚本,确保 slr_utilsreport_utilsparams_utils 等关键命名空间中的函数均已成功注册,防止因路径错误或依赖缺失导致运行时异常。

    set script_dir [file normalize [file join [file dirname [info script]] "../"]]
    source [file join $script_dir "modules/slr_utils.tcl"]
    source [file join $script_dir "modules/report_utils.tcl"]
    source [file join $script_dir "modules/params_utils.tcl"]
    
    set failed 0
    
    if {[info commands ::xilinx::designutils::lshift] eq ""} {
        puts "Error: ::xilinx::designutils::lshift not defined"
        set failed 1
    }
    
    if {[info commands ::xilinx::designutils::report_failfast] eq ""} {
        puts "Error: ::xilinx::designutils::report_failfast not defined"
        set failed 1
    }
    
    if {[info commands ::params_utils::set_defaults] eq ""} {
        puts "Error: ::params_utils::set_defaults not defined"
        set failed 1
    }
    
    if {$failed == 0} {
        puts "All modules loaded successfully."
    } else {
        puts "Module verification failed."
        exit 1
    }

    tcl/timing_analysis.tcl

    主入口脚本,负责整体流程编排:加载配置 → 初始化参数 → 加载全部模块 → 打开 DCP → 按顺序调用各分析函数。其设计体现了典型的 网络/系统 层面的分层抽象思想——将复杂时序分析任务解耦为可插拔、可配置的原子模块。

    #########################################################################################
    ## Author  : yufl935
    ## File    : timing_analysis.tcl
    ## Company : Xilinx
    ## Description : 时序分析工具主入口脚本 (Main Entry Point)
    ##               负责配置加载、模块初始化及核心分析流程的调度。
    ##               实际业务逻辑已分散至 tcl/modules/ 下的各独立模块中。
    ## 使用步骤如下:
    #########################################################################################
    # Step 1: 安装Design Utilities工具
    #-------> Tools -> Xilinx Tcl Store -> Design Utilities
    ########################################################
    # Step 2: 设置参数 (或使用 config.json/yaml)
    #-------> vivado_version
    # 版本格式应为dddd.d,如2018.2
    # 注意: 2018.2.1格式不被接受
    #-------> dcp_type
    # 取值范围:
    # 1 => 综合后 (post_synth) 
    # 2 => 优化后 (post_opt)
    # 3 => 布局后 (post_place)
    # 4 => 物理优化后(布局后) (post_phys_opt after place_design)
    # 5 => 布线后 (post_route)
    # 6 => 物理优化后(布线后) (post_phys_opt after route_design)
    #-------> mode
    # 1 => 用户定义模式
    # 2 => 默认模式
    # 3 => 完整模式
    #-------> dcp_is_open
    # 1 => DCP文件已打开
    # 0 => DCP文件未打开,需要先打开DCP
    ########################################################
    #########################################################################################
    # 设置总开始时间,用于计算整个脚本执行时间
    set total_start_time [clock format [clock seconds] -format "%s"]
    set script_dir [file normalize [file dirname [info script]]]
    
    # Load Config Loader
    source [file join $script_dir "modules/config_loader.tcl"]
    
    # 1. Load Configuration
    # Priority: env(CONFIG_FILE) > config.json > config/config.json > config.yaml > config/config.yaml
    set config_file ""
    if {[info exists ::env(CONFIG_FILE)] && [file exists $::env(CONFIG_FILE)]} {
        set config_file $::env(CONFIG_FILE)
    }
    
    if {$config_file eq ""} {
        foreach fname {"config.json" "config/config.json" "config.yaml" "config/config.yaml"} {
            set fpath [file join $script_dir $fname]
            if {[file exists $fpath]} {
                set config_file $fpath
                break
            }
            # Also check relative to CWD if script_dir logic fails or is different
            if {[file exists $fname]} {
                set config_file $fname
                break
            }
        }
    }
    
    if {$config_file eq ""} {
        puts "Error: No configuration file found (checked config.json, config/config.json, .yaml)"
        return -code 1
    }
    
    puts "Loading configuration from $config_file..."
    set cfg [::config_loader::load_config $config_file]
    
    # Apply top-level keys to global variables
    dict for {k v} $cfg {
        if {$k ne "steps"} {
            set $k $v
        }
    }
    
    # Apply steps keys
    if {[dict exists $cfg steps]} {
        dict for {k v} [dict get $cfg steps] {
            set $k $v
        }
    }
    
    # Source new modules
    source [file join $script_dir "modules/repo_utils.tcl"]
    source [file join $script_dir "modules/yosys_flow.tcl"]
    source [file join $script_dir "modules/slr_utils.tcl"]
    source [file join $script_dir "modules/report_utils.tcl"]
    source [file join $script_dir "modules/params_utils.tcl"]
    
    if {[llength [info commands get_timing_paths]] == 0} {
        # Run Yosys flow if Vivado commands are not available
        ::yosys_flow::run_flow $script_dir
        return
    }
    
    # Source existing modules
    source $script_dir/modules/common_utils.tcl
    source $script_dir/modules/csv_reporters.tcl
    source $script_dir/modules/timing_checks.tcl
    source $script_dir/modules/slr_analysis.tcl
    source $script_dir/modules/bram_uram_analysis.tcl
    source $script_dir/modules/dsp_analysis.tcl
    source $script_dir/modules/cell_analysis.tcl
    source $script_dir/modules/design_analysis.tcl
    
    # Set default parameters
    ::params_utils::set_defaults
    
    #########################################################################################
    # 打开DCP文件
    if {$dcp_is_open==0} {
      # 使用glob命令查找符合模式的DCP文件
      set target_dcp [glob -nocomplain $dcp_name]
      # 获取找到的DCP文件数量
      set dcp_num [llength $target_dcp]
    
      # 检查DCP文件数量是否合法
      if {$dcp_num==0} {
        puts "当前工作目录中没有找到DCP文件"
        return -code 1
      } elseif {$dcp_num>1} {
        puts "找到多个DCP文件,无法确定使用哪个"
        return -code 1
      } else {
        # 记录打开DCP文件的开始时间
        set open_dcp_start_time [clock format [clock seconds] -format "%s"]
        # 打开DCP文件
        open_checkpoint $target_dcp
        # 记录打开DCP文件的结束时间
        set open_dcp_end_time [clock format [clock seconds] -format "%s"]
      }
    }
    
    #########################################################################################
    # 创建Report目录用于存放报告文件
    if {![info exists report_dir]} {
      set report_dir [file join [pwd] Report]
    }
    file mkdir $report_dir
    # 切换到Report目录
    cd $report_dir
    
    # Check DCP type and Mode
    ::params_utils::check_dcp_and_mode
    
    #########################################################################################
    # 根据选择的检查项执行相应的分析
    #########################################################################################
    set index 1
    set sn [timing_analysis::get_sn $index $phase]
    timing_analysis::my_check_timing $sn $en_check_timing
    incr index
    set sn [timing_analysis::get_sn $index $phase]
    timing_analysis::my_timing_summary $sn $dcp_type $en_timing_summary
    incr index
    set sn [timing_analysis::get_sn $index $phase]
    timing_analysis::my_neg_slack_timing_report $sn $dcp_type $en_neg_slack_timing
    incr index
    set sn [timing_analysis::get_sn $index $phase]
    set block [get_cells -hierarchical -filter "IS_PRIMITIVE==0 && IS_COMPOSITE==0 && REF_NAME!=IBUF && REF_NAME!=OBUF" -quiet]
    timing_analysis::report_block2block $sn $block $en_block2block
    
    set clock_list [get_clocks -quiet]
    foreach clock_name $clock_list {
      incr index
      set sn [timing_analysis::get_sn $index $phase]
      set period [get_property PERIOD $clock_name]
      set delay  [timing_analysis::get_timing_base [list $dcp_type -1] $is_accurate]
      timing_analysis::report_high_logic_level $sn $clock_name $delay $en_high_logic_level
    }
    
    incr index
    set sn [timing_analysis::get_sn $index $phase]
    timing_analysis::report_paths_crossing_slrs $sn $dcp_type $en_get_cells_crossing_slrs $en_crossing_slrs
    
    set used_bram [get_cells -hierarchical -filter "REF_NAME =~ RAMB18* || REF_NAME =~ RAMB36* || REF_NAME =~ FIFO18* || REF_NAME =~ FIFO36*" -quiet]
    incr index
    set sn [timing_analysis::get_sn $index $phase]
    timing_analysis::get_no_reg_bram $sn $used_bram $en_check_bram
    
    set used_uram [get_cells -hierarchical -filter "REF_NAME =~ URAM*" -quiet]
    incr index
    set sn [timing_analysis::get_sn $index $phase]
    timing_analysis::get_no_reg_uram $sn $used_uram $en_check_uram
    
    set used_dsp [get_cells -hierarchical -filter "REF_NAME =~ DSP*" -quiet]
    incr index
    set sn [timing_analysis::get_sn $index $phase]
    timing_analysis::get_no_mreg_dsp $sn $used_dsp $en_check_dsp_mreg
    incr index
    set sn [timing_analysis::get_sn $index $phase]
    timing_analysis::get_no_preg_dsp $sn $used_dsp $en_check_dsp_preg
    
    set lower_srl [get_cells -hierarchical -filter "REF_NAME =~ SRL16* || REF_NAME =~ SRLC32*" -quiet]
    incr index
    set sn [timing_analysis::get_sn $index $phase]
    timing_analysis::get_lower_depth_srl $sn $lower_srl $en_check_lower_srl
    
    set combined_lut [get_cells -hierarchical -filter "REF_NAME =~ LUT* && IS_COMBINED==1" -quiet]
    incr index
    set sn [timing_analysis::get_sn $index $phase]
    timing_analysis::get_combined_lut $sn $combined_lut $en_check_combined_lut
    
    set muxfx_cells [get_cells -hierarchical -filter "REF_NAME =~ MUXF7 || REF_NAME =~ MUXF8 || REF_NAME =~ MUXF9" -quiet]
    incr index
    set sn [timing_analysis::get_sn $index $phase]
    timing_analysis::get_muxfx $sn $muxfx_cells $en_check_muxfx
    
    set latch_cells [get_cells -hierarchical -filter "IS_LATCH==1" -quiet]
    incr index
    set sn [timing_analysis::get_sn $index $phase]
    timing_analysis::get_latch $sn $latch_cells $en_check_latch
    
    incr index
    set sn [timing_analysis::get_sn $index $phase]
    timing_analysis::report_clock_characteristics $sn $en_clock_characteristics
    
    incr index
    set sn [timing_analysis::get_sn $index $phase]
    timing_analysis::my_clock_networks $sn $en_clock_networks
    
    incr index
    set sn [timing_analysis::get_sn $index $phase]
    timing_analysis::my_report_cdc $sn $en_cdc
    
    incr index
    set sn [timing_analysis::get_sn $index $phase]
    timing_analysis::my_clock_interaction $sn $en_clock_interaction
    
    incr index
    set sn [timing_analysis::get_sn $index $phase]
    timing_analysis::report_congestion_level $sn $en_congestion
    
    incr index
    set sn [timing_analysis::get_sn $index $phase]
    timing_analysis::my_report_exceptions $sn $en_exceptions
    
    incr index
    set sn [timing_analysis::get_sn $index $phase]
    timing_analysis::my_report_utilization $sn $en_util
    
    incr index
    set sn [timing_analysis::get_sn $index $phase]
    timing_analysis::my_report_methodology $sn $en_methodology
    
    incr index
    set sn [timing_analysis::get_sn $index $phase]
    if {$en_qor} {
      report_qor_suggestions -name ${sn}_qor_suggestions_analysis -file ${sn}_qor_suggestions_analysis.rpt
    }
    
    incr index
    set sn [timing_analysis::get_sn $index $phase]
    set slrs [expr {[llength [get_slrs -quiet]] == 1 ? 1 : 0}]
    timing_analysis::report_failfast $sn $slrs $dcp_type $en_failfast
    
    incr index
    set sn [timing_analysis::get_sn $index $phase]
    timing_analysis::high_fanout_nets $sn $slrs $dcp_type $en_high_fanout_nets
    
    incr index
    set sn [timing_analysis::get_sn $index $phase]
    timing_analysis::my_ram_util $sn $en_ram_util
    
    #########################################################################################
    # 打印脚本执行时间
    #########################################################################################
    set total_end_time [clock format [clock seconds] -format "%s"]
    set total_exec_time [expr $total_end_time - $total_start_time]
    puts "Total execution time: $total_exec_time seconds"

    配置文件

    config/config.json

    {
      "vivado_version": "2022.2",
      "dcp_type": 5,
      "dcp_name": "*routed.dcp",
      "report_dir": "Report",
      "yosys_verilog_files": ["XilinxTclStore/tclapp/xilinx/xsim/test/src/top.v"],
      "yosys_top_module": "top",
      "steps": {
        "user_check_timing": true,
        "user_timing_summary": true,
        "user_neg_slack_timing": true,
        "user_qor": false,
        "user_failfast": false
      }
    }

    config/config.yaml

    # Vivado Configuration
    vivado_bin: vivado
    dcp_dir: ""
    dcp_name: "*routed.dcp"
    vivado_version: "2022.2"
    dcp_type: 5
    mode: 1
    
    # Yosys Configuration
    yosys_bin: yosys
    yosys_verilog_files: ["XilinxTclStore/tclapp/xilinx/xsim/test/src/top.v"]   # Example: ["/path/to/design.v", "/path/to/utils.v"]
    yosys_top_module: top
    
    # Output
    report_dir: Report
    
    # Analysis Steps (true/false)
    steps:
      user_check_timing: true
      user_timing_summary: true
      user_neg_slack_timing: true
      user_block2block: true
      user_high_logic_level: true
      user_crossing_slrs: false
      user_get_cells_crossing_slrs: false
      user_check_bram: true
      user_check_uram: true
      user_check_dsp_mreg: true
      user_check_dsp_preg: true
      user_check_lower_srl: true
      user_check_combined_lut: true
      user_check_muxfx: true
      user_check_latch: true
      user_clock_characteristics: true
      user_clock_networks: true
      user_cdc: true
      user_clock_interaction: true
      user_congestion: true
      user_exceptions: true
      user_util: true
      user_methodology: true
      user_qor: false
      user_failfast: false
      user_high_fanout_nets: true
      user_ram_util: true

    构建与 Git 配置

    .gitmodules

    # Xilinx Tcl Store 仓库地址
    # 用于 Vivado 或 Vitis 的 Tcl 脚本扩展仓库
    [submodule "XilinxTclStore"]
    path = XilinxTclStore
    url = https://github.com/Xilinx/XilinxTclStore.git
    shallow = true

    .gitignore

    
    *Script*
    Report/
    auto_generated.ys
    XilinxTclStore
    .vscode/

    Makefile

    
    XILINXTA_DIR := $(CURDIR)
    REPORT_DIR ?= Report
    TCLSH ?= tclsh
    VIVADO ?= vivado
    YOSYS ?= yosys
    
    .PHONY: all timing yosys_gen yosys_run config_test vivado_batch clean distclean help
    
    all: module_test config_test yosys_test timing
    
    help:
        @echo "XilinxTimingAnalysis Makefile 使用说明"
        @echo "  make                 # 模块验证 + 配置验证 + Yosys集成测试 + 生成自动脚本"
        @echo "  make module_test     # 验证新模块加载与核心函数"
        @echo "  make config_test     # 验证 JSON/YAML 配置解析 (支持 config/ 目录)"
        @echo "  make yosys_test      # 验证 Yosys 脚本生成流程 (使用 test/test_yosys_gen.tcl)"
        @echo "  make timing          # 运行主脚本(非 Vivado 环境下生成 Report/auto_generated.ys/.f)"
        @echo "  make yosys_run       # 运行 Yosys 使用 Report/auto_generated.ys"
        @echo "  make vivado_batch DCP_DIR=/path/to/project  # Vivado 批处理模式执行"
        @echo "  make clean           # 清理生成物"
        @echo "  make distclean       # 深度清理(含测试输出)"
    
    module_test:
        $(TCLSH) "$(XILINXTA_DIR)/tcl/test/verify_modules.tcl"
    
    config_test:
        $(TCLSH) "$(XILINXTA_DIR)/tcl/test/test_loader.tcl"
    
    yosys_test:
        $(TCLSH) "$(XILINXTA_DIR)/tcl/test/test_yosys_gen.tcl"
    
    timing:
        $(TCLSH) "$(XILINXTA_DIR)/tcl/timing_analysis.tcl" || true
    
    yosys_gen: timing
    
    yosys_run:
        @if [ ! -f "$(REPORT_DIR)/auto_generated.ys" ]; then $(TCLSH) "$(XILINXTA_DIR)/tcl/timing_analysis.tcl"; fi; \
        $(YOSYS) -s "$(REPORT_DIR)/auto_generated.ys" || true
    
    vivado_batch:
        @if [ -z "$(DCP_DIR)" ]; then \
          echo "DCP_DIR is not set"; \
          exit 1; \
        fi; \
        cd "$(DCP_DIR)" && $(VIVADO) -mode batch -source "$(XILINXTA_DIR)/tcl/timing_analysis.tcl"
    
    clean:
        rm -rf "$(REPORT_DIR)" auto_generated.ys auto_generated.f yosys_timing.log
    
    distclean: clean
        rm -rf tcl/test/output



    上一篇:Oracle 修复与MySQL社区关系,承诺开源关键功能并重组团队
    下一篇:深入实战:Linux内核同步机制演进,从自旋锁到RCU的全面解析与场景对比
    您需要登录后才可以回帖 登录 | 立即注册

    手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

    GMT+8, 2026-2-4 23:12 , Processed in 0.367645 second(s), 42 queries , Gzip On.

    Powered by Discuz! X3.5

    © 2025-2026 云栈社区.

    快速回复 返回顶部 返回列表