#!/usr/bin/perl -w
# See copyright, etc in below POD section.
######################################################################

require 5.005;
use Getopt::Long;
use IO::File;
use Pod::Usage;
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::Latex;
use SystemC::Vregs::Output::Param;
use SystemC::Vregs::TableExtract;

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

$VERSION = '1.463';

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

#======================================================================
# 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 = 0;
my $opten_class_h = 0;
my $opten_class_cpp = 0;
my $opten_defs_h = 0;
my $opten_defs_pm = 0;
my $opten_defs_v = 0;
my $opten_hash_pm = 0;
my $opten_info_cpp = 0;
my $opten_latex = 0;
my $opten_param_v = 0;
my $opten_struct_h = 0;

if (! GetOptions (
		  "help"	=> \&usage,
		  "debug"	=> sub { debug(1); },
		  "debugi=i"	=> sub { debug($_[1]); },
		  "version"	=> sub { print "Version $VERSION\n"; exit(0); },
		  "<>"		=> \&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; },
		  "latex!"	=> sub { $opten_latex		=$_[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,
		  "filename-latex=s"	=> \$opten_latex,
		  "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;
					 $opten_latex = $ena;
				     },
		  )) {
    die "%Error: Bad usage, try 'vreg --help'\n";
}

$opt_output.="/" if ($opt_output ne "" && $opt_output !~ m!/$!);
$opt_package or die "%Error: No --package specified; see --help\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,
	     change_error=>{},  # Don't report errors as temp won't exist
	     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"));
    }
    if ($opten_latex) {
	SystemC::Vregs::Output::Latex->new->write
	    (%p, filename=>_def_fn($opten_latex, "${opt_output}${opt_package}_latex.tex"));
    }
}

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

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

sub usage {
    print "Version $VERSION\n";
    pod2usage(-verbose=>2, -exitval => 2);
    exit (1);
}

sub debug {
    $Debug = shift || 1;
    $SystemC::Vregs::TableExtract::Debug = 1 if $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, Framemaker, LyX, or Latex) 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.  Vreg can also
create LaTex tables based on the register layouts.

=head1 GENERAL 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 --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 --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 --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 --version

Displays program version and exits.

=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 OUTPUT FILE TYPE ARGUMENTS

=over 4

=item --asm_h

Create an _asm.h file.

=item --class_cpp

Create a _class.cpp file.

=item --class_h

Create a _class.h C++ file.

=item --defs_h

Create a _defs.h file.

=item --defs_pm

Create a _defs.pl file.

=item --defs_v

Create a _defs.v file.

=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 --files

Create all files. Equivalent to: --class_h --class_cpp --defs_h --defs_pm
--defs_v --info_cpp --hash_pm --params_v --struct_h

=item --hash_pm

Create a _hash.pm file.

=item --info_cpp

Create an _info.cpp file.

=item --latex

Creates latex tables from the vregs definition, suitable for formatting
into printable documentation.

=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 --params_v

Create a _defs.pm file.

=item --struct_h

Create a _struct.h C file.

=back

=head1 DISTRIBUTION

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

Copyright 2001-2009 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 Version 3 or the Perl Artistic License
Version 2.0.

=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:
