## GetInfo.pm -- Database generator for Verilog design
## $Revision: #2 $$Date: 2005/10/10 $$Author: Rohit Mishra $
## Written by: Rohit Mishra <rohit@rohitmishra.com>
############################################################
package Verilog::GetInfo;

require 5.6.1;
use vars qw( $VERSION );
use strict;
use Carp;
use FileHandle;

$VERSION = '1.0.1';
##################################################################


##################################################################
# Package subroutines
##################################################################

##################################################################
sub new {
##################################################################
	@_ >= 1 or croak 'usage: $verilog_handle = Verilog::GetInfo->new(\@command_line)';

	my $class = ref($_[0]) || $_[0];
        my $junk = shift(@_);
        my $command_line = shift(@_);
	my $self = {};
        my $command = undef;
        my $fh = undef; 
	
	# Database Hashes
	$self->{'DATABASE'} = { };
	$self->{'HIERARCHY'} = { };
	$self->{'Handle'} = undef;
	@{$self->{'NULL'}} = ('None');

	# Global Variables
	$self->{'GLOBAL'} = { };
	$self->{'GLOBAL'}->{'TRUE'} = 1;
	$self->{'GLOBAL'}->{'FALSE'} = 0;
	$self->{'GLOBAL'}->{'flag'} = 0;
	$self->{'GLOBAL'}->{'flag1'} = 0;
	$self->{'GLOBAL'}->{'level'} = 0;
	$self->{'GLOBAL'}->{'connec_flag'} = 0;
	@{$self->{'GLOBAL'}->{'indent'}} = ("\t");

 	# bless it as a Parser object
 	bless $self, $class;
        $command = $self->Parse_Command_Line( $command_line );
	if( ! defined $self->{'DATABASE'}->{'LOGFILE'} ){
               $self->{'DATABASE'}->{'LOGFILE'} = "verilog-shell_$VERSION\.log"; 
	}
        open( LOG, ">$self->{'DATABASE'}->{'LOGFILE'}" ) || die("$!");
        $self->{Handle} = *LOG;
        select($self->{Handle});
        $| = 1;
        select(STDOUT);
        $| = 1;
	$self->Message($self->{Handle},"=" x 72);
	$self->Message($self->{Handle},"Command line: $command");
	$self->Message($self->{Handle},"=" x 72);
	$self->Read_Verilog_Files;
	return $self;
}

##################################################################
sub Parse_Command_Line(){
##################################################################
	@_ == 2
        or croak 'usage: $files = $verilog_handle->Parse_Command_Line( \@ARGV )';
        my $self = shift(@_);
	my $command_line = shift(@_);
	my $command = "$0\n";
	my $arguments;
	my $macro;
	my $value;
	my $line;
	my $directory;
	my $fh = FileHandle->new();


	while( $arguments = shift( @$command_line ) ){
		chomp( $arguments );
		$arguments =~ s/\s+//g;
		if( $arguments =~ m|\+define\+([A-Za-z0-9_]+)="(.*?)"| ){
			$macro = $1;
			$value = $2;
			$self->{'DATABASE'}->{'DEFINES'}->{$macro} = $value;
			$command .= " $arguments\n";
		} elsif( $arguments =~ m|\+define\+([A-Za-z0-9_]+)| ){
			$macro = $1;
			$self->{'DATABASE'}->{'DEFINES'}->{$macro} = "junk";
			$command .= " $arguments\n";
		} elsif( $arguments =~ m|\+incdir\+(.*)| ){
			$directory = $1;
			push( @{$self->{'DATABASE'}->{'INCDIR'}}, $directory );
			$command .= " $arguments\n";
		} elsif( $arguments =~ m|\+([A-Za-z0-9_]+)| ){
			$macro = $1;
			$self->{'DATABASE'}->{'PLUSARGS'}->{$macro} = "junk";
			$command .= " $arguments\n";
		} elsif( $arguments =~ m|\-f| ){
			$arguments = shift( @$command_line );
			push( @{$self->{'DATABASE'}->{'FILES'}}, $arguments );
			$command .= " -f $arguments\n";
		} elsif( $arguments =~ m|\-v| ){
			$arguments = shift( @$command_line );
			push( @{$self->{'DATABASE'}->{'LIBFILES'}}, $arguments );
			$command .= " -v $arguments\n";
		} elsif( $arguments =~ m|\-y| ){
			$arguments = shift( @$command_line );
                        push( @{$self->{'DATABASE'}->{'LIBDIRS'}}, $arguments );	
			$command .= " -y $arguments\n";
		} elsif( $arguments =~ m|\-l| ){
			$arguments = shift( @$command_line );
			$self->{'DATABASE'}->{'LOGFILE'} = $arguments;
			$command .= " -l $arguments\n";
		} else {
			push( @{$self->{'DATABASE'}->{'FILES'}}, $arguments );
			$command .= " $arguments\n";
			next;
		}
	}
        return $command;
}

##################################################################
sub Read_Verilog_Files(){
##################################################################
	@_ == 1 
	or croak 'usage: $verilog_handle->Read_Verilog_Files';
	my $self = shift(@_);
	my $file;
	my $top;
	
	my @files = @{$self->{'DATABASE'}->{'FILES'}};

	foreach $file ( @files ){
		my @data = $self->Verilog_To_Text( $file );
		$self->Read_Text( $file, @data );
	}
	
	$self->Generate_Module_Database;
	$top = $self->Get_Toplevel;
	foreach my $toplevel ( @$top ){
		$self->{'HIERARCHY'}->{$toplevel} = $self->Hierarchy( $toplevel );
	}
	$self->{'GLOBAL'}->{'flag'} = 0;
}
##################################################################
sub Verilog_To_Text(){
##################################################################
	@_ == 2 
	or croak 'usage: $verilog_handle->Verilog_To_Text( $filename )';
	my $self = shift( @_ );
	my $file = shift( @_ );
	my $line;
	my @data = ();

	my $fh = FileHandle->new();

	

	if( $fh->open( $file ) ){
		while( $line = $fh->getline() ){
			chomp($line);
			if( ( $line =~ m|^\s*//| ) || ( $line =~ m|^\s*$| ) ){
				next;
			} elsif( $line =~ m|(include\s+"\s*[A-Za-z0-9_/\.]+\s*")| ){
				push( @data, "`$1" );
			} elsif( $line =~ m|(.*?)//| ){ 
				push( @data, "$1" );
			} elsif( ( $line =~ m|^\s*/\*| ) && ( $line =~ m|\*/\s*$| ) ){
				next;
			} elsif( ( $line =~ m|^\s*/\*| ) && ( $line !~ m|\*/\s*$| ) ){
				while( $line = $fh->getline() ){
					if( $line =~ m|\*/\s*$| ){
						last;
					}
				}
			} elsif( $line =~ m|(.*?)/\*(.*?)\*/(.*?)| ){
				push( @data, "$1"."$3" );
			} else {
				push( @data, $line );
			}
		}
	} else {
		$self->Error( "Open failed: $file" );
                exit("1");
	}
	return( @data );
}


##################################################################
sub Read_Text(){
##################################################################
	@_ >= 2
        or croak 'usage: $verilog_handle->Read_Text( $file,@data )';
        my $self = shift( @_ );
        my $file = shift( @_ );
        my @data = @_;
        my $line;
	my @data1 = ();
	my $level = 0;
	my $tag = 0;
	my $tag1 = 0;
	my %ifdef = ();
	my %else = ();
	my $i;
	my $line_number = 0;
	my $macro;
	my $value;
	my $module = undef;
	my @expand = ();

	if( $self->{'GLOBAL'}->{'level'} == 0 ){
		$self->Echo($self->{Handle}, "Reading file: $file" );
	} else {
		$self->Echo($self->{Handle}, "Reading level $self->{'GLOBAL'}->{'level'} include file: $file" );
	}

	while( $line = shift( @data )){
		$line_number++;

		# define ifdefs, elses and endif tags

		if( $line =~ m|\s*ifdef\s+(\S+)| ){
			$level++;
			if( defined $self->{'DATABASE'}->{'DEFINES'}->{$1} ){
				$ifdef{$level} = $self->{'GLOBAL'}->{'TRUE'};
				$else{$level} = $self->{'GLOBAL'}->{'FALSE'};
			} else {
				$ifdef{$level} = $self->{'GLOBAL'}->{'FALSE'};
				$else{$level} = $self->{'GLOBAL'}->{'FALSE'};
			}
			next;
		}
		if( $line =~ m|\s*else| ){
			$else{$level} = $self->{'GLOBAL'}->{'TRUE'};
			next;
		}
		if( $line =~ m|\s*endif| ){
			undef $ifdef{$level}; 	
			if( $else{$level} == $self->{'GLOBAL'}->{'TRUE'} ){
				undef $else{$level};
			}
			$level--;
			next;
		}


		# ifdef conditions for which it should not move fwd

		if( defined $ifdef{$level} && defined $else{$level} ){
			if( $ifdef{$level} == $self->{'GLOBAL'}->{'FALSE'} && $else{$level} == $self->{'GLOBAL'}->{'FALSE'} ){
				next;
			} elsif( $ifdef{$level} == $self->{'GLOBAL'}->{'TRUE'} && $else{$level} == $self->{'GLOBAL'}->{'TRUE'} ){
				next;
			} elsif( $ifdef{$level} == $self->{'GLOBAL'}->{'FALSE'} && $else{$level} == $self->{'GLOBAL'}->{'TRUE'} ){

				# check for previous ifdefs
				foreach $i ( keys %ifdef ){
					if( defined $ifdef{$i} && defined $else{$i} ){
						if( $i < $level && $ifdef{$i} == $self->{'GLOBAL'}->{'FALSE'} && $else{$i} == $self->{'GLOBAL'}->{'FALSE'} ){
							$tag = 1;
							last;
						} elsif( $i < $level && $ifdef{$i} == $self->{'GLOBAL'}->{'TRUE'}  && $else{$i} == $self->{'GLOBAL'}->{'TRUE'} ){
							$tag = 1;
							last;
						}
					}
				}
				if( $tag == 1 ){
					$tag = 0;
					next;
				}
			} elsif( $ifdef{$level} == $self->{'GLOBAL'}->{'TRUE'} && $else{$level} == $self->{'GLOBAL'}->{'FALSE'} ){

                                # check for previous ifdefs
                                foreach $i ( keys %ifdef ){
                                        if( defined $ifdef{$i} && defined $else{$i} ){
                                                if( $i < $level && $ifdef{$i} == $self->{'GLOBAL'}->{'FALSE'} && $else{$i} == $self->{'GLOBAL'}->{'FALSE'} ){
                                                        $tag = 1;
                                                        last;
                                                } elsif( $i < $level && $ifdef{$i} == $self->{'GLOBAL'}->{'TRUE'}  && $else{$i} == $self->{'GLOBAL'}->{'TRUE'} ){
                                                        $tag = 1;
                                                        last;
                                                }
                                        }
                                }
                                if( $tag == 1 ){
                                        $tag = 0;
                                        next;
                                }
			}
		}
		
		# plusargs conditions for which it should not move fwd.

		if( ($line =~ m|\$test\$plusargs\s*\(\s*\"\s*([A-Za-z0-9_]+)\s*"\s*\)|g) ){ # TEMP rohit&& ($line =~ m|\bbegin\b|g) ){
			$macro = $1;
			if( !defined $self->{'DATABASE'}->{'PLUSARGS'}->{$macro} ){
				$self->Warning( $self->{Handle},"Plusarg ($macro) not defined. Skipping.. ");
			        while( $line = shift( @data ) ){
                                        if( $line =~ m|\bend\b| ){
                                                last;
                                        } else {
                                                next;
                                        }
                                }
			} else {
				$self->Warning( $self->{Handle},"Plusarg ($macro) defined.");
			}
		} elsif( $line =~ m|\$test\$plusargs\s*\(\s*\"\s*([A-Za-z0-9_]+)\s*"\s*\)| ){
			$macro = $1;
			if( !defined $self->{'DATABASE'}->{'PLUSARGS'}->{$macro} ){
				$self->Warning( $self->{Handle},"Plusarg ($macro) not defined. Skipping.. ");
				$line = shift( @data );
				if( $line =~ m|\bbegin\b| ){
                                	while( $line = shift( @data ) ){
                                       		 if( $line =~ m|\bend\b| ){
                                       		         last;
                                       		 } else {
                                       		         next;
                                       		 }
                                	}
				} else {
					unshift( @data, $line );
					next;
				}
                        } else {
				$self->Warning( $self->{Handle},"Plusarg ($macro) defined.");
			}
		}


		if( $line =~ m|\s*define\s+([a-zA-Z0-9_]+)\s+"([\s\S]+)"| ){
			$macro = $1;
			$value = $2;
			@expand = ( $value =~ m|([A-Za-z0-9_]+)|g ); 
			while( @expand ){
				foreach ( @expand ){
					if( defined $self->{'DATABASE'}->{'DEFINES'}->{$_} ){
						$value =~ s/\`$_/$self->{'DATABASE'}->{'DEFINES'}->{$_}/g;
					} else {
						$value =~ s/\`$_//g;
					}
				}
				@expand = ( $value =~ m|([A-Za-z0-9_]+)|g );
			}
			$value =~ s/[\s\{\}\"]+//g;
			$value = join( "", split( /\,/, $value ) );

			if( defined $self->{'DATABASE'}->{'DEFINES'}->{$macro} ){
				$self->Warning( $self->{Handle},"Text macro ($macro) re-defined. Replaced with new definition.\n$file: $line_number");
			}
			$self->{'DATABASE'}->{'DEFINES'}->{$macro} = $value;
		} elsif( $line =~ m|\s*`define\s+([a-zA-Z0-9_]+)\s+(\S+)| ){
			$macro = $1;
			$value = $2;
			@expand = ( $value =~ m|\`([A-Za-z0-9_]+)|g );
                        while( @expand ){
                                foreach ( @expand ){
                                        if( defined $self->{'DATABASE'}->{'DEFINES'}->{$_} ){
                                                $value =~ s/\`$_/$self->{'DATABASE'}->{'DEFINES'}->{$_}/g;
                                        } else {
						$value =~ s/\`$_//g;
					}
                                }
                                @expand = ( $value =~ m|\`([A-Za-z0-9_]+)|g );
                        }
                        $value =~ s/[\s\{\}\"]+//g;
                        $value = join( "", split( /\,/, $value ) );

			if( defined $self->{'DATABASE'}->{'DEFINES'}->{$macro} ){
				$self->Warning( $self->{Handle},"Text macro ($macro) re-defined. Replaced with new definition.\n$file: $line_number");
			}
			$self->{'DATABASE'}->{'DEFINES'}->{$macro} = $value;
		} elsif( $line =~ m|\s*`define\s+([a-zA-Z0-9_]+)| ){
			$macro = $1;
			if( defined $self->{'DATABASE'}->{'DEFINES'}->{$macro} ){
				$self->Warning( $self->{Handle},"Text macro ($macro) re-defined. Replaced with new definition.\n$file: $line_number");
			}
			$self->{'DATABASE'}->{'DEFINES'}->{$macro} = "junk";
		} elsif( $line =~ m|\s*`include(.*)|g ){
			$value = $1;
			@expand = ( $value =~ m|`([A-Za-z0-9_]+)|g );
			while( @expand ){
				foreach ( @expand ){
					if( defined $self->{'DATABASE'}->{'DEFINES'}->{$_} ){
                                       	        $value =~ s/\`$_/$self->{'DATABASE'}->{'DEFINES'}->{$_}/g;
                                       	} else {
                                       		$value =~ s/\`$_//g;
                                       	}		
				}
				@expand = ( $value =~ m|`([A-Za-z0-9_]+)|g );
			}
			$value =~ s/[\s\{\}\"]+//g;
                        $value = join( "", split( /\,/, $value ) );
			push( @data1, "`include \"$value\"" );
                } elsif( $line =~ m|`[A-Za-z0-9_]+|g ){
                        # Temporarily removed ` include expansions
                        #@expand = ( $line =~ m|`([A-Za-z0-9_]+)|g );
                        #while( @expand ){               
                        #        foreach ( @expand ){   
                        #                if( defined $self->{'DATABASE'}->{'DEFINES'}->{$_} ){  
                        #                        $line =~ s/\`$_/$self->{'DATABASE'}->{'DEFINES'}->{$_}/g;
                        #                } else {
                        #                        $line =~ s/\`$_//g;
                        #                }              
                        #        }
                        #        @expand = ( $line =~ m|([A-Za-z0-9_]+)|g );
                        #}
                        #$line =~ s/[\s\{\}\"]+//g;
                        #$line = join( "", split( /\,/, $line ) );
                        #push( @data1, "\"$line\"" );
		} else {
			push( @data1, $line );
			next;
		}
	}
	$line_number = 0;

	while( $line = shift( @data1 )){
		if( $line =~ m|\s*include\s+"(\S+)"| ){
			my $incl_file = $1;
			$self->{'GLOBAL'}->{'level'}++;
			if( ! -f $incl_file ){ 
				foreach my $tmpvar ( @{$self->{'DATABASE'}->{'INCDIR'}} ){
					if( -f "$tmpvar/$incl_file" ){
						$incl_file = "$tmpvar/$incl_file";
						last;
					}
				}	
			}
       			push( @{$self->{'DATABASE'}->{'FILES'}},$incl_file);
			my @data1 = $self->Verilog_To_Text( $incl_file );
			$self->Read_Text( $incl_file, @data1 );
			$self->{'GLOBAL'}->{'level'}--;
		} elsif( $line =~ m|^\s*\-y\s+(\S+)| ){
			my $libdir = $1;
                        push( @{$self->{'DATABASE'}->{'LIBDIRS'}}, $libdir );	
			$self->Cache_Libdir( $libdir );
		} elsif( $line =~ m|^\s*\-v\s+(\S+)| ){
			my $libfile = $1;
                        push( @{$self->{'DATABASE'}->{'LIBFILES'}}, $libfile );	
			$self->Cache_Libfile( $libfile );
		} elsif( $line =~ m|\+define\+(\S+)| ){
			$macro = $1;
			$self->{'DATABASE'}->{'DEFINES'}->{$macro} = "junk";
		} elsif( $line =~ m|\+libext\+(\S+)| ){
			my $libext = $1;
			@{$self->{'DATABASE'}->{'LIBEXT'}} = split(/\+/, $libext );
		} elsif( $line =~ m|^\s*\+([A-Za-z0-9_]+)| ){
			$macro = $1;
			$self->{'DATABASE'}->{'PLUSARGS'}->{$macro} = "junk";
		} elsif( -f $line ){
       			push( @{$self->{'DATABASE'}->{'FILES'}},$line);
			my @data1 = $self->Verilog_To_Text( $line );
			$self->Read_Text( $line, @data1 );
		} else {
			push( @{$self->{'DATABASE'}->{'FULLVIEW'}}, $line );
			$self->{'DATABASE'}->{'FULL'}->{'CONTENTS'} .= "$line\n"; 
			next;
		}
	}
	return;
}

##################################################################
sub Cache_Libdir(){
##################################################################
	@_ == 2
        or croak 'usage: $verilog_handle->Cache_Libdir( $libdir )';
	my $self = shift(@_);
	my $libdir = shift(@_);
	my $line;
	my $libext;
	my $file;
	my @data = ();
	
	$self->Message($self->{Handle},"Scanning library directory: $libdir\n");
	opendir( DIR, $libdir ) || $self->Warning( $self->{Handle},"Could not open library directory:\n$libdir\n");
	while( $file = readdir( DIR ) ){
		foreach $libext ( @{$self->{'DATABASE'}->{'LIBEXT'}} ){
		        $libext =~ s/\s+//g;
			if( $file =~ m|$libext$| ){
				@data = $self->Verilog_To_Text( "$libdir/$file" );
				while( $line = shift( @data )){
					$self->{'DATABASE'}->{'LIBRARY'} .= "$line\n";
				}
			} else {
				next;
			}
		} 
	}
	closedir( DIR );
}

##################################################################
sub Cache_Libfile(){
##################################################################
        @_ == 2
        or croak 'usage: $verilog_handle->Cache_Libfile( $libfile )';
        my $self = shift(@_);
        my $libfile = shift(@_);
        my $line;
        my @data = $self->Verilog_To_Text( $libfile );
	$self->Message($self->{Handle},"Scanning library file: $libfile\n");
	while( $line = shift( @data )){
		$self->{'DATABASE'}->{'LIBRARY_FILE'} .= "$line\n";
	}	
}



##################################################################
sub Generate_Module_Database(){
##################################################################
	@_ == 1
        or croak 'usage: $verilog_handle->Generate_Module_Database';
        my $self = shift(@_);
        my @database = @{$self->{'DATABASE'}->{'FULLVIEW'}};
	my $line;
	my $module;
	my $i = 0;


	while( $line = shift( @database ) ){
		if ( $line =~ /\s*module\s+([A-Za-z0-9_]+)/ ){
			$module = $1;
			$self->{'DATABASE'}->{'MODULES'}->{$module}->{'CONTENTS'} .= "$line\n";
			push( @{$self->{'DATABASE'}->{'MODULES'}->{$module}->{'ARRAY'}}, $line );
			while( $line = shift( @database )){
				$self->{'DATABASE'}->{'MODULES'}->{$module}->{'CONTENTS'} .= "$line\n";
				push( @{$self->{'DATABASE'}->{'MODULES'}->{$module}->{'ARRAY'}}, $line );
				if( $line =~ /\s*endmodule/ ){
					last;
				} else {
					next;
				}
			}
			unshift( @database, $line );
			$i++;
		}
	}

	$self->{'DATABASE'}->{'MODCNT'} = $i;
}

##################################################################
sub Get_Inputs(){
##################################################################
        @_ == 2
        or croak 'usage: @inputs = $verilog_handle->Get_Inputs( $module )';
        my $self = shift(@_);
	my $module = shift(@_);
	my $line;
	my @inputs = ();

	if( !defined $self->{'DATABASE'}->{'MODULES'}->{$module} ){
		$self->Error("Module ($module) not present in the design.\n");
		return;
	}
	my $data = $self->{'DATABASE'}->{'MODULES'}->{$module}->{'CONTENTS'};

	$data =~ s/\n//g;
	$data =~ s/\s+//g;
	if( $data =~ m|module$module;| ){
		return( @{$self->{'NULL'}} );
	}
	my @data = split(/;/,$data );

	while( $line = shift( @data )){
		if( $line =~ /^input(.*)/ ){
			$line = $1;
			if( $line =~ /\,/ ){
				push( @inputs, split(/,/,$line ));
			} else {
				push( @inputs, $line );
			}
		}
	}

	@{$self->{'DATABASE'}->{'MODULES'}->{$module}->{'INPUTS'}} = @inputs;
	
	return( @{$self->{'DATABASE'}->{'MODULES'}->{$module}->{'INPUTS'}} );
}

##################################################################
sub Get_Outputs(){
##################################################################
        @_ == 2
        or croak 'usage: @outputs = $verilog_handle->Get_Outputs( $module )';
        my $self = shift(@_);
        my $module = shift(@_);
        my $line;
        my @outputs = ();

	if( !defined $self->{'DATABASE'}->{'MODULES'}->{$module} ){
		$self->Error("Module ($module) not present in the design.\n");
		return;
	}
        my $data = $self->{'DATABASE'}->{'MODULES'}->{$module}->{'CONTENTS'};

        $data =~ s/\n//g;
        $data =~ s/\s+//g;
	if( $data =~ m|module$module;| ){
		return( @{$self->{'NULL'}} );
        }
        my @data = split(/;/,$data );

        while( $line = shift( @data )){
                if( $line =~ /^output(.*)/ ){
                        $line = $1;
                        if( $line =~ /\,/ ){
                                push( @outputs, split(/,/,$line ));
                        } else {
                                push( @outputs, $line );
                        }
                }
        }

        @{$self->{'DATABASE'}->{'MODULES'}->{$module}->{'OUTPUTS'}} = @outputs;

        return( @{$self->{'DATABASE'}->{'MODULES'}->{$module}->{'OUTPUTS'}} );
}


##################################################################
sub Get_Ports(){
##################################################################
        @_ == 2
        or croak 'usage: @ports = $verilog_handle->Get_Ports( $module )';
        my $self = shift(@_);
        my $module = shift(@_);
        my $line;
	my $portlist;

	if( !defined $self->{'DATABASE'}->{'MODULES'}->{$module} ){
		$self->Error("Module ($module) not present in the design.\n");
		return;
	}
        my $data = $self->{'DATABASE'}->{'MODULES'}->{$module}->{'CONTENTS'};
	$data =~ s/\n//g;
	$data =~ s/\s+//g;
	if( $data =~ m|module$module;| ){
                return( @{$self->{'NULL'}} );
        }	
	if( $data =~ m|module$module\((.*?)\);| ){
		$portlist = $1;
	}
	my @ports = split(/,/, $portlist );
	@{$self->{'DATABASE'}->{'MODULES'}->{$module}->{'PORTS'}} = @ports;

       	return( @{$self->{'DATABASE'}->{'MODULES'}->{$module}->{'PORTS'}} );
}


##################################################################
sub GetLoads(){
##################################################################
        @_ == 2
        or croak 'usage: @loads = $verilog_handle->GetLoads( $signal )'; 
        my $self = shift(@_);
        my $signal = shift(@_);
        my @loads = ();
        my $line = undef;

}

##################################################################
sub Get_Regs(){
##################################################################
        @_ == 2
        or croak 'usage: @regs = $verilog_handle->Get_Regs( $module )';
        my $self = shift(@_);
        my $module = shift(@_);
        my $line;
        my @regs = ();

	if( !defined $self->{'DATABASE'}->{'MODULES'}->{$module} ){
		$self->Error("Module ($module) not present in the design.\n");
		return;
	}
        my $data = $self->{'DATABASE'}->{'MODULES'}->{$module}->{'CONTENTS'};
        $data =~ s/\n//g;
        $data =~ s/\s+//g;
        my @data = split(/;/,$data );

        while( $line = shift( @data )){
                if( $line =~ /^reg(.*)/ ){
                        $line = $1;
                        if( $line =~ /\,/ ){
                                push( @regs, split(/,/,$line ));
                        } else {
                                push( @regs, $line );
                        }
                }
        }

        @{$self->{'DATABASE'}->{'MODULES'}->{$module}->{'REGS'}} = @regs;

	if( @{$self->{'DATABASE'}->{'MODULES'}->{$module}->{'REGS'}} ){
        	return( @{$self->{'DATABASE'}->{'MODULES'}->{$module}->{'REGS'}} );
	} else {
		return( @{$self->{'NULL'}} );
	}
}

##################################################################
sub Get_Wires(){
##################################################################
        @_ == 2
        or croak 'usage: @wires = $verilog_handle->Get_Wires( $module )';
        my $self = shift(@_);
        my $module = shift(@_);
        my $line;
        my @wires = ();

	if( !defined $self->{'DATABASE'}->{'MODULES'}->{$module} ){
		$self->Error("Module ($module) not present in the design.\n");
		return;
	}
        my $data = $self->{'DATABASE'}->{'MODULES'}->{$module}->{'CONTENTS'};
        $data =~ s/\n//g;
        $data =~ s/\s+//g;
        my @data = split(/;/,$data );

        while( $line = shift( @data )){
                if( $line =~ /^wire(.*)/ ){
                        $line = $1;
                        if( $line =~ /\,/ ){
                                push( @wires, split(/,/,$line ));
                        } else {
                                push( @wires, $line );
                        }
                }
        }

        @{$self->{'DATABASE'}->{'MODULES'}->{$module}->{'WIRES'}} = @wires;

	if( @{$self->{'DATABASE'}->{'MODULES'}->{$module}->{'WIRES'}} ){
        	return( @{$self->{'DATABASE'}->{'MODULES'}->{$module}->{'WIRES'}} );
	} else {
                return( @{$self->{'NULL'}} );
        }

}

##################################################################
sub Get_Inouts(){
##################################################################
        @_ == 2
        or croak 'usage: @inouts = $verilog_handle->Get_Inouts( $module )';
        my $self = shift(@_);
        my $module = shift(@_);
        my $line;
        my @inouts = ();

	if( !defined $self->{'DATABASE'}->{'MODULES'}->{$module} ){
		$self->Error("Module ($module) not present in the design.\n");
		return;
	}
        my $data = $self->{'DATABASE'}->{'MODULES'}->{$module}->{'CONTENTS'};
        $data =~ s/\n//g;
        $data =~ s/\s+//g;
	if( $data =~ m|module$module;| ){
                return( @{$self->{'NULL'}} );
        }
        my @data = split(/;/,$data );

        while( $line = shift( @data )){
                if( $line =~ /^inout(.*)/ ){
                        $line = $1;
                        if( $line =~ /\,/ ){
                                push( @inouts, split(/,/,$line ));
                        } else {
                                push( @inouts, $line );
                        }
                }
        }

        @{$self->{'DATABASE'}->{'MODULES'}->{$module}->{'INOUTS'}} = @inouts;

        return( @{$self->{'DATABASE'}->{'MODULES'}->{$module}->{'INOUTS'}} );
}

##################################################################
sub Get_Toplevel(){
##################################################################
        @_ == 1
        or croak 'usage: $toplevel = $verilog_handle->Get_Toplevel';
        my $self = shift(@_);
        my $module;
        my @toplevel = ();
	my @top = ();
	my %found = ();

        my $data = $self->{'DATABASE'}->{'FULL'}->{'CONTENTS'};
	
	$data =~ s/\n+/ /g;

	%found = ( $data =~ m|[^\.]([A-Za-z0-9_]+)\s+([\#\(\)A-Za-z0-9_]+)\s*\(|g );

	foreach $module ( sort keys %{$self->{'DATABASE'}->{'MODULES'}} ){
		if( !defined $found{$module} ){
			push( @toplevel, $module );
			push( @top, "$module\n" );
		}
	}

        $self->Message($self->{Handle}, "Highest level modules:\n @top" );

        $self->{'DATABASE'}->{'TOPLEVEL'} = \@toplevel;

        return( $self->{'DATABASE'}->{'TOPLEVEL'} );
}



##################################################################
sub Hierarchy(){
##################################################################
        @_ == 2
        or croak 'usage: $verilog_handle->Hierarchy( $top )';
        my $self = shift(@_);
        my $toplevel = shift(@_);
        my $module;
        my $data;
        my $hierarchy = { };
        my @found = ();
	my @mod_inst = ();

	if( ! defined $self->{'DATABASE'}->{'MODULES'}->{$toplevel} ){
		$self->Error("Module ($toplevel) is not present in the design.\n");
		return undef;
	}
	
	if( $self->{'GLOBAL'}->{'flag'} == 0 ){
		$self->Message($self->{Handle},"\tBuilding module hierarchy hashes ..\n");
		$self->{'GLOBAL'}->{'flag'} = 1;
	}

        $data = $self->{'DATABASE'}->{'MODULES'}->{$toplevel}->{'CONTENTS'};
	$data =~ s/\n+/ /g;
	@found = ( $data =~ m|([A-Za-z0-9_]+\s+[A-Za-z0-9_]+)\s*\(\s*[\.\\A-Za-z0-9_\[\]]+\s*\(|g );
	foreach $module ( @found ){
		@mod_inst = split( /\s+/, $module );
		$mod_inst[0] =~ s/\s+//g;
		$mod_inst[1] =~ s/\s+//g;
		if( defined $self->{'DATABASE'}->{'MODULES'}->{$mod_inst[0]} ){
			$self->{'DATABASE'}->{'INSTANCES'}->{$mod_inst[1]} = $mod_inst[0];
			$hierarchy->{$mod_inst[1]} = $self->Hierarchy( $mod_inst[0] );
		} else {
			push( @{$self->{'DATABASE'}->{'MODULES'}->{'NODEFINITION'}}, $mod_inst[0] );
			next;
		}
	}
        return( $hierarchy );
}

##################################################################
sub Print_Hierarchy(){
##################################################################
        @_ == 2
        or croak 'usage: $verilog_handle->Print_Hierarchy( $hierarchy )';
	my $self = shift(@_);
	my $hierarchy = shift(@_);
	my $module;
	
	if( ! defined $hierarchy ){
		$self->Error("No hierarchy to display.\n");
		return undef;
	}
	foreach $module ( keys %{$hierarchy} ){
		$self->Message($self->{Handle},"@{$self->{'GLOBAL'}->{'indent'}} $module\n");
		push( @{$self->{'GLOBAL'}->{'indent'}},"\t");
		$self->Print_Hierarchy( $hierarchy->{$module} );
		pop( @{$self->{'GLOBAL'}->{'indent'}});
	}
	return;
}


##################################################################
sub Get_Define(){
##################################################################
        @_ >= 1
        or croak 'usage: $verilog_handle->Get_Define or $verilog_handle->Get_Define( $define )';
        my $self = shift(@_);
	my $define;
	if( @_ ){
		$define = shift(@_);
		if( defined $self->{'DATABASE'}->{'DEFINES'}->{$define} ){
			if( $self->{'DATABASE'}->{'DEFINES'}->{$define} eq "junk" ){
				$self->Message($self->{Handle},"Text Macro $define set.");
				return;
			} else {
				$self->Message($self->{Handle},"Text Macro $define set to $self->{'DATABASE'}->{'DEFINES'}->{$define}\n");
				return;
			}
		} else {
			$self->Message($self->{Handle},"Text Macro $define not defined.\n");
			return;
		}
	} else {
		foreach $define ( sort keys %{$self->{'DATABASE'}->{'DEFINES'}} ){
			if( $self->{'DATABASE'}->{'DEFINES'}->{$define} eq "junk" ){
				$self->Message($self->{Handle},"Text macro $define set.\n");
			} else {
				$self->Message($self->{Handle},"Text macro $define set to $self->{'DATABASE'}->{'DEFINES'}->{$define}\n");
			}
		}
		return;
	}
	return;
}

##################################################################
sub Get_Files{
##################################################################
	@_ == 1 
	or croak 'usage: @verilog_files = $verilog_handle->Get_Files';
	if( @_ ){
		return ( @{$_[0]->{'DATABASE'}->{'FILES'}} );
	} else {
		return undef;
	}
}

##################################################################
sub Get_Module_Count{
##################################################################
        @_ == 1
        or croak 'usage: $no_of_modules = $verilog_handle->Get_Module_Count';

        if( @_ ){
                return ( $_[0]->{'DATABASE'}->{'MODCNT'});
        } else {
                return undef;
        }
}

##################################################################
sub Get_Modules{
##################################################################
	@_ == 1
	or croak 'usage: @modules = $verilog_handle->Get_Modules';
	my $self = $_[0];
	my @modules = ();

	if( @_ ){
		foreach ( keys ( %{$self->{'DATABASE'}->{'MODULES'}} ) ){
			push( @modules,$_);
		}
		return( @modules );
	} else {
		return undef;
	}
}

##################################################################
sub Get_Full_Design{
##################################################################
        @_ == 1
        or croak 'usage: @design = $verilog_handle->Get_Full_Design';
        if( @_ ){
                return( @{$_[0]->{'DATABASE'}->{'FULLVIEW'}} );
        } else {
                return undef;
        }
}

##################################################################
sub Get_Module_Contents(){
##################################################################
        @_ == 2
        or croak 'usage: $module_content = $verilog_handle->Get_Module_Contents( $module )';

	my $self = shift(@_);
	my $module = shift(@_);

        if( defined $module ){
                return( @{$self->{'DATABASE'}->{'MODULES'}->{$module}->{'ARRAY'}} );
        } else {
                return undef;
        }
}


##################################################################
sub Message(){
##################################################################
        @_ <= 3
        or croak 'usage: $verilog_handle->Message( <message> ); or $verilog_handle->Message( <file handle>, <message> );';
	my $self = shift;
        my $fh = undef;
        my $msg = undef;
        if( @_ == 2 ){
                $fh = shift;
	        $msg = shift;
        } else {
	        $msg = shift;
        }
        $msg =~ s/(.{72,72})/$1\n/g; 
        if( defined $fh ){
                print $fh "$msg\n";
                print "$msg\n";
        } else {
                print "$msg\n";
        }
}

##################################################################
sub Warning(){
##################################################################
        @_ <= 3
        or croak 'usage: $verilog_handle->Warning( <message> ); or $verilog_handle->Warning( <file handle>, <message> );';
	my $self = shift;
        my $fh = undef;
        my $msg = undef;
        if( @_ == 2 ){
                $fh = shift;
	        $msg = shift;
        } else {
	        $msg = shift;
        }
        $msg =~ s|\n+|\n         |g;
        $msg =~ s/(.{72,72})/$1\n         /g; 
        if( defined $fh ){
                print $fh "Warning! $msg\n";
                print "Warning! $msg\n";
        } else {
                print "Warning! $msg\n";
        }
 
}

##################################################################
sub Echo(){
##################################################################
        @_ <= 3
        or croak 'usage: $verilog_handle->Echo( <message> ); or $verilog_handle->Echo( <file handle>, <message> );';
	my $self = shift;
        my $fh = undef;
        my $msg = undef;
        if( @_ == 2 ){
                $fh = shift;
	        $msg = shift;
        } else {
	        $msg = shift;
        }
        if( defined $fh ){
                print $fh $msg,"\n";
                print $msg,"\n";
        } else {
                print $msg,"\n";
        }  
}

##################################################################
sub Error(){
##################################################################
        @_ <= 3
        or croak 'usage: $verilog_handle->Error( <message> ); or $verilog_handle->Error( <file handle>, <message> );';
	my $self = shift;
        my $fh = undef;
        my $msg = undef;
        if( @_ == 2 ){
                $fh = shift;
	        $msg = shift;
        } else {
	        $msg = shift;
        }
        $msg =~ s/(.{72,72})/$1\nError! /g; 
        if( defined $fh ){
                print $fh "-" x 80,"\n";
                print $fh "Error! $msg\n";
                print $fh "-" x 80,"\n";
        } else {
                print "-" x 80,"\n";
                print "Error! $msg\n";
                print "-" x 80,"\n";
        }
}

1;

__END__

=head1 NAME

GetInfo.pm - Get information on a verilog HDL design

=head1 SYNOPSIS

Verilog::GetInfo.pm   version 1.0.1

=head1 STATUS

GetInfo.pm is in its beta version. Report all bugs to the author.

=head1 DESCRIPTION

Verilog::Getinfo.pm is a complete verilog HDL language parser. 

          $handle = Verilog::GetInfo->new(\@command_line)';

Above command will return a handle to the new verilog design database.
@command_line is the verilog command line options understood by GetInfo.
Most of the standard (IEEE 1364) command line options are supported.

Some of the options which are supported are:
-v, -y, + ( plusargs ), +define+, -l etc.

GetInfo understands most of the in-the-file options of Verilog HDL language:
`ifdef...`else...`endif
`include "filename.v"
`define macro value
`define macro

Some of the functions which return information about the design are:

=item Get_Inputs( module_name ) - Returns list of input ports of the module.
      It will return an array, with all the inputs along with their bus 
      widths.

=item Get_Outputs( module_name ) - Returns list of output ports of the module.
      An array with all the output ports listed along with their bus
      widths.

=item Get_Ports( module_name ) - Returns list of ports of a module.

=item Get_Regs( module_name ) - Returns list of registers defined in a module.

=item Get_Wires( module_name) - Returns list of wires defined in a module.

=item Get_Inouts( module_name) - Returns list of inout ports in a module.

=item Get_Toplevel() - Returns list of module names, which are not instantiated
      inside any other modules. 

=item Hierarchy( module_name ) - The module_name can be any module in the design.
      Returns a reference to a hash containing the hierarchy information of the
      module. It will report all the sub-modules below the module provided as the
      input to the function.

=item Print_Hierarchy( module_name ) - This function calls the function Hierarchy(),
      and prints the hierarchy in the nice human readable form.

=item Get_Define() - If an input is given to this function it will check, whether
      it is defined. If it is defined, it will check whether its a macro definition
      and return the value of the macro. 
      If no input is given, it will return all the defines that have been defined
      in the design, with their values.


=item Get_Files() - This function returns all the design files read into the
      database.

=item Get_Module_Count() - This function returns the total number of modules
      in the design.

=item Get_Modules() - This functions lists all the modules in the design.

=item Get_Full_Design() - This function returns a full flat netlist in an 
      array. Note: The array will be huge.

=item Get_Module_Contents() - This function returns the contents ( without comments )
      of a module provided as an input to this function.

=item Message(), Warning(), Echo(), Error() - Display functions. Will display lot 
      of information when parsing the design files.


=head1 BUGS
No known bugs. Plz report all the bugs to the author ( see AUTHOR section below )


=head1 AUTHOR

Rohit Mishra E<lt>F<rohit@rohitmishra.com>E<gt>

=head1 COPYRIGHT

Copyright (c) 2005 Rohit Mishra <rohit@rohitmishra.com>. All rights reserved
This program is a free software; you can redistribute it and/or modify it
under the same terms as Perl itself.

=cut