#!/usr/bin/perl -w
#$Id: vreg 29376 2007-01-02 14:50:38Z wsnyder $
######################################################################
#
# Copyright 2001-2007 by Wilson Snyder.  This program is free software;
# you can redistribute it and/or modify it under the terms of either the GNU
# General Public License or the Perl Artistic License.
# 
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
######################################################################

require 5.005;
use Getopt::Long;
use IO::File;
use Pod::Text;
use Data::Dumper; $Data::Dumper::Indent=1; #Debug

use FindBin qw($RealBin);
use lib "$RealBin/..";
use lib "$RealBin/blib/lib";
use lib "$RealBin/blib/arch";
use SystemC::Vregs;
use SystemC::Vregs::Output::Class;
use SystemC::Vregs::Output::Defines;
use SystemC::Vregs::Output::Hash;
use SystemC::Vregs::Output::Info;
use SystemC::Vregs::Output::Param;
use SystemC::Vregs::TableExtract;

use strict;
use vars qw($Debug @Opt_Html_Files);

#======================================================================

#======================================================================
# main

autoflush STDOUT 1;
autoflush STDERR 1;

$Debug = 0;
our $Dry_Temp_File;
my @orig_argv = @ARGV;
@Opt_Html_Files = ();

my $opt_address_bits = 40;
my %opt_attributes;
my $opt_change_diff;
my $opt_comments = 1;
my $opt_dry_run;
my $opt_headers;
my $opt_if_product;
my $opt_output = "";
my $opt_package;
my $opt_rebuild_comment = "$0 @orig_argv";
my $opt_rm;
my $opt_rules;
my $opt_vregs;
my %opt_change_error;
our @opt_library_vregs = ();

my $opten_asm_h = 1;
my $opten_class_h = 1;
my $opten_class_cpp = 1;
my $opten_defs_h = 1;
my $opten_defs_pm = 1;
my $opten_defs_v = 1;
my $opten_info_cpp = 1;
my $opten_param_v = 1;
my $opten_struct_h = 1;
my $opten_hash_pm = 1;

if (! GetOptions (
		  "help"	=> \&usage,
		  "debug"	=> \&debug,
		  "<>"		=> \&parameter,
		  "address_bits=i" => \$opt_address_bits,
		  "change-diff=s" => \$opt_change_diff,
		  "change-error=s" => sub { shift; my $f=shift; $opt_change_error{$f}=1;},
		  "dry-run!"	=> \$opt_dry_run,
		  "headers!"	=> \$opt_headers,
		  "html=s"	=> sub { shift; push @Opt_Html_Files, shift; },
		  "if-product=s" => \$opt_if_product,
		  "library=s"	=> sub { shift; push @opt_library_vregs, shift; },
		  "output=s"	=> \$opt_output,
		  "package=s"	=> \$opt_package,
		  "comments!"	=> \$opt_comments,
		  "rebuild-comment=s"	=> \$opt_rebuild_comment,
		  "rm"		=> \$opt_rm,
		  "rules=s"	=> \$opt_rules,
		  "v2k!"	=> sub { shift; $opt_attributes{v2k} = shift; },
		  "vregs=s"	=> \$opt_vregs,
		  # File Enables (if adding one, add to files! also)
		  "asm_h!"	=> sub { $opten_asm_h		=$_[1]?1:0; },
		  "class_cpp!"	=> sub { $opten_class_cpp	=$_[1]?1:0; },
		  "class_h!"	=> sub { $opten_class_h	=$_[1]?1:0; },
		  "defs_h!"	=> sub { $opten_defs_h	=$_[1]?1:0; },
		  "defs_pm!"	=> sub { $opten_defs_pm	=$_[1]?1:0; },
		  "defs_v!"	=> sub { $opten_defs_v	=$_[1]?1:0; },
		  "hash_pm!"	=> sub { $opten_hash_pm	=$_[1]?1:0; },
		  "info_cpp!"	=> sub { $opten_info_cpp	=$_[1]?1:0; },
		  "param_v!"	=> sub { $opten_param_v	=$_[1]?1:0; },
		  "struct_h!"	=> sub { $opten_struct_h	=$_[1]?1:0; },
		  "filename-asm-h=s"	=> \$opten_asm_h,
		  "filename-class-cpp=s"=> \$opten_class_cpp,
		  "filename-class-h=s"	=> \$opten_class_h,
		  "filename-defs-h=s"	=> \$opten_defs_h,
		  "filename-defs-pm=s"	=> \$opten_defs_pm,
		  "filename-defs-v=s"	=> \$opten_defs_v,
		  "filename-hash-pm=s"	=> \$opten_hash_pm,
		  "filename-info-cpp=s"	=> \$opten_info_cpp,
		  "filename-param-v=s"	=> \$opten_param_v,
		  "filename-struct-h=s"	=> \$opten_struct_h,
		  "files!"	=> sub { my $ena = $_[1] ? 1:0;
					 $opten_asm_h = $ena;
					 $opten_class_cpp = $ena;
					 $opten_class_h = $ena;
					 $opten_defs_h = $ena;
					 $opten_defs_pm = $ena;
					 $opten_defs_v = $ena;
					 $opten_hash_pm = $ena;
					 $opten_info_cpp = $ena;
					 $opten_param_v = $ena;
					 $opten_struct_h = $ena;
				     },
		  )) {
    usage();
}

