#!perl -w
# -d:ptkdb
# Chuck Benz, Hollis, NH   Copyright (c)2002
# http://asics.chuckbenz.com
#
# The information and description contained herein is the
# property of Chuck Benz.
#
# Permission is granted for any reuse of this information
# and description as long as this copyright notice is
# preserved.  Modifications may be made as long as this
# notice is preserved.
# csrGen.pl - by Chuck Benz
#
$version = '$Id: csrGen.pl,v 1.32 2004/09/16 23:59:48 cbenz Exp cbenz $ ' ;
$version =~ s/\$Id/ csrGenId/ ;
#
# $Log: csrGen.pl,v $
# Revision 1.32  2004/09/16 23:59:48  cbenz
# check perl version.
#
# Revision 1.2  2004/08/03 20:14:28  cbenz
# update
#
# Revision 1.31  2004/08/03 19:59:53  cbenz
# Add HTML documentation of generated registers
#
# Revision 1.30  2003/09/15 03:19:29  cbenz
# Adding data dumper for use with csrgen2header
#
# Revision 1.29  2003/05/20 17:50:41  cbenz
# Add some identification to output with RCS ID.
#
# Revision 1.28  2003/05/20 17:45:58  cbenz
# Infer the 'intern' property if a flop has already been declared.
# And if flop is already declared, don't do the 'reg ' declaration.
# Note that if flop isn't declared, and field is marked intern, all
# is still fine - reg declaration will be written.  Inference just
# doesn't work if %F is after then csr declaration.
#
#
# Version 1.27, 19-May-2003, Infer SUBM if it is not specified.  Create a per
#				CSR list of properties for that purpose.
#				Does have a ~30-40% performance impact
# Version 1.26, 10-Apr-2003, Have %FVAL and %RESETVAL use rest of line;
#				add %INCLUDE, %BASEADDR;
#				correct use of address multiple with %AREPEAT
# Version 1.25,  3-Feb-2003, don't use /*AUTOREGS*/ and /*AUTOWIRE*/, add defines, dec or hex addresses
# Version 1.24, 15-Jan-2003, incorporated Steve Sherman's improvements:
#				%AUTO to support the Emacs Verilog-Mode AUTO extensions
#				hex addresses in comments in case statements
#			     add INCRS, INCR, DECRS, DECR, PULSEA, and PULSE field types.
#			     also changed some defaults, and dropped case sensitivity.
# Version 1.23,  8-Nov-2002, added SOR, DOR, DORS, IOR, IORS, WO, W1S field types; COR fields aren't written
# Version 1.22, 18-Sep-2002, allow endloop as loopend alternative
# Version 1.21, 13-Sep-2002, fix bug to parsing strings more precisely
# Versoin 1.19, 23-Jul-2002, add %LOOP to %V block as well.
# Version 1.18, 19-Jul-2002, Add %LOOP construct, and "%" syntax to %*REPEAT...
# Version 1.16,  7-Jul-2002, consolidate some code into subs; 
#                            REPEAT version of R/W/I/O,
#                            W1C forcing, %FVAL,
#                            pass comments through,
#                            pipelining of read bus,
#                            bug fix with repeat and stickys
# Version 1.4,   2-May-2002, add %FREPEAT
# Version 1.3,  11-Apr-2002, fixes and added wirelist
# See csrGen.txt for a description.
#
die "Requires perl V5.5 or greater" if $] < 5.005;
# Start by initializing default values
$blockname = "chip_up_ifc" ;
$clockname = "clock" ;
$resetlname = "initl" ;
$roaddr = 0 ;
$coraddr = 0 ;
$w1caddr = 0 ;
$w1cname = "" ;
$corname = "" ;
$wtask = "" ;
$rtask = "" ;
$hexaddress = "" ;
$interruptmask = 0 ;
$resetvalue = 0 ;
$writedata = "up_datain" ;
$readdata = "up_dataout_D" ;
$addressmultiple = 1 ;
$automatics = 0 ; 
$v2k = 0 ;
$readmuxdivider = 0 ;
$totalflopcount = 0 ;
$Reg = "" ;
$subsetrange = "" ;
$subsetmsb = 0 ;
$subsetlsb = 0 ;
$LoopCnt = 0 ;
$baseaddr = 0 ;
my (@iolist) ;
my (@inplist) ;
my (@outlist) ;
my (@reglist) ;
my (@repeatlist) ;
my (@scratcha) ;
my (@wirelist) ;
my (@opencomment) ;
my (@definelist) ;
my (@htmllist) ;
my %RegDefs = (); # stores definitions; see bottom of file for format
my $ReservedField  = 'RESERVED_FOR_csrGen' ;
my %ReservedFields = ( 'ADDRESS'   => 'ADDRESS'		# for checking spelling only
		       , 'REPEAT'    => 'REPEAT'    
		       , 'INCREMENT' => 'INCREMENT' 
		       ) ;

my (@temp) ;
my (%intrvectlist) ;

