blob: 5ce34ae7a1c9f7860025ccb1eba561281f9a80f1 [file] [log] [blame]
#===========================================================================
# File Name : Source: /net/splinter/fed/cvs/infrastructure/applets/compatibility.tcl,v
# Date Created : 07/20/2010
# Date Modified : Date: 2012/03/07 02:42:47
# Version : Revision: 1.35
# Summary : Provide non-standard TCL commands to enable script reuse across Cadence
# Keywords : TCL compatibility script port EDI ae-ware ETS LEC conformal
#
# Description:
# RC has solid TCL support but also adds several useful commands that are
# not a standard part of TCL. Commands like add_command_help, parse_options,
# as well as others are frequently used in RC ae-ware scripts but not supported
# by most tools. In order to enable easy port of RC script to other tools
# within Cadence, this scripts are needed to emulate in other tools
# what is otherwise embedded in RC exectuable
#
# Assumptions:
# This script will be loaded in a Cadence tool that supports TCL
#
#===========================================================================
##nagelfar syntax get_metric
##nagelfar syntax get_version
##nagelfar syntax get_version_info
##nagelfar syntax getVersion
##nagelfar syntax dbGet
##nagelfar syntax get_design
##nagelfar syntax get_root_module
##nagelfar syntax getLogFileName
##nagelfar syntax timedesign
global currTool
# Load Cadence Compatibility Layer when not in RTL Compiler
if {[expr {[llength [info commands attribute_exists]] && \
[attribute_exists -type root program_short_name] && \
[string equal [get_attribute program_short_name /] "rc"]}]} {
set currTool rc
} else {
switch -regexp [info nameofexecutable] {
{/(velocity|encounter)$} { set currTool encounter }
{/ctos(gui)?$} { set currTool ctos }
{/(lec|LEC|verify)$} { set currTool lec }
{/(tclsh|tkcon)} { set currTool tclsh }
default { set currTool "unknown" }
}
}
# exit if running in RTL Compiler; no emulation needed there
if {![string match "rc" ${currTool}]} {
# Put all ae-ware in a flat namespace to ease reference from one application to another
# As support for additional ae-ware is added through the compatibility layer, they will
# likely need to be added here if they have references to other ae-ware supported through
# the compatiblity layer
set ::ns(compat) "::aeware::compat"
set ::ns(parse_opt) $::ns(compat)
set ::ns(genrpt) $::ns(compat)
set ::ns(applet) $::ns(compat)
set ::ns(redirect) $::ns(compat)
set ::ns(regress) $::ns(compat)
if {![llength [info procs alias]]} { proc alias {commandname command} { eval {interp alias {} $commandname {}} $command } }
alias hidden_proc proc
alias tcl_source source
alias lcd cd
alias lls ls
alias lpwd pwd
alias ${::ns(compat)}::get_attr ${::ns(compat)}::get_attribute
alias ${::ns(compat)}::set_attr ${::ns(compat)}::set_attribute
alias ${::ns(compat)}::define_attr ${::ns(compat)}::define_attribute
if {[string match "lec" ${currTool}]} { rename add_command_help _add_command_help }
namespace eval $::ns(compat) {
variable quietMode 0
variable rootAttributes
if {![llength [info commands ::add_command_help]]} { namespace export add_command_help }
if {![llength [info commands ::calling_proc]]} { namespace export calling_proc }
if {![llength [info commands ::dispatch_subcommand]]} { namespace export dispatch_subcommand }
if {![llength [info commands ::define_attribute]]} { namespace export define_attribute }
if {![llength [info commands ::get_attribute]]} { namespace export get_attribute }
if {![llength [info commands ::set_attribute]]} { namespace export set_attribute }
proc basename {fullName} { return "[file tail $fullName]" }
proc convert_unit {args} {
switch -- [parse_options "calling_proc" {} $args \
"-to sos unit to convert input to(default is picoseconds)" toUnit \
"srs string containing value to convert" inputStr] {
-2 { return }
0 { return -code error }
}
set inVal [lindex $inputStr 0]
set inUnit [lindex $inputStr 1]
}
proc EdiMetricsPresent {args} {
set metricsCookie ".edi_metrics"
file delete ${metricsCookie}
##nagelfar ignore
::se::save_metric_file ${metricsCookie}
set returnVal "false"
switch [llength $args] {
0 { if {[file exists "${metricsCookie}"]} { set returnVal "true" } }
1 {
if {[file exists "${metricsCookie}"]} {
set metricFile [open ${metricsCookie} r]
while {![eof $metricFile]} {
set metricLine [gets $metricFile]
if {![regexp {se::save_metric\s+(.*)\s+(\{.*\})} $metricLine match metricName metricVal]} { continue }
if {[string match $metricName $args]} { set returnVal true; break; }
}
close $metricFile
}
}
default { return "Error:EdiMetricsPresent allows 1 or 0 arguments!" }
}
return ${returnVal}
}
proc ::aeware::compat::find {args} {
global currTool
if {[regexp {(program_version|memory_usage|runtime)} $args]} {
if {[regexp {\-(des|desi|desig|design)} $args]} { return }
if {[regexp {(root.*\-attr|-attr.*root)} $args]} { return 1 }
}
if {[regexp {\-cost_group\s+\*} $args]} {
switch ${currTool} {
"encounter" {
# For EDI, only safe way to do this is generating a report and extracting the path groups from there
set logFile [open [getLogFileName] r]
seek $logFile 0 end
timedesign -reportonly
timedesign -hold -reportonly
while {![eof $logFile]} {
set logLine [gets $logFile]
if {[regexp {\|\s+Setup mode\s+\|\s+all\s+\|\s+(.*)\s+\|} ${logLine} match rawCgInfo]} { break }
}
close $logFile
regsub -all {\s+} ${rawCgInfo} "" ediCgInfo
# regsub {\|clkgate} ${ediCgInfo} "" ediCgInfo
# regsub {\|in2reg} ${ediCgInfo} "|inclkSrc2reg" ediCgInfo
# regsub {\|in2out} ${ediCgInfo} "|inclkSrc2out" ediCgInfo
return [split ${ediCgInfo} "|"]
# unset -nocomplain ediCgInfo
# foreach_in_collection cgName [get_path_groups] { lappend ediCgInfo [get_property $cgName name] }
# puts "$ediCgInfo"
}
"ctos" { return }
}
}
if {[regexp {\-(des|desi|desig|design)\s+(\S+)} $args match findOpt findStr]} {
switch ${currTool} {
"encounter" { return [dbGet top.name] }
"ctos" { return [get_design] }
"lec" { return [get_root_module] }
}
}
return
}
proc what_is {args} { return }
proc calling_proc {args} {
switch -- [parse_options "calling_proc" {} $args \
"nos number of levels to go back for calling procs name" levels] {
-2 { return }
0 { return -code error }
}
# default is current level
if {[llength $levels]} {
if {$levels <= 0} { return -code error "Error: calling_proc argument must be > 0" }
} else {
set levels 0
}
# don't count this call to calling_proc
incr levels
# puts "levels = $levels"
# puts "i_level = [info level]"
# puts "i_level_expr = [info level [expr {[info level] - $levels}]]"
# puts "index = [lindex [info level [expr {[info level] - $levels}]] 0]"
# puts "ns_tail = [namespace tail [lindex [info level [expr {[info level] - $levels}]] 0]]"
return [namespace tail [lindex [info level [expr {[info level] - $levels}]] 0]]
}
proc get_attribute {args} {
global currTool
variable rootAttributes
variable $::ns(compat)::quietMode
switch -- [parse_options [calling_proc] {} $args \
"srs attribute name" attrName \
"srm object of interest (must be unique)" objInfo] {
-2 { return }
0 { return -code error }
}
foreach obj $objInfo {
if {[string match "encounter" ${currTool}] &&
[regexp "metric_?(value|tcl)?" $obj match metricArg]} {
if {[llength ${metricArg}]} {
set metricArg "-${metricArg}"
} else {
set metricArg "-value"
}
set metricValue "N/A"
if {[eval EdiMetricsPresent $attrName]} { set metricValue [eval get_metric $attrName ${metricArg}] }
return $metricValue
}
if {[regexp {slack} $attrName]} {
if {[eval EdiMetricsPresent timing.setup.WNS.${obj}]} {
set metricValue [get_metric timing.setup.WNS.${obj} -value]
return ${metricValue}
} else {
puts "Warning: No \'slack\' metrics available for ${obj}"
return "N/A"
}
}
if {!${quietMode} && ![regexp {/} $obj]} {
puts "Warning: only root attributes are currently supported by Cadence Compatibility Layer(CCL)"
puts " EDI also supports metrics; specify \'metric\' as the object type"
puts "\tget_attribute $attrName $objInfo"
return -code error
}
}
switch -regexp -- $attrName {
{^(runtime|super_thread_runtime|super_thread_total_runtime)$} {
switch -regexp ${currTool} {
{(encounter|ctos|lec)} {
set f [open "|ps -p [pid] -o cputime --noheaders"]
set size [string trim [read $f]]
close $f
if {[regexp {(0*(\d+)-)?0*(\d+):0*(\d+):0*(\d+)} $size -> xx days hours min sec]} {
set secTime 0
if {[llength ${days}]} { incr secTime [expr {$days * 24 * 60 * 60}] }
incr secTime [expr {$hours * 60 * 60}]
incr secTime [expr {$min * 60}]
incr secTime $sec
}
return $secTime
}
{(tclsh)} {
set f [open "|ps -p [pid] -o cputime --noheaders"]
set size [string trim [read $f]]
close $f
if {[regexp {(0*(\d+)-)?0*(\d+):0*(\d+):0*(\d+)} $size -> xx days hours min sec]} {
set secTime 0
if {[llength ${days}]} { incr secTime [expr {$days * 24 * 60 * 60}] }
incr secTime [expr {$hours * 60 * 60}]
incr secTime [expr {$min * 60}]
incr secTime $sec
}
return $secTime
}
}
}
{^memory_usage$} {
switch -regexp ${currTool} {
{(encounter|ctos|lec)} {
set f [open "|ps -p [pid] -o vsz --noheaders"]
set size [string trim [read $f]]
close $f
return [format "%.2f" [expr {$size / 1024.0}]]
}
}
}
{^program_version$} {
switch ${currTool} {
"encounter" { return "[getVersion]" }
"ctos" { return [format "%s(%s)" [join [lrange [get_version] 0 2] .] [join [lrange [get_version] 4 5] {}]] }
"lec" { return [format "%s(%s)" [lindex [get_version_info] 0] [lindex [get_version_info] 2]] }
}
}
{^lp_power_unit$} { return "nW" }
{^lp_insert_clock_gating$} { return "false" }
{^super_thread_servers$} { return }
{^auto_super_thread$} { return 0 }
default {
if {[info exists rootAttributes(value,$attrName)]} {
return "$rootAttributes(value,$attrName)"
} elseif {!${quietMode}} {
return -code error "Error: attribute \'$attrName\' is currently not supported by Cadence Compatibility Layer(CCL)"
}
}
}
return
}
proc set_attribute {args} {
variable rootAttributes
variable $::ns(compat)::quietMode
set quiet 0
if {[info exists quietMode] && ${quietMode}} { set quiet 1 }
switch -- [parse_options [calling_proc] {} $args \
"-quiet bos keeps quiet unless there are problems" quiet \
"-lock bos attribute becomes read only once locked" lockAttr \
"srs attribute name" attrName \
"srs new value. A compound string (containing spaces) should be represented as a list (using double-quotes or braces)" attrVal \
"srm object(s) of interest" objInfo] {
-2 { return }
0 { return -code error }
}
foreach obj $objInfo {
if {!${quiet} && ![regexp {/} $obj]} {
puts "Error: only root attributes are currently supported by Cadence Compatibility Layer(CCL)"
puts "\t\'$attrName\' will not be set to \'$attrVal\'"
return -code error
}
if {[info exists rootAttributes(value,$attrName)]} {
if {![eval IsValidArg $rootAttributes(dtype,$attrName) [list $attrVal]]} {
puts " set_attribute $attrName $attrVal $objInfo"
return -code error
}
if {!$quiet} {
puts " Setting attribute of \'$obj\': \'$attrName\' = ${attrVal}"
}
# Support for -check_function in define_attribute
if {[info exists rootAttributes(checkFunc,${attrName})]} { eval $rootAttributes(checkFunc,${attrName}) ${obj} ${attrVal} }
set rootAttributes(value,$attrName) $attrVal
} else {
return -code error "Error: attribute \'$attrName\' not found"
}
}
}
proc define_attribute {args} {
variable rootAttributes
variable $::ns(compat)::quietMode
set checkFunc {}
set setFunc {}
set computeFunc {}
set checkFuncHelp "name of Tcl proc used to check value. The Tcl proc returns '1' if value is OK, '0' otherwise and requires two arguments: <object> <value>"
set setFuncHelp "name of Tcl proc used to set value. The Tcl proc returns the new value and requires three arguments: <object> <new_value> <current_value>"
set computeFuncHelp "name of Tcl proc used to compute value. If this option is used the attribute becomes read-only, since its value is always computed. The Tcl proc"
switch -- [parse_options [calling_proc] {} $args \
"-category srs category name" catName \
"-data_type srs data type (boolean, \'fixed point\', \'floating point number\', integer, string)" dataType \
"-obj_type srs object type" objType \
"-default_value sos default value" defaultVal \
"-help_string sos help string" helpString \
"-more_help_string sos extended help string" extraHelp \
"-hidden bos hide attribute" isHidden \
"-obsolete bos obsolete attribute" isObsolete \
"-check_function sos $checkFuncHelp" checkFunc \
"-compute_function sos $computeFuncHelp" computeFunc \
"-set_function sos $setFuncHelp" setFunc \
"-skip_in_db bos if set, command write_db will not store attribute settings in the database" skipDB \
"srs attribute name" attrName] {
-2 { return }
0 { return -code error }
}
if {!${quietMode} && [llength $setFunc]} {
puts "Warning: user-attributes with -set_function are not supported by Cadence Compatibility Layer(CCL)"
puts "\tattribute \'$attrName\' will not be supported"
return
}
if {!${quietMode} && [llength $computeFunc]} {
puts "Warning: user-attributes with -compute_function are not supported by Cadence Compatibility Layer(CCL)"
puts "\tattribute \'$attrName\' will not be supported"
return
}
if {!${quietMode} && ![regexp {root} $objType]} {
puts "Warning: only root level user-attributes are supported by Cadence Compatibility Layer(CCL)"
puts "\tattribute \'$attrName\' will not be supported"
return
}
set rootAttributes(value,$attrName) $defaultVal
set rootAttributes(dtype,$attrName) $dataType
set rootAttributes(value,$attrName) $defaultVal
if {[llength ${checkFunc}]} { set rootAttributes(checkFunc,$attrName) $checkFunc }
}
# Currently, add_command_help is not emulated but implemented without doing anyhting to avoid errors
if {![llength [info commands ::add_command_help]]} { proc add_command_help {args} {} }
# Provide help for command containing subcommands, edit_netlist is
# an example
proc subcommand_help {command help subcommands} {
puts " $command: $help"
puts "Usage: $command subcommand \[-h\]"
puts ""
puts " The 'subcommand' can be one of the following:"
# get length of longest command name - used to pretty print help
set mlen 0
foreach subcommand $subcommands {
set clen [string length [lindex $subcommand 0]]
set flag [lindex $subcommand 1]
if {$flag eq "hidden"} {
continue
}
if {$clen > $mlen} {
set mlen $clen
}
}
foreach subcommand $subcommands {
set flag [lindex $subcommand 1]
if {$flag eq "hidden"} {
continue
}
set cmd_name [lindex $subcommand 0]
set cmd_help [lindex $subcommand 3]
set scount [expr {$mlen -[string length $cmd_name]}]
set spaces [string range [string repeat " " 50] 0 $scount]
puts "[format "%s%s- %s" $cmd_name $spaces $cmd_help]"
}
}
# Fire off the appropriate subcommand. edit_netlist uses this.
# subcommands are formatted as: <name> <hidden | visible> <cmd> <help>
proc dispatch_subcommand {command c_args subcommands help} {
if {[llength $c_args] < 1} {
subcommand_help $command $help $subcommands
return -code error "Failed on '$command $c_args'"
} else {
set subcommand [lindex $c_args 0]
if {[string match "-h*" $subcommand]} {
subcommand_help $command $help $subcommands
return
} else {
set matches {}
set mlist {}
set cmdhelp ""
foreach type $subcommands {
set this_subcommand [lindex $type 0]
if {[string match "$subcommand*" $this_subcommand]} {
if {([lindex $type 1] eq "hidden") && \
($subcommand ne $this_subcommand)} {
continue
}
set function [lindex $type 2]
set cmdhelp [lindex $type 3]
lappend matches $this_subcommand
lappend mlist $type
}
}
# get exact match for subcommand for cases with same beginning
# e.g. 'report power' and 'report power_domain'
set exact 0
foreach type $matches {
if {$type eq $subcommand} {
set exact 1
}
}
if {$exact} {
set matches $subcommand
foreach type $mlist {
if {$subcommand eq [lindex $type 0]} {
set function [lindex $type 2]
set cmdhelp [lindex $type 3]
break
}
}
}
if {[llength $matches] > 1} {
#Get the matching subcommand with least string length
if { [string length $subcommand] > 2 } {
set type [lindex [lsort -index 0 $mlist] 0]
set function [lindex $type 2]
set cmdhelp [lindex $type 3]
puts stdout "Ambiguous subcommand: $matches"
puts stdout "Choosing subcommand: '[lindex $type 0]'"
} else {
puts stdout "Ambiguous subcommand: $matches"
subcommand_help $command $help $subcommands
return -code error "Failed on '$command $c_args'"
}
}
if {[llength $matches] == 0} {
puts stdout "Cannot find subcommand: $subcommand"
subcommand_help $command $help $subcommands
return -code error "Failed on '$command $c_args'"
}
}
}
# Don't use concat because concat is slow.
set cmd [list $function]
set found_help 0
foreach arg [lrange $c_args 1 end] {
lappend cmd $arg
if {($arg eq "-h") || ($arg eq "-he") ||
($arg eq "-hel") || ($arg eq "-help")} {
set found_help 1
}
}
# print help for hidden subcommands
# ARGH- the regexp is converting vdir objects into strings!
# Please be certain that you watch out for vdir objects.
#if {[regexp {\-h} $cmd] && ($cmdhelp ne "")}
if {$found_help && ($cmdhelp ne "")} {
puts " $command $matches: $cmdhelp"
}
eval $cmd
}
}
namespace eval $::ns(parse_opt) {
if {![llength [info commands ::parse_options]]} { namespace export parse_options }
proc UsageInfo {cmd cmdOptions} {
upvar $cmdOptions optInfo
set cmdVersion {}
if { [expr {[llength $cmd] > 1}] && [string is double [lindex $cmd end]] } {
set cmdVersion [lindex $cmd end]
set cmd [lreplace ${cmd} end end]
}
set usageString {}
set usageHeader "\nUsage: $cmd"
if {[llength ${cmdVersion}]} { set usageHeader "\nCommand Version: ${cmdVersion}\nUsage: $cmd" }
foreach opt [lsort [array names optInfo var,*]] {
regsub "var," $opt "" optUsage
regsub "var," $opt "rqmt," optRqmt
regsub "var," $opt "help," optHelp
for {set i 0} {$i < [llength $optInfo($opt)]} {incr i} {
if {![regexp {b\w\w} [lindex $optInfo($optRqmt) $i]]} { append optUsage " " }
if {![regexp {\-.*} $optUsage]} { set optUsage "" }
switch -regexp [lindex $optInfo($optRqmt) $i] {
{n\w\w} { set optUsage "${optUsage}<integer>" }
{f\w\w} { set optUsage "${optUsage}<float>" }
{s\w\w} { set optUsage "${optUsage}<string>" }
}
if {[regexp {\wO\w} [lindex $optInfo($optRqmt) $i]]} { continue }
if {[regexp {\wo\w} [lindex $optInfo($optRqmt) $i]]} { set optUsage "\[${optUsage}\]" }
if {[regexp {\w\wm} [lindex $optInfo($optRqmt) $i]]} { set optUsage "${optUsage}+" }
append usageHeader " ${optUsage}"
lappend usageString " ${optUsage}:"
lappend usageString " [lindex $optInfo($optHelp) $i]"
}
}
# Output usage help messsage
puts "$usageHeader\n"
foreach usageLine ${usageString} { puts "$usageLine" }
}
proc ParseHelp {} {
puts "
Usage: parse_options cmd file_var args \[code var\]*
cmd <string>:
name of the command whose options are being parsed
file_var <string>:
name of variable to hold file id
args <string>:
options being passed to the command
code <string>:
option parsing directive
var <string>:
name of variable to hold option result
The option parsing directive should be in the form:
(<-name> )?<x><y><z>(<dirtypes>)? <help>
where <-name> is a flag, for example \"-max\"
multiple string objects('srm' and 'som') are not supported for flagged options.
<x> is a character indicating the option type
d: directory object
n: integer
f: float
b: boolean
s: string
<y> is a character indicating optional, required or obsolete
o: optional
r: required
x: obsolete
<z> is a character indicating single or multiple
s: single value only
m: multiple values accepted
<dirtypes> is a string indicating directory types
The types must be separated by | characters
and must be bounded by parentheses.
For example, (port|pin) indicates that both
port and pin directories are accepted.
<help> is a string returned to the user if the
\'-h\' flag is given.
"
}
proc parse_options {args} {
set cmd [lindex $args 0]
set file_var [lindex $args 1]
set parseArgs [lindex $args 2]
set parseOptions [lrange $args 3 end]
set switchName {}
set helpString {}
set parseRqmt {}
if {[info exists optInfo]} { unset optInfo }
if {[regexp {\-(h|he|hel|help)($|\s)} $cmd]} {
eval ParseHelp
return
}
foreach opt $parseOptions {
if {[llength $opt] == 1} {
set indexName "unflagged"
if {[llength $switchName] == 1} { regsub -all {\s} $switchName "" indexName }
switch -regexp $parseRqmt {
{b\wm} {
puts "Error: Boolean options cannot be multi-valued"
return -code error
}
{br\w} {
puts "Error: Boolean options cannot be required - useless"
return -code error
}
{bos} {
if {[string match "unflagged" $indexName]} {
puts "Error: Boolean options must be unflagged"
return -code error
}
}
{s\wm} {
if {[llength $switchName] == 1} {
puts "Error: \'som\' and \'srm\' do not support flagged options"
return -code error
}
}
}
lappend optInfo(var,$indexName) $opt
lappend optInfo(rqmt,$indexName) $parseRqmt
lappend optInfo(used,$indexName) 0
lappend optInfo(help,$indexName) $helpString
continue
}
if {![regexp {^\s*(\-\w+)?\s*([dbsnf][orO][sm])(\(\S+\))?\s+(.*)} $opt match switchName parseRqmt objRqmt helpString]} {
puts "Error: Invalid parse requirement string"
eval ParseHelp
return -code error
}
}
# probably needs to be removed and integrated into next parsing section
if {[regexp {(^|\s)\-(h|he|hel|help)($|\s)} $parseArgs]} {
eval UsageInfo [list $cmd] optInfo
return -2
}
set unflagCount 0
if {[info exists optInfo(var,unflagged)]} { set unflagCount [llength $optInfo(var,unflagged)] }
set unflagIndex 0
set argCount [llength $parseArgs]
# foreach foo [array names optInfo *] { puts "optInfo($foo) -> $optInfo($foo)" ; set ::optInfo($foo) $optInfo($foo); }
# Process flags and flag arguments
for {set i 0} {$i < $argCount} {incr i} {
set currentArg [lindex $parseArgs $i]
if {[regexp {\-} $currentArg]} {
set matchIndex [lsearch -regexp -all [array names optInfo var,*] $currentArg]
if { [llength $matchIndex] > 1 } { return -code error "Error: ambiguous flag \'$currentArg\' specified" }
if { [llength $matchIndex] == 1 } { regsub "var," [lindex [array names optInfo var,*] $matchIndex] "" currentArg }
}
# Identify and process flagged options
# llength [lsearch -regexp -all [array names optInfo var,*] $currentArg]
# info exists optInfo(var,$currentArg)
if { [info exists optInfo(var,$currentArg)] || [expr {$unflagCount != 0}] } {
set argVal {}
if {[info exists optInfo(var,$currentArg)]} {
upvar $optInfo(var,$currentArg) argVar
set argRqmt $optInfo(rqmt,$currentArg)
if {[regexp {b(o|O)s} $argRqmt]} {
set argVar 1
set optInfo(used,$currentArg) 1
continue
}
# If not flagged boolean, make sure there are additional arguments
# if not enough arguments, signal error
incr i
if { $i == $argCount } {
puts "Error: \'$currentArg\' requires a valid argument"
eval UsageInfo [list $cmd] optInfo
return -code error
}
set optInfo(used,$currentArg) 1
set currentArg [lindex $parseArgs $i]
} else {
upvar [lindex $optInfo(var,unflagged) $unflagIndex] argVar
set argRqmt [lindex $optInfo(rqmt,unflagged) $unflagIndex]
lset optInfo(used,unflagged) $unflagIndex 1
incr unflagCount -1
incr unflagIndex
}
}
if { ![info exists optInfo(var,$currentArg)] &&
[expr {$unflagCount == 0}] &&
[expr {[llength $argVal] != 0}] &&
![regexp {\w\wm} $argRqmt] } {
puts "Error: \'$currentArg\' is not a valid option"
eval UsageInfo [list $cmd] optInfo
return -code error
}
if {![llength $argVal]} {
if {![eval IsValidArg $argRqmt [list $currentArg]]} {
eval UsageInfo [list $cmd] optInfo
return -code error
}
}
lappend argVal $currentArg
if {[regexp {\w\ws} $argRqmt]} {
set argVar [lindex $argVal 0]
} else {
set argVar $argVal
}
}
# Check for missing 'required' arguments
foreach optUsed [array names optInfo used,*] {
regsub "used," $optUsed "rqmt," optRqmt
regsub "used," $optUsed "" unusedArg
for {set i 0} {$i < [llength $optInfo($optUsed)]} {incr i} {
if {![lindex $optInfo($optUsed) $i]} {
switch -regexp [lindex $optInfo($optRqmt) $i] {
{\wr\w} {
if {[regexp {unflagged} $unusedArg]} { set unusedArg "<string>" }
eval UsageInfo [list $cmd] optInfo
return -code error
}
{b(o|O)s} {
upvar $optInfo(var,$unusedArg) argVar
set argVar 0
}
{(d|s|n|f)(o|O)\w} {
upvar $optInfo(var,$unusedArg) argVar
if {![info exists argVar]} { set argVar {} }
}
}
}
}
}
return 1
}
proc IsValidArg {rqmt str} {
switch -regexp $rqmt {
{boolean$} {
if {![string is boolean $str]} {
puts "Error: \'$str\' is not of type booelan when boolean required"
return 0
}
}
{(n\w\w|integer)$} {
if {![string is integer $str]} {
puts "Error: \'$str\' is not of type integer when integer required"
return 0
}
}
{(f\w\w|floating point)$} {
if {![string is double $str]} {
puts "Error: \'$str\' is not of type float when float required"
return 0
}
}
default { return 1 }
}
return 1
}
}
namespace eval $::ns(regress) {
variable buffer {}
variable delta 0.0
variable delta_hi_margin 1.25
variable delta_lo_margin 0.60
variable delta_hi_margin_by_os
array set delta_hi_margin_by_os {AIX 5.0 SunOS 3.0}
variable ignore_no_echo 0
variable count_levels 0
if {![llength [info commands ::assert]]} { namespace export assert }
if {![llength [info commands ::regress]]} { namespace export regress }
# -------------------------------------------------------------------------
# assert -- asserts that an expression evaluates to non-zero.
# Used in regression tests
proc assert {args} {
switch -- [parse_options [calling_proc] {} $args \
"srs expression to test the assertion against - return zero will produce error" test_expr] {
-2 { return }
0 { return -code error "Failed on assert" }
}
if {[catch {uplevel expr [list $test_expr]} rsl] == 1} { return -code error "Failed on badly formed assert: $rsl" }
if {!$rsl} { return -code error "Failed on assert(\{$test_expr\})" }
return
}
# -------------------------------------------------------------------------
# regress
# Used in regression tests
proc regress {args} {
variable $::ns(regress)::buffer
variable $::ns(regress)::count_levels
variable $::ns(regress)::delta
variable $::ns(regress)::ignore_no_echo
set help_str {
e.g. regress {
puts foo
} {
see "f"
see_no "x"
}
Supported regress commands are:
see - find a string in the output buffer and advance to the end of what matched
peek - find a string in the output buffer but don't advance
see_no - don't find a string in the output buffer
rsee - regular expression version of see
rpeek - regular expression version of peek
rsee_no - regular expression version of see_no
}
switch -- [parse_options [calling_proc] {} $args \
"-no_echo bos don't print anything in the log" no_echo \
"srs code to check" script \
"srs checking script \n$help_str" see_commands] {
-2 { return }
0 { return -code error "Failed on regress" }
}
set last_buffer "buffer"
set last_delta "delta"
# lint
set rsl {}
incr count_levels
if {$no_echo && !$ignore_no_echo} {
set script_ok 1
##nagelfar ignore E Bad option -mesg to redirect
redirect -mesg -variable buffer {
if {[catch {uplevel $script} rsl]} {
set script_ok 0
}
}
if { $script_ok == 0 } {
puts $buffer
set buffer $last_buffer
return -code error "$rsl\nFailed on regress"
}
} else {
##nagelfar ignore E Bad option -mesg to redirect
redirect -mesg -variable -tee "buffer" {
if {[catch {uplevel $script} rsl]} {
set buffer $last_buffer
return -code error "$rsl\nFailed on regress"
}
}
}
incr count_levels -1
##nagelfar ignore Only braced
if {[catch {namespace eval $::ns(regress) $see_commands} eval_rsl]} {
if { $no_echo && !$ignore_no_echo } {
puts $buffer
}
if {! [regexp "Failed on" $eval_rsl]} {
set buffer $last_buffer
set delta $last_delta
return -code error "$eval_rsl\nFailed on regress"
}
return -code error $eval_rsl
}
set buffer $last_buffer
set delta $last_delta
return $rsl
}
# Check for the given string in the current buffer, but leave
# the buffer alone
proc peek {string} {
variable $::ns(regress)::buffer
set index [string first $string $buffer]
if {$index < 0} {
return -code error "Failed on \'regress peek\' [list $string]"
}
}
# Check for the given string in the current buffer, and chop
# off everything in the buffer up through what matched
proc see {string} {
variable $::ns(regress)::buffer
set index [string first $string $buffer]
if {$index < 0} {
return -code error "Failed on \'regress see\' [list $string]"
}
# move the current position after what we just matched
set index [expr {$index + [string length $string]}]
set buffer [string range $buffer $index end]
}
# Check that the given string doesn't exist in the current
# buffer.
proc see_no {string} {
variable $::ns(regress)::buffer
set index [string first $string $buffer]
if {$index >= 0} {
return -code error "Failed on \'regress see_no\' [list $string]"
}
}
# Check that the given regular expression matches in the current
# buffer.
proc rpeek {regexp} {
variable $::ns(regress)::buffer
if {! [regexp -- $regexp $buffer]} {
return -code error "Failed on \'regress rpeek\' [list $regexp]"
}
}
# Check that the given regular expression matches in the current
# buffer and chop off everything in the buffer up through what
# matched.
proc rsee {regexp} {
variable $::ns(regress)::buffer
if {! [regexp -- $regexp $buffer match]} {
return -code error "Failed on \'regress rsee\' [list $regexp]"
}
# move the current position after what we just matched
set index [string first $match $buffer]
assert {$index >= 0}
set index [expr {$index + [string length $match]}]
set buffer [string range $buffer $index end]
}
# Check that the given regular expression doesn't match in the current
# buffer.
proc rsee_no {regexp} {
variable $::ns(regress)::buffer
if {[regexp -- $regexp $buffer]} {
return -code error "Failed on \'regress rsee_no\' [list $regexp]"
}
}
}
namespace eval $::ns(redirect) {
if {![llength [info commands ::redirect]]} { namespace export redirect }
proc write_gzip_file {filename} {
set gzip_com [exec which gzip]
if {[llength $gzip_com] == 0} {
return -code error \
"Failed on 'write_gzip_file': unable to find gzip command"
}
if {[catch {open "|$gzip_com -c > $filename" w} file_id]} {
return -code error \
"Failed on 'write_gzip_file': unable to open $filename"
}
return $file_id
}
proc append_gzip_file {filename} {
set gzip_com [exec which gzip]
if {[llength $gzip_com] == 0} {
return -code error \
"Failed on 'append_gzip_file': unable to find gzip command"
}
if {[catch {open "|$gzip_com -c >> $filename" a} file_id]} {
return -code error \
"Failed on 'append_gzip_file': unable to open $filename"
}
return $file_id
}
if {! [llength [info commands ::tcl_puts]]} {
rename ::puts ::tcl_puts
} elseif {![llength [info commands $::ns(redirect)::file_puts]]} {
puts "Warning: \'puts\' could not be aliased to \'tcl_puts\'"
puts "\t\'tcl_puts\' already exists"
}
# make puts refer to the new tcl_puts
interp alias {} ::puts {} ::tcl_puts
# A replacement version of puts. All this layer does is to send puts
# commands that would have gone to stdout to the filehandle
# given in the file argument
proc file_puts {tee file args} {
switch [llength $args] {
1 {
::tcl_puts $file [lindex $args 0]
if {$tee} {
::tcl_puts [lindex $args 0]
}
}
2 {
if {[lindex $args 0] eq "-nonewline"} {
::tcl_puts -nonewline $file [lindex $args 1]
if {$tee} {
::tcl_puts -nonewline [lindex $args 1]
}
} elseif {[lindex $args 0] eq "stdout"} {
::tcl_puts $file [lindex $args 1]
if {$tee} {
::tcl_puts [lindex $args 1]
}
} else {
::tcl_puts [lindex $args 0] [lindex $args 1]
}
}
3 {
if {[lindex $args 1] eq "stdout"} {
::tcl_puts [lindex $args 0] $file [lindex $args 2]
if {$tee} {
::tcl_puts [lindex $args 0] [lindex $args 2]
}
} else {
::tcl_puts [lindex $args 0] [lindex $args 1] [lindex $args 2]
}
}
default {
eval {::tcl_puts $args}
}
}
}
# A replacement version of puts. All this layer does is to send puts
# commands that would have gone to stdout to the variable given
# in the given level's stack frame
proc var_puts {tee args} {
variable result
switch [llength $args] {
1 {
append result [lindex $args 0] "\n"
if {$tee} {
::tcl_puts [lindex $args 0]
}
}
2 {
if {[lindex $args 0] eq "-nonewline"} {
append result [lindex $args 1]
if {$tee} {
::tcl_puts -nonewline [lindex $args 1]
}
} elseif {[lindex $args 0] eq "stdout"} {
append result [lindex $args 1] "\n"
if {$tee} {
::tcl_puts [lindex $args 1]
}
} else {
::tcl_puts [lindex $args 0] [lindex $args 1]
}
}
3 {
if {[lindex $args 1] eq "stdout"} {
append result [lindex $args 2]
if {$tee} {
::tcl_puts [lindex $args 0] [lindex $args 2]
}
} else {
::tcl_puts [lindex $args 0] [lindex $args 1] [lindex $args 2]
}
}
default {
eval "::tcl_puts $args"
}
}
}
proc redirect {args} {
switch -- [parse_options [calling_proc] {} $args \
"-append bos appends to the file or variable" append \
"-tee bos writes to stdout also" tee \
"-mesg bos ignored for all other tools (for now)" mesg \
"-variable bos writes to Tcl variable instead of a file" variable \
"srs the file or variable name to write into" file_var_name \
"srs the Tcl code to execute" script] {
-2 { return }
0 { return -code error }
}
if {([llength $file_var_name] != 1) && \
([string index $file_var_name 0] ne "|")} {
return -code error "$file_var_name doesn't look like a filename"
}
if {$variable} {
# remember what the alias for puts is currently so we can restore it
set prev_alias [interp alias {} ::puts]
# override the current puts alias
interp alias {} ::puts {} $::ns(redirect)::var_puts $tee
# remember the value of the redirect buffer so we can restore it
namespace eval $::ns(redirect) {
if {[info exists result]} { set prev_result $result }
set result {}
}
} else {
# open the file requested
if {[file extension $file_var_name] eq ".gz"} {
if {$append} {
set file [append_gzip_file $file_var_name]
} else {
set file [write_gzip_file $file_var_name]
}
} else {
if {$append} {
set file [open $file_var_name a]
} else {
set file [open $file_var_name w]
}
}
# remember what the alias for puts is currently so we can restore it
set prev_alias [interp alias {} ::puts]
# override the current puts alias
interp alias {} ::puts {} [set ::ns(redirect)]::file_puts $tee $file
}
# evaluate the given script
set error [catch {uplevel $script} rsl]
# put the puts alias back where it was before
eval {interp alias {} ::puts {}} $prev_alias
if {$variable} {
upvar $file_var_name my_variable
if {$append} {
append my_variable [namespace eval $::ns(redirect) { set result }]
} else {
set my_variable [namespace eval $::ns(redirect) { set result }]
}
if {[namespace eval $::ns(redirect) { info exists prev_result }]} {
if {$tee} { namespace eval $::ns(redirect) { append prev_result $result } }
namespace eval $::ns(redirect) { set result $prev_result }
}
} else {
# put the stdout handle back where it was before
close $file
}
# return the results of the executed script
if {$error} {
return -code "error" $rsl
}
return $rsl
}
}
# This section added to implement compatible attributes or dummies for compatibility purposes
namespace eval $::ns(compat) {
# This attribute is defined such that setting it in all tools does not cause any errors
# however, the attribute may not perform the intended tasks in all tools
catch {
define_attribute \
-category compatibility \
-data_type string \
-obj_type root \
-default_value "<compatiblity-only>" \
-help_string "Require all commands executed in the session to be echo'ed to the log." \
source_verbose
}
catch {
define_attribute \
-category compatibility \
-data_type boolean \
-obj_type root \
-default_value true \
-help_string "do not echo CCL warning messages" \
ccl_display_warnings
}
}
if {![llength [info commands ::define_attribute]]} {
namespace import $::ns(compat)::define_attribute
}
if {![llength [info commands ::set_attribute]]} {
namespace import $::ns(compat)::set_attribute
}
if {![llength [info commands ::get_attribute]]} {
namespace import $::ns(compat)::get_attribute
}
if {![llength [info commands ::dispatch_subcommand]]} {
namespace import $::ns(compat)::dispatch_subcommand
}
if {![llength [info commands ::redirect]]} {
namespace import $::ns(redirect)::redirect
}
if {![llength [info commands ::assert]]} {
namespace import $::ns(regress)::assert
}
if {![llength [info commands ::regress]]} {
namespace import $::ns(regress)::regress
}
if {![llength [info commands ::add_command_help]]} {
namespace import $::ns(compat)::add_command_help
}
if {![llength [info commands ::calling_proc]]} {
namespace import $::ns(compat)::calling_proc
add_command_help calling_proc "Enable porting of RC ae-ware scripts"
}
if {[llength [info commands $::ns(compat)::parse_options]]} {
if {![llength [info commands ::parse_options]]} {
namespace import $::ns(parse_opt)::parse_options
add_command_help parse_options "Enable porting of RC ae-ware scripts"
}
}
add_command_help $::ns(compat)::define_attribute "Enable porting of RC ae-ware scripts"
add_command_help $::ns(compat)::get_attribute "Enable porting of RC ae-ware scripts"
add_command_help $::ns(redirect)::redirect "redirects stdout to a file or variable temporarily"
regexp {\d+(\.\d+)+} {Revision: 1.35} pkgRev
package provide compatibility $pkgRev
}
# parse_options bla {} "-a dd -b -c ff st1 st2" "-a srs a opt" aopt "-b bos b bool" bopt "-c srs c option" copt "srs unflagged 1" u1 "srs unflagged 2" u2
# parse_options bla {} "-a dd" "-a srs a opt" aopt
# parse_options bla {} "-b" "-b bos b bool" bopt
# parse_options bla {} "st1" "srs unflagged 1" u1
# parse_options bla {} "4 -fii" "nos unflagged 1" u1
# parse_options test {} "-help" "-c srs cat" copt "-set_function sos sfunc" sopt
# parse_options test {} "-he" "-helper bos dummy" hbool
# parse_options bla {} {-c dskj -d jdskla -help_string "dsd djskal"} "-c srs cat" copt "-d srs dtype" dopt "-help_string sos hstring" hopt
# parse_options bla {} {-c dskj -d jdskla -help_string "dsd djskal"} "-c_1 srs cat" copt "-d_1 srs dtype" dopt "-help_string sos hstring" hopt
# parse_options bla {} {-c dskj } "-c_1 srs cat" copt
# parse_options bla {} "fieldInfo emac" "srm opt" aopt
# define_attribute -obj obj -data data -cat cat -def def -comp comp -help_s help bla
#===========================================================================
#
# Copyright 1997-2013 Cadence Design Systems, Inc. All rights reserved worldwide.
#
# The Tcl computer program and related information (collectively "Licensed Material")
# contained herein are protected by copyright law and international treaties.
#
# Cadence grants Recipient of the Licensed Material a nonexclusive right
# to use, copy, and modify the Licensed Material. Should Recipient
# desire to distribute any portion of the Licensed Material, Recipient
# must obtain Cadence's permission. In no event shall Recipient use the
# Licensed Material for benchmarking purposes against Cadence's products.
#
# The Licensed Material is provided to Recipient to use at Recipient's
# own risk. The Licensed Material may not be compatible with current or
# future versions of Cadence products, and Cadence will not provide any
# technical support for the Licensed Material, whether modified or not
# by the Recipient. THE LICENSED MATERIAL IS PROVIDED "AS IS" AND WITH
# NO WARRANTIES, INCLUDING WITHOUT LIMITATION ANY EXPRESS WARRANTIES OR
# IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE.
#
# IN NO EVENT SHALL CADENCE BE LIABLE TO RECIPIENT OR ANY THIRD PARTY
# FOR ANY INCIDENTAL, INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES, OR ANY
# OTHER DAMAGES WHATSOEVER (INCLUDING, WITHOUT LIMITATION, DAMAGES FOR
# LOSS OF BUSINESS PROFITS, BUSINESS INTERRUPTION, LOSS OF BUSINESS
# INFORMATION, OR OTHER PECUNIARY LOSS) ARISING OUT OF THE USE OR
# INABILITY TO USE LICENSED MATERIAL, WHETHER OR NOT THE POSSIBILITY OR
# CAUSE OF SUCH DAMAGES WAS KNOWN TO CADENCE.
#
# Cadence Design Systems, Inc.
# 2655 Seely Avenue
# San Jose, CA 95134
#
#===========================================================================