$opt_output.="/" if ($opt_output ne "" && $opt_output !~ m!/$!);
$opt_package or die "%Error: No --package specified\n";

$opt_vregs = "${opt_output}${opt_package}.vregs" if !defined $opt_vregs;

my @vr_params = (address_bits=>$opt_address_bits,
		 comments=>$opt_comments,
 		 rebuild_comment=>$opt_rebuild_comment,
		 attributes=>\%opt_attributes,
		 if_product=>$opt_if_product,
		 );

my @opt_library_vr;
foreach my $vlib (@opt_library_vregs) {
    my $vr = new SystemC::Vregs (@vr_params,
				 libraries=>\@opt_library_vr,);
    print "Reading library $vlib.vregs\n" if $Debug;
    $vr->regs_read ("${opt_output}${vlib}.vregs");
    $vr->check();
    $vr->exit_if_error();
    push @opt_library_vr, $vr;
}

@vr_params = (@vr_params,
	      libraries=>\@opt_library_vr,
	      param_always_32bits=>undef,);

my $vr = new SystemC::Vregs (@vr_params,
			     name=>$opt_package,
			     );

if ($#Opt_Html_Files >= 0) {
    foreach my $filename (@Opt_Html_Files) {
	print "Reading $filename\n";
	$opt_headers = 1 if !defined $opt_headers;
	if ($opt_rm) {
	    (my $rm = $filename) =~ s/\.html?$/_files/;
	    system ("rm -rf $rm") if -d $rm;
	    SystemC::Vregs::TableExtract::clean_html_file($filename);
	}
	$vr->html_read ($filename);
    }
    $vr->exit_if_error();
    $vr->check();
    $vr->exit_if_error();

    my %p = (pack=>$vr,
	     keep_timestamp => 1,	# Don't write if the same
	     dry_run=>$opt_dry_run,
	     change_diff=>$opt_change_diff,
	     change_error=>\%opt_change_error,
	     );
    SystemC::Vregs::Output::Layout->new->write
	(%p, filename=>$opt_vregs);

    if ($opt_dry_run) {  # Need to write for below step to read
	$Dry_Temp_File = ($ENV{TEMP}||$ENV{TMP}||"/tmp")."/.vreg_$$.vregs";
	SystemC::Vregs::Output::Layout->new->write
	    (%p, 
	     dry_run=>0,
	     filename=>$Dry_Temp_File);
    }
}
	      
