URL
https://opencores.org/ocsvn/test_project/test_project/trunk
Subversion Repositories test_project
[/] [test_project/] [trunk/] [rtl/] [verilog/] [components/] [wb_sdram_ctrl/] [fizzim.pl] - Rev 37
Go to most recent revision | Compare with Previous | Blame | View Log
#!/usr/local/bin/perl # This file is part of fizzim # Copyright (C) 2007 Zimmer Design Services # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. # # Although not specifically required by GPL, if you improve/modify this # software, please make it available to other users either by posting # it yourself or sending it back to ZDS (paulzimmer@zimmerdesignservices.com) # Consider this an ethical obligation rather than a legal one. # # $Id: fizzim.pl,v 2.0 2008/09/18 22:07:19 pzimmer Exp pzimmer $ # Issues: # should dp pre-case values look at implied_loopback and/or default...x? $| = 1; # force immediate output # Make sure calling location is in INC path use File::Basename; $my_dir = &dirname($0); $me = &basename($0); unshift(@INC,"$my_dir"); # Figure out revision info # The reference to Revision below will get RCS to input the revision $scriptversion = '$Revision: 2.0 $'; # Extract the rev number $scriptversion =~ s/^\$Revision: //; $scriptversion =~ s/ \$//; # Scan for -help and -version for early exit. If found, call parser if (join(" ",@ARGV) =~ /-h/) { &process_options; exit; # redundant - will exit anyway } if (join(" ",@ARGV) =~ /-vers/) { &process_options; exit; # redundant - will exit anyway } elsif (join(" ",@ARGV) =~ /-v(\s|$)/) { print STDERR "Note that -v is ambiguous. If you want version, you must use at least -vers \n"; } # Scan for -debug be_cmd before calling process_options $debug_sections{"be_cmd"} = (join(" ",@ARGV) =~ /-d\S*\s+be_cmd/); # Scan for -debug parse_input before calling parser $debug_sections{"parse_input"} = (join(" ",@ARGV) =~ /-d\S*\s+parse_input/); # Need to fetch the be_cmd from the file before parsing inputs. # Perl wants to treat the actual command-line args as file names, so # save them away and clear @ARGV. @orig_argv = @ARGV; @ARGV = (); # Parse the input. Do this before option processing so that we can get # be_cmd. &set_myattributes; # sets the myattributes hash to know what to parse &parse_input; # Process command-line options # Re-build @ARGV using the options from be_cmd FIRST (so actual command-line # options will have priority) ($be_options = $globals{machine}{be_cmd}{value}) =~ s/^[^-]*//; @ARGV = (split(/\s+/,$be_options),@orig_argv); &process_options; # Output is a bit messy. To allow for -terse, output is held in a buffer # (pbuf) and printed at the end. Warnings and "assertions" are held in their # own arrays, with indexes to match the line they follow in pbuf. # "assertions" are comment lines, other than warnings, to be added to the # output. # Init printbuffer $pbuf[0] = ""; # Rename hashes %states = %state; undef %state; %transitions = %transition; undef %transition; # Massage output data structures. foreach $output (sort keys %{ $globals{outputs} }) { if ($globals{outputs}{$output}{type} eq "comb") { $comb_outputs{$output}{"default_value"} = $globals{outputs}{$output}{value}; } elsif ($globals{outputs}{$output}{type} eq "reg") { if ($encoding eq "onehot") { # For onehot, all reg's are effectively regdp's $regdp_outputs{$output}{"default_value"} = $globals{outputs}{$output}{value}; } else { $reg_outputs{$output}{"default_value"} = &convert2bin($globals{outputs}{$output}{value}); } } elsif ($globals{outputs}{$output}{type} eq "regdp") { $regdp_outputs{$output}{"default_value"} = $globals{outputs}{$output}{value}; } else { &error($indent,"No type found for output $output"); } } # Encodings: # HEROS: # Highly Encoded with Registered Outputs as Statebits # All registered outputs will be encoded as state bits. Additional # statebits will be added as needed to meet other requirements # (mininum encoding, graycode transitions). # # onehot: # One-hot encoding per Steve Golson. All reg'd outputs are effectively # regdp's. # # Encoding cannot easily be an attribute on the fsm, because we need to know # the encoding before we parse... # Create module statement # Because verilog doesn't allow the list to end with a "," we # create the line first (in $line) but don't print it. It is printed # only when the NEXT sign input/output is handled (since we then # know we can leave the "," at the end. # When both loops (input and output) are finished, we change the "," # to a ");" and print the last line. ####################### # State assignment ####################### $section = "assign_states"; # for debug # allstates is an array of all the states in alpha order, but with the # reset state moved to the front (FIRST) if (exists $globals{machine}{reset_state}{value}) { $reset_state = $globals{machine}{reset_state}{value}; push(@allstates,$reset_state); } else { # If no reset, warn user &warning($indent,"No reset specified"); } foreach $state (sort keys %states) { unless ($state eq $reset_state) { push(@allstates,$state); } } &debug("allstates is @allstates\n",0,"assign_states"); if ($encoding eq "heros") { # Massage the data structures before calling assign_states # Look at all registered outputs and assign them to state bits $statebit = 0; # init statebit pointer # Loop through all reg'd outputs foreach $output (sort keys %reg_outputs) { # Determine upper and lower parts of [upper:lower] if (($upper,$lower) = ($output =~ /\[(\d+):(\d+)\]/)) { $reg_outputs{$output}{"range"} = "$upper:$lower"; } else { # If no range match, this must be single bit $upper = 0; $lower = 0; } # Set the lower (where the range will start to the current pointer) $statebit_lower = $statebit; # Bump the pointer up until we reach $upper for (my $bit = $lower; $bit < $upper; $bit++) { $statebit++; } if ($statebit == $statebit_lower) { # single bit $reg_outputs{$output}{"statebit_range"} = $statebit; &debug("Assigned $output to statebits $reg_outputs{$output}{statebit_range}",0,"$section"); } else { $reg_outputs{$output}{"statebit_range"} = "$statebit:$statebit_lower"; &debug("Assigned $output to statebits $reg_outputs{$output}{statebit_range}",0,"$section"); } $reg_outputs{$output}{"statebit_lower"} = $statebit_lower; $reg_outputs{$output}{"statebit_upper"} = $statebit; # Set %fixedbits on all states to show these bits as fixed foreach $state (keys %states) { if ((!exists $states{$state}{attributes}{$output}{value}) || ($states{$state}{attributes}{$output}{value} eq "")) { $value = $reg_outputs{$output}{"default_value"}; &debug("// Warning: No value for Moore bit $output for state $state - using default $value",0,"$section"); } else { $value = &convert2bin($states{$state}{attributes}{$output}{value}); } # Make sure it is a legal binary value and the correct size if ($value !~ /^[01]+$/) { &error($indent,"Value $value of output $output for state $state is not a legal binary number"); } # Checking the bit width make it impossible to use 'h0f (tic without size) if (length($value) != $upper - $lower + 1) { &error($indent,"Value $value of output $output for state $state is not the correct bit width"); } $target_valuebit = 0; for ($target_statebit = $statebit; $target_statebit >= $statebit_lower; $target_statebit--) { $bitvalue = substr($value,$target_valuebit,1); if ($bitvalue eq "") {&error($indent,"Value of output $output in state $state has fewer bits than required\n");} &debug("Setting fixedbit $target_statebit to $bitvalue for output $output in state $state",0,"$section"); $fixedbits{$state}{$target_statebit} = $bitvalue; $target_valuebit++; } } $statebit++; } $regbits = $statebit; # Create graytransitions hash foreach $trans (keys %transitions) { if ($transitions{$trans}{attributes}{graycode}{value} || $transitions{$trans}{attributes}{graytransition}{value}) { &debug("Setting transition $trans as gray",0,"$section"); push( @{ $graytransitions{"$transitions{$trans}{startState}"} },$transitions{$trans}{endState}); } } # Calculate limits of state assignment search $required_bits = &required_bits($#allstates + 1); $minbits = $required_bits; $minbits = $regbits if ($regbits > $minbits); unless ($maxbits) { $maxbits = $required_bits + $regbits; # Bump maxbits in the presence of graycodes $maxbits = $maxbits + 2 if (scalar(keys %graytransitions)); } &debug("minbits is $minbits, maxbits is $maxbits",0,"assign_states"); # Force reset state to be allones or allzeros if requested by setting # fixbits values. if (exists $globals{machine}{reset_state}{value}) { $reset_state = $globals{machine}{reset_state}{value}; if ($globals{machine}{reset_state}{type} eq "allzeros") { for ($bit = 0; $bit <= $maxbits - 1 ; $bit++) { # If there is a comflicting fixedbits setting from registered # outputs, error out. if ((exists $fixedbits{$reset_state}{$bit}) && $fixedbits{$reset_state}{$bit} == 1) { &error($indent,"allzeros constraint for reset state $reset_state is incompatible with output setting"); } $fixedbits{$reset_state}{$bit} = 0; &debug("Setting fixedbit $bit to 0 for reset state $reset_state",0,"$section"); } } elsif ($globals{machine}{reset_state}{type} eq "allones") { for ($bit = 0; $bit <= $maxbits - 1 ; $bit++) { # If there is a comflicting fixedbits setting from registered # outputs, error out. if ((exists $fixedbits{$reset_state}{$bit}) && $fixedbits{$reset_state}{$bit} == 0) { &error($indent,"allones constraint for bit $bit of reset state $reset_state is incompatible with output setting"); } $fixedbits{$reset_state}{$bit} = 1; &debug("Setting fixedbit $bit to 1 for reset state $reset_state",0,"$section"); } } } # Debug output prior to calling &assign_states if ($global_debug || $debug_sections{"$section"}) { foreach $state (@allstates) { for ($statebit = 0; $statebit < $regbits; $statebit++) { if (exists $fixedbits{$state}{$statebit}) { &debug("fixedbits{$state}{$statebit} is $fixedbits{$state}{$statebit}",0,"$section"); } } } &debug("regbits is $regbits",0,"$section"); foreach $trans (keys %graytransitions) { &debug("graytransitions{$trans} is @{ $graytransitions{$trans} }",0,"$section"); } } # Call &assign_states_hero to do the state assignment &assign_states_hero; # Rename variables to standard names $total_statebits = $bits; %state_val2name = %$v2nref; } elsif ($encoding eq "onehot") { # Check for errors # No graycodes foreach $trans (keys %transitions) { if ($transitions{$trans}{attributes}{graycode}{value} || $transitions{$trans}{attributes}{graytransition}{value}) { &error($indent,"Cannot have graycode transitions with onehot encoding"); } } # Reset state must not be allones or allzeros if ($globals{machine}{reset_state}{type} =~ /(allones)|(allzeros)/) { &error($indent,"Cannot have reset_state type of allones or allzeros with onehot encoding. Try changing to \"anyvalue\""); } $total_statebits = scalar(keys %states); $statebit = $total_statebits - 1 ; # init statebit pointer foreach $state (keys %states) { $state_val2name{$statebit} = $state; $statebit--; } $zero_padded_one = sprintf("%s'b%s%s",$total_statebits,"0" x ($total_statebits - 1), "1"); } else { &error($indent,"Unknown encoding $encoding\n"); } $last_statebit = $total_statebits - 1; # Figure out longest state name for formatting purposes foreach $state (@allstates) { $statename_length = length($state) if (length($state) > $statename_length); } # length of "state[IDLE]" $statename_length_onehot = $statename_length + length($statevar) +2; $nextstatename_length_onehot = $statename_length + length($nextstatevar) +2; $level4indent = $indentstring x 4; #eval "\$level4indent = \"$indentstring\" x 4"; ####################### # module open ####################### # Handle top-of-file comments if ($include_at_top_of_file = $globals{machine}{include_at_top_of_file}{value}) { open(FILE,"<$include_at_top_of_file") or &warning($indent,"Could not open file $include_at_top_of_file specified by attribute insert_at_top_of_file"); while (<FILE>) { print; } } &print($indent,"$globals{machine}{insert_at_top_of_file}{value}\n"); # Output version data if ($addversion) { ($sec,$min,$hour,$day, $month, $year) = (localtime)[0..5]; $date = sprintf("%04d:%02d:%02d",$year+1900,$month+1,$day); $time = sprintf("%02d:%02d:%02d",$hour,$min,$sec); &print($indent,"// Created by $me version $scriptversion on $date at $time\n\n"); } # Handle module comments if ($globals{machine}{name}{comment}) { $comment = " // $globals{machine}{name}{comment}"; } else { $comment = ""; } # Print module line &print($indent++,"module $globals{machine}{name}{value} ($comment\n"); &print($indent,"$globals{machine}{insert_in_module_declaration}{value}\n") if $globals{machine}{insert_in_module_declaration}{value}; # outputs foreach $output (sort keys %{ $globals{outputs} }) { # Print previous line if ($line) { &print($indent, "$line"); $line = ""; } # Check for reserved words if (exists $reserved_words{$output}) { &error($indent," Cannot use reserved word \"$output\" as an output. If you are trying to bring out the internal variables, look at the stateout and nextstateout attributes. If you have an actual output of this name, try using the -statevar, -nextstatevar, or -statenamevar switches to rename the internal signal to avoid the conflict. "); } # Handle comments if ($globals{outputs}{$output}{comment}) { $comment = "// $globals{outputs}{$output}{comment}"; } else { $comment = ""; } # Oddly, the comb outputs are reg's (because they will be assigned to # in the comb always block), but reg outputs are wire's (because they # will have continuous assignment to state bits). Regdp (registered # datapath) are reg's. if ($globals{outputs}{$output}{type} eq "comb") { $type = "reg"; } elsif ($globals{outputs}{$output}{type} eq "reg") { if ($encoding eq "onehot") { $type = "reg"; } else { $type = "wire"; } } elsif ($globals{outputs}{$output}{type} eq "regdp") { $type = "reg"; } else { &error($indent,"No type found for output $output"); } # Handle multibit outputs if (($name,$range) = ($output =~ /^([^\[]+)\s*(\[.*\])/)) { $line = sprintf("%-30s %-s\n","output $type $range $name,",$comment); ($upper,$lower) = $range =~ /(\d+):(\d+)/; $widths{$name} = abs($upper - $lower) + 1; #print "$name width is $widths{$name}\n"; #$line = "output $type $range $name,\n"; } else { $line = sprintf("%-30s %-s\n","output $type $output,",$comment); $widths{$output} = 1; #print "$output width is $widths{$output}\n"; #$line = "output $type $output,\n"; } # Remove trailing blanks $line =~ s/\s*\n$/\n/; } # Handle stateout, nextstateout foreach $att ("stateout", "nextstateout") { ($var = $att) =~ s/out//; # turns "stateout" into "state" $var = eval "\$${var}var"; # makes var = $statevar if ($stateout = $globals{"machine"}{$att}{"value"}) { # Check to make sure no bounds given if ($stateout =~ /\[|\]/) { &error($indent,"stateout signal \"$stateout\" should not have bit field - this will be determined automatically"); } # Print previous line if ($line) { &print($indent, "$line"); $line = ""; } if ($stateout eq $var) { $line = "output reg [$last_statebit:0] $stateout,\n"; } else { $line = "output wire [$last_statebit:0] $stateout,\n"; } } } # inputs foreach $input (sort keys %{ $globals{inputs} }) { # Print previous line if ($line) { &print($indent, "$line"); $line = ""; } # Check for reserved words if (exists $reserved_words{$input}) { &error($indent," Cannot use reserved word \"$input\" as an input. If you have an actual input of this name, try using the -statevar, -nextstatevar, or -statenamevar switches to rename the internal signal to avoid the conflict. "); } # Handle comments if ($globals{inputs}{$input}{comment}) { $comment = "// $globals{inputs}{$input}{comment}"; } else { $comment = ""; } # Handle multibit inputs if (($name,$range) = ($input =~ /^([^\[]+)\s*(\[.*\])/)) { $line = sprintf("%-30s %-s\n","input wire $range $name,",$comment); #$line = "input wire $range $name,\n"; } else { $line = sprintf("%-30s %-s\n","input wire $input,",$comment); #$line = "input wire $input,\n"; } # Remove trailing blanks $line =~ s/\s*\n$/\n/; } # print last line and close if ($line =~ s/^(.*),/\1 /) { $line = $line . ");\n"; # Add ); on a new line } else { &error($indent,"No inputs or outputs specified"); } &print($indent, "$line"); &print($indent,"\n"); &print($indent,"$globals{machine}{insert_at_top_of_module}{value}\n"); ####################### # Print out state parameter definition ####################### &print($indent,"\n\n"); &print($indent,"// state bits\n"); # DC gives a warning that not all tool support bounds on parameters, so # don't use them. #&print($indent,"parameter [$last_statebit:0]\n"); &print($indent,"parameter \n"); $line = ""; # Print out state encoding in @allstates order foreach $state (@allstates) { $comment = ""; # Same trick - print previous line to avoid trailing "," if ($line) { &print($indent, "$line"); $line = ""; } # Search through all assigned values looking for the one that matches $state foreach $val (keys %state_val2name) { if ($state_val2name{$val} eq $state) { # If there are output encoded ($regbits is not 0), prepare a comment # that shows the value of each output in this state. if ($regbits) { $val_length = length($val); foreach $regout (sort { $reg_outputs{$a}{statebit_lower} <=> $reg_outputs{$b}{statebit_lower} } keys %reg_outputs) { $lower = $reg_outputs{$regout}{statebit_lower}; $upper = $reg_outputs{$regout}{statebit_upper}; $bitvalue = substr($val,$val_length - $upper - 1,$upper - $lower + 1); $comment = "$regout=${bitvalue} ${comment}"; } # Do extra bits if ($val_length - 1 > $upper) { $bitvalue = substr($val,($val_length - 1) - ($upper + 1),$val_length - $upper - 1); $comment = "extra=${bitvalue} ${comment}"; } $comment = "// $comment"; } # end of encoded output comment code if ($encoding eq "heros") { $line = sprintf("%-${statename_length}s%s","$state"," = ${total_statebits}'b${val}, ${comment}\n"); } elsif ($encoding eq "onehot") { $line = sprintf("%-${statename_length}s%s","$state"," = ${val}, ${comment}\n"); } } } } # Substitute ; for , and print last line. $line =~ s/^(.*),/\1;/; &print($indent, "$line"); ####################### # state, nextstate reg lines ####################### &print($indent, "\n"); # Handle stateout, nextstateout attributes foreach $att ("stateout", "nextstateout") { ($var = $att) =~ s/out//; # turns "stateout" into "state" $var = eval "\$${var}var"; # makes var = $statevar if ($stateout = $globals{"machine"}{$att}{"value"}) { if ($stateout eq $var) { # If stateout is set the same as $statevar, then the reg statement was # done # in the module header. Do nothing. } else { # If stateout is NOT set the same as $statevar, then the module statement # had a "wire" for stateout and we need to create the reg statement # here and an assign of the wire to the reg. &print($indent, "reg [$last_statebit:0] $var;\n"); &print($indent, "assign $stateout = $var;\n"); } } else { # No attribute set - print out reg statement &print($indent, "reg [$last_statebit:0] $var;\n"); } } ####################### # Output combinational block ####################### $section = "comb_block"; &print($indent, "\n"); &print($indent, "// comb always block\n"); &print($indent++, "always @* begin\n"); if ($encoding eq "heros") { # For heros, defaults ahead of case depend on fsm attributes if ($globals{machine}{implied_loopback}{value}) { &print($indent, "$nextstatevar = $statevar; // default to hold value because implied_loopback is set\n"); } elsif ($globals{machine}{default_state_is_x}{value}) { #&print($indent, sprintf("$nextstatevar = ${total_statebits}\'b%s; // default to x because default_state_is_x is set\n","x" x $total_statebits)); &print($indent, sprintf("$nextstatevar = ${total_statebits}\'b%s; // default to x because default_state_is_x is set\n","x")); } else { &warning($indent,"Neither implied_loopback nor default_state_is_x attribute is set on state machine - this could result in latches being inferred"); } } elsif ($encoding eq "onehot") { &print($indent, sprintf("$nextstatevar = ${total_statebits}\'b%s;\n","0" x $total_statebits)); } # Combinational defaults. # To get better synth results "out of the box", use a 0 default if no # default is defined. To get the old "hold value" # behavior, it will be necessary for the user to set the default to # be the variable name (ie, if the variable name is "foo", make the default # value "foo"). foreach $combout (sort keys %comb_outputs) { if (exists $comb_outputs{$combout}{default_value} && ($comb_outputs{$combout}{default_value} ne "") ) { $comb_defaults{$combout} = $comb_outputs{$combout}{default_value}; &print($indent,"$combout = $comb_defaults{$combout}; // default\n"); } else { $comb_defaults{$combout} = "0"; # Use string zero &warning($indent,"Combinational output $combout has no default value - using 0"); &print($indent,"$combout = $comb_defaults{$combout}; // default to zero for better synth results (no default set in .fzm file)\n"); #&print($indent,"$combout = $widths{$combout}'b$comb_defaults{$combout}; // default to zero for better synth results (no default set in .fzm file)\n"); } } # State "case" block if ($encoding eq "heros") { &print($indent++,"case ($statevar)\n"); } elsif ($encoding eq "onehot") { # Cliff says 1'b1 much better... #&print($indent++,"case (1'b1) // synopsys parallel_case full_case\n"); # Figure out the onehot pragma. Defaults to # "synopsys parallel_case full_case", but can be overridden by attribute. if (exists $globals{machine}{onehot_pragma}{value} ) { &warning($indent,"Using override value from attribute onehot_pragma"); &print($indent++,"case (1'b1) // $globals{machine}{onehot_pragma}{value}\n"); } else { &print($indent++,"case (1'b1) // synopsys parallel_case full_case\n"); } } #&assertion($indent,"// Test assertions"); # Output state code in @allstates order foreach $state (@allstates) { %non_default_on_state_value_found = (); # hash to keep track of mealy info if ($states{$state}{attributes}{comment}) { $comment = " // $states{$state}{attributes}{comment}"; } else { $comment = ""; } if ($encoding eq "heros") { #&print($indent++,"$state:" . " begin${comment}\n"); &print($indent++,sprintf("%-${statename_length}s:",$state) . " begin${comment}\n"); } elsif ($encoding eq "onehot") { #&print($indent++,"$statevar\[$state\]: begin${comment}\n"); &print($indent++,sprintf("%-${statename_length_onehot}s:","$statevar\[$state\]") . " begin${comment}\n"); } # Search state attributes for outputs. If a combinational output is found, # assign its value here (maybe). foreach $comb_output (sort keys %comb_outputs) { if (exists $states{$state}{attributes}{$comb_output}{value}) { $value = $states{$state}{attributes}{$comb_output}{value}; } else { $value = $comb_outputs{$comb_output}{default_value}; } # Check to see if output is assigned on transitions. If so, check to see # if a non-default value has been assigned in this state. # If so, output assignment statement, set flag and issue warning. if (exists $globals{"trans"}{$comb_output}{"value"}) { # yup, we're in mealy-on-transitions mode if ($value ne $comb_outputs{$comb_output}{default_value}) { # If output has a non-default value in this state, warn user and print it anyway &warning($indent,"Combinational output $comb_output is assigned on transitions, but has a non-default value \"$value\" in state $state"); # Check that resulting value is non-null &error($indent,"Value of comb output $comb_output cannot be determined in state $state") if ($value eq ""); # Print the default statement anyway if it doesn't match default from above &print($indent,"$comb_output = $value;\n") unless ($value eq $comb_defaults{$comb_output}); # Set flag to suppress non-default on-transition values in this state $non_default_on_state_value_found{$comb_output} = 1; } } else { # NOT in mealy-on-transitions mode, so print the default statement # Check that resulting value is non-null &error($indent,"Value of comb output $comb_output cannot be determined in state $state") if ($value eq ""); # Only print the default statement if the comb output is NOT assigned # on transitions (and it doesn't match default above) &print($indent,"$comb_output = $value;\n") unless ($value eq $comb_defaults{$comb_output}); } } # Search transitions for all those starting on this state. # Sort by priority, then output vlog if/elseif/else # equation to switch to correct endState. @transitions_from_this_state = (); foreach $trans (keys %transitions) { if ($transitions{$trans}{"startState"} eq $state) { &debug("Transition $trans starts on state $state, adding to array",0,$section); push(@transitions_from_this_state,$trans); } } # nextstate determination # Sort by priority, followed by equation. The equation part ensures # that "1" will be last. @transitions_from_this_state = sort sort_by_priority_then_equation_equal_1 @transitions_from_this_state; #print("After sort, transitions_from_this_state for state $state is @transitions_from_this_state\n"); &debug("After sort, transitions_from_this_state for state $state is @transitions_from_this_state",0,$section); # Check for problems with priority undef %priorities_seen; if ($#transitions_from_this_state > 0) { for (my $i = 0; $i<=$#transitions_from_this_state; $i++) { $trans = $transitions_from_this_state[$i]; $equation = $transitions{$trans}{attributes}{equation}{value}; # If no priority exists, warn unless # 1) this is the last transition and equation is "1" # 2) There are only two transitions, and the last one has equation of "1" if (!($pri = $transitions{$trans}{attributes}{priority}{value})) { #print "looking at $trans: $i of $#transitions_from_this_state\n"; #print "equation: $equation\n"; unless ( # last and equation is 1 ($equation eq "1" && $i == $#transitions_from_this_state) || # Exactly 2 transitions and last has equation of 1 ($#transitions_from_this_state == 1 && $transitions{$transitions_from_this_state[$#transitions_from_this_state]}{attributes}{equation}{value} eq "1") ) { &warning($indent,"State $state has multiple exit transitions, and transition $trans has no defined priority"); } } else { # If priority exists, but it's a duplicate, warn unless THIS transition # has an equation of 1 (note that sort means that if two transitions # have the same priority, the one with an equation of 1 will come last). if (exists $priorities_seen{$pri}) { unless ($equation eq "1") { &warning($indent,"State $state has multiple exit transitions, and transition $trans has the same priority as transition $priorities_seen{$pri}"); } } else { $priorities_seen{$pri} = $trans; } } } } for (my $i = 0; $i<=$#transitions_from_this_state; $i++) { $trans = $transitions_from_this_state[$i]; $equation = $transitions{$trans}{attributes}{equation}{value}; #print "trans is $trans\n"; #print STDERR "equation is $equation\n"; $equation = 1 unless ($equation); # force null string to "1" $eq1seen = 0; # Check for unreachable transitions. If the equation is 1 and # there are more transitions following, it's an error. if ($equation eq "1" && $#transitions_from_this_state > $i) { &error($indent,"State $state has an always true equation for transition $trans, but has lower priority transitions as well"); } if ($i == 0) { # first item in list # If always true, just output "begin" without the if # (need a begin/end block in case there are comb outputs) if ($equation eq "1") { &print($indent++,"begin\n"); $eq1seen = 1; } else { &print($indent++,"if ($equation) begin\n"); } } elsif ($i == $#transitions_from_this_state) { # last item in list # Note that this won't execute if there was only one item in # the list (due to use of elsif) if ($equation eq "1") { &print($indent++,"else begin\n"); $eq1seen = 1; } else { &print($indent++,"else if ($equation) begin\n"); } } else { # intermediate terms &print($indent++,"else if ($equation) begin\n"); } $nextstate = $transitions{$trans}{endState}; # Handle transition comments if ($transitions{$trans}{attributes}{comment}) { $comment = " // $transitions{$trans}{attributes}{comment}"; } else { $comment = ""; } if ($encoding eq "heros") { foreach $graystate (@{ $graytransitions{$state} }) { if ($graystate eq $nextstate) { $comment = $comment . " // graycoded"; } } &print($indent,"$nextstatevar = $nextstate;$comment\n"); } elsif ($encoding eq "onehot") { #&print($indent,"$nextstatevar = $zero_padded_one << $nextstate;\n"); &print($indent,"$nextstatevar\[$nextstate\] = 1'b1;$comment\n"); } # Add comb outputs foreach $comb_output (keys %{ $transitions{$trans}{attributes} }) { if ($transitions{$trans}{attributes}{$comb_output}{type} eq "output") { if (exists $transitions{$trans}{attributes}{$comb_output}{value}) { $value = $transitions{$trans}{attributes}{$comb_output}{value}; } else { $value = $globals{"trans"}{$comb_output}{"value"} ; # default value defined in transitions table } # Check that resulting value is non-null &error($indent,"Value of comb output $comb_output cannot be determined on transition $trans") if ($value eq ""); # Print only if no non-default state value was found, or this is a # non-default transition value. if (!$non_default_on_state_value_found{$comb_output} || $value ne $globals{"trans"}{$comb_output}{"value"}) { &print($indent,"$comb_output = $value;\n") unless ($value eq $comb_defaults{$comb_output}); } } } &print(--$indent,"end\n"); # end of if/else/elseif ... or begin block } # Check for the case of onehot without an equation "1". If this is found # and implied_loopback is on, add an else to stay put. If # default_state_is_x is on, make bit an x. if ($encoding eq "onehot") { if (!$eq1seen) { if ($globals{machine}{implied_loopback}{value}) { &print($indent++,"else begin\n"); &print($indent,"$nextstatevar\[$state\] = 1'b1; // Added because implied_loopback is true\n"); &print(--$indent,"end\n"); } elsif ($globals{machine}{default_state_is_x}{value}) { &print($indent++,"else begin\n"); &print($indent,"$nextstatevar\[$state\] = 1'bx; // Added because default_state_is_x is true\n"); &print(--$indent,"end\n"); } else { &warning($indent,"Neither implied_loopback nor default_state_is_x attribute is set on onehot state machine and there is no loopback arc - this could result in latches being inferred"); } } } &print(--$indent,"end\n"); # end of case match } &print(--$indent, "endcase\n"); &print(--$indent, "end\n"); ####################### # State sequential block ####################### $section = "seq_block"; &print($indent, "\n"); unless ($encoding eq "onehot") { &print($indent,"// Assign reg'd outputs to state bits\n"); foreach $regout (sort { $reg_outputs{$a}{statebit_lower} <=> $reg_outputs{$b}{statebit_lower} } keys %reg_outputs) { &print($indent,"assign $regout = $statevar\[$reg_outputs{$regout}{statebit_range}\];\n"); } &print($indent,"\n"); } &print($indent, "// sequential always block\n"); # Create the always @ line using a sub so it can be used by datapath # as well. $line = &create_sequential_always; &print($indent++,"$line"); if ($reset_signal) { $bang = "!" if ($reset_edge =~ /neg/); &print($indent++,"if (${bang}${reset_signal})\n"); if ($encoding eq "heros") { &print($indent,"$statevar <= $reset_state;\n"); } elsif ($encoding eq "onehot") { &print($indent,"$statevar <= $zero_padded_one << $reset_state;\n"); } &print(--$indent,"else\n"); } &print($indent," $statevar <= $nextstatevar;\n"); &print(--$indent,"end\n"); ####################### # Datapath sequential block ####################### $section = "dp_seq_block"; if (scalar(%regdp_outputs)) { &print($indent, "\n"); &print($indent, "// datapath sequential always block\n"); # Create the always @ line $line = &create_sequential_always; &print($indent++,"$line"); if ($reset_signal) { $bang = "!" if ($reset_edge =~ /neg/); &print($indent++,"if (${bang}${reset_signal}) begin\n"); # Assign reset values to datapath registers. This is assumed # to be the value in the reset_state. If not specified, assume # the default value. foreach $regdp_output (sort keys %regdp_outputs) { if (exists $states{$reset_state}{attributes}{$regdp_output}{value}) { $value = $states{$reset_state}{attributes}{$regdp_output}{value}; } else { &warning($indent,"No reset value for datapath output $regdp_output set in reset state $reset_state - Assiging a reset value of $value based on default"); $value = $regdp_outputs{$regdp_output}{default_value}; } # Check that resulting value is non-null &error($indent,"Reset value of datapath output $regdp_output cannot be determined ") if ($value eq ""); &print($indent,"$regdp_output <= $value;\n"); } &print(--$indent,"end\n"); &print($indent++,"else begin\n"); } else { &print(--$indent,"begin\n"); } # Output defaults. # To get better synth results "out of the box", use a 0 default if no # default is defined. To get the old "hold value in illegal states" # behavior, it will be necessary for the user to set the default to # be the variable name (ie, if the variable name is "foo", make the default # value "foo"). foreach $regdp_output (sort keys %regdp_outputs) { if (exists $regdp_outputs{$regdp_output}{default_value} && ($regdp_outputs{$regdp_output}{default_value} ne "") ) { $dp_seq_defaults{$regdp_output} = $regdp_outputs{$regdp_output}{default_value}; &print($indent,"$regdp_output <= $dp_seq_defaults{$regdp_output}; // default\n"); } else { $dp_seq_defaults{$regdp_output} = "0"; # Use string zero &warning($indent,"Datapath output $regdp_output has no default value - using 0"); &print($indent,"$regdp_output <= $dp_seq_defaults{$regdp_output}; // default to zero for better synth results (no default set in .fzm file)\n"); #&print($indent,"$regdp_output <= $widths{$regdp_output}'b$dp_seq_defaults{$regdp_output}; // default to zero for better synth results (no default set in .fzm file)\n"); } } # State "case" block if ($encoding eq "heros") { &print($indent++,"case ($nextstatevar)\n"); } elsif ($encoding eq "onehot") { #&print($indent++,"case (1'b1) // synopsys parallel_case full_case\n"); # Figure out the onehot pragma. Defaults to # "synopsys parallel_case full_case", but can be overridden by attribute. if (exists $globals{machine}{onehot_pragma}{value} ) { &warning($indent,"Using override value from attribute onehot_pragma"); &print($indent++,"case (1'b1) // $globals{machine}{onehot_pragma}{value}\n"); } else { &print($indent++,"case (1'b1) // synopsys parallel_case full_case\n"); } } $keep_case = 0; # flag to keep case statement (at least one non-default found) foreach $state (@allstates) { $keep_state = 0; # flag to keep this state # Create state + begin (might be "popped" if $keep_state doesn't get set) if ($encoding eq "heros") { #&print($indent++,"$state: begin\n"); &print($indent++,sprintf("%-${statename_length}s:",$state) . " begin\n"); } elsif ($encoding eq "onehot") { #&print($indent++,"$nextstatevar\[$state\]: begin\n"); &print($indent++,sprintf("%-${nextstatename_length_onehot}s:","$nextstatevar\[$state\]") . " begin\n"); } # Search state attributes for regdp outputs. If a regdp output is found, # assign its value here foreach $regdp_output (sort keys %regdp_outputs) { if (exists $states{$state}{attributes}{$regdp_output}{value}) { $value = $states{$state}{attributes}{$regdp_output}{value}; } if (exists($dp_seq_defaults{$regdp_output}) && ($value eq $dp_seq_defaults{$regdp_output})) { # skip - covered by default } else { # Check that resulting value is non-null &error($indent,"Value of regdp output $regdp_output cannot be determined in state $state") if ($value eq ""); &print($indent,"$regdp_output <= $value;\n"); $keep_state = 1; $keep_case = 1; } } # If we did something here, output the end if ($keep_state) { &print(--$indent,"end\n"); # end of case match # Otherwise, pop the state + begin off the print buffer and clean up the indent. } else { $indent--; pop(@pbuf); } } # Similarly, keep case and output endcase only if we found something no-default (flat is set) if ($keep_case) { &print(--$indent, "endcase\n"); } else { pop(@pbuf); &warning($indent,"Did not find any non-default values for any datapath outputs - suppressing case statement"); $indent--; } &print(--$indent,"end\n"); # end of if not reset &print(--$indent,"end\n"); # end of always } ####################### # State name-as-ascii block ####################### if ($simcode) { $section = "ascii_block"; &print($indent, "\n"); &print($indent, "// This code allows you to see state names in simulation\n"); #&print($indent, "// synopsys translate_off\n"); &print($indent, "`ifndef SYNTHESIS\n"); &print($indent, "reg [" . ($statename_length * 8 - 1) . ":0] $statenamevar;\n"); &print($indent++, "always @* begin\n"); # State "case" block if ($encoding eq "heros") { &print($indent++,"case ($statevar)\n"); } elsif ($encoding eq "onehot") { &print($indent++,"case (1)\n"); } foreach $state (@allstates) { if ($encoding eq "heros") { #&print($indent++,"$state:\n"); &print($indent++,sprintf("%-${statename_length}s:",$state) . "\n"); } elsif ($encoding eq "onehot") { #&print($indent++,"$statevar\[$state\]:\n"); &print($indent++,sprintf("%-${statename_length_onehot}s:","$statevar\[$state\]") . "\n"); } &print($indent++,"$statenamevar = \"$state\";\n"); $indent--; $indent--; } # add default for X #&print($indent++,"default:\n"); &print($indent++,sprintf("%-${statename_length}s:","default") . "\n"); &print($indent++,sprintf("$statenamevar = \"%s\";\n", "X" x ($statename_length))); $indent--; $indent--; &print(--$indent, "endcase\n"); &print(--$indent, "end\n"); #&print($indent, "// synopsys translate_on\n\n"); &print($indent, "`endif\n\n"); } &print($indent,"$globals{machine}{insert_at_bottom_of_module}{value}\n"); # endmodule and bottom-of-file &print(--$indent,"endmodule\n"); &print($indent,"$globals{machine}{insert_at_bottom_of_file}{value}\n"); &print_output; ####################################################################### # Subroutines ####################################################################### sub create_sequential_always { # Check to make sure clock data is ok if (exists $globals{machine}{clock}{value}) { $clk = $globals{machine}{clock}{value}; } else { &error($indent,"No clock specified"); } if (! exists $globals{inputs}{$clk}) { #&warning($indent,"Specified clock signal $clk is not an input"); &error($indent,"Specified clock signal $clk is not an input"); } if ($globals{machine}{clock}{type} !~ /^(pos)|(neg)edge$/) { &error($indent,"Clock type not specified as posedge or negedge"); } # Build clock portion of always @(posedge...) $line = "always @($globals{machine}{clock}{type} $clk" ; # Add reset if given if (exists $globals{machine}{reset_signal}{value}) { $reset_signal = $globals{machine}{reset_signal}{value}; if (! exists $globals{inputs}{$reset_signal}) { #&warning($indent,"Specified reset signal $reset_signal is not an input"); &error($indent,"Specified reset signal $reset_signal is not an input"); } if ($globals{machine}{reset_signal}{type} =~ /^((neg)|(pos))edge$/) { $sync = "async"; } elsif ($globals{machine}{reset_signal}{type} eq "positive") { $sync = "sync"; } elsif ($globals{machine}{reset_signal}{type} eq "negative") { $sync = "sync"; } else { &error($indent,"reset_signal $reset_signal not specified as type posedge, negedge, positive or negative"); } $reset_edge = $globals{machine}{reset_signal}{type}; if ($sync eq "async") { $line = $line . " or $reset_edge $reset_signal"; } if (! exists $globals{machine}{reset_state}{value}) { &error($indent,"Reset signal given, but no reset state found."); } else { $reset_state = $globals{machine}{reset_state}{value}; } } return $line = $line . ") begin\n"; } sub sort_by_priority_then_equation_equal_1 { #&debug("a priority: $a -> $transitions{$a}{attributes}{priority}{value}",0,$section); #&debug("a equation: $a -> $transitions{$a}{attributes}{equation}{value}",0,$section); #&debug("a: $a -> $a",0,$section); #&debug("b priority: $b -> $transitions{$b}{attributes}{priority}{value}",0,$section); #&debug("b equation: $b -> $transitions{$b}{attributes}{equation}{value}",0,$section); #&debug("b: $a -> $b",0,$section); # &debug("priority sort result is: " . $transitions{$a}{attributes}{priority}{value} <=> $transitions{$b}{attributes}{priority}{value},0,$section); $transitions{$a}{attributes}{priority}{value} <=> $transitions{$b}{attributes}{priority}{value} || ($transitions{$a}{attributes}{equation}{value} eq "1") cmp ($transitions{$b}{attributes}{equation}{value} eq "1") # finally, sort by trans name just so order will be predictable || $a cmp $b } sub parse_input { my %myattributes_forcompare; &debug("Start of parse_input\"$_\"",0,"parse_input"); # Create local version of myattributes with substition done to enable # compares foreach $entry (keys %myattributes) { $entry =~ s/\*[^\*]*\*/[^\"]+/g; $myattributes_forcompare{$entry} = 1; } while (<>) { chomp; s/##.*$// ;# Remove comments s/^\s*//; # Remove leading whitespace # Toss status/endstatus if (/^\s*<status>/) { until (/^\s*<\/status>/) { $_ = <>; } next; } # Ignore drawArea (strips this level entirely from out data # structures) next if (/drawArea/); # Look for endtoken first to allow elsif to handle token case if (($endtoken) = (/^\s*<\/(.*)>/)) { &debug("endtoken: $endtoken from \"$_\"",0,"parse_input"); &debug("ptr was \"$ptr\"",0,"parse_input"); # Found an endtoken. If this is the array, clear the array value. # Otherwise, remove everything from this token to the end from ptr. if ($array eq $endtoken) { $array = ""; $ptr = ""; } else { $ptr =~ s/{\"\Q$endtoken\E\"}.*$//; } &debug("new array is \"${array}\"",0,"parse_input"); &debug("new ptr is \"${ptr}\"",0,"parse_input"); &debug("",0,"parse_input"); } elsif (($token) = (/^\s*<(.*)>/)) { &debug("token: $token from \"$_\"",0,"parse_input"); # Found new token. If array is blank, this is the new array. # Otherwise, append to ptr. if ($array eq "") { $array = "$token"; } else { $ptr = $ptr . "{\"$token\"}"; } &debug("new array is \"${array}\"",0,"parse_input"); &debug("new ptr is \"${ptr}\"",0,"parse_input"); &debug("",0,"parse_input"); } else { $value = $_; &debug("value: $value from \"$_\"",0,"parse_input"); # Found a value instead of a token. Use array and ptr to set # the value using eval. # First, turn state name (or transition name) into index &debug("old ptr (in value block) is \"$ptr\"",0,"parse_input"); if ($ptr =~ s/({\"attributes\"}).*name.*value\"}/{\"$value\"}\1/) { # If this already exists, it means we have a duplicate entry! if (exists $${array}{"$value"}{"attributes"}) { &error($indent,"Error - found duplicate entry for $array $value"); } &debug("new ptr (in value block) is \"$ptr\"",0,"parse_input"); } else { $keep = 0; foreach $entry (keys %myattributes_forcompare) { #print STDERR "Looking at myatt $entry\n"; if ("${array}${ptr}" =~ $entry) { &debug("Got match to $entry\n",0,"parse_input"); $keep = 1; } } #$cmd = "\$${array}${ptr} = q{${value}};"; $value =~ s/"/\\"/g; # escape quotes for next line $cmd = "\$${array}${ptr} = \"$value\";"; if ($keep) { &debug("cmd is \"$cmd\"",0,"parse_input"); eval $cmd unless (!$array); } else { &debug("skipped cmd \"$cmd\"",0,"parse_input"); } } } } &debug("End of parse_input\"$_\"\n\n",0,"parse_input"); # Check some random values #&debug("state0 vis is $states{state0}{attributes}{vis}",0,"parse_input"); #&debug("trans0 startState is $transitions{trans0}{startState}",0,"parse_input"); #&debug("trans0 endState is $transitions{trans0}{endState}",0,"parse_input"); } sub assign_states_hero { # print &dec2bin(1,5) . "\n"; # print &dec2bin(17,5) . "\n"; # @statevals = &get_statevals(5); # create global statevals # print "@statevals\n"; # exit; for ($bits = $minbits ; $bits <= $maxbits ; $bits++) { &debug("Trying $bits bits for states",0,"bits"); @statevals = &get_statevals($bits); # create global statevals #print "statevals: @statevals\n"; # Call recursive subroutine to assign states ($success,$uaref,$v2nref) = &attempt_assign_states( \@allstates, \%state_val2name, 0); last if $success; } if (!$success) { &error($indent,"No valid state assignment found in range of $minbits to $maxbits"); } } sub attempt_assign_states { my ($uaref,$v2nref,$depth) = @_; my (@unassigned_states); my (%state_val2name); my ($state,$stateval); # Dereference arguments into local data structures @unassigned_states = @$uaref; %state_val2name = %$v2nref; while ($state = shift(@unassigned_states)) { &debug("attempt_assign_states working on state $state",$depth,"assign_states"); STATEVAL : foreach $stateval (@statevals) { &debug("attempt_assign_states looking at stateval $stateval for uniqueness",$depth,"assign_states"); next if ($state_val2name{$stateval}); # must be unique &debug("attempt_assign_states trying stateval $stateval for state $state",$depth,"assign_states"); if (&matches_fixedbits($state,$stateval) && &matches_graycodes($state,$stateval,%state_val2name)) { # looks good at this level &debug("$stateval for state $state passes checks at this level",$depth,"assign_states"); $state_val2name{$stateval} = $state; if (!@unassigned_states) { # if nothing left, we're done &debug("attempt_assign_states returning success",$depth,"assign_states"); return (1, \@unassigned_states, \%state_val2name); } else { # otherwise, keep trying recursively $depth++; ($found_state,$uaref1,$v2nref1) = &attempt_assign_states( \@unassigned_states, \%state_val2name,$depth); } if ($found_state) { # good right the way down %state_val2name = %$v2nref1; last STATEVAL; } else { &debug("stateval $stateval for state $state looked good, but failed recursively - trying next",$depth,"assign_states"); delete $state_val2name{$stateval}; } } } return ($found_state, \@unassigned_states, \%state_val2name); } } sub matches_fixedbits { my ($state,$stateval) = @_; # Use a bit index that runs right to left for ($bit = 0 ; $bit < length($stateval) ; $bit++) { $substrbit = length($stateval) - $bit - 1 ; &debug("matches_fixbits looking at bit $bit of $stateval (" . substr($stateval,$substrbit,1) . ") for $state",$depth,"mf"); if ( (exists $fixedbits{$state}{$bit}) && ($fixedbits{$state}{$bit} ne (substr($stateval,$substrbit,1))) ) { &debug("matches_fixbits found an illegal value at bit $bit of $stateval for $state",$depth,"mf"); return 0; } } return 1; } sub matches_graycodes { my ($state,$stateval,%state_val2name) = @_; return ( &matches_graycodes_to_this_state($state,$stateval,%state_val2name) && &matches_graycodes_from_this_state($state,$stateval,%state_val2name) ); } sub matches_graycodes_to_this_state { my ($state,$stateval,%state_val2name) = @_; my ($otherstateval,$graystate); # look at each currently defined state (in state_val2name) foreach $otherstateval (keys %state_val2name) { $otherstate = $state_val2name{$otherstateval}; # get the name if (exists $graytransitions{$otherstate}) { # if it has a gray list foreach $graystate (@{ $graytransitions{$otherstate} }) { # look through the list if ($graystate eq $state) { #I'm in his list &debug("matches_graycodes_to_this_state checking $graystate ($otherstateval) against $state ($stateval)",$depth,"mgto"); # check to see if it is legal if (!&isgraytransition($stateval,$otherstateval)) { return 0; } } } } } return 1; # if nothing illegal found, all must be legal } sub matches_graycodes_from_this_state { my ($state,$stateval,%state_val2name) = @_; my ($otherstateval,$graystate); # look at each state that should be a gray transition from this state return 1 if (!exists $graytransitions{$state}); foreach $graystate (@{ $graytransitions{$state} }) { &debug("matches_graycodes_from_this_state looking at gray state $graystate for state $state",$depth,"mgfrom"); # find the encoding for the gray state foreach $otherstateval (keys %state_val2name) { &debug("matches_graycodes_from_this_state looking at otherstateval $otherstateval",$depth,"mgfrom"); if ($state_val2name{$otherstateval} eq $graystate) { &debug("Checking $graystate ($otherstateval) against $state ($stateval)",$depth,"mgfrom"); # check to see if it is legal if (!&isgraytransition($stateval,$otherstateval)) { return 0; } } } } return 1; # if nothing illegal found, all must be legal } sub isgraytransition { my ($stateval1,$stateval2) = @_; my ($diffs); # using perl's normal left to right bit order &debug("isgraytransition checking $stateval1 against $stateval2",$depth,"isgraytrans"); for ($bit = 0 ; $bit < length($stateval1) ; $bit++) { if (substr($stateval1,$bit,1) ne substr($stateval2,$bit,1)) { $diffs++ } } &debug("isgraytransition found $diffs diffs",$depth,"isgraytrans"); return ($diffs <= 1); } sub required_bits { my ($n) = @_; $div= log($n) / log(2); $base = sprintf("%d",$div); if (($div - $base) > 0) {$base++;} return $base; } sub hex2bin { my ($hex) = @_; my ($bin,$i); %hex2bin = ( 0 => "0000", 1 => "0001", 2 => "0010", 3 => "0011", 4 => "0100", 5 => "0101", 6 => "0110", 7 => "0111", 8 => "1000", 9 => "1001", a => "1010", b => "1011", c => "1100", d => "1101", e => "1110", f => "1111", A => "1010", B => "1011", C => "1100", D => "1101", E => "1110", F => "1111", ); for ($i = 0; $i < length($hex) ; $i++) { $bin = $bin . $hex2bin{substr($hex,$i,1)}; } return $bin; } sub octal2bin { my ($octal) = @_; my ($bin,$i); %octal2bin = ( 0 => "000", 1 => "001", 2 => "010", 3 => "011", 4 => "100", 5 => "101", 6 => "110", 7 => "111", ); for ($i = 0; $i < length($octal) ; $i++) { $bin = $bin . $octal2bin{substr($octal,$i,1)}; } return $bin; } sub dec2bin { my ($dec,$bits) = @_; my ($hex,$bin,$i); $hex = sprintf("%x",$dec); $bin = &hex2bin($hex); # Zero extend #print "dec2bin bin was $bin\n"; $bin = "0" x ($bits - length($bin)) . $bin; #print "dec2bin extended bin is $bin\n"; return substr($bin,length($bin) - $bits,$bits); } sub convert2bin { my ($value) = @_; my ($bits,$val); #print "Starting convert2bin on value $value\n"; if (($bitlen,$format,$val) = ($value =~ /^(?:(\d*))?'(?:([bdoh]))?([0-9a-fA-F]*)$/)) { } else { $format = "b" unless ($format); $val = $value; } #print "value: $value: bitlen: $bitlen format: $format val: $val\n"; if ($format eq "b") { #print "converted a bin $value: bitlen: $bitlen format: $format val: $val\n"; $bitlen = length($val) unless ($bitlen); # default length if ($bitlen != length($val)) { &error($indent,"Binary value $value has a size that doesn't match the value"); } $value = substr($val,length($val) - $bitlen,$bitlen); } elsif ($format eq "h") { #print "converted a hex $value: bitlen: $bitlen format: $format val: $val\n"; $bitlen = 4 * length($val) unless ($bitlen); # default length $value = &hex2bin($val); # Zero extend #print "value was $value\n"; $value = "0" x ($bitlen - length($value)) . $value; #print "value extended is $value\n"; $value = substr($value,length($value) - $bitlen,$bitlen); } elsif ($format eq "o") { #print "converted a octal $value: bitlen: $bitlen format: $format val: $val\n"; $bitlen = 3 * length($val) unless ($bitlen); # default length $value = &octal2bin($val); $value = substr($value,length($value) - $bitlen,$bitlen); } elsif ($format eq "d") { #print "converted a dec $value: bitlen: $bitlen format: $format val: $val\n"; $value = &dec2bin($val,$bitlen); } else { &error($indent,"Got an unrecognized format $format in convert2bin"); } #print "returning $value\n"; #print "\n"; return $value; } sub get_statevals { my($bits) = @_; my(@statevals); my($i); my($bin); for ($i = 0 ; $i < 2 ** $bits ; $i++) { $bin = &dec2bin($i,$bits); push(@statevals,$bin); } return @statevals; } sub debug { my ($string,$depth,$section) = @_; if ($global_debug || $debug_sections{$section}) { print STDERR " " x $depth . "<db${section}>: $string\n"; } } sub error { my ($indent,$string) = @_; # indent is ignored. It is just there to make it easy to switch btw # warnings and errors. &print_output; print "\n\nError: $string - exiting\n"; print STDERR "\n\nError: $string - exiting\n"; exit; } sub warning { my($indent,$string) = @_; my($group,$number) = (); # Determine warning number based on string. It would be cleaner to just # have the call use the number, but that makes it difficult to see what # the warning is when reading the code. if ($string =~ /No reset specified/) {$group = "R"; $number = 1;} elsif ($string =~ /No reset value for datapath output \S+ set in reset state \S+ - Assiging a reset value of \S+ based on default/) {$group = "R"; $number = 5;} # Now an error: #elsif ($string =~ /Specified reset signal \S+ is not an input/) {$group = "R"; $number = 6;} elsif ($string =~ /Neither implied_loopback nor default_state_is_x attribute is set on state machine - this could result in latches being inferred/) {$group = "I"; $number = 2;} elsif ($string =~ /State \S+ has multiple exit transitions, and transition \S+ has no defined priority/) {$group = "P"; $number = 3;} elsif ($string =~ /State \S+ has multiple exit transitions, and transition \S+ has the same priority as transition .*/) {$group = "P" ; $number = "4"} elsif ($string =~ /Combinational output \S+ is assigned on transitions, but has a non-default value ".+" in state \S+/) {$group = "C" ; $number = 7;} elsif ($string =~ /Neither implied_loopback nor default_state_is_x attribute is set on onehot state machine and there is no loopback arc - this could result in latches being inferred/) {$group = "I"; $number = 8;} elsif ($string =~ /Did not find any non-default values for any datapath outputs - suppressing case statement/) {$group = "D"; $number = 9;} elsif ($string =~ /Combinational output \S+ has no default value/) {$group = "C" ; $number = 10;} elsif ($string =~ /Datapath output \S+ has no default value/) {$group = "D" ; $number = 11;} elsif ($string =~ /Using override value from attribute/) {$group = "O" ; $number = 12;} # Output warning unless suppressed unless ($nowarns{"${group}${number}"} || $nowarns{$group}) { eval "\$myindent = \"$indentstring\" x $indent"; if ($warnout eq "stdout" || $warnout eq "both") { # warnings are stored in an array whose indeces correspond to the # previous line of pbuf. Use concat in case there are multiple warnings # associated with the same line. $warnings[$#pbuf] = $warnings[$#pbuf] . "${myindent}// Warning $group$number: $string \n"; } if ($warnout eq "stderr" || $warnout eq "both") { print STDERR "\n\nWarning $group$number: $string \n"; } } } sub assertion { my($indent,$string) = @_; eval "\$myindent = \"$indentstring\" x $indent"; $assertions[$#pbuf] = $assertions[$#pbuf] . "${myindent}${string}\n"; } sub print { my($indent,$string) = @_; my($skip,$maxlen,$thislen) = 0; my($tag) = "//tersetag"; my($i,$j); $section = "terse"; # for debug # -terse handling # If you plan to follow or modify the -terse code, be sure to have an # ample supply of barf bags near to hand. It is HORRIBLE. # # The basic idea is that all calls to &print go to an array (pbuf). This # is done even without -terse, just to be consistent. Warnings and # "comments" must go to separate arrays that parallel pbuf. This makes # it easier for the tersify code to find its way around. # # When -terse is on, it looks for "end" and "endcase" (plus wire statements), # and starts poking around in the pbuf to try to get rid of begin/end pairs. # When it finds an "endcase", it backs up through pbuf to tag lines as # statement and non-statement, and to calculate how much indent will be # required to make the assignment statements line up. It then goes back # forward through pbuf and makes these modifications. # # Yech! # 1) Get rid of unnecessary wire statements if ($terse && ($string =~ /^\s*(in|out)put wire /)) { ($temp = $string) =~ s/\/\/.*$//; unless ($temp =~ /\[\d+:\d+\]/) { $string =~ s/ wire//; } } # 2) Get rid of extra begin/end pairs # if ($terse && ($string =~ /^\s*end\s*$/)) { # a) If we're on an "end" line and the next-to-previous line ends in begin, # strip the begin and newline and set skip flag. # ex: # if (foo) begin # bar <= 0 # end # or: # begin # bar <= 0 # end if ($pbuf[$#pbuf - 1] =~ s/^([^:]*)begin\s*$/\1/) { $skip = 1; # If resulting string is empty, remove it if ($pbuf[$#pbuf - 1] =~ /^\s*$/) { splice(@pbuf, $#pbuf -1, 1); } # Unindent eval "\$myindent = \"$indentstring\""; $pbuf[$#pbuf] =~ s/$myindent//; # b) If we're on an "end" line and the next-to-previous line looks like # a "IDLE:" line, strip the begin and newline and set skip flag. # ex: # IDLE: begin # bar <= 0 # end } elsif (&is_stateline($pbuf[$#pbuf - 1])) { if ($pbuf[$#pbuf - 1] =~ s/(\S+\s*:)\s+begin\s*$/\1/) { $skip = 1; # Unindent #eval "\$myindent = \"$indentstring\""; #$pbuf[$#pbuf] =~ s/$myindent//; } # c) If we're on an "end" line and the next-to-previous line is "else", # 2 lines up from that is "if", and # 1 line from that is "state:" # IDLE: begin < -4 # if (foo) < -3 # bar <=1 < -2 # else < -1 # bar <= 0 < 0 # end < curr # remove the begin and newline, join the lines and set skip flag # (lines are joined explicitly because "case" step needs all "statements" # on a single line). # Also, scoot the -1 line (else) over by the length to the ":" plus 1 } elsif ( $pbuf[$#pbuf -1] =~ /else / && $pbuf[$#pbuf -3] =~ /if / && $pbuf[$#pbuf -4] =~ /begin\s*$/ # no comment on state begin && (&is_stateline($pbuf[$#pbuf - 4])) ) { $pbuf[$#pbuf - 4] =~ s/(\S+\s*:)\s+begin\s*$/\1/; # strip begin # Do the "scoot" $colonpos = index($pbuf[$#pbuf-4],":"); ($temp = $pbuf[$#pbuf-4]) =~ s/^(\s*).*$/\1/; $startpos = length($temp); $pbuf[$#pbuf-1] = " " x ($colonpos - $startpos) . $pbuf[$#pbuf-1]; # Remove indent on 4 and join -3 and -4 into -3 $pbuf[$#pbuf-3] =~ s/^$level4indent/ /; $pbuf[$#pbuf-3] = $pbuf[$#pbuf-4] . $pbuf[$#pbuf-3]; # And snuff out -4 splice(@pbuf, $#pbuf -4, 1); $skip = 1; # d) If we're on an "end" line and their are "stacked begin/begin end/end # sets, like this: # IDLE: begin < -? # begin < -? # bar <=1 < -? # bar <= 0 < -1 # end < 0 # end < curr # remove the inside begin/end pair, but do not set skip flag. } elsif ($pbuf[$#pbuf] =~ /^\s*end/) { # previous was ALSO end # troll through the buffer looking for the previous begin $i = $#pbuf-1; while ($pbuf[$i] !~ /^[^\/]*begin/ && $pbuf[$i] !~ /^\s*end/ && $i > 0) { $i-- } # $i is now pointing at begin (or at end or start of buffer) # If it is begin, then do our thing if ($pbuf[$i] =~ /^\s*begin/) { # MUST be a PURE begin if ($pbuf[$i-1] =~ /^[^\/]*begin/) { # Previous is ALSO a begin # snuff out $i entry splice(@pbuf, $i, 1); # snuff out last entry (previous end) splice(@pbuf, $#pbuf, 1); } } } else { #print $string; #print $pbuf[$#pbuf - 1]; } } # Change the statename (sim code) section to put state and assignment # on the same line if ($terse && ($string =~ /$statenamevar\s+=/)) { &debug("Found statename line \"$string\"\n",0,$section); &debug("$pbuf[$#pbuf]\n",0,$section); chomp($pbuf[$#pbuf]); } # 3) At endcase, back up and re-format assignment lines to make them line up if ($terse && ($string =~ /^\s*endcase/)) { &debug("\nBefore 1st pass:\n",0,$section); &debug(join("",@pbuf),0,$section); #for ($i=0; $i<=$#pbuf; $i++) { # print "$i : $pbuf[$i]\n"; #} # 1st pass: Back up through the buffer to the case statement and gather # information about the longest statement line. Also, tag the lines as # statement (#) and provide their length if applicable by pre-pending # the tag. This will be used and removed on the 2nd pass. $maxlen = 0; $i = $#pbuf; $thislen = 0; while ($pbuf[$i] !~ /^\s*case/ && $i > 0) { &debug("\n\n1st pass line is:\n",0,$section); &debug("\"$pbuf[$i]\"",0,$section); if ($pbuf[$i] =~ /^\s*(if)|(else)|(end)|(begin)/ || &is_stateline($pbuf[$i])) { # "statement" lines &debug("\nIt's a statement line\n",0,$section); # Get length and set maxlen ($nocomment = $pbuf[$i]) =~ s/\s*\/\/.*$//; $nocomment =~ s/\s*\/\*.*$//; # for /* - not sure if this is necessary $thislen = length($nocomment); &debug("got a match: thislen is $thislen\n",0,$section); $maxlen = $thislen if ($maxlen < $thislen); if ($pbuf[$i] =~ /\n$/) { # lines with eol should just to be marked $pbuf[$i] =~ s/^/## /; } else { # If "state:" line has no eol, these will be joined. Get rid # of the indent. # First, find the previous "state:" line # $j = $i-1; # while (!&is_stateline($pbuf[$j]) && ($pbuf[$j] !~ /^\s*case/) && $j > 0) { # $j--; # } # # Check to see whether it has an eol # if ($pbuf[$j] !~ /.*\n$/) { # # Get rid of indent # $pbuf[$i] =~ s/^$level4indent/ /; # } # Tag with length (will be removed on 2nd pass) $pbuf[$i] =~ s/^/#$thislen /; } } else { &debug("\nIt's an assignment line\n",0,$section); } &debug("\n1st pass modified line is:\n",0,$section); &debug("\"$pbuf[$i]\"",0,$section); $i--; } &debug("\nBefore 2nd pass:\n",0,$section); &debug(join("",@pbuf),0,$section); # 2nd pass: go forward through the case section of the buffer and make the # appropriate mods $maxlen++; &debug("maxlen is $maxlen\n",0,$section); # $i is from above! $i++; # go to next after case statement while ($i <= $#pbuf) { &debug("\n2nd pass line is:\n",0,$section); &debug("\"$pbuf[$i]\"",0,$section); if ($pbuf[$i] =~ /^#/) { # statement line of some sort if ($pbuf[$i] =~ s/^#(\d+) //) { # this is a line without a begin $pad = " " x ($maxlen - $1); # calculate pad for later lines } else { $pbuf[$i] =~ s/^## //; #$pad = ""; $pad = " " x $maxlen; } &debug("taglen is $1 ; pad is \"$pad\"\n",0,$section); &debug("modified statement line is \"$pbuf[$i]\"\n",0,$section); } else { $pbuf[$i] =~ s/^\s*/$pad/; &debug("modified assignment line is \n\"$pbuf[$i]\"\n",0,$section); &debug("last 2 lines: \n" . $pbuf[$i-1] . $pbuf[$i],0,$section); } $i++; } } #push(@pbuf,$indentstring x $indent . "$string") unless ($skip); # Handle indent in a way that makes regexp's work eval "\$myindent = \"$indentstring\" x $indent"; $string = $myindent . $string; push(@pbuf,$string) unless ($skip); } sub print_output { # Dump print buffer and warnings for ($i=0; $i<=$#pbuf; $i++) { #print "$i : $pbuf[$i]"; print $pbuf[$i]; print $warnings[$i]; print $assertions[$i]; } } sub is_stateline { my($line) = @_; $line =~ s/\s*\/\/.*$//; # get rid of comments return ($line =~ /$nextstatevar\[\S+\]\s*:/) || ($line =~ /$statevar\[\S+\]\s*:/) || ($line =~ /\S+\s*:/ && ! ($line =~ /\d:\d/)) || ($line =~ /^\s*default\s*:/) ; } sub process_options { # Debug stuff &debug("orig_argv: " . join(" ",@orig_argv),0,"be_cmd"); &debug("ARGV: " . join(" ",@ARGV),0,"be_cmd"); use Getopt::Long; # Default non-null options $encoding = "heros"; $statevar = "state"; $nextstatevar = "nextstate"; $statenamevar = "statename"; $warnout = "both"; $simcode = 1; $indentstring = " "; # Process options die unless GetOptions( "help" => \$help, "verbose!" => \$verbose, "debug=s" => \@debug_sections, "encoding=s" => \$encoding, "simcode!" => \$simcode, "maxbits=s" => \$maxbits, "statevar=s" => \$statevar, "nextstatevar=s" => \$nextstatevar, "statenamevar=s" => \$statenamevar, "warnout=s" => \$warnout, "nowarn=s" => \@nowarns, "terse!" => \$terse, "sunburst!" => \$terse, "indentstring=s" => \$indentstring, "version!" => \$version, "addversion!" => \$addversion, ); if ($help) { &show_help; exit; } if ($version) { print "$me version $scriptversion\n"; exit; } # Turn debug array into a hash foreach $section (@debug_sections) { $debug_sections{$section} = 1; } # Turn nowarn array into a hash foreach $nowarn (@nowarns) { $nowarns{$nowarn} = 1; } # Create reserved words hash %reserved_words = ( $statevar => 1, $nextstatevar => 1, $statenamevar => 1, ); } sub show_help { print STDERR "Syntax: $me [options] options: -help Show this help -verbose Be Chatty -debug section Turn on debug messages for section <section> (Can be used multiple times) -encoding <encoding> Use encoding <encoding> -simcode Add code to add ascii state names in simulation -maxbits <value> For heros encoding, set upper limit of search range to <value> -statevar <name> Use <name> instead of \"state\" for the state variable -nextstatevar <name> Use <name> instead of \"nextstate\" for the nextstate variable -statenamevar <name> Use <name> instead of \"statename\" for the ASCII state name variable -warnout <output> Control warning output. <output> = stdout | stderr | both -nowarn <number> Suppress warning number <number> (Can be used multiple times) -version Just echo version and exit -addversion Add version to output as a comment -terse/-sunburst Use Sunburst Design (Cliff Cummings) output format Note: boolean switches accept the \"no\" syntax to turn them off (So, -nosimcode turns off simcode) For details on recognized attributes, use \"-help -verbose\" \n"; if ($verbose) { &set_myattributes; print STDERR "Recognized attributes:\n"; foreach $entry (sort keys %myattributes) { $string = $myattributes{$entry}; unless ($string =~ /^</) { # Skip entries marked with <> # Massage the output appearance #print "$entry:\n\t$string\n"; foreach $array ("globals", "state", "transition") { $entry =~ s/^($array){"([^"]+)"}{/\1 > \2 > /; } #$entry =~ s/"//g; $entry =~ s/}{/ /g; $entry =~ s/[{}]//g; $entry =~ s/"value"/<value>/; $entry =~ s/"vis"/<vis>/; $entry =~ s/"type"/<type>/; $entry =~ s/ "attributes"//; $entry =~ s/ machine / state machine /; $entry =~ s/\*(\S+)\*/\$\1\$/g; $string =~ s/\*(\S+)\*/\$\1\$/g; print STDERR "$entry:\n\t$string\n\n"; } } } } sub set_myattributes { # Cannot set based on encoding because file must be parsed to figure out # the encoding. Cannot parse twice because it comes from STDIN. #if ($encoding eq "heros" || $encoding eq "onehot") { %myattributes = ( 'globals{"machine"}{"name"}{"value"}' => 'fsm module name', 'globals{"machine"}{"clock"}{"value"}' => 'clock signal', 'globals{"machine"}{"clock"}{"type"}' => 'clock signal type ("posedge", "negedge")', 'globals{"machine"}{"reset_signal"}{"value"}' => 'reset signal', 'globals{"machine"}{"reset_signal"}{"type"}' => 'reset signal type ("posedge", "negedge", "positive", "negative")', 'globals{"machine"}{"reset_state"}{"value"}' => 'reset state', 'globals{"machine"}{"reset_state"}{"type"}' => 'reset state type ("allzeros", "allones", "anyvalue")', 'globals{"machine"}{"implied_loopback"}{"value"}' => 'Every state has an implied loopback (Note: overrides default_state_is_x)', 'globals{"machine"}{"default_state_is_x"}{"value"}' => 'If no valid transition occur, set state to x', 'globals{"machine"}{"insert_at_top_of_file"}{"value"}' => 'Text to insert at the top of the file (use \n to get newline)', 'globals{"machine"}{"insert_in_module_declaration"}{"value"}' => 'Text to insert at the top of the module (after module statement) (use \n to get newline)', 'globals{"machine"}{"insert_at_top_of_module"}{"value"}' => 'Text to insert at the top of the module (after module statement) (use \n to get newline)', 'globals{"machine"}{"insert_at_bottom_of_file"}{"value"}' => 'Text to insert at the bottom of the file (use \n to get newline)', 'globals{"machine"}{"insert_at_bottom_of_module"}{"value"}' => 'Text to insert at the bottom of the module (after module statement) (use \n to get newline)', 'globals{"machine"}{"stateout"}{"value"}' => 'Output on which to send out the state vector', 'globals{"machine"}{"nextstateout"}{"value"}' => 'Output on which to send out the nextstate vector', 'globals{"machine"}{"include_at_top_of_file"}{"value"}' => 'File to include (read) at the top of the file (value is file name)', 'globals{"machine"}{"be_cmd"}{"value"}' => 'command to run backend', 'globals{"machine"}{"onehot_pragma"}{"value"}' => 'Override for synopsys parallel_case full_case on onehot case statements', 'globals{"inputs"}{"*input*"}{"value"}' => 'input signal *input*', 'globals{"inputs"}{"*input*"}{"comment"}' => 'Comment for input signal *input*', 'globals{"outputs"}{"*output*"}{"value"}' => 'output signal *output*', 'globals{"outputs"}{"*output*"}{"type"}' => 'output signal type ("reg", "regdp" or "comb")', 'globals{"outputs"}{"*output*"}{"comment"}' => 'Comment for output signal *output*', 'globals{"machine"}{"name"}{"comment"}' => 'FSM name comment', 'globals{"trans"}{"*output*"}{"value"}' => 'output signal *output* on transitions', 'state{"*state*"}{"attributes"}{"*output*"}{"value"}' => 'Value of output signal *state* (if *output* is an output)', 'state{"*state*"}{"attributes"}{"vis"}' => '<internal - forces state to be parsed even if no outputs', 'state{"*state*"}{"attributes"}{"comment"}' => 'Comment for state *state*', 'transition{"*transition*"}{"attributes"}{"equation"}{"value"}' => 'Transition equation for transition *transition*', 'transition{"*transition*"}{"attributes"}{"*output*"}{"value"}' => 'Value of output signal *output* (if *output* is an output) in transition *transition*', 'transition{"*transition*"}{"attributes"}{"*output*"}{"type"}' => 'Type of output signal *output* (if *output* is an output, it will be "output") in transition *transition*', 'transition{"*transition*"}{"attributes"}{"priority"}{"value"}' => 'Priority of transition *transition* relative to other transitions FROM that state', 'transition{"*transition*"}{"startState"}' => '<Internal>', 'transition{"*transition*"}{"endState"}' => '<Internal>', 'transition{"*transition*"}{"attributes"}{"graycode"}{"value"}' => 'If set, transition must be gray-coded', 'transition{"*transition*"}{"attributes"}{"graytransition"}{"value"}' => '<Internal - backward compatibility>', 'transition{"*transition*"}{"attributes"}{"comment"}' => 'Comment for transition *transition*', ); #} else { # die "Can't do set_myattributes for encoding \"$encoding\"\n"; #} } # when doing require or use we must return 1 1
Go to most recent revision | Compare with Previous | Blame | View Log