#!/usr/bin/perl -w # magma_gen_eco - a perl script that generates ECO TCL scripts for Magma # # Perl By Jeff Winston (http://www.kwcpa.com/tools), # TCL by Kevin Joyce of Edgerate Consulting (http://www.edgerate.com) # # Rev. 3.0, 3/15/07 Copyright (C) 2004, 2005, 2006, 2007 # # Added many commands # changed use of "data only" to "data list" # source file is written to output file as comment # # --------------------------------------------------------------------- # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # #of the License, or (at your option) any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, # USA. or check out http://www.gnu.org/copyleft/gpl.html # --------------------------------------------------------------------- # # ####################################################################### # Currently this is a beta script and may have typos. Please let me # # know if you find any. - jw # ####################################################################### # # # This script requires a 'database' file that is generated from Magma # using the command # # report arc -file arc.rpt # $database_filename = "/projects/rockford/utils/arc.rpt"; # # you also need to define the as is done below # $libname = "tcb013ghp_211a"; # # it also helps to have the pre-eco netlist if you are doing real edits # # This script can be used in two different ways. In the simplest way, you # specify buffer additions, buffer deletions, wiring changes, or cell # replacements in a command language, and the script generates the # proper MAGMA TCL commands. # # A more advanced use is to generate hold fix ECOs from a Primetime Report. # To use this mode, you have to set up a small array, some variables and a # Hash below so that the script has a selection of buffers it can use to # add delays. # # a more complete description of how to use the script can be read by # calling the script with no arguments ########## The following variables have to be set up only if you are # generating hold fixes from a PrimeTime report # # Setup variables for Hold-fixing mode start here $margin = .05; # We are trying to achieve a hold margin of 50ps $slow_mult = 0.5; # Indicates that buffers provide 1/0.5 = twice as much delay # in the slow corner compared to the fast one. # This is a list of the possible slack reductions achievable in the fast corner # by use of different buffers. The values are in ps. %search=(); @slack_reductions = (40,75,125,250,350); # This Hash associates a buffer name with each delay. Update as needed. %slack_buf = (); $slack_buf{350} = "DLY4X1"; $slack_buf{250} = "DLY3X1"; $slack_buf{125} = "DLY2X1"; $slack_buf{75} = "DLY1X1"; $slack_buf{40} = "BUFX2"; ############### end of hold-fixing mode setup # general initialization use Fcntl; $| = 1; # enable autoflush # declare arrays %family=(); %filetype=(); %pinslack=(); %trans=(); # eco number and counter $econum=0; # parse input line $default_output_filename = 1; $gen_hold_rpt = 0; $overwrite =0; $eco_base=0; $netlist_name=0; if ($#ARGV == -1) { print_help(); } while ($#ARGV > -1) { if ($ARGV[0] eq "-ov") { shift @ARGV; $overwrite = 1; } elsif (($ARGV[0] eq "-s") || ($ARGV[0] eq "-f") || ($ARGV[0] eq "-c")) { if ($#ARGV < 1) { print "No argument for file descriptor. Use -h for help. Exiting\n"; exit; } $type = shift @ARGV; $filename = shift @ARGV; push @files, $filename; $filetype{$filename} = substr($type,1,1); if (($filetype{$filename} eq "s") || ($filetype{$filename} eq "f")) { $gen_hold_rpt = 1; } } elsif ($ARGV[0] eq "-h") { print_help(); } elsif ($ARGV[0] eq "-o") { if ($#ARGV < 1) { print "No argument for output filename, Use -h for help. Exiting\n"; exit; } shift @ARGV; $output_file = shift @ARGV; $default_output_filename = 0; } elsif ($ARGV[0] eq "-b") { if ($#ARGV < 1) { print "No argument for eco basename. Use -h for help. Exiting\n"; exit; } shift @ARGV; $eco_base = shift @ARGV; } elsif ($ARGV[0] eq "-n") { if ($#ARGV < 1) { print "No argument for netlist. Use -h for help. Exiting\n"; exit; } shift @ARGV; $netlist_name = shift @ARGV; } else { print "Unknown argument $ARGV[0]. Use -h for help. Exiting\n"; exit; } } # if no eco_base specified, make one from the filename if ($eco_base eq "0") { $eco_base = $filename; $dot = index($eco_base,"."); if ($dot > 0) { $eco_base = substr($eco_base,0,$dot); } $sl = rindex($eco_base,"/"); if ($sl > 0) { $eco_base = substr($eco_base,$sl+1); } } print "magma_gen_eco.pl by Jeff Winston & Kevin Joyce\n\n"; # read database read_database($database_filename); # read_netlist if ($netlist_name ne 0) { read_netlist($netlist_name); } # open output files if ($default_output_filename == 1) { $output_file = "ecos_" . $eco_base . ".tcl"; } if ($overwrite == 0) { if (defined(sysopen(TMPFIL,$output_file,O_RDONLY))) { print "$output_file exists, Aborting\n"; close (TMPFIL); exit; } } open (OUTFIL, "> $output_file") or die "Failed opening $output_file\n"; print OUTFIL "# Generated by magma_gen_eco.pl\n\n"; if ($gen_hold_rpt == 1) { $gen_hold_rpt_file = "eco_hold_report_" . $eco_base . ".log"; open (HOLD_RPT_FIL, "> $gen_hold_rpt_file") or die "Failed opening $gen_hold_rpt_file\n"; print HOLD_RPT_FIL "Slack Reduction Table\n"; print HOLD_RPT_FIL "=====================\n"; foreach $slack (@slack_reductions) { print HOLD_RPT_FIL "$slack $slack_buf{$slack}\n"; } print HOLD_RPT_FIL "\n"; } # print command files to output file foreach $file (@files) { if ($filetype{$file} eq "c") { @source = `cat $file`; print OUTFIL "\n###### Source of $file\n"; foreach $ln (@source) { chomp $ln; print OUTFIL "# $ln\n"; } print OUTFIL "######\n\n"; } } # process files foreach $file (@files) { open (INFIL, "< $file") or die "Error opening $file"; # process command file if ($filetype{$file} eq "c") { while (defined($line = )) { chomp $line; # process command line if ((length($line) > 3) && (substr($line,0,1) ne "#")) { @words0 = split " ",$line; @words=(); $last = -1; foreach $word (@words0) { if (substr($word,0,1) eq "\[") { $words[$last] = $words[$last] . " " . $word; } else { push @words, $word; $last++; } } # generate globally-used labels $ecostr = $eco_base . "_" . $econum; $eco_netname = "eco_net_" . "$ecostr"; $eco_bufname = "eco_buf_" . "$ecostr"; # ECHO and DEFINE are processed right here if ($words[0] eq "ECHO") { if (length($line) < 5) { print OUTFIL "\n"; } else { print OUTFIL "# " . substr($line,5) . "\n"; } } elsif (($words[0] eq "DEFINE") && ($#words == 2)) { $trans{$words[1]} = $words[2]; print OUTFIL "# Defined $words[1] as synonym for $words[2]\n"; } # all other commands generate subroutine calls elsif (($words[0] eq "PREBUF") && ($#words == 2)) { pre_buf($words[1],$words[2],0,0,0); $econum++; } elsif (($words[0] eq "PREBUFX") && ($#words == 4)) { pre_buf($words[1],$words[2],1,$words[3],$words[4]); $econum++; } elsif (($words[0] eq "POSTBUF") && ($#words == 2)) { post_buf($words[1],$words[2],0,0,0); $econum++; } elsif (($words[0] eq "POSTBUFX") && ($#words == 4)) { post_buf($words[1],$words[2],1,$words[3],$words[4]); $econum++; } elsif (($words[0] eq "DELBUF") && ($#words == 1)) { del_buf($words[1]); $econum++; } elsif (($words[0] eq "DIODE") && ($#words == 1)) { add_diode($words[1]); $econum++; } elsif (($words[0] eq "REPCEL") && ($#words == 2)) { replace_cell($words[1],$words[2]); $econum++; } elsif (($words[0] eq "DETACH") && ($#words == 1)) { detach_pin($words[1]); $econum++; } elsif (($words[0] eq "ASSNET") && ($#words == 2)) { assign_net($words[1],$words[2]); $econum++; } elsif (($words[0] eq "ADDNET") && ($#words == 1)) { create_net($words[1]); $econum++; } elsif (($words[0] eq "ATTACH") && ($#words == 2)) { connect_pin_to_net($words[1],$words[2]); $econum++; } elsif (($words[0] eq "DELCEL") && ($#words == 1)) { del_cell($words[1]); $econum++; } elsif (($words[0] eq "ADDCEL") && ($#words == 2)) { add_cell($words[1],$words[2]); $econum++; } elsif (($words[0] eq "MOVPIN") && ($#words == 2)) { detach_pin($words[1]); connect_pin_to_net($words[1],$words[2]); $econum++; } elsif (($words[0] eq "DETASS") && ($#words == 2)) { assign_net($words[1],$words[2]); detach_pin($words[1]); $econum++; } elsif (($words[0] eq "MOVNET") && ($#words == 2)) { $netname = "NET_" . $econum; assign_net($words[1],$netname); detach_pin($words[1]); connect_pin_to_net($words[2],$netname); $econum++; } elsif (($words[0] eq "BRNNET") && ($#words == 2)) { $oldpin = get_net($words[1]); $netname = "NET_" . $econum; assign_net($oldpin, $netname); connect_pin_to_net($words[2],$netname); $econum++; } else { print "ERROR: Unrecognized line $line\n"; } } } } else { # process Primetime File $mult=1; if ($filetype{$file} eq "s") { $mult = $slow_mult; } $met=0;$look=0; while (($met < 1) && (defined($line = ))) { chomp $line; # parse line @words = split " ",$line; if ($#words > -1) { # startpoint = start collecting if ($words[0] eq "Endpoint:") { $end_point = $words[1]; } elsif ($words[0] eq "clock") { # start looking for the flop pin $look=1; } elsif (($look==1) && ($words[0] =~ $end_point)) { # did we find it? $look=0; @components = split "/",$words[0]; if (($components[$#components] eq "D") || ($components[$#components] eq "SI") ) { $flop_pin = $words[0]; # we did! } } elsif ($words[0] eq "slack") { if (($words[1] =~ "MET") && ($words[$#words] >= $margin)) { # no more vios, we're done $met=1; } elsif ($words[0] eq "slack") { # a violation $slack = $words[$#words] * $slow_mult; # adjust the slack if (defined($pinslack{$flop_pin})) { # store it with the pin if ($slack < $pinslack{$flop_pin}) { if ($gen_hold_rpt == 1) { print HOLD_RPT_FIL "CHANGE $file: Pin: $flop_pin New: $slack Old: $pinslack{$flop_pin}\n"; } $pinslack{$flop_pin} = $slack; } } else { $pinslack{$flop_pin} = $slack; push @flop_pins, $flop_pin; if ($gen_hold_rpt == 1) { print HOLD_RPT_FIL "ADD $file: Pin: $flop_pin New: $slack\n"; } } } } } } } close(INFIL); print "Finished processing input file $file\n"; print OUTFIL "\n# Finished processing input file $file\n"; } print "Generating ECOs\n"; # process all flop pins foreach $flop_pin (@flop_pins) { $slack = $pinslack{$flop_pin}; while ($slack < $margin) { if ($gen_hold_rpt == 1) {print HOLD_RPT_FIL "\nProcessing $flop_pin with slack $slack\n";} @return_data = get_buf($slack); $slack = $return_data[0]; $ecostr = $eco_base . "_" . $econum; pre_buf($flop_pin,$return_data[1],0,0,0); $econum++; } } print "$econum ECOs performed, $output_file written\n"; if ($gen_hold_rpt == 1) { print "$gen_hold_rpt_file written\n"; } exit; # subroutines start here sub pre_buf { # insert a buffer BEFORE another buffer # get arguments $target_pin = do_trans(shift @_); $buffer_type = shift @_; $enable_forcexy = shift @_; $forcex = shift @_; $forcey = shift @_; # deconstruct target pin @components = get_components($target_pin); $target_cell = $components[0]; # get buffer family if (!(exists($family{$buffer_type}))) { print "ERROR: No family for $buffer_type. Exiting\n"; exit; } $buffer_type = $family{$buffer_type} . "/" . $buffer_type; # write ECO print OUTFIL "\n# $econum Adding $buffer_type before $target_pin. New elements numbered $ecostr\n"; print OUTFIL "set receive_pin \$m/$target_pin\n"; print OUTFIL "set receive_net [data list pin_net \$receive_pin]\n"; print OUTFIL "set receive_cell [data find \$m $target_cell -type cell]\n"; print OUTFIL "set new_dly_cell [data create cell \$l/$buffer_type \$m $eco_bufname]\n"; print OUTFIL "set intermed_net [data create net \$m $eco_netname]\n"; print OUTFIL "data detach \$receive_net net_pin \$receive_pin\n"; print OUTFIL "data attach \$receive_net net_pin \$new_dly_cell/A\n"; print OUTFIL "data attach \$intermed_net net_pin \$new_dly_cell/Y\n"; print OUTFIL "data attach \$intermed_net net_pin \$receive_pin\n"; print OUTFIL "set box [data list cell_outline \$receive_cell]\n"; if ($enable_forcexy == 0) { print OUTFIL "set xval [query box left \$box]\n"; print OUTFIL "set yval [query box bottom \$box]\n"; print OUTFIL "run plan move cell \$new_dly_cell \"\$xval \$yval\"\n"; } else { print OUTFIL "run plan move cell \$new_dly_cell \"$forcex $forcey\"\n"; } } sub post_buf { # insert a buffer AFTER another buffer # get arguments $target_pin = do_trans(shift @_); $buffer_type = shift @_; $enable_forcexy = shift @_; $forcex = shift @_; $forcey = shift @_; # deconstruct target pin @components = get_components($target_pin); $target_cell = $components[0]; # get buffer family if (!(exists($family{$buffer_type}))) { print "ERROR: No family for $buffer_type. Exiting\n"; exit; } $buffer_type = $family{$buffer_type} . "/" . $buffer_type; # write ECO print OUTFIL "\n# $econum Adding $buffer_type after $target_pin. New elements numbered $ecostr\n"; print OUTFIL "set drive_pin \$m/$target_pin\n"; print OUTFIL "set driven_net [data list pin_net \$drive_pin]\n"; print OUTFIL "set old_drv_cell [data find \$m $target_cell -type cell]\n"; print OUTFIL "set new_drv_cell [data create cell \$l/$buffer_type \$m $eco_bufname]\n"; print OUTFIL "set intermed_net [data create net \$m $eco_netname]\n"; print OUTFIL "data detach \$driven_net net_pin \$drive_pin\n"; print OUTFIL "data attach \$driven_net net_pin \$new_drv_cell/Y\n"; print OUTFIL "data attach \$intermed_net net_pin \$new_drv_cell/A\n"; print OUTFIL "data attach \$intermed_net net_pin \$drive_pin\n"; print OUTFIL "set box [data list cell_outline \$old_drv_cell]\n"; if ($enable_forcexy == 0) { print OUTFIL "set xval [query box left \$box]\n"; print OUTFIL "set yval [query box bottom \$box]\n"; print OUTFIL "run plan move cell \$new_drv_cell \"\$xval \$yval\"\n"; } else { print OUTFIL "run plan move cell \$new_drv_cell \"$forcex $forcey\"\n"; } } sub add_diode { # attach a diode to a net $target_pin = do_trans(shift @_); @components = get_components($target_pin); $target_cell = $components[0]; print OUTFIL "\n# $econum Adding Diode to $target_pin. New elements numbered $ecostr\n"; print OUTFIL "set target_pin \$m/$target_pin\n"; print OUTFIL "set target_net [data list pin_net \$target_pin]\n"; print OUTFIL "set target_cell [data find \$m $target_cell -type cell]\n"; print OUTFIL "set ant_cell [data create cell \$l/ANTENNA/ANTENNA \$m eco_antenna_$ecostr]\n"; print OUTFIL "data attach \$target_net net_pin \$ant_cell/A\n"; print OUTFIL "set box [data list cell_outline \$target_cell]\n"; print OUTFIL "set xval [query box left \$box]\n"; print OUTFIL "set yval [query box bottom \$box]\n"; print OUTFIL "run plan move cell \$ant_cell \"\$xval \$yval\"\n"; } sub del_buf { # get arguments $target_buf = do_trans(shift @_); # write ECO print OUTFIL "\n# $econum Removing $target_buf\n"; print OUTFIL "set del_buf [data find \$m $target_buf -type cell]\n"; print OUTFIL "set del_net [data list pin_net \$del_buf/pin:Y]\n"; print OUTFIL "set retain_net [data list pin_net \$del_buf/pin:A]\n"; print OUTFIL "set sink_pin [query node successors \$del_buf/pin:Y]\n"; print OUTFIL "data detach \$del_net net_pin \$sink_pin\n"; print OUTFIL "data attach \$retain_net net_pin \$sink_pin\n"; print OUTFIL "data delete object \$del_buf\n"; print OUTFIL "data delete object \$del_net\n"; } sub del_cell { # get arguments $target_cel = do_trans(shift @_); # write ECO print OUTFIL "\n# $econum Removing $target_cel\n"; print OUTFIL "set del_cel [data find \$m $target_cel -type cell]\n"; print OUTFIL "data delete object \$del_cel\n"; } sub add_cell { # get arguments $new_cell = shift @_; $name = shift @_; # check for cell family if (!(exists($family{$new_cell}))) { print "ERROR: No family for $new_cell. Exiting\n"; exit; } # generate new full cell name $new_full_cell = $family{$new_cell} . "/" . $new_cell; $new_full_cell = $new_cell; # write ECO print OUTFIL "\n# $econum Adding $new_cell. \n"; print OUTFIL "data create $new_full_cell \$m $name\n"; } sub replace_cell { # get arguments $old_cell = do_trans(shift @_); $new_cell = shift @_; # check for cell family if (!(exists($family{$new_cell}))) { print "ERROR: No family for $new_cell. Exiting\n"; exit; } # generate new full cell name $new_full_cell = $family{$new_cell} . "/" . $new_cell; # write ECO print OUTFIL "\n# $econum Replacing $old_cell with $new_full_cell\n"; print OUTFIL "data attach \$m/$old_cell cell_model \$l/$new_full_cell\n"; } sub detach_pin { # detach a pin from its net # get argument $pin = do_trans(shift @_); # write ECO print OUTFIL "\n# $econum Detaching pin $pin\n"; print OUTFIL "set eco_pin \$m/$pin\n"; print OUTFIL "set eco_net [data list pin_net \$eco_pin]\n"; print OUTFIL "data detach \$eco_net net_pin \$eco_pin\n"; } sub assign_net { # define a variable for a known net # get arguments $pin = do_trans(shift @_); $var = shift @_; # write ECO print OUTFIL "\n# $econum Assigning net connected to $pin to \"$var\"\n"; print OUTFIL "set search_pin \$m/$pin\n"; print OUTFIL "set $var [data list pin_net \$search_pin]\n"; } sub create_net { # define a variable for a new net (and create the net) # get argument $var = shift @_; # write ECO print OUTFIL "\n# $econum Creating net $eco_netname and assigning to \"$var\"\n"; print OUTFIL "set $var [data create net \$m $eco_netname]\n"; } sub connect_pin_to_net { # make connection between pin and defined net # get arguments $pin1 = do_trans(shift @_); $var = shift @_; # write ECO print OUTFIL "\n# $econum Connecting $pin1 to net \"$var\"\n"; print OUTFIL "set eco_pin \$m/$pin1\n"; print OUTFIL "data attach \$$var net_pin \$eco_pin\n"; } sub get_components { # deconstruct full pin name $full = shift @_; @tokens = split "/",$full; $pin_len = length($tokens[$#tokens]); $cell_len = length($full) - (1 + $pin_len); $cell = substr($full,0 ,$cell_len); $pin = substr($full,$cell_len+1,$pin_len); return($cell,$pin); } sub get_buf { # select proper hold buffer # get arguments $slack = shift @_; $x= $#slack_reductions; $orig_slack = $slack; # find the first buffer smaller than needed, and then use the next largest one while (($x >= 0) && ((.001 * $slack_reductions[$x]) > ($margin + (-1 * $slack)))) {$x--}; if ($x < $#slack_reductions) {$x++}; $slack = $slack + ($slack_reductions[$x] * .001); $buf = $slack_buf{$slack_reductions[$x]}; if ($gen_hold_rpt == 1) {print HOLD_RPT_FIL "Adding Buffer $buf to reduce slack from $orig_slack to $slack\n";} return($slack,$buf); } # instantiate DEFINE sub do_trans { $arg = shift @_; if ($trans{$arg}) { return($trans{$arg}); } else { return ($arg); } } # read in a voneline'd netlist to generate a hash of netnames and a pin each one connects to sub read_netlist { print "Loading Netlist..."; $z=0; $netlist = shift @_; open (INFIL, "< $netlist") or die "Error opening $file"; while (defined(my $line = )) { $z++; if ($z==10000) { print "."; $z=0; } chomp $line; if (index($line,".") > 0) { $line =~ s/\ \[/\%\[/g; $line =~ s/\.//g; $line =~ s/\,//g; $line =~ s/\(/\ /g; $line =~ s/\)//g; $line =~ s/\;//g; @list = split " ",$line; shift @list; $iname = shift @list; while ($#list > 0) { $pin = shift @list; $wire = shift @list; $wire =~ s/\%\[/\ \[/g; $search{$wire} = $iname . "/" . $pin; } } } print "Netlist loaded\n"; close(INFIL); } # search the hash sub get_net { $pin = shift @_; if (defined($search{$pin})) { print OUTFIL "\n# $econum Found net $pin at $search{$pin}\n"; return($search{$pin}); } else { return("NET_NOT_FOUND_FOR_" . $pin); } } # read in the database sub read_database { $database_file = shift @_; open(INFIL, $database_file) or die "Failed to open $database_file\n"; while (defined($line = )) { chomp $line; if (length($line) > 5) { @words = split " ",$line; # get family name - look for library string if ($words[0] =~ $libname) { $sl = rindex($words[0],"/"); # fam name is suffix of first word $fam = substr($words[0],$sl+1); } # otherwise, if first word starts with capitals, it's an element elsif ((substr($words[0],0,2) =~ m/[A-Z][A-Z]/) && ($words[0] !~ "SUPER")) { $family{$words[0]} = $fam; } } } close (INFIL); # if ((length($line) > 5) && (substr($line,0,1) ne "#")) { # chomp $line; # @words = split " ", $line; # if ($#words != 1) { # print "Bad line in database: $line\n"; # } else { # $family{$words[1]} = $words[0]; # } # } # } } sub print_help { print "Usage: magma_gen_eco.pl [-ov] { < -s | -f | -c > } x N [-o ] [-b ] [-n netlist] \n"; print "\nThe main command line arguments are a list of files, each followed by an identifying qualifier which\n"; print "denotes whether the file is a command file (-c), or a Primetime report (-f or -s). Command files\n"; print "are used to generate any type of ECO, but the script can also use a Primetime MIN report to generate\n"; print "hold fix ECOs. See the comments in the script for more details. -f indicates a FAST corner file,\n"; print "-s refers to a slow corner file. For command files, the allowed commands and format are described below:\n"; print "\nThese commands are used for ease of coding\n"; print " DEFINE # for pin or instance names\n"; print " ECHO # echos to output file as comment\n"; print "\nThese commands are typically used for all-layer ECOs\n"; print " PREBUF # add buffer before pin\n"; print " PREBUFX # add buffer before pin, locate at X Y\n"; print " POSTBUF # add buffer after pin\n"; print " POSTBUFX # add buffer after pin, locate at X Y\n"; print " DIODE # attach diode to pin\n"; print " DELBUF # remove buffer (fanout must be 1)\n"; print " REPCEL # replace cell\n"; print " ADDCEL # add cell, assumes you'll attached it later\n"; print "\nThese commands are typically used for metal-only ECOs\n"; print " DETACH # detach pin from its net\n"; print " ASSNET # assign net attached to pin to \n"; print " DETASS # do an ASSNET, followed by a DETACH \n"; print " ADDNET # create net, net name is generated automatically\n"; print " DELCEL # remove cell, assumes it's already detached\n"; print " ATTACH # attach pin to \n"; print " MOVPIN # equivalent of a DETACH and ATTACH\n"; print " MOVNET # assigns a NET to , detaches , and\n"; print " # attaches to the NET\n"; print " BRNNET # identifies a pin attached to , assigns a NET\n"; print " # to the pin, and connects to the NET\n"; print "\nThis file can also contain comments (# in line 1), or blank lines.\n"; print "\n-ov allows the script to over-write old output files.\n"; print "The is used to uniquely tag new elements created by the ECOs.\n"; print "The output filename defaults to ecos_.tcl.\n"; print "The netlist is used to get pinnames for netnames for BRRNET, and must have\n"; print "been run through the voneline utility (http://www.veripool.com)\n\n"; exit; }