#!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, " \n") } else {push (@htmllist, " - ", $commentfield, "\n") } @repeatlist = () ; while (<$tmpfp>) { if (/^[0-9]/) { push (@repeatlist, $_) ; } elsif (/^%/) { doaddrepeat() ; push (@htmllist, "
Addresses: ", $Regaddr) ; if ($commentfield eq "") { 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, " \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, "
Address: ", $Regaddr) ; if ($commentfield eq "") { 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); }