if ($opt_headers) {
    my $v2 = new SystemC::Vregs (@vr_params);
    if ($Dry_Temp_File) {
	# We didn't write out the vregs file, so we can't read it.  Just grab the data directly.
	$v2->regs_read ($Dry_Temp_File);
    } else {
	$v2->regs_read ($opt_vregs);
    }
    $v2->exit_if_error();
    $v2->remove_if_mismatch();
    $v2->check();
    $v2->exit_if_error();
    (!$opt_rules || -r $opt_rules) or die "%Error: Can't find rules file $opt_rules\n";
    $v2->rules_read ($opt_rules||"${opt_output}${opt_package}__rules.pl");

    my %p = (pack=>$v2,
	     keep_timestamp => 1,	# Don't write if the same
	     dry_run=>$opt_dry_run,
	     change_diff=>$opt_change_diff,
	     change_error=>\%opt_change_error,
	     );
    #SystemC::Vregs::Output::Layout->new->write (%p, filename=>"${opt_output}${opt_package}.2.vregs");
    if ($opten_defs_v) {
	SystemC::Vregs::Output::Defines->new->write
	    (%p, filename=>_def_fn($opten_defs_v, "${opt_output}${opt_package}_defs.v"),
	     language=>'Verilog',);
    }
    if ($opten_defs_h) {
	SystemC::Vregs::Output::Defines->new->write
	    (%p, filename=>_def_fn($opten_defs_h, "${opt_output}${opt_package}_defs.h"),
	     language=>'C',);
    }
    if ($opten_defs_pm) {
	SystemC::Vregs::Output::Defines->new->write
	    (%p, filename=>_def_fn($opten_defs_pm, "${opt_output}${opt_package}_defs.pm"),
	     language=>'Perl',);
    }
    if ($opten_asm_h) {
	SystemC::Vregs::Output::Defines->new->write
	    (%p, filename=>_def_fn($opten_asm_h, "${opt_output}${opt_package}_asm.h"),
	     language=>'Gas',);
    }
    if ($opten_hash_pm) {
	SystemC::Vregs::Output::Hash->new->write
	    (%p, filename=>_def_fn($opten_hash_pm, "${opt_output}${opt_package}_hash.pm",));
    }
    if ($opten_param_v) {
	SystemC::Vregs::Output::Param->new->write
	    (%p, filename=>_def_fn($opten_param_v, "${opt_output}${opt_package}_param.v",));
    }
    if ($opten_info_cpp) {
	my $fn = _def_fn($opten_info_cpp, "${opt_output}${opt_package}_info.cpp");
	SystemC::Vregs::Output::Info->new->write_cpp
	    (%p, filename=>$fn);
	(my $fn2 = $fn) =~ s/\.cpp$/\.h/;
	SystemC::Vregs::Output::Info->new->write_h
	    (%p, filename=>$fn2);
    }
    if ($opten_class_h) {
	SystemC::Vregs::Output::Class->new->write_class_h
	    (%p, filename=>_def_fn($opten_class_h, "${opt_output}${opt_package}_class.h"));
    }
    if ($opten_class_cpp) {
	SystemC::Vregs::Output::Class->new->write_class_cpp
	    (%p, filename=>_def_fn($opten_class_cpp, "${opt_output}${opt_package}_class.cpp"));
    }
    if ($opten_struct_h) {
	SystemC::Vregs::Output::Class->new->write_struct_h
	    (%p, filename=>_def_fn($opten_struct_h, "${opt_output}${opt_package}_struct.h"));
    }
}

#----------------------------------------------------------------------

END {
    unlink $Dry_Temp_File if $Dry_Temp_File;
    $Dry_Temp_File = undef;
}

sub usage {
    print '$Id: vreg 29376 2007-01-02 14:50:38Z wsnyder $ ', "\n";
    $SIG{__WARN__} = sub{};	#pod2text isn't clean.
    pod2text($0);
    exit (1);
}

sub debug {
    $Debug = 1;
    #$SystemC::Vregs::TableExtract::Debug = 1;
    $SystemC::Vregs::Debug = 1;
}

sub parameter {
    my $param = shift;
    die "%Error: Unknown parameter: $param\n";
}
 
#######################################################################

sub _def_fn {
    my $switch = shift;
    my $default = shift;
    # Calculate default or passed in filename
    return (($switch eq "1") ? $default : $switch);
}

#######################################################################
__END__

=pod

=head1 NAME

vreg - Register configuration utility

=head1 SYNOPSIS

  vreg --help
  vreg ...

=head1 DESCRIPTION

Vreg has two major uses.  The first vreg converts a HTML document (saved
via Word or Framemaker) to a vreg file.  The second generates standard
headers from the .html or from a .vregs file.

=head1 HTML CONVERSION

