############################################################################### # # File: rvp.pm # RCS: $Header: /home/cc/v2html/build/../RCS/rvp.pm,v 7.61 2006/03/25 22:24:57 cc Exp $ # Description: The Rough Verilog Parser Perl Module # Author: Costas Calamvokis # Created: Fri Apr 10 16:59:30 1998 # Modified: Thu Jan 12 10:45:27 2006 # Language: Perl # # Copyright 1998-2006 Costas Calamvokis # # This file nay be copied, modified and distributed only in accordance # with the terms of the limited licence contained in the accompanying # file LICENCE.TXT. # ############################################################################### # =head1 rvp - Rough Verilog Parser Perl Module The basic idea is that first you call read_verilog will a list of all of your files. The files are parsed and information stored away. You are then handed back a pointer to the information which you can use in calls to the various get_ function to get information about the verilog design. For Example: #!/usr/bin/perl -w use rvp; # use the rough verilog parser # Read in all the files specified on the command line $vdata = rvp->read_verilog(\@ARGV,[],{},1,[],[],''); # Print out all the modules found foreach $module ($vdata->get_modules()) { print "$module\n"; } Unless you are doing something very strange, you can probably ignore all of the functions that have the words 'context' or 'anchors' in them! =cut package rvp; #use strict; #use Tie::IxHash; use File::Basename; use vars qw(@verilog_gatetype_keywords $verilog_gatetype_regexp @verilog_compiler_keywords @verilog_signal_keywords @verilog_sigs $verilog_sigs_regexp $quiet $debug $VID $HVID $VNUM $takenArcs $baseEval $rvpEval $debugEval $languageDef $vid_vnum_or_string $version $VERSION); BEGIN { # $VERSION is used by 'use', but keep $version for backwards compatibility $version = '$Header: /home/cc/v2html/build/../RCS/rvp.pm,v 7.61 2006/03/25 22:24:57 cc Exp $'; #' $version =~ s/^\S+ \S+ (\S+) .*$/$1/; $VERSION = $version; @verilog_signal_keywords = qw(input output inout wire tri tri1 supply0 wand triand tri0 supply1 wor time trireg trior reg integer real realtime genvar ); @verilog_sigs = @verilog_signal_keywords; # for backwards compatiblity #V2001 $verilog_sigs_regexp = "\\b(?:" . join("|",@verilog_signal_keywords) . ")\\b"; @verilog_gatetype_keywords = qw(and nand or nor xor xnor buf bufif0 bufif1 not notif0 notif1 pulldown pullup nmos rnmos pmos rpmos cmos rcmos tran rtran tranif0 rtranif0 tranif1 rtranif1 ); $verilog_gatetype_regexp = "\\b(?:" . join("|",@verilog_gatetype_keywords) . ")\\b"; # Note: optimisation code in _search() assumes all of # these compiler keywords contain a ` @verilog_compiler_keywords = qw( `celldefine `define `delay_mode_path `disable_portfaults `else `enable_portfaults `endcelldefine `endif `ifdef `include `nosuppress_faults `suppress_faults `timescale `undef `resetall `delay_mode_distributed `default_nettype `file `line `ifndef `elsif ); #` # a verilog identifier is this reg exp # a non-escaped identifier is A-Z a-z _ 0-9 or $ # an escaped identifier is \ followed by non-whitespace # why \\\\\S+ ? This gets \\\S+ in to the string then when it # it used we get it searching for \ followed by non-whitespace (\S+) $VID = '(?:[A-Za-z_][A-Za-z_0-9\$]*|\\\\\S+)'; # hierarchical VID - just $VID(.$VID)+ but can't write it like this # because of \ escaping (and must include whitespace after esc.ids.) $HVID = '(?:(?:[A-Za-z_][A-Za-z_0-9\$]*|\\\\\S+\s+)'. '(?:\.(?:[A-Za-z_][A-Za-z_0-9\$]*|\\\\\S+\s+))+)'; # V2001: added [sS] - is this correct $VNUM= '(?:(?:[0-9]*\'[sS]?[bBhHdDoO]\s*[0-9A-Fa-f_zZxX?]+)|(?:[-0-9Ee._]+))'; $quiet=0; $debug=0; } ########################################################################### =head1 read_verilog reads in verilog files, parses them and stores results in an internal data structure (which I call a RVP database). Arguments: - reference to array of files to read (can have paths) - reference to hash of defines with names as keys - reference to array of global includes - not used anymore, just kept for backwards compatibility - quite flag. 1=be quiet, 0=be chatty. - reference to array of include directories - reference to array of library directories - library extension string (eg '.v') or reference to array of strings Returns: - a pointer to the internal data structure. Example: $defines{'TRUE'}=1; # same as +define+TRUE=1 on verilog cmd line $vdata = rvp->read_verilog(\@files,[],\%defines,1, \@inc_dirs,\@lib_dirs,\@lib_exts); =cut sub read_verilog { # be backwards compatible with non-OO call my $class = ("ARRAY" eq ref $_[0]) ? "rvp" : shift; my ($files,$global_includes,$cmd_line_defines,$local_quiet,$inc_dirs, $lib_dirs,$lib_ext_arg,$exp) = @_; my ($file,$fb,$old_quiet,@search_files,@new_search_files,$lib_exts); my $self; die "read_verilog needs an array ref as arg 1" unless "ARRAY" eq ref $files; die "read_verilog needs an hash ref as arg 2" unless "HASH" eq ref $cmd_line_defines; die "read_verilog needs 0 or 1 as arg 3" unless $local_quiet==0 || $local_quiet==1; # be backwards compatible if (!defined($inc_dirs)) { $inc_dirs=[]; } if (!defined($lib_dirs)) { $lib_dirs=[]; } if (!defined($lib_ext_arg)) { # no libexts given $lib_exts=['']; } elsif (!ref($lib_ext_arg)) { # a string given $lib_exts=[$lib_ext_arg]; } else { # an array ref given $lib_exts=$lib_ext_arg; } # make the parser if (! defined &parse_line) { my $perlCode=_make_parser( $debug ? [ $baseEval,$debugEval,$rvpEval ]: [ $baseEval,$rvpEval ] , $debug ); if ($debug) { open(PC,">v2html-parser.pl"); print PC $perlCode; } eval($perlCode); print STDERR $@ if ($@); } if (! defined &_parse_line) { die "Parse code generation failed";} $old_quiet=$quiet; $quiet=$local_quiet; # set up top of main data structure $self = {}; $self->{files} = {}; # information on each file $self->{modules} = {}; # pointers to module info in {files} $self->{defines} = {}; $self->{ignored_modules} = {}; # list of modules were duplicates were found $self->{unresolved_modules} = {}; # modules we have not found yet $self->{problems} = []; # warning/confused messages bless($self,$class); foreach my $d (keys(%$cmd_line_defines)) { _add_define($self->{defines}, $d , $cmd_line_defines->{$d}, '', 0 ); } # go through all the files and find information @new_search_files = @{$files}; while (@new_search_files) { @search_files = @new_search_files; @new_search_files = (); foreach $file (@search_files) { $self->_search($file,$inc_dirs); } push( @new_search_files , _resolve_modules( $self, $lib_dirs, $lib_exts ) ); } if ($debug) { _check_coverage(); } # cross reference files' information print "Cross referencing\n" unless $quiet; $self->_cross_reference(); $quiet=$old_quiet; foreach my $m ( sort (keys %{$self->{unresolved_modules}} )) { # find somewhere it is instantiated for warning message my $file=""; my $line=""; foreach my $m2 (sort (keys %{$self->{modules}})) { foreach my $inst (@{$self->{modules}{$m2}{instances}}) { if ($inst->{module} eq $m) { $file = $inst->{file}; $line = $inst->{line}; last; } } } #$self->_add_warning("$file:$line: Could not find module $m"); } return $self; } ########################################################################### =head1 get_problems Return any problems that happened during parsing Returns: - array of strings of problems. Each one is: "TYPE:FILE:LINE: description" =cut sub get_problems { my ($self) = @_; return (@{$self->{problems}}); } ########################################################################### =head1 set_debug Turns on debug printing in the parser. Returns: - nothing =cut sub set_debug { $debug=1; } ########################################################################### =head1 unset_debug Turns off debug printing in the parser. Returns: - nothing =cut sub unset_debug { $debug=0; } ########################################################################### =head1 get_files Get a list of all the files in the database. Returns: - list of all the files Example: @all_files = $vdata->get_files(); =cut sub get_files{ my ($self) = @_; if (wantarray) { return sort (keys %{$self->{files}}); } else { # in a scalar context keys returns the number of elements - sort doesn't return keys %{$self->{files}}; } } ########################################################################### =head1 get_files_modules Get a list of all the modules in a particular file. Arguments: - name of file Returns: - list of module names Example: @modules = $vdata->get_files_modules($file); =cut sub get_files_modules{ my ($self,$file) = @_; my (@modules,$m); foreach $m (sort (keys %{$self->{files}{$file}{modules}})) { push(@modules,$m) } return @modules; } ########################################################################### =head1 get_files_full_name Get the full name (including path) of a file. Arguments: - name of file Returns: - full path name Example $full_name = $vdata->get_files_full_name($file); =cut sub get_files_full_name{ my ($self,$file) = @_; return $self->{files}{$file}{full_name}; } ########################################################################### =head1 get_files_stats Get statistics about a file Arguments: - name of file Returns: - number of lines in the file (more later...) Example $full_name = $vdata->get_files_stats($file); =cut sub get_files_stats{ my ($self,$file) = @_; return $self->{files}{$file}{lines}; } ########################################################################### =head1 file_exists Test if a particular module file in the database. Arguments: - file name to test. Returns: - 1 if exists otherwise 0 Example: if ($vdata->file_exists($file)).... =cut sub file_exists{ my ($self,$file) = @_; return exists($self->{files}{_ffile($file)}); } ########################################################################### =head1 get_modules Get a list of all the modules in the database. Returns: - list of all the modules Example: @all_modules = $vdata->get_modules(); =cut sub get_modules{ my ($self) = @_; if (wantarray) { return sort (keys %{$self->{modules}}); } else { # in a scalar context keys returns the number of elements - sort doesn't return keys %{$self->{modules}}; } } ########################################################################### =head1 get_modules_t_and_f Get a list of all the tasks and functions in a particular module. Arguments: - name of module Returns: - list of tasks and function names Example: if ( @t_and_f = $vdata->get_modules_t_and_f($m))... =cut # return a list of all the tasks and functions in a module sub get_modules_t_and_f{ my ($self,$module) = @_; if (wantarray) { return sort (keys %{$self->{modules}{$module}{t_and_f}}); } else { # in a scalar context keys returns the number of elements - sort doesn't return keys %{$self->{modules}{$module}{t_and_f}}; } } ########################################################################### =head1 get_modules_t_or_f Get information on a task or function in a module. Arguments: - module name - task or function name Returns: - A 4 element list: type (task or function), definition line, file, anchor Example: ($t_type,$t_line ,$t_file,$t_anchor)= $vdata->get_modules_t_or_f($m,$tf); =cut sub get_modules_t_or_f{ my ($self,$mod,$t_or_f) = @_; if (exists($self->{modules}{$mod}{t_and_f}{$t_or_f})) { return($self->{modules}{$mod}{t_and_f}{$t_or_f}{type}, $self->{modules}{$mod}{t_and_f}{$t_or_f}{line}, $self->{modules}{$mod}{t_and_f}{$t_or_f}{file}, $self->{modules}{$mod}{t_and_f}{$t_or_f}{anchor}); } else { return (); } } ########################################################################### =head1 get_modules_signals Get a list of all the signals in a particular module. Arguments: - name of module Returns: - list of signal names Example: if ( @signs = $vdata->get_modules_signals($m))... =cut # return a list of all the tasks and functions in a module sub get_modules_signals{ my ($self,$module) = @_; if (wantarray) { return sort (keys %{$self->{modules}{$module}{signals}}); } else { # in a scalar context keys returns the number of elements - sort doesn't return keys %{$self->{modules}{$module}{signals}}; } } ########################################################################### =head1 get_modules_file Get the file name (no path) that a module is defined in. Arguments: - module name Returns: - file name without path, and the line number module starts on Example: ($f) = $vdata->get_modules_file($m); =cut # get the file name that contains a module sub get_modules_file{ my ($self,$module) = @_; return ($self->{modules}{$module}{file},$self->{modules}{$module}{line}); } ########################################################################### =head1 get_modules_type Get the type of the module - It is one of: module, macromodule or primitive (rvp treats these all as modules). Arguments: - module name Returns: - type Example: $t = $vdata->get_modules_type($m); =cut # get the file name that contains a module sub get_modules_type{ my ($self,$module) = @_; return ($self->{modules}{$module}{type}); } ########################################################################### =head1 get_files_includes Get the file names (no path) of files included in a file. Arguments: - file name Returns: - list of file names without paths Example: @f = $vdata->get_files_includes($file); =cut sub get_files_includes { my ($self,$f) = @_; my @includes_found = (); if (exists($self->{files}{$f})) { foreach my $inc ( sort ( keys %{$self->{files}{$f}{includes}} )) { push(@includes_found,$inc); # do the includes for the included file push(@includes_found, $self->get_files_includes($inc)); } } return @includes_found; } ########################################################################### =head1 get_files_included_by Get the file names (no path) of files that included this file. Arguments: - file name Returns: - list of file names without paths Example: @f = $vdata->get_files_included_by($file); =cut sub get_files_included_by { my ($self,$f) = @_; return @{$self->{files}{$f}{included_by}}; } ########################################################################### =head1 module_ignored Test if a particular module has been ignored because of duplicates found Arguments: - module name to test Returns: - 1 if ignored otherwise 0 Example: if ($vdata->module_ignored($module)).... =cut sub module_ignored { my ($self,$module) = @_; return (exists($self->{modules}{$module}) && $self->{modules}{$module}{duplicate}); } ########################################################################### =head1 module_exists Test if a particular module exists in the database. Arguments: - module name to test Returns: - 1 if exists otherwise 0 Example: if ($vdata->module_exists($module)).... =cut sub module_exists{ my ($self,$module) = @_; return exists($self->{modules}{$module}); } ########################################################################### =head1 get_ignored_modules Return a list of the ignored modules. These are modules where duplicates have been found. Returns: - List of ignored modules Example: - foreach $module ($vdata->get_ignored_modules()).... =cut sub get_ignored_modules { my ($self) = @_; my @ig =(); foreach my $m (sort (keys %{$self->{modules}})) { push(@ig, $m) if ($self->{modules}{$m}{duplicate}); } return @ig; } ########################################################################### =head1 get_module_signal Get information about a particular signal in a particular module. Arguments: - name of module - name of signal Returns: - A list containing: - the line signal is defined - the line signal is assigned first (or -1) - line in instantiating module where an input is driven from (or -1) - the type of the signal (input,output,reg etc) - the file the signal is in - posedge flag (1 if signal ever seen with posedge) - negedge flag (1 if signal ever seen with negedge) - second type (eg reg for a registered output) - signal real source file - signal real source line - range string if any ( not including [ and ] ) - the file signal is assigned first (or '') - file for the instantiating module where an input is driven from (or "") - a pointer to an array of dimensions for memories each element of the array is a dimension, array is empty for non-memories Note posedge and negedge information is propagated up the hierarchy to attached signals. It is not propagated down the hierarchy. Example: ($s_line,$s_a_line,$s_i_line,$s_type,$s_file,$s_p,$s_n, $s_type2,$s_r_file,$s_r_line,$range,$s_a_file,$s_i_file) = $vdata->get_module_signal($m,$sig); =cut sub get_module_signal{ my ($self,$module,$sig) = @_; if (exists( $self->{modules}{$module}{signals}{$sig} )) { return ($self->{modules}{$module}{signals}{$sig}{line}, $self->{modules}{$module}{signals}{$sig}{a_line}, $self->{modules}{$module}{signals}{$sig}{i_line}, $self->{modules}{$module}{signals}{$sig}{type}, $self->{modules}{$module}{signals}{$sig}{file}, $self->{modules}{$module}{signals}{$sig}{posedge}, $self->{modules}{$module}{signals}{$sig}{negedge}, $self->{modules}{$module}{signals}{$sig}{type2}, $self->{modules}{$module}{signals}{$sig}{source}{file}, $self->{modules}{$module}{signals}{$sig}{source}{line}, $self->{modules}{$module}{signals}{$sig}{range}, $self->{modules}{$module}{signals}{$sig}{a_file}, $self->{modules}{$module}{signals}{$sig}{i_file}, $self->{modules}{$module}{signals}{$sig}{dimensions}); } else { return (); } } ########################################################################### =head1 get_first_signal_port_con Get the first port that this signal in this module is connected to. Arguments: - module name - signal name Returns: - a 5 element list: instantiated module name, instance name port name, line number and file Example: ($im,$in,$p,$l,$f)=$vdata->get_first_signal_port_con($m,$s); =cut sub get_first_signal_port_con{ my ($self,$module,$signal) = @_; $self->{current_signal_port_con} =0; $self->{current_signal_port_con_module}=$module; $self->{current_signal_port_con_module_signal}=$signal; return $self->get_next_signal_port_con(); } ########################################################################### =head1 get_next_signal_port_con Get the next port that this signal in this module is connected to. Returns: - a 5 element list: instantiated module name, instance name port name, line number and file Example: ($im,$in,$p,$l,$f)=$vdata->get_next_signal_port_con(); =cut sub get_next_signal_port_con{ my ($self) = @_; my ($module,$signal,$i,$pcref); $module = $self->{current_signal_port_con_module}; $signal = $self->{current_signal_port_con_module_signal}; $i = $self->{current_signal_port_con}; $pcref = $self->{modules}{$module}{signals}{$signal}{port_con}; if (@{$pcref} > $i ) { $self->{current_signal_port_con}++; return ( $pcref->[$i]{module},$pcref->[$i]{inst},$pcref->[$i]{port}, $pcref->[$i]{line},$pcref->[$i]{file}); } else { return (); } } ########################################################################### =head1 get_first_signal_con_to Get the first signal that is connected to this port in an instantiation of this module. This only works for instances that use the .port(sig) notation. Arguments: - module name - signal name Returns: - a 4 element list: signal connected to this port module signal is in instance (of this module) where the connection occurs Example: ($cts,$ctm,$cti)=$vdata->get_first_signal_con_to($m,$s); =cut sub get_first_signal_con_to{ my ($self,$module,$signal) = @_; $self->{current_signal_con_to} =0; $self->{current_signal_con_to_module}=$module; $self->{current_signal_con_to_module_signal}=$signal; return $self->get_next_signal_con_to(); } ########################################################################### =head1 get_next_signal_con_to Get the next signal that is connected to this port in an instantiation of this module. This only works for instances that use the .port(sig) notation. Arguments: - module name - signal name Returns: - a 4 element list: signal connected to this port module signal is in instance (of this module) where the connection occurs Example: ($cts,$ctm,$cti)=$vdata->get_next_signal_con_to(); =cut sub get_next_signal_con_to{ my ($self) = @_; my ($module,$signal,$i,$ctref); $module = $self->{current_signal_con_to_module}; $signal = $self->{current_signal_con_to_module_signal}; $i = $self->{current_signal_con_to}; $ctref = $self->{modules}{$module}{signals}{$signal}{con_to}; if (@{$ctref} > $i ) { $self->{current_signal_con_to}++; return ( $ctref->[$i]{signal},$ctref->[$i]{module},$ctref->[$i]{inst}); } else { return (); } } ########################################################################### =head1 get_first_instantiator Get the first thing that instantiates this module. Arguments: - module name Returns: - a 4 element list: instantiating module, file, instance name, line Example: ($im,$f,$i) = $vdata->get_first_instantiator($m ); =cut # Get the first thing that instantiates or empty list if none. # Returns: { module, file, inst } sub get_first_instantiator{ my ($self,$module) = @_; if ( exists( $self->{modules}{$module} )) { $self->{current_instantiator} =0; $self->{current_instantiator_module}=$module; return $self->get_next_instantiator(); } else { return (); } } ########################################################################### =head1 get_next_instantiator Get the first thing that instantiates the module specified in get_first_instantiator (or _by_context). Returns: - a 4 element list: instantiating module, file, instance name, line Example: ($im,$f,$i) = $vdata->get_next_instantiator(); =cut sub get_next_instantiator{ my ($self) = @_; my ($module,$i); $module = $self->{current_instantiator_module}; $i = $self->{current_instantiator}; if (@{$self->{modules}{$module}{inst_by}} > $i ) { $self->{current_instantiator}++; return ($self->{modules}{$module}{inst_by}[$i]{module}, $self->{modules}{$module}{inst_by}[$i]{file}, $self->{modules}{$module}{inst_by}[$i]{inst}, $self->{modules}{$module}{inst_by}[$i]{line} ); } else { return (); } } ########################################################################### =head1 get_first_instantiation Get the first thing that this module instantiates. Arguments: - module name Returns: - a 4 element list: instantiated module name, file, instance name, and line number Example: ($im,$f,$i,$l) = $vdata->get_first_instantiation($m); =cut sub get_first_instantiation{ my ($self,$module) = @_; if ( exists( $self->{modules}{$module} )) { $self->{current_instantiation} =0; $self->{current_instantiation_module}=$module; return $self->get_next_instantiation(); } else { return (); } } ########################################################################### =head1 get_next_instantiation Get the next thing that this module instantiates. Returns: - a 4 element list: instantiated module name, file, instance name, and line number Example: ($im,$f,$i,$l) = $vdata->get_next_instantiation(); =cut sub get_next_instantiation{ my ($self) = @_; my ($module,$i); $module = $self->{current_instantiation_module}; $i = $self->{current_instantiation}; if (@{$self->{modules}{$module}{instances}} > $i ) { $self->{current_instantiation}++; return ($self->{modules}{$module}{instances}[$i]{module}, $self->{modules}{$module}{instances}[$i]{file}, $self->{modules}{$module}{instances}[$i]{inst_name}, $self->{modules}{$module}{instances}[$i]{line} ); } else { return (); } } ########################################################################### =head1 get_current_instantiations_port_con Gets the port connections for the current instantiations (which is got using get_first_instantiation and get_next_instantiation). If the instantiation does not use .port(...) syntax and rvp does not have the access to the source of the module then the port names will be returned as numbers in connection order starting at 0. Returns: - A hash (well, really a list that can be assigned to a hash). The keys of the hash are the port names. The values of the hash is everything (except comments) that appeared in the brackets in the verilog. Example: %port_con = $vdata->get_current_instantiations_port_con(); foreach $port (keys %port_con) { ... =cut sub get_current_instantiations_port_con{ my ($self) = @_; my ($module,$i); $module = $self->{current_instantiation_module}; $i = $self->{current_instantiation} - 1; if (@{$self->{modules}{$module}{instances}} > $i ) { return (%{$self->{modules}{$module}{instances}[$i]{connections}}); } else { return {}; } } ########################################################################### =head1 get_current_instantiations_parameters Gets the parameters for the current instantiations (which is set using get_first_instantiation and get_next_instantiation). If the instantiation parameters does not use the verilog 2001 .name(...) syntax and rvp does not have the access to the source of the module then the parameter names will be returned as numbers reflecting the order (starting at 0). Returns: - A hash (well, really a list that can be assigned to a hash). The keys of the hash are the parameters names. The values of the hash is everything (except comments) in the value. Example: %parameters = $vdata->get_current_instantiations_parameters(); foreach my $p (keys %parameters) { ... =cut sub get_current_instantiations_parameters{ my ($self) = @_; my ($module,$i); $module = $self->{current_instantiation_module}; $i = $self->{current_instantiation} - 1; my %r; if (@{$self->{modules}{$module}{instances}} > $i ) { foreach my $p (keys %{$self->{modules}{$module}{instances}[$i]{parameters}}) { $r{$p}=$self->{modules}{$module}{instances}[$i]{parameters}{$p}{value}; } } return %r; } ########################################################################### =head1 get_modules_parameters Gets the parameters for a module. Arguments: - module name Returns: - A hash (well, really a list that can be assigned to a hash). The keys of the hash are the parameters names. The values of the hash is everything (except comments) in the value. Example: %parameters = $vdata->get_modules_parameters(); foreach my $p (keys %parameters) { ... =cut sub get_modules_parameters{ my ($self,$module) = @_; my %r; foreach my $p (keys %{$self->{modules}{$module}{parameters}}) { $r{$p}=$self->{modules}{$module}{parameters}{$p}{value}; } return %r; } ####################################################### ################### Modified ########################### ####################################################### =head1 get_modules_parameters_not_local Gets the parameters for a module. Arguments: - module name Returns: - A hash (well, really a list that can be assigned to a hash). The keys of the hash are the parameters names. The values of the hash is everything (except comments) in the value. Example: %parameters = $vdata->get_modules_parameters(); foreach my $p (keys %parameters) { ... =cut sub get_modules_parameters_not_local{ my ($self,$module) = @_; my %r; foreach my $p (keys %{$self->{modules}{$module}{parameters}}) { if ($self->{modules}{$module}{parameters}{$p}{ptype} ne "localparam"){ #print "$p\n"; $r{$p}=$self->{modules}{$module}{parameters}{$p}{value}; } } return %r; } =head1 get_modules_parameters_not_local_in_order Gets the parameter names in_order for a module. =cut sub get_modules_parameters_not_local_order{ my ($self,$module) = @_; my @r=@{$self->{modules}{$module}{parameter_order}};#param/localparam inorder my @w; #parameter inorder foreach my $p (@r) { if ($self->{modules}{$module}{parameters}{$p}{ptype} ne "localparam"){ push(@w,$p); } } return @w; } =head1 get_module_ports_inorder Gets the parameter names in_order for a module. =cut sub get_module_ports_order{ my ($self,$module) = @_; return @{$self->{modules}{$module}{port_order}}; } ########################################################################### =head1 get_define Find out where a define is defined and what the value is Arguments: - name of the define Optional arguments where a you want the correct location and value for a particular use of a multiplely defined define: - file where define is used - line where define is used Returns: - list with three elements: file, line, value or if the define does not exist it returns a empty list. if the define was defined on the command line it sets file="" and line=0 Example: ($f,$l,$v) = $vdata->get_define($word,$file,$line); =cut sub get_define { my ($self,$define,$file,$line) = @_; if ( !defined($self) || !defined($define) || ( defined($file) && !defined($line) ) ) { die "Get define takes either two or four arguments"; } $define =~ s/^\`// ; # remove the ` if any if (!exists( $self->{defines}{$define} )) { return (); } my $index = 0; my $dh = $self->{defines}{$define}; if (defined($file) && exists($dh->{used}{$file}) && exists($dh->{used}{$file}{$line})) { $index = $dh->{used}{$file}{$line}; } if ($index eq "XX") { # define has been undefed return (); } return ( $dh->{defined}[$index]{file}, $dh->{defined}[$index]{line}, $dh->{defined}[$index]{value}); } ########################################################################### =head1 get_context Get the context (if any) for a line in a file. Arguments: - file name - line number Returns: - line number if there is a context, zero if there is none. Example: $l = $vdata->get_context($filename,$line); =cut sub get_context{ my ($self,$file,$line) = @_; if ( exists( $self->{files}{$file}{contexts}{$line} )) { return $line; } else { return 0; } } ########################################################################### =head1 get_module_start_by_context Test if the context is a module definition start. Arguments: - file name - line number Returns: - module name if it is a module start, 0 otherwise Example: if($vdata->get_module_start_by_context($filename,$line)).. =cut # return true if the context for this line is a module start sub get_module_start_by_context{ my ($self,$file,$line) = @_; if ( exists( $self->{files}{$file}{contexts}{$line}{module_start})) { return $self->{files}{$file}{contexts}{$line}{module_start}; } else { return 0; } } ########################################################################### =head1 get_has_value_by_context Check if the context has a value (ie a new module or something). Contexts that just turn on and off preprocessor ignoring do not have values. Arguments: - file name - line number Returns: - 1 if there is a value, 0 otherwise Example: if ($vdata->get_has_value_by_context($file,$line)).. =cut sub get_has_value_by_context{ my ($self,$file,$line) = @_; return exists( $self->{files}{$file}{contexts}{$line}{value}); } ########################################################################### =head1 get_context_name_type Find the reason for a new context - is it a module / function or task. Contexts that just turn on and off preprocessor ignoring do not have values. Arguments: - file name - line number Returns: - name - type [ module | function | task ] Example: ($n,$t)=$vdata->get_context_name_type($file,$line); =cut sub get_context_name_type{ my ($self,$file,$line) = @_; my ($name,$type); $type=''; if (exists( $self->{files}{$file}{contexts}{$line}{value})) { $name= $self->{files}{$file}{contexts}{$line}{value}{name}; if (exists( $self->{files}{$file}{contexts}{$line}{value}{type})) { $type=$self->{files}{$file}{contexts}{$line}{value}{type}; $type='module' if ($type eq 'primitive' || $type eq 'macromodule'); } return ($name,$type); } else { return (); } } ########################################################################### =head1 get_pre_ignore_by_context Test if the context is preprocessor ignore. Arguments: - file name - line number Returns: - 1 if it is, 0 otherwise Example: if ($vdata->get_pre_ignore_by_context($file,$line)).. =cut sub get_pre_ignore_by_context{ my ($self,$file,$line) = @_; if (exists($self->{files}{$file}{contexts}{$line}{pre_ignore})) { return $self->{files}{$file}{contexts}{$line}{pre_ignore}; } else { return 0; } } ########################################################################### =head1 get_first_instantiator_by_context Get the first thing that instantiates this module using the context. The context must be a module_start. Arguments: - file name (for context) - line name (for context) Returns: - a 4 element list: instantiating module, file, instance name, line Example: @i=$vdata->get_first_instantiator_by_context($f,$l ); =cut sub get_first_instantiator_by_context{ my ($self,$file,$line) = @_; # note: the second exists() checks that the module still exists as # it could have been deleted because a duplicate was found if (exists($self->{files}{$file}{contexts}{$line}{module_start}) && exists($self->{modules} {$self->{files}{$file}{contexts}{$line}{module_start}}) && exists($self->{files}{$file}{contexts}{$line}{value}{inst_by})) { $self->{current_instantiator} =0; $self->{current_instantiator_module}= $self->{files}{$file}{contexts}{$line}{module_start}; return $self->get_next_instantiator(); } else { return (); } } ########################################################################### =head1 get_inst_on_line Gets the instance name of a line in a file Arguments: - file name - line number Returns: - name if the line has an instance name, 0 otherwise Example: if ( $new_inst = $vdata->get_inst_on_line($file,$line) ) ... =cut sub get_inst_on_line{ my ($self,$file,$line) = @_; if ( exists( $self->{files}{$file}{instance_lines}{$line})){ return $self->{files}{$file}{instance_lines}{$line}; } else { return 0; } } ########################################################################### =head1 get_signal_by_context Same as get_module_signal but works by specifying a context. Arguments: - context file name - context line number - signal name Returns: same as get_module_signal Example: =cut # get a signal by context - returns: line, a_line, i_line, type, file sub get_signal_by_context{ my ($self,$file,$cline,$sig) = @_; my $sigp; # in tasks and functions signals can come from module (m_signals) # or from the task or function itself (which gets precedence). if (exists( $self->{files}{$file}{contexts}{$cline}{value}{signals}{$sig} )) { print " found signal $sig\n" if $debug; $sigp=$self->{files}{$file}{contexts}{$cline}{value}{signals}{$sig}; } elsif (exists( $self->{files}{$file}{contexts}{$cline}{value}{m_signals}{$sig} )) { print " found m_signal $sig\n" if $debug; $sigp=$self->{files}{$file}{contexts}{$cline}{value}{m_signals}{$sig}; } else { return (); } return ($sigp->{line}, $sigp->{a_line}, $sigp->{i_line}, $sigp->{type}, $sigp->{file}, $sigp->{posedge}, $sigp->{negedge}, $sigp->{type2}, $sigp->{source}{file}, $sigp->{source}{line}, $sigp->{range}, $sigp->{a_file}, $sigp->{i_file}, $sigp->{dimensions}); } ########################################################################### =head1 get_t_or_f_by_context Same as get_modules_t_or_f but works by specifying a context. Arguments: - context file name - context line number - task name Returns: - same as get_modules_t_or_f Example: =cut sub get_t_or_f_by_context{ my ($self,$cfile,$cline,$t_or_f) = @_; if (exists($self->{files}{$cfile}{contexts}{$cline}{value}{t_and_f}{$t_or_f})) { return($self->{files}{$cfile}{contexts}{$cline}{value}{t_and_f}{$t_or_f}{type}, $self->{files}{$cfile}{contexts}{$cline}{value}{t_and_f}{$t_or_f}{line}, $self->{files}{$cfile}{contexts}{$cline}{value}{t_and_f}{$t_or_f}{file}, $self->{files}{$cfile}{contexts}{$cline}{value}{t_and_f}{$t_or_f}{anchor}); } else { return (); } } ########################################################################### =head1 get_parameter_by_context Return the file and line for a named parameter using context Arguments: - context file name - context line number - parameter name Returns: - file and line of definition Example: =cut sub get_parameter_by_context{ my ($self,$cfile,$cline,$parameter) = @_; if (exists($self->{files}{$cfile}{contexts}{$cline}{value}{parameters}{$parameter})) { return($self->{files}{$cfile}{contexts}{$cline}{value}{parameters}{$parameter}{file}, $self->{files}{$cfile}{contexts}{$cline}{value}{parameters}{$parameter}{line}); } else { return (); } } ########################################################################### =head1 get_anchors Get the anchors for a line in a file. Returns: - a list of anchors Example: foreach $anchor ( $vdata->get_anchors($file,$line) ) .. =cut sub get_anchors{ my ($self,$file,$line) = @_; if (exists($self->{files}{$file}{anchors}{$line})) { return @{$self->{files}{$file}{anchors}{$line}}; } else { return (); } } ########################################################################### =head1 expand_defines Expand the defines in a line of verilog code. for best use this should be called line by line, so that the defines get the correct values when defines are defined multiple times Arguments: - a pointer to the string to expand the defines in - the file the line is from - the line number of the line Returns: - nothing Example: $vdata->expand_defines(\$line_to_expand,$file,$line); =cut ############################################################################### # sub expand_defines { my ($self,$bufp,$file,$line) = @_; if (exists($self->{files}{$file}{define_lines}{$line})) { # do not expand on a `define line - it doesn't make sense to do so # as the substitution of defines in the value only occurs at the # time of use when they could have different values! return; } while ( $$bufp =~ m/^(.*?)\`($VID)/ ) { my $b = $1; my $d = $2; my $dq = quotemeta($d); my $v; if ((undef,undef,$v) = $self->get_define($d,$file,$line)) { $$bufp =~ s/\`$dq/$v/; } else { $$bufp =~ s/\`$dq/_BaCkQuOtE_$dq/; } } $$bufp =~ s/_BaCkQuOtE_/\`/g; } ########################################################################### =head1 verilog_gatetype_keywords Returns: - a list of verilog gatetype keywords Example: @keywords = rvp->verilog_gatetype_keywords(); =cut sub verilog_gatetype_keywords { return (@verilog_gatetype_keywords); } ########################################################################### =head1 verilog_compiler_keywords Returns: - a list of verilog compiler keywords Example: @keywords = rvp->verilog_compiler_keywords(); =cut sub verilog_compiler_keywords { return (@verilog_compiler_keywords); } ########################################################################### =head1 verilog_signal_keywords Returns: - a list of verilog signal keywords Example: @keywords = rvp->verilog_signal_keywords(); =cut sub verilog_signal_keywords { return (@verilog_signal_keywords); } ########################################################################### =head1 chunk_read_init Initialise a file for chunk reading (see chunk_read for more details). It actually reads the whole file into a string, which chunk_read then reads a chunk at a time. The file is closed before chuck_read_init returns. Arguments: - the file to read (with path if needed) - tabstop: 0 = leave tabs alone N = turn tabs spaces with each tabstop=N Returns: - a handle to pass to chunk_read or 0 if file open fails Example: my $chunkRead = rvp->chunkr_read_init($f,$opts{tabstop}); =cut sub chunk_read_init { my ($class,$f,$tabstop) = @_; local (*F); open(F,"<$f") || return 0; my $chunk = { type => "", text => "", isANewLine => 0, isStart => 0, isEnd => 1 , line => 0 }; my $this = { chunk => $chunk , tabstop => $tabstop , linebuf => "" , state => 0 , fh => *F }; return $this; } ########################################################################### =head1 chunk_read Reads verilog a chunk at a time. The file is opened using chunk_read_init. Then chunk_read is used to read the file a chunk at a time. A chunk is a line or part of a line that is all the same type. The types are: comment - either // or /* */ comment attribute - verilog 2001 (* *) atribute include - a line containing `include "file" string - a string code - anything else (verilog code, defines, compliler keywords) Nothing is removed from the file, so if each chunk is printed after being read you will end up with exactly the same file as you put in. Arguments: - handle (from chunk_read_init) Returns: - 0 at the end of file, or a hash ref with the following keys: type - one of the types (see above) text - the text read from the file line - the line number the text is on isANewLine - true if chunk is the first chunk of the line isStart - true if the chunk is the start (eg "/*..." for a comment ) isEnd - true if the chunk is the end ( eg "*/" ) NOTE: isEnd is set to undefined for a type="code" that ends in a newline. This is because chunk_read doesn't know if the code is ending or not. If you need to know in this case you can read the next chunk and see what type it is. Example: my $chunkRead = rvp->chunk_read_init($f,0); while ($chunk = rvp->chunk_read($chunkRead)) { print $chunk->{text} unless $chunk->{type} eq "comment"; } =cut sub chunk_read { my ($class,$this) = @_; my $chunk = $this->{chunk}; $chunk->{isStart} = $chunk->{isEnd}; $chunk->{isEnd} = 0; $chunk->{isANewLine} = 0; if ( $this->{linebuf} eq "" ) { if (!defined($this->{linebuf} = readline($this->{fh}))) { close($this->{fh}); return 0; } $chunk->{isANewLine} = 1; $chunk->{line}++; if ($this->{tabstop}!=0) { # 1 while is some stupid perl thing meaning while (cond) {} may be a bit faster? 1 while ($this->{linebuf} =~ s/(^|\n)([^\t\n]*)(\t+)/ $1. $2 . (" " x ($this->{tabstop} * length($3) - (length($2) % $this->{tabstop}))) /gsex); } } STATE_SWITCH: if ( $this->{state} == 0 ) { $chunk->{type} = "code"; if ( $this->{linebuf} =~ s%^(.*?)((/\*)| # anything followed by /* comment (//)| # or // comment (\(\*(?!\s*\)))| # or (* attribute (but not (*) (\`include\s)| # or `include (\")| # or start of string (import\s)) # import package %$2%ox ) { $chunk->{isEnd} = 1; $chunk->{text} = $1; if (defined($3)) { $this->{state} = 1; # long comment } elsif (defined($4) ||defined($8) ) { $this->{state} = 2; # short comment } elsif (defined($5)) { $this->{state} = 3; # attribute } elsif (defined($6)) { $this->{state} = 4; # include } elsif (defined($7)) { $this->{state} = 5; # string } else { die "chunk_read internal error!"; } if (!$chunk->{text}) { # this happens if we are in state code and a new line # starts with something that isn't code. So we change # and go back to the top. $chunk->{isStart} = 1; $chunk->{isEnd} = 0; goto STATE_SWITCH; } } else { $chunk->{text} = $this->{linebuf}; $this->{linebuf} = ""; # in this case we might be at end, but we don't really know! $chunk->{isEnd} = undef; } } elsif ( $this->{state} == 1 ) { $chunk->{type} = "comment"; # this first test is needed to work so /*/ */ works if ( $chunk->{isStart} && $this->{linebuf} =~ s%^/\*%% ) { $chunk->{text} = "/*"; } else { $chunk->{text} = ""; } if ( $this->{linebuf} =~ s%^(.*?\*/)%% ) { # anything followed by */ $chunk->{text} .= $1; $this->{state} = 0; $chunk->{isEnd} = 1; } else { $chunk->{text} .= $this->{linebuf}; $this->{linebuf} = ""; } } elsif ( $this->{state} == 2 ) { $chunk->{type} = "comment"; $chunk->{text} = $this->{linebuf}; $chunk->{isEnd} = 1; $this->{linebuf} = ""; if ( $chunk->{text} =~ s/\n$// ) { $this->{linebuf} = "\n"; } $this->{state} = 0; } elsif ( $this->{state} == 3 ) { $chunk->{type} = "attribute"; if ( $this->{linebuf} =~ s%^(.*?\*\))%% ) { # anything followed by *) $chunk->{text} = $1; $this->{state} = 0; $chunk->{isEnd} = 1; } else { $chunk->{text} = $this->{linebuf}; $this->{linebuf} = ""; } } elsif ( $this->{state} == 4 ) { $chunk->{type} = "include"; $chunk->{isEnd} = 1; if ( $this->{linebuf} =~ s%^(\`include\s+\".*?\")%% ) { $chunk->{text} = $1; $this->{state} = 0; } else { # this is an error - just return the line as code - the parser will # report the error $chunk->{type} = 0; $chunk->{text} = $this->{linebuf}; $this->{linebuf} = ""; } } elsif ( $this->{state} == 5 ) { $chunk->{type} = "string"; # string all on one line if ( $this->{linebuf} =~ s%^(\"(?:(?:\\\\)|(?:\\\")|(?:[^\"]))*?\")%% ) { $chunk->{text} = $1; $this->{state} = 0; $chunk->{isEnd} = 1; } # end of multiline string (doesn't start with quote) elsif ( $this->{linebuf} =~ s%^([^\"](?:(?:\\\\)|(?:\\\")|(?:[^\"]))*?\")%% ) { $chunk->{text} = $1; $this->{state} = 0; $chunk->{isEnd} = 1; } # middle of multiline string else { $chunk->{text} = $this->{linebuf}; $this->{linebuf} = ""; } } return $chunk; } ############################################################################### # RVP internal functions from now on.... (they all start with _ to # let you know they are internal ############################################################################### ############################################################################### # search a file, putting the data in $self # Note: be careful coding in the main loop... there are a few optimisations # which result in big chunks of code being skipped if the line does not # contain certain characters (eg ' " / *) sub _search { my ($self,$f,$inc_dirs) = @_; my $verilog_compiler_keywords_regexp = "(?:" . join("|",@verilog_compiler_keywords) . ")"; my $file=_ffile($f); _init_file($self->{files},$f); print "Searching $f " unless $quiet; my $chunkRead= rvp->chunk_read_init($f,0) || die "Error: can not open file $f to read: $!\n"; my $file_dir = dirname($f); my $rs = {}; $rs->{modules} = $self->{modules}; $rs->{files} = $self->{files}; $rs->{unres_mod} = $self->{unresolved_modules}; $rs->{module} = ''; $rs->{function} = ''; $rs->{task} = ''; $rs->{t} = undef; # temp store $rs->{p} = undef; my $printline = 1000; my $ps = {}; my $nest=0; my $nest_at_ignore; my @ignore_from_elsif; my $ignoring=0; my @fileStack =(); my $pp_ignore; my $chunk; while (1) { while ($chunk = rvp->chunk_read($chunkRead)) { $self->{files}{$file}{lines} = $chunk->{line}; if ($chunk->{line}>$printline && !$quiet) { $printline+=1000; $|=1; # turn on autoflush print "."; $|=0 unless $debug; # turn off autoflush } # deal quickly with blank lines if ( $chunk->{text} =~ m/^\s*\n/ ) { next; } if ( $chunk->{type} eq "code" ) { #################################################### # Optimisation: if there are no ` # we can parse the line now if ( $chunk->{text} !~ m|[\`]| ) { if ($nest && $ignoring) { next; } $self->_parse_line($chunk->{text},$file,$chunk->{line},$ps,$rs); next; } # handle ifdefs if ($nest && $ignoring) { if ( $chunk->{text} =~ m/^\s*\`(?:ifdef|ifndef)\s+($VID)/ ) { print " Found at line $chunk->{line} : if[n]def (nest=$nest)\n" if $debug; $nest++; } elsif ( $chunk->{text} =~ m/^\s*\`(else|(?:elsif\s+($VID)))/ ) { print " Found at line $chunk->{line} : $1 (nest=$nest)\n" if $debug; if ($1 eq 'else' || _parsing_is_defined($self->{defines},$2, $file,$chunk->{line})) { # true elsif or plain else if ($nest == $nest_at_ignore && !$ignore_from_elsif[$nest]) { $ignoring=0; $$pp_ignore = $chunk->{line}; } } } elsif ( $chunk->{text} =~ m/^\s*\`endif/ ) { print " Found at line $chunk->{line} : endif (nest=$nest)\n" if $debug; if ($nest == $nest_at_ignore) { $ignoring=0; $$pp_ignore = $chunk->{line}; } $nest--; } next; } # handle the case where the endif is on the same line as the ifdef # (note: generally I only accept endif at the start of a line) if ( $chunk->{text} =~ m/\`(ifdef|ifndef)\s+($VID).*\`endif/ ) { print "$file: ifdef and endif on same line\n" if $debug; my $is_defined = _parsing_is_defined($self->{defines},$2, $file,$chunk->{line}); if ( (($1 eq 'ifdef' ) && !$is_defined) || (($1 eq 'ifndef') && $is_defined)) { # replace ifdef with nothing $chunk->{text} =~ s/\`(ifdef|ifndef)\s+($VID)(.*)\`endif//; } else { # replace ifdef with what is between the ifdef and endif $chunk->{text} =~ s/\`(ifdef|ifndef)\s+($VID)(.*)\`endif/$3/; } } if ( $chunk->{text} =~ m/^\s*\`(ifdef|ifndef)\s+($VID)/ ) { $nest++; print " Found at line $chunk->{line} : $1 $2 (nest=$nest)\n" if $debug; my $is_defined = _parsing_is_defined($self->{defines},$2, $file,$chunk->{line}); if ( (($1 eq 'ifdef' ) && !$is_defined) || (($1 eq 'ifndef') && $is_defined)) { $ignoring=1; $self->{files}{$file}{contexts}{$chunk->{line}}{pre_ignore} = 'XX'; $pp_ignore = \$self->{files}{$file}{contexts}{$chunk->{line}}{pre_ignore}; $nest_at_ignore=$nest; $ignore_from_elsif[$nest]=0; } next; } if ( $chunk->{text} =~ m/^\s*\`(else|(?:elsif\s+($VID)))/ ) { print " Found at line $chunk->{line} : $1 (nest=$nest)\n" if $debug; if ($nest) { $ignoring=1; $self->{files}{$file}{contexts}{$chunk->{line}}{pre_ignore} = 'XX'; $pp_ignore = \$self->{files}{$file}{contexts}{$chunk->{line}}{pre_ignore}; $nest_at_ignore=$nest; # an ignore from an elsif means you will never stop ignoring # at this nest level $ignore_from_elsif[$nest]=($1 ne 'else'); } else { $self->_add_warning("$file:$chunk->{line}: found $1 without \`ifdef"); } next; } if ( $chunk->{text} =~ m/^\s*\`endif/ ) { print " Found at line $chunk->{line} : endif (nest=$nest)\n" if $debug; if ($nest) { $nest--; } else { $self->_add_warning("$file:$chunk->{line}: found \`endif without \`ifdef"); } next; } # match define. Note: /s makes the .* match the \n too if ( $chunk->{text} =~ m/^\s*\`define\s+($VID)(.*)/s ) { my $def = $1; my $rest = defined($2)?$2:''; my $defLine = $chunk->{line}; $self->{files}{$file}{define_lines}{$chunk->{line}} = 1; # _parsing_expand_defines is called to register the use # of any multiplely defined defines in the value part of # the define my $tmpValue=$rest; $self->_parsing_expand_defines(\$tmpValue,$file,$chunk->{line}); # handle multiline defines: read more stuff if line ends in backslash # (revisit: verilog spec says leave the newline in the value) # also keep adding stuff to value until it ends in a newline or comment # because strings are seperated out, `define T $display("test") # is delivered as chunks '`define T $display(' ,'"test"', ')\n' while ( (($rest =~ s|\\\n|| ) || ($rest !~ m/\n$/) ) && ($chunk = rvp->chunk_read($chunkRead))) { last if $chunk->{type} eq "comment"; $rest .= $chunk->{text}; $self->{files}{$file}{define_lines}{$chunk->{line}} = 1; # _parsing_expand_defines call: see comment ~15 lines back my $tmpValue=$chunk->{text}; $self->_parsing_expand_defines(\$tmpValue,$file,$chunk->{line}); } my $value = $rest; $value =~ s/^\s+(.*)(\n)?/$1/; print " Found in $file line $defLine : define $def = $value\n" if $debug; _add_define($self->{defines}, $def , $value , $file, $defLine ); _add_anchor($self->{files}{$file}{anchors},$defLine,""); # Don't substitute now: [defines] shall be substituted after the # original macro is substituted, not when it is defined(1364-2001 pg353) next; } if ( $chunk->{text} =~ m/^\s*\`undef\s+($VID)/ ) { _undef_define($self->{defines},$1); print " Found at line $chunk->{line} : undef $1\n" if $debug; next; } if ( $chunk->{text} =~ m/^\s*$verilog_compiler_keywords_regexp/ ) { next; } $self->_parsing_expand_defines(\$chunk->{text},$file,$chunk->{line}); # Note this is called from two other places (optimisations) $self->_parse_line($chunk->{text},$file,$chunk->{line},$ps,$rs); } elsif ( $chunk->{type} eq "include" ) { if ($nest && $ignoring) { next; } $chunk->{text} =~ m/^\s*\`include\s+\"(.*?)\"/ ; # revisit - need to check for recursive includes print " Found at line $chunk->{line} : include $1\n" if $debug; $self->{files}{$file}{includes}{_ffile($1)}=$chunk->{line}; my $inc_file = $1; my $inc_file_and_path = _scan_dirs($inc_file,$inc_dirs,$file_dir); if ($inc_file_and_path) { push(@fileStack,$chunkRead,$f); $f = $inc_file_and_path; $file=_ffile($f); $file_dir = dirname($f); if (!exists($self->{files}{$file})) { _init_file($self->{files},$f); if (exists($rs->{modules}{$rs->{module}})) { $self->{files}{$file}{contexts}{"1"}{value} = $rs->{modules}{$rs->{module}}; } } print "\n Include: $f " unless $quiet; $chunkRead=rvp->chunk_read_init($f,0); } else { $self->_add_warning("$file:$chunk->{line}: Include file $inc_file not found"); } next; } if (defined($pp_ignore) && $pp_ignore eq "XX") { # no endif $$pp_ignore = $chunk->{line}; } } # check if we were included from another file if (0==scalar(@fileStack)) { print "Stack is empty\n" if $debug; last; } else { $f = pop(@fileStack); $chunkRead = pop(@fileStack); $file = _ffile($f); $file_dir = dirname($f); print "\n Back to $f" unless $quiet; } } print "\n" unless $quiet; $self->_check_end_state($file,$self->{files}{$file}{lines},$ps); } sub _open_file { my ($f) = @_; local (*F); print "Searching $f " unless $quiet; open(F,"<$f") || die "Error: can not open file $f to read: $!\n "; return *F; } # only for use while parsing - returns the last defined value # in a multiple define case, and also sets up the {used} info # for use later when querying the database # returns ($value,$errcode) # where $errcode = 0 value ok # 1 value never defined # 2 value has been undefined sub _parsing_get_define_value { my ($defines,$define,$file,$line) = @_; if (!exists( $defines->{$define} )) { return ('',1); } my $index = 0; my $dh = $defines->{$define}; if ( 1 < @{$dh->{defined}} ) { $index = $#{$dh->{defined}}; $dh->{used}{$file}{$line} = $index; } if ($dh->{defined}[$index]{undefed}) { $dh->{used}{$file}{$line} = "XX"; return ('',2); } return ( $dh->{defined}[$index]{value} , 0 ); } sub _parsing_is_defined { my ($defines,$define,$file,$line) = @_; my $v; my $errcode; ($v,$errcode) = _parsing_get_define_value($defines,$define,$file,$line); if ( ($errcode == 1) || # never defined ($errcode == 2) ) { # defined then undefed return 0; } elsif ($errcode == 0) { return 1; } else { die "parsing_is_defined internal error code=$errcode"; } } sub _undef_define { my ($defines,$define) = @_; if (exists( $defines->{$define} )) { my $index = $#{$defines->{$define}{defined}}; $defines->{$define}{defined}[$index]{undefed} = 1; } } ############################################################################### # for best use this should be called line by line, so that the # defines get the correct values when defines are defined multiple # times # - this function is only used during the initial parsing of the files # (it has the error reproting code in it), use expand_defines() other times # it also expands on define lines (used to register the use of multiple # define defines) which expand_defines doesn't # sub _parsing_expand_defines { my ($self,$bufp,$file,$line) = @_; my $defines = $self->{defines}; while ( $$bufp =~ m/^(.*?)\`($VID)/ ) { my $b = $1; my $d = $2; my $dq = quotemeta($d); my $v; my $errCode=0; ($v,$errCode)=_parsing_get_define_value($defines,$d,$file,$line); if ($errCode == 0) { # no error $$bufp =~ s/\`$dq/$v/; } else { if ($errCode == 2) { # defined but then undefed $self->_add_warning("$file:$line: define `$d used after undef"); $$bufp =~ s/\`$dq//; } elsif ($b =~ m/^\s*$/) { $self->_add_warning("$file:$line: unknown define: `$d, guessing it is a compiler directive"); $$bufp=''; } else { $self->_add_warning("$file:$line: found undefined define `$d"); $$bufp =~ s/\`$dq//; } } } } ############################################################################### # Look through all the include/library directories for an include/library file # optional $file_dir is used when including - here a relative path is # relative to the file doing the including, so check this it checks this sub _scan_dirs { my ($fname,$inc_dirs,$file_dir) = @_; my ($dir); if ( $fname =~ m|^/| ) { # an absolute path return "$fname" if ( -r "$fname" && ! -d "$fname"); } if (defined($file_dir) && -r "$file_dir/$fname" && ! -d "$file_dir/$fname") { return "$file_dir/$fname"; } else { foreach $dir (@{$inc_dirs}) { $dir =~ s|/$||; return "$dir/$fname" if ( -r "$dir/$fname" && ! -d "$dir/$fname"); } } return ''; } ############################################################################### # Take a look through the unresolved modules , delete any that have already # been found, and for the others look on the search path # sub _resolve_modules { my ($self,$lib_dirs, $lib_exts)= @_; my ($m,$file,@resolved,$lib_ext); @resolved=(); foreach $m (sort (keys %{$self->{unresolved_modules}})) { if ( exists( $self->{modules}{$m} )) { delete( $self->{unresolved_modules}{$m} ); } else { foreach $lib_ext (@{$lib_exts}) { if ($file = _scan_dirs("$m$lib_ext",$lib_dirs)){ delete( $self->{unresolved_modules}{$m} ); print "resolve_modules: found $m in $file\n" if $debug; push(@resolved,$file); last; } } } } return @resolved; } ############################################################################### # Initialize fdata->{files}{FILE} which stores file data # sub _init_file { my ($fdataf,$file) = @_; my ($fb); $fb = _ffile($file); $fdataf->{$fb} = {}; # set up hash for each file $fdataf->{$fb}{full_name} = $file; $fdataf->{$fb}{anchors} = {}; $fdataf->{$fb}{modules} = {}; $fdataf->{$fb}{contexts} = {}; $fdataf->{$fb}{includes} = {}; $fdataf->{$fb}{inc_done} = 0; $fdataf->{$fb}{lines} = 0; $fdataf->{$fb}{instance_lines} = {}; $fdataf->{$fb}{define_lines} = {}; $fdataf->{$fb}{included_by} = []; } ############################################################################### # Initialize fdata->{FILE}{modules}{MODULE} which stores # module (or macromodule or primitive) data # sub _init_module { my ($modules,$module,$file,$line,$type) = @_; die "Error: attempt to reinit module" if (exists($modules->{$module})); $modules->{$module}{line} = $line; $modules->{$module}{name} = $module; $modules->{$module}{type} = $type; $modules->{$module}{end} = -1; $modules->{$module}{file} = $file; $modules->{$module}{t_and_f} = {}; # tasks and functions $modules->{$module}{signals} = {}; $modules->{$module}{parameter_order}= []; $modules->{$module}{parameters}= {}; $modules->{$module}{instances} = []; # things that this module instantiates $modules->{$module}{inst_by} = []; # things that instantiated this module $modules->{$module}{port_order} = []; $modules->{$module}{named_ports} = 1; # assume named ports in instantiations $modules->{$module}{duplicate} = 0; # set if another definition is found } ############################################################################### # Initialize fdata->{FILE}{modules}{MODULE}{t_and_f}{TF} which # stores tasks and functions' data # sub _init_t_and_f { my ($self,$module,$type,$tf,$file,$line,$anchor) = @_; if (exists($module->{t_and_f}{$tf})) { $self->_add_warning("$file:$line new definition of $tf ". "(discarding previous from ". "$module->{t_and_f}{$tf}{file}:$module->{t_and_f}{$tf}{line})"); } $module->{t_and_f}{$tf} = {}; $module->{t_and_f}{$tf}{type} = $type; $module->{t_and_f}{$tf}{name} = $tf; $module->{t_and_f}{$tf}{line} = $line; $module->{t_and_f}{$tf}{end} = -1; $module->{t_and_f}{$tf}{file} = $file; $module->{t_and_f}{$tf}{signals} = {}; $module->{t_and_f}{$tf}{anchor} = $anchor; # point up at things to share with module: # - task and functions # - module signals $module->{t_and_f}{$tf}{t_and_f} = $module->{t_and_f}; $module->{t_and_f}{$tf}{parameters} = $module->{parameters}; $module->{t_and_f}{$tf}{parameter_order} = $module->{parameter_order}; $module->{t_and_f}{$tf}{m_signals} = $module->{signals}; } # note returns 1 if a signal is added (and an anchor needs to be dropped) sub _init_signal { my ($self,$signals,$name,$type,$type2,$range,$file,$line,$warnDuplicate,$dims) = @_; if (exists( $signals->{$name} )) { if ($warnDuplicate) { if (($signals->{$name}{type} eq "output")|| ($signals->{$name}{type} eq "inout")|| ($signals->{$name}{type} eq "input")) { if (($signals->{$name}{type} eq "input") && ($type eq "reg")) { $self->_add_warning("$file:$line: ignoring definition". " of input $name as reg (defined as input at". " $signals->{$name}{file}:$signals->{$name}{line})"); } else { $signals->{$name}{type2}=$type; } } elsif (($signals->{$name}{type} eq "reg")&& # reg before output (($type eq "output") || ($type eq "inout"))) { $signals->{$name}{type}=$type; $signals->{$name}{type2}="reg"; } else { $self->_add_warning("$file:$line: ignoring another definition". " of signal $name ($type) first seen as". " $signals->{$name}{type} at". " $signals->{$name}{file}:$signals->{$name}{line}"); } } return 0; } else { $signals->{$name} = { type => $type, file => $file, line => $line, a_line => -1, a_file => "", i_line => -1, i_file => "", port_con => [], con_to => [], posedge => 0, negedge => 0, type2 => $type2, source => { checked => 0, file => "" , line => "" }, range => $range, dimensions => $dims, }; return 1; } } ############################################################################### # Add an anchor to the list of anchors that need to be put in # the file # sub _add_anchor { my ($anchors,$line,$name) = @_; my ($a,$no_name_exists); if (! exists($anchors->{$line}) ) { $anchors->{$line} = []; } if ( $name ) { push( @{$anchors->{$line}} , $name ); } else { # if no name is specified then you'll get the line number # as the name, but make sure this only happens once $no_name_exists = 0; foreach $a ( @{$anchors->{$line}} ) { if ($a eq $line) { $no_name_exists=1; last; } } push( @{$anchors->{$line}} , $line ) unless ($no_name_exists); } } sub _add_define { my ($defines,$def_name,$def_value,$file,$line) = @_; $def_value = '' if (!defined($def_value)); $def_value =~ s/\s+$//; # remove whitespace from end of define if (!exists($defines->{$def_name})) { $defines->{$def_name} = { defined => [] , used => {} }; } if ( (1 == @{$defines->{$def_name}{defined}}) && ($defines->{$def_name}{defined}[0]{file} eq $file) && ($defines->{$def_name}{defined}[0]{line} == $line) ) { # if the define is already defined once (and only once) and that # was the same def (file & line the same - for instance in included # file) then there is no need to do anything } else { push (@{$defines->{$def_name}{defined}}, { line => $line, file => $file , value => $def_value, undefed => 0 }); } } ############################################################################### # Cross referencing ############################################################################### ############################################################################### # Cross-reference all the files: # - find the modules and set up $self->{modules} # - store the data about where it is instatiated with each module # - check for self instantiation # - check for files with modules + instances outside modules # - set a_line for signals driven by output and i_line # sub _cross_reference { my ($self) = @_; my ($f,$m,$fr,$mr,$m2,$inst,$sig,$sigp,$port_con,$param,$i,$port,$con_to); # stores the instantiation data in an # array so that we can easily tell which modules # are disconnected and which are the tops of the # hierarchy and makes it easier to go up foreach $m (sort (keys %{$self->{modules}})) { print " Making inst_by for $m\n" if $debug; foreach $m2 (sort (keys %{$self->{modules}})) { foreach $inst (@{$self->{modules}{$m2}{instances}}) { if (($inst->{module} eq $m) && exists($self->{modules}{$m})) { print " inst by $m2\n" if $debug; push( @{$self->{modules}{$m}{inst_by}}, { module => $m2, file => $inst->{file}, inst => $inst->{inst_name} , line => $inst->{line} } ); } } } } # Find any modules that appear to instantiate themselves # (to prevent getting into infinite recursions later on) foreach $m (sort (keys %{$self->{modules}})) { print " Checking self instantiations for $m\n" if $debug; foreach $inst (@{$self->{modules}{$m}{instances}}) { if ($inst->{module} eq $m) { $self->_add_warning("$inst->{file}:$inst->{line}: $m ". "instantiates itself"); $inst->{module} = '_ERROR_SELF_INSTANTIATION_'; # remove the port con for all signals not attached foreach $sig (sort (keys %{$self->{modules}{$m}{signals}})) { $sigp = $self->{modules}{$m}{signals}{$sig}; my $port_con_ok=[]; foreach $port_con (@{$sigp->{port_con}}) { if ($port_con->{module} ne $m) { push(@$port_con_ok,$port_con); } else { print " Deleting connection for $sig\n" if $debug; } } $sigp->{port_con} = $port_con_ok; } } } } # Go through instances without named ports (port will be a number instead) and # resolve name if you can, otherwise delete. These can appear in signal's port_con # lists and in instances connections lists. foreach $m (sort (keys %{$self->{modules}})) { if (0 == $self->{modules}{$m}{named_ports}) { $f = $self->{modules}{$m}{file}; # for error messages print " Resolving numbered port connections in $m\n" if $debug; foreach $sig (sort (keys %{$self->{modules}{$m}{signals}})) { print " doing $sig\n" if $debug; $sigp = $self->{modules}{$m}{signals}{$sig}; foreach $port_con (@{$sigp->{port_con}}) { if ($port_con->{port} =~ m/^[0-9]/ ) { if ( exists( $self->{modules}{$port_con->{module}}) ) { $m2 = $self->{modules}{$port_con->{module}}; if (defined($m2->{port_order}[$port_con->{port}])) { $port_con->{port}=$m2->{port_order}[$port_con->{port}]; } else { $self->_add_warning("$port_con->{file}:$port_con->{line}:". " could not resolve port number to name"); } } } } } foreach $inst (@{$self->{modules}{$m}{instances}}) { if ( exists( $self->{modules}{$inst->{module}}) ) { $m2 = $self->{modules}{$inst->{module}}; foreach $port (sort (keys %{$inst->{connections}})) { last if ($port !~ m/^[0-9]/); # if any are named, all are named if (defined($m2->{port_order}[$port])) { # move old connection to named port $inst->{connections}{$m2->{port_order}[$port]} = $inst->{connections}{$port}; # remove old numbered port from hash delete($inst->{connections}{$port}); } else { $self->_add_warning("$inst->{file}:$inst->{line}:". "could not resolve port number $port to name)"); } } } } } } # Go through all instances with parameter lists and try to resolve names parameter # foreach $m (sort (keys %{$self->{modules}})) { foreach $inst (@{$self->{modules}{$m}{instances}}) { if ($inst->{parameters}) { if ( exists( $self->{modules}{$inst->{module}}) ) { my $mp=$self->{modules}{$inst->{module}}; foreach my $p (sort (keys %{$inst->{parameters}})){ last if ( $p !~ m/^[0-9]+$/ ); my $pn = $mp->{parameter_order}[$p]; if ($pn) { $inst->{parameters}{$pn} = $inst->{parameters}{$p}; delete($inst->{parameters}{$p}); print "$inst->{parameters}{$pn}{file}:". "$inst->{parameters}{$pn}{line}: ". "Resolved $p to $pn = $inst->{parameters}{$pn}{value}\n" if $debug; } else { $self->_add_warning("$inst->{parameters}{$p}{file}:". "$inst->{parameters}{$p}{line} ". "could not resolve parameter number $p to name"); } } } } } } # Go through all the modules and each signal inside # looking at whether the signal is connected to any outputs # (set the a_line on the first one if it is not already set) # Also, when you see a signal connected to an input (and that # submod is only instantiated once) reach down into the submod # and set the i_line of that signal, so that clicking on the # input can pop you up to the line that input is driven in # one of the instantiations foreach $m (sort (keys %{$self->{modules}})) { print " Finding port connections in $m\n" if $debug; foreach $sig (sort (keys %{$self->{modules}{$m}{signals}})) { print " checking signal $sig\n" if $debug; $sigp = $self->{modules}{$m}{signals}{$sig}; foreach $port_con (@{$sigp->{port_con}}) { if ( exists( $self->{modules}{$port_con->{module}}) ) { print " connection to $port_con->{module}\n" if $debug; $m2 = $self->{modules}{$port_con->{module}}; if (exists( $m2->{signals}{$port_con->{port}})) { push(@{$m2->{signals}{$port_con->{port}}{con_to}}, { signal => $sig , module => $m , inst => $port_con->{inst}}); if ( ($m2->{signals}{$port_con->{port}}{type} eq 'output') && ($sigp->{a_line} == -1)) { $sigp->{driven_by_port}=1; $sigp->{a_line} = $port_con->{line}; $sigp->{a_file} = $port_con->{file}; _add_anchor($self->{files}{$port_con->{file}}{anchors}, $port_con->{line},''); } elsif ($m2->{signals}{$port_con->{port}}{type} eq 'input') { $m2->{signals}{$port_con->{port}}{driven_by_port}=1; if (scalar(@{$m2->{inst_by}}) && ($m2->{signals}{$port_con->{port}}{i_line}==-1)) { $m2->{signals}{$port_con->{port}}{i_line}= $port_con->{line}; $m2->{signals}{$port_con->{port}}{i_file}= $port_con->{file}; _add_anchor($self->{files}{$port_con->{file}}{anchors}, $port_con->{line},''); print " set i_line $port_con->{port} ". "$port_con->{file}:$port_con->{line}\n" if $debug; } } } } } } } # find all signal sources foreach $m (sort (keys %{$self->{modules}})) { print " Finding signal sources in $m\n" if $debug; foreach $sig (sort (keys %{$self->{modules}{$m}{signals}})) { $sigp = $self->{modules}{$m}{signals}{$sig}; next if $sigp->{source}{checked}; print " finding signal source for $sig of $m\n" if $debug; $sigp->{source} = $self->_find_signal_source($sigp); } } # propagate the posedge, negedge stuff up the hierarchy foreach $m (sort (keys %{$self->{modules}})) { # only do the recursion for top level modules if ( 0== @{$self->{modules}{$m}{inst_by}} ) { $self->_prop_edges($m); } } # get included_by information foreach $f ( sort (keys %{$self->{files}} )) { foreach $i ($self->get_files_includes($f)) { if (exists $self->{files}{$i}) { push( @{$self->{files}{$i}{included_by}} , $f ); } } } } sub _find_signal_source { my ($self,$sigp) = @_; my ($con_to,$port_con,$ret_val); if ($sigp->{source}{checked}) { print " source already found\n" if $debug; $ret_val = $sigp->{source}; } else { $ret_val = { checked => 1, file => '' , line => '' }; if (exists($sigp->{driven_by_port})) { print " drive by port\n" if $debug; foreach $con_to (@{$sigp->{con_to}}) { # if ($self->{modules}{$con_to->{module}}{signals}{$con_to->{signal}}{type} eq 'input') { if ($sigp->{type} eq 'input') { print " following input $con_to->{signal} $con_to->{module} $con_to->{inst}\n" if $debug; if (!exists($self->{modules}{$con_to->{module}}{signals}{$con_to->{signal}}{i_line})) { die "Error: $con_to->{signal} does not exist $!"; } $ret_val = $self->_find_signal_source( $self->{modules}{$con_to->{module}}{signals}{$con_to->{signal}}); } } foreach $port_con (@{$sigp->{port_con}}) { if (exists ($self->{modules}{$port_con->{module}})) { if (exists($self->{modules}{$port_con->{module}}{signals}{$port_con->{port}})) { if ($self->{modules}{$port_con->{module}}{signals}{$port_con->{port}}{type} eq 'output') { print " following output $port_con->{port} $port_con->{module} $port_con->{inst}\n" if $debug; $ret_val = $self->_find_signal_source( $self->{modules}{$port_con->{module}}{signals}{$port_con->{port}}); } } else { $self->_add_warning("$port_con->{file}:$port_con->{line}:". " Connection to nonexistent port ". " $port_con->{port} of module $port_con->{module}"); } } } } else { if ($sigp->{a_line}==-1) { if ($sigp->{type} eq 'input') { print " signal is an input not driven at higher level\n" if $debug; $ret_val = { checked => 1, file => $sigp->{file} , line => $sigp->{line} }; } else { print " signal has unknown source\n" if $debug; } } else { print " signal is driven in this module\n" if $debug; $ret_val = { checked => 1 , file => $sigp->{a_file} , line => $sigp->{a_line} }; } } } $sigp->{source} = $ret_val; return $ret_val; } ############################################################################### # Propagate posedge and negedge attributes of signals up the hierarchy # sub _prop_edges { my ($self,$m) = @_; my ($imod,@inst,$sig,$sigp,$port_con,$m2); print "Prop_edges $m\n" if $debug; for ( ($imod) = $self->get_first_instantiation($m) ; $imod; ($imod) = $self->get_next_instantiation()) { push(@inst,$imod) if (exists( $self->{modules}{$imod})); } foreach $imod (@inst) { $self->_prop_edges($imod); } # Propagate all the edges up the hierarchy foreach $sig (sort (keys %{$self->{modules}{$m}{signals}})) { print " checking signal $sig\n" if $debug; $sigp = $self->{modules}{$m}{signals}{$sig}; foreach $port_con (@{$sigp->{port_con}}) { if ( exists( $self->{modules}{$port_con->{module}}) ) { print " connection to $port_con->{module}\n" if $debug; $m2 = $self->{modules}{$port_con->{module}}; if (exists( $m2->{signals}{$port_con->{port}})) { print "Propagating posedge on $sig from $port_con->{module} to $m\n" if ($debug && (!$sigp->{posedge}) && $m2->{signals}{$port_con->{port}}{posedge}); $sigp->{posedge} |= $m2->{signals}{$port_con->{port}}{posedge}; $sigp->{negedge} |= $m2->{signals}{$port_con->{port}}{negedge}; } } } } } ############################################################################### # given a source file name work out the file without the path # sub _ffile { my ($sfile) = @_; $sfile =~ s/^.*[\/\\]//; return $sfile; } sub _add_warning { my ($self,$p) = @_; print "Warning:$p\n" if $debug; push (@{$self->{problems}},"Warning:$p"); } sub _add_confused { my ($self,$p) = @_; print "Confused:$p\n" if $debug; push (@{$self->{problems}},"Confused:$p"); } ############################################################################### # BEGIN { $baseEval = { START => { MODULE => '$rs->{t}={ type=>$match, line=>$line };', }, MODULE => { SIGNAL => '$rs->{t}={ type=>$match, range=>"", dimensions=>[], name=>"" , type2=>"",block=>0};', # if you add to this also edit {AFTER_INST}{COMMA} INST => '$rs->{t}={ mod=>$match, line=>$line, name=>"" , port=>0 , params=>{}, param_number=>0 , portName=>"" , vids=>[]};', }, MODULE_NAME => { NAME => 'my $nState="MODULE_PPL"; my $type = $rs->{t}{type}; $rs->{t}=undef;', }, IN_CONCAT => { VID => 'push(@{$rs->{t}{vids}},{name=>$match,line=>$line}) if (exists($rs->{t}{vids}));', }, IN_BRACKET => { VID => 'IN_CONCAT:VID', }, SCALARED_OR_VECTORED => { TYPE => 'if ($match eq "reg") { $rs->{t}{type2} = "reg"; }' }, SIGNAL_NAME => { VID => '$rs->{t}{name}=$match; $rs->{t}{line}=$line;', }, SIGNAL_AFTER_EQUALS => { END => '$rs->{t}=undef;', }, INST_PARAM_BRACKET => { NO_BRACKET => '$self->_add_warning("$file:$line: possible missing brackets after \# in instantiation");', }, INST_NAME => { VID => '$rs->{t}{name}=$match;', }, INST_PORTS => { COMMA => '$rs->{t}{port}++;', }, INST_PORT_NAME => { NAME => '$rs->{t}{portName}=$match; $rs->{t}{vids} = [];', # throw away any instance parameters picked up }, INST_NAMED_PORT_CON => { VID => 'push(@{$rs->{t}{vids}},{name=>$match,line=>$line});', }, INST_NAMED_PORT_CON_AFTER => { COMMA => 'if ($rs->{t}{portName} eq "") { $rs->{t}{portName}=$rs->{t}{port}++; } my @vids = @{$rs->{t}{vids}}; my $portName = $rs->{t}{portName}; $rs->{t}{portName}=""; $rs->{t}{vids}=[];', BRACKET => 'INST_NAMED_PORT_CON_AFTER:COMMA', }, INST_NUMBERED_PORT => { COMMA => 'INST_NAMED_PORT_CON_AFTER:COMMA', BRACKET => 'INST_NAMED_PORT_CON_AFTER:COMMA', VID => 'push(@{$rs->{t}{vids}},{name=>$match,line=>$line});', }, AFTER_INST => { SEMICOLON => '$rs->{t}=undef;', COMMA => '$rs->{t}{line}=$line; $rs->{t}{name}=""; $rs->{t}{port}=0; $rs->{t}{portName}=""; $rs->{t}{vids}=[];', }, SIGNAL_AFTER_NAME => { SEMICOLON => '$rs->{t}=undef;', }, IN_EVENT_BRACKET => { EDGE => '$rs->{t}={ type=>$match };', }, IN_EVENT_BRACKET_EDGE => { VID => 'my $edgeType = $rs->{t}{type}; $rs->{t}=undef;', }, STMNT => { ASSIGN_OR_TASK => '$rs->{t}={ vids=>[{name=>$match,line=>$line}]};', HIER_ASSIGN_OR_TASK => '$rs->{t}={ vids=>[]};', CONCAT => '$rs->{t}={ vids=>[]};', }, STMNT_ASSIGN_OR_TASK => { # copy of STMNT_ASSIGN EQUALS => 'my @vids = @{$rs->{t}{vids}}; $rs->{t}=undef;', # Revisit: this arc doesn't exist anymore - put this into smnt_semicolon # SEMICOLON => '$rs->{t}=undef;', BRACKET => '$rs->{t}=undef;', }, STMNT_ASSIGN => { # copy of STMNT_ASSIGN_OR_TASK EQUALS => 'STMNT_ASSIGN_OR_TASK:EQUALS', }, IN_SIG_RANGE => { END => '$rs->{t}{range}=$fromLastPos;', }, IN_MEM_RANGE => { END => 'push(@{$rs->{t}{dimensions}},$fromLastPos);', }, ANSI_PORTS_TYPE => { # V2001 ansi ports TYPE => '$rs->{t}={ type=>$match, range=>"", dimensions=>[], name=>"" , type2=>"",block=>0};', }, ANSI_PORTS_TYPE2 => { # V2001 ansi ports TYPE => 'if ($match eq "reg") { $rs->{t}{type2} = "reg"; }', }, ANSI_PORTS_SIGNAL_NAME => { # V2001 ansi ports VID => '$rs->{t}{name}=$match; $rs->{t}{line}=$line;', }, }; ############################################################ # debugEval ############################################################ $debugEval = { ANSI_PORTS_SIGNAL_NAME => { VID => 'print "Found $rs->{t}{type} $rs->{t}{name} $rs->{t}{range} [$line]\n";', }, SIGNAL_NAME => { VID => 'print "Found $rs->{t}{type} $rs->{t}{name} $rs->{t}{range} [$line]\n";', }, INST_BRACKET => { PORTS => 'print "found instance $rs->{t}{name} of $rs->{t}{mod} [$rs->{t}{line}]\n";', }, INST_NAMED_PORT_CON_AFTER => { COMMA => 'my @vidnames; foreach my $vid (@vids) {push @vidnames,$vid->{name};} print " Port $portName connected to ".join(",",@vidnames)."\n";', BRACKET => 'INST_NAMED_PORT_CON_AFTER:COMMA', }, INST_NUMBERED_PORT => { COMMA => 'INST_NAMED_PORT_CON_AFTER:COMMA', BRACKET => 'INST_NAMED_PORT_CON_AFTER:COMMA', }, }; ############################################################ # rvpEval ############################################################ $rvpEval = { MODULE => { ENDMODULE => 'if ((($rs->{p}{type} eq "primitive")&&($match ne "endprimitive"))|| (($rs->{p}{type} ne "primitive")&&($match eq "endprimitive"))){ $self->_add_warning("$file:$line: module of type". " $rs->{p}{type} ended by $match"); } $rs->{modules}{$rs->{module}}{end} = $line; $rs->{module} = ""; $rs->{files}{$file}{contexts}{$line}{value}= { name=>"",type=>"" }; $rs->{p}= undef;', PARAM => '$rs->{t} = { ptype => $match };', # parameter of localparam }, MODULE_NAME => { NAME => 'if (exists($rs->{modules}{$match})) { $nState = "IGNORE_MODULE"; $rs->{modules}{$match}{duplicate} = 1; $self->_add_warning("$file:$line ignoring new definition of ". "module $match, previous was at ". "$rs->{modules}{$match}{file}:$rs->{modules}{$match}{line})"); } else { $rs->{module}=$match; _init_module($rs->{modules},$rs->{module},$file,$line,$type); $rs->{files}{$file}{modules}{$rs->{module}} = $rs->{modules}{$rs->{module}}; _add_anchor($rs->{files}{$file}{anchors},$line,$rs->{module}); $rs->{files}{$file}{contexts}{$line}{value}= $rs->{p}= $rs->{modules}{$rs->{module}}; $rs->{files}{$file}{contexts}{$line}{module_start}= $rs->{module}; }', }, MODULE_PORTS => { VID => 'push(@{$rs->{p}{port_order}},$match);', }, FUNCTION => { NAME => '$rs->{function}=$match; $self->_init_t_and_f($rs->{modules}{$rs->{module}},"function", $rs->{function},$file,$line,$rs->{module}."_".$rs->{function}); _add_anchor($rs->{files}{$file}{anchors},$line,$rs->{module}."_".$rs->{function}); $rs->{files}{$file}{contexts}{$line}{value}= $rs->{p}= $rs->{modules}{$rs->{module}}{t_and_f}{$rs->{function}};', }, TASK => { NAME => '$rs->{task}=$match; $self->_init_t_and_f($rs->{modules}{$rs->{module}},"task", $rs->{task},$file,$line,$rs->{module}. "_" .$rs->{task}); _add_anchor($rs->{files}{$file}{anchors},$line,$rs->{module}. "_" . $rs->{task}); $rs->{files}{$file}{contexts}{$line}{value}= $rs->{p}= $rs->{modules}{$rs->{module}}{t_and_f}{$rs->{task}};', }, ENDTASK => { ENDTASK => '$rs->{modules}{$rs->{module}}{t_and_f}{$rs->{task}}{end} = $line; $rs->{task}=""; $rs->{files}{$file}{contexts}{$line}{value}= $rs->{p}= $rs->{modules}{$rs->{module}};', }, T_SIGNAL => { SIGNAL => '$rs->{t}={ type=>$match, range=>"", dimensions=>[], name=>"" , type2=>"" , block=>0};', ENDTASK => 'ENDTASK:ENDTASK', PARAM => 'MODULE:PARAM', # not realy needed yet because T/F parameters are ignored }, ENDFUNCTION => { ENDFUNCTION => '$rs->{modules}{$rs->{module}}{t_and_f}{$rs->{function}}{end} = $line; $rs->{function}=""; $rs->{files}{$file}{contexts}{$line}{value}= $rs->{p}= $rs->{modules}{$rs->{module}};', }, F_SIGNAL => { SIGNAL => '$rs->{t}={ type=>$match, range=>"", dimensions=>[], name=>"" , type2=>"",block=>0};', ENDFUNCTION => 'ENDFUNCTION:ENDFUNCTION', PARAM => 'MODULE:PARAM', # not realy needed yet because T/F parameters are ignored }, BLOCK_SIGNAL => { SIGNAL => '$rs->{t}={ type=>$match, range=>"", dimensions=>[], name=>"" , type2=>"" , block=>1};', }, PARAM_NAME => { NAME => 'if ( ($rs->{function} eq "") && ($rs->{task} eq "")) { # ignore parameters in tasks and functions $rs->{t}= { file => $file, line => $line , value => "" , ptype => $rs->{t}{ptype}}; # ptype is same as the last one push(@{$rs->{p}{parameter_order}}, $match) unless ($rs->{t}{ptype} eq "localparam"); $rs->{p}{parameters}{$match}=$rs->{t}; _add_anchor($rs->{files}{$file}{anchors},$line,""); }', }, PPL_PARAM => { PARAM => '$rs->{t} = { ptype => "parameter" };', # this can't be a localparam }, PPL_NAME => { NAME => 'PARAM_NAME:NAME', }, PARAM_AFTER_EQUALS => { COMMA => '$rs->{t}{value} = $fromLastPos;', SEMICOLON => 'PARAM_AFTER_EQUALS:COMMA', }, PPL_AFTER_EQUALS => { COMMA => 'PARAM_AFTER_EQUALS:COMMA', END => 'PARAM_AFTER_EQUALS:COMMA', }, ASSIGN => { VID => 'if ( exists($rs->{p}{signals}{$match}) && ($rs->{p}{signals}{$match}{a_line} == -1)) { $rs->{p}{signals}{$match}{a_line} = $line; $rs->{p}{signals}{$match}{a_file} = $file; _add_anchor($rs->{files}{$file}{anchors},$line,""); }', }, SIGNAL_NAME => { # note skip signals local to a block ({block}==1) VID => 'if ($rs->{t}{block} != 1) { $self->_init_signal($rs->{p}{signals},$match,$rs->{t}{type},$rs->{t}{type2}, $rs->{t}{range},$file,$line,1,$rs->{t}{dimensions}) && _add_anchor($rs->{files}{$file}{anchors},$line,""); }', }, SIGNAL_AFTER_NAME => { # don't assign a_line for reg at definition, as this is # only the initial value ASSIGN => 'if ($rs->{t}{block} != 1) { if ( $rs->{p}{signals}{$rs->{t}{name}}{type} ne "reg" ) { $rs->{p}{signals}{$rs->{t}{name}}{a_line}=$rs->{t}{line}; $rs->{p}{signals}{$rs->{t}{name}}{a_file}=$file; _add_anchor($rs->{files}{$file}{anchors},$rs->{t}{line},""); } }', }, INST_PARAM_VALUE => { # Note: the code is nearly the same in INST_PARAM_VALUE:COMMA, # and INST_PARAM_BRACKET:NO_BRACKET, but the first uses $fromLastPos # and the second uses $match to capture the parameter value COMMA => 'my $inst_num= $#{$rs->{p}{instances}}; $rs->{t}{params}{$rs->{t}{param_number}} = { file => $file , line => $line , value => $fromLastPos }; $rs->{t}{param_number}++;', END => 'INST_PARAM_VALUE:COMMA', }, INST_PARAM_BRACKET => { # Note: the code is nearly the same in INST_PARAM_VALUE:COMMA, # and INST_PARAM_BRACKET:NO_BRACKET, but the first uses $fromLastPos # and the second uses $match to capture the parameter value NO_BRACKET => 'my $inst_num= $#{$rs->{p}{instances}}; $rs->{t}{params}{$rs->{t}{param_number}} = { file => $file , line => $line , value => $match }; $rs->{t}{param_number}++;', }, INST_BRACKET => { PORTS => '$rs->{unres_mod}{$rs->{t}{mod}}=$rs->{t}{mod}; $rs->{files}{$file}{instance_lines}{$rs->{t}{line}} = $rs->{t}{mod}; push( @{$rs->{p}{instances}} , { module => $rs->{t}{mod} , inst_name => $rs->{t}{name} , file => $file, line => $rs->{t}{line}, parameters => $rs->{t}{params}, connections => {} }); _add_anchor($rs->{files}{$file}{anchors},$rs->{t}{line}, $rs->{module}."_".$rs->{t}{name});', }, INST_NAMED_PORT_CON_AFTER => { COMMA => 'my $inst_num= $#{$rs->{p}{instances}}; $rs->{p}{instances}[$inst_num]{connections}{$portName}=$fromLastPos; if ($portName =~ /^[0-9]/ ) { # clear named_ports flag if port is a number $rs->{p}{named_ports} = 0; } else { # remove the bracket from the end if a named port $rs->{p}{instances}[$inst_num]{connections}{$portName}=~s/\)\s*$//s; } foreach my $s (@vids) { $self->_init_signal($rs->{p}{signals},$s->{name},"wire","","",$file,$s->{line},0,$rs->{t}{dimensions}) && _add_anchor($rs->{files}{$file}{anchors},$s->{line},""); push( @{$rs->{p}{signals}{$s->{name}}{port_con}}, { port => $portName , line => $s->{line}, file => $file, module => $rs->{t}{mod} , inst => $rs->{t}{name} }); }', BRACKET => 'INST_NAMED_PORT_CON_AFTER:COMMA', }, INST_NUMBERED_PORT => { COMMA => 'INST_NAMED_PORT_CON_AFTER:COMMA', BRACKET => 'INST_NAMED_PORT_CON_AFTER:COMMA', }, IN_EVENT_BRACKET_EDGE => { VID => 'if (exists($rs->{p}{signals}{$match})) { $rs->{p}{signals}{$match}{$edgeType}=1; };', }, STMNT_ASSIGN_OR_TASK => { # copy of STMNT_ASSIGN EQUALS => 'foreach my $s (@vids) { my $sigp = undef; if ( exists($rs->{p}{signals}{$s->{name}} )) { $sigp = $rs->{p}{signals}{$s->{name}}; } elsif ( exists($rs->{p}{m_signals}) && exists($rs->{p}{m_signals}{$s->{name}}) ) { $sigp = $rs->{p}{m_signals}{$s->{name}}; } if (defined($sigp) && ($sigp->{a_line}==-1)) { $sigp->{a_line}=$s->{line}; $sigp->{a_file}=$file; _add_anchor($rs->{files}{$file}{anchors},$s->{line},""); } }', }, STMNT_ASSIGN => { # copy of STMNT_ASSIGN_OR_TASK EQUALS => 'STMNT_ASSIGN_OR_TASK:EQUALS', }, ANSI_PORTS_SIGNAL_NAME => { # V2001 ansi ports VID => '$self->_init_signal($rs->{p}{signals},$match,$rs->{t}{type},$rs->{t}{type2}, $rs->{t}{range},$file,$line,1,$rs->{t}{dimensions}); push(@{$rs->{p}{port_order}},$match) if exists $rs->{p}{port_order}; _add_anchor($rs->{files}{$file}{anchors},$line,"");', }, }; ############################################################ # language definition ############################################################ $vid_vnum_or_string = [ { arcName=> 'HVID', regexp=> '$HVID', nextState=> ['$ps->{curState}'] ,}, # hier id { arcName=> 'VID', regexp=> '$VID' , nextState=> ['$ps->{curState}'] ,}, { arcName=> 'NUMBER', regexp=> '$VNUM', nextState=> ['$ps->{curState}'] ,}, { arcName=> 'STRING', regexp=> '\\"', nextState=> ['IN_STRING','$ps->{curState}'],}, ]; $languageDef = [ { stateName => 'START', confusedNextState => 'START', search => [ { arcName => 'MODULE' , regexp => '\b(?:module|macromodule|primitive)\b', nextState => ['MODULE_NAME'] ,}, { arcName => 'CONFIG', regexp => '\bconfig\b', # V2001 nextState => ['CONFIG'] , }, { arcName => 'LIBRARY', regexp => '\blibrary\b', # V2001 nextState => ['LIBRARY'] , }, ], }, { stateName => 'MODULE', confusedNextState => 'MODULE', search => [ { arcName => 'ENDMODULE' , regexp => '\b(?:end(?:module|primitive))\b', nextState => ['START'] , }, { arcName => 'FUNCTION', regexp => '\bfunction\b', nextState => ['FUNCTION'] , }, { arcName => 'TASK', regexp => '\btask\b', nextState => ['TASK'] , }, { arcName => 'PARAM', regexp => '\b(?:parameter|localparam)\b', # v2001: localparm nextState => ['PARAM_TYPE','MODULE'] , }, { arcName => 'SPECIFY', regexp => '\bspecify\b', nextState => ['SPECIFY'] , }, { arcName => 'TABLE', regexp => '\btable\b', nextState => ['TABLE'] , }, { arcName => 'EVENT_DECLARATION' , regexp => '\bevent\b' , nextState => ['EVENT_DECLARATION'] , }, { arcName => 'DEFPARAM' , regexp => '\bdefparam\b' , nextState => ['DEFPARAM'] , }, { arcName => 'GATE' , regexp => "$verilog_gatetype_regexp" , nextState => ['GATE'] , }, { arcName => 'ASSIGN' , regexp => '\bassign\b' , nextState => ['ASSIGN'] , }, { arcName => 'SIGNAL' , regexp => "$verilog_sigs_regexp" , nextState => ['DRIVE_STRENGTH','MODULE'] , }, { arcName => 'INITIAL_OR_ALWAYS', regexp => '\b(?:initial|always)\b' , nextState => ['STMNT','MODULE'] , }, { arcName => 'GENERATE', regexp => '\bgenerate\b', # V2001 nextState => ['GENERATE'] , }, { arcName => 'INST', regexp => '$VID' , nextState => ['INST_PARAM'] , }, # don't put any more states here because $VID matches almost anything ], },################ END OF MODULE STATE { stateName => 'MODULE_NAME', search => # $nState is usually MODULE_PPL, but is set to # IGNORE_MODULE when a duplicate module is found [ { arcName => 'NAME', regexp => '$VID' , nextState => ['$nState'] , }, ], }, { stateName => 'IGNORE_MODULE' , # just look for endmodule allowAnything => 1, search => [ { arcName => 'ENDMODULE' , regexp => '\bendmodule\b', nextState => ['START'], }, @$vid_vnum_or_string, ], }, { stateName => 'MODULE_PPL' , # v2001 module_parameter_port_list (A.1.3) failNextState => ['MODULE_PORTS'], search => [ { regexp => '#', nextState => ['PPL_BRACKET'], }, ], }, { stateName => 'MODULE_PORTS' , # just look for signals until ; allowAnything => 1, search => [ { arcName => 'TYPE' , regexp => '\b(?:input|output|inout)\b', # V2001 ansi ports nextState => ['ANSI_PORTS_TYPE','MODULE'], resetPos => 1, }, { arcName => 'END', regexp => ';' , nextState => ['MODULE'] , }, @$vid_vnum_or_string, ], }, { stateName => 'FUNCTION' , search => [ { arcName => 'RANGE', regexp => '\[', nextState => ['IN_RANGE','FUNCTION'] , }, { arcName => 'TYPE', regexp => '\b(?:real|integer|time|realtime)\b', nextState => ['FUNCTION'] , }, { arcName => 'SIGNED', regexp => '\bsigned\b' ,nextState => ['FUNCTION'] , }, # V2001 { arcName => 'AUTO', regexp => '\bautomatic\b' ,nextState => ['FUNCTION'] , }, # V2001 { arcName => 'NAME', regexp => '$VID' , nextState => ['FUNCTION_AFTER_NAME'] , }, ], }, { stateName => 'FUNCTION_AFTER_NAME' , search => [ { arcName => 'SEMICOLON', regexp => ';', nextState => ['F_SIGNAL'] , }, { arcName => 'BRACKET', regexp => '\(' , # V2001 nextState => ['ANSI_PORTS_TYPE','F_SIGNAL'] , }, ], }, { stateName => 'TASK' , search => [ { arcName => 'AUTO', regexp => '\bautomatic\b', nextState => ['TASK'],}, # V2001 { arcName => 'NAME', regexp => '$VID', nextState => ['TASK_AFTER_NAME'],},], }, { stateName => 'TASK_AFTER_NAME' , search => [ { arcName => 'SEMICOLON', regexp => ';', nextState => ['T_SIGNAL'] , }, { arcName => 'BRACKET', regexp => '\(' , # V2001 nextState => ['ANSI_PORTS_TYPE','T_SIGNAL'] , }, ], }, { stateName => 'T_SIGNAL' , failNextState => ['STMNT','ENDTASK'], search => [ { arcName => 'ENDTASK', regexp => '\bendtask\b', nextState => ['MODULE'] , }, { arcName => 'SIGNAL' , regexp => "$verilog_sigs_regexp" , nextState => ['DRIVE_STRENGTH','T_SIGNAL'] , }, { arcName => 'PARAM', regexp => '\b(?:parameter|localparam)\b', # v2001: localparm nextState => ['PARAM_TYPE','T_SIGNAL'] , }, ], }, { stateName => 'ENDTASK', search => [ { arcName => 'ENDTASK', regexp => '\bendtask\b', nextState => ['MODULE'] , }, ], }, { stateName => 'F_SIGNAL' , failNextState => ['STMNT','ENDFUNCTION'], search => [ { arcName => 'ENDFUNCTION', regexp => '\bendfunction\b', nextState => ['MODULE'] , }, { arcName => 'SIGNAL' , regexp => "$verilog_sigs_regexp" , nextState => ['DRIVE_STRENGTH','F_SIGNAL'] , }, { arcName => 'PARAM', regexp => '\b(?:parameter|localparam)\b', # v2001: localparm nextState => ['PARAM_TYPE','F_SIGNAL'] , }, ], }, { stateName => 'ENDFUNCTION', search => [ { arcName => 'ENDFUNCTION', regexp => '\bendfunction\b', nextState => ['MODULE'] , }, ], }, { stateName => 'PARAM_TYPE', failNextState => ['PARAM_NAME'], search => [ { arcName => 'RANGE', regexp => '\[' , nextState => ['IN_RANGE','PARAM_NAME'] , }, { arcName => 'SIGNED', regexp => '\bsigned\b' , nextState => ['PARAM_TYPE'] , }, # may be followed by a range { arcName => 'OTHER', regexp => '\b(?:integer|real|realtime|time)\b' , nextState => ['PARAM_NAME'] , }, ], }, { stateName => 'PARAM_NAME', search => [ { arcName => 'NAME', regexp => '$VID' , nextState => ['PARAMETER_EQUAL','PARAM_AFTER_EQUALS'] , }, ], }, { stateName => 'PARAMETER_EQUAL', search => [ { regexp => '=' , storePos => 1, }, ] }, { stateName => 'PARAM_AFTER_EQUALS', allowAnything => 1, search => [ { arcName => 'CONCAT', regexp => '\{' , nextState => ['IN_CONCAT','PARAM_AFTER_EQUALS'] , }, { arcName => 'COMMA', regexp => ',' , nextState => ['PARAM_NAME'] , }, { arcName => 'SEMICOLON', regexp => ';' , }, @$vid_vnum_or_string, ] }, { stateName => 'IN_CONCAT', allowAnything => 1, search => [ { arcName => 'CONCAT' , regexp => '\{' , nextState => ['IN_CONCAT','IN_CONCAT'] , }, { arcName => 'END' , regexp => '}' , }, # pop up @$vid_vnum_or_string, ] }, { stateName => 'IN_RANGE', allowAnything => 1, search => [ { arcName => 'RANGE' , regexp => '\[' , nextState => ['IN_RANGE','IN_RANGE'] , }, { arcName => 'END' , regexp => '\]' , }, # pop up @$vid_vnum_or_string, ] }, { stateName => 'IN_SIG_RANGE', # just like in range, but stores allowAnything => 1, search => [ { arcName => 'RANGE' , regexp => '\[' , nextState => ['IN_SIG_RANGE','IN_SIG_RANGE'] , }, { arcName => 'END' , regexp => '\]' , }, # pop up @$vid_vnum_or_string, ] }, { stateName => 'IN_MEM_RANGE', # just like in range, but stores allowAnything => 1, search => [ { arcName => 'RANGE' , regexp => '\[' , nextState => ['IN_MEM_RANGE','IN_MEM_RANGE'] , }, { arcName => 'END' , regexp => '\]' , }, # pop up @$vid_vnum_or_string, ] }, { stateName => 'IN_BRACKET', allowAnything => 1, search => [ { arcName => 'BRACKET' , regexp => '\(' , nextState => ['IN_BRACKET','IN_BRACKET'] , }, { arcName => 'END' , regexp => '\)' , }, # pop up @$vid_vnum_or_string, ] }, { stateName => 'IN_STRING', allowAnything => 1, search => [ # note: put \" in regexp so that emacs colouring doesn't get confused { arcName => 'ESCAPED_QUOTE' , regexp => '\\\\\\"' , # match \" nextState => ['IN_STRING'] , }, # match \\ (to make sure that \\" does not match \" { arcName => 'ESCAPE' , regexp => '\\\\\\\\' , nextState => ['IN_STRING'] , }, { arcName => 'END' , regexp => '\\"' , }, # match " and pop up ] }, { stateName => 'SPECIFY', allowAnything => 1, search => [ { regexp => '\bendspecify\b' , nextState => ['MODULE'] ,}, @$vid_vnum_or_string,], }, { stateName => 'TABLE', allowAnything => 1, search => [ { regexp => '\bendtable\b' , nextState => ['MODULE'] ,}, @$vid_vnum_or_string,], }, { stateName => 'EVENT_DECLARATION' , # just look for ; allowAnything => 1, search => [ { regexp => ';' , nextState => ['MODULE'] , }, @$vid_vnum_or_string,], }, { stateName => 'DEFPARAM' , # just look for ; allowAnything => 1, search => [ { regexp => ';' , nextState => ['MODULE'] , }, @$vid_vnum_or_string,], }, { # REVISIT: could find signal driven by gate here (is output always the first one??) stateName => 'GATE' , allowAnything => 1, search => [ { regexp => ';' , nextState => ['MODULE'] , }, @$vid_vnum_or_string,], }, { stateName => 'ASSIGN', allowAnything => 1, search => [ { arcName => 'RANGE' , regexp => '\[' , nextState => ['IN_RANGE','ASSIGN'] , }, { arcName => 'EQUALS' , regexp => '=' , nextState => ['ASSIGN_AFTER_EQUALS'] , }, @$vid_vnum_or_string, ] }, { stateName => 'ASSIGN_AFTER_EQUALS' , allowAnything => 1, search => [ { arcName=>'COMMA', regexp => ',', nextState => ['ASSIGN'],}, { arcName=>'CONCAT', regexp => '\{', nextState => ['IN_CONCAT','ASSIGN_AFTER_EQUALS'],}, # don't get confused by function calls (which can also contain commas) { arcName=>'BRACKET', regexp => '\(', nextState => ['IN_BRACKET','ASSIGN_AFTER_EQUALS'],}, { arcName=>'END', regexp => ';', nextState => ['MODULE'],}, @$vid_vnum_or_string, ], }, { stateName => 'DRIVE_STRENGTH', # signal defs - drive strength or charge strength failNextState => ['SCALARED_OR_VECTORED'], search => [ { regexp => '\(', nextState => ['IN_BRACKET','SCALARED_OR_VECTORED'],}], }, { # REVISIT: V2001 - the name of this is misleading now stateName => 'SCALARED_OR_VECTORED', # for signal defs failNextState => ['SIGNAL_RANGE'], search => [ { regexp => '\b(?:scalared|vectored)\b', nextState => ['SIGNAL_RANGE'],}, { arcName => 'TYPE' , regexp => "$verilog_sigs_regexp", # V2001 nextState => ['SCALARED_OR_VECTORED'],}, { regexp => '\b(?:signed)\b', nextState => ['SCALARED_OR_VECTORED'],},], # V2001 }, { stateName => 'SIGNAL_RANGE', # for signal defs failNextState => ['SIGNAL_DELAY'], search => [ { regexp => '\[', nextState => ['IN_SIG_RANGE','SIGNAL_DELAY'], storePos => 1,}, ], }, { stateName => 'SIGNAL_DELAY', # for signal defs failNextState => ['SIGNAL_NAME'], search => [ { regexp => '\#', nextState => ['DELAY_VALUE','SIGNAL_NAME'],}, ], }, { stateName => 'SIGNAL_NAME', # for signal defs search => [ { arcName => 'VID' , regexp => '$VID', nextState => ['SIGNAL_AFTER_NAME'], }, ], }, { # for signal defs stateName => 'SIGNAL_AFTER_NAME', search => [ { regexp => ',', nextState => ['SIGNAL_NAME'],}, { regexp => '\[', nextState => ['IN_MEM_RANGE','SIGNAL_AFTER_NAME'], storePos => 1 , }, # memories { arcName => 'SEMICOLON' , regexp => ';',}, # pop up { arcName => 'ASSIGN', regexp => '=', nextState => ['SIGNAL_AFTER_EQUALS'],} ], }, { stateName => 'SIGNAL_AFTER_EQUALS' , allowAnything => 1, search => [ { regexp => ',', nextState => ['SIGNAL_NAME'],}, { regexp => '\{', nextState => ['IN_CONCAT','SIGNAL_AFTER_EQUALS'],}, { regexp => '\(', nextState => ['IN_BRACKET','SIGNAL_AFTER_EQUALS'],}, { arcName => 'END', regexp => ';', }, # pop up @$vid_vnum_or_string, ], }, { stateName => 'INST_PARAM', failNextState => ['INST_NAME'], search => [ { regexp => '\#', nextState=> ['INST_PARAM_BRACKET'],},], }, { stateName => 'INST_PARAM_BRACKET', search => [ { arcName => 'BRACKET' , regexp => '\(', storePos => 1, nextState=> ['INST_PARAM_VALUE'],}, # this is here to catch and illegal case which DC accepts { arcName => 'NO_BRACKET' , regexp => '($VID|$VNUM)', nextState=> ['INST_NAME'],}, ], }, { stateName => 'INST_PARAM_VALUE', allowAnything => 1, search => [ { regexp => '\(', nextState=> ['IN_BRACKET','INST_PARAM_VALUE'],}, { regexp => '\[', nextState => ['IN_RANGE','INST_PARAM_VALUE'],}, { regexp => '\{', nextState => ['IN_CONCAT','INST_PARAM_VALUE'],}, { arcName => 'COMMA' , regexp => ',', storePos => 1, nextState=> ['INST_PARAM_VALUE'],}, { arcName => 'END' , regexp => '\)', nextState=> ['INST_NAME'],}, ], }, { stateName => 'INST_NAME', failNextState => ['INST_BRACKET'], search => [ { arcName => 'VID' , regexp => '$VID', nextState => ['INST_RANGE'], }, ], }, { stateName => 'INST_NO_NAME' , allowAnything => 1, search => [ { regexp => ';' , }, @$vid_vnum_or_string,], }, { stateName => 'INST_RANGE', failNextState => ['INST_BRACKET'], search => [ { regexp => '\[', nextState => ['IN_RANGE','INST_BRACKET'],}, ], }, { stateName => 'INST_BRACKET', search => [ { arcName => 'PORTS' , regexp => '\(', nextState => ['INST_PORTS'],},], }, { stateName => 'INST_PORTS', failNextState => ['INST_NUMBERED_PORT'], failStorePos => 1, search => [ { arcName => 'COMMA', regexp => ',', nextState => ['INST_PORTS'], }, { regexp => '\.', nextState => ['INST_PORT_NAME'], }, { regexp => '\)', nextState => ['AFTER_INST'], }, ], }, { stateName => 'INST_PORT_NAME', search => [ { arcName => 'NAME' , regexp => '$VID', nextState => ['INST_NAMED_PORT_BRACKET','INST_NAMED_PORT_CON', 'INST_NAMED_PORT_CON_AFTER'], }, ], }, { stateName => 'INST_NAMED_PORT_BRACKET', search => [ { regexp => '\(' , storePos => 1, },] }, { stateName => 'INST_NAMED_PORT_CON', allowAnything => 1, search => [ { regexp => '\[' , nextState => ['IN_RANGE','INST_NAMED_PORT_CON'] , }, { regexp => '\{' , nextState => ['IN_CONCAT','INST_NAMED_PORT_CON'] , }, { regexp => '\(' , nextState => ['INST_NAMED_PORT_CON','INST_NAMED_PORT_CON'], }, { arcName => 'END', regexp => '\)' , }, # pop up @$vid_vnum_or_string, ] }, { stateName => 'INST_NAMED_PORT_CON_AFTER', search => [ { arcName => 'BRACKET', regexp => '\)' , nextState => ['AFTER_INST']}, { arcName => 'COMMA' , regexp => ',' , nextState => ['INST_DOT']}, ] }, { stateName => 'INST_DOT', search => [ { regexp => '\.' , nextState => ['INST_PORT_NAME']}, { regexp => ',' , nextState => ['INST_DOT']}, # blank port ] }, { stateName => 'INST_NUMBERED_PORT', allowAnything => 1, search => [ { regexp => '\[', nextState => ['IN_RANGE','INST_NUMBERED_PORT'],}, { regexp => '\{', nextState => ['IN_CONCAT','INST_NUMBERED_PORT'],}, { regexp => '\(', nextState => ['IN_BRACKET','INST_NUMBERED_PORT'],}, { arcName => 'BRACKET' , regexp => '\)', nextState => ['AFTER_INST'], }, { arcName => 'COMMA' , regexp => ',' , nextState => ['INST_NUMBERED_PORT'], storePos => 1, }, @$vid_vnum_or_string, ] }, { stateName => 'AFTER_INST', search => [ { arcName => 'SEMICOLON', regexp => ';', nextState => ['MODULE'], }, { arcName => 'COMMA', regexp => ',', nextState => ['INST_NAME'], }, ] }, { stateName => 'STMNT', search => [ { arcName => 'IF', regexp => '\bif\b' , nextState => ['BRACKET','IN_BRACKET','STMNT','MAYBE_ELSE'] ,}, { arcName => 'REPEAT_WHILE_FOR_WAIT', regexp => '\b(?:repeat|while|for|wait)\b' , nextState => ['BRACKET','IN_BRACKET','STMNT'] , }, { arcName => 'FOREVER', regexp => '\bforever\b' , nextState => ['STMNT'] , }, { arcName => 'CASE', regexp => '\bcase[xz]?\b' , nextState => ['BRACKET','IN_BRACKET','CASE_ITEM'] , }, { arcName => 'BEGIN', regexp => '\bbegin\b' , nextState => ['BLOCK_NAME','IN_SEQ_BLOCK'] , }, { arcName => 'FORK', regexp => '\bfork\b' , nextState => ['BLOCK_NAME','IN_PAR_BLOCK'] , }, { arcName => 'DELAY', regexp => '\#' , nextState => ['DELAY_VALUE','STMNT'] , }, { arcName => 'EVENT_CONTROL', regexp => '\@' , nextState => ['EVENT_CONTROL'] , }, { arcName => 'SYSTEM_TASK', regexp => '\$$VID' , nextState => ['SYSTEM_TASK'] , }, { arcName => 'DISABLE_ASSIGN_DEASSIGN_FORCE_RELEASE', regexp => '\b(?:disable|assign|deassign|force|release)\b', nextState => ['STMNT_JUNK_TO_SEMICOLON'] , }, # just throw stuff away # a assignment to a hierarchical thing mustn't collect the vid # like a normal assign as hierarchical nets/signals will confuse downstream code { arcName => 'HIER_ASSIGN_OR_TASK', regexp => '$HVID' , nextState => ['STMNT_ASSIGN_OR_TASK'] , }, { arcName => 'ASSIGN_OR_TASK', regexp => '$VID' , nextState => ['STMNT_ASSIGN_OR_TASK'] , }, { arcName => 'CONCAT', regexp => '\{' , nextState => ['IN_CONCAT','STMNT_ASSIGN'] , }, { arcName => 'NULL', regexp => ';' , }, # pop up { arcName => 'POINTY_THING', regexp => '->' , # not sure what this is! nextState => ['POINTY_THING_NAME'] , }, ], }, { stateName => 'MAYBE_ELSE', failNextState => [] , # don't get confused, just pop the stack for the next state search => [{ arcName => 'ELSE', regexp => '\belse\b' , nextState => ['STMNT'],},] }, { stateName => 'BLOCK_NAME', failNextState => [] , # don't get confused, just pop the stack for the next state search => [{ arcName => 'COLON', regexp => ':' , nextState => ['BLOCK_NAME_AFTER_COLON'] ,},] }, { stateName => 'BLOCK_NAME_AFTER_COLON', search => [ { arcName => 'VID', regexp => '$VID' , nextState => ['BLOCK_SIGNAL'],}, ] }, { stateName => 'BLOCK_SIGNAL' , failNextState => [], # don't get confused, just pop the stack for the next state search => [ { arcName => 'SIGNAL' , regexp => "$verilog_sigs_regexp" , nextState => ['DRIVE_STRENGTH','BLOCK_SIGNAL'] , }, ], }, { stateName => 'IN_SEQ_BLOCK', failNextState => ['STMNT','IN_SEQ_BLOCK'] , search => [{ arcName => 'END', regexp => '\bend\b' , }, ] }, { stateName => 'IN_PAR_BLOCK', failNextState => ['STMNT','IN_PAR_BLOCK'] , search => [{ arcName => 'JOIN', regexp => '\bjoin\b' , }, ] }, { stateName => 'DELAY_VALUE', search => [{ arcName => 'NUMBER', regexp => '$VNUM', nextState => ['DELAY_COLON1'] }, { arcName => 'ID', regexp => '$VID', nextState => ['DELAY_COLON1'], }, { arcName => 'BRACKET', regexp => '\(', nextState => ['IN_BRACKET','DELAY_COLON1'],},] }, { stateName => 'DELAY_COLON1', failNextState => [] , # popup search => [{ arcName => 'COLON', regexp => ':' , nextState => ['DELAY_VALUE2'] },] }, { stateName => 'DELAY_VALUE2', search => [{ arcName => 'NUMBER', regexp => '$VNUM', nextState => ['DELAY_COLON2'] }, { arcName => 'ID', regexp => '$VID', nextState => ['DELAY_COLON2'], }, { arcName => 'BRACKET', regexp => '\(', nextState => ['IN_BRACKET','DELAY_COLON2'],},] }, { stateName => 'DELAY_COLON2', search => [{ arcName => 'COLON', regexp => ':' , nextState => ['DELAY_VALUE3'] },] }, { stateName => 'DELAY_VALUE3', search => [{ arcName => 'NUMBER', regexp => '$VNUM', }, { arcName => 'ID', regexp => '$VID', }, { arcName => 'BRACKET', regexp => '\(', nextState => ['IN_BRACKET'],}, ] }, { stateName => 'EVENT_CONTROL', search => [ { arcName => 'ID', regexp => '(?:$HVID|$VID)', nextState => ['STMNT'], }, { arcName => 'STAR', regexp => '\*', nextState => ['STMNT'], }, # V2001 { arcName => 'BRACKET', regexp => '\(', nextState => ['IN_EVENT_BRACKET','STMNT'], }, ] }, { stateName => 'IN_EVENT_BRACKET', allowAnything => 1, search => [ # must go before vid_vnum_or_string as posedge and negedge look like VIDs { arcName => 'EDGE' , regexp => '\b(?:posedge|negedge)\b' , nextState => ['IN_EVENT_BRACKET_EDGE'] , }, { arcName => 'BRACKET' , regexp => '\(' , nextState => ['IN_EVENT_BRACKET','IN_EVENT_BRACKET'] , }, { arcName => 'STAR', regexp => '\*', nextState => ['IN_EVENT_BRACKET'], }, # V2001 { arcName => 'END' , regexp => '\)' , }, # popup @$vid_vnum_or_string, ] }, { # in theory there could be an expression here, I just take the first VID stateName => 'IN_EVENT_BRACKET_EDGE', failNextState => ['IN_EVENT_BRACKET'] , search => [{ arcName => 'VID', regexp => '$VID', nextState => ['IN_EVENT_BRACKET'],},], }, { stateName => 'STMNT_ASSIGN_OR_TASK', failNextState => ['STMNT_SEMICOLON'], search => [ { arcName => 'EQUALS', regexp => '[<]?=', nextState => ['STMNT_JUNK_TO_SEMICOLON'], }, { arcName => 'RANGE', regexp => '\[', nextState => ['IN_RANGE','STMNT_ASSIGN'],}, { arcName => 'BRACKET', regexp => '\(', # task with params nextState => ['IN_BRACKET','STMNT_SEMICOLON'], }, ] }, { stateName => 'STMNT_ASSIGN', search => [ { arcName => 'EQUALS', regexp => '[<]?=', nextState => ['STMNT_JUNK_TO_SEMICOLON'],}, { arcName => 'RANGE', regexp => '\[', nextState => ['IN_RANGE','STMNT_ASSIGN'],}, ], }, { stateName => 'SYSTEM_TASK', failNextState => ['STMNT_SEMICOLON'], search => [ { arcName => 'BRACKET', regexp => '\(', nextState => ['IN_BRACKET','STMNT_SEMICOLON'], }, ], }, { stateName => 'POINTY_THING_NAME', search => [{ arcName => 'VID', regexp => '(?:$HVID|$VID)', nextState => ['STMNT_SEMICOLON'], }, ], }, { stateName => 'CASE_ITEM', allowAnything => 1, search => [ { arcName => 'END', regexp => '\bendcase\b', }, { arcName => 'COLON', regexp => ':', nextState => ['STMNT','CASE_ITEM'], }, { arcName => 'DEFAULT', regexp => '\bdefault\b', nextState => ['MAYBE_COLON','STMNT','CASE_ITEM'], }, # don't get confused by colons in ranges { arcName => 'RANGE', regexp => '\[', nextState => ['IN_RANGE','CASE_ITEM'], }, @$vid_vnum_or_string, ], }, { stateName => 'MAYBE_COLON', failNextState => [], search => [ { regexp => ':' , }, ] }, { # look for ; but also allow the ending of a statement with an end # even though it is not really legal (verilog seems to accept it, so I do too) stateName => 'STMNT_JUNK_TO_SEMICOLON' , allowAnything => 1, search => [ { regexp => ';' , }, # popup and reset pos to before the end/join cope with nosemicolon case { regexp => '\b(?:end|join|endtask|endfunction)\b' , resetPos => 1, }, @$vid_vnum_or_string, ], }, { stateName => 'STMNT_SEMICOLON', search => [ { regexp => ';' , }, # popup and reset pos to before the end/join cope with nosemicolon case { regexp => '\b(?:end|join|endtask|endfunction)\b' , resetPos => 1, }, ], }, { stateName => 'BRACKET', search => [ { regexp => '\(' , },] }, { stateName => 'SEMICOLON', search => [ { regexp => ';' , },] }, # V2001 { stateName => 'CONFIG', allowAnything => 1, search => [ { regexp => '\bendconfig\b' , nextState => ['START'] ,}, @$vid_vnum_or_string,], }, { stateName => 'LIBRARY' , # just look for ; allowAnything => 1, search => [ { regexp => ';' , nextState => ['START'] , }, @$vid_vnum_or_string,], }, { stateName => 'GENERATE', allowAnything => 1, search => [ { regexp => '\bendgenerate\b' , nextState => ['MODULE'] ,}, @$vid_vnum_or_string,], }, { # V2001 ansi module ports stateName => 'ANSI_PORTS_TYPE', failNextState => ['ANSI_PORTS_TYPE2'], search => [ { arcName => 'TYPE' , regexp => '\b(?:input|output|inout)\b', nextState => ['ANSI_PORTS_TYPE2'],}, # a null list. note this is only possible for a task or function # (a null module port list can't look like an ansi port list) # but it is not legal acording to the BNF. I allow it any way. { regexp => '\)', nextState => ['SEMICOLON'], }, ], }, { # V2001 ansi module ports stateName => 'ANSI_PORTS_TYPE2', failNextState => ['ANSI_PORTS_SIGNAL_RANGE'], search => [ { arcName => 'TYPE' , regexp => "$verilog_sigs_regexp", nextState => ['ANSI_PORTS_TYPE2'],}, { regexp => '\b(?:signed)\b', nextState => ['ANSI_PORTS_TYPE2'],},], }, { # V2001 ansi module ports stateName => 'ANSI_PORTS_SIGNAL_RANGE', # for signal defs failNextState => ['ANSI_PORTS_SIGNAL_NAME'], search => [ { regexp => '\[', nextState => ['IN_SIG_RANGE','ANSI_PORTS_SIGNAL_NAME'], storePos => 1,}, ], }, { # V2001 ansi module ports stateName => 'ANSI_PORTS_SIGNAL_NAME', search => [ { arcName => 'TYPE' , regexp => '\b(?:input|output|inout)\b', nextState => ['ANSI_PORTS_TYPE'], resetPos => 1, }, { arcName => 'VID' , regexp => '$VID', nextState => ['ANSI_PORTS_SIGNAL_AFTER_NAME'], }, ], }, { # V2001 ansi module ports stateName => 'ANSI_PORTS_SIGNAL_AFTER_NAME', search => [ { regexp => ',', nextState => ['ANSI_PORTS_SIGNAL_NAME'],}, { regexp => '\[', nextState => ['IN_MEM_RANGE','ANSI_PORTS_SIGNAL_AFTER_NAME'],}, # memories { regexp => '\)', nextState => ['SEMICOLON'], } # semicolon, then pop up ], }, { # v2001 module_parameter_port_list (A.1.3) stateName => 'PPL_BRACKET' , search => [ { regexp => '\(', nextState => ['PPL_PARAM'], }, ], }, { # v2001 module_parameter_port_list (A.1.3) stateName => 'PPL_PARAM' , search => [ { arcName=>'PARAM', regexp=>'\bparameter\b', nextState => ['PPL_TYPE'],},], }, { # v2001 module_parameter_port_list (A.1.3) stateName => 'PPL_TYPE', failNextState => ['PPL_NAME'], search => [ { arcName => 'RANGE', regexp => '\[' , nextState => ['IN_RANGE','PPL_NAME'] , }, { arcName => 'SIGNED', regexp => '\bsigned\b' , nextState => ['PPL_TYPE'] , }, # may be followed by a range { arcName => 'OTHER', regexp => '\b(?:integer|real|realtime|time)\b' , nextState => ['PPL_NAME'] , }, ], }, { # v2001 module_parameter_port_list (A.1.3) stateName => 'PPL_NAME', search => [ { arcName => 'NAME', regexp => '$VID' , nextState => ['PARAMETER_EQUAL','PPL_AFTER_EQUALS'] , }, ], }, { # v2001 module_parameter_port_list (A.1.3) stateName => 'PPL_AFTER_EQUALS', allowAnything => 1, search => [ { arcName => 'CONCAT', regexp => '\{' , nextState => ['IN_CONCAT','PPL_AFTER_EQUALS'] , }, { arcName => 'BRACKET', regexp => '\(' , nextState => ['IN_BRACKET','PPL_AFTER_EQUALS'] , }, { arcName => 'COMMA', regexp => ',' , nextState => ['PPL_PARAM_OR_NAME'] , }, { arcName => 'END', regexp => '\)' , nextState => ['MODULE_PORTS'] , }, @$vid_vnum_or_string, ] }, { # v2001 module_parameter_port_list (A.1.3) stateName => 'PPL_PARAM_OR_NAME' , failNextState => ['PPL_NAME'], search => [ { regexp => '\bparameter\b', nextState => ['PPL_TYPE'], }, ], }, ]; } ############################################################ # make the parser, and return it as a string ############################################################ sub _make_parser { my ($evalDefs,$genDebugCode) = @_; _check_data_structures($evalDefs); my $perlCode; # the perl code we are making my $debugPrint = $genDebugCode ? 'print "---- $ps->{curState} $file:$line (".pos($code).")\\n" if defined $ps->{curState} && defined pos($code);':''; # vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv $perlCode .= <{curState})){ \$ps->{curState} = undef; \$ps->{prevState}= undef; \$ps->{nextStateStack}= ["START"]; \$ps->{storing}= 0; \$ps->{stored}= ""; \$ps->{confusedNextState}= "START"; } my \$storePos = -1; my \$lastPos = 0; my \$posMark; my \$fromLastPos; PARSE_LINE_LOOP: while (1) { \$lastPos = pos(\$code) if (defined(pos(\$code))); if ( \$code =~ m/\\G\\s*\\Z/gs ) { last PARSE_LINE_LOOP; } else { pos(\$code) = \$lastPos; } \$code =~ m/\\G\\s*/gs ; # skip any whitespace \$ps->{prevState} = \$ps->{curState}; \$ps->{curState} = pop(\@{\$ps->{nextStateStack}}) or die "Error: No next state after \$ps->{prevState} ". "\$file line \$line :\n \$code"; $debugPrint goto \$ps->{curState}; die \"Confused: Bad state \$ps->{curState}\"; CONFUSED: \$posMark = ''; # make the position marker: tricky because code can contain tabs # which we want to match in the blank space before the ^ \$posMark = substr(\$code,0,\$lastPos); \$posMark =~ tr/\t/ /c ; # turn anything that isn't a tab into a space \$posMark .= "^" ; if (substr(\$code,length(\$code)-1,1) ne "\\n") { \$posMark="\\n".\$posMark; } \$self->_add_confused("\$file:\$line: in state \$ps->{prevState}:\\n". "\$code".\$posMark); \@{\$ps->{nextStateStack}} = (\$ps->{confusedNextState}); return; # ignore the rest of the line EOF # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ foreach my $state (@$languageDef) { my $stateName = $state->{stateName}; my $allowAnything = exists($state->{allowAnything}) && $state->{allowAnything}; my $re = $allowAnything ? '' : '\G'; # allowAnything==0 forces a match # where we left off last time $perlCode.= " $stateName:\n"; if (exists($state->{confusedNextState})) { $perlCode.= " \$ps->{confusedNextState}=\"$state->{confusedNextState}\";\n"; } if (exists($state->{search})) { my @searchTerms=(); foreach my $search (@{$state->{search}}) { push @searchTerms, $search->{regexp}; } $re .= "(?:(". join(")|(",@searchTerms)."))"; my $failNextState=''; if (exists($state->{failNextState})) { if (scalar(@{$state->{failNextState}}) != 0) { $failNextState="\"". join('","',reverse(@{$state->{failNextState}})). "\""; } # else leave it set at nothing - means just popup } else { $failNextState='"CONFUSED"'; } $perlCode.= " if (\$code =~ m/$re/gos) {\n"; my $elsif2="if"; my $i=0; foreach my $search (@{$state->{search}}) { $i++; my $arcName = exists($search->{arcName}) ? $search->{arcName} : ''; $perlCode.= " $elsif2 (defined(\$$i)) {\n"; if ($genDebugCode) { $perlCode.=" print \"---- -$arcName (\$$i)->\\n\";\n"; $perlCode.=" \$takenArcs->{'$stateName'}{$i}++;\n"; } $elsif2="elsif"; if (exists($search->{resetPos}) && $search->{resetPos}) { $perlCode.=" pos(\$code)=pos(\$code)-length(\$$i);\n"; } if (exists($search->{arcName})) { $perlCode.= # " " . _make_eval_code($evalDefs,$stateName, $search->{arcName},$i,$genDebugCode); } if (exists $search->{nextState}) { $perlCode.= " push (\@{\$ps->{nextStateStack}}, \"". join('","',reverse(@{$search->{nextState}}))."\");\n"; } if (exists($search->{storePos}) && $search->{storePos}) { $perlCode.= " \$ps->{storing} == 0 or\n"; $perlCode.= " die \"Setting storing ". "flag when it is already set: $stateName:$arcName\";\n"; $perlCode.= " \$storePos = pos(\$code);\n"; $perlCode.= " \$ps->{storing} = 1;\n"; $perlCode.= " \$ps->{stored} = '';\n"; } $perlCode.= " }\n"; } $perlCode.= " }\n"; if ($allowAnything) { $perlCode.= " else { ". "push(\@{\$ps->{nextStateStack}},\"$stateName\"); last PARSE_LINE_LOOP; }\n"; } else { $perlCode.= " else {\n"; if (exists($state->{failStorePos}) && $state->{failStorePos}) { $perlCode.= " \$ps->{storing} == 0 or\n"; $perlCode.= " die \"Setting storing ". "flag when it is already set: $stateName:fail\";\n"; #NB:uses lastPos here because there was no match, so can't # use pos(code) $perlCode.= " \$storePos = \$lastPos;\n"; $perlCode.= " \$ps->{storing} = 1;\n"; $perlCode.= " \$ps->{stored} = '';\n"; } if ($failNextState) { $perlCode.="push(\@{\$ps->{nextStateStack}},$failNextState);"; } $perlCode.= " pos(\$code)=\$lastPos; }\n"; } } $perlCode.= " next PARSE_LINE_LOOP;\n"; } $perlCode.= " }\n"; $perlCode.= " if (\$storePos!=-1) { \$ps->{stored}=substr(\$code,\$storePos);}\n"; $perlCode.= " elsif ( \$ps->{storing} ) { \$ps->{stored} .= \$code; }\n"; $perlCode.= "}\n"; return $perlCode; } sub _make_eval_code { my ($evalDefs,$stateName,$arcName,$matchNo,$genDebugCode) = @_; my $eval=''; foreach my $evalDef (@$evalDefs) { if (exists($evalDef->{$stateName}{$arcName})) { if ( $evalDef->{$stateName}{$arcName} =~ m/^(\w+?):(\w+?)$/ ) { $eval.=$evalDef->{$1}{$2}; } else { $eval.=$evalDef->{$stateName}{$arcName}; } $eval.="\n"; } } # replace $match variable with the actual number of the match $eval=~ s/\$match/\$$matchNo/g; # if fromLastPos is used then generate the code to work it out if ($eval =~ /\$fromLastPos/) { my $e; $e .= "\$ps->{storing}==1 or die \"fromLastPos used and storing was not set\";\n"; $e .= "if (\$storePos==-1) {\n"; # on another line $e .= " \$fromLastPos=\$ps->{stored}."; # what was before $e .= " substr(\$code,0,pos(\$code)-length(\$$matchNo));\n"; # some of this line $e .= "}\n"; $e .= "else {\n"; $e .= " \$fromLastPos=substr(\$code,\$storePos,pos(\$code)". "-\$storePos-length(\$$matchNo));\n"; $e .= "}\n"; $e .= "\$ps->{storing}=0;\n"; $e .= "\$ps->{stored}='';\n"; $eval = $e . $eval; } return $eval; } sub _check_end_state { my ($self,$file,$line,$ps) = @_; if (!exists($ps->{curState})){ # parse_line was never called, file only contained comments, defines etc return; } $ps->{prevState} = $ps->{curState}; $ps->{curState} = pop(@{$ps->{nextStateStack}}) or $self->_add_confused("$file:$line:". "No next state after $ps->{prevState} at EOF"); if ($ps->{curState} ne 'START') { $self->_add_confused("$file:$line:". " at EOF in state $ps->{curState}". (($ps->{curState} eq 'CONFUSED')? ",prevState was $ps->{prevState}":"")); } if (@{$ps->{nextStateStack}}) { $self->_add_confused("$file:$line:". " at EOF, state stack not empty: ". join(" ",@{$ps->{nextStateStack}})); } # at the moment I don't check these: # $ps->{storing}= 0; # $ps->{stored}= ""; } sub _check_data_structures { my ($evalDefs) = @_; my %stateNames; my %statesUnused; foreach my $sp (@$languageDef) { die "Not hash!" unless ref($sp) eq "HASH"; if (!exists($sp->{stateName})) { die "State without name!"; } die "Duplicate state$sp->{stateName}" if exists $stateNames{$sp->{stateName}}; $stateNames{$sp->{stateName}} = $sp; } %statesUnused = %stateNames; # check language def first foreach my $sp (@$languageDef) { my %t = %$sp; if (!exists($sp->{search})) { die "State without search!"; } die "search $sp->{stateName} not array" unless ref($t{search}) eq "ARRAY"; my %arcNames; foreach my $arc (@{$sp->{search}}) { my %a = %$arc; die "arc without regexp in $sp->{stateName}" unless exists $a{regexp}; delete $a{regexp}; if (exists($a{nextState})) { die "nextState not array" unless ref($a{nextState}) eq "ARRAY"; foreach my $n (@{$a{nextState}}) { next if ($n =~ m/^\$/); #can't check variable ones die "Bad Next state $n" unless exists $stateNames{$n}; delete($statesUnused{$n}) if exists $statesUnused{$n}; } delete $a{nextState}; } if (exists($a{arcName})) { die "Duplicate arc $a{arcName}" if exists $arcNames{$a{arcName}}; $arcNames{$a{arcName}} = 1; delete $a{arcName}; } delete $a{resetPos}; delete $a{storePos}; foreach my $k (sort (keys %a)) { die "Bad key $k in arc of state $t{stateName}"; } } delete $t{stateName}; delete $t{search}; delete $t{allowAnything} if exists $t{allowAnything}; if (exists($t{confusedNextState})) { die "Bad Next confused state $t{confusedNextState}" unless exists $stateNames{$t{confusedNextState}}; delete $t{confusedNextState}; } foreach my $n (@{$t{failNextState}}) { next if ($n =~ m/^\$/); #can't check variable ones die "Bad Next fail state $n" unless exists $stateNames{$n}; delete($statesUnused{$n}) if exists $statesUnused{$n}; } delete $t{failNextState} if exists $t{failNextState}; delete $t{failStorePos} if exists $t{failStorePos}; foreach my $k (sort (keys %t)) { die "Bad key $k in languageDef state $sp->{stateName}"; } } # REVISIT: MODULE PORTS looks like it is unused because it is got to # by setting $nState - should have a flag in language def that turns # off this check on a per state basis. foreach my $state (sort (keys %statesUnused)) { #die "State $state was not used"; print "Warning: State $state looks like it was not used\n" if $debug; } foreach my $evalDef (@$evalDefs) { foreach my $state (sort (keys %$evalDef)) { if (!exists($stateNames{$state})) { die "Couldn't find state $state"; } my $statep = $stateNames{$state}; foreach my $arc (sort (keys %{$evalDef->{$state}})) { my $found = 0; foreach my $s (@{$statep->{search}}) { if (exists($s->{arcName}) && ($s->{arcName} eq $arc)) { $found=1; last; } } if ($found == 0) { die "No arc $arc in state $state"; } if ( $evalDef->{$state}{$arc} =~ m/^(\w+?):(\w+?)$/ ) { die "No code found for $evalDef->{$state}{$arc}" unless exists $evalDef->{$1}{$2}; } } } } } sub _check_coverage { print "\n\nCoverage Information:\n"; foreach my $sp (@$languageDef) { if (!exists($takenArcs->{$sp->{stateName}})) { print " State $sp->{stateName}: no arcs take (except fail maybe)\n"; } else { my $i=0; foreach my $arc (@{$sp->{search}}) { $i++; if (!exists( $takenArcs->{$sp->{stateName}}{$i} )) { my $arcName = $i; $arcName = $arc->{arcName} if exists $arc->{arcName}; print " Arc $arcName of $sp->{stateName} was never taken\n"; } } } } } ########################################################################### # when doing require or use we must return 1 1;

Error running this command: diff -w -U 5 "/tmp/3ZXbNY" ""

diff: : No such file or directory