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 ;

$b = 0x11; # Hexadecimal (17 in decimal)


$c = 021; # Octal (17 in decimal)
$d = 0b10001; # Binary (17 in decimal)
$f = 3.142; # Floating point

$a = $a + 1; # Add 1 to variable $a
$a += 1; # Add 1 to variable $a
$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: '; # No interpolation with 'single quotes'
$b = "$a$c\n"; # Interpolation (variable substitution) with "double
quotes"
# \n is the newline character
print $b; # This makes "Number of DFFs: 17\n" appear on the
standard output
print $a, $c, "\n"; # As does this line because print takes
# a comma separated list of arguments to print
print "That's all\n"; # 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
print 'one' lt 'two'; # Prints 1
# ASCII-betically 'o' is less than 't'
print 'buf4' lt 'buf3'; # Prints nothing (that is undef, numerically zero)
# Perl's undefined value is undef
# ASCII-betically '4' is not less than '3'

Logic and Truth


Perl considers these to be false:
0; # Integer zero
0.0; # Decimal zero
'0'; # String containing a single zero character
''; # Empty string
undef; # 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
# || Logical OR
# and Logical AND
# && Logical AND
# not Logical NOT
# ! Logical NOT
# | Bitwise OR
# & Bitwise AND
# ~ Bitwise NOT

print 6 & 5; # prints 4, 0b0110 & 0b0101 = 0b0100


print 6 | 5; # prints 7, 0b0110 | 0b0101 = 0b0111
print ! 0; # prints 1
print ! 5; # prints nothing (that is undef or false)
print ~5; # 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]; # Prints element 0, that is, 'X_LUT4'


print $components[5]; # 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' );
# ^ > means open in write mode
print FILE1 "The first line to file1.txt\n";
print FILE1 "The final line to file1.txt\n";
close( FILE1 ); # Don't have to explicitly
close a file

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' ); # Open in read mode - the default mode
$first_line = <FILE2>; # Reads the first line from file2.txt into
$first_line.
# Includes the newline character, \n.
while( $line = <FILE2> ) {
print $line; # Read and print remaining lines from file2.txt.
} # When every line has been read <FILE2> returns
undef.

$standard_input = <STDIN>; # Read a line from the standard input.


# Can be the keyboard if run from the command
line.

chomp( $standard_input ); # 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 White space character, space, tab or newline
# \S 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+)/ ) {
# ^^^ Instance label
# ^^^ Zero or more white space characters
# ^ :
# ^^^ Zero or more white space characters
# ^^^^^ Group containing a word beginning
with X_
# (copied
into $1)
print "Found instantiation of $1\n";
}
}

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

\([^)]+\) # Match first timing data field eg. (4:5:6)


\s* # 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

\([^)]+\) # Match first timing data field eg. (4:5:6)


\s* # 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.

You might also like