Perl For Programming & System Administration  - 讨论区

标题:Top 10 Tips for efficient Perl Scripting for Chip Designers

2011年03月30日 星期三 21:46


By Umang Mistry, Sibridge Technologies

Bringing automation to ASIC design typically includes the use of "Scripts". In the new competitive generation of chip designing where Time-to-Market is so critical, you need a way to finish your automation tasks in smarter ways. The Perl scripting language is the designer's best friend in meeting milestone targets. However, at times, scripts are so bulky, messy or complicated, that they will require more time in debugging and fixing the scripting issues rather than using to increase efficiency.

There are several pitfalls which may lead to inefficiency in Perl scripts. One major factor is that many chip design engineers are not good software programmers. Most of them work with HDLs, HVLs or C/C++ which gives them limited exposure to scripts. So, they tend to write scripts like they write "C" programs or any other RTL. But for ASIC engineers, knowledge of basic concepts of Perl helps to significantly increase the efficiency of Scripts. This inspired me to share the following "Top 10 tips" to write efficient Perl scripts in the world of ASIC. 

1. Use "strict" and "warnings" to identify common problems at an early stage

Perl scripting is very flexible when it comes to syntax checking and that is the reason why it is like a double edged sword. Flexibility in syntax checking allows the designer to write hassle free code but on the other hand it is more error prone due to human limitations. 

To avoid this, use of "use strict;" and "use warnings;" semantics or "-w" option in the script is advantageous. This helps to identify common mistakes, like same name variables declaration, undefined or un-initialized variables etc., which may cause unpredictable behavior of the script.

#! /usr/bin/perl –w
use strict;
use warnings;

2. Use Subroutines for reusability and generalized code

Many new designers write code in a similar way as they think, which results in bulky repetitive code in the script.
Let's see the following example, where we want to print the data in hexadecimal format:

If ($data == 1)
printf ("data received = %x",$rcv1_data);
elsif ($data == 2)
printf ("data received = %x",$rcv2_data);
printf ("ERROR : Illegal input %x",$data);

Now, if the above code is replicated 10-15 times to check the different "$data" value and to print output, then it will add about 60-90 additional lines. This is enough to make a script bulky. A simple solution is to move the code to a subroutine and to use the subroutine whenever required. Check the following code for code optimization with subroutine:

#Subroutine to check data value
sub data_chk(){
  my  $data = shift; 
  my $rcv1_data = shift;
  my $rcv2_data = shift;
  If ($data == 1)
  printf ("data received = %x",$rcv1_data);
  elsif ($data == 2)
  printf ("data received = %x",$rcv2_data);
 printf ("ERROR : Illegal input %d",$data);

# Subroutine usage
&data_chk ($data, $rec1_data, $rec2_data);
&data_chk ($data_new, $rec1_data, $rec2_data);

As we can see in the above example, subroutines provide the ability to define generalized and reusable code. This will not only reduce the number of lines but also increase the efficiency of the designer during debug and upgrade process. 

3. Use Hashes for optimization

Most ASIC designers are comfortable working with arrays. So, they prefer to use an array in place of a "Hash" for certain Perl functionalities such as looking for required patterns or contents from the given set of data. Implementation of such tasks with array requires some sort of code which traverses through the entire array and gets the required data. This definitely takes more processing time and reduces the efficiency of the code by unnecessarily accessing all the indices.  
The following example shows one commonly used method to access data through arrays: 

@array = ("module", "test_name", "test_number");

@content_array = ("Multiplexer", "sanity_check", "test_1");
# Access the name
for ($i = 0; $i <= $#array; $i ++) {
   if ($array[$i] eq "module") {
       $module = $content_array [$i];

As we can see from the code, we need at least two arrays and a bunch of code to find value of the "module". This could be easily replaced by a "Hash" as follows:

%hash = ("module" => "Multiplexer", "test_name" => "sanity_check", "test_number" => "test_1");
$module = $hash {"module"};

Thus, Hash helps the designer reduce code in a significant way. Hash also helps to add more generalized code which is really helpful in tasks like creating a number of different combinations. Check the following example:

%hash = ("operator" => ["+", "-"], "r_value" => "a", "l_value" => "b");
$i = 0;
foreach $key (keys %hash) {
   print "c = " . $hash{"r_value"} . " " . $hash{"operator"}->[$i] . " " . $hash{"l_value"};

After execution, this code will produce both the combinations of the equation as an output:

      c = a + b;
      c = a – b; 

4. Use OOP for modularity in the code

OOP (Object Oriented Programming) methodology is of great help when it comes to reusability. Perl provides Object Oriented support by means of "package". Designers can define their own package and a set of code which they can use whenever needed. But do remember to create a constructor ("new" subroutine in Perl) for each package. Check the following example how it can be used to improve  code efficiency:

package gen_random;
sub new {
    my $seed = shift;
    if (undef $seed) {
      $seed = time();
sub random_data {
   my $max_val = shift;
   my $data = $rand($max_val);
   return $data;
sub dist_random_data {
sub weighted_random_data {

      Above code can be used as follows :
             my $rand_obj1 = gen_random->new("1");
             my $rand_obj2 = gen_random->new("2");
             my $data = $rand_obj1->random_data(10);
             my $data1 = $rand_obj1->dist_random_data(20);
             my $data2 = $rand_obj2->weighted_random_data(15);

5. Use a common place to define all the hardcoded values for easy accessibility

Most of the time in Chip industry, scripts are used for regression. This kind of script always requires different paths, constant values etc. to be defined. Most of the designer defines these quantities at the place where they are used. This practice is fine as far as script is small, but when dealing with larger scripts, this code will take lot of debugging time. 
So, the solution is to use the "use constant" directive at the start of the script. This will act as a constant parameter and allow easy modification and debugging. Also, usage of system environment variables wherever required allows the script to run on different machines with different users. Check the following code:

       use constant SIM_PATH = "/project/my_area/$ENV{USER}/sim";
       use constant MAX_ITR = 10;
       for ($i = 0 ; $i <= MAX_ITR; $i++) {
           $cmd = "cd " . SIM_PATH . "; make test";
           system ("$cmd");

This code allows the designer to easily modify the constant arguments with ease of accessibility for each user.

6. Use "shift" instead of "@_" for subroutine arguments to avoid unexpected results

Typically, the designer uses "@_"to get argument values in subroutine, where the "@" refers to an array comprising of all the arguments passed. Review the following example:
       sub my_sub {
           print "First argument = $_[0]";
           print "Second argument = $_[1]";
           print "Third argument = $_[2]";
      &my_sub ("first", "second", "third");

For this sample code, the designer has to remember the index of the argument array for all the arguments used. This can cause error prone code, when it comes to multiple arguments. To reduce this risk, the designer can use "shift", which will make sure that the subroutine has all the required arguments and the designer can access it with meaningful names. Examine the following code for argument passing with "shift":

      sub my_sub {
           my $first = shift;
           my $second = shift;
           my $third = shift; 
           print "First argument = $first";
           print "Second argument = $second";;
           print "Third argument = $third";
       &my_sub ("first", "second", "third");

7. Use Regular Expressions instead of "Split" to increase performance

Using "Split" is a common practice when it comes to getting a specified pattern from a line or bigger pattern. Following is the typical usage of "Split" function: 

    $line = "I am a designer";
    @array = split (" ", $line); # split line with "space"
    $last_word = $array[3]; # will store word "designer" in $last_word

As shown in above code, "Split" function uses arrays to store the patterns generated after breaking the whole sentence or word or any big pattern. As it uses arrays, it definitely grabs memory to store the elements. If you have 25-30 such arrays with big patterns, it will certainly degrade your PC's performance. To solve this, the best solution is to use "Regular Expressions". Regular Expressions are expressions which let designers search for required patterns. The following code shows how you can use Regular expression instead of Split.

    $line = "I am a designer";
    $last_word = $1 if ($line =~ /.*(designer)/);# $1 stores the pattern "designer"


    $last_word =$line; # copies "I am a designer" to $last_word

    $last_word =~ s/I am a //; # This will remove "I am a " string from $last_word.

8. Use Comments to accelerate debugging process

Meaningful comments are the heart of any code. It will give the designer or debugger some familiarity about the code and more importantly its intent. Quite often, scripts developed by one designer, are used (or reused) by another. In this scenario, if any error or unexpected behavior occurs and if comments are not proper or missing, it will be very difficult for the debugger to trace the cause of failure. So, meaningful comments definitely save time and increase efficiency of the script debugging process.

9. Use proper Error messaging to identify system level issues

Proper error messaging is a great way to improve efficiency of debugging. If you know the correct message generated by script, then your half task is done. Following are a few examples of useful error messages:

  • $FPTR->open(">file.txt") || die ("$0 : ERROR1 : Cannot open file <file.txt> for writing. System Error : $!"); # Issues error with System error message
  • if (! (-e PATH/$file)) {
    print "$0 : ERROR2 : <$file> doesn't exists at " . PATH ; 
    exit 1; 
    } # Issues error and exit the script with return code = 1
  • if (system ("cp $file1 $file2")) { 
    die ("$0 : ERROR3 : Cannot copy <$file1> to <$file2>, System Error : $!");

10. Use "ready to use" modules to avoid "reinventing the wheel"

This is the most important point to note. While developing scripts, one can come across so many situations where one needs to develop new code/package for the script. For Chip industry, examples such as Random data generation, Case statements, File management, generation of report etc., are very common. For all these functionalities, many designers develop their own packages or subroutines which consumes needless time. The solution to this is CPAN: CPAN is the Perl archive network where you can get a solution to most of your problems. Designers can download almost all the required packages/modules which can save valuable time.
By using some or all of the above techniques, chip designers can markedly improve the efficiency of their scripts. As I mentioned earlier, in this competitive world where Time-to-Market is very critical, a small change in the traditional way of script-writing can make a huge difference in performance and efficiency.
Umang Mistry
Member, Technical Staff
Sibridge Technologies, Ahmedabad



    请 登录 后回复。还没有在Zeuux哲思注册吗?现在 注册 !

    Zeuux © 2024