# Then read the file
@ARGV = ('-') unless @ARGV ;
while ($ARGV = shift) {
    $fpi = 1 ;
    open $fp[$fpi], $ARGV or die $!;
  CHANGEFILE: while ($fpi > 0) {
      $tmpfp = $fp[$fpi] ;
    PERCENT: while (<$tmpfp>) {
	@line = split ;
	if (/^\#/) { next }
	elsif (/^%INCLUDE/i) {
	    # it would be nice to have a list of directories to check
	    # but for now we'll require this to be relative to CWD rather than
	    # relative to the base directory of the file that does the including...
	    open $fp[++$fpi], $line[1] or die "error opening include file: $line[1]" ;
	    next CHANGEFILE ;
	}
	elsif (/^%BASE/i) { 
	    if ($line[1] =~ /^0/) { $baseaddr = hex($line[1]) }
	    else {$baseaddr = $line[1]}
	} 
	elsif (/^%B/i) { $blockname = $line[1] }
	elsif (/^%C/i) { $clockname = $line[1] }
	elsif (/^%RST/i) { $resetlname = $line[1] }
	elsif (/^%WD/i) { $writedata = $line[1] }
	elsif (/^%RD/i) { $readdata = $line[1] }
	elsif (/^%AM/i) { $addressmultiple = $line[1] }
	elsif (/^%AUTO/i) { $automatics = 1 }
	elsif (/^%SENSTAR/i) { $v2k = 1 }
	elsif (/^%W1C/i) { $w1cname = $line[1] }
	elsif (/^%COR/i) { $corname = $line[1] }
	elsif (/^%RM/i) { $readmuxdivider = $line[1] }
	elsif (/^%REV/i) {
	    # if line includes the RCS/CVS ID tag, take all after that, else take full line
	    if (($pos = index ($_, "\$Id")) > 0) {
		$version = $version . "\n// from input revision" . substr ($_, $pos + 3) ;
	    }
	    else {
		$version = $version . "\n// from input revision: " . $_ ;
	    }
	}
	elsif (/^%RREPEAT/i) {
	    $repeatcount = $line[1] ;
	    shift(@line) ;
	    donamewidth () ;
	    for ($i=0;$i<$repeatcount;$i++) {
		push (@reglist, "reg $width $name$i ;\n") ;
	    }
	}
	elsif (/^%R/i) {
	    donamewidth () ;
	    push (@reglist, "reg $width $name ;\n") ;
	}
	elsif (/^%WREPEAT/i) {
	    $repeatcount = $line[1] ;
	    shift(@line) ;
	    donamewidth () ;
	    for ($i=0;$i<$repeatcount;$i++) {
		push (@reglist, "wire $width $name$i ;\n") ;
		push (@senslist, "$name$i") ;
	    }
	}
	elsif (/^%W/i) {
	    donamewidth () ;
	    push (@reglist, "wire $width $name ;\n") ;
	    push (@senslist, $name) ;
	}
	elsif (/^%RESETVALREPEAT/i) {
	    shift (@line) ;
	    $repeatcount = shift (@line) ;
	    $name = shift (@line) ;
	    $temp = join ' ', @line ;
	    for ($i=0;$i<$repeatcount;$i++) {
		($resetval{"$name$i"} = $temp) =~ s/%/$i/g ;
	    }
	}
	elsif (/^%RESETVAL/i) {
	    shift (@line) ;
	    $name = shift (@line) ;
	    $resetval{$name} = join ' ', @line ;
	}
	elsif (/^%FVALREPEAT/i) {
	    shift (@line) ;
	    $repeatcount = shift (@line) ;
	    $name = shift (@line) ;
	    $temp = join ' ', @line ;
	    for ($i=0;$i<$repeatcount;$i++) {
		($flopval{"$name$i"} = $temp) =~ s/%/$i/g ;
	    }
	}
	elsif (/^%FVAL/i) {
	    shift (@line) ;
	    $name = shift (@line) ;
	    $flopval{"$name"} = join ' ', @line ;
	}
	elsif (/^%FREPEAT/i) {
	    $repeatcount = $line[1] ;
	    shift(@line) ;
	    donamewidthfloprepeat () ;
	    for ($i=0;$i<$repeatcount;$i++) {
		push (@floplist, "$name$i") ;
		if (@line > 3) { ($resetval{"$name$i"} = $line[3]) =~ s/%/$i/g }
		else  { $resetval{"$name$i"} = 0 }
		if (@line > 4) {
		    push (@reglist, "reg $width $name$i ;\n") ;
		    ($flopval{"$name$i"} = $line[4]) =~ s/%/$i/g ;
		}
		else  {
		    push (@reglist, "reg $width $name$i, $name${i}_D ;\n") ;
		    $flopval{"$name$i"} = "$name${i}_D" ;
		      push (@defvallist, "  $name${i}_D = $name$i ;\n") ;
		}
	    }
	}
	elsif (/^%F/i) {
	    donamewidthflop () ;
	    push (@floplist, $name) ;
	    if (@line > 3) { $resetval{$name} = $line[3] }
	    else  { $resetval{$name} = 0 }
	    if (@line > 4) {
		push (@reglist, "reg $width $name ;\n") ;
		$flopval{$name} = $line[4] ;
	    }
	    else  {
		  push (@reglist, "reg $width $name, ${name}_D ;\n") ;
		  $flopval{$name} = "${name}_D" ;
		  push (@defvallist, "  ${name}_D = $name ;\n") ;
	      }
	}
	elsif (/^%IREPEAT/i) {
	    $repeatcount = $line[1] ;
	    shift(@line) ;
	    donamewidth () ;
	    for ($i=0;$i<$repeatcount;$i++) {
		addinput ("$name$i", $width) ;
	    }  
	}
	elsif (/^%I/i) {
	    donamewidth () ;
	    addinput ($name, $width) ;
	}
	elsif (/^%OFREPEAT/i) {
	    $repeatcount = $line[1] ;
	    shift(@line) ;
	    donamewidthfloprepeat () ;
	    for ($i=0;$i<$repeatcount;$i++) {
		addoutput ("$name$i", $width);
		push (@floplist, "$name$i") ;
		if (@line > 3) { $resetval{"$name$i"} = $line[3] }
		else  { $resetval{"$name$i"} = 0 }
		if (@line > 4) {
		    push (@reglist, "reg $width $name$i ;\n") ;
		    ($flopval{"$name$i"} = $line[4]) =~ s/%/$i/g ;
		}
		else  {
		    push (@reglist, "reg $width $name$i, $name${i}_D ;\n") ;
		    $flopval{"$name$i"} = "$name${i}_D" ;
		    push (@defvallist, "  $name${i}_D = $name$i ;\n") ;
		}
	    }
	}
	elsif (/^%OF/i) {
	    donamewidthflop () ;
	    addoutput ($name, $width);
	    $totalflopcount = $totalflopcount + 1 ;
	    push (@floplist, $name) ;
	    if (@line > 3) { $resetval{$name} = $line[3] }
	    else  { $resetval{$name} = 0 }
	    if (@line > 4) {
		push (@reglist, "reg $width $name ;\n") ;
		  $flopval{$name} = $line[4] ;
	    }
	    else  {
		push (@reglist, "reg $width $name, ${name}_D ;\n") ;
		$flopval{$name} = "${name}_D" ;
		push (@defvallist, "  ${name}_D = $name ;\n") ;
	    }
	}
	elsif (/^%OREPEAT/i) {
	    $repeatcount = $line[1] ;
	    shift(@line) ;
	    donamewidth () ;
	    for ($i=0;$i<$repeatcount;$i++) {
		addoutput ("$name$i", $width);
	    }
	}
	elsif (/^%O/i) {
	    donamewidth () ;
	    addoutput ($name, $width);
	}
	elsif (/^%VCL/i) {
	    while (<$tmpfp>) {
		if (/^%E/i) { next PERCENT }
		if (/%intrlogic/i) { push (@reglist, "reg  interrupt ;\n") ; }
		  push (@comblogiclist, $_) ;
	    }
	}
	elsif (/^%V/i) {
	    while (<$tmpfp>) {
		if (/^%E/i) { next PERCENT }
		push (@veriloglist, $_) ;
	    }
	}
	elsif (/^\/\//) {
	    push (@opencomment, $_) ; 
	}
	elsif (/^\/\*.*\*\//) {
	    push (@opencomment, $_) ; 
	}
	elsif (/^\/\*/) {
	    push (@opencomment, $_) ;
	    while (<$tmpfp>) {
		push (@opencomment, $_) ;
		if (/\*\//) { next PERCENT }
	    }
	}
	elsif (/^%AREPEAT/i) {
	    if ($line[1] =~ /^0x/) { $startaddr = hex($line[1]) }
	    else { $startaddr = $line[1] } ;
	    $startaddr = $startaddr + $baseaddr ;
	    if (($startaddr % $addressmultiple) != 0) {
		die "start address not multiple of ${addressmultiple}: \n@line\n" ;
	    }
	    $startaddr = $startaddr / $addressmultiple ;
	    if ($line[2] =~ /^0x/) { $repeatcount = hex($line[2]) }
	    else { $repeatcount = $line[2] } ;
	    shift (@line) ; shift (@line) ; shift (@line) ;
	    if (@line > 0) { $increment = shift(@line) ; }
	    else { $increment = $addressmultiple ; }
	    if (($increment % $addressmultiple) != 0) {
		die "increment not multiple of ${addressmultiple}: \n@line\n" ;
	    }
	    getaddrflags (@line) ;

	    push (@htmllist, "<table border=2 bordercolor=black cellspacing=0 width=570>\n");
	    if ($increment == $addressmultiple) {
		$Regaddr = sprintf( "0x%04x through 0x%04x", $startaddr * $addressmultiple,
				    $startaddr * $addressmultiple + ($repeatcount-1) * $increment) ;
		}
	    else {
		$Regaddr = sprintf( "0x%04x through 0x%04x, incrementing by 0x%04x", 
				    $startaddr * $addressmultiple,
				    $startaddr * $addressmultiple + ($repeatcount-1) * $increment,
				    $increment) ;
		}
	    push (@htmllist, " <tr><td colspan=4 width=100% bgcolor=#c0c0c0><b>Addresses: ", $Regaddr) ;
	    if ($commentfield eq "") { push (@htmllist, " </b></td></tr>\n") }
	    else {push (@htmllist, " - ", $commentfield, "</b></td></tr>\n") }

	    @repeatlist = () ;
	    while (<$tmpfp>) {
		if (/^[0-9]/) { push (@repeatlist, $_) ; }
		elsif (/^%/) {
		    doaddrepeat() ;
		    push (@htmllist, "</table><br>\n") ;
		    redo PERCENT ;
		}
	    }
	    doaddrepeat() ;
	    last ;  # needed if end of file during a %AREPEAT block
	}
	elsif (/^%A/i) {
	    if ($line[1] =~ /^0/) { $address = hex($line[1]) }
	    else { $address = $line[1] } ;
	    $intaddr = int (($baseaddr + $address) / $addressmultiple) ;
	    if ($readmuxdivider == 0) { $readdatabus = $readdata }
	    else { 
		$mid = int($intaddr / $readmuxdivider) ;
		  $readdatabus = "${readdata}$mid" ;
	    }
	    if (($intaddr * $addressmultiple) != ($baseaddr + $address)) {
		die "address not multiple of ${addressmultiple}: \n@line\n" ; }
	    shift (@line) ; shift (@line) ;
	    getaddrflags (@line) ;
	    $hexaddress = sprintf ( "0x%0x", $baseaddr + $address ) ;
	    $printaddress = $intaddr * $addressmultiple ;
	    push (@readlist, "$intaddr: begin // $hexaddress\n") ;
	    push (@writelist, "$intaddr: begin // $hexaddress\n") ;

	    # if we had named registers, that would be the top level 	# Jak
	    # index instead of Reg just using the address as a string.	# Jak
	    $Reg = sprintf( "Addr_0x%08x", $intaddr ) ; 		# Jak
	    # Note to downstream programs: this address is DECIMAL		# Jak
	    AddRegToRegDefs( $Reg , 'ADDRESS'   , $intaddr );		# Jak
	    AddRegToRegDefs( $Reg , 'REPEAT'    , 1        );		# Jak
	    AddRegToRegDefs( $Reg , 'INCREMENT' , 1        );		# Jak

	    push (@htmllist, "<table border=2 bordercolor=black cellspacing=0 width=570>\n");
	    $Regaddr = sprintf( "0x%04x", $intaddr ) ;
	    push (@htmllist, " <tr><td colspan=4 width=100% bgcolor=#c0c0c0><b>Address: ", $Regaddr) ;
	    if ($commentfield eq "") { push (@htmllist, " </b></td></tr>\n") }
	    else {push (@htmllist, " - ", $commentfield, "</b></td></tr>\n") }
	    
	    while (<$tmpfp>) {
		if (/^%/) {
		    if (($rtask ne "") && ($rtask ne "-")) {
			push (@readlist, "$rtask ;\n")
			}
		    if (($wtask ne "") && ($wtask ne "-")) {
			push (@writelist, "$wtask ;\n")
			}
		    push (@readlist, "end\n") ;
		    push (@writelist, "end\n") ;
		    push (@htmllist, "</table><br>\n") ;
		    redo PERCENT ;
		} ;
		if (/^[0-9]/) {
		    doaline (@_) ;
		}
	    }
	    last ;  # needed if end of file during a %A block - but note possible bug -  
	            # might miss adding task calls? and "end\n" ???
	}
    }
      $fpi-- ;
  }
}

foreach $reg (keys (%submax)) {
    %prop = @{$regProps{$reg}} ;
    if ($prop{'submdone'}) { next } ;
    $width = "\t[$submax{$reg}:$submin{$reg}]\t" ;
    $name = $nameo = $reg ;
    $size = $submax{$reg} - $submin{$reg} ;
    $flopsize = $size + 1 ;
    push (@definelist, "define csr_${name}_address $prop{'printaddress'}\n") ;
    push (@definelist, "define csr_${name}_width $flopsize\n") ;
    push (@definelist, "define csr_${name}_range $submax{$reg}:$submin{$reg}\n") ;
    if ($prop{'shadowfield'}) { next } ;

    if ( ! $prop{'internfield'}) { 
	push (@iolist, " ,$name\n") ;
    }
    if ($prop{'cntrfield'} && ! $prop{'internfield'}) {
	push (@inplist, "input \t\t $name;\n") ;
	push (@wirelist, "wire \t\t $name;\n") ;
	push (@senslist, $name) ;
    }
    elsif (($prop{'rofield'} || $prop{'stickyfield'} || $prop{'sticky0field'}) && ! $prop{'internfield'}) {
	push (@inplist, "input $width $name;\n") ;
	push (@wirelist, "wire $width $name;\n") ;
	push (@senslist, $name) ;
    }
    elsif ( ! $prop{'internfield'}) {
	push (@outlist, "output $width $name;\n") ;
	push (@wirelist, "wire $width $name;\n") ;
	push (@reglist, "reg $width $name, ${name}_D;\n") ;
	if ($pulseAckfield) { push (@inplist, "input \t\t ${name}_ack;\n") ; }
    }
    elsif ( ! ($prop{'rofield'} || $prop{'stickyfield'} || $prop{'sticky0field'} || $prop{'cntrfield'})) {
	push (@reglist, "reg $width $name, ${name}_D;\n") ;
    }

    if ($prop{'stickyfield'} || $prop{'sticky0field'}) {
	$name = "${name}S" ;
	push (@reglist, "reg $width ${name}, ${name}_D;\n");
    }
    if ($prop{'cntrfield'}) {
	$name = "${name}_cntr" ;
	push (@reglist, "reg $width ${name}, ${name}_D;\n");
   }

    if ( ! $prop{'rofield'}) {
	$totalflopcount = $totalflopcount + $submax{$reg} - $submin{$reg} ;
	push (@floplist, $name) ;
	if (($prop{'resetvalue'} ne "") && ($prop{'resetvalue'} ne "0")) {
	    if ($prop{'lastLoopValue'} != 0) {
		$LoopCntp1 = $prop{'lastLoopValue'} + 1;
		if ($prop{'resetvalue'} eq "1") { $resetval{$name} = "{${LoopCntp1}{1'b1}}" ; }
		else {$resetvalue{$name} = "{${LoopCntp1}{$prop{'resetvalue'}}}" ; }
	    }
	    else { $resetval{$name} = $prop{'resetvalue'} }
	}
	else { $resetval{$name} = 0 }
	$flopval{$name} = "${name}_D" ;
	if ($prop{'stickyfield'} & ($prop{'w1cname'} eq "")) {
	    push (@defvallist,
		  "  ${name}_D = $name | $nameo ;\n") ;
	}
	elsif ($prop{'stickyfield'}) {
	    push (@defvallist,
		  "  ${name}_D = $name | $nameo | $prop{'w1cname'} ;\n") ;
	}
	elsif ($prop{'sticky0field'}) {
	    push (@defvallist,
		  "  ${name}_D = $name & $nameo ;\n") ;
	    }
	elsif ($prop{'incrsfield'}) {
	    push (@defvallist,
		  "  ${name}_D = (& $name) ? $name : ($name + $nameo) ;\n") ;
	    }
	elsif ($prop{'incrfield'}) {
	    push (@defvallist,
		  "  ${name}_D = $name + $nameo ;\n") ;
	}
	elsif ($prop{'decrsfield'}) {
	    push (@defvallist,
		  "  ${name}_D = (| $name) ? ($name - $nameo) : $name ;\n") ;
	}
	elsif ($prop{'decrfield'}) {
	    push (@defvallist,
		      "  ${name}_D = $name - $nameo ;\n") ;
	}
	elsif ($prop{'pulseAckfield'}) {
	    push (@defvallist,
		  "  ${name}_D = ${name}_ack ? 0 : $name ;\n") ;
	}
	elsif ($prop{'pulsefield'}) {
	    push (@defvallist,
		  "  ${name}_D = 0 ;\n") ;
	}
	else { push (@defvallist, "  ${name}_D = $name ;\n") }
    }
}

# Now write out the verilog code

my $oFile = "${blockname}.v" ;
open (OUT, ">$oFile") || die "cannot open verilog output file: '$oFile'" ;
print OUT "// Created by csrGen.pl, http://asics.chuckbenz.com \n//$version\n" ;
print OUT @opencomment ;

if ($automatics) {
    print OUT "\n\nmodule $blockname (/*AUTOARG*/);\n\n" ;
}
else {
    print OUT "\n\nmodule $blockname ($clockname, $resetlname \n" ;
    print OUT @iolist, " ) ;\n\n\n" ;
}

print OUT "input $clockname, $resetlname ;\n" ;
print OUT @inplist ;
print OUT @outlist ;

# if ($automatics) {
#    print OUT "\n/*AUTOREG*/\n/*AUTOWIRE*/\n" ;
#}
#else {
#   print OUT @reglist ;
#}
print OUT @reglist ;

while (defined ($_ = shift (@veriloglist))) {
    if (/^\s*%loop/i) {
	@line = split ;
	if (@line > 2) {
	    $loopstart = $line[1] ;
	    $loopend = $line[2] ;
	}
	else {
	    $loopstart = 0 ;
	    $loopend = $line[1] - 1 ;
	}
	@looplist = "" ;
	while (defined ($_ = shift (@veriloglist))) {
	    if ((/^\s*%loopend/i) || (/^\s*%endloop/i)) { last ; }
	    else { push (@looplist, $_) ; }
	}
	for ($i=$loopstart;$i<=$loopend;$i++) {
	    foreach (@looplist) {
		($text = $_) =~ s/%/$i/g ;
		print OUT $text ;
	    }
	}
    }
    else { print OUT $_ } 
}

if ($v2k) {
    print OUT "\nalways @ (*" ;
}
elsif($automatics) {
    print OUT "\nalways @ (/*AUTOSENSE*/" ;
}
else {
    print OUT "\n\nalways @ (" ;
    $first = 0 ;
    foreach (@senslist, @floplist) {
        if ($first > 0) { print OUT "or $_\n" }
        else { print OUT "$_\n" ; $first = 1 }
    }
}

print OUT ") begin\n" ;
print OUT @defvallist ;
while (defined ($_ = shift (@comblogiclist))) {
    
    if (/^\s*%readcase/i) {
        print OUT @readlist
    }
    elsif (/^\s*%writecase/i) {
        print OUT @writelist
    }
    elsif (/^\s*%intrlogic/i) {
        # prepare the interrupt vector now - assume that width of the
        # mask is the vector width.
        @intrvectlist = "  up_intr_vector = {\n" ;
        for ($i = $intrvectwidth ; $i >= 0 ; $i--) {
            if (defined $intrvectlist{$i}) {
                push (@intrvectlist, "\t\t", $intrvectlist{$i}, "S") ;
            }
            else { push (@intrvectlist, "\t\t1'b0") }
            if ($i != 0) { push (@intrvectlist, ",\n") }
        }
        print OUT @intrvectlist, " } ;\n" ;
#       print OUT @intrlist ;
        print OUT "  interrupt = ((up_intr_vector & $interruptmask) != 0) ;\n"
    }
    elsif (/^\s*%loop/i) {
	@line = split ;
	if (@line > 2) {
	    $loopstart = $line[1] ;
	    $loopend = $line[2] ;
	}
	else {
	    $loopstart = 0 ;
	    $loopend = $line[1] - 1 ;
	}
	@looplist = "" ;
	while (defined ($_ = shift (@comblogiclist))) {
	    if ((/^\s*%loopend/i) || (/^\s*%endloop/i)) { last ; }
	    else { push (@looplist, $_) ; }
	}
	for ($i=$loopstart;$i<=$loopend;$i++) {
	    foreach (@looplist) {
		($text = $_) =~ s/%/$i/g ;
		print OUT $text ;
	    }
	}
    }
    else { print OUT $_ }
}
print OUT "end\n\n" ;
print OUT "always @ (posedge $clockname or negedge $resetlname)\n" ;
print OUT "  if ( ! $resetlname) begin\n" ;
foreach (@floplist) { printf (OUT "    $_ <= %s ;\n", $resetval{$_}) }
print OUT "  end\n  else begin\n" ;
foreach (@floplist) { printf (OUT "    $_ <= %s ;\n", $flopval{$_}) }
print OUT "  end\n" ;
print OUT "endmodule\n" ;
#print OUT "// $interruptmask\n" ;
close (OUT) ;

open (INST, ">${blockname}.inst") || die "cannot open instfile" ;
print INST " $blockname ULP (.$clockname($clockname)\n" ;
print INST "   ,.$resetlname($resetlname)\n" ;
foreach (@iolist) {
    $pin = substr ($_, 2) ;
    chomp ($pin) ;
    print INST "   ,.$pin($pin)\n" ;
}
print INST "   ) ;\n" ;
close (INST) ;

open (WIRE, ">${blockname}.wire") || die "cannot open wirefile" ;
print WIRE @wirelist ;
close (WIRE) ;

open (DEFS, ">${blockname}.defs") || die "cannot open definitions file" ;
print DEFS @definelist ;
close (DEFS) ;

open (HTML, ">${blockname}_doc.html") || die "cannot open html file" ;
print HTML "<html>\n" ;
print HTML "<meta version=\n$version>\n" ;
print HTML @htmllist ;
print HTML "</html>\n" ;


print "total flop count: $totalflopcount\n" ;

if($automatics) {
  system "emacs --batch $oFile -l ~/elisp/verilog-mode.el -f verilog-auto -f save-buffer";
}

DumpHeader();	# dump out a header definition for post-processing

# that's all, folks

sub addoutput {
    push (@iolist, " ,$_[0]\n") ;
    push (@outlist, "output $_[1] $_[0];\n") ;
    push (@wirelist, "wire $_[1] $_[0];\n") ;
}
sub addinput {
    push (@iolist, " ,$_[0]\n") ;
    push (@inplist, "input $_[1] $_[0];\n") ;
    push (@wirelist, "wire $_[1] $_[0];\n") ;
    push (@senslist, "$_[0]") ;
}

sub getaddrflags {
    $roaddr = 0 ;
    $coraddr = 0 ;
    $w1caddr = 0 ;
    $wtask = "" ;
    $rtask = "" ;
    $skipquote = 0 ;
    $commentfield = "" ;
#    $addrname = "" ;
    foreach (@_) {
#	print "dbg: $skipquote '$_' '$commentfield'\n" ;
        if    (($skipquote == 1) && /^\"$/) { $skipquote = 0 }
	elsif (($skipquote == 0) && /^\"$/) { $skipquote = 1 }
        elsif (/^\".*\"$/) { $commentfield = $commentfield . " " . $_ ; next }
        elsif (/^\"/) { $commentfield = $commentfield . " " . $_ ; $skipquote = 1 }
        elsif (($skipquote == 1) && /\"$/) { $commentfield = $commentfield . " " . $_ ; $skipquote = 0 }
        elsif ($skipquote) { $commentfield = $commentfield . " " . $_ ; next }
        elsif (/^ro$/i) { $roaddr = 1 }
        elsif (/^cor$/i) { $coraddr = 1 }
        elsif (/^w1c$/i) { $w1caddr = 1 }
        elsif ($wtask eq "") { $wtask = $_ }
        elsif ($rtask eq "") { $rtask = $_ }
        else { die "unknown addr field: $_, wtask $wtask, rtask $rtask." }
    }
    $commentfield =~ s/\"//g ;
}

sub doaline {
    @line = split ;
    @dimensions = split (/:/, shift (@line)) ;
    $name = $nameo = shift (@line) ;
    if ((defined $bussi) && ($bussi == 0)) { $rawname = $repeatname{$name} }
    else { $rawname = $nameo } ;
#    $rawname = $nameo ;
    (defined $dimensions[1]) || ($dimensions[1] = $dimensions[0]);
    if ($dimensions[1] > $dimensions[0]) {
	@dimensions[0,1] = reverse (@dimensions[0,1]) ; }
    $msbit = $dimensions[0] ;
    $lsbit = $dimensions[1] ;
    $size = $msbit - $lsbit ;
    $flopsize = $size + 1 ;
    getlineflags (@line) ;
    AddFieldsToRegDefs( $Reg , $name ) if ($LoopCnt == 0) ;
    if ($subsetmsb > 0) { $flopsize = $subsetmsb + 1} ;
    if (($subsetrange eq "")  || ($subsetmsb > 0)) {
	push (@definelist, "define csr_${name}_address $printaddress\n") ;
	push (@definelist, "define csr_${name}_width $flopsize\n") ;
	if ($subsetmsb > 0) {
	    push (@definelist, "define csr_${name}_range ${subsetmsb}:0\n") ;
	}
	elsif ($msbit == $lsbit) {
	    push (@definelist, "define csr_${name}_range ${msbit}\n") ;
	}
	else {
	    push (@definelist, "define csr_${name}_range ${msbit}:${lsbit}\n") ;
	}
    }
    if (($subsetmsb != 0) && ($LoopCnt != 0) && ($resetvalue ne "0")) {
	$LoopCntp1 = $LoopCnt + 1 ;
	if ($resetvalue eq "1") { $resetvalue = "{${LoopCntp1}{1'b1}}" ; }
	else { $resetvalue = "{${LoopCntp1}{$resetvalue}}" ; }
    }
    # now the printing...
    if ($shadowfield) {return} ;
    # first, i/o and reg declarations.
    if (($subsetmsb == 0) && ($subsetrange ne "")) {
	$internfield = 1
	}
    if ($subsetmsb > 0) { $size = $subsetmsb } ;
    if ( ! $internfield) { push (@iolist, " ,$name\n") } ;
    if (($dimensions[1] == $dimensions[0]) && ($subsetmsb == 0)) {
	$width = "\t\t" }
    else { $width = "\t[$size:0]\t" } ;
    if ($cntrfield && ! $internfield) {
	push (@inplist, "input \t\t $name;\n") ;
	push (@wirelist, "wire \t\t $name;\n") ;
	push (@senslist, $name) ;
    }
    elsif (($rofield || $stickyfield || $sticky0field) && ! $internfield) {
	push (@inplist, "input $width $name;\n") ;
	push (@wirelist, "wire $width $name;\n") ;
	push (@senslist, $name) ;
    }
    elsif ( ! $internfield) {
	push (@outlist, "output $width $name;\n") ;
	push (@wirelist, "wire $width $name;\n") ;
	push (@reglist, "reg $width $name, ${name}_D;\n") ;
	if ($pulseAckfield) { push (@inplist, "input \t\t ${name}_ack;\n") ; }
    }
    elsif ((($subsetrange eq "")  || ($subsetmsb > 0)) &&
	   ! ($rofield || $stickyfield || $sticky0field || $cntrfield) &&
	   ! defined $flopval{$name}) {
	push (@reglist, "reg $width $name, ${name}_D;\n") ;
    }
    if ($interruptmask eq "1") {
	$interruptmask = $name ;
	push (@reglist, "reg $width up_intr_vector ;\n") ;
	$intrvectwidth = $msbit + 1 ;
    }
    if ($interruptfield) {
	if ($msbit != $lsbit) { die "$name: wide INTR?\n" }
	$intrvectlist{$msbit} = $name ;
#                        push (@intrlist, " if ($name) ${name}S_D = 1 ;\n") ;
    }
    if ($stickyfield || $sticky0field) {
	$name = "${name}S" ;
	if (($subsetrange eq "") || ($subsetmsb > 0)) {
	    push (@reglist, "reg $width ${name}, ${name}_D;\n");
	}
    }
    if ($cntrfield) {
	$name = "${name}_cntr" ;
	if (($subsetrange eq "") || ($subsetmsb > 0)) {
	    push (@reglist, "reg $width ${name}, ${name}_D;\n");
	}
    }
    # then, always CL list
    # next, read case, write cases
    if ($msbit == $lsbit) { $width = "[$msbit]" }
    else { $width = "[$msbit:$lsbit]" }
    if ( ! $wofield) {
	push (@readlist,
	      "  ${readdatabus}$width = $name$subsetrange ;\n") ;
    }
    if ($corfield) { 
	push (@readlist, "  ${name}_D$subsetrange = 0 ;\n") ;
    }
    elsif ($dorsfield) { 
	push (@readlist, "  ${name}_D$subsetrange = ($name == 0) ? 0 : ($name - 1) ;\n") ;
    }
    elsif ($dorfield) { 
	push (@readlist, "  ${name}_D$subsetrange = $name - 1 ;\n") ;
    }
    elsif ($iorsfield) { 
	push (@readlist, "  ${name}_D$subsetrange = (&$name) ? $name : ($name + 1) ;\n") ;
    }
    elsif ($iorfield) { 
	push (@readlist, "  ${name}_D$subsetrange = $name + 1 ;\n") ;
    }
    if ( ! $rofield) {
	if ($w1cfield & $stickyfield) {
	    push (@writelist, "  ${name}_D$subsetrange = ",
		  "(${name}_D$subsetrange & ~${writedata}$width)",
		  " | $nameo$subsetrange ;\n");
	}
	elsif ($w1cfield) {
	    push (@writelist, "  ${name}_D$subsetrange = ",
		  "(${name}_D$subsetrange & ~${writedata}$width)",
		  " ;\n");
	}
	elsif ($w1sfield & $sticky0field) {
	    push (@writelist, "  ${name}_D$subsetrange = ",
		  "(${name}_D$subsetrange | ${writedata}$width)",
		  " & $nameo$subsetrange ;\n");
	}
	elsif ($w1sfield) {
	    push (@writelist, "  ${name}_D$subsetrange = ",
		  "(${name}_D$subsetrange | ${writedata}$width)",
		  " ;\n");
	}
# ambiguous point here - should COR/SOR fields be writeable ? maybe a global switch ?
#	elsif ( ! $corfield && ! $sorfield) {
	else {
	    push (@writelist, "  ${name}_D$subsetrange = ",
		  "${writedata}$width ;\n");
	}
	# and the flops.
	if (($subsetmsb > 0) || ($subsetrange eq "")) {
	    $totalflopcount = $totalflopcount + $flopsize ;
	    push (@floplist, $name) ;
	    if ($resetvalue ne "") {
		$resetval{$name} = $resetvalue
		}
	    else  { $resetval{$name} = 0 }
	    $flopval{$name} = "${name}_D" ;
	    # this below needs to be cleaned up - cor is broke,
	    # and sticky is not same as w1c...
	    if ($stickyfield & ($w1cname eq "")) {
		push (@defvallist,
		      "  ${name}_D = $name | $nameo ;\n") ;
	    }
	    elsif ($stickyfield) {
		push (@defvallist,
		      "  ${name}_D = $name | $nameo | $w1cname ;\n") ;
	    }
	    elsif ($sticky0field) {
		push (@defvallist,
		      "  ${name}_D = $name & $nameo ;\n") ;
	    }
	    elsif ($incrsfield) {
		push (@defvallist,
		      "  ${name}_D = (& $name) ? $name : ($name + $nameo) ;\n") ;
	    }
	    elsif ($incrfield) {
		push (@defvallist,
		      "  ${name}_D = $name + $nameo ;\n") ;
	    }
	    elsif ($decrsfield) {
		push (@defvallist,
		      "  ${name}_D = (| $name) ? ($name - $nameo) : $name ;\n") ;
	    }
	    elsif ($decrfield) {
		push (@defvallist,
		      "  ${name}_D = $name - $nameo ;\n") ;
	    }
	    elsif ($pulseAckfield) {
		push (@defvallist,
		      "  ${name}_D = ${name}_ack ? 0 : $name ;\n") ;
	    }
	    elsif ($pulsefield) {
		push (@defvallist,
		      "  ${name}_D = 0 ;\n") ;
	    }
	    else { push (@defvallist, "  ${name}_D = $name ;\n") }
	}
    }

    if ( ( ! defined $bussi) || ($bussi == 0)) {
	push (@htmllist, "<tr><td width=10% align=right>") ;
	if ($msbit == $lsbit) { push (@htmllist, "$msbit") }
	else { push (@htmllist, "$msbit:$lsbit") }
	push (@htmllist, "</td>\n<td width=15% align=center>") ;
	if ($w1cfield || $userw1cfield) {push (@htmllist, "RW1C")}
	elsif ($w1sfield) {push (@htmllist, "RW1S")}
	elsif ($corfield) {push (@htmllist, "COR")}
	elsif ($sorfield) {push (@htmllist, "SOR")}
	elsif ($dorsfield || $dorfield) {push (@htmllist, "DOR")}
	elsif ($iorsfield || $iorfield) {push (@htmllist, "IOR")}
	elsif ($pulsefield || $pulseAckfield) {push (@htmllist, "Pulse")}
	elsif ($rofield) {push (@htmllist, "RO")}
	elsif ($wofield) {push (@htmllist, "WO")}
	else {push (@htmllist, "R/W")} ;
	push (@htmllist, "</td>\n<td width=10% align=center>") ;
	push (@htmllist, $resetvalue);
	push (@htmllist, "</td>\n<td width=65%>");
	if ($commentfield eq "") { push (@htmllist, $rawname, "</td></tr>\n") }
	else {push (@htmllist, $commentfield, "</td></tr>\n") }
    }
#               print "$name $dimensions[0] : $dimensions[1]\n" ;
#               (defined $maindata{$intaddr}{$dimensions[0]}[0])
#                   die "field collision, addr $intaddr, msb $dimensions[0]" ;
#                push(@line, @dimensions[0,1]) ;
#                @maindata{$intaddr}{$msbit} = @line;
}

sub doalist {
    $hexaddress = sprintf ( "0x%0x", $intaddr * $addressmultiple ) ;
    $printaddress = $intaddr * $addressmultiple ;
    push (@readlist, "$intaddr: begin // $hexaddress\n") ;
    push (@writelist, "$intaddr: begin // $hexaddress\n") ;
    if ($readmuxdivider == 0) { $readdatabus = $readdata }
    else { 
	$mid = int($intaddr / $readmuxdivider) ;
	$readdatabus = "${readdata}$mid" ;
    }

    foreach (@scratcha) {
	doaline ($_) ;
    }
    if (($rtask ne "") && ($rtask ne "-")) {
	push (@readlist, "$rtask ;\n")
	}
    if (($wtask ne "") && ($wtask ne "-")) {
	push (@writelist, "$wtask ;\n")
	}
    push (@readlist, "end\n") ;
    push (@writelist, "end\n") ;

}

sub doaddrepeat {
    local ($bussi) ;
    $intaddr = $startaddr ;

    for ($i = 0 ; $i < $repeatcount ;  $i = $i + 1) {
	$intaddr = $startaddr + ($i * $increment) ;
	$bussi = $i ;
	@scratcha = () ;
	foreach (@repeatlist) {
	    @line = split ;
	    $range = shift (@line) ;
	    $name = $nameo = shift (@line) ;
	    $buss = 0 ;
	    @rest = () ;
	    while (defined ($x = shift (@line))) {
##		print "$i $_;\n" ;
		if ($x eq "bussintern") { 
		    $buss = 1 ; push (@rest, "intern") ;}
		elsif ($x eq "buss") { $buss = 1 ; }
		elsif ($x eq "robussintern") {
		    $buss = 1 ; push (@rest, "intern ro") ;}
		elsif ($x eq "robuss") { 
		    $buss = 1 ; push (@rest, "ro") ;}
		else { push (@rest, $x) ; }
	    } ;
	    if ($buss == 0) { 
		$name = "${name}$bussi" ; 
		$repeatname{$name} = "${nameo}<b><i>N</b></i>" ;
	    }
	    # don't need SUBM anymore, but it's known to be safe.
	    elsif ($i < ($repeatcount - 1)) {
		$repeatname{$name} = "${name}<b><i>[N]</b></i>" ;
		push (@rest, "SUB $bussi") ; 
	    }
	    else {
		$repeatname{$name} = "${name}<b><i>[N]</b></i>" ;
		push (@rest, "SUBM $bussi") ; 
	    }
	    push (@scratcha, "$range $name @rest\n") ;
##	    print ("me: $range $name @rest\n") ;
	}
#	print "%A $intaddr\n" ;
#	foreach (@scratcha) { print $_ ; }
	$LoopCnt = $i ;
	doalist ;
    }
    $LoopCnt = 0 ;
}

sub getlineflags {
    $rofield = $roaddr ;
    $corfield = $coraddr ;
    $w1cfield = $w1caddr ;
    $userw1cfield = 0 ;
    $stickyfield = 0 ;
    $internfield = 0 ;
    if (defined $flopval{$name}) { $internfield = 1 } ;
    $interruptfield = 0 ;
    $resetvalue = 0 ;
    $subsetrange = "" ;
    $subsetmsb = 0 ;
    $subsetlsb = 0 ;
    $shadowfield = 0 ;
    $sorfield = 0 ;
    $wofield = 0 ;
    $w1sfield = 0 ;
    $dorsfield = 0 ;
    $dorfield = 0 ;
    $iorsfield = 0 ;
    $iorfield = 0 ;
    $sticky0field = 0 ;
    $incrsfield = 0 ;
    $incrfield = 0 ;
    $decrsfield = 0 ;
    $decrfield = 0 ;
    $cntrfield = 0 ;
    $pulsefield = 0 ;
    $pulseAckfield = 0 ;
    $skipquote = 0 ;
    $commentfield = "" ;
    foreach (@_) {
        if ($getsubm) {
            $subsetrange = "[$_]" ;
            ($subsetmsb, $subsetlsb, @temp) = split (/:/, $_) ;
            $getsubm = $getsub = 0 ;
	    $submax{$name} = $subsetmsb ;
            next ;
            }
        if ($getsub) {
            $subsetrange = "[$_]" ;
	    @temp = split (/:/, $_) ;
	    $tempmsb = $temp[0] ;
	    if (@temp == 1) { $templsb = $tempmsb }
	    else { $templsb = $temp[1] } ;
	    if ( ( ! defined $submax{$name}) || ($tempmsb > $submax{$name})) {
		$submax{$name} = $tempmsb ;
	    }
	    if ( ( ! defined $submin{$name}) || ($templsb < $submin{$name})) {
		$submin{$name} = $templsb ;
	    }
            $getsub = 0 ;
            next ;
        }
        if    (($skipquote == 1) && /^\"$/) { $skipquote = 0 }
	elsif (($skipquote == 0) && /^\"$/) { $skipquote = 1 }
        elsif (/^\".*\"$/) { $commentfield = $commentfield . " " . $_ ; next }
        elsif (/^\"/) { $commentfield = $commentfield . " " . $_ ; $skipquote = 1 }
        elsif (($skipquote == 1) && /\"$/) { $commentfield = $commentfield . " " . $_ ; $skipquote = 0 }
        elsif ($skipquote) { $commentfield = $commentfield . " " . $_ ; next }
        elsif (/^ro$/i) { $rofield = 1 } 
        elsif (/^cor$/i) { $corfield = 1 } 
        elsif (/^sor$/i) { $sorfield = 1 } 
        elsif (/^dors$/i) { $dorsfield = 1 } 
        elsif (/^iors$/i) { $iorsfield = 1 } 
        elsif (/^dor$/i) { $dorfield = 1 } 
        elsif (/^ior$/i) { $iorfield = 1 } 
	elsif (/^incrs$/i) { $incrsfield = 1 ; $cntrfield = 1 }
	elsif (/^incr$/i) { $incrfield = 1 ; $cntrfield = 1 }
	elsif (/^decrs$/i) { $decrsfield = 1 ; $cntrfield = 1 }
	elsif (/^decr$/i) { $decrfield = 1 ; $cntrfield = 1 }
	elsif (/^pulsea$/i) { $pulseAckfield = 0 }
	elsif (/^pulse$/i) { $pulsefield = 0 ; $wofield = 1 }
        elsif (/^w1c$/i) { $w1cfield = 1 } 
        elsif (/^w1s$/i) { $w1sfield = 1 } 
        elsif (/^wo$/i) { $wofield = 1 } 
	elsif (/^st0$/i) {$sticky0field = 1 } 
        elsif (/^st$/i) { $stickyfield = 1 } 
        elsif (/^intern$/i) { $internfield = 1 } 
        elsif (/^intrmask$/i) { $interruptmask = 1 } 
        elsif (/^interrupt$|^intr$/i) {
            $interruptfield = 1 ;
            $stickyfield = 1 ;
        } 
        elsif (/^0x[0-9].*/) { $resetvalue = hex($_) ; } 
        elsif (/^[0-9].*/) { $resetvalue = $_ ; } 
        elsif (/^subm$/i) { $getsubm = 1 } 
        elsif (/^sub$/i) { $getsub = 1 } 
	elsif (/^userw1c$/i) {$userw1cfield = 1 }
	elsif (/^shadow$/i) { $shadowfield = 1 } 
        else { die "unknown csr field: $_, line: @_." } ;
# want to check for conflicting parameters now
	if ($rofield && ! $stickyfield && ($corfield || $w1cfield)) {
	    printf ("ERR: illegal combination of parameters on %s register\n", $name) ;
	}
    }
    $commentfield =~ s/\"//g ;
    $submdone = 0 ;
    if (defined @{$regProps{$name}}) { 
	%prop = @{$regProps{$name}} ;
	$submdone = $prop{'submdone'} ;
    }
    if ($subsetmsb > 0) { $submdone = 1 }
    @{$regProps{$name}} = 
	('addr', $address, 'msbit', $msbit, 'lsbit', $lsbit, 'size', $size, 
	 'rofield', $rofield, 'corfield', $corfield, 'w1cfield', $w1cfield,
	 'userw1cfield', $userw1cfield, 'stickyfield', $stickyfield,
	 'internfield', $internfield, 'interruptfield', $interruptfield,
	 'resetvalue', $resetvalue, 'subsetrange', $subsetrange,
	 'subsetmsb', $subsetmsb,
	 'subsetlsb', $subsetlsb,
	 'submdone', $submdone,
	 'shadowfield', $shadowfield,
	 'sorfield', $sorfield,
	 'wofield', $wofield,
	 'w1sfield', $w1sfield,
	 'dorsfield', $dorsfield,
	 'dorfield', $dorfield,
	 'iorsfield', $iorsfield,
	 'iorfield', $iorfield,
	 'sticky0field', $sticky0field,
	 'incrsfield', $incrsfield,
	 'incrfield', $incrfield,
	 'decrsfield', $decrsfield,
	 'decrfield', $decrfield,
	 'cntrfield', $cntrfield,
	 'pulsefield', $pulsefield,
	 'pulseAckfield', $pulseAckfield,
	 'w1cname', $w1cname, 'lastLoopValue', $LoopCnt,
	 'printaddress', $printaddress) ;
# uncomment the below line to make subm treated same as inferring submsb from all sub declarations.
# (I tested the inferred subm ability by doing this)
#    $subsetmsb = 0 ;
}

sub donamewidth {
    $name = $line[1] ;
    if ((@line > 2) && ($line[2] > 1)) {
	$width = $line[2] - 1  ;
	$width = "\t[$width:0]\t" ;
    }
    else {$width = "\t\t" }
}

sub donamewidthflop {
    $name = $line[1] ;
    if ((@line > 2) && ($line[2] > 1)) {
	$width = $line[2] - 1  ;
	$totalflopcount = $totalflopcount + $width ;
	$width = "\t[$width:0]\t" ;
    }
    else {
	$width = "\t\t" ;
	$totalflopcount = $totalflopcount + 1 ;
    }
}

sub donamewidthfloprepeat {
    $name = $line[1] ;
    if ((@line > 2) && ($line[2] > 1)) {
        $width = $line[2] - 1  ;
        $totalflopcount = $totalflopcount + $width * $repeatcount ;
        $width = "\t[$width:0]\t" ;
    }
    else {
        $width = "\t\t" ;
        $totalflopcount = $totalflopcount + $repeatcount ;
    }
}
# -----------------------------------------------------------------------------------
# Format of %RegDefs hash: each entry is the mnemonic of a register address,
# which in this case is the address itself because we don't support mnemonics.
#   %RegDefs = { $Reg  => { 'RESERVED_FOR_csrGen' => { 'ADDRESS'   => $address
#                         			     , 'INCREMENT' => $increment
#                                                    , 'REPEAT'    => $repeatcount
#			  },
#                           $Field  => {  'w1c' 	=> 0,
#                                         'cor' 	=> 0,
#                                         'width' 	=> '32',
#                                         'internal' 	=> 1,
#                                         'reset' 	=> 0,
#                                         'sticky' 	=> 0,
#                                         'lsb' 	=> '0',
#                                         'subsetlsb' 	=> 0,
#                                         'subsetmsb' 	=> 0,
#                                         'msb' 	=> '31',
#                                         'ro' 		=> 0,
#                                         'interrupt' 	=> 0
#                                      }
#                          }
#		} ;
#
# -----------------------------------------------------------------------------------
  sub AddRegToRegDefs {
   my $Reg   = shift @_ ;
   my $Attr  = shift @_ ;
   my $Value = shift @_ ;
# -----------------------------------------------------------------------------------
# This routine adds the current (global) variables to the hash we dump out in 
# DumpDefs.  This may be called out in 1 of two ways:
#	%A		simple address definition:  
#	%AREPEAT	repeating addr definition:
# -----------------------------------------------------------------------------------
#  print "warning: register already defined: $Reg\n"		
#		if (exists($RegDefs{$Reg}) && ($Attr eq 'ADDRESS') && 
#		    (($subsetrange eq "") || ($subsetmsb != 0)));

  die "[internal error] unknown Reserved Field abbreviation: $Attr"
  		if (!exists($ReservedFields{$Attr}));

  die "[internal error] duplicate reserved field: $Attr for Reg $Reg"
		if (exists($RegDefs{$Reg}{$ReservedField}{$Attr}));	

  if (($subsetrange eq "") || ($subsetmsb != 0)) {
      $RegDefs{$Reg}{$ReservedField}{$Attr} = $Value ;
  } 		
}
# -----------------------------------------------------------------------------------
  sub AddFieldsToRegDefs {
   my $Reg   = shift @_ ;
   my $Field = shift @_ ;
# -----------------------------------------------------------------------------------
# This sub just adds fields to a register that should already be defined.
# -----------------------------------------------------------------------------------
  die "[internal error] undefined Reg!" if (!defined($Reg));

  print "warning: field already defined: $Field\n" 	  
	     if (exists($RegDefs{$Reg}{$Field})) ;	  

  $RegDefs{$Reg}{$Field}{'msb'        } = $msbit 	; 
  $RegDefs{$Reg}{$Field}{'lsb'        } = $lsbit 	; 
  $RegDefs{$Reg}{$Field}{'width'      } = $flopsize  	; 

  # these are from getlineflags() -----------------------  
  $RegDefs{$Reg}{$Field}{'ro'         } = $rofield && ! $userw1cfield ; 
  $RegDefs{$Reg}{$Field}{'cor'        } = $corfield	; 
  $RegDefs{$Reg}{$Field}{'w1c'        } = $w1cfield || $userw1cfield ; 
  $RegDefs{$Reg}{$Field}{'sticky'     } = $stickyfield 	; 
  $RegDefs{$Reg}{$Field}{'internal'   } = $internfield 	; 
  $RegDefs{$Reg}{$Field}{'interrupt'  } = $interruptfield; 
  $RegDefs{$Reg}{$Field}{'reset'      } = $resetvalue 	; 
  $RegDefs{$Reg}{$Field}{'subsetlsb'  } = $subsetlsb 	; 
  $RegDefs{$Reg}{$Field}{'subsetmsb'  } = $subsetmsb 	; 
  $RegDefs{$Reg}{$Field}{'shadowfield'} = $shadowfield  ;
}

# -----------------------------------------------------------------------------------
# This sub uses the Data::Dumper routine to dump out the contents of the register
# definitions.  A customized post-processing program can reformat this for whatever
# language is appropriate.  To get the data back into a perl program, you 'require'
# the file: 
# -----------------------------------------------------------------------------------
  use Data::Dumper ;
  sub DumpHeader {
# -----------------------------------------------------------------------------------
  my $oFile = "${blockname}_data.pm" ; # you 'eval' this in perl to get the data

  open (OUT, ">$oFile") || die "cannot open dump output file: $oFile\n" ;
  print OUT Dumper(\%RegDefs);
  close(OUT);
}

