#!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, "
\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, " | Addresses: ", $Regaddr) ;
if ($commentfield eq "") { push (@htmllist, " |
\n") }
else {push (@htmllist, " - ", $commentfield, "\n") }
@repeatlist = () ;
while (<$tmpfp>) {
if (/^[0-9]/) { push (@repeatlist, $_) ; }
elsif (/^%/) {
doaddrepeat() ;
push (@htmllist, "
\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, "\n");
$Regaddr = sprintf( "0x%04x", $intaddr ) ;
push (@htmllist, " | Address: ", $Regaddr) ;
if ($commentfield eq "") { push (@htmllist, " |
\n") }
else {push (@htmllist, " - ", $commentfield, "\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, "
\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 "\n" ;
print HTML "\n" ;
print HTML @htmllist ;
print 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, "| ") ;
if ($msbit == $lsbit) { push (@htmllist, "$msbit") }
else { push (@htmllist, "$msbit:$lsbit") }
push (@htmllist, " | \n") ;
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, " | \n") ;
push (@htmllist, $resetvalue);
push (@htmllist, " | \n");
if ($commentfield eq "") { push (@htmllist, $rawname, " |
\n") }
else {push (@htmllist, $commentfield, "\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}N" ;
}
# don't need SUBM anymore, but it's known to be safe.
elsif ($i < ($repeatcount - 1)) {
$repeatname{$name} = "${name}[N]" ;
push (@rest, "SUB $bussi") ;
}
else {
$repeatname{$name} = "${name}[N]" ;
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);
}