diff --git a/bin/htmlgen/filelist_compile.py b/bin/htmlgen/filelist_compile.py new file mode 100755 index 0000000000000000000000000000000000000000..1ccbf2afa0faab6483f56b9c0aea9d90518a8a4e --- /dev/null +++ b/bin/htmlgen/filelist_compile.py @@ -0,0 +1,104 @@ +#!/usr/bin/env python3 +#------------------------------------------------------------------------------------ +# Verilog Filelist compilation script +# A joint work commissioned on behalf of SoC Labs, under Arm Academic Access license. +# +# Contributors +# +# David Mapstone (d.a.mapstone@soton.ac.uk) +# Copyright (c) 2023, SoC Labs (www.soclabs.org) +#------------------------------------------------------------------------------------ + +import argparse +import os + +verilog_extensions = (".v", ".sv") + +filelist_header = """//----------------------------------------------------------------------------- +// AUTOGENERATED: Compiled Filelist +// A joint work commissioned on behalf of SoC Labs, under Arm Academic Access license. +// +// Contributors +// +// David Mapstone (d.a.mapstone@soton.ac.uk) +// +// Copyright � 2021-3, SoC Labs (www.soclabs.org) +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +// Abstract : Verilog Command File with expanded system variables +//----------------------------------------------------------------------------- + +""" + +def env_var_substitute(path): + # Interpret the path and remove brackets from path + sub_path = path.translate(str.maketrans('', '', '()')) + # Expand environment variables in Path + sub_path = os.path.expandvars(sub_path) + return sub_path + +def read_list(filelist): + # Create Filelist List Structure + compiled_filelist = [] + # Open Filelist and Read Lines + f = open(filelist, "r") + filelines = f.readlines() + f.close() + # Remove Black Lines from list + filelines = [x.rstrip("\n") for x in filelines] + filelines = [x for x in filelines if x != ""] + # Iterate over list and process + for line in filelines: + # Remove whitespace and split line into arguments + line_list = line.strip().split() + # print(line_list) + # Check Line isn't a comment + if not line_list[0].startswith("//"): + # If line is a reference to another filelist + if line_list[0] == "-f": + # Recursively call this function and append list to this compiled Filelist + print(line_list[1]) + compiled_filelist += read_list(env_var_substitute(line_list[1])) + + elif line_list[0] == "-y": + # Append to filelist + for file in os.listdir(env_var_substitute(line_list[1])): + if file.endswith(verilog_extensions): + compiled_filelist.append(env_var_substitute(line_list[1])+"/"+str(file)) + + elif line_list[0].startswith("+incdir+"): + # Append to filelist + for file in os.listdir(env_var_substitute(line_list[0].lstrip("+incdir+"))): + if file.endswith(verilog_extensions): + compiled_filelist.append(env_var_substitute(line_list[0].lstrip("+incdir+"))+"/"+str(file)) + + # If file list a verilog file + elif line_list[0].endswith(verilog_extensions): + # Append to filelist + compiled_filelist.append(env_var_substitute(line_list[0])) + return compiled_filelist + + +def filelist_compile(args): + input_filelist = args.filelist + output_filelist = args.output + print("------------------") + print("Compiling Filelist") + print("------------------") + filelist = read_list(input_filelist) + filelist = [x+"\n" for x in filelist] + filelist_str = filelist_header + for path in filelist: filelist_str += path + print("Compile Done") + print("------------------") + f_outlist = open(output_filelist, "w") + f_outlist.write(filelist_str) + f_outlist.close() + +if __name__ == "__main__": + # Capture Arguments from Command Line + parser = argparse.ArgumentParser(description='Compiles Filelist to Read') + parser.add_argument("-f", "--filelist", type=str, help="Input Filelist to Read") + parser.add_argument("-o", "--output", type=str, help="Output Filelist location") + args = parser.parse_args() + filelist_compile(args) \ No newline at end of file diff --git a/bin/htmlgen/makefile b/bin/htmlgen/makefile new file mode 100644 index 0000000000000000000000000000000000000000..e89d9d0b141a4d6b4e386e158196cd04495bdf60 --- /dev/null +++ b/bin/htmlgen/makefile @@ -0,0 +1,33 @@ +#----------------------------------------------------------------------------- +# HTML Generatoration Makefile +# A joint work commissioned on behalf of SoC Labs, under Arm Academic Access license. +# +# Contributors +# +# David Mapstone (d.a.mapstone@soton.ac.uk) +# +# Copyright � 2021-3, SoC Labs (www.soclabs.org) +#----------------------------------------------------------------------------- + +# Top-level module of Hierarchy +TOP_MODULE ?= nanosoc_chip + +# Filelist to give to v2html +FILELIST ?= $(PROJECT_DIR)/flist/project/system.flist + +# Directory to store generated HTML +OUT_DIR ?= $(PROJECT_DIR) + +# Name of generated filelist by python script +OUTPUT_FILELIST := $(OUT_DIR)/filelist.flist + +bootrom: + make -C $(NANOSOC_TECH_DIR)/system bootrom + +htmlgen: bootrom + @echo building HTML tree + @mkdir -p $(OUT_DIR)/build + @(cd $(OUT_DIR)/build; \ + rm *.html; rm *.gif; \ + $(SOCTOOLS_FLOW_DIR)/bin/htmlgen/filelist_compile.py -f $(FILELIST) -o $(OUTPUT_FILELIST) ; \ + $(SOCTOOLS_FLOW_DIR)/bin/htmlgen/v2html/v2html -f $(OUTPUT_FILELIST) -ht $(TOP_MODULE) ; ) diff --git a/bin/htmlgen/v2html/LICENCE.TXT b/bin/htmlgen/v2html/LICENCE.TXT new file mode 100644 index 0000000000000000000000000000000000000000..e2a72de1787620ebf7c84fe3b6cdc146bf37a4f0 --- /dev/null +++ b/bin/htmlgen/v2html/LICENCE.TXT @@ -0,0 +1,67 @@ + v2html Conversion Software + + Limited Copyright Licence + +BACKGROUND + +The accompanying v2html conversion software (the software) is a +copyrighted work. This document describes conditions under which the +software can be copied, modified and distributed. + +All rights, including copyright, in the software are retained by the +owner(s) as identified in each software file. + +Subject to the conditions described below, you may modify source +files included with the software and distribute the modified +versions. The software accompanying this notice may include such +derivative works. + +CONDITIONS FOR AUTHORIZED COPYING, MODIFICATION AND DISTRIBUTION + +If you agree to all of the following conditions, you may copy, modify +and distribute the software subject to these conditions. + +You do not need to agree to these conditions. However, if you do not +agree to all of the following conditions, you are not authorized to +do anything with regard to the copyrighted work that is within the +exclusive domain of a copyright owner, such as copying, modification, +and distribution. + +(1) No fee (monetary or otherwise) may be charged for the software or +any derivative work (whether in exchange for copying, distribution, +use, or otherwise). + +(2) The software includes copyright notices, references to this +document, and warnings concerning the intended use of the software. +All such notices are to be included in all copies of the software. +Any code copied or moved to a file not having such notices must be +accompanied by such notices. Notices that are displayed to a user +during execution of the software are to remain intact. + +(3) This document may not be modified. A copy of this document must +be included with each copy of the software, including derivative +works. + +(4) Failure to abide by these conditions terminates any rights that +you may otherwise have had under this licence. + +(5) NO WARRANTY: The software has NO warranty from anyone; for +example, there is NO warranty that the software is error free and +there is NO warranty with respect to infringement of patents, +copyrights or any other intellectual property rights. The software +is provided "AS IS." NO support is provided for the software. + +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE SPECIFICALLY DISCLAIMED. + +(6) LIMITATIONS OF REMEDIES AND LIABILITY: +In no event shall Hewlett-Packard be liable for direct, indirect, +special, incidental or consequential damages (including lost +profits), whether based on contract, tort or any other legal theory. + +(7) DERIVATIVE WORKS: In this document, the phrase "derivative work" +is intended to include only those works that include sufficient +copyrightable material from the software such that the derivative +work would be a copyright infringement if made without authorization +of the owner of the copyright in the software. + diff --git a/bin/htmlgen/v2html/README b/bin/htmlgen/v2html/README new file mode 100644 index 0000000000000000000000000000000000000000..e705ed620ce47245260b1dc75c4ac8653111df26 --- /dev/null +++ b/bin/htmlgen/v2html/README @@ -0,0 +1,43 @@ + +v2html - verilog to html converter +---------------------------------- + +See LICENCE.TXT for details of licencing. + +See http://www.burbleland.com/v2html for more information. + +Files included: + +README - this file +v2html - v2html perl script +v2html-cgi - CGI helper script (for collapsible hierarchy) +LICENCE.TXT - licence +v2html.man.html - man page in html format +v2html.1 - man page in nroff format + +- Make sure you have perl version 5.004 or later (perl -v tells you + the version). + +Unix Installation +----------------- + +- Copy v2html to somewhere like /usr/local/bin. +- If you are using the CGI script then see your web-server's documentation + on where to put CGI scripts. +- Copy v2html.1 to somewhere like /usr/local/man. + +If your copy of perl is not in /usr/bin then you will have to edit the +first line of v2html and v2html-cgi to refer to where your copy of +perl in installed. Alternatively you could install a link from +/usr/bin/perl to your copy of perl. + +Windows Installation +-------------------- + +- Put the v2html script somewhere (eg C:\v2html) +- Then, assuming you have Perl installed as c:\perl\bin\perl.exe, you should + be able to run v2html from a DOS box with a command like this: + + c:\perl\bin\perl.exe c:\v2html\v2html -f my_cmd_file.vc + + diff --git a/bin/htmlgen/v2html/v2html b/bin/htmlgen/v2html/v2html new file mode 100755 index 0000000000000000000000000000000000000000..6e0f8c74c5352f81c1ee177c38dca81046966b58 --- /dev/null +++ b/bin/htmlgen/v2html/v2html @@ -0,0 +1,7488 @@ +#!/usr/bin/perl -w +############################################################################### +# +# File: v2html +# RCS: $Header: /home/cc/v2html/build/../RCS/v2html,v 7.30.1.3 2006/05/03 20:19:55 cc Exp $ +# Description: Verilog to html converter +# Author: Costas Calamvokis +# Created: Wed Aug 20 11:24:31 1997 +# Modified: Fri Apr 7 11:03:32 2006 +# Language: Perl +# +# Copyright 1998-2006 Costas Calamvokis +# Copyright 1997 Hewlett-Packard Company +# +# 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. +# +############################################################################### + +#use strict; + +# Define the global variables + +use vars qw($output_hier $output_index $hier_file $frame_file $quiet $incremental + $maint $frames $frame_bottom $frame_middle $frame_top $frame_code + $link_to_source $cgi_script $web_base $js_hier + $js_file $out_dir $js_cookies + $print_unconnected $print_no_mods $grey_ifdefed_out $cgi_key + $t_and_f_in_hier $compress @compress_cmd $compress_extension + $hier_comment $tabstop $js_sigs $help_info @inc_dirs @lib_dirs + @lib_exts %navbar %classes $lines_per_file $index_info $style_sheet_link + @output_files @args $css + + $verilog_db + + @verilog_keywords + %verilog_keywords_hash %verilog_compiler_keywords_hash + + $version $vert_frames $mail_regexp $http_regexp $VID $icon_c $icon_i + $icon_x @newer_files @files $file @hier_tops %cmd_line_defines + $module $debug $ul_id @index_stack ); + +# check the perl version is high enough +if ( $] < 5.004 ) { + print "Error: this script can only run with Perl version 5.004 or greater\n"; + exit 1; +} + +# initialize +&init; + +# read the arguments +&process_args; + +# legal stuff - do not delete +if (!$quiet) { + print "v2html version $version. See LICENCE.TXT for licence.\n"; + print " Copyright 1998-2006 Costas Calamvokis\n"; + print " Copyright 1997 Hewlett-Packard Company\n"; +} + +$style_sheet_link = "<link rel=\"Stylesheet\" title=\"v2html stylesheet\" ". + "media=\"Screen\" href=\"$css\">\n"; + +# set up the navbar +$navbar{order} = [ ]; +if ( $output_hier ) { + push ( @{$navbar{order}} , ( 'Hierarchy' ) ); +} +if ( $output_index ) { + push ( @{$navbar{order}} , ( 'Files' , 'Modules' , 'Signals' , 'Tasks' , 'Functions' )); +} +push ( @{$navbar{order}} , ( 'Help' ) ); +# now set what each navbar item links to +$navbar{Hierarchy} = $hier_file; +($navbar{Files} = $hier_file) =~ s/(\..*|)$/-f$1/; +($navbar{Modules} = $hier_file) =~ s/(\..*|)$/-m$1/; +($navbar{Signals} = $hier_file) =~ s/(\..*|)$/-s$1/; +($navbar{Tasks} = $hier_file) =~ s/(\..*|)$/-t$1/; +($navbar{Functions}= $hier_file) =~ s/(\..*|)$/-fn$1/; +$navbar{Help} = "http://www.burbleland.com/v2html/help_7_30.html?$help_info"; + +# check incremental +if ($incremental) { + if (check_incremental($out_dir)) { + print "All html files are up to date, nothing to do.\n" unless $quiet; + exit 0; + } + print "Rebuilding all files\n" unless $quiet; +} + +$verilog_db = &rvp::read_verilog(\@files,[],\%cmd_line_defines, + $quiet,\@inc_dirs,\@lib_dirs,\@lib_exts); + +if ($output_index) { + print "Writing indexes\n" unless $quiet; + print_indexes($verilog_db,$js_hier); +} + +# write the hierarchy (must be done after the indexes) +if ($output_hier) { + print "Writing hierarchy to $hier_file\n" unless $quiet; + print_hier($hier_file,$js_hier,\@hier_tops); +} + +# write the frames +if ($frames) { + print "Writing frames to $frame_file\n" unless $quiet; + if ((@hier_tops)) { + print_frame_top($frame_file,$hier_file,join(", ",@hier_tops), + $js_hier,$vert_frames); + } + else { + print_frame_top($frame_file,$hier_file,$web_base, + $js_hier,$vert_frames); + } +} + +# print out gif icons used +print_gifs($out_dir, ($cgi_script || $js_hier), $output_index ); + +# print out the cascading style sheet +print_css($out_dir); + +# convert each file +foreach $file (&rvp::get_files($verilog_db)) { + convert(&rvp::get_files_full_name($verilog_db,$file)); +} + +# Write out a file list for incremental - we can not rely on the user provided +# one because we may have read more files finding modules and includes +if ($incremental) { + write_filelist($verilog_db,$out_dir); +} + +foreach my $problem (&rvp::get_problems($verilog_db)) { + if ( $problem !~ m/\n/ ) { + $problem =~ s/(.{60,79}) /$1\n /g; + } + print "$problem.\n"; +} + +exit 0; + +############################################################################### +# Subroutines +############################################################################### + +############################################################################### +# initialize global variables +# +sub init { + + @output_files=(); # stores a list of files we've written for incremental + $output_hier = 1; + $output_index = 1; + $hier_file = 'hierarchy.html'; + $frame_file="frame.html"; + $quiet = 0; + $incremental = 0; + $maint = ''; + $frames = 0; + $vert_frames = 0; + $frame_bottom = ""; + $frame_middle = ""; + $frame_code = ""; # frame for jumping to code + $frame_top = ""; + $link_to_source = 0; + $cgi_script = ""; + $web_base = ""; + $js_file = ""; + $help_info = ""; + $print_unconnected=1; + $print_no_mods=1; + $grey_ifdefed_out = 1; + $t_and_f_in_hier=0; + $compress=0; + $hier_comment = ""; + @compress_cmd = ('compress', '-f'); + $compress_extension = '.Z'; + srand( time() ^ ( $$ + ( $$ << 15))); + $cgi_key= substr(rand,2,-1); + $tabstop=0; + @inc_dirs=('.'); + @lib_dirs=('.'); + @lib_exts=(''); + $lines_per_file=1000; + $js_hier =1; # javascript hierarchy + $js_sigs =1; # javascript signals + $js_cookies=1; # javascript cookies + $css= 'v2html.css'; # default cascading style sheet name + + $out_dir='./'; + + @verilog_keywords = ( + qw( + always assign attribute begin case casex casez deassign default + defparam disable edge else end endattribute endcase endfunction + endmodule endprimitive endspecify endtable endtask event for force + forever fork function highz0 highz1 if initial join large macromodule + medium module negedge parameter posedge primitive pull0 pull1 release + repeat rtranif1 scalared small specify specparam strength strong0 + strong1 table task trior use vectored wait weak0 weak1 while), + (@rvp::verilog_gatetype_keywords), + (@rvp::verilog_sigs), + qw( + config endconfig design instance liblist use library cell + generate endgenerate automatic)); # V2001 + + @verilog_keywords_hash{@verilog_keywords} = "" x @verilog_keywords; + + @verilog_compiler_keywords_hash{@rvp::verilog_compiler_keywords} = + "" x @rvp::verilog_compiler_keywords; + + + $version = '$Header: /home/cc/v2html/build/../RCS/v2html,v 7.30.1.3 2006/05/03 20:19:55 cc Exp $'; #' + $version =~ s/^\S+ \S+ (\S+) .*$/$1/; + + # map from easy to remember names to the crytic class names used in html files + %classes = (comment=>'C', pp_ignore=>'P', string=>'S', compiler=>'M', + systemtask=>'ST', keyword=>'K', signal_input=>'SI', + signal_output=>'SO', signal_output_reg=>'SOR', + signal_inout_reg=>'SIOR', signal_inout=>'SIO', signal_reg=>'SR', + signal_integer=>'SIT', signal_wire=>'SW', signal_tri=>'STI', + signal_tri0=>'ST0', signal_tri1=>'ST1', signal_triand=>'STA', + signal_trireg=>'STR', signal_supply0=>'SS0',signal_trior=>'STO', + signal_supply1=>'SS1', signal_wand=>'SWA', signal_wor=>'SWO', + signal_time=>'STM', signal_realtime=>'SRT', signal_real=>'SRL', + module=>'MM', task=>'T', + function=>'F', define=>'D', parameter=>'PA', + navbar=>'NB', + signal_genvar=>'GV', attribute=>'AT', # V2001 + ); + + # the regexp to find a mail address in a comment (for linking) + $mail_regexp='\b([^@ \t\n]+@[^@ \t\n\.]+\.[^@ \n\t]+)\b'; + $http_regexp='\b(http:[^ \n\t]+)'; + + # 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+'; + + # icons for hierarchy when using javascript + $icon_c = "<img align=bottom border=0 src=\"v2html-c.gif\">"; + $icon_x = "<img align=bottom border=0 src=\"v2html-x.gif\">"; + $icon_i = "<img align=top border=0 alt=\"Index\" src=\"v2html-i.gif\">"; +} + +############################################################################### +# Command line processing +############################################################################### + +############################################################################### +# Set up any variables as specified in the arg list. +# Put input filenames in @files +# +sub process_args { + my ($f,$value,%files_seen); + + # $#ARGV is the index of last arg. + + @args=( @ARGV ); + + while ($_ = $ARGV[0]) { + shift(@ARGV); + if ( /^-debug$/ ) { + $debug = 1; + &rvp::set_debug(); + next; + } + elsif ( /^-o$/ ) { + $out_dir = shift(@ARGV); + $out_dir =~ s/[\/]?$/\//; + next; + } + elsif ( /^-m$/ ) { + &usage("$_ needs an argument") if ($#ARGV < 0); + $maint = shift(@ARGV); + next; + } + elsif ( /^-ht$/ ) { + &usage("$_ needs an argument") if ($#ARGV < 0); + push(@hier_tops,shift(@ARGV)); + next; + } + elsif ( /^-h$/ ) { + &usage("$_ needs an argument") if ($#ARGV < 0); + $hier_file = shift(@ARGV); + next; + } + elsif ( /^-hc$/ ) { + &usage("$_ needs an argument") if ($#ARGV < 0); + $hier_comment = shift(@ARGV); + quote_html(\$hier_comment); + next; + } + elsif ( /^-nh$/ ) { + $output_hier = 0; + $help_info.='nh-'; + next; + } + elsif ( /^-htf$/ ) { + $t_and_f_in_hier=1; + $help_info.='htf-'; + next; + } + elsif ( /^-nu$/ ) { + $print_unconnected = 0; + $help_info.='nu-'; + next; + } + elsif ( /^-nnm$/ ) { + $print_no_mods = 0; + $help_info.='nnm-'; + next; + } + elsif ( /^-ni$/ ) { + $grey_ifdefed_out = 0; + $help_info.='ni-'; + next; + } + elsif ( /^-nindex$/ ) { + $output_index = 0; + $help_info.='nindex-'; + next; + } + elsif ( /^-q$/ ) { + $quiet = 1; + next; + } + elsif ( /^-i$/ ) { + $incremental = 1; + $help_info.='i-'; + next; + } + elsif ( /^-c$/ ) { + &usage("$_ needs two arguments") if ($#ARGV < 1); + $js_hier =0; + $cgi_script = shift(@ARGV); + $web_base = shift(@ARGV); + if (($cgi_script !~ m&^/&) || ($web_base !~ m&^/&)) { + die "\nError: -c option must be followed by:\n". + " /path/cgi_script_name\n". + " /path_to_html_files\n". + " relative web server's root (not file system root)\n\n"; + } + $web_base =~ s|/$||; + $help_info.='c-'; + next; + } + elsif ( /^-js$/ || /^-qs$/ || /^-nqs$/ ) { + print "Warning: obsolete option $_\n"; + # js hierarchy is now on by default - qs now wrapped into sigpopup + next; + } + elsif ( /^-njshier$/ ) { + $js_hier = 0; + $help_info.='njshier-'; + next; + } + elsif ( /^-nsigpopup$/ ) { + $js_sigs = 0; + $help_info.='nsigpopup-'; + next; + } + elsif ( /^-ncookies$/ ) { + $js_cookies = 0; + $help_info.='ncookies-'; + next; + } + elsif ( /^-s$/ ) { + $link_to_source = 1; + $help_info.='s-'; + next; + } + elsif ( /^-z$/ ) { + $compress = 1; + $help_info.='z-'; + next; + } + elsif ( /^-zc$/ ) { + &usage("$_ needs an argument") if ($#ARGV < 0); + $_ = shift(@ARGV); + @compress_cmd = split ; + next; + } + elsif ( /^-ze$/ ) { + &usage("$_ needs an argument") if ($#ARGV < 0); + $compress_extension = shift(@ARGV); + next; + } + elsif ( /^-font$/ ) { + &usage if ($#ARGV < 0); + $_ = shift(@ARGV); + print "Warning -font option option is no longer supported ". + "edit cascading style sheet (v2html.css) instead\n"; + next; + } + elsif ( /^-f$/ ) { + &usage("$_ needs an argument") if ($#ARGV < 0); + add_options_from_file(shift(@ARGV)); + next; + } + elsif ( /^-F$/ || /^-VF$/ ) { + $frames = 1; + if ( /^-VF$/ ) { + $vert_frames = 1; + $help_info.='VF-'; + } + else { + $help_info.='F-'; + } + $frame_bottom = 'target="bottom"'; + $frame_middle = 'target="middle"'; + $frame_code = 'target="middle"'; + $frame_top = 'target="upper"'; + if ($#ARGV >= 0) { + if ($ARGV[0] =~ m/.*\.html$/) { + $frame_file = shift(@ARGV); + } + } + next; + } + elsif ( /^-g$/ ) { + print "Warning: obsolete option $_\n"; + $f = shift(@ARGV); + } + elsif ( /^-lines$/ ) { + &usage("$_ needs an argument") if ($#ARGV < 0); + $lines_per_file = shift(@ARGV); + } + elsif ( /^\+define\+($VID)(?:(?:=)(.*))?$/o ) { + # define with optional value (+define+NAME or +define+NAME=VALUE + if ($2) { + $cmd_line_defines{$1}=$2; + } + else { + $cmd_line_defines{$1}=""; + } + } + elsif ( /^-k$/ ) { + &usage("$_ needs an argument") if ($#ARGV < 0); + $cgi_key = shift(@ARGV); + } + elsif ( /^-tab$/ ) { + &usage("$_ needs an argument") if ($#ARGV < 0); + $tabstop = shift(@ARGV); + next; + } + elsif ( /^-css$/ ) { + &usage("$_ needs an argument") if ($#ARGV < 0); + $css = shift(@ARGV); + } + elsif ( /^-y$/ ) { + &usage("$_ needs an argument") if ($#ARGV < 0); + push(@lib_dirs,shift(@ARGV)); + next; + } + elsif ( /^\+incdir\+(.+)$/ ) { + push(@inc_dirs,split(/\+/,$1)); + next; + } + elsif ( /^\+libext\+(.+)$/ ) { + push(@lib_exts,split(/\+/,$1)); + next; + } + elsif ( /^-exp$/ ) { + print "Warning: obsolete option -exp (defines are always expanded)\n"; + next; + } + else { + if ( /^-v$/ ) { # -v file is exactly the same as file without -v + &usage("$_ needs an argument") if ($#ARGV < 0); + $_=shift(@ARGV); + } + my @fglobbed; + if (-r $_) { @fglobbed = ("$_"); } + else { @fglobbed = glob($_); } + if ( 1 == @fglobbed && ! -r $fglobbed[0] ) { # arg didn't expand & is not readable as a file + # try it as a VCS/verilog with no parameters + if (/^-([BCIMRSVu])|(Mupdate)|(ID)|(O0)|(PP)|(RI)|(RIG)|(RPP)$/ || + /^-(line)|(lmc-hm)|(lmc-swif)|(location)|(platform)$/ || + /^-(p[a-zA-Z0-9]+)$/ || + /^\+.*$/ ) { + print "Warning: ignoring VCS/verilog option $_\n" unless $quiet; + next; + } + # try it as a VCS/verilog with one parameter + if (/^-([jJlPL])|(ASFLAGS)|(CC)|(CFLAGS)|(LDFLAGS)|(as)$/ || + /^-(cc)|(grw)|(ld)|(syslib)|(vcd)$/) { + $value = $_ ." ". shift(@ARGV); + print "Warning: ignoring VCS/verilog option $value\n" + unless $quiet; + next; + } + # report an error: if it doesn't look like a bad option + # report it as an unreadable file + if ( /^[-+]/ ) { &usage("Unrecognized option: $_"); } + else { &usage("Verilog file $_ is not readable"); } + } + else { + # Must be a file + foreach $f ( @fglobbed ) { + if ( -r $f ) { # see if it is readable + if (exists($files_seen{$f})) { + print "Warning: ignoring duplicate file on command line $f\n"; + } + else { + push(@files,$f); + } + $files_seen{$f}=1; + } + else { + &usage("Verilog file $_ is not readable"); + } + } + } + } + } + + &usage("No verilog files specified") if (@files == 0); + + die "\nError: You can not use Javascript and CGI at the same time\n\n" + if ($js_hier && $cgi_script); + + $frame_code = 'target="_top"' if ($js_hier && !$frames); + + +} + +############################################################################### +# show usage +# +sub usage { + my ($msg) = @_; + + print "\nError: $msg\n"; + print <<EOF; + +usage: v2html [options] file1 [file2] ... + +Verilog like options: + + +define: Specify a define for use when processing, can be either: + +define+NAME or +define+NAME=VALUE + +incdir: Specify a directory to search for includes +incdir+DIR_NAME + -y : Specify a library to search for modules eg: -y DIR_NAME + +libext: Specify a the extension for libraries eg: +libext+.v or +libext+.v+.V + -v : Specify a library file (same as putting the file without -v) + +options: + -f : file to read options and file names from + -o : output directory for html files (default: current directory). + -q : be quiet. + -m : mail address for site maintainer. + -F : generate output using three frames - default output is frame.html + you can change this by putting a file name (with .html extension + after the -F option) + -VF : same as -F but does vertical frames with the hierarchy down the side + -i : incremental - check dates of files, if all .v.html files are newer + than their .v files then do nothing, otherwise convert them all. + -ht : specify a top module to print the hierarchy for (the default + is to do all hierarchies found in the input files) multiple + -ht options can be specified. + -htf : print tasks and functions in the hierarchy + -hc : a comment to print at the top of the hierarchy + -nh : skip writing the hierarchy. + -nindex : skip writing the indexes. + -nu : skip writing the list of unconnected modules in the hierarchy + -nnm : skip writing the list of files with no modules in the hierarchy + -ni : do not 'grey out' code that is ifdefed out + -s : link to the source (in footer of each page) only works if: + - your web server has access to the source code + - you run v2html in the output directory or use absolute + path names for the verilog + -njshier : turn off Javascript hierarchy generation. + -ncookies : turn off Javascript cookies (for remembering js hierarchy state) + -nsigpopup: turn off Javascript signal popup window. + -css : specify a URL for the cascading style sheet to use + -z : compress html files + -zc : compress command to use (default is compress) + -ze : extension of compressed files (default is .Z) + -tab : set tabstop to specified value + -lines : lines per html page (big files are split across multiple pages) + -c : activate CGI features (expanding of hierarchy) - to use this + you must have the v2html CGI script on your webserver - see man page. + -k : key to use for hierarchy CGI (default is to use a random one) + -h : name of file to write the hierarchy to (default: hierarchy.html). + -debug : useless info for debugging + +Ignored options: + + most VCS options (the only conflicts with v2html options are -o -s and -i) + all +options not mentioned above + +example: + v2html -hc "Our Chip" -o /users/www/p/html -m Joe_Blogs\@jb.com + +incdir+/p/includes -y /p/verilog +libext+.v+.V /p/verilog/chip_top.v + +See http://www.burbleland.com/v2html/v2html.html for more details + +EOF + + exit 1; + +} #' + +############################################################################### +# The argument specifies a file that contains arguments - exactly the +# same as on the command line +# comments are # or // you can quote things like this: -zc 'gzip -f' +# +sub add_options_from_file { + my ($file) = @_; + my (@args_found,$text_s); + + open(F,"<$file") || usage("can not open $file to read arguments"); + $/ = undef; + $text_s = <F>; + $/ = "\n"; + close(F); + + while ($text_s =~ m%((?:/\*.*?\*/)|(?://[^\n]*(?:\n|\Z))|(?:#[^\n]*\n)|(?:'[^']*')|(?:"[^"]*")|(?:\S+))%gs ) + { #'){ get emacs mode back in sync! + $_ = $1; + if ( m%((?:/\*.*?\*/)|(?://[^\n]*(?:\n|\Z)))%s ) { # comment, chuck it + print " cmdfile: chucking comment :|$_|\n" if $debug; + } + elsif ( m/^['"]/ ) { # "']{ + s/\A.//; + s/.\Z//; + print " cmdfile: found string |$_|\n" if $debug; + push(@args_found,$_); + } + else { + print " cmdfile: found word |$_|\n" if $debug; + push(@args_found,$_); + } + } + + unshift( @ARGV, @args_found ); + push( @args , @args_found ); +} + +############################################################################### +# Misc functions +############################################################################### + +############################################################################### +# given a source file name work out the .html file name +# without the path +# +sub hfile { + my ($sfile,$sline) = @_; + my ($page); + + # debugging checks + # die "No line passed to hfile!\n" unless defined($sline); + # die "bad sline: $sline\n" unless $sline=~m/^[0-9-]+$/; + + # NB sline starts at 1 + $page = ($sline <= $lines_per_file) ? '' : + ".p" . (1+int(($sline-1)/$lines_per_file)); + $sfile =~ s/^.*[\/\\]//; + $sfile .= "$page.html"; + $sfile .= $compress_extension if $compress; + + return $sfile; +} + +############################################################################### +# given a source file name work out the file without the path +# +sub ffile { + my ($sfile) = @_; + + $sfile =~ s/^.*[\/\\]//; + + return $sfile; +} + +############################################################################### +# Check whether we need to rebuild - delete any old files if we do +# +sub check_incremental { + my ($out_dir) = @_; + my ($incr_file,$filelist,$rebuild,@old_input_files,@old_output_files,@old_args, + $file,$mtime,$mtime_s,$mtime_o,$section); + local(*F); + + $incr_file="$out_dir.v2html_incr"; + + $rebuild=0; + if ( ! -r $incr_file ) { + print "Could not find file list $incr_file\n" unless $quiet; + $rebuild= 1; + } + + # read the incremental file, and check that the options match + if ($rebuild==0) { + open(F,"<$incr_file") || die "can not open $filelist read to file list"; + $section=1; + # Read incr file, be quite careful, as anything read into old_output_files + # can get deleted + while (<F>) { + chomp; + if (m/^---output files/) { + if ($section==1) { + $section++; + } + else { + print "Corrupt incremental file $out_dir.v2html_incr, remove and retry\n"; + exit; + } + } + elsif (m/^---options/) { + if ($section==2) { + $section++; + } + else { + print "Corrupt incremental file $out_dir.v2html_incr, remove and retry\n"; + exit; + } + } + else { + if ($section==1) { push(@old_input_files,$_); } + elsif ($section==2) { push(@old_output_files,$_); } + elsif ($section==3) { push(@old_args,$_); } + } + } + if ($section!=3) { + print "Corrupt incremental file $out_dir.v2html_incr, remove and retry\n"; exit; + } + + if ("@args" ne "@old_args") { + print "Arguments are different\n" unless $quiet; + $rebuild=1; + } + } + if ($rebuild==0) { + $mtime_s=$mtime_o=0; + # find the newest source file + foreach $file (@old_input_files) { + if ( -r $file ) { + $mtime = (stat( $file ))[9]; + $mtime_s = ($mtime>$mtime_s) ? $mtime : $mtime_s; + } + else { + print "Source file $file has gone\n" unless $quiet; + $rebuild=1; + last; + } + } + # find the oldest output file + foreach $file (@old_output_files) { + if ( -r $file ) { + $mtime = (stat( $file ))[9]; + $mtime_o = (($mtime<$mtime_o)||($mtime_o==0)) ? $mtime : $mtime_o; + } + else { + print "Output file $file has gone\n" unless $quiet; + $rebuild=1; + last; + } + } + } + + if ($rebuild==0) { + if ($mtime_o < $mtime_s ) { + print "Some source files are newer than the output files\n" + unless $quiet; + $rebuild=1; + } + elsif ($mtime_o < (stat( $0 ))[9]) { + print "Some output files are older than $0\n" + unless $quiet; + $rebuild=1; + } + } + + # Need to remove files produced on the last run to stop junk accumulating + # in the output dir (now that multiple pages + if ($rebuild) { + print "Removing old output files\n" unless $quiet; + foreach $file (@old_output_files) { + if ( $file =~ m/\.html/ ) { + unlink($file); + } + else { + print "Warning: skipping remove of $file (does not end in .html)\n"; + } + } + } + + return ($rebuild==0); +} + +############################################################################### +# Write out the files read for incremental compiles +# +sub write_filelist { + my ($fdata,$out_dir)= @_; + my ($file,$opt); + local (*F); + + open(F,">$out_dir.v2html_incr") || die "Could not write to $out_dir.v2html_incr"; + + foreach $file (sort &rvp::get_files($verilog_db)) { + print F &rvp::get_files_full_name($verilog_db,$file) . "\n"; + } + + print F "---output files\n"; + foreach $file (@output_files) { + print F "$file\n"; + } + + print F "---options\n"; + foreach $opt (@args) { + print F "$opt\n"; + } + close(F); +} + + + +############################################################################### +# Print out the footer +# arguments: $out: output file handle +# $src: name of source file (to put in footer) +# $js: flag telling it whether to print it, or +# to generate javascript to print it +sub print_footer { + my ($out,$src,$narrow,$js) = @_; + + + print_h_or_js($out,"<hr>\n",$js); + print_h_or_js($out,"<table>\n <tr><td><i>This page:<\/i><\/td>\n",$js); + + print_h_or_js($out,"<td> </td></tr><tr>\n",$js) if $narrow; + + if ($maint ne '') { + print_h_or_js($out," <td><i>Maintained by:<\/i><\/td>\n",$js); + print_h_or_js($out," <td><i><a href=\"mailto:" . $maint . "\">\n",$js); + print_h_or_js($out," " . $maint . "<\/a><\/i><\/tr>\n<tr>\n",$js); + print_h_or_js($out,"<td> </td>\n",$js) if !$narrow; + } + + print_h_or_js($out," <td><i>Created:<\/i><\/td><td><i>" . + localtime() . "<\/i><\/td><\/tr>\n",$js); + + if ($src ne '') { + print_h_or_js($out,"<tr>\n",$js); + print_h_or_js($out," <td> </td>\n",$js) if !$narrow; + print_h_or_js($out," <td><i>From:<\/i><\/td><td><i>\n",$js); + + if ($link_to_source) { + print_h_or_js($out," <a href=\"" . $src . "\">\n",$js); + quote_html(\$src); + print_h_or_js($out,$src . "<\/a>",$js); + } + else { + quote_html(\$src); + print_h_or_js($out,$src,$js); + } + print_h_or_js($out, "<\/i><\/td><\/tr>\n",$js); + } + + print_h_or_js($out,"</table>\n<hr>\n",$js); + + ################################################################## + # Do not alter any of this footer information # + ################################################################## + print_h_or_js($out,'<table width="100%"><tr><td><i>Verilog converted to html by ',$js); + print_h_or_js($out,' <a target="_top" '. + 'href="http://www.burbleland.com/v2html/v2html.html">',$js); + print_h_or_js($out," v2html $version<\/a> \n",$js); + print_h_or_js($out," (written by",$js); + print_h_or_js($out," <a href=\"mailto:v2html730\@burbleland.com\">",$js); + print_h_or_js($out,"Costas Calamvokis<\/a>).<\/i></td>",$js); + print_h_or_js($out,'<td align="right"><b>'. + '<a href="http://www.burbleland.com/v2html/help_7_30.html?'. + $help_info.'">Help</a></b></td>',$js) if !$narrow; + print_h_or_js($out,'</tr></table>',$js); + ################################################################## + # End of footer information # + ################################################################## + + # put a big blank table (90% of the size of the window) at the + # bottom of the page to make it scroll better + print_h_or_js($out, "<table height=\"90%\"><tr><td></td></tr></table>\n" , $js); +} + + +############################################################################### +# Print out a navigation bar +############################################################################### + +sub print_navbar { + my ($out,$js,$type,$data,$prevnext,$prevnext_file,$narrow) = @_; + my ($elem,$col,$elem_num); + + $col = @{$data->{order}}; + + # skip hierarchy if frames are on - it'll always be in top frame + if ($frames) { + foreach $elem (@{$data->{order}}) { + $col-- if $elem eq 'Hierarchy'; + } + } + + if ($type eq 'Hierarchy' && ($cgi_script||$js_hier)) { + $col+=2; # put in two extra buttons for the show all/hide all + } + + + # prev next takes an extra column + $col++ if $prevnext; + + return unless ($col); + + my $width = $narrow ? 33 : int(100/$col); + + my $td_code="<td align=\"center\" width=\"$width\%\" ". + "onmousedown=\"this.style.border=\\'inset\\';\" ". + "onmouseup=\"this.style.border=\\'outset\\';\" "; + + # if we are framed then we can't just set location when a table + # element is clicked, have to set parent.middle.location to put + # code into the middle frame, and + my $frame_code_js; + my $frame_top_js; + ($frame_code_js = $frame_code) =~ s/^.*"(.*)".*$/parent.$1./; + $frame_code_js="" if $frame_code_js =~ m/_top/; + ($frame_top_js = $frame_top) =~ s/^.*"(.*)".*$/parent.$1./; + + + print_h_or_js($out,"<center><table class=$classes{navbar} cols=$col ". + "><tr>",$js); + + + if ($prevnext) { + print_h_or_js($out,"$td_code onclick=\"${frame_code_js}location=\\'$prevnext_file\\';\">". + "<a $frame_code href=\"$prevnext_file\">$prevnext</a></td>", + $js); + } + $elem_num=0; + foreach $elem (@{$data->{order}}) { + if ($type eq 'Hierarchy' && $elem eq 'Hierarchy') { + if ($cgi_script) { + print_h_or_js($out,"$td_code onclick=\"${frame_top_js}location=\\'$cgi_script$web_base/?k=$cgi_key&x=C&in=$hier_file&f=$frames\\';\"><a $frame_top " . + "href=\"$cgi_script$web_base/?k=$cgi_key&x=C&in=$hier_file&f=$frames\">" . + "Hide All</a></td>\n",0); + print_h_or_js($out,"$td_code onclick=\"${frame_top_js}location=\\'$cgi_script$web_base/?k=$cgi_key&x=A&in=$hier_file&f=$frames\\';\"><a $frame_top " . + "href=\"$cgi_script$web_base/?k=$cgi_key&x=A&in=$hier_file&f=$frames\">" . + "Show All</a></td>\n",0); + $elem_num+=2; + } + if ($js_hier && $js) { + print_h_or_js($out,"$td_code onclick=\"javascript:parent.printIt(\\'C\\',-1)\">". + "<a href=\"javascript:parent.printIt(\\'C\\',-1);\">". + "Hide All</a></td>\n",1); + print_h_or_js($out,"$td_code onclick=\"javascript:parent.printIt(\\'A\\',-1);\">". + "<a href=\"javascript:parent.printIt(\\'A\\',-1)\">". + "Show All</a></td>\n",1); + $elem_num+=2; + } + } + # skip hierarchy if frames are on - it'll always be in top frame + next if (($elem eq 'Hierarchy') && $frames); + if ($narrow && ($elem_num!=0) && (($elem_num%3)==0)) { + print_h_or_js($out,"</tr><tr>",$js); + } + $elem_num++; + if ( $elem eq $type ) { + print_h_or_js($out,"$td_code>". + "<font color=\"#808080\">$elem</font></td>",$js); + } + else { + my $i = $data->{$elem}; + $i =~ s|\\|\\\\|; + print_h_or_js($out,"$td_code onclick=\"${frame_code_js}location=\\'$i\\';\">". + "<a $frame_code href=\"$data->{$elem}\">$elem</a></td>", + $js); + } + } + + print_h_or_js($out,"<\/tr><\/table><\/center>\n",$js); + +} + +############################################################################### +# Index printing +############################################################################### + +############################################################################### +# Collect the data and print all indexes +# +sub print_indexes { + my ($fdata,$js) = @_; + + my (@files,@modules,@signals,@tasks,@functions,$m,$sig,$tf,$t_type,$indl); + + # this stores the mapping between index letter and index file name + # for each of the indexes, as well as the nav bar + $index_info = {}; + + # approx line count we'd like for an index, divide be expected lines + # per entry to give number of elements per index that we need to + # call print index + $indl = 1000; + + @files = &rvp::get_files($fdata); # get the files + @modules = &rvp::get_modules($fdata); # get the modules + + # get the signals + @signals = (); + foreach $m (&rvp::get_modules($fdata)) { + foreach $sig (&rvp::get_modules_signals($fdata,$m)) { + push (@signals,"$sig $m"); + } + } + + # get the tasks and functions + @tasks = @functions = (); + foreach $m (&rvp::get_modules($fdata)) { + foreach $tf (&rvp::get_modules_t_and_f($fdata,$m)) { + ($t_type)=&rvp::get_modules_t_or_f($fdata,$m,$tf); + if ($t_type eq 'task') { push (@tasks,"$tf $m"); } + else { push (@functions,"$tf $m"); } + } + } + + # sort and split them across files + $index_info->{Files} = calc_index($fdata,$navbar{Files},\@files, $indl/4); + $index_info->{Modules} = calc_index($fdata,$navbar{Modules},\@modules,$indl/8); + $index_info->{Signals} = calc_index($fdata,$navbar{Signals},\@signals, $indl/20); + $index_info->{Tasks} = calc_index($fdata,$navbar{Tasks},\@tasks, $indl/3); + $index_info->{Functions}= calc_index($fdata,$navbar{Functions},\@functions, $indl/3); + + # now print all the indexes + print_index($fdata,$index_info->{Files},'Files',\@files,\&print_file_index); + print_index($fdata,$index_info->{Modules},'Modules',\@modules,\&print_module_index); + print_index($fdata,$index_info->{Signals},'Signals',\@signals,\&print_signal_index); + print_index($fdata,$index_info->{Tasks},'Tasks',\@tasks,\&print_tf_index); + print_index($fdata,$index_info->{Functions},'Functions',\@functions,\&print_tf_index); +} + +############################################################################### +# calculate all sorts of stuff about the index (also sorts the data) +# returns a hash for the that is stored in $index_info->{type} +# +sub calc_index { + my ($fdata,$fname, $data, $items_per_index) = @_; + my ($first_char,$elem_first_char,$info,$page,$ofile,$i,$items); + + # do a case insensitive sort (but make sure it is determinate if the + # items only differ in case) + @{$data} = sort { (uc($a) ne uc($b)) ? uc($a) cmp uc($b) : + $a cmp $b } @{$data}; + + # work out the indexes + $first_char=""; # make sure it will not match + $page=$items=0; + + $info = {}; + $info->{nb} = {}; # navbar, labels and links eg {A}='hierarchy-fn#index--A' + $info->{nb}{order} = []; # the navbar order, array of other keys (A,B...) + $info->{letters} = {}; # hash mapping start letters to index files + $info->{pages} = 0; # number of pages + $info->{files} = []; # array of hashes { name, start, end } indexed 1,2... + + for ($i=0;$i<scalar(@{$data});$i++) { + $elem_first_char=uc(substr($data->[$i],0,1)); + if ($elem_first_char ne $first_char) { + if ($items==0 || $items_per_index<$items) { + if ($page) { $info->{files}[$page]{end} = $i; } + $page++; + $ofile=$fname; + $ofile =~ s/.html$/.p$page.html/ if ($page != 1); + $info->{files}[$page] = { name => $ofile, start => $i}; + $items=0; + } + $info->{letters}{$elem_first_char} = $ofile; + $first_char=$elem_first_char; + push(@{$info->{nb}{order}},$first_char); + $info->{nb}{$first_char} = "$ofile#index--$first_char"; + } + $items++; + } + if ($page) { + $info->{pages}=$page; + $info->{files}[$page]{end} = $i; + } + else { # handle case where there was no data + $info->{pages}=1; + $info->{files}[1] = { name => $fname , start => 0 , end => 0 }; + } + return $info; +} + +############################################################################### +# Print one index - calls out to printfn to print each entry +# +sub print_index { + my ($fdata,$info, $type, $data, $printfn) = @_; + my ($i,$first_char,$elem_first_char,$page,$ofile); + local (*OUT); + + + for ($page=1;$page<=$info->{pages};$page++) { + $ofile=$info->{files}[$page]{name}; + + open(OUT,">$out_dir$ofile") || + die "Error: can not open file $out_dir$ofile to write: $!\n"; + push(@output_files,"$out_dir$ofile"); + print OUT "<!-- v2html $type index -->\n"; + print OUT "<html><head>\n"; + print OUT "<title>$type index".(($page==1)?"":" page $page")."</title>\n"; + print OUT $style_sheet_link; + print OUT "</head>\n"; + + if ($js_sigs) { + # dummy function in case someone tries to do a search in the index + print OUT "<script language=\"JavaScript\" type=\"text\/javascript\"><!--\n"; + print_js_common(*OUT); + print OUT "function search () { return false; }\n"; + print OUT "// -->\n"; + print OUT "</script>\n"; + } + + print OUT "<body>\n"; + + print OUT "<a name=\"top_of_page\"></a>\n"; + + if ($page==1) { print_navbar(*OUT,0,$type,\%navbar,'','',0); } + else { print_navbar(*OUT,0,$type,\%navbar,"Prev Page", + $info->{files}[$page-1]{name}.'#bottom_of_page', + 0); } + print_navbar(*OUT,0,'',$info->{nb},'','',0); + print OUT "<center><h3>$type index</h3></center>\n"; + + $first_char=""; # make sure it will not match + + for ($i=$info->{files}[$page]{start};$i<$info->{files}[$page]{end};$i++) { + $elem_first_char=uc(substr($data->[$i],0,1)); + if ($elem_first_char ne $first_char) { + $first_char=$elem_first_char; + print OUT "<a name=\"index--$first_char\"></a>\n"; + print_navbar(*OUT,0,'', + { order=>[$first_char], $first_char=>"#top_of_page"}, + '','',0); + } + &{$printfn}(*OUT,$fdata,$data->[$i]); + } + + print_navbar(*OUT,0,'',$info->{nb},'','',0); + if ($page==$info->{pages}) { print_navbar(*OUT,0,$type,\%navbar,'',''); } + else { print_navbar(*OUT,0,$type,\%navbar,"Next Page", + $info->{files}[$page+1]{name},0); } + print_footer(*OUT,'',0,0); + print OUT "</body>\n"; + print OUT "</html>\n"; + close(OUT); + } + +} + +############################################################################### +# Called to print on entry in the file index +# +sub print_file_index { + my ($out,$fdata,$data) = @_; + my ($m,$ms,$qword,$title,$inc,$i,$comma,$inc_by); + + $title = "<b><a name=\"$data\"></a>". + "<a $frame_middle href=\"".hfile($data,1)."\">$data</a></b>\n"; + + $ms=$comma=''; + foreach $m (sort &rvp::get_files_modules($fdata,$data)) { + $qword = $m; quote_html(\$qword); + if (&rvp::module_exists($fdata,$m)) { + $ms .= "$comma<a href=\"".index_link("Modules",$m)."\">$qword</a> "; + } + else { $ms .= "$comma$qword "; } + $comma=', '; + } + + $inc=$comma=''; + foreach $i (sort &rvp::get_files_includes($fdata,$data)) { + $qword = $i; quote_html(\$qword); + if (&rvp::file_exists($fdata,$i)) { + $inc .= "$comma<a href=\"".index_link("Files",$i)."\">$qword</a> "; + } + else { $inc .= "$comma$qword "; } + $comma=', '; + } + + $inc_by=$comma=''; + foreach $i (sort &rvp::get_files_included_by($fdata,$data)) { + $qword = $i; quote_html(\$qword); + if (&rvp::file_exists($fdata,$i)) { + $inc_by .= "$comma<a href=\"".index_link("Files",$i)."\">$qword</a> "; + } + else { $inc_by .= "$comma$qword "; } + $comma=', '; + } + + print_itable( $out , $title , + [ "Full name:" , &rvp::get_files_full_name($fdata,$data) , + "Modules:" , $ms , + "Includes:" , $inc , + "Included by:", $inc_by ]); + +} + +############################################################################### +# Called to print on entry in the module index +# +sub print_module_index { + my ($out,$fdata,$data) = @_; + my ($qword,$title,$inst,$m,$f,$i,$file,$inst_by,$tasks,$funcs,@t_and_f,$tf,$t_type, + $comma,$fcomma,$m_line,$type); + + ($file,$m_line)=&rvp::get_modules_file($fdata,$data); + $type=&rvp::get_modules_type($fdata,$data); + $qword=$data; quote_html(\$qword); + $title = "<b><a name=\"$data\"></a>". + "<a $frame_middle href=\"". + hfile($file,$m_line)."#$data\">$qword</a></b>\n"; + $title .= "<i>($type)</i>\n" unless $type eq 'module'; + + $inst=$comma=''; + ($m,$f,$i) = &rvp::get_first_instantiation($fdata,$data ); + while ($m) { + if (&rvp::module_exists($fdata,$m)) { + $inst.="$comma<a href=\"".index_link("Modules",$m)."\">$m:$i</a> "; + } + else { + $inst.="$comma$m:$i "; + } + $comma=', '; + ($m,$f,$i) = &rvp::get_next_instantiation($fdata); + } + + $inst_by=$comma=''; + ($m,$f,$i) = &rvp::get_first_instantiator($fdata,$data ); + while ($m) { + if (&rvp::module_exists($fdata,$m)) { + $inst_by.="$comma<a href=\"".index_link("Modules",$m)."\">$m:$i</a> "; + } + else { + $inst_by.="$comma$m:$i "; + } + $comma=', '; + ($m,$f,$i) = &rvp::get_next_instantiator($fdata); + } + + + $tasks=$funcs=$comma=$fcomma=''; + if ( @t_and_f = &rvp::get_modules_t_and_f($fdata,$data) ) { + foreach $tf (sort @t_and_f) { + ($t_type)=&rvp::get_modules_t_or_f($fdata,$data,$tf); + if ($t_type eq 'function') { + $funcs.="$fcomma<a href=\"".index_link("Functions","${tf}___$data"). + "\">$tf<\/a> "; + $fcomma=', '; + } + else { + $tasks.="$comma<a href=\"".index_link("Tasks","${tf}___$data"). + "\">$tf<\/a> "; + $comma=', '; + } + } + } + + + print_itable( $out , $title , + [ "File:" , "<a href=\"".index_link("Files",$file)."\">$file</a>" . + ((&rvp::module_ignored($fdata,$data)) ? + " (Warning: Duplicate definition found)" : "") , + "Instantiates:" , $inst , + "Instantiated by:" , $inst_by, + "Tasks:" , $tasks , + "Functions:" , $funcs ]); + +} + +############################################################################### +# Called to print on entry in the Signal index +# +sub print_signal_index { + my ($out,$fdata,$data) = @_; + my ($qword,$sig,$m,$title,$s_line,$s_i_line,$s_type,$s_file, + $s_pos,$s_neg,$s_type2,$im,$in,$p,$port_con,$comma,$con_to,$cts,$cti,$ctm); + + ($sig,$m) = split(' ',$data); + + ($s_line,undef,$s_i_line,$s_type,$s_file,$s_pos,$s_neg,$s_type2) = + &rvp::get_module_signal($fdata,$m,$sig); + $s_type.=" $s_type2" if $s_type2; + $s_type.=" (used in \@posedge)" if $s_pos; + $s_type.=" (used in \@negedge)" if $s_neg; + + $qword="$sig : $m"; quote_html(\$qword); + $title = "<b><a name=\"$sig"."___$m\"></a>". + "<a $frame_middle href=\"". + hfile($s_file,$s_line)."#$s_line\">$qword</a></b> : $s_type"; + + $port_con=$comma=''; + ($im,$in,$p,)=&rvp::get_first_signal_port_con($fdata,$m,$sig); + while ($im) { + if (&rvp::module_exists($fdata,$im) && ($p !~ m/^[0-9]/)) { + $port_con.="$comma<a href=\"".index_link("Signals","${p}___$im")."\">$im:$in:$p</a> "; + } + else { $port_con.="$comma$im:$in:$p "; } + $comma=', '; + ($im,$in,$p,)=&rvp::get_next_signal_port_con($fdata); + } + + $con_to=$comma=''; + ($cts,$ctm,$cti)=&rvp::get_first_signal_con_to($fdata,$m,$sig); + while ($cts) { + if (&rvp::module_exists($fdata,$ctm) && ($cts !~ m/^[0-9]/)) { + $con_to.="$comma<a href=\"".index_link("Signals","${cts}___$ctm")."\">$ctm:$cti:$cts</a> "; + } + else { $con_to.="$comma$ctm:$cti:$cts "; } + $comma=', '; + ($cts,$ctm,$cti)=&rvp::get_next_signal_con_to($fdata); + } + + # Only print table if there are some portcons to save space + if ($port_con || $con_to) { + print_itable( $out , $title , + [ "Connects down to:" , $port_con , + "Connects up to:" , $con_to ]); + } + else { + print_itable( $out , $title , []); + } + +} + +############################################################################### +# Called to print on entry in the Tasks or Functions index +# +sub print_tf_index { + my ($out,$fdata,$data) = @_; + my ($qword,$tf,$m,$title,$t_type,$t_line ,$t_file,$t_anchor); + + ($tf,$m) = split(' ',$data); + + ($t_type,$t_line ,$t_file,$t_anchor)=&rvp::get_modules_t_or_f($fdata,$m,$tf); + + + $qword="$tf"; quote_html(\$qword); + $title = "<b><a name=\"$tf"."___$m\"></a>". + "<a $frame_middle href=\"". + hfile($t_file,$t_line)."#$t_anchor\">$qword</a></b>"; + + print_itable( $out , $title , + [ "File:" , "<a href=\"".index_link("Files",$t_file). + "\">$t_file</a>" , + "Module:" , "<a href=\"".index_link("Modules",$m)."\">$m</a>" ]); + +} + +############################################################################### +# Print out a table in the index, called with the output file, the title and +# a pointer to an array of table pairs (column 1 and column 2) +# +sub print_itable { + my ($out,$title,$tab) = @_; + my ($f1,$f2,$empty); + print $out " $title\n"; + if (@{$tab}) { + $empty=1; + print $out "<div align=right><table cols=2 width=\"97%\">\n"; + while ($f1=shift(@{$tab})) { + $f2=shift(@{$tab}); + die "print_itable internal error" unless (defined($f2)); + if ($f2 ne '') { + $empty=0; + print $out "<tr><td valign=top width=\"17%\"><i>$f1</i></td>". + "<td valign=top width=\"83%\">$f2</td></tr>\n"; + } + } + print $out "<tr><td></td></tr>\n" if ($empty); # NS does not like empty tables + print $out "</table></div>\n"; + } + else { + print $out "<br>\n"; + } +} + +sub index_link { + my ($type,$elem) = @_; + + # debugging checks + print "Error: Bad index link $type $elem\n" + unless (exists($index_info->{$type}{letters}{uc(substr($elem,0,1))})); + return $index_info->{$type}{letters}{uc(substr($elem,0,1))} . "#$elem"; +} + +############################################################################### +# Hierarchy and frame printing +############################################################################### + +############################################################################### +# Print out the hierarchy +# arguments: output_file_name, javascript flag +# +sub print_hier { + my ($h_file,$js,$hier_tops_p) = @_; + my ($out_file,$m,$imod); + local (*HIER,*JSF,*FT); + + # write out the blank printIt file - this is a work around for a NS6 bug (22681) + open(FT,">$out_dir"."blank_printIt.html") || + die "Error: can not open file blank_printIt.html to write: $!\n "; + push(@output_files,"$out_dir"."blank_printIt.html"); + print FT "<html><head>\n"; + print FT "<title></title>\n"; + print FT $style_sheet_link; + print FT "</head><body onload=\"javascript:parent.printIt('',-1)\"></body></html>\n"; + close (FT); + + + $out_file = $out_dir . $h_file; + open(HIER,">$out_file") || + die "Error: can not open file $out_file to write: $!\n"; + push(@output_files,"$out_file"); + + if ((@{$hier_tops_p})) { + # check that all the hierarchy tops (specified with -ht) exist + foreach $module (@{$hier_tops_p}) { + die "Error: Did not find top module $module\n" + if (!&rvp::module_exists($verilog_db,$module)); + } + } + else { + # search for all hier tops - modules that are not instantiated + # but do instantiate at least one module that we have source for + MOD_LOOP: foreach $m (sort &rvp::get_modules($verilog_db)) { + print " checking if module $m is a top\n" if $debug; + if (! &rvp::get_first_instantiator($verilog_db,$m)) { + ($imod) = &rvp::get_first_instantiation($verilog_db,$m ); + while ($imod) { + print " checking instantiation $imod\n" if $debug; + if (&rvp::module_exists($verilog_db,$imod)) { + push(@{$hier_tops_p},$m); + next MOD_LOOP; + } + ($imod) = &rvp::get_next_instantiation($verilog_db); + } + } + } + } + + if ($js) { + print " doing js version\n" if $debug; + $js_file = $out_file; + $js_file =~ s/.html$//; + $js_file = $js_file . ".js"; + open(JSF,">$js_file") || + die "Error: can not open file $js_file to write: $!\n"; + + # do the java script version first + print_js_hier_head(*JSF,join(" ",@{$hier_tops_p})); + print_hier_body(*JSF,$hier_tops_p,1); + print_js_hier_tail(*JSF); + close(JSF); + } + + # now do the html version + print " doing html version\n" if $debug; + print_hier_head(*HIER,join(" ",@{$hier_tops_p}),$js,$js_file,$h_file); + print_hier_body(*HIER,$hier_tops_p,0); + print_hier_tail(*HIER,$js); + close(HIER); + print " done html version\n" if $debug; +} + +############################################################################### +# Print out header information in hierarchy file +# +sub print_hier_head { + my ($out,$t,$js,$js_file,$h_file) = @_; + + print " print_hier_head\n" if $debug; + print $out "<!-- v2html hierarchy "; + print $out "K=$cgi_key " if $cgi_script; + print $out "-->\n"; + print $out "<html><head>\n"; + + if ($js) { + print_js_include( $out , $js_file); + unlink($js_file) unless $frames; # do not need the javascript file anymore + } + + print $out "<title>hierarchy: $t</title>\n"; + print $out $style_sheet_link; + print $out "</head>\n"; + + print $out "<noscript>" if ($js); + + print $out "<body>\n"; + + + print $out "<center><b>The Javascript features of this page are not ". + "working in your browser. Either you do not have a Javascript capable". + " browser (NS3, IE4 or later) or you have Javascript". + " disabled in your preferences.</b></center><br>" + if ($js); +} + +############################################################################### +# Print out the hierarchy +# arguments: output_file_name, javascript flag +# +sub print_hier_body { + my ($out,$hier_tops_p,$js) = @_; + my ($f,$m,$m_line,$fb,@no_mods,$nm,@unconnected,$imod); + + print " print_hier_body\n" if $debug; + $ul_id=0; # used for folding hierarchy + + print_navbar($out,$js,'Hierarchy',\%navbar,'','',$vert_frames); + print_h_or_js($out, "<center><h3>$hier_comment</h3></center>\n",$js) if $hier_comment; + + # + # Go through each module you were asked for + # + foreach $m (@{$hier_tops_p}) { + ($f,$m_line) = &rvp::get_modules_file($verilog_db,$m); + print_h_or_js($out, "<h3>Hierarchy for " . $m . "</h3>\n",$js); + print_h_or_js($out, "<nobr><ul>\n",$js); + print_tree($out,$m,$m_line,$f,0,1,$js); + print_h_or_js($out, "</ul></nobr>\n",$js); + print_h_or_js($out, "<hr>\n",$js); + } + + # + # now do files containing no modules + # + if ($print_no_mods) { + foreach $f (&rvp::get_files($verilog_db)) { + push(@no_mods,$f) + if (0 == &rvp::get_files_modules($verilog_db,$f)); + } + if (@no_mods) { + print_h_or_js($out, "<h3>Files containing no modules</h3>\n",$js); + print_h_or_js($out, "<ul>\n",$js); + foreach $f (@no_mods) { + ($fb = $f) =~ s/^.*\///; + print_h_or_js($out, "<li>" . "<a $frame_code href=\"" + . hfile($f,1) . "\">$fb</a>\n",$js); + if ($output_index) { + print_h_or_js($out,"<a $frame_code href=\"". + index_link("Files",$fb)."\">$icon_i<\/a>\n",$js); + } + } + print_h_or_js($out, "</ul>\n",$js); + print_h_or_js($out, "<hr>\n",$js); + } + } + + # + # Now do unconnected modules + # (unreffed modules that do not instantiate any other modules + # that we have source for) + # + if ($print_unconnected) { + MOD_LOOP: foreach $m (sort &rvp::get_modules($verilog_db)) { + print " checking if module $m is unconnected\n" if $debug; + # the hier tops have been done already, so ignore them + foreach $imod (@{$hier_tops_p}) { + if ($imod eq $m) { next MOD_LOOP; } + } + if ( ! &rvp::get_first_instantiator($verilog_db,$m)) { + ($imod) = &rvp::get_first_instantiation($verilog_db,$m ); + while ($imod) { + if (&rvp::module_exists($verilog_db,$imod)) { + next MOD_LOOP; + } + ($imod) = &rvp::get_next_instantiation($verilog_db ); + } + # if we got here we are unconnected + push(@unconnected,$m); + } + } + if (scalar(@unconnected) != 0) { + if ((scalar(@unconnected) == 1)&&(!(@{$hier_tops_p}))) { + print_h_or_js($out, "<h3>$unconnected[0]</h3>\n",$js); + } + else { + print_h_or_js($out, "<h3>Unconnected modules</h3>\n",$js); + } + + print_h_or_js($out, "<nobr><ul>\n",$js); + foreach $m (@unconnected) { + # even though we're unconnected print_tree is called + # because there may be submodules we do not have source for + ($f,$m_line) = &rvp::get_modules_file($verilog_db,$m); + print_tree($out,$m,$m_line,$f,0,1,$js); + } + print_h_or_js($out, "</ul></nobr>\n",$js); + print_h_or_js($out, "<hr>\n",$js); + } + } +} + +############################################################################### +# Recursively Print the hierarchical tree for a module +# +sub print_tree { + my ($out,$m,$m_line,$f,$depth,$n_inst,$js) = @_; + my (@t,$tt,$count,$this_ul_id,$this_ces_arg,$prev_ces_arg,$i,$imod,$tl,$tf); + + print " print_hier_tree($out,$m,$m_line,$f,$depth,$n_inst,$js)\n" if $debug; + if ($js) { + if ( defined($index_stack[0]) ) { + $prev_ces_arg = "ul_i_" . $index_stack[$#index_stack]; + } + else { + $prev_ces_arg = "1"; + } + print $out " if ( $prev_ces_arg ) {\n"; + } + + print_h_or_js($out," " x $depth . "<li>" . + "<a $frame_code href=\"" . + hfile($f,$m_line) . "#" . $m . "\">" . $m . "</a>\n",$js); + if ($output_index) { + print_h_or_js($out,"<a $frame_code href=\"".index_link("Modules",$m)."\">". + "$icon_i<\/a>\n",$js); + } + + + print_h_or_js($out," " x $depth . "x $n_inst\n",$js) if ($n_inst!=1); + print $out " }\n" if ($js); + + @t=(); + if ( (($imod) = &rvp::get_first_instantiation($verilog_db,$m )) || + ($t_and_f_in_hier && scalar(&rvp::get_modules_t_and_f($verilog_db,$m)))) { + # get all the things it instantiates and call print tree on them + while ($imod) { + push(@t,$imod); + ($imod) = &rvp::get_next_instantiation($verilog_db); + } + @t = sort( @t ); + $this_ul_id = $ul_id; + $ul_id++; + push(@index_stack,$this_ul_id); + $this_ces_arg = "ul_i_$this_ul_id"; + if ($js) { + print $out " $this_ces_arg = " . + "$prev_ces_arg && check_expand_string(control,$this_ul_id);\n"; + print_h_or_js($out,"<a name=\"ul_id_$this_ul_id\"></a>\n",1); + print $out " if ( $this_ces_arg ) {\n"; + print_h_or_js($out,"<a href=\"javascript:parent.printIt(\\'' + " . + " new_expand_string(control,$this_ul_id,'C') + " . + " '\\', $this_ul_id)". + "\"> $icon_c</a>\n<ul>\n",1); + print $out " }\n",; + print $out " else { \n"; + print $out " if ( $prev_ces_arg ) {\n"; + print_h_or_js($out,"<a href=\"javascript:parent.printIt(\\'' + " . + " new_expand_string(control,$this_ul_id,'X') + " . + " '\\', $this_ul_id)". + "\"> $icon_x</a>\n",1); + print $out " }\n }\n"; + + } + else { + print_h_or_js($out, "<ul> <!-- ul_id=$this_ul_id -->\n",0); + } + for ($i=0;$i<=$#t;$i++) { + $tt = $t[$i]; + $count=1; + while (($i<$#t) && ($t[$i] eq $t[$i+1])) { $i++; $count++; } + + if ( ! &rvp::module_exists($verilog_db,$tt) ) { + print $out " if ( $this_ces_arg ) {\n" if ($js); + print_h_or_js($out," " x $depth . " <li>$tt\n",$js); + print_h_or_js($out," " x $depth . " x $count\n",$js) + if ($count!=1); + print_h_or_js($out, + " (not linked because of duplicate definition)\n", + $js) + if (&rvp::module_ignored($verilog_db,$tt)); + print $out " }\n" if ($js); + } + else { + ($tf,$tl) = &rvp::get_modules_file($verilog_db,$tt), + print_tree($out,$tt,$tl,$tf,$depth+1,$count,$js); + } + } + if ($t_and_f_in_hier) { + print $out " if ( $this_ces_arg ) {\n" if $js; + &print_hier_t_and_f($out,$m,$depth,$js); + print $out " }\n" if $js; + } + if ($js) { + print $out " if ( $this_ces_arg ) {\n"; + print_h_or_js($out, "</ul>\n",1); + # Print the string if it is getting too long, this stops netscape's memory usage exploding + # when viewing large hierarchies. Open the document before doing the first write. + print $out " if (Text.length >5000) { if (!doc_open) { parent.upper.document.open(); doc_open=1; }". + " parent.upper.document.write(Text); Text=''; }\n"; + print $out " }\n"; + } + else { + print_h_or_js($out, "</ul> <!-- ul_id=$this_ul_id -->\n",0); + } + pop(@index_stack); + } + +} + +sub print_hier_t_and_f { + my($out,$m,$depth,$js) = @_; + my (@t_and_f,$tf,$t_type,$t_line,$t_file,$t_anchor); + + if ( @t_and_f = &rvp::get_modules_t_and_f($verilog_db,$m) ) { + foreach $tf (sort @t_and_f) { + ($t_type,$t_line ,$t_file,$t_anchor)= + &rvp::get_modules_t_or_f($verilog_db,$m,$tf); + $t_type = 'func' if ($t_type eq 'function'); + print_h_or_js($out," " x $depth . "<li><i>$t_type:</i> " . + "<a $frame_code href=\"" . + hfile($t_file,$t_line) . "#" . $t_anchor . "\">" . + $tf . "</a>\n",$js); + if ($output_index) { + if ($t_type eq 'task') { + print_h_or_js($out,"<a $frame_code href=\"". + index_link("Tasks","${tf}___$m")."\">". + "$icon_i<\/a>\n",$js); + } + else { + print_h_or_js($out,"<a $frame_code href=\"". + index_link("Functions","${tf}___$m")."\">". + "$icon_i<\/a>\n",$js); + } + } + } + } + +} + +############################################################################### +# Either print a string directly or output some javascript +# that will end up printing it +# +sub print_h_or_js { + my ($out,$s,$js) = @_; + + + + if ($js) { + $s =~ s/\n/\\n/g; + $s =~ s|</|<\\/|g; + print $out " Text += '" . $s . "';\n"; + } + else { + $s =~ s|\\'|'|g; #' + print $out $s; + } +} + +############################################################################### +# print the bottom of the hierarchy +# +sub print_hier_tail { + my ($out,$js) = @_; + + print " print_hier_tail\n" if $debug; + print_navbar($out,0,'Hierarchy',\%navbar,'','',$vert_frames); + print_footer($out,'',$vert_frames,0); + print $out "</body>\n"; + + if ($js) { + print $out "</noscript>\n"; + print $out " <frameset cols=\"100%,*\">\n"; + print $out " <frame name=\"upper\" " . + "src=\"blank_printIt.html\">\n"; + print $out " </frameset>\n"; + } + print $out "</html>\n"; +} + +############################################################################### +# Print the top of the javascript hierarchy (functions that +# will end up printing the hierarchy) +# +sub print_js_hier_head { + my ($out,$t) = @_; + + print_js_common($out); + + #####################JAVASCRIPT START################################## + print $out <<EOF; + +// dummy function in case someone tries to do a search in the hierarchy window +function search () { return false; } + +function check_expand_string (control,index) { + if (control.charAt(0)=='A') { + return 1; + } + else if ( index < control.length ) { + return (control.charAt(index)=='X'); + } + else { + return 0; + } +} + +function new_expand_string (control,index,v) { + var newString; + if ( index < control.length ) { + newString = control.substring(0,index) + v + + control.substring(index+1,control.length); + } + else { + if (control == 'A') { newString = 'X'; } + else { newString = control; } + for (var i=0; i<(index-control.length); i++) { + if (control == 'A') { newString += 'X' } + else { newString += 'C' } + } + newString += v; + } + return newString; +} + +function getCookie(Name) { + var search = Name + "="; + if (document.cookie.length > 0) { // if there are any cookies + offset = document.cookie.indexOf(search) ; + if (offset != -1) { // if cookie exists + offset += search.length ; + // set index of beginning of value + end = document.cookie.indexOf(";", offset) ; + // set index of end of cookie value + if (end == -1) + end = document.cookie.length; + return unescape(document.cookie.substring(offset, end)); + } + } + return ''; +} + +function setCookie(name, value) { + var today = new Date(); + var expires = new Date(); + expires.setTime(today.getTime() + 1000*60*60*24*365); + + document.cookie = name + "=" + escape(value) + + "; expires=" + expires.toGMTString(); +} + +function printIt (control,loc) { + var Text=''; + var doc_open=0; +EOF + #####################JAVASCRIPT END#################################### + print $out " if (control.length==0) control=getCookie(\"v2html $t\");\n" + if $js_cookies; + print $out " if (control.length==0) control='C';\n"; + print $out " setCookie(\"v2html $t\",control);\n" + if ($js_cookies); + + + print_h_or_js($out,"<html><head>\n",1); + print_h_or_js($out,"<title>hierarchy: $t</title>$style_sheet_link</head>\n",1); + print_h_or_js($out, "<body>",1); + +} + +############################################################################### +# Print out the end of the javascript hierarchy +# +sub print_js_hier_tail { + my ($out) = @_; + + print_navbar($out,1,'Hierarchy',\%navbar,'','',$vert_frames); + print_footer($out,'',$vert_frames,1); + + #####################JAVASCRIPT START################################## + print $out <<EOF; + + if (!doc_open) parent.upper.document.open(); + parent.upper.document.write(Text); + Text=''; + parent.upper.document.write('<\\/body><\\/html>'); + parent.upper.document.close(); + if (loc != -1) { + if (is_nav5up) + // - 18 needed because this is the height of the icon (I think) + parent.upper.scrollTo(0,parent.upper.document.anchors[loc].offsetTop-18); + else if (is_nav4up) + parent.upper.scrollTo(0,parent.upper.document.anchors[loc].y); + else if (is_ie4up) + parent.upper.document.anchors[loc].scrollIntoView(true) + } +} + +EOF + #####################JAVASCRIPT END#################################### +} + +############################################################################### +# Print out the html that will include the javascript +# +sub print_js_include { + my ($out , $jsf) = @_; + + # this does not work without server's mimetypes being configured: + # print $out "<script language=\"JavaScript\" src=\"$jsf\"></script>\n"; + + # this works whatever (copy all the js code into current file!) + print $out "<script language=\"JavaScript\" type=\"text\/javascript\"><!--\n"; + open (TTT,"<$jsf") || die "Error: can not open file $jsf to read: $!\n"; + while (<TTT>) { print $out $_; } + close(TTT); + print $out "// -->\n"; + print $out "</script>\n"; +} + +sub print_js_sigpopup { + my ($out) = @_; + + # Be careful when writing this: comments get stripped out later + # rather crudely - so using // in a string for example will break! + #####################JAVASCRIPT START################################## + my $script = <<EOF; + +var disabled=1; +if (!is_nav4up) { + var event=false; // prevent from dieing ns3 and ie4up +} +var last_link=0; // last link found in search +var last_class=null; // class of last link changed to highlighted + +// main quick search function called when any link is clicked +function qs(e,t,extra_info_index) { + var inc=0,bnum=0,i,j; + if (disabled) return false; + // buttons for signal window in order of extra info + var sig_buttons = [ "Definition" , "Local Driver" , + "Up to Input Driver" , "Find Source" , "Index"]; + + if (is_nav4up || is_ie4up) { + // test if quicksearch is wanted: + // forwards = button 2 or button 1 + control + // backwards = button 2 + shift or button 1 + shift + + if (((e.which==2) && (!(e.modifiers&Event.SHIFT_MASK))) || + ((e.which==1) && (e.modifiers&Event.CONTROL_MASK))) inc = 1; + else if (((e.which==2) && (e.modifiers&Event.SHIFT_MASK)) || + ((e.which==1) && (e.modifiers&Event.SHIFT_MASK))) inc = -1; + + if (inc == 0 && extra_info_index == 0) { // no quick search and no extra_info, so just return + return true; // follow link as normal + } + + var linkText = is_nav4up ? t.text : t.innerText; + var linkY = is_nav4up && ! is_nav5up ? t.y : t.offsetTop; + // find the index of the current link + window.status="Searching..."; + if ((last_link==-1) || (document.links[last_link]!=t)) // try previous link first + for (last_link=0;last_link<document.links.length;last_link++) + if (document.links[last_link] == t) + break; + + if (inc != 0) { // do the quick search + return search(linkText,linkY,last_link,inc,1); + } + else { // do something with the extra_info + window.status=""; + extra_info_index--; // when passed, 0 means no data so decrement to get index + if (extra_info[extra_info_index][0] != 'S') { // check the type is signal + return true; + } + // open a window, or get a handle to an existing window - unfortunately + // if the window is open and has been resized manually it will be + // changed back to its initial size (I do not know anyway around this + // apart from opening it with nosize then checking if it has already + // been written and if not closing it and opening one with default size. + // This would look very clunky though!) + var w = window.open('','SignalPopUp','width=200,height=235'); + + // check to see if the window is already written and if it is then + // if the latest file is in the same dir as the file that first + // opened to window - if no then close and reopen the window + // otherwise the old links go screwy + if (null != w.document.forms[0]) { + if ((window.location.pathname.substring(0,window.location.pathname.lastIndexOf(dirSep)))!= + (w.pn.substring(0,w.pn.lastIndexOf(dirSep)))) { + w.close(); + w = window.open('','SignalPopUp','width=200,height=235'); + } + } + + w.focus(); // raise popup window (does not work on linux NS4.51) + + if (null == w.document.forms[0]) { // true if the window has not yet been written + var Text = '<html><head></head>'; + + + // make some variables that are local to the popup window + if (is_nav4up) { // can not get the script way to work in NS... + w.loc = new Array(10); + w.sel = null; + w.pn = window.location.pathname; + } + else { // ...can not get the w. way to work in IE + Text += '<script>var loc = new Array(10);<\\/script>\\n'; + Text += '<script>var sel;<\\/script>\\n'; + Text += '<script>var pn = opener.location.pathname;<\\/script>\\n'; + } + + Text += '<body bgcolor="white">\\n'; + Text += '<form>'; + Text += ' <select onchange="opener.setbuttons(window);">\\n'; + // ns4 on windows does not grow the width so start it really wide + Text += ' <option>---------------------------</option>\\n'; + // ns4 on windows does not grow the length and IE4 dies if + // I leave the field blank! + for (j=0;j<9;j++) Text += ' <option>-</option>\\n'; + Text += ' </select>\\n'; + Text += '</form>'; + + Text += '<table cellspacing=0 cellpadding=0>\\n'; + + // length-1 because location 0 of extra_info is the type, so ignore it + for (var i=0;i<(extra_info[extra_info_index].length-1);i++) { + Text += hbutton(sig_buttons[i], + 'opener.location=loc[sel.selectedIndex]['+i+'];', + bnum++); + } + Text += hbutton("Search Backwards", + 'opener.search(sel.options[ sel.selectedIndex ].text,' + + '0,opener.last_link,-1,0);',bnum++); + Text += hbutton("Search Forwards", + 'opener.search(sel.options[ sel.selectedIndex ].text,' + + '0,opener.last_link, 1,0);',bnum++); + Text += hbutton("Close","window.close();",bnum++); + + Text += '</table>\\n'; + Text += '</body></html>\\n'; + + w.document.open(); + w.document.write(Text); + w.document.close(); + w.document.forms[0].elements[0].options[0].text = linkText; + // save space in the window code by defining this short cut + w.sel = w.document.forms[0].elements[0]; // refer to the forms elements + + for (j=0;j<10;j++) w.loc[j] = new Array(sig_buttons.length); + copy_into_loc0(w,extra_info_index); + } + else { + var opts = w.document.forms[0].elements[0].options; + // this code is obsolete - I have to start at size 10 anyway + // because ns4 under windows will not grow the list dynamically + if ( opts.length<10 ) { + w.loc[opts.length] = new Array; + opts.length++; + } + for (i=opts.length-2;i>=0;i--) { + opts[i+1].text=opts[i].text; + for (var j=0;j<w.loc[i].length;j++) w.loc[i+1][j] = w.loc[i][j]; + } + opts[0].text = linkText; + copy_into_loc0(w,extra_info_index); + } + + return false; + } + } + return true; +} + +// Return the html for a button labeled with text that does the specified +// action. Also needs bnum which is the number of the buttons image in the +// imagesarray. +// +function hbutton (text,action,bnum) { + return ' <tr><td><a href="" '+ + 'onmousedown="'+ + 'if (images['+bnum+'].src.match(/v2html-b2.gif/)) return false; ' + + 'images['+bnum+'].src=\\'v2html-b3.gif\\';" '+ + 'onmouseup="'+ + 'if (images['+bnum+'].src.match(/v2html-b2.gif/)) return false; ' + + 'images['+bnum+'].src=\\'v2html-b1.gif\\';" '+ + 'onclick="'+ + // do not do anything if the button is greyed out + 'if (images['+bnum+'].src.match(/v2html-b2.gif/)) return false; ' + + // do action + action + + ' return false;">'+ + '<img border=0 src="v2html-b1.gif"></a></td>' + + '<td style="font-family:sans-serif; font-weight:bold; font-size:14px;"> '+text+'</td></tr>\\n'; +} + +function copy_into_loc0 (w,extra_info_index) { + for (var i=1;i<extra_info[extra_info_index].length;i++) { + w.loc[0][i-1] = extra_info[extra_info_index][i]; + } + // the next line is here because mozilla 1.0 (and ns7 pr1) do not + // update desplay of the first droplist idem when the item changes + // (only when the selected Index changes) + w.document.forms[0].elements[0].selectedIndex=1; + w.document.forms[0].elements[0].selectedIndex=0; + setbuttons(w); +} + +function search(text,y,i,inc,relative) { + var nextpage,wrappage,linkText,linkY; + + window.status="Searching..."; + if (last_class) document.links[i].className=last_class; + + while (1) { + for (i+=inc;i<document.links.length && i>=0;i+=inc) { + linkText = is_nav4up ? document.links[i].text : document.links[i].innerText; + linkY = is_nav4up && ! is_nav5up ? document.links[i].y : document.links[i].offsetTop; + if ((linkText == text) && (linkY != y)) { + window.status=""; + // NOTE: have not done relative (used up Quick search) for IE4 + // because I have not got the button capture to work for QS under IE4 + if (is_nav4up) + if (relative) window.scrollBy(0,linkY - y); + else window.scrollTo(0,linkY); + else if (is_ie4up) + document.links[i].scrollIntoView(true); // IE put link at top of screen + last_link=i; + last_class=document.links[i].className; + document.links[i].className='HI'; + return false; + } + } + nextpage = (inc==1) ? next_page() : prev_page(); + wrappage = (inc==1) ? first_page() : last_page(); + if (nextpage!="" || wrappage!="") { + if (nextpage=="") { // only ask when wrapping around + if (!confirm(text + " not found. Search again from "+((inc==1)?"first":"last")+" page?")) + return false; + nextpage=wrappage; + } + location=nextpage+ "?" + escape(text) + "&" + ( y - window.pageYOffset ) + "&" + inc; + return false; + } + if (confirm(text + " not found. Search again from "+((inc==1)?"start":"end")+"?")) { + if (inc==1) i=-1; + else i=document.links.length; + } else return false; + } + return true; +} + +function loadqs() { + var opt=location.search, text="", s="", y=0, si=0, inc=1; + + if (opt.length==0) return true; // no query string + for (var i=1;i<opt.length;i++) { // parse query string: search_text&y_pos&direction + if (opt.charAt(i) != "&") + s += opt.charAt(i); + else { + if (text=="") text=unescape(s); + else y=s; + s=""; + } + } + if (text=="") return true; + if (s == "-1") { si=document.links.length-1; inc=-1; } + window.scrollTo(0,0); + search(text,y,si,inc); + return true; +} +EOF + #####################JAVASCRIPT END#################################### + + if (!$debug) { + # save space in each file + $script =~ s|//.*$||gm; # strip comments + $script =~ s|^\s+||gm; # strip leading whitespace + $script =~ s|^\n||gm; # strip blank lines + } + + print $out "<script language=\"JavaScript\" type=\"text\/javascript\"><!--\n"; + print_js_common($out); + print $out "// Unindented and uncommented to save spave ". + "- look in v2html for a prettier version\n"; + print $out $script; + print $out "// -->\n"; + print $out "</script>\n"; + +} + + +############################################################################### +# Print javascript that occurs in every file +# +sub print_js_common { + my ($out) = @_; + + #####################JAVASCRIPT START################################## + my $script= <<EOF; +// this code is a cut down version of the netscape detector code +var agt=navigator.userAgent.toLowerCase(); +var is_nav = ((agt.indexOf('mozilla')!=-1) && + (agt.indexOf('spoofer')==-1) && + (agt.indexOf('compatible') == -1) && + (agt.indexOf('opera')==-1) && + (agt.indexOf('webtv')==-1)); +var is_major = parseInt(navigator.appVersion); +var is_nav4up = (is_nav && (is_major >= 4)); +var is_ie = (agt.indexOf("msie") != -1); +var is_ie4up = (is_ie && (is_major >= 4)); +var is_nav5up = (is_nav && (is_major >= 5)); + +// dirSep stores the directory separator. Note IE file urls +// have \ _and_ / in - so look for \ and assume it is the separator +// if you find it. Otherwise use the unix / +var dirSep = (window.location.pathname.indexOf('\\\\') != -1) ? '\\\\' : '/' ; + +// Grey out buttons that will do nothing +// - called from the signal popup, so must be in all files to avoid errors +function setbuttons (wndw) { + var i; + + // first do the buttons from extra_info (locations stored in loc) + var sl=wndw.loc[ wndw.document.forms[0].elements[0].selectedIndex ]; + + for (i=0;i<sl.length;i++) { + if(sl[i]) wndw.document.images[i].src='v2html-b1.gif'; + else wndw.document.images[i].src='v2html-b2.gif'; + } + // now do the search buttons + if ( wndw.document.forms[0].elements[0].options[ + wndw.document.forms[0].elements[0].selectedIndex ].text != '-') { + wndw.document.images[i ].src='v2html-b1.gif'; + wndw.document.images[i+1].src='v2html-b1.gif'; + } + else { + wndw.document.images[i ].src='v2html-b2.gif'; + wndw.document.images[i+1].src='v2html-b2.gif'; + } +} + +EOF + #####################JAVASCRIPT END#################################### + + if (!$debug) { + # save space in each file + $script =~ s|//.*$||gm; # strip comments + $script =~ s|^\s+||gm; # strip leading whitespace + $script =~ s|^\n||gm; # strip blank lines + } + print $out $script; +} + + +############################################################################### +# Print the frame.html file +# +sub print_frame_top { + my ($f,$h,$title,$js,$vert_frames) = @_; + my ($out_file); + local (*FT); + + # first write out the blank file + open(FT,">$out_dir"."blank.html") || + die "Error: can not open file blank.html to write: $!\n "; + push(@output_files,"$out_dir"."blank.html"); + print FT "<html><head>\n"; + print FT "<title>verilog</title>\n"; + print FT $style_sheet_link; + print FT "</head><body></body></html>\n"; + close (FT); + + + # now output the frame top + $out_file = $out_dir . $f; + open(FT,">$out_file") || + die "Error: can not open file $out_file to write: $!\n "; + push(@output_files,$out_file); + + print FT "<html><head>\n"; + + if ($js) { + print_js_include( *FT , $js_file); + unlink($js_file); # do not need the javascript file anymore + } + + print FT "<title>verilog view: $title</title>\n"; + print FT $style_sheet_link; + print FT "</head>\n"; + + print FT "<noscript>\n" if ($js); + print FT frameset( $vert_frames, $h ); + + if ($js) { + print FT "</noscript>\n"; + print FT frameset($vert_frames,"blank_printIt.html"); + } + print FT "</html>\n"; + + close(FT); + +} + +sub frameset { + my ($vert_frames,$middle_page_url) = @_; + my ($rval); + if ($vert_frames) { + $rval= "<frameset cols=\"30%,70%\">\n". + " <frame name=\"upper\" src=\"$middle_page_url\">\n". + " <frameset rows=\"90%,10%\">\n". + " <frame name=\"middle\" src=\"blank.html\">\n". + " <frame name=\"bottom\" src=\"blank.html\">\n". + " </frameset>\n"; + } + else { + $rval= "<frameset rows=\"20%,70%,10%\">\n". + " <frame name=\"upper\" src=\"$middle_page_url\">\n". + " <frame name=\"middle\" src=\"blank.html\">\n". + " <frame name=\"bottom\" src=\"blank.html\">\n"; + } + if ($middle_page_url !~ /^javascript/) { + $rval .= "<body>\n<noframes>\n". + " Your browser does not support frames click\n". + " <a href=\"$middle_page_url\">here</a> to see the hierarchy\n". + "</noframes></body>\n"; + } + $rval.="</frameset>\n"; + return $rval; + +} + + +############################################################################### +# Output open an closed suitcase icons +# scripts/encode_gif.pl can generate the print_gif code automagically +# +sub print_gifs { + my ($out_dir,$print_xc,$print_i)=@_ ; + + if ($print_xc) { + # uuencoded gif file for v2html-c.gif + print_gif( "${out_dir}v2html-c.gif" , + 'M1TE&.#EA&P`2`,(``/C\^`````@@6+C,R)AD,#`P,/_______R\'Y!`$*``<`'."\n". + 'M+``````;`!(```-:>!?<K##*&(2]-LP-*_[.PRU?"8Z>J6I;6KI8^,!7,-#V'."\n". + 'MH.L"8^:WT&XG*/A`PZ2RJ($!E4NCPO6$#IF=FA6*S?:V45:V"NY2R%NSA*8J'."\n". + '.2%\'NN%PN&BUD>\'L"`#L`'."\n". + '`'."\n"); + # uuencoded gif file for v2html-x.gif + print_gif( "${out_dir}v2html-x.gif" , + 'M1TE&.#EA%``2`,(``/C\^````+C,R`@@6#`P,,C\^/_______R\'Y!`$*``<`'."\n". + 'M+``````4`!(```-->!?<K#"J(*JM049ZNWO3T%FB-PE#JJ8CFG\'MF!),\'`\T'."\n". + 'M;)-XO7<]W6_F^Y%R1AXR60DR44Z45$2=HFB+E79+P"ZZX+"81H!\S@Y"(0$`'."\n". + '!.P``'."\n". + '`'."\n"); + + } + # uuencoded gif file for v2html-up.gif + print_gif( "${out_dir}v2html-up.gif" , + 'M1TE&.#EA&P`2`*$``/C\^````+C,R/___R\'Y!`$*``,`+``````;`!(```(Z'."\n". + 'MG(^IRQC18@KB24E%A7=EO7432%KB\)&:>:7JRD7N"\O/K=XWIN?[B?(!#RX6'."\n". + '/L!@[ZGY#U#+9C$H-!0`['."\n". + '`'."\n"); + if ($print_i) { + # uuencoded gif file for v2html-i.gif + print_gif( "${out_dir}v2html-i.gif" , + 'M1TE&.#EA!``\'`(```/____\``"\'Y!`$`````+``````$``<```((1`*&R9>N'."\n". + '$D"L`.P``'."\n". + '`'."\n"); + } + + if ($js_sigs) { + print_gif( "${out_dir}v2html-b1.gif" , + 'M1TE&.#EA&P`2`,(``/C\^+BXN`@@6,C\^+C,R#`P,/_______R\'Y!`$*``<`'."\n". + 'M+``````;`!(```-+>+K<_C#*$ZJ].%>FNQ9<,!!D:9ZE`"[5B+ZF&KHP++-B'."\n". + 'L7=]*J]NK7NZ\'XE&&Q%CP2$N2C#YG:AF5$J"JK\';++2R]W\'"X,"F;SXX$`#L`'."\n". + '`'."\n"); + print_gif( "${out_dir}v2html-b2.gif" , + 'M1TE&.#EA&P`2`,(``/C\^+BXN`@@6+C,R,C\^#`P,/_______R\'Y!`$*``<`'."\n". + 'M+``````;`!(```-0>+K<_C#*$P*]-N-=F?Y<*#36<)CHJ:;FZ!W$*K.RNY1S'."\n". + 'MGMJ*%=/`&DE\';`U_Q1SO$FP>EKYD$"I5\'JNTY6C[[\'*_AT)##"Y[N^*)>LUN'."\n". + '$)```.P``'."\n". + '`'."\n"); + print_gif( "${out_dir}v2html-b3.gif" , + 'M1TE&.#EA&P`2`,(``/C\^#`P,`@@6+BXN+C,R/___________R\'Y!`$*``<`'."\n". + 'M+``````;`!(```-(>+K<_C#*$X2]..O`1-6@-G0"89YHB@[C8JFPRI)Q;<YN'."\n". + 'I:<>X\NZPWN$\'3`F)Q=-1EU2V?,PF82EU=EC8K\'8KY\'J]D[!X[$@``#L`'."\n". + '`'."\n"); #`) + } +} + +sub print_gif { + my ($file,$data) = @_; + + print "Writing icon $file\n" unless $quiet; + open(GIF,">$file") || + die "Error: can not open file $file to write: $!\n "; + binmode(GIF); + print GIF unpack("u",$data); + close(GIF); +} + + +############################################################################### +# Output cascading style sheet +# +sub print_css { + my ($out_dir)=@_ ; + my ($c,$tag,$prop,$s,$checksum,$line); + + # do not overwrite + if ( -r "${out_dir}v2html.css") { + open(CSS,"<${out_dir}v2html.css") || + die "Error: can not open file ${out_dir}v2html.css to check: $!\n "; + + $checksum=$line=0; + while( defined($_=<CSS>)) { + $checksum += unpack("%32C*",$_) * ($line + 73); + $checksum %= 65535; + $line++; + } + close(CSS); + if (($checksum != 38677) && # the CSS generated by v2html 5.0 to 5.10 + ($checksum != 17848) && # temp checksum for versions 5.11 to 5.14 + ($checksum != 33561) && # temp checksum for versions to 6.14 + ($checksum != 56981) && # checksum for versions later than 6.39 + ($checksum != 39241) # checksum for versions later than 7.13 + ) { + print "Not overwriting edited CSS ". + "${out_dir}v2html.css (Checksum=$checksum)\n"; + return; + } + } + open(CSS,">${out_dir}v2html.css") || + die "Error: can not open file ${out_dir}v2html.css to write: $!\n "; + + # After changing the CSS run v2html in a blank directory twice + # the second time it should say: + # Not overwriting edited CSS new_html/v2html.css (Checksum=XXXXX) + # Put the XXXXX value into the list of checksum above, so that newer v2html + # will overwrite unedited CSS files created by older v2html versions. + print CSS <<EOF; +BODY { /* main_body */ + background:#FFFFFF; + color:#000000; +} +A { /* default link */ + text-decoration:underline; + color:#08215A; +} +TABLE.NB { /* navbar table */ + width:100%; +} +TABLE.NB TD { /* navbar table text */ + font-family:sans-serif; + background:#BBCCCC; + padding:0; + border:outset; + font-weight:bold; + font-size:12px; + text-decoration:none; +} +TABLE.NB A { /* navbar table links */ + color:#08215A; + text-decoration:none; +} +A.HI { background:#FFFF00; } /* highlighted link found in search (not in NS4.x)*/ + +/* verilog language elements */ +SPAN.C { color:#0000FF; } /* comment (non-link) */ +A.C { color:#0000FF; background:#CCCCFF; } /* comment (link) */ +SPAN.M { color:#9933FF; font-weight:bold; } /* compiler (non-link) */ +SPAN.D { color:#990099; } /* define (non-link) */ +A.D { color:#990099; } /* define (link) */ +SPAN.F { color:#CC0033; } /* function (non-link) */ +A.F { color:#CC0033; } /* function (link) */ +SPAN.K { color:#CC3300; } /* keyword (non-link) */ +SPAN.MM { color:#6600FF; } /* module (non-link) */ +A.MM { color:#6600FF; } /* module (link) */ +SPAN.PA { color:#CC0066; } /* parameter (non-link) */ +A.PA { color:#CC0066; } /* parameter (link) */ +SPAN.P { color:#999999; } /* pre-processor ignore (non-link) */ +SPAN.S { color:#009900; } /* string (non-link) */ +A.S { color:#009900; } /* string (link) */ +SPAN.ST { color:#9933FF; } /* systemtask (non-link) */ +SPAN.T { color:#660033; } /* task (non-link) */ +A.T { color:#660033; } /* task (link) */ + +SPAN.SIO { color:#00CC99; } /* signal inout (non-link) */ +A.SIO { color:#00CC99; } /* signal inout (link) */ +SPAN.SIOR { color:#009999; } /* signal inout_reg (non-link) */ +A.SIOR { color:#009999; } /* signal inout_reg (link) */ +SPAN.SI { color:#006600; } /* signal input (non-link) */ +A.SI { color:#006600; } /* signal input (link) */ +SPAN.SIT { color:#663333; } /* signal integer (non-link) */ +A.SIT { color:#663333; } /* signal integer (link) */ +SPAN.SO { color:#000066; } /* signal output (non-link) */ +A.SO { color:#000066; } /* signal output (link) */ +SPAN.SOR { color:#0000CC; } /* signal output_reg (non-link) */ +A.SOR { color:#0000CC; } /* signal output_reg (link) */ +SPAN.SRL { color:#663333; } /* signal real (non-link) */ +A.SRL { color:#663333; } /* signal real (link) */ +SPAN.SR { color:#FF9933; } /* signal reg (non-link) */ +A.SR { color:#FF9933; } /* signal reg (link) */ +SPAN.SS0 { color:#CC6600; } /* signal supply0 (non-link) */ +A.SS0 { color:#CC6600; } /* signal supply0 (link) */ +SPAN.SS1 { color:#CC6600; } /* signal supply1 (non-link) */ +A.SS1 { color:#CC6600; } /* signal supply1 (link) */ +SPAN.STM { color:#CC3333; } /* signal time (non-link) */ +A.STM { color:#CC3333; } /* signal time (link) */ +SPAN.SRT { color:#CC3333; } /* signal realtime (non-link)*/ +A.SRT { color:#CC3333; } /* signal realtime (link) */ +SPAN.STI { color:#CC6600; } /* signal tri (non-link) */ +A.STI { color:#CC6600; } /* signal tri (link) */ +SPAN.ST0 { color:#CC6600; } /* signal tri0 (non-link) */ +A.ST0 { color:#CC6600; } /* signal tri0 (link) */ +SPAN.ST1 { color:#CC6600; } /* signal tri1 (non-link) */ +A.ST1 { color:#CC6600; } /* signal tri1 (link) */ +SPAN.STA { color:#CC6600; } /* signal triand (non-link) */ +A.STA { color:#CC6600; } /* signal triand (link) */ +SPAN.STO { color:#CC6600; } /* signal trior (non-link) */ +A.STO { color:#CC6600; } /* signal trior (link) */ +SPAN.STR { color:#CC6600; } /* signal trireg (non-link) */ +A.STR { color:#CC6600; } /* signal trireg (link) */ +SPAN.SWA { color:#CC6600; } /* signal wand (non-link) */ +A.SWA { color:#CC6600; } /* signal wand (link) */ +SPAN.SW { color:#CC6600; } /* signal wire (non-link) */ +A.SW { color:#CC6600; } /* signal wire (link) */ +SPAN.SWO { color:#CC6600; } /* signal wor (non-link) */ +A.SWO { color:#CC6600; } /* signal wor (link) */ +SPAN.GV { color:#CC6600; font-weight:bold } /* V2001 genvar */ +A.GV { color:#CC6600; font-weight:bold } /* V2001 genvar */ +SPAN.AT { color:#CC00CC; font-weight:bold } /* V2001 attribute */ +A.AT { color:#CC00CC; font-weight:bold } /* V2001 attribute */ + +EOF + close(CSS); +} + +############################################################################### +# Converting verilog files as html +############################################################################### + +############################################################################### +# Convert a file to html +# +sub convert { + my ($f) = @_; + my ($match,$start,$end,$length,$line,$previous,$last_pos, + $text_s,$include,$fb,$out_file,$vstate,$out,$i); + local (*F); + + $line = 1; + $last_pos = 0; + + print "Converting $f\n" unless $quiet; + open(F,"<$f") || die "Error: can not open file $f to read: $!\n"; + $/ = undef; + $text_s = <F>; + $/ = "\n"; + close(F); + + if ($tabstop!=0) { + 1 while ($text_s =~ s/(^|\n)([^\t\n]*)(\t+)/ + $1. $2 . (" " x ($tabstop * length($3) - (length($2) % $tabstop))) + /gsex); + } + + + $fb = ffile($f); + + # this stores the state needed in output_v + $vstate = {}; + $vstate->{context} = '1'; + $vstate->{cur_inst} = '$NONE'; # current instance + $vstate->{cur_inst_exists} = 0; # current instance can be linked to + $vstate->{page} = 0; # page we're on + $vstate->{rhs} = 0; # on right hand side of = or <= + $vstate->{inst_link_done} = 0; # done the linking of the instance name + $vstate->{line_started} = 0; # done initial stuff for line + $vstate->{pre_ignore} = 0; # preprocessor told us to ignore + # lines until this one + $vstate->{name} = ''; # name of module or t or f we are in + $vstate->{type} = ''; # type of context module,task or function + $vstate->{lines} = &rvp::get_files_stats( $verilog_db, $fb ); + $vstate->{page_nb} = {}; # navbar for pages + + # we can put in shortened links (ie #x instead of file#x) unless we are using + # qs on a multipage file (because the ?query_string breaks with short links) + $vstate->{short_links} = (!$js_sigs) || ($vstate->{lines} < $lines_per_file); + + # store extra info for output at end of file in javascript array + # {NAME}{vindex} = number of index in javascript array + # {NAME}{value} = [] list of strings to store in array at this index + $vstate->{extra_info} = {}; + $vstate->{extra_info_last} = 0; + + # make page navbar for multipage files + if ($vstate->{lines} > $lines_per_file) { + $vstate->{page_nb}{order} = [ ]; + for ($i=1;(($i-1)*$lines_per_file)<$vstate->{lines};$i++) { + push ( @{$vstate->{page_nb}{order}} , $i ); + $vstate->{page_nb}{$i}= hfile($f,(($i-1)*$lines_per_file)+1); + } + } + + if ($text_s) { + while ($text_s =~ + m%\G((/\*.*?\*/)| # /* comments */ + (\(\*.*?\*\))| # (* attributes *) V2001 + (//.*?(?:\n|\Z))| # // comment + (\`include\s+\".*?\")| # include (string unblanked) + (\"(?:(?:\\\\)|(?:\\\")|([^\"]))*?\")| # string - ignore \\ and \" + (.*? # anything else followed by.. + (?=(/\*| # /* + (\(\*)| # or (* attribute + \(\*(?!\s*\))| # or (* [but not (*)] V2001 + //| # or // + \"| # or quote + \`include\s+\".*?\"| # or include + \Z) # or end of string + )) + )%gsox ) { + + $match = $1; + + if ( ($vstate->{page}*$lines_per_file) < $line ) { + convert_end_file($out,$f,$line-$lines_per_file,1,$vstate) + if $vstate->{page}; + $vstate->{page}++; + $out=convert_start_file($f,$line,$vstate->{page},$vstate->{lines}, + $vstate->{page_nb}); + $vstate->{file} = hfile($f,$line); # current html file + } + + if ( $match =~ m%^/% ) { + # either sort of comment + quote_html(\$match); + # link mail addresses in comments + $match =~ s/$mail_regexp/<a CLASS=C href=\"mailto:$1\">$1<\/a>/g; + # turn urls into links + $match =~ s/$http_regexp/<a CLASS=C href=\"$1\">$1<\/a>/g; + if ($grey_ifdefed_out && $vstate->{pre_ignore}) { + print_with_font($out,"pp_ignore",$match); + } + else { + print_with_font($out,"comment",$match); + } + } + elsif ( $match =~ m%^\(\*(?!\s*\))% ) { + # attribute, but not (*) + quote_html(\$match); + if ($grey_ifdefed_out && $vstate->{pre_ignore}) { + print_with_font($out,"pp_ignore",$match); + } + else { + print_with_font($out,"attribute",$match); + } + } + elsif ( $match =~ m%^\"% ) { + # string + quote_html(\$match); + if ($grey_ifdefed_out && $vstate->{pre_ignore}) { + print_with_font($out,"pp_ignore",$match); + } + else { + print_with_font($out,"string",$match); + } + } + elsif ( $match =~ m%\`include(\s+)\"(.*?)\"% ) { + # include + if ($grey_ifdefed_out && $vstate->{pre_ignore}) { + quote_html(\$match); + print_with_font($out,"pp_ignore",$match); + } + else { + print_with_font($out,"compiler","`include"); + print $out "$1"; + $include = $2; + if ( &rvp::file_exists( $verilog_db, ffile($include) )) { + print_link($out,$vstate->{file},$frame_middle,$js_sigs, + hfile($include,1),'',""$include"", + "string",$vstate->{short_links},0); + } + else { + print_with_font($out,"string", ""$include""); + } + } + } + elsif ( $match =~ m%\A\s*\Z% ) { + # whitespace + print $out $match; + } + else { + # verilog code (note: it can change $out if a new page is needed) + $out=&output_v($out,$match,$line,$fb,$vstate,$f); + } + + $line += ($match =~ tr/\n/\n/); + } + print $out "\n"; + } + else { # handle zero length files gracefully + $out=convert_start_file($f,$line,1,$vstate->{lines},$vstate->{page_nb}); + } + + convert_end_file($out,$f,$line,0,$vstate); + +} + +sub convert_end_file { + my ($out,$f,$line,$more,$vstate)=@_; + my ($out_file,$next_file,$first_file,@extra_info_array,$e); + print $out "</pre>\n"; + + $first_file= (($vstate->{page}!=1)&&(!$more)) ? hfile($f,1) : ""; # used to wrap qs() on the last page + $next_file = $more ? hfile($f,$line+$lines_per_file) : ""; # used to qs() to next page + + if (%{$vstate->{page_nb}}) { + print_navbar($out,0,$vstate->{page},$vstate->{page_nb},($more?"Next":""),$next_file,0); + } + if ( (!$frames) && ($output_hier || $output_index)) { + print_navbar($out,0,'Verilog',\%navbar,'','',0); + } + if ($js_sigs) { + print $out "<script language=\"JavaScript\"type=\"text\/javascript\"><!--\n"; + print $out "function next_page() { return \"$next_file\"; }\n"; + print $out "function first_page() { return \"$first_file\"; }\n"; + # put the values of extra info into the extra info array ordered by + # the index value for printing out + foreach $e (keys %{$vstate->{extra_info}}) { + $extra_info_array[$vstate->{extra_info}{$e}{vindex}] = + '"'. join('","',@{$vstate->{extra_info}{$e}{value}}) . '"'; + } + print $out "var extra_info = [\n"; + print $out "[" . join("],\n[",@extra_info_array) . "]\n"; + print $out "];\n"; + print $out "disabled=0;\n"; + print $out "\/\/ -->\n<\/script>\n"; + # clear extra info + $vstate->{extra_info} = {}; + $vstate->{extra_info_last} = 0; + } + + print_footer($out,$f,0,0); + print $out "</body>\n"; + print $out "</html>\n"; + + close($out); + + if ($compress) { + $out_file = $out_dir . hfile($f,$line); + # In this case we do not want the $compress_extension on + # the file name + $out_file =~ s/$compress_extension$//; + system ( @compress_cmd , $out_file ) == 0 or die "System call failed"; + } +} + +sub convert_start_file { + my ($f,$line,$page,$lines,$page_nb)=@_; + my ($out_file,$prev_file,$last_file); + + local (*C_OUT); + + # last file to blank if there only is one file + $last_file= (hfile($f,$lines) eq hfile($f,0)) ? "" : hfile($f,$lines); + $prev_file= ($page==1)? "" : hfile($f,$line-$lines_per_file); + + $out_file = $out_dir . hfile($f,$line); + + push(@output_files,$out_file); + # In this case we do not want the $compress_extension on + # the file name + $out_file =~ s/$compress_extension$// if $compress; + print "Outputting $out_file\n" if $debug; + + open(C_OUT,">$out_file") || + die "Error: can not open file $out_file to write: $!\n"; + + print C_OUT '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">'."\n"; + + print C_OUT "<html><head>\n"; + print C_OUT "<title>$f" . (($page==1)?"":" page $page"). "</title>\n"; + print C_OUT $style_sheet_link; + print C_OUT "</head>\n"; + print_js_sigpopup(*C_OUT) if ($js_sigs); + print C_OUT "<body".($js_sigs ? " onload='loadqs();'" : "" ).">\n"; + + if ($js_sigs) { + print C_OUT "<script language=\"JavaScript\"type=\"text\/javascript\"><!--\n"; + print C_OUT "function prev_page() { return \"$prev_file\"; }\n"; + print C_OUT "function last_page() { return \"$last_file\"; }\n"; + print C_OUT "\/\/ -->\n<\/script>\n"; + } + if ( (!$frames) && ($output_hier || $output_index)) { + print_navbar(*C_OUT,0,'Verilog',\%navbar,'','',0); + } + if (%{$page_nb}) { + print_navbar(*C_OUT,0,$page,$page_nb,($prev_file)?"Prev":"", + $prev_file."#bottom_of_page",0); + } + print C_OUT "<pre>\n"; + return *C_OUT; +} + +############################################################################### +# quote a html string +# +sub quote_html { + my ($s) = @_; + + $$s =~ s/&/&/g; + $$s =~ s/</</g; + $$s =~ s/>/>/g; + $$s =~ s/\"/"/g; +} + +############################################################################### +# print some text with html font information +# +sub print_with_font { + my ($out,$fontname,$s) = @_; + + print $out "<span class=$classes{$fontname}>$s</span>"; +} + +############################################################################### +# print a link with class information +# +sub print_link { + my ($out,$current_file,$target,$js_sigs,$link_file,$link_anchor, + $link_text,$link_type,$short_links,$extra_data_index) = @_; + my ($href,$class); + + $class=''; + $class=" class=$classes{$link_type}" if exists($classes{$link_type}); + + + $href=''; + $href.=$link_file unless $short_links && ($link_file eq $current_file) && $link_anchor; + + $href.="#$link_anchor" if $link_anchor; + + print $out "<a $target"; + print $out " onClick=\"return qs(event,this,$extra_data_index)\"" if ($js_sigs); # for quick search + print $out " $class href=\"$href\">$link_text</a>"; +} + +############################################################################### +# Put all the stuff for a signal into the extra info array this is outputted +# as a javascript array and then used in the qs() javascript function. +# search for sig_buttons to see how it is used +# info should be pointer to: +# [ [def_file,def_line],[a_file,a_line],[i_file,i_line],[src_file,src_line] +# Returns the index to be used in the qs call +sub signal_extra_info { + my ($sig,$vstate,$index_ref,$info) = @_; + + my $ex_name= "S_${sig}___$vstate->{name}"; + + unless (exists $vstate->{extra_info}{$ex_name}) { + $vstate->{extra_info}{$ex_name} = { + vindex => $vstate->{extra_info_last}++ , + value => [ 'S' ] }; # type of extra info + foreach my $i (@$info) { + my $file = $i->[0]; + my $line = $i->[1]; + push(@{$vstate->{extra_info}{$ex_name}{value}}, + ((($file eq "")||($line == -1)) ? "" : + hfile($file,$line)."#".$line)); + } + push(@{$vstate->{extra_info}{$ex_name}{value}},$index_ref); + } + + # Note that the index placed in the file starts at 1 + # because 0 means no extra info + return ($vstate->{extra_info}{$ex_name}{vindex}+1); +} + +############################################################################### +# output a chunk of verilog +# +sub output_v { + my ($out,$s,$line,$file,$vstate,$sfile) = @_; + my ($anchor,$linebuf,$word,$link_line,$on_def,$qword,$inst,$n, + $new_context,$pre_ignore,$nextisdefine,$m,$ms,$f,$new_inst,$s_line, + $s_a_line,$s_a_file,$s_i_line,$s_i_file,$s_type,$s_file,$s_type2, + $t_type,$t_line,$t_file,$t_anchor,$p,$ex_index,$s_src_file,$s_src_line, + $index_ref); + + # do a line at a time (so it can put down the anchors, + # and change the contexts and current instance). + while ( $s =~ m/^(.*?(?:\n|\Z))/gm ) { + $linebuf = $1; + $nextisdefine=0; # for linking define after ifdef, define and undef + + if ( $line != $vstate->{line_started} ) { + # we are on a new line - first see if we need a new file + if ( ($vstate->{page}*$lines_per_file) < $line ) { + convert_end_file($out,$sfile,$line-$lines_per_file,1,$vstate) + if $vstate->{page}; + $vstate->{page}++; + $out=convert_start_file($sfile,$line,$vstate->{page},$vstate->{lines}, + $vstate->{page_nb}); + $vstate->{file} = hfile($sfile,$line); # current html file + } + + # do the anchors + foreach $anchor ( &rvp::get_anchors($verilog_db,$file,$line) ) { + print $out "<a name=\"$anchor\"></a>"; + } + + # change the context if there is a new one + if ( $new_context = &rvp::get_context($verilog_db,$file,$line) ) { + + print " new context $new_context\n" if $debug; + + if ( &rvp::get_has_value_by_context($verilog_db,$file,$line) ) { + $vstate->{context} = $new_context; + ($vstate->{name},$vstate->{type}) = + &rvp::get_context_name_type($verilog_db,$file,$line); + } + + if ( (! $vstate->{pre_ignore}) && # not already ignoring + ($pre_ignore = &rvp::get_pre_ignore_by_context($verilog_db, + $file,$line))){ + print " ignoring until $pre_ignore\n" if $debug; + $vstate->{pre_ignore} = $pre_ignore; + } + + if ( &rvp::get_module_start_by_context($verilog_db,$file,$new_context) && + (($m,$f,$inst,$link_line) = &rvp::get_first_instantiator_by_context( + $verilog_db,$file,$new_context))) { + print " putting in up links\n" if $debug; + # put in the up links + $n=0; + while ($m) { + $n++; + if ($n>32) { + print $out "<b><i>... (truncated)</i></b>"; + last; + } + print $out "<a $frame_middle href=\"". + hfile($f,$link_line) ."#" . $m . "_" . $inst . "\">". + "<img alt=\"[Up: $m $inst]\" align=bottom border=0 ". + "src=\"v2html-up.gif\"><\/a>"; + ($m,$f,$inst,$link_line)=&rvp::get_next_instantiator($verilog_db); + } + print $out "\n"; + } + } + + # change the current inst if there is a new one + if ( $new_inst = &rvp::get_inst_on_line($verilog_db,$file,$line) ) { + $vstate->{cur_inst}= $new_inst; + $vstate->{cur_inst_exists}=&rvp::module_exists($verilog_db,$new_inst); + } + } + # do not do all that stuff again for this line + $vstate->{line_started} = $line; + + if ($grey_ifdefed_out && $vstate->{pre_ignore}) { + if ($vstate->{pre_ignore}==$line) { + $vstate->{pre_ignore}=0; + } + else { + if ( $linebuf =~ m/^\s*$/ ) { + print $out $linebuf; + } + else { + quote_html(\$linebuf); + print_with_font($out,"pp_ignore",$linebuf); + } + $line++; + next; + } + } + + # do a word at a time within the line + foreach $word ( split /([.\`\$]?(?:$VID))/ , $linebuf ) { + $qword=$word; + quote_html(\$qword); + + if ($word !~ m/[.\`\$]?$VID/o ) { # anything that is not a $VID + # when we see a ; the next stuff is on the left had side + # when we see a = (not == != === !==) or <= we are on rhs + if ($word =~ m/;/) { + $vstate->{rhs}=0; + $vstate->{inst_link_done} = 0; + $vstate->{cur_inst} = '$NONE'; + $vstate->{cur_inst_exists} = 0; + } + elsif ($word =~ m/((\A|[^=!])=(\Z|[^=!]))|<=/ ) { + $vstate->{rhs}=1; + } + print $out $qword; + } + elsif ($nextisdefine) { + if ((($f,$link_line)=&rvp::get_define($verilog_db,$word, + $file,$line))&& $f && $link_line && + ($js_sigs || ($link_line!=$line))) { + print_link($out,$vstate->{file},$frame_bottom,$js_sigs, + hfile($f,$link_line),$link_line,$qword,"define", + $vstate->{short_links},0); + } + else { + print_with_font($out,"define",$qword); + } + $nextisdefine=0; + } + elsif ($word =~ m/^\$($VID)/o ) { + print_with_font($out,"systemtask",$qword); + } + elsif ($word =~ m/^\`/ ) { + # colour link 'define 'timescale etc. + if ( exists( $verilog_compiler_keywords_hash{$word} ) ) { + print_with_font($out,"compiler",$qword); + $nextisdefine = ($word eq "`ifdef" || $word eq "`ifndef" || + $word eq "`define" || $word eq "`undef"); + } + # link 'defines + elsif ((($f,$link_line) = &rvp::get_define($verilog_db,$word, + $file,$line)) && $f && $link_line) { + print " linking define $word at $line\n" if $debug; + # do not link quote, makes qs work better because after `define or `ifdef + # it appears without the quote. Also looks better + $qword=~s/^\`//; + print_with_font($out,"define",'`'); + print_link($out,$vstate->{file},$frame_bottom,$js_sigs, + hfile($f,$link_line),$link_line,$qword,"define", + $vstate->{short_links},0); + } + else { + print_with_font($out,"define",$qword); + } + } + # link instance module names to the module definition + elsif ( $word eq $vstate->{cur_inst} ) { + # check we have not already done it to cope with the case + # where the instance name is the same as the modules name + if ( !$vstate->{inst_link_done} ) { + if ( $vstate->{cur_inst_exists} ) { + ($f,$link_line) = &rvp::get_modules_file($verilog_db,$word); + print " linking module $word at $line\n" if $debug; + print_link($out,$vstate->{file},$frame_middle,$js_sigs, + hfile($f,$link_line),$word,$qword,"module", + $vstate->{short_links},0); + } + else { + # unlinkable instance + print_with_font($out,"module",$qword); + } + $vstate->{inst_link_done}=1; + } + else { + print $out $qword; + } + } + elsif ( $word eq $vstate->{name} && $vstate->{type} eq 'module' && + $line == $vstate->{context} ) { + print_with_font($out,"module",$qword); + print $out "<a $frame_middle href=\"". + index_link("Modules",$word)."\">". + "$icon_i<\/a>" if ($output_index); + } + # link .out( ) ports, set $rhs for .in( ) ports + elsif ($word =~ m/^\.($VID)/o && $vstate->{cur_inst_exists}) { + if (($s_line,$s_a_line,undef,$s_type,$s_file,undef,undef,$s_type2, + undef,undef,undef,$s_a_file,undef) = + &rvp::get_module_signal($verilog_db,$vstate->{cur_inst},$1)) { + $vstate->{rhs}=0; + $link_line = -1; + $f=''; + $s_type2 = ($s_type2 eq 'reg') ? "_reg" : ""; + if ($s_type eq 'output') { + print " linking port .$1 at $line\n" if $debug; + if ($s_a_line != -1) { $link_line = $s_a_line; $f = $s_a_file;} + else { $link_line = $s_line; $f = $s_file;} + } + if ($link_line != -1) { + print_link($out,$vstate->{file},$frame_middle,$js_sigs, + hfile($f,$link_line),$link_line,$qword, + "signal_$s_type$s_type2",$vstate->{short_links},0); + } + else { + print_with_font($out,"signal_$s_type$s_type2",$qword); + } + if ($s_type eq 'input') { $vstate->{rhs}=1; } + } + else { + # should only get here if there is a port used that + # does not exist on the submodule + print $out $qword; + } + } + # link signals + elsif (($s_line,$s_a_line,$s_i_line,$s_type,$s_file,$p,$n,$s_type2, + $s_src_file,$s_src_line,undef,$s_a_file,$s_i_file) = + &rvp::get_signal_by_context($verilog_db,$file, + $vstate->{context},$word)) { + + # figure out the link to link to the index + if ($output_index && $vstate->{name} && + ($vstate->{type} eq 'module')) { + $index_ref=index_link("Signals","${word}___$vstate->{name}"); + } + else { $index_ref = ''; } + # only do extra_info for module signals (not tasks or functions + if ($vstate->{name} && ($vstate->{type} eq 'module')) { + $ex_index=signal_extra_info($word,$vstate,$index_ref, + [ [$s_file, $s_line], + [$s_a_file, $s_a_line], + [$s_i_file, $s_i_line], + [$s_src_file,$s_src_line] ]); + } + else { $ex_index=0; } + # link the definition to the driver if you can, + # otherwise do not link it to anything + $on_def=(($s_file eq $file) && ($s_line == $line)); + $s_type2 = ($s_type2 eq 'reg') ? "_reg" : ""; + if (($vstate->{rhs} || $on_def) && ($s_a_line != -1)) { + $link_line = $s_a_line; + $f = $s_a_file; + } + else { + # do not link to itself unless doing quick search + if ($on_def && !$js_sigs) { $link_line = -1; $f =""; } + else { $link_line = $s_line ; $f = $s_file; } + } + + if ($on_def && ($s_i_line!=-1)) { + # link to an instantiator + if ($s_i_file) { + print_link($out,$vstate->{file},$frame_middle,$js_sigs, + hfile($s_i_file,$s_i_line),$s_i_line,$qword, + "signal_$s_type$s_type2",$vstate->{short_links}, + $ex_index); + } + else { # we only get here in certain strange include situations + print_with_font($out,"signal_$s_type$s_type2",$qword); + } + } + elsif ($link_line != -1) { + print_link($out,$vstate->{file},$frame_middle,$js_sigs, + hfile($f,$link_line),$link_line,$qword, + "signal_$s_type$s_type2",$vstate->{short_links},$ex_index); + } + else { + print_with_font($out,"signal_$s_type$s_type2",$qword); + } + # put in index link - when on def, but not in quick search mode + if ($index_ref && $on_def && !$js_sigs) { + print $out "<a $frame_middle href=\"$index_ref\">". + "$icon_i<\/a>"; + } + + } + # link parameters + elsif (($f,$link_line)= + &rvp::get_parameter_by_context($verilog_db,$file,$vstate->{context}, + $word)) { + # do not link on definition line + if (($f eq $file) && (($link_line == $line)&&!$js_sigs)) { + print_with_font($out,"parameter",$qword); + } + else { + print " linking parameter $word at $line\n" if $debug; + print_link($out,$vstate->{file},$frame_bottom,$js_sigs, + hfile($f,$link_line),$link_line,$qword, + "parameter",$vstate->{short_links},0); + } + } + # tasks & functions + elsif (($t_type,$t_line,$t_file,$t_anchor)= + &rvp::get_t_or_f_by_context($verilog_db,$file,$vstate->{context}, + $word)) { + # do not link on definition line + if (($t_file eq $file) && (($t_line == $line)&&!$js_sigs)) { + print_with_font($out,$t_type,$qword); + } + else { + print " linking $t_type $word at $line\n" if $debug; + print_link($out,$vstate->{file},$frame_middle,$js_sigs, + hfile($t_file,$t_line),$t_anchor,$qword,$t_type, + $vstate->{short_links},0); + } + } + # colour begin end always etc. + elsif ( exists( $verilog_keywords_hash{$word} ) ) { + print_with_font($out,"keyword",$qword); + } + else { + print $out $qword; + } + } + $line++; + } + return $out; +} + +#####The rest of this file is the rough verilog parser perl module############# +############################################################################### +# +# File: rvp.pm +# RCS: $Header: /home/cc/v2html/build/../RCS/v2html,v 7.30.1.3 2006/05/03 20:19:55 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 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); + +my $msort; + + +BEGIN { + # a hack to make it match with the version that doesn't sort + # the hashes. This is only done when PERL_HASH_SEED=0 ie when + # hash randomising is explicitly turned off, because with + # hash randomising on it won't match unless sorting is on. + if ( $ENV{PERL_HASH_SEED} eq "0" ) { + $msort = sub { return @_ }; + } + else { + $msort = sub { return sort @_ }; + } + + + # $VERSION is used by 'use', but keep $version for backwards compatibility + $version = '$Header: /home/cc/v2html/build/../RCS/v2html,v 7.30.1.3 2006/05/03 20:19:55 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 ( &$msort (keys %{$self->{unresolved_modules}} )) { + # find somewhere it is instantiated for warning message + my $file=""; + my $line=""; + foreach my $m2 (&$msort (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 &$msort (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 (&$msort (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 &$msort (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 &$msort (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 &$msort (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 ( &$msort ( 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 (&$msort (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; +} + + +########################################################################### + +=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 + %$2%ox ) { + $chunk->{isEnd} = 1; + $chunk->{text} = $1; + if (defined($3)) { + $this->{state} = 1; # long comment + } + elsif (defined($4)) { + $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 (&$msort (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 (&$msort (keys %{$self->{modules}})) { + print " Making inst_by for $m\n" if $debug; + foreach $m2 (&$msort (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 (&$msort (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 (&$msort (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 (&$msort (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 (&$msort (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 (&$msort (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 (&$msort (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 (&$msort (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 (&$msort (keys %{$self->{modules}})) { + print " Finding port connections in $m\n" if $debug; + foreach $sig (&$msort (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 (&$msort (keys %{$self->{modules}})) { + print " Finding signal sources in $m\n" if $debug; + foreach $sig (&$msort (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 (&$msort (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 ( &$msort (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 (&$msort (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 .= <<EOF; +sub _parse_line { + + my (\$self,\$code,\$file,\$line,\$ps,\$rs) = \@_; + + if (!exists(\$ps->{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 (&$msort (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 (&$msort (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 (&$msort (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 (&$msort (keys %$evalDef)) { + if (!exists($stateNames{$state})) { + die "Couldn't find state $state"; + } + my $statep = $stateNames{$state}; + + foreach my $arc (&$msort (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; + diff --git a/bin/htmlgen/v2html/v2html-cgi b/bin/htmlgen/v2html/v2html-cgi new file mode 100755 index 0000000000000000000000000000000000000000..9fb68e95bdcefa46b29301d55e246c02ce69a6d6 --- /dev/null +++ b/bin/htmlgen/v2html/v2html-cgi @@ -0,0 +1,311 @@ +#!/usr/local/bin/perl -w +############################################################################### +# +# File: v2html-cgi +# RCS: $Header: v2html-cgi,v 3.1 1999/09/21 18:38:43 cc Exp $ +# Description: CGI script for helping v2html generated html +# Author: Costas Calamvokis +# Created: Wed Sep 3 08:52:08 1997 +# Modified: Tue Sep 21 11:34:21 1999 (Costas Calamvokis) v2html@burbleland.com +# Language: Perl +# +# Copyright 1998 Costas Calamvokis +# Copyright 1997 Hewlett-Packard Company +# +# 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. +# +############################################################################### + +# +# Currently does: +# Expanding/compressing hierarchies: +# - Takes a query like ?k=9437645&x=XXXXCCXC&in=hierarchy.html +# and generates html of the hierarchy in in accoring to the string +# x, each character in the string x represents one list in the .html +# file which can either be eXpanded or Compressed. +# - After each module it also generates a [X] or [C] link which when +# clicked causes this script to be called again with a new string +# which results in that module being expanded or compressed. +# - This is made much easier by v2html which marks each list that is +# with a candidate for expansion/compression with a +# number when it generates the html code. +# + +# only have one file error message to avoid leaking information +# through errors (uncomment the helpful error during debugging) +$file_error_message= "v2html-cgi error.<P>\n"; + +print "Content-Type: text/html\n\n"; + +# environment variables that should be set by the web server +&check_input('QUERY_STRING',%ENV); +&check_input('SCRIPT_NAME',%ENV); +&check_input('PATH_INFO',%ENV); +&check_input('PATH_TRANSLATED',%ENV); + +# Get the arguements specified in the URL +%args= getcgivars(); + +# query variables that should be set in the URL +# (eg .. ?k=9999x=CXCX&in=hierarchy.html +&check_input('x',%args); +&check_input('f',%args); +&check_input('in',%args); +&check_input('k',%args); + +$k = $args{'k'}; +$expand_string =$args{'x'}; +$infile = $args{'in'}; +$framed = $args{'f'}; + +# remove the / from script name if it is there +$ENV{'SCRIPT_NAME'} =~ s#^/## ; + +# set up the cgi script and path info that we'll put in the expand/compress +# links +$cgi_script_and_path_info= "/" . $ENV{'SCRIPT_NAME'} . $ENV{'PATH_INFO'}; + + +$marker = " <!-- v2html_handle --> "; +$printing=1; # start out printing the file +$ul_id=0; + +# Work out the hierarchy file to read +$file=$ENV{'PATH_TRANSLATED'} . $infile; + +# Remove any .. in the file name so people can't look at files +# that are not under the web root +$file=~ s/\.\.//g; + +# open the hierarchy file +unless (open(F,"<$file")) { + print $file_error_message; + # this less cryptic message could give intruders clues about your files + #print "Couldn't open $ENV{'PATH_TRANSLATED'}$infile\n"; + exit; +} + + +# check that the hierarchy file starts with "<!- v2html hierarchy" comment +# and that the key is correct +$_ = <F>; +&security_check_hierarchy($_); + + +# Have a look for the briefcase icons - if they don't exist +# use [C] and [X] +&find_icons; + +# set up the extra infomation needed to do framed output +if ($framed eq "1") { $target='target="upper"'; } +else { $target=''; } + +# +# main loop +# +while (<F>) { + # print the place to find the .v.html files at the bottom of the + # header - if we don't do this then it'll look for them under + # the cgi-bin directory + if (m&</head>&) { + print "<base href=\"http://$ENV{'SERVER_NAME'}" . + ":$ENV{'SERVER_PORT'}$ENV{'PATH_INFO'}\">\n"; + } + + if ($printing) { + if (m&<ul> <!-- ul_id=([0-9]+) -->&) { + if (&check_expand_string($1)) { + # This is expanded now, so print compressor + print $marker . + "<A name=\"ul_id_$1\"></A>\n"; + print $marker . + "<A $target href=\"$cgi_script_and_path_info?k=$k&x=" . + &new_expand_string($1,"C") . + "&in=$infile&f=$framed#ul_id_$1\"> $icon_c</A>\n"; + # keep printing + print $_; + } + else { + $ul_id=$1; + # This is compressed now, so print expander + print $marker . + "<A name=\"ul_id_$ul_id\"></A>\n"; + print $marker . + "<A $target href=\"$cgi_script_and_path_info?k=$k&x=" . + &new_expand_string($ul_id,"X") . + "&in=$infile&f=$framed#ul_id_$1\"> $icon_x</A>\n"; + # stop printing + $printing=0; + } + } + else { + # print everything else except compressors and expanders + # which are generated fresh each time + if (! m/$marker/) { + print $_; + } + } + } + else { + # not printing, look for the end of the ul_id which stopped the + # print. + if (m&</ul> <!-- ul_id=$ul_id -->&) { + $printing=1; + } + } + + + +} + +exit; + +########################################################################### +# Subroutines +########################################################################### + +# +# Takes one arguement - the number in the character string to +# look at. +# Returns 1 if the list is expanded and 0 if it is compressed +# if the number is off the end of the expand string then it +# returns 1 +# +sub check_expand_string { + local($u) = @_; + local($c); + + if ($expand_string eq 'A') { + return 1; + } + elsif (length($expand_string)>$u) { + $c = substr($expand_string,$u,1); + if ($c eq 'X'){ + return 1; + } + else { + return 0; + } + } + else { + return 0; + } + +} + +# +# Generate a new expand_string for a compressor or an expander link +# Takes two arguments, the number of the list and the new character +# to put in ('C' for compressor, 'X' for expander) +# +sub new_expand_string { + local($u,$c) = @_; + local($new_string,$l); + + $l = length($expand_string); + if ($l > $u) { + $new_string = $expand_string; + substr($new_string,$u,1) = $c; + } + else { + if ($expand_string eq "A") { + $new_string = "X" . "X" x ($u-$l) . $c; + } + else { + $new_string = $expand_string . "C" x ($u-$l) . $c; + } + } + + return $new_string; +} + +# +# check that the input array %a has an element $s +# +sub check_input { + local($s,%a) = @_; + if (!exists($a{$s})) { + print "v2html-cgi: fatal error, didn't get required parameter $s.<P>\n"; + if ($s eq 'k') { + print " This may be because the hierarchy was generated\n" . + " by v2html 2.0. If so regenerate using a newer version<P>\n"; + } + exit; + } +} + +# +# Read all CGI vars into an associative array. +# If multiple input fields have the same name, they are concatenated into +# one array element and delimited with the \0 character. +# This is a simple version, that assumes a request method of GET. +# +sub getcgivars { + local(%in) ; + local($name, $value) ; + + # Resolve and unencode name/value pairs into %in + foreach (split('&', $ENV{'QUERY_STRING'})) { + s/\+/ /g ; + ($name, $value)= split('=', $_, 2) ; + $name=~ s/%(..)/sprintf("%c",hex($1))/ge ; + $value=~ s/%(..)/sprintf("%c",hex($1))/ge ; + $in{$name}.= "\0" if defined($in{$name}) ; # concatenate multiple vars + $in{$name}.= $value ; + } + + return %in ; + +} + +# +# Do security checks on the hierarchy file +# make sure we don't: +# serve files that are not v2html hierarchies +# serve files that users can't get access to through the http demon +# (by checking that the key is right) +# +sub security_check_hierarchy { + my ($first_line) = @_; + + if ($first_line =~ /^<!-- v2html hierarchy/) { + if ($first_line =~ /^<!-- v2html hierarchy K=$k /) { + print $first_line; + } + else { + print $file_error_message; + # this less cryptic message could give intruders clues about your files + #print "$ENV{'PATH_TRANSLATED'}$infile: bad key<P>\n"; + exit; + } + } + else { + print $file_error_message; + # this less cryptic message could give intruders clues about your files + #print "$ENV{'PATH_TRANSLATED'}$infile is not a v2html hierarchy file\n"; + exit; + } +} + +# +# Look for the briefcase icons - if they don't exist use [C] and [X] +# +sub find_icons { + + if (( -r "$ENV{'PATH_TRANSLATED'}/v2html-c.gif" ) && + ( -r "$ENV{'PATH_TRANSLATED'}/v2html-x.gif" )) { + $icon_c = "<IMG align=bottom border=0 SRC=\"v2html-c.gif\">"; + $icon_x = "<IMG align=bottom border=0 SRC=\"v2html-x.gif\">"; + } + else { + $icon_c = " [C]"; + $icon_x = " [X]"; + } + + +} + + + diff --git a/bin/htmlgen/v2html/v2html.1 b/bin/htmlgen/v2html/v2html.1 new file mode 100644 index 0000000000000000000000000000000000000000..ea1b36051668c6179270cd3293c5f65a7f556772 --- /dev/null +++ b/bin/htmlgen/v2html/v2html.1 @@ -0,0 +1,553 @@ +.\" Automatically generated by Pod::Man v1.37, Pod::Parser v1.32 +.\" +.\" Standard preamble: +.\" ======================================================================== +.de Sh \" Subsection heading +.br +.if t .Sp +.ne 5 +.PP +\fB\\$1\fR +.PP +.. +.de Sp \" Vertical space (when we can't use .PP) +.if t .sp .5v +.if n .sp +.. +.de Vb \" Begin verbatim text +.ft CW +.nf +.ne \\$1 +.. +.de Ve \" End verbatim text +.ft R +.fi +.. +.\" Set up some character translations and predefined strings. \*(-- will +.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left +.\" double quote, and \*(R" will give a right double quote. \*(C+ will +.\" give a nicer C++. Capital omega is used to do unbreakable dashes and +.\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, +.\" nothing in troff, for use with C<>. +.tr \(*W- +.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' +.ie n \{\ +. ds -- \(*W- +. ds PI pi +. if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch +. if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch +. ds L" "" +. ds R" "" +. ds C` "" +. ds C' "" +'br\} +.el\{\ +. ds -- \|\(em\| +. ds PI \(*p +. ds L" `` +. ds R" '' +'br\} +.\" +.\" If the F register is turned on, we'll generate index entries on stderr for +.\" titles (.TH), headers (.SH), subsections (.Sh), items (.Ip), and index +.\" entries marked with X<> in POD. Of course, you'll have to process the +.\" output yourself in some meaningful fashion. +.if \nF \{\ +. de IX +. tm Index:\\$1\t\\n%\t"\\$2" +.. +. nr % 0 +. rr F +.\} +.\" +.\" For nroff, turn off justification. Always turn off hyphenation; it makes +.\" way too many mistakes in technical documents. +.hy 0 +.if n .na +.\" +.\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). +.\" Fear. Run. Save yourself. No user-serviceable parts. +. \" fudge factors for nroff and troff +.if n \{\ +. ds #H 0 +. ds #V .8m +. ds #F .3m +. ds #[ \f1 +. ds #] \fP +.\} +.if t \{\ +. ds #H ((1u-(\\\\n(.fu%2u))*.13m) +. ds #V .6m +. ds #F 0 +. ds #[ \& +. ds #] \& +.\} +. \" simple accents for nroff and troff +.if n \{\ +. ds ' \& +. ds ` \& +. ds ^ \& +. ds , \& +. ds ~ ~ +. ds / +.\} +.if t \{\ +. ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" +. ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' +. ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' +. ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' +. ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' +. ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' +.\} +. \" troff and (daisy-wheel) nroff accents +.ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' +.ds 8 \h'\*(#H'\(*b\h'-\*(#H' +.ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] +.ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' +.ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' +.ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] +.ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] +.ds ae a\h'-(\w'a'u*4/10)'e +.ds Ae A\h'-(\w'A'u*4/10)'E +. \" corrections for vroff +.if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' +.if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' +. \" for low resolution devices (crt and lpr) +.if \n(.H>23 .if \n(.V>19 \ +\{\ +. ds : e +. ds 8 ss +. ds o a +. ds d- d\h'-1'\(ga +. ds D- D\h'-1'\(hy +. ds th \o'bp' +. ds Th \o'LP' +. ds ae ae +. ds Ae AE +.\} +.rm #[ #] #H #V #F C +.\" ======================================================================== +.\" +.IX Title "V2HTML 1" +.TH V2HTML 1 "January 2009" "v2html 7.30.1.3" "v2html" +.SH "NAME" +v2html \- Verilog to HTML converter +.SH "SYNOPSIS" +.IX Header "SYNOPSIS" +\&\fBv2html\fR [options] file1 [file2] ... +.PP +\&\fBv2html\fR is a perl 5 script that converts a bunch of verilog files to +html, linking various things to their definitions. At the moment +it handles: +.IP "\- modules" 5 +.IX Item "- modules" +.PD 0 +.IP "\- tasks" 5 +.IX Item "- tasks" +.IP "\- functions" 5 +.IX Item "- functions" +.IP "\- includes" 5 +.IX Item "- includes" +.IP "\- defines" 5 +.IX Item "- defines" +.IP "\- parameters" 5 +.IX Item "- parameters" +.IP "\- inputs,outputs,inouts" 5 +.IX Item "- inputs,outputs,inouts" +.IP "\- signals: wire, reg etc." 5 +.IX Item "- signals: wire, reg etc." +.IP "\- mail addresses in comments" 5 +.IX Item "- mail addresses in comments" +.IP "\- http \s-1URLS\s0 (http://....) in comments" 5 +.IX Item "- http URLS (http://....) in comments" +.PD +.PP +It also generates a page containing all the modules it found arranged +hierarchically and separate index pages for all of the files, modules, +signals, tasks and functions. +.PP +Once you have the html files they can be installed on a web\-server, or +simply viewed using \*(L"Open File...\*(R" in your web browser. +.PP +Details on navigating around the converted files, and many examples +can be found at http://www.burbleland.com/v2html/v2html.html . +.PP +Note that as of version 5.0 \fBv2html\fR uses \fIcascading style sheets\fR +to colour the display. This means that pages viewed with old browsers +that do not support \fIcascading style sheets\fR (for instance Netscape +3) will not be coloured. +.SH "DESCRIPTION" +.IX Header "DESCRIPTION" +In addition to the switches detailed below \fBv2html\fR also accepts and +ignores most VCS/VerilogXL options and all other options starting with a +plus. This means that you should be able to run it with the same +command file you use for running simulations. +.PP +Switches: +.IP "\fB\-q\fR" 5 +.IX Item "-q" +Be quiet \- so don't print all those informative messages about what +it is doing. +.IP "\fB+define+NAME[=VALUE]\fR" 5 +.IX Item "+define+NAME[=VALUE]" +Pre-define a value. This is just the same as putting: +.Sp +.Vb 1 +\& \(aqdefine NAME [VALUE] +.Ve +.Sp +at the top of each of your input files. The rather strange syntax is the +same as VerilogXL's. This option is useful for controlling which ifdefs appear +true to \fBv2html\fR. +.IP "\fB+incdir+DIR\fR" 5 +.IX Item "+incdir+DIR" +Specify a directory to search for include files (just like VerilogXL). +.IP "\fB\-y\fR\ \s-1DIR\s0" 5 +.IX Item "-yDIR" +Specify a directory to search for modules (just like VerilogXL). Note that +\&\fBv2html\fR doesn't do any fancy search orders like VerilogXL \- it just searches +the directories in the order that you specify them on the command line. +.IP "\fB+libext+EXT\fR" 5 +.IX Item "+libext+EXT" +Specify an extension use when searching for modules (just like +VerilogXL). You can specify multiple extensions using +\&\fB+libext+EXT1+EXT2+EXT2\fR or by specifying multiple \fB+libext+EXT\fR +options. +.IP "\fB\-v\fR\ \s-1LIBFILE\s0" 5 +.IX Item "-vLIBFILE" +Specify a library file to use (just like VerilogXL). +.IP "\fB\-f\fR\ command_lines_options_file" 5 +.IX Item "-fcommand_lines_options_file" +Specify a file to get more command line options from. For instance you +could put a list of all your files in src_files and then use \fB\-f\fR\ src_file. You can also put in just about anything that you can put +on the command line. Comments can be included using # or // at the +start of the line. Wildcards are not allowed in the file names. Use +single or double quotes to quote arguments that have spaces in them +for example: +.Sp +.Vb 3 +\& # v2html \-f file \- turn on gzip compression +\& \-z \-ze .gz +\& \-zc \(aqgzip \-f\(aq +.Ve +.Sp +You can have as many \-f options as you want and you can probably put +\&\-f options in the file too. +.IP "\fB\-m\fR\ mail_addr" 5 +.IX Item "-mmail_addr" +A mail address of the site maintainer. This is placed in a field at +the bottom of each file like this: +.Sp +.Vb 4 +\& This page: +\& Maintained by: Joe_Bloggs@barking.com +\& Created: Thu Nov 6 08:53:37 1997 +\& From: test2.v +.Ve +.IP "\fB\-i\fR" 5 +.IX Item "-i" +Incremental mode. At the moment this isn't very incremental! \fBv2html\fR +checks the dates on all of the files that it read last time it ran and +all of the output files it generated. If any of the input files are +newer than the output files, or if the command line options have +changed it \fBdeletes\fR all of the files it made last time and rebuilds +everything. The delete is done to stop old files accumulating in the +output directory. +.Sp +\&\fBv2html\fR keeps track of all the information it needs to do this in +a file called .v2html_incr . +.IP "\fB\-o\fR\ output_dir" 5 +.IX Item "-ooutput_dir" +Set the output directory for the html files. The default is the current +directory. +.IP "\fB\-css\fR\ cascading_style_sheet" 5 +.IX Item "-csscascading_style_sheet" +The appearance of all the different elements of the html page can be altered +using a \fIcascading style sheet\fR. By default \fBv2html\fR uses a file called +\&\fIv2html.css\fR in the same directory as the html files. You can customize the +appearance of the html files by editing this file (\fBv2html\fR will create one +if it does not exist, but will not overwrite an existing one). +.Sp +If you have many different html directories which you want to use the +one central cascading style sheet then you can use the \fB\-css\fR option +to specify one. For example: +.Sp +.Vb 1 +\& \-css http://www.barking.com/joes_style.css +.Ve +.IP "\fB\-h\fR\ hier_file" 5 +.IX Item "-hhier_file" +Set the name of the file that the hierarchy is written to. The default +is hierarchy.html. This is also used as a base for the index file +names. These file names are formed put appending \f(CW\*(C`\-f\*(C'\fR, \f(CW\*(C`\-m\*(C'\fR, +\&\f(CW\*(C`\-s\*(C'\fR, \f(CW\*(C`\-t\*(C'\fR and \f(CW\*(C`\-fn\*(C'\fR to the part of the hierarchy file name +before the first dot (so hier.htm will put the files index in +hier\-f.htm). +.IP "\fB\-ht\fR\ top_module" 5 +.IX Item "-httop_module" +Set the name of a top module you want in the hierarchy. You can specify +multiple \fB\-ht\fR options to specify multiple top modules. +.Sp +If you don't specify any \fB\-ht\fR options then \fBv2html\fR will inspect +the hierarchy and find all the top modules in the verilog files and +print these out. The 'top modules' are modules that are not +instantiated but instantiate other modules that \fBv2html\fR has found +the definition of. +.IP "\fB\-hc\fR\ hierarchy_comment" 5 +.IX Item "-hchierarchy_comment" +Specifies some text to put at the top of the hierarchy. For instance: +.Sp +.Vb 1 +\& v2html \-hc "This is our ASIC" *.v +.Ve +.IP "\fB\-htf\fR" 5 +.IX Item "-htf" +Turns on printing of tasks and functions in the hierarchy. By default they +are not printed. +.IP "\fB\-lines\fR\ max_lines_per_file" 5 +.IX Item "-linesmax_lines_per_file" +In order to speed up viewing \fBv2html\fR splits large verilog files across multiple +html pages. This option lets you specify how many lines you want on a page. The +default is 1000. +.IP "\fB\-nnm\fR" 5 +.IX Item "-nnm" +No \*(L"No modules\*(R". By default the hierarchy contains three sections, the +hierarchies of the top modules, a list of files containing no modules +and a list of unconnected modules (modules that are not instantiated +but also do not qualify as top modules). The \fB\-nnm\fR makes +\&\fBv2html\fR skip printing the list of files containing no modules. +See \fB\-nu\fR. +.IP "\fB\-nu\fR" 5 +.IX Item "-nu" +No unconnected. See \fB\-nnm\fR. Makes \fBv2html\fR skip writing the list +of unconnected modules in the hierarchy file. +.IP "\fB\-nh\fR" 5 +.IX Item "-nh" +No hierarchy. Don't print out the hierarchy. +.IP "\fB\-nindex\fR" 5 +.IX Item "-nindex" +No indexes. Don't print out the indexes. +.IP "\fB\-ni\fR" 5 +.IX Item "-ni" +By default \fBv2html\fR 'greys out' any code that is ifdefed out. The \fB\-ni\fR +turns this greying out off. Note that v2html always ignores code that is +ifdefed out when it is parsing. +.IP "\fB\-z\fR" 5 +.IX Item "-z" +Compress the html files generated (and make sure the links point to +the compressed versions). This can be useful if you convert machine +generated code, like \s-1ASIC\s0 \s-1RAM\s0 macros which are huge before they are +converted and even bigger afterwards. +.IP "\fB\-zc\fR\ compresser" 5 +.IX Item "-zccompresser" +The executable to use to compress the html files if \fB\-z\fR is used. The +Default is 'compress \f(CW\*(C`\-f\*(C'\fR'. For instance to use gzip use \fB\-zc\fR 'gzip \f(CW\*(C`\-f\*(C'\fR' +(the \f(CW\*(C`\-f\*(C'\fR stops gzip prompting you about overwriting files). +.IP "\fB\-ze\fR\ compressed_extension" 5 +.IX Item "-zecompressed_extension" +The extension that your compress executable uses. The default is '.Z'. If you +were using gzip then you'd want \fB\-ze\fR .gz +.IP "\fB\-F\fR\ [frame_file.html]" 5 +.IX Item "-F[frame_file.html]" +Frame mode. Using \fB\-F\fR turns on the generation of framed output +where a top level frame file is generated that creates three +frames in your browser, the top one for the hierarchy the middle +one for the code and the bottom one for any definitions to appear +in. +.Sp +The default name for the frame file is frame.html. This default +can be overridden by specifying a file name after the \fB\-F\fR option. +.IP "\fB\-VF\fR\ [frame_file.html]" 5 +.IX Item "-VF[frame_file.html]" +Same as \fB\-F\fR but arranges the frames vertically so that the hierarchy is +down the side. +.IP "\fB\-s\fR" 5 +.IX Item "-s" +Link to the source. This causes the file name in From field of +the page footer to become a link to the unconverted verilog +file: +.Sp +.Vb 4 +\& This page: +\& Maintained by: Joe_Bloggs@barking.com +\& Created: Thu Nov 6 08:53:37 1997 +\& From: /asic/verilog/test2.v +.Ve +.Sp +For this to work your web server must have access to the source code. +Also, you must either run v2html in the output directory or use +absolute path names for the verilog files. +.Sp +For example, if the source is in /home/asic/verilog and the html files +want to end up in /home/www/verilog then there are two ways to run it +to get \fB\-s\fR to work: +.Sp +.Vb 3 +\& 1) In the output directory with verilog files specified by relative paths: +\& cd /home/www/verilog +\& v2html \-s ../../asic/verilog/*.v +.Ve +.Sp +.Vb 3 +\& 2) In any directory with verilog files specified by absolute paths: +\& cd /anywhere +\& v2html \-s \-o /home/www/verilog /home/asic/verilog/*.v +.Ve +.IP "\fB\-c\fR\ /cgi_script\ /path_to_html_files" 5 +.IX Item "-c/cgi_script/path_to_html_files" +Activate \s-1CGI\s0 features which allow the user to hide and show +regions of the hierarchy in a similar way to the old file manager +on windows 3.1. This method only works if you put the files on +a web server. +.Sp +To use this you must have installed the v2html \s-1CGI\s0 script on your +web\-server. The /cgi_script is the name of the \s-1CGI\s0 script (with path). +The /path_to_v_files is the directory you are putting your html files. +.Sp +These paths are the paths your web server sees (not the full paths on +the system) so is the same path that appears after \fIhttp://server\fR when +accessing the files. +.Sp +Here's an example: +.Sp +.Vb 2 +\& cp v2html\-cgi /opt/CERNhttpd/cgi\-bin/ +\& chmod 755 /opt/CERNhttpd/cgi\-bin/v2html\-cgi +.Ve +.Sp +.Vb 2 +\& cd /home/web/v2html/example/ex1 +\& v2html \-c /cgi\-bin/v2html\-cgi /v2html/example/ex1 ../verilog/*.v +.Ve +.Sp +Note that \fBv2html\fR can't check the parameters to \fB\-c\fR while +converting the files. You'll have to do it yourself by viewing the +hierarchy in your web browser and clicking on \fB[Hide\ All]\fR at the +top of the hierarchy. Make sure you view the file using the web server +(use \fIhttp://server/v2html/example/ex1/hierarchy.html\fR rather than +\&\fIfile:/home/web/v2html/example/ex1/hierarchy.html\fR). +.Sp +Depending on your webserver you may also need to use the \-css to +specify the full \s-1URL\s0 to your cascading stylesheet eg: +.Sp +.Vb 3 +\& v2html \-c /cgi\-bin/v2html\-cgi /v2html/examples/millennium_clock/hier_cgi +\& \-css http://www.burbleland.com/v2html/examples/millennium_clock/hier_cgi/v2html.css +\& *.v +.Ve +.Sp +If you get a message like this when you click on \fB[Hide\ All]\fR: +.Sp +.Vb 2 +\& Bad script request \-\- neither \(aq/opt/CERNhttpd/cgi\-bin/v2html\-cg\(aq +\& nor \(aq/opt/CERNhttpd/cgi\-bin/v2html\-cg.pp\(aq is executable +.Ve +.Sp +Then either there is either a problem with the installation of the cgi +script or you have incorrectly specified the first parameter to \fB\-c\fR. +.Sp +If you get a message like this: +.Sp +.Vb 1 +\& v2html error. +.Ve +.Sp +then you have probably got the second parameter to \fB\-c\fR wrong. +.IP "\fB\-k\fR\ key_string" 5 +.IX Item "-kkey_string" +Specify the key to use for to stop people looking at hierarchy files +that are protected by web-server security. The default is to use a +random key, but this means that you can't have bookmarks of the +hierarchy in various states (because the bookmark will contain the +key, and the key will change each time you run \fBv2html\fR). To get +round this problem you can use \fB\-k\fR and always have the same +key string. The key can be any string of digits and letters. +.IP "\fB\-njshier\fR" 5 +.IX Item "-njshier" +Deactivate Javascript features that allow the user to hide and +collapse regions of the hierarchy. +.IP "\fB\-ncookies\fR" 5 +.IX Item "-ncookies" +The Javascript version of the hierarchy uses cookies to remember the state +you left the hierarchy in, so when you next visit the hierarchy page it will +be in the same state. If you hate cookies then use the \fB\-ncookies\fR option +to turn them off. +.IP "\fB\-nsigpopup\fR" 5 +.IX Item "-nsigpopup" +Turn off the generation of javascript that does the signal popup window. +Specifying this option also turns off \*(L"Quick Search\*(R". +.IP "\fB\-tab\fR\ value" 5 +.IX Item "-tabvalue" +Expand tabs to the specified value. +.IP "\fB\-debug\fR" 5 +.IX Item "-debug" +Turn on lots of debugging information. +.SH "AUTHOR" +.IX Header "AUTHOR" +Costas Calamvokis <\fIv2html730@burbleland.com\fR>. +.SH "EXAMPLES" +.IX Header "EXAMPLES" +Here is an example where \fBv2html\fR is run in the directory containing +the verilog files (note the \fB\-o\fR option): +.PP +.Vb 3 +\& cd /users/jb/verilog_files/ +\& v2html \-F my_frame.html \-h my_hier.html \-ht chip_top \-htf \-nu +\& \-o /users/www/project/verilog \-m Joe_Blogs@barking.com \-s *.v +.Ve +.PP +As the verilog files don't have absolute paths and we aren't running +in the destination directory can't use \fB\-s\fR (link to source) as the +links \fBv2html\fR will create wouldn't allow the web server to find the +files. +.PP +Here is an example where \fBv2html\fR is run in the directory where +we want the html files (no \fB\-o\fR option): +.PP +.Vb 4 +\& cd /users/www/project/verilog +\& v2html \-F my_frame.html \-h my_hier.html \-ht chip_top \-nu \-htf +\& \-m Joe_Blogs@barking.com \-s +\& \-c /cgi\-bin/v2html\-cgi /project/verilog ../../../jb/verilog_files/*.v +.Ve +.PP +Here we can use the \fB\-s\fR option because we are running the the destination +directory, so the links \fBv2html\fR creates to the source will work +(providing the web server is allowed to server files from +/users/jb/verilog_files). +.SH "DIAGNOSTICS" +.IX Header "DIAGNOSTICS" +By default \fBv2html\fR tells you a lot about what it is doing (this is +because it is slow and if it didn't you'd think it had crashed!). These +messages can get in the way of the warnings \fBv2html\fR produces, so if +you have a problem first try \fB\-q\fR (quiet) to see if there are any warnings +you missed in the deluge of messages. +.PP +Most of the Error messages concern failures to open files, I guess these +will be caused by bad permissions, or you pointing \fBv2html\fR at files +or directories that don't exist. +.PP +The errors that say things like: +.PP +.Vb 5 +\& Warning: Confused in t.v line 2 (state=SIGNAL_AFTER_NAME): +\& wire g xx; +\& ^ +\&mean that you have written some verilog that I wasn\(aqt expecting \- send +\&it to me and I\(aqll see what I can do. +.Ve +.PP +Most of the warnings concern things that \fBv2html\fR will ignore because +it found more than one of them. The most common is a duplicate module +being found because an old copy of one of the files is lurking in +your source directory. The easiest way around this is to use the +\&\fB\-f\fR option something like this: +.PP +.Vb 2 +\& ls /path/*.v | grep \-v old_module_file.v > src_files +\& v2html \-f src_files +.Ve +.PP +Generally \fBv2html\fR will ignore duplicate things (so for example +modules won't appear in the hierarchy), but sometimes it will just pick +one of them, so watch those warnings. diff --git a/bin/htmlgen/v2html/v2html.man.html b/bin/htmlgen/v2html/v2html.man.html new file mode 100644 index 0000000000000000000000000000000000000000..f50978af0b88a7995106eff72c4e804acc20636f --- /dev/null +++ b/bin/htmlgen/v2html/v2html.man.html @@ -0,0 +1,471 @@ +<?xml version="1.0" ?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> +<head> +<title>v2html - Verilog to HTML converter</title> +<meta http-equiv="content-type" content="text/html; charset=utf-8" /> +<link rev="made" href="mailto:root@localhost" /> +</head> + +<body style="background-color: white"> + +<p><a name="__index__"></a></p> +<!-- INDEX BEGIN --> + +<ul> + + <li><a href="#name">NAME</a></li> + <li><a href="#synopsis">SYNOPSIS</a></li> + <li><a href="#description">DESCRIPTION</a></li> + <li><a href="#author">AUTHOR</a></li> + <li><a href="#examples">EXAMPLES</a></li> + <li><a href="#diagnostics">DIAGNOSTICS</a></li> +</ul> +<!-- INDEX END --> + +<hr /> +<p> +</p> +<h1><a name="name">NAME</a></h1> +<p>v2html - Verilog to HTML converter</p> +<p> +</p> +<hr /> +<h1><a name="synopsis">SYNOPSIS</a></h1> +<p><strong>v2html</strong> [options] file1 [file2] ...</p> +<p><strong>v2html</strong> is a perl 5 script that converts a bunch of verilog files to +html, linking various things to their definitions. At the moment +it handles:</p> +<dl> +<dt><strong><a name="item__2d_modules">- modules</a></strong></dt> + +<dt><strong><a name="item__2d_tasks">- tasks</a></strong></dt> + +<dt><strong><a name="item__2d_functions">- functions</a></strong></dt> + +<dt><strong><a name="item__2d_includes">- includes</a></strong></dt> + +<dt><strong><a name="item__2d_defines">- defines</a></strong></dt> + +<dt><strong><a name="item__2d_parameters">- parameters</a></strong></dt> + +<dt><strong><a name="item__2d_inputs_2coutputs_2cinouts">- inputs,outputs,inouts</a></strong></dt> + +<dt><strong><a name="item__2d_signals_3a_wire_2c_reg_etc_2e">- signals: wire, reg etc.</a></strong></dt> + +<dt><strong><a name="item__2d_mail_addresses_in_comments">- mail addresses in comments</a></strong></dt> + +<dt><strong><a name="item_urls">- http URLS (http://....) in comments</a></strong></dt> + +</dl> +<p>It also generates a page containing all the modules it found arranged +hierarchically and separate index pages for all of the files, modules, +signals, tasks and functions.</p> +<p>Once you have the html files they can be installed on a web-server, or +simply viewed using ``Open File...'' in your web browser.</p> +<p>Details on navigating around the converted files, and many examples +can be found at <a href="http://www.burbleland.com/v2html/v2html.html">http://www.burbleland.com/v2html/v2html.html</a> .</p> +<p>Note that as of version 5.0 <strong>v2html</strong> uses <em>cascading style sheets</em> +to colour the display. This means that pages viewed with old browsers +that do not support <em>cascading style sheets</em> (for instance Netscape +3) will not be coloured.</p> +<p> +</p> +<hr /> +<h1><a name="description">DESCRIPTION</a></h1> +<p>In addition to the switches detailed below <strong>v2html</strong> also accepts and +ignores most VCS/VerilogXL options and all other options starting with a +plus. This means that you should be able to run it with the same +command file you use for running simulations.</p> +<p>Switches:</p> +<dl> +<dt><strong><a name="item__2dq"><strong>-q</strong></a></strong></dt> + +<dd> +<p>Be quiet - so don't print all those informative messages about what +it is doing.</p> +</dd> +<dt><strong><a name="item__2bdefine_2bname_5b_3dvalue_5d"><strong>+define+NAME[=VALUE]</strong></a></strong></dt> + +<dd> +<p>Pre-define a value. This is just the same as putting:</p> +<pre> + 'define NAME [VALUE]</pre> +<p>at the top of each of your input files. The rather strange syntax is the +same as VerilogXL's. This option is useful for controlling which ifdefs appear +true to <strong>v2html</strong>.</p> +</dd> +<dt><strong><a name="item__2bincdir_2bdir"><strong>+incdir+DIR</strong></a></strong></dt> + +<dd> +<p>Specify a directory to search for include files (just like VerilogXL).</p> +</dd> +<dt><strong><a name="item__2dy_dir"><strong>-y</strong> DIR</a></strong></dt> + +<dd> +<p>Specify a directory to search for modules (just like VerilogXL). Note that +<strong>v2html</strong> doesn't do any fancy search orders like VerilogXL - it just searches +the directories in the order that you specify them on the command line.</p> +</dd> +<dt><strong><a name="item__2blibext_2bext"><strong>+libext+EXT</strong></a></strong></dt> + +<dd> +<p>Specify an extension use when searching for modules (just like +VerilogXL). You can specify multiple extensions using +<strong>+libext+EXT1+EXT2+EXT2</strong> or by specifying multiple <strong>+libext+EXT</strong> +options.</p> +</dd> +<dt><strong><a name="item__2dv_libfile"><strong>-v</strong> LIBFILE</a></strong></dt> + +<dd> +<p>Specify a library file to use (just like VerilogXL).</p> +</dd> +<dt><strong><a name="item__2df_command_lines_options_file"><strong>-f</strong> command_lines_options_file</a></strong></dt> + +<dd> +<p>Specify a file to get more command line options from. For instance you +could put a list of all your files in src_files and then use <strong>-f</strong> +src_file. You can also put in just about anything that you can put +on the command line. Comments can be included using # or // at the +start of the line. Wildcards are not allowed in the file names. Use +single or double quotes to quote arguments that have spaces in them +for example:</p> +<pre> + # v2html -f file - turn on gzip compression + -z -ze .gz + -zc 'gzip -f' + +You can have as many -f options as you want and you can probably put +-f options in the file too.</pre> +</dd> +<dt><strong><a name="item__2dm_mail_addr"><strong>-m</strong> mail_addr</a></strong></dt> + +<dd> +<p>A mail address of the site maintainer. This is placed in a field at +the bottom of each file like this:</p> +<pre> + This page: + Maintained by: Joe_Bloggs@barking.com + Created: Thu Nov 6 08:53:37 1997 + From: test2.v</pre> +</dd> +<dt><strong><a name="item__2di"><strong>-i</strong></a></strong></dt> + +<dd> +<p>Incremental mode. At the moment this isn't very incremental! <strong>v2html</strong> +checks the dates on all of the files that it read last time it ran and +all of the output files it generated. If any of the input files are +newer than the output files, or if the command line options have +changed it <strong>deletes</strong> all of the files it made last time and rebuilds +everything. The delete is done to stop old files accumulating in the +output directory.</p> +<p><strong>v2html</strong> keeps track of all the information it needs to do this in +a file called .v2html_incr .</p> +</dd> +<dt><strong><a name="item__2do_output_dir"><strong>-o</strong> output_dir</a></strong></dt> + +<dd> +<p>Set the output directory for the html files. The default is the current +directory.</p> +</dd> +<dt><strong><a name="item__2dcss_cascading_style_sheet"><strong>-css</strong> cascading_style_sheet</a></strong></dt> + +<dd> +<p>The appearance of all the different elements of the html page can be altered +using a <em>cascading style sheet</em>. By default <strong>v2html</strong> uses a file called +<em>v2html.css</em> in the same directory as the html files. You can customize the +appearance of the html files by editing this file (<strong>v2html</strong> will create one +if it does not exist, but will not overwrite an existing one).</p> +<p>If you have many different html directories which you want to use the +one central cascading style sheet then you can use the <strong>-css</strong> option +to specify one. For example:</p> +<pre> + -css <a href="http://www.barking.com/joes_style.css">http://www.barking.com/joes_style.css</a></pre> +</dd> +<dt><strong><a name="item__2dh_hier_file"><strong>-h</strong> hier_file</a></strong></dt> + +<dd> +<p>Set the name of the file that the hierarchy is written to. The default +is hierarchy.html. This is also used as a base for the index file +names. These file names are formed put appending <code>-f</code>, <code>-m</code>, +<a href="#item__2ds"><code>-s</code></a>, <code>-t</code> and <code>-fn</code> to the part of the hierarchy file name +before the first dot (so hier.htm will put the files index in +hier-f.htm).</p> +</dd> +<dt><strong><a name="item__2dht_top_module"><strong>-ht</strong> top_module</a></strong></dt> + +<dd> +<p>Set the name of a top module you want in the hierarchy. You can specify +multiple <strong>-ht</strong> options to specify multiple top modules.</p> +<p>If you don't specify any <strong>-ht</strong> options then <strong>v2html</strong> will inspect +the hierarchy and find all the top modules in the verilog files and +print these out. The 'top modules' are modules that are not +instantiated but instantiate other modules that <strong>v2html</strong> has found +the definition of.</p> +</dd> +<dt><strong><a name="item__2dhc_hierarchy_comment"><strong>-hc</strong> hierarchy_comment</a></strong></dt> + +<dd> +<p>Specifies some text to put at the top of the hierarchy. For instance:</p> +<pre> + v2html -hc "This is our ASIC" *.v</pre> +</dd> +<dt><strong><a name="item__2dhtf"><strong>-htf</strong></a></strong></dt> + +<dd> +<p>Turns on printing of tasks and functions in the hierarchy. By default they +are not printed.</p> +</dd> +<dt><strong><a name="item__2dlines_max_lines_per_file"><strong>-lines</strong> max_lines_per_file</a></strong></dt> + +<dd> +<p>In order to speed up viewing <strong>v2html</strong> splits large verilog files across multiple +html pages. This option lets you specify how many lines you want on a page. The +default is 1000.</p> +</dd> +<dt><strong><a name="item__2dnnm"><strong>-nnm</strong></a></strong></dt> + +<dd> +<p>No ``No modules''. By default the hierarchy contains three sections, the +hierarchies of the top modules, a list of files containing no modules +and a list of unconnected modules (modules that are not instantiated +but also do not qualify as top modules). The <strong>-nnm</strong> makes +<strong>v2html</strong> skip printing the list of files containing no modules. +See <strong>-nu</strong>.</p> +</dd> +<dt><strong><a name="item__2dnu"><strong>-nu</strong></a></strong></dt> + +<dd> +<p>No unconnected. See <strong>-nnm</strong>. Makes <strong>v2html</strong> skip writing the list +of unconnected modules in the hierarchy file.</p> +</dd> +<dt><strong><a name="item__2dnh"><strong>-nh</strong></a></strong></dt> + +<dd> +<p>No hierarchy. Don't print out the hierarchy.</p> +</dd> +<dt><strong><a name="item__2dnindex"><strong>-nindex</strong></a></strong></dt> + +<dd> +<p>No indexes. Don't print out the indexes.</p> +</dd> +<dt><strong><a name="item__2dni"><strong>-ni</strong></a></strong></dt> + +<dd> +<p>By default <strong>v2html</strong> 'greys out' any code that is ifdefed out. The <strong>-ni</strong> +turns this greying out off. Note that v2html always ignores code that is +ifdefed out when it is parsing.</p> +</dd> +<dt><strong><a name="item__2dz"><strong>-z</strong></a></strong></dt> + +<dd> +<p>Compress the html files generated (and make sure the links point to +the compressed versions). This can be useful if you convert machine +generated code, like ASIC RAM macros which are huge before they are +converted and even bigger afterwards.</p> +</dd> +<dt><strong><a name="item__2dzc_compresser"><strong>-zc</strong> compresser</a></strong></dt> + +<dd> +<p>The executable to use to compress the html files if <strong>-z</strong> is used. The +Default is 'compress <code>-f</code>'. For instance to use gzip use <strong>-zc</strong> 'gzip <code>-f</code>' +(the <code>-f</code> stops gzip prompting you about overwriting files).</p> +</dd> +<dt><strong><a name="item__2dze_compressed_extension"><strong>-ze</strong> compressed_extension</a></strong></dt> + +<dd> +<p>The extension that your compress executable uses. The default is '.Z'. If you +were using gzip then you'd want <strong>-ze</strong> .gz</p> +</dd> +<dt><strong><a name="item__2df__5bframe_file_2ehtml_5d"><strong>-F</strong> [frame_file.html]</a></strong></dt> + +<dd> +<p>Frame mode. Using <strong>-F</strong> turns on the generation of framed output +where a top level frame file is generated that creates three +frames in your browser, the top one for the hierarchy the middle +one for the code and the bottom one for any definitions to appear +in.</p> +<p>The default name for the frame file is frame.html. This default +can be overridden by specifying a file name after the <strong>-F</strong> option.</p> +</dd> +<dt><strong><a name="item__2dvf__5bframe_file_2ehtml_5d"><strong>-VF</strong> [frame_file.html]</a></strong></dt> + +<dd> +<p>Same as <strong>-F</strong> but arranges the frames vertically so that the hierarchy is +down the side.</p> +</dd> +<dt><strong><a name="item__2ds"><strong>-s</strong></a></strong></dt> + +<dd> +<p>Link to the source. This causes the file name in From field of +the page footer to become a link to the unconverted verilog +file:</p> +<pre> + This page: + Maintained by: Joe_Bloggs@barking.com + Created: Thu Nov 6 08:53:37 1997 + From: /asic/verilog/test2.v</pre> +<p>For this to work your web server must have access to the source code. +Also, you must either run v2html in the output directory or use +absolute path names for the verilog files.</p> +<p>For example, if the source is in /home/asic/verilog and the html files +want to end up in /home/www/verilog then there are two ways to run it +to get <strong>-s</strong> to work:</p> +<pre> + 1) In the output directory with verilog files specified by relative paths: + cd /home/www/verilog + v2html -s ../../asic/verilog/*.v</pre> +<pre> + 2) In any directory with verilog files specified by absolute paths: + cd /anywhere + v2html -s -o /home/www/verilog /home/asic/verilog/*.v</pre> +</dd> +<dt><strong><a name="item__2dc__2fcgi_script__2fpath_to_html_files"><strong>-c</strong> /cgi_script /path_to_html_files</a></strong></dt> + +<dd> +<p>Activate CGI features which allow the user to hide and show +regions of the hierarchy in a similar way to the old file manager +on windows 3.1. This method only works if you put the files on +a web server.</p> +<p>To use this you must have installed the v2html CGI script on your +web-server. The /cgi_script is the name of the CGI script (with path). +The /path_to_v_files is the directory you are putting your html files.</p> +<p>These paths are the paths your web server sees (not the full paths on +the system) so is the same path that appears after <em><a href="http://server">http://server</a></em> when +accessing the files.</p> +<p>Here's an example:</p> +<pre> + cp v2html-cgi /opt/CERNhttpd/cgi-bin/ + chmod 755 /opt/CERNhttpd/cgi-bin/v2html-cgi</pre> +<pre> + cd /home/web/v2html/example/ex1 + v2html -c /cgi-bin/v2html-cgi /v2html/example/ex1 ../verilog/*.v</pre> +<p>Note that <strong>v2html</strong> can't check the parameters to <strong>-c</strong> while +converting the files. You'll have to do it yourself by viewing the +hierarchy in your web browser and clicking on <strong>[Hide All]</strong> at the +top of the hierarchy. Make sure you view the file using the web server +(use <em><a href="http://server/v2html/example/ex1/hierarchy.html">http://server/v2html/example/ex1/hierarchy.html</a></em> rather than +<em>file:/home/web/v2html/example/ex1/hierarchy.html</em>).</p> +<p>Depending on your webserver you may also need to use the -css to +specify the full URL to your cascading stylesheet eg:</p> +<pre> + v2html -c /cgi-bin/v2html-cgi /v2html/examples/millennium_clock/hier_cgi + -css <a href="http://www.burbleland.com/v2html/examples/millennium_clock/hier_cgi/v2html.css">http://www.burbleland.com/v2html/examples/millennium_clock/hier_cgi/v2html.css</a> + *.v</pre> +<p>If you get a message like this when you click on <strong>[Hide All]</strong>:</p> +<pre> + Bad script request -- neither '/opt/CERNhttpd/cgi-bin/v2html-cg' + nor '/opt/CERNhttpd/cgi-bin/v2html-cg.pp' is executable</pre> +<p>Then either there is either a problem with the installation of the cgi +script or you have incorrectly specified the first parameter to <strong>-c</strong>.</p> +<p>If you get a message like this:</p> +<pre> + v2html error.</pre> +<p>then you have probably got the second parameter to <strong>-c</strong> wrong.</p> +</dd> +<dt><strong><a name="item__2dk_key_string"><strong>-k</strong> key_string</a></strong></dt> + +<dd> +<p>Specify the key to use for to stop people looking at hierarchy files +that are protected by web-server security. The default is to use a +random key, but this means that you can't have bookmarks of the +hierarchy in various states (because the bookmark will contain the +key, and the key will change each time you run <strong>v2html</strong>). To get +round this problem you can use <strong>-k</strong> and always have the same +key string. The key can be any string of digits and letters.</p> +</dd> +<dt><strong><a name="item__2dnjshier"><strong>-njshier</strong></a></strong></dt> + +<dd> +<p>Deactivate Javascript features that allow the user to hide and +collapse regions of the hierarchy.</p> +</dd> +<dt><strong><a name="item__2dncookies"><strong>-ncookies</strong></a></strong></dt> + +<dd> +<p>The Javascript version of the hierarchy uses cookies to remember the state +you left the hierarchy in, so when you next visit the hierarchy page it will +be in the same state. If you hate cookies then use the <strong>-ncookies</strong> option +to turn them off.</p> +</dd> +<dt><strong><a name="item__2dnsigpopup"><strong>-nsigpopup</strong></a></strong></dt> + +<dd> +<p>Turn off the generation of javascript that does the signal popup window. +Specifying this option also turns off ``Quick Search''.</p> +</dd> +<dt><strong><a name="item__2dtab_value"><strong>-tab</strong> value</a></strong></dt> + +<dd> +<p>Expand tabs to the specified value.</p> +</dd> +<dt><strong><a name="item__2ddebug"><strong>-debug</strong></a></strong></dt> + +<dd> +<p>Turn on lots of debugging information.</p> +</dd> +</dl> +<p> +</p> +<hr /> +<h1><a name="author">AUTHOR</a></h1> +<p>Costas Calamvokis <<em><a href="mailto:v2html730@burbleland.com">v2html730@burbleland.com</a></em>>.</p> +<p> +</p> +<hr /> +<h1><a name="examples">EXAMPLES</a></h1> +<p>Here is an example where <strong>v2html</strong> is run in the directory containing +the verilog files (note the <strong>-o</strong> option):</p> +<pre> + cd /users/jb/verilog_files/ + v2html -F my_frame.html -h my_hier.html -ht chip_top -htf -nu + -o /users/www/project/verilog -m Joe_Blogs@barking.com -s *.v</pre> +<p>As the verilog files don't have absolute paths and we aren't running +in the destination directory can't use <strong>-s</strong> (link to source) as the +links <strong>v2html</strong> will create wouldn't allow the web server to find the +files.</p> +<p>Here is an example where <strong>v2html</strong> is run in the directory where +we want the html files (no <strong>-o</strong> option):</p> +<pre> + cd /users/www/project/verilog + v2html -F my_frame.html -h my_hier.html -ht chip_top -nu -htf + -m Joe_Blogs@barking.com -s + -c /cgi-bin/v2html-cgi /project/verilog ../../../jb/verilog_files/*.v</pre> +<p>Here we can use the <strong>-s</strong> option because we are running the the destination +directory, so the links <strong>v2html</strong> creates to the source will work +(providing the web server is allowed to server files from +/users/jb/verilog_files).</p> +<p> +</p> +<hr /> +<h1><a name="diagnostics">DIAGNOSTICS</a></h1> +<p>By default <strong>v2html</strong> tells you a lot about what it is doing (this is +because it is slow and if it didn't you'd think it had crashed!). These +messages can get in the way of the warnings <strong>v2html</strong> produces, so if +you have a problem first try <strong>-q</strong> (quiet) to see if there are any warnings +you missed in the deluge of messages.</p> +<p>Most of the Error messages concern failures to open files, I guess these +will be caused by bad permissions, or you pointing <strong>v2html</strong> at files +or directories that don't exist.</p> +<p>The errors that say things like:</p> +<pre> + Warning: Confused in t.v line 2 (state=SIGNAL_AFTER_NAME): + wire g xx; + ^ +mean that you have written some verilog that I wasn't expecting - send +it to me and I'll see what I can do.</pre> +<p>Most of the warnings concern things that <strong>v2html</strong> will ignore because +it found more than one of them. The most common is a duplicate module +being found because an old copy of one of the files is lurking in +your source directory. The easiest way around this is to use the +<strong>-f</strong> option something like this:</p> +<pre> + ls /path/*.v | grep -v old_module_file.v > src_files + v2html -f src_files</pre> +<p>Generally <strong>v2html</strong> will ignore duplicate things (so for example +modules won't appear in the hierarchy), but sometimes it will just pick +one of them, so watch those warnings.</p> + +</body> + +</html> diff --git a/tools/htmlgen b/tools/htmlgen new file mode 100644 index 0000000000000000000000000000000000000000..a9bf588e2f88457fdf73ac7361ef1d596fb81453 --- /dev/null +++ b/tools/htmlgen @@ -0,0 +1 @@ +#!/bin/bash diff --git a/tools/socpull b/tools/socpull new file mode 100755 index 0000000000000000000000000000000000000000..42ff6515f1c6ed566a464cc2f9cc307878b517f0 --- /dev/null +++ b/tools/socpull @@ -0,0 +1,10 @@ +#!/bin/bash + +# Update all Submodules to latest commit +cd $DESIGN_ROOT; git submodule foreach --recursive git pull +# for d in $DESIGN_ROOT/* ; do +# if [ -f "$d/.git" ]; then +# echo "Git Pulling $d" +# cd $d; git pull; cd .. +# fi +# done \ No newline at end of file diff --git a/tools/socsim b/tools/socsim new file mode 100755 index 0000000000000000000000000000000000000000..194c887da51b7c398a430ffb98f9f61ed7138a02 --- /dev/null +++ b/tools/socsim @@ -0,0 +1,47 @@ +#----------------------------------------------------------------------------- +# SoC Labs socsim script to run required simulation +# A joint work commissioned on behalf of SoC Labs, under Arm Academic Access license. +# +# Contributors +# +# David Mapstone (d.a.mapstone@soton.ac.uk) +# +# Copyright 2023, SoC Labs (www.soclabs.org) +#----------------------------------------------------------------------------- + +#!/usr/bin/env bash + +# Check arguments passed to SoCSim +if [ $# -eq 0 ] + then + echo "No arguments supplied" +else + + # If clean command passed in clean a specific directory or all + if [ ${1} == "clean" ]; then + if [ $# -eq 1 ]; then + echo "No clean arguments supplied" + else + if [ ${2} == "all" ]; then + # Clean all simualation directories + echo "Cleaning all Project Simulation Directories" + rm -rf $PROJECT_DIR/simulate/sim + mkdir -p $PROJECT_DIR/simulate/sim + else + # Remove specific simulaiton directory + SIM_DIR=$PROJECT_DIR/simulate/sim/${2} + if [ -d "$SIM_DIR" ]; then + rm -rf $SIM_DIR + else + echo "Simulation Directory '${2}' doesn't exist" + fi + fi + fi + fi + + # Find a simulation script in the SoCSim environments of all subrepos + simscript=$(find ${SOCSIM_PATH//:/\ } -name "${1}.sh") + + # Run Script if Found + $simscript $@ +fi \ No newline at end of file