#!/usr/local/bin/perl -w # # Copyright 1998, 1999, 2000, 2001, 2002 Providenza & Boekelheide, Inc # Feel free to use this, let me know of any bugs or enhancements # John Providenza # # # Create a Vi style tags file for Verilog files # Scan Verilog files to extract function/task/module keywords # # 4-1-99 added support for -y flag to specify library directories # it can be used on the command line ("-y /proj/lib") # or in the file_of_files # # 11-1-00 fixed Function detection to support function size info, ie # function [10:0] foo ; # # 6-1-01 changed from hashes to an array to store the module info. # this allows us to remember identical tag names that could # be in different files # # 7-18-01 fixed 2 problems with functions. # 1) a scalar declaration (function foo;) failed # 2) functions burried in funny comments failed # # 9-25-01 1) added scanning of +incdir+ directories specified in a -f file. # 2) deleted duplicate filenames from processing # # 10-4-01 process `include directive # # 10-16-01 1) cleanup on 'function' scanning # 2) for `include, auto-add directory that calling file is in # # 2-22-02 1) mods to add the basename of `include files as a tag so that # simple includes files with only parameters can be pushed in to. # # 3-29-02 Yet another fix to function finding # # Note that the "file_of_file_names" may also include lines like # -y /proj/lib_dir # +incdir+foo/bar # This can be used to suck in all verilog files from a libray directory $ARGV[0] || die "usage:\n vtags.pl [-f file_of_file_names] [-y lib_dir] [file] ...\n" ; $file_cnt = 0 ; my @inc_dirs = (); my @inc_files = (); my @missing_includes = (); my $file_types = "[vV][tT]*"; # allow either .v or .vt files sub read_lib_dir { my $lib_dir_name = shift ; my $type = shift; print "including $type directory $lib_dir_name\n" ; opendir (LIBDIR, $lib_dir_name) || die "can't open $type $lib_dir_name\n" ; my @lib_files = readdir(LIBDIR) ; foreach $lib_file (@lib_files) { push(@files, "$lib_dir_name/$lib_file") if ($lib_file =~ /\.v$/) ; } closedir (LIBDIR) ; } # look for the include file in our path.... # this includes our current directory as well as the include paths # called with the include file name to look for as well as the calling # file path sub do_include { my $ifile = shift; my $call_file = shift; my $call_dir = $call_file; $call_dir =~ s,/\w[\w.]+$,, ; my $found = ""; foreach my $dir (($call_dir, @inc_dirs)) { if (-r "$dir/$ifile") { $found = "$dir/$ifile"; last; } } if ($found ne "") { # print "do_include: adding $found\n"; push (@inc_files, $found); # also add the basename of the file as a tag JIC the include # file doesn't have any modules $ifile =~ s/\.\S+$//; push (@tags, "$ifile\t$found\t1"); } else { push (@missing_includes, $ifile); } } # check a line for interesting things to process: # function, task, module, `include sub proc_line { # get the input line and line number $_ = shift; my $line_num = shift; if (/^\s*`include\s+"(\S+)"/) { # look for the include file in our path.... &do_include ($1, $file); } else { # remove strings s,\\.,,g; # no \ style escapes... s,"[^"]*",,g; # remove matched " s,'[^']*',,g; # remove matched ' # check for keywords, remember them if (/\b(module|task)\s+([a-zA-Z]\w+)/) { # print "Got $1 $2\n"; push (@tags, "$2\t$file\t$line_num"); } elsif (/\bfunction(\s*\[\s*\d+\s*:\s*\d+\s*]\s*|\s+real\s+|\s+integer\s+|\s+)([a-zA-Z]\w*)/) { #print "Got function $2\n"; push (@tags, "$2\t$file\t$line_num"); } } } # read all the files scanning for module/task/function keywords sub scan_files { my (@files) = @_; $last_file = ""; foreach $file (sort(@files)) { # skip duplicate file names next if ($last_file eq $file); $last_file = $file; open (INPUT, $file) || (warn "can't open verilog file $file\n", next) ; $file_cnt++ ; $in_comment = 0 ; while () { # Scan each line for keywords while keeping track of comments # This can be written much more concisely, but this tries to # minimize the code for each common case if (!$in_comment) { # not currently in a block comment if (m,/[/*],) { # some kind of start of comment s,//.*$|/\*.*\*/,, ; # strip //.... and /*....*/ comments $in_comment = s,/\*.*$,, ; # remove /*...$ comment text } &proc_line ($_, $.) if (/include|module|task|function/); } else { # we're in a comment already if ( !($in_comment = !s,^.*\*/,,) ) { s,//.*$|/\*.*\*/,, ; # strip //.... and /*....*/ comments $in_comment = s,/\*.*$,, ; # remove /*...$ comment text &proc_line ($_, $.) if (/include|module|task|function/); } } } close (INPUT) ; } } # expand command line to remove '-f' flags while ($n_arg = shift) { if ($n_arg eq "-f") { # process the list of files $n_arg = shift ; open(FILES, $n_arg) || die "can't open cmd line file $n_arg\n" ; while () { chomp ; if (/^-y\s+(.*$)/) { # we got a -y argument specifying a library directory. read_lib_dir ($1, "lib_dir") ; } elsif (/\+incdir\+(\S+)/) { push (@inc_dirs, $1) ; } else { s/\s*$// ; # remove trailing blanks s/^.* // ; # remove any leading -flags /\.$file_types\s*$/ && push(@files, $_) ; # save only .v files } } } elsif ($n_arg eq "-y") { # we got a -y argument specifying a library directory. read_lib_dir (shift, "lib_dir") ; } elsif ($n_arg =~ /\.$file_types\s*$/ ) { push (@files, $n_arg) ; # save .v files from cmd line } } # print "all Verilog input files are: @files\n" ; # scan all the specified input files for modules, tasks, functions, `includes # when done, we'll have added tags and created two arrays for `includes: # one if for includes in the same directory as the calling file or in the # include path, the other for missing includes &scan_files (@files) ; print "looking for missing include files...\n"; # these were not found in the include_path or in the directory of the # calling file foreach $file (@files) { $file =~ s,/[^/]+$,,; # remove trailing file name to get path push (@dirs, $file); } foreach $missing (@missing_includes) { $last_dir = ""; my $found = 0; foreach $dir (sort(@dirs)) { if ($last_dir ne $dir) { my $target = "$dir/$missing"; if (-f $target) { print "adding include file $missing from $dir\n"; push(@inc_files, $target); $found = 1; # also add the basename of the file as a tag JIC the include # file doesn't have any modules my $base_name = $missing; $base_name =~ s/\.\S+$//; push (@tags, "$base_name\t$target\t1"); } } $last_dir = $dir; } if ($found == 0) { print "NOTE: couldn't find include file $missing\n"; } } # now process the include files &scan_files (@inc_files) ; # send out the tags file open(TAGS, ">tags") || die "can't open tags file\n" ; my $tag_cnt = 0; my $last_tag = ""; foreach $i (sort (@tags) ) { print TAGS "$i\n" if ($i ne $last_tag); $last_tag = $i; $tag_cnt++; } print "$file_cnt files scanned, $tag_cnt tags found\n" ;