When the --html switch is used, vreg generates a .vregs file from a .html
file.  The file format is described in vregs_spec.doc or vregs_spec.htm.

When saving HTML, be sure to change both the filename to .htm and the
file format to HTML.  If inside Microsoft Word, exit.
     
=head1 HEADER CREATION

When the --header switch is used, vreg reads a .vregs file and creates
definition and class headers in the --output directory.

=head1 ARGUMENTS

=over 4

=item --change-diff I<program>

With --change-error, use this program to determine if the error should be
reported or not.  The program should exit with non-zero status to raise an
error.

=item --change-error I<language>

Report error if file of the given language would change.  Use ALL for all
languages.  This option may be given multiple times to specify multiple
languages.

=item --dry-run

Don't write any output files, mearly report which files would change.

=item --help

Displays this message and program version and exits.

=item --headers

Creates the header files and other files after the hardware.vregs
file has changed.

=item --html I<html_filename>

Reads the specified filename and creates a .vregs file from the html.

=item --if-product I<product>

Only include registers in output with a Product column matching that
specified.  See the vregs_spec document for more information.

=item --filename-asm-h I<filename>

Specify the output filename for the asm_h file.
By default this is {--output}{package_name}_asm.h.

Similar options exist for all of the other output files.

=item --library I<package>

Specifies a package to be used to resolve subclasses.  The library package
will only be read, not changed in any way.

=item --noasm_h

Suppresses creating the _asm.h file.

=item --nocomments

Add minimal comment to the header files, to save disk space and time.
Users of the file will have to then look at the .vregs file to determine
what bits represent.

=item --noclass_cpp

Suppresses creating the _class.cpp file.

=item --noclass_h

Suppresses creating the _class.h C++ file.

=item --nodefs_h

Suppresses creating the _defs.h file.

=item --nodefs_pm

Suppresses creating the _defs.pl file.

=item --nodefs_v

Suppresses creating the _defs.v file.

=item --nohash_pm

Suppresses creating the _hash.pm file.

=item --noinfo_cpp

Suppresses creating the _info.cpp file.

=item --noparams_v

Suppresses creating the _defs.pm file.

=item --nostruct_h

Suppresses creating the _struct.h C file.

=item --nofiles

Suppresses creating all files. Equivalent to: --noclass_h --noclass_cpp
--nodefs_h --nodefs_pm --nodefs_v --noinfo_cpp --nohash_pm --noparams_v
--nostruct_h

You can then use specific enables after the --nofiles to turn on the
desired files, for example: --nofiles --class_h --defs_h --hash_pm 

=item --output

Specifies the directory name to be used for all output files.

=item --package

Specifies the name of the package; used for creating all filenames.

=item --rebuild-comment

Specifies a comment to be placed into output files describing to users how
to rebuild the file.

=item --rm

When used with the --html switch, remove a directory named
I<html_name>_files.  (This generally contains images written by Microsoft
Word that are not needed.)  Also, clean up the HTML file to compress out
useless style tags that make the file less humanly readable.

=item --rules

Specify the name of the rules file.  (See L<SystemC::Vregs::Rules>.)
Defaults to the I<out_directory>/I<package>__rules.pl.

=item -v2k

Use Verilog-2001 constructs, such as localparam.  Identical to the v2k
package attribute.

=item -vregs

Specify vregs format file to be read or written.  Defaults to be in the
directory specified by --output with the filename I<package>.vregs.

=back

=head1 DISTRIBUTION

Vregs is part of the L<http://www.veripool.com/> free Verilog software tool
suite.  The latest version is available from CPAN and from
L<http://www.veripool.com/vregs.html>.

Copyright 2001-2007 by Wilson Snyder.  This package is free software; you
can redistribute it and/or modify it under the terms of either the GNU
Lesser General Public License or the Perl Artistic License.

=head1 AUTHORS

Wilson Snyder <wsnyder@wsnyder.org>

=head1 SEE ALSO

L<SystemC::Vregs>, L<SystemC::Vregs::Rules>

L<vregs_spec.htm> from the SystemC::Vregs distribution.

=cut

######################################################################
### Local Variables:
### compile-command: "make && make test"
### End:
