You are on page 1of 16

Perl For Hardware Design

Perl for EDA
Perl is a high-level programming language. Larry Wall invented Perl and thousands have contributed their time making it a very powerful tool. Perl borrows heavily from the C programming language and copies the really useful bits from sed, awk, Unix shell, and many other tools and languages.

Perl Productivity
Perl's process, file, and text manipulation facilities make it particularly well-suited for tasks involving automatic code generation, report filtering, netlist patching, generating test vectors and controlling tools. Perl is FREE so maybe you should use it too. These pages offer advice and examples for improving design productivity through with Perl.

Quick Start Perl
In just just 20 minutes this tutorial shows you the core elements of the powerful Perl language. Start filtering netlists and generating reports straight away. Make time today to learn some Perl.

Scalar Variables
Scalar variables have no explicit type. Scalar variables do not have to be declared. Context is very important in Perl. Mathematical operators treat variables as numbers, integers or floating point, depending on the context. String operators treat variables as strings of characters. Conversion between strings and numbers is automatic. Everything in Perl behaves in the most obvious common-sense way. Perl is very intuitive. Here are some scalar variables and some numeric operators:
# End of line comments begin with a # $a = 17; # Scalar variables begin with a dollar symbol # The Perl assigment operator is = # Statements finish with a semicolon ; # # # # Hexadecimal (17 in decimal) Octal (17 in decimal) Binary (17 in decimal) Floating point

$b $c $d $f

= = = =

0x11; 021; 0b10001; 3.142;

$a = $a + 1; $a += 1; $a++;

# Add 1 to variable $a # Add 1 to variable $a # Add 1 to variable $a

$b = $b * 10; # Multiply variable $b by 10; $b *= 10; # Multiply variable $b by 10; # Other arithmetic operators include: # ** Exponentiation # % Modulo division # ++ Auto increment

# # # # # # # #

-< > == != <= >= <=>

Auto decrement Numeric less than Numeric greater than Numeric equality Numeric inequality Numeric less than or equal to Numeric greater than or equal to Numeric compare: Returns -1 0 1

Scalar variables can store strings of characters or words. Single quotes, 'like these', allow spaces to appear inside strings. Double quotes, "like these", allow variables to be automatically substituted or interpolated inside strings.
$a = 'Number of DFFs: '; # $b = "$a$c\n"; # quotes" # print $b; # standard output print $a, $c, "\n"; # # print "That's all\n"; # # # # # # # # # # # # No interpolation with 'single quotes' Interpolation (variable substitution) with "double \n is the newline character This makes "Number of DFFs: 17\n" appear on the As does this line because print takes a comma separated list of arguments to print No commas means a list of one element String operators include: lt String less than gt String greater than le String less than or equal to ge String greater than or equal to cmp String compare: Returns -1 0 1 Prints 1 ASCII-betically 'o' is less than 't' Prints nothing (that is undef, numerically zero) Perl's undefined value is undef ASCII-betically '4' is not less than '3'

print 'one' lt 'two'; print 'buf4' lt 'buf3';

Logic and Truth
Perl considers these to be false:
0; 0.0; '0'; ''; undef; # # # # # Integer zero Decimal zero String containing a single zero character Empty string Undefined

Everything else is true. Here are some logical operators:
$a = 0; $b = 45; # More than one statement per line possible print( $a and $b++ ); # prints 0 * $a = 22; print( $a and $b++ ); # prints 45 * print $b; # prints 46 # * $b++ only evaluated when $a was true # Some logic operators take shortcuts # Other logical operators include # or Logical OR

# # # # # # # # print print print print print 6 & 5; 6 | 5; ! 0; ! 5; ~5; # # # # # #

|| and && not ! | & ~

Logical Logical Logical Logical Logical Bitwise Bitwise Bitwise

OR AND AND NOT NOT OR AND NOT

prints 4, 0b0110 & 0b0101 = 0b0100 prints 7, 0b0110 | 0b0101 = 0b0111 prints 1 prints nothing (that is undef or false) prints 4294967290, same as: 0b11111111111111111111111111111010

Arrays and Hashes
An array is a list of scalar variables. The first element has index 0. The @ symbol is used to denote an array variable.
@components = ( 'X_LUT4', 'X_AND2', 'X_BUFGMUX', 'X_BUF_PP', 'X_FF' ); # or use qw''. Saves typing commas or quotes, gives the same result # qw stands for Quoted Words @components = qw'X_LUT4 X_AND2 X_BUFGMUX X_BUF_PP X_FF'; # or even put the data in columns, gives the same result again @components = qw' X_LUT4 X_AND2 X_BUFGMUX X_BUF_PP X_FF '; # Easier to read this way push( @components, 'X_MUX2' ); # Push another item onto the top push( @components, 'X_ONE' ); # And one more print $components[0]; print $components[5]; # Prints element 0, that is, 'X_LUT4' # Prints element 5, that is, 'X_MUX2'

print "@components\n"; # Prints everything separated by spaces: # X_LUT4 X_AND2 X_BUFGMUX X_BUF_PP X_FF X_MUX2 X_ONE print @components ; # No double quotes, no spaces: # X_LUT4X_AND2X_BUFGMUXX_BUF_PPX_FFX_MUX2X_ONE

In a scalar context an array variable returns its size, the number of elements it contains. The test expression of a while loop is tested for true or false, a scalar question, a scalar context. The shift statement removes items from the bottom of the array, one at a time.
while( @components ) { # ^^^^^^^^^^^^^ Array in scalar context $next_component = shift( @components ); print "$next_component\n";

} # Array variable @components is now empty

In this example @components begins with size 7, which is true. After 7 loops each of the 7 elements have been shifted or removed from the bottom of the array. In the while test expression @components would have returned 7, 6, 5, 4, 3, 2, 1 and finally 0. Zero is false, end of while loop. Hash arrays differ from arrays because the elements are not ordered numerically but associated with unique strings or keys. Hash arrays are associative arrays because they associate values with keys. Maintaining many separate counters is a good hash array application as we will see later. Hashes make a big contribution to Perl's text processing power. The % symbol is used to denote a hash array.
# Initialising several hash keys %components = qw' X_LUT4 0 X_AND2 0 X_BUFGMUX 0 X_BUF_PP 0 X_FF 0 '; # ^^^^^^^^^ keys # ^ values $components{'X_LUT4'} = 1; # Set key X_LUT4 to the value 1 $components{'X_LUT4'}++; # Increment value associated with X_LUT4 print $components{'X_FF'}; # Print value associated with X_FF @keys = keys %components; # Get a list of hash keys print "@keys\n"; # Print them - order is indeterminate %components = (); # Emptying the components hash

Command Line Arguments
There is a special array called @ARGV. Command line arguments are automatically copied into this array variable.
# This script is called process_netlist.pl # Perl scripts often have the file extension .pl $netlist_filename = $ARGV[0]; $report_filename = $ARGV[1]; print " Processing $netlist_filename\n"; print " Writing report to $report_filename\n"; print " ARGV contains '@ARGV'\n"; # Use it in this way: # # # # # C:\perl process_netlist.pl chip_timesim.vhd report.txt Processing chip_timesim.vhd Writing report to report.txt ARGV contains 'chip_timesim.vhd report.txt' C:\

Conditions

Perl has many conditional statements. The if statement asks a true/false question. If the answer is true it executes a block of code.
if( $ff_count == 1 ) # ^^^^^^^^^^^^^^ Is this expression true or false? { # Do this action if it is true print "There is 1 flip flop\n"; } else { # Do this action if it is false print "There are $ff_count flip flops\n"; } # More compact layout if( $ff_count == 1 ) { print "There is 1 flip flop\n"; } else { print "There are $ff_count flip flops\n"; }

It is not necessary to have an else part. The (round brackets) are required around the expression. The {curly brackets} are required around the actions. The while loop repeatedly executes a block of statements while a conditional expression is true.
# Counting to one hundred while( $count < 100 ) { $count++; # Perl assumes $count == 0 the first time print "$count\n"; }

Variables do not have to be declared or initialised. Perl will create the variable when it is first used. If the variable is first used in a numeric context then its undefined initial value will be interpreted as zero. If the variable is first used in a string context then its undefined initial value will be interpreted as an empty string. Perl's default behaviour makes good sense. Counting begins at zero. Writing begins with an blank page. Another loop statement is foreach. It is used for looping through a list of scalars, numeric or string.
foreach $course ( 'VHDL', 'SystemVerilog', 'SystemC', 'Perl', 'Tcl/Tk', 'PSL' ) { print "There is a $course Doulos training course\n"; } # $course is the loop variable. # It takes the string value 'VHDL' for the first loop # and 'PSL' for the last loop. # Get a list from an array variable foreach $component ( @components ) { print "Component is $component\n"; }

Files

Text files are created with the open and print statements. Perl uses file handles to refer to each open file. A file handle is just a string, conventionally written in uppercase letters without quotes. Use the print statement to write text into a file.
open( FILE1, '>file1.txt' ); # ^ print FILE1 "The first line to file1.txt\n"; print FILE1 "The final line to file1.txt\n"; close( FILE1 ); close a file > means open in write mode # Don't have to explicitly

print STDOUT "This goes to the standard output\n"; print "So does this\n"; # ^^^^^^ STDOUT is a file handle that always # refers to the standard output. # It is the default so doesn't have to be stated.

Text files are read using the open statement and the input record operator. Standard input, the input typed into the keyboard in a command line application, can be read from the STDIN file handle.
open( FILE2, 'file2.txt' ); $first_line = <FILE2>; $first_line. while( $line = <FILE2> ) { print $line; } undef. $standard_input = <STDIN>; line. chomp( $standard_input ); # Open in read mode - the default mode # Reads the first line from file2.txt into # Includes the newline character, \n. # Read and print remaining lines from file2.txt. # When every line has been read <FILE2> returns # Read a line from the standard input. # Can be the keyboard if run from the command # Remove the trailing newline character

Here is a short program. It reads every line from a file named in the first command line argument. The lines are written to a report file named in the second command line argument. Numbers are printed at the beginning of each line.
$netlist_filename = $ARGV[0]; $report_filename = $ARGV[1]; open( FILE_IN, $netlist_filename ); open( FILE_OUT, ">$report_filename" ); while( $line = <FILE_IN> ) { $line_count++; print FILE_OUT "$line_count: $line"; } # perl filter_netlist.pl chip_timesim.vhd report.txt

This Perl script does the same using standard input and standard output.
while( $line = <STDIN> ) { $line_count++; print "$line_count: $line"; }

# perl filter_netlist.pl < chip_timesim.vhd > report.txt

Pattern Matching
Perl's matching operator uses regular expressions to search a string for a match. Regular expressions are patterns used to find a match for a string of characters contained in a longer string. Regular expressions are built from character classes. The simplest character class is a single character. The letter A matches a capital letter A once. Character classes only match one character but the character can be any from a list of qualifying characters.
$string = "Novice to Expert in a 3 day Perl course.\n"; print $string; if( $string =~ m/Expert/ ) { # A successful match returns 1 so this statement is executed print "This string contains the substring 'Expert'\n"; } # m stands for match # Forward slashes are used to /delimit/ regular expressions. # =~ tells the m operator which string to search. # The m is optional when // are used.

Regular Expressions
Individual letters are very limited character classes. Ranges of characters are matched using character class shortcuts. Any alphanumeric character matches \w, including underscores. Conveniently, most languages recognise identifiers containing letters, digits and underscores. Quantifiers allow character classes to be repeated. The most common quantifiers are question mark, ?, asterisk, *, and plus, +. Zero or one repetition is ?. Zero or more repetitions is *. One or more repetitions is +.
use English; $string = "Novice to Expert in a 3 day Perl course.\n"; if( $string =~ /\w+/ ) { # \w+ matches one or more alphanumeric characters in a row print "Matched: $MATCH\n"; # Matched: Novice }

Readable English names for some Perl special variables are provided by the English Perl module. $MATCH gets a copy of the substring successfully matched. Without using the English Perl module $MATCH would have to be called $&.
use English; $string = "Novice to Expert in a 3 day Perl course.\n"; if( $string =~ /Perl\s+\w+/ ) { # ^^^^ matches Perl # ^^^ matches one or more white space characters # (including space, tab and newline) # ^^^ matches one or more alphanumeric characters print "Matched: $MATCH\n"; # Matched: Perl course } # \w? Zero or one letter, digit or underscore # \w One letter, digit or underscore # \w* Zero or more letters, digits or underscores # \w+ One or more letters, digits or underscores # \W One character but not a letter, digit or underscore

# #

\s \S

White space character, space, tab or newline One character but not a space, tab or newline

Groups
Sub expressions can be grouped together and stored in back reference buffers. Round brackets are used to (group) sub expressions. The back reference buffers have the names $1, $2, $3 etc. The substring that matches the first bracketed group is copied into $1. The second into $2 etc. There is a Xilinx Vital timing model in a file called chip_timesim.vhd. Here is one component instantiation from it:
c4_n001449 : X_LUT4 generic map( INIT => X"0001" ) port map ( ADR0 => c4_count(4), ADR1 => c4_count(18), ADR2 => c4_count(3), ADR3 => c4_count(5), O => CHOICE121_pack_1 );

The component name, X_LUT4, could be matched using a regular expression containing a group. This short script opens the file, finds every component instantiation reporting the component name.
open( VHDL_FILE, 'chip_timesim.vhd' ); while( $line = <VHDL_FILE> ) { if( $line =~ /\w+\s*:\s*(X_\w+)/ ) { # ^^^ # ^^^ # ^ # ^^^ # ^^^^^ with X_ # into $1) print "Found instantiation of $1\n"; } }

Instance label Zero or more white space characters : Zero or more white space characters Group containing a word beginning (copied

Netlist Filtering
The following script takes the filename of a Xilinx netlist from the command line. It finds and counts every component instantiation. Finally, it prints a list of all the component names found and the number of appearances.
# Pulling it all together # Everything in this script is described above $netlist_filename = $ARGV[0]; open( VHDL_FILE, $netlist_filename );

while( $line = <VHDL_FILE> ) { if( $line =~ /\w+\s*:\s*(X_\w+)/ ) { $component_hash{$1}++; } } @name_array = keys %component_hash; foreach $component_name ( @name_array ) { print "$component_name: $component_hash{$component_name}\n"; }

Extracting information from text files is easy given a little Perl knowledge. The following output was generated by the above script:
X_FF: 56 X_AND2: 29 X_ONE: 25 X_INV_PP: 23 X_BUF_PP: 395 X_ZERO: 4 X_TOC: 1 X_XOR2: 53 X_BUFGMUX: 1 X_OR2: 8 X_ROC: 1 X_MUX2: 96 X_LUT4: 123 X_TRI_PP: 20

Automatic Code Generation
Perl has been used for translating between related languages: VHDL to Verilog, Xilinx Netlist Files to VHDL, VHDL to SystemC, etc. Source files with low originality are prime candidates for automatic generation. VHDL engineers regularly write testbenches. We've written an online demonstration to show you how Perl can do it for you. VHDL Testbench Creation Using Perl Hardware engineers using VHDL often need to test RTL code using a testbench. Given an entity declaration writing a testbench skeleton is a standard text manipulation procedure. Each one may take five to ten minutes. Every design unit in a project needs a testbench. Generating testbench skeletons automatically can save hours per project. However, a little Perl programming can reduce that time to seconds in future.

Demonstration
Our web server is set up to run Perl scripts. We've written a Perl script to generate a skeleton testbench given an entity declaration. In fact, if an architecture is supplied then the Perl script will add in a Reset and Clock generator process (if the architecture uses a clock). Configuration declaration(s) are generated too. Perl is also useful for Coding Style conformance checking and enforcement.

Report Filtering
EDA tools are notorious for creating verbose report files. Many megabytes of text may contain little interesting information. Perl is well matched to extracting key information quickly.

Netlist Patching
EDA tools within design flows never fit together perfectly. Often, really useful features are unsupported. Netlists generated from one tool need modifying before the next tool will read them. Perl is well matched to intelligent search and replace jobs.

SDF File Patching Using Perl
Text files sometimes have to be modified in ways not supported by EDA tools. Standard Delay Format (SDF) is one of the many types of text files used by EDA engineers. Manual editing with a text editor is a good one off solution when there aren't too many changes to make. However, when the same modifications or patches must be applied repeatedly then a little help from Perl saves many hours per project. Here we present a Perl script showing some simple but powerful time saving tricks to patch an SDF file. It was inspired by a delegate attending the Doulos Essential Perl course. He had a script to patch an SDF file zeroing delays in specific parts of his gate-level simulation. His script worked but took 30 minutes to process a typical 70MB SDF file. After the course his script was re-written to do the same job in just 5 seconds! In this example we're going to copy the SDF file zeroing IOPATH timing data fields when the INSTANCE name matches one listed in a separate file. Many patching operations are possible but they're all variations on this theme. Here's the script, we'll break it down and explain each chunk further down the page:
#!/usr/local/bin/perl # patch_sdf.pl # Version 1.0 # 8 Jan 2003, SD # www.doulos.com # # Copyright (c) 2003, Doulos Limited # Training and consultancy for hardware engineers. # # Perl script for patching Standard Delay Format files. unless( @ARGV == 3 ) { die <<EOF; This Perl script patches Standard Delay Format (SDF) files. It accepts three filenames as command line arguments: 1. SDF file for reading and patching 2. File containing a list of INSTANCEs for patching 3. SDF file for writing patched file Call the script like this: perl patch_sdf.pl in.sdf celltypes.txt out.sdf

EOF } # Read three command line argument filenames ( $fn_in, $fn_instances, $fn_out ) = @ARGV; open( IN, $fn_in ) or die "Cannot open $fn_in\n"; open( INSTANCES, $fn_instances ) or die "Cannot open $fn_instances\n"; open( OUT, ">$fn_out" ) or die "Cannot create $fn_out\n"; # Undefine $INPUT_RECORD_SEPARATOR to slurp the whole file # Store INSTANCE names in %instances for fast lookups # Every string of non-white space is an INSTANCE name undef $/; foreach( <INSTANCES> =~ /\S+/g ) { $instances{$_} = 1 } # # # # Here's a neat trick to save parsing every line Set $/, the $INPUT_RECORD_SEPARATOR, to '(INSTANCE' Now the record input operator loads one CELL entry each time. The first field is the INSTANCE name

$/ = '(INSTANCE'; print OUT scalar( <IN> ); # First record only - force scalar context while( <IN> ) { # Match the instance name and check if it's in %instances if( /([^\s)]+)/o and $instances{$1} ) { print "Found INSTANCE $1\n"; delete( $instances{$1} ); # So instances not found can be reported # Match this: # IOPATH SET O (6500:6500:6500)(6500:6500:6500) # -----$1-----# End up with this: # IOPATH SET O (0:0:0)(0:0:0) s/ ( IOPATH[^(]+ # Find "IOPATH" and everything before "(" ) # Back reference in $1 \([^)]+\) \s* \([^)]+\) # Match first timing data field eg. (4:5:6) # Possible white space # Match second timing data field

/$1(0:0:0)(0:0:0)/gox; # Substitute globally # # } Could have written this but it's not so easy to read s/(IOPATH[^(]+)\([^)]+\)\s*\([^)]+\)/$1(0:0:0)(0:0:0)/go;

} print OUT;

# Report instances that were not found # These will be the labels remaining in the %instances hash foreach( keys %instances ) { print "Not found: INSTANCE $_\n";

} print "$fn_out generated\n";

How It Works
You may copy/modify and use this script with your own SDF files. Download the demo then unzip it: gzip -d doulos_demo_sdf.pl This file, doulos_demo_sdf_pa.pl, is a self extracting Perl archive. Execute it to create a directory called doulos_demo_sdf/ containing all necessary files to experiment with sdf_patch.pl. The example works on both Unix and Windows. perl doulos_demo_sdf_pa.pl

Checking Command Line Arguments
This script must be called with three arguments: the name of an SDF file to read, the name of a text file containing a list of instance names to patch and the name of a file to write the patched SDF file to. In a list context the @ARGV array returns all command line arguments. But, in a scalar context, provided here by the == operator, an array returns its number of elements. Unless there are exactly three we want our script to die and print an explanation.
unless( @ARGV == 3 ) { die <<EOF; This Perl script patches Standard Delay Format (SDF) files. It accepts three filenames as command line arguments: 1. SDF file for reading and patching 2. File containing a list of INSTANCEs for patching 3. SDF file for writing patched file Call the script like this: perl patch_sdf.pl in.sdf celltypes.txt out.sdf EOF }

Here file syntax like this <<EOF; . . . . EOF tells Perl to interpret the enclosed lines as an interpolated string. That is, a string that would otherwise have been enclosed in double quotes allowing variable interpolation (though none is used here). It's a neat syntax for clearly presenting a chunk of text inside a Perl script. It also self-documents the script.

Open Three Files
There are three files, so we'll open them. Being engineers and knowing this script is a point solution never extending beyond 30 lines of code we'll dispense with unnecessary formalities like declaring and scoping variables. However, meaningful variable names will save time when reusing and extending scripts later. Don't be afraid to type longer names. (If you can't touch type - learn quickly!) Capitalised identifiers are file handles by convention in Perl so the name only has to tell us which file handle it is. IN, INSTANCES and OUT are succinct and self-documenting.
( $fn_in, $fn_instances, $fn_out ) = @ARGV;

open( IN, $fn_in ) or die "Cannot open $fn_in\n"; open( INSTANCES, $fn_instances ) or die "Cannot open $fn_instances\n"; open( OUT, ">$fn_out" ) or die "Cannot create $fn_out\n";

In Perl open or die is a great self-documenting construction. It pinpoints problems very quickly if you add a meaningful comment. Consider adding $! the $OS_ERROR special variable to the error string it will contain the file system error message. To save time, complexity and typing we deliberately chose not close these file handles explicitly. Perl tidies up for us once it runs off the end of the script closing all open file handles. Of course there are occasions when closing file handles explicitly part way through a script is necessary.

Reading The Instance List
The record input operator, <FILE_HANDLE>, reads everything upto and including the first occurrence of the string stored in $/, the special variable $INPUT_RECORD_SEPARATOR. Undefining $/ causes the record input operator to read the whole file in one go because undef doesn't occur before the end of file. $/ always has the initial value "\n". Changing $/ may cause trouble if another part of the Perl script is going to assume it still has its initial value. Assigning "\n" back to $/ afterwards might save frustration in longer scripts.
undef $/; foreach( <INSTANCES> =~ /\S+/g ) { $instances{$_} = 1 }

The instances.txt file contains a list of names separated by white space. Keys for each instance name are added to the %instances hash. True values, 1s, are associated with each key. Perl interprets this last line by first reading a record from INSTANCES, the whole file into one string. Next this string is bound to the matching operator, /..../, using the binding operator =~. The regular expression \S+ greedily matches the first substring of non-white space characters. The whole matching operation is evaluated in a list context provided by foreach and with the global modifier g so returns a list of every instance name. No loop variable is specified so Perl assumes the default $_. Inside the loop $_ is used to add a key/value pair to the %instances hash.

Preparing To Read, Patch And Write Each Record
Speed is very important when patching files that could be tens of MB long. For best performance we need to select a pragmatic approach to parsing the SDF file. We could slurp the whole file into a scalar variable but this could overstretch our computer's resources. We could process the file one line at a time but each record spans several lines and would entail extra work keeping track of where we are. Ideally we need to read one complete record at a time. There's a really neat trick to do this with Perl. Setting $/ to the literal string '(INSTANCE' makes this patching process very efficient. Each use of the record input operator reads in exactly one record. It does split the records part way down so each string read begins with the instance name of one record and continues to the space in front of the instance name of the following record. But who cares? Excepting the very first read, each read contains the right amount of information to process. ie. An instance name and the IOPATH lines that may need patching.

$/ = '(INSTANCE'; print OUT scalar( <IN> ); # First record only - force scalar context

The first read gets everything up to the first instance name. We'll print this straight out. The record input operator would read every record in one go inside print's list context so the scalar command is used to force only one read. Subsequent record input operations read a string ending in '(INSTANCE' like this one:
Data_0_INBLOCK_IBUF) (DELAY (ABSOLUTE (IOPATH I O (3000:3000:3000)(3000:3000:3000)) ) ) ) (CELL (CELLTYPE "X_BUF") (INSTANCE

For this record the instance name is Data_0_INBLOCK_IBUF which is one of those listed in the demonstration so all six 3000s need changing to 0s.

Record Patching
Here's the SDF record patching bit:
while( <IN> ) { # Match the instance name and check if it's in %instances if( /([^\s)]+)/o and $instances{$1} ) { print "Found INSTANCE $1\n"; delete( $instances{$1} ); # So instances not found can be reported # Match this: # IOPATH SET O (6500:6500:6500)(6500:6500:6500) # -----$1-----# End up with this: # IOPATH SET O (0:0:0)(0:0:0) s/ ( IOPATH[^(]+ # Find "IOPATH" and everything before "(" ) # Back reference in $1 \([^)]+\) \s* \([^)]+\) # Match first timing data field eg. (4:5:6) # Possible white space # Match second timing data field

/$1(0:0:0)(0:0:0)/gox; # Substitute globally # # } Could have written this but it's not so easy to read s/(IOPATH[^(]+)\([^)]+\)\s*\([^)]+\)/$1(0:0:0)(0:0:0)/go; } print OUT;

Comments may or may not help. There isn't much here without comments, it's just harder to see what's happening:
while( <IN> ) { if( /([^\s)]+)/o and $instances{$1} ) { print "Found INSTANCE $1\n";

delete( $instances{$1} ); s/(IOPATH[^(]+)\([^)]+\)\s*\([^)]+\)/$1(0:0:0)(0:0:0)/go; } print OUT; }

Some special Perl magic happens when the record input operator and nothing else appears as the condition to a while loop. Perl automagically inserts $_ like this: while( $_ = <IN> ) {. Before printing every record read into $_ a substitution is done conditionally. Statement print OUT; defaults to printing $_ to file handle OUT in the absence of an argument. The if statement back references the first identifier into $1. The regular expression [^s)]+ matches the leftmost substring of characters not including white space or literal ) characters. That is, the instance name which may be followed by a space or ). The instance name, now back referenced in $1, is looked up in the %instances hash to see if it's one of the records to modify. If it is then print a comment to say we've found an instance name from the list and delete its key/value pair from the hash. This is necessary to produce a report of instance names not found. Finally use a substitution operation to find each PATHIO entry and zero its timing data fields. Modifier g finds and substitutes every PATHIO entry. Without it only the first entry would be substituted. Modifier o means compile once. This is a regular expression engine optimisation that tells Perl not to recompile the regular expression every time through the loop. Sometimes recompiling is necessary because interpolated variables in the regular expression change each time through the loop. Modifier x directs Perl's regular expression engine to ignore white space and end of line comments following # symbols. It's a neat way to comment regular expressions so others can read your code and, perhaps more importantly, you can understand your code when you come back to it later. Adding comments with a sample showing which bit is matched and which bit back referenced may help. Negated character classes can be the fastest way to match strings using Perl's regular expression engine. Consider [^(]+, which matches a string of characters upto but not including the next (. In this case everything between PATHIO and the first (. For each character Perl only has to check it's not a (. That's much faster than say [\w\s]+ where Perl would have to compare each character against more than 60 possibilities. For many scripts these optimisations don't matter. We're paying particular attention to them here because the same operation may be done thousands or even millions of times per run.

Missed Instances
foreach( keys %instances ) { print "Not found: INSTANCE $_\n"; } print "$fn_out generated\n";

Finally, if there are any entries not deleted from the %instances hash then we need to report their absence from this SDF file. Then remind the user of the name of the new SDF file just generated.

Generating Test Vectors

Perl can apply rules for generating sophisticated test vector patterns: ATM packets, MPEG streams, QAM samples with added noise, etc.

Controlling Tools
Perl is able to call any tool installed on its host computer. Perl can even run Telnet and FTP sessions to control remote computers. Any task involving more than one tool can be automated using a Perl script. A Perl script might run a tool, filter the textual output then decide whether to: modify constraints and try again, move on to the next tool in the flow or abort and send email notification to a project leader.

Where To Get Perl
Many operating systems support Perl. Most importantly ports exist for, Linux, Windows and versions of Unix. The standard Perl release is available in source code only. However, hardware engineers don't have time to compile their own and binary distributions are freely available

Regular Expressions
Almost every Perl script uses regular expressions. We've written a Perl Regular Expression Viewer tool to help you write them faster. Download your free copy now.