################################################################## # Copyright (C) 2000 Greg London All Rights Reserved. # This program is free software; you can redistribute it and/or # modify it under the same terms as Perl itself. ################################################################## require 5; use strict; ################################################################## package Hardware::Verilog::StdLogic; ################################################################## use vars qw ( $VERSION ); $VERSION = '0.03'; ################################################################## use Data::Dumper; # print Dumper($reference); # in Parser.pm, use the following line ot access dumper: # Hardware::Verilog::StdLogic::dumper(\$item{module_instance}); sub dumper { print Dumper(shift(@_)); } sub new { my ($class, $string) = @_; $string = "1'b0" unless(defined($string)); #print "new StdLogic, value is $string \n"; if ((substr($string,0,1) eq '"') and (substr($string, -1, 1) eq '"')) { my $r_hash = { 'width'=>1, 'binary'=>'1' }; bless $r_hash, $class; return $r_hash; } $string =~ s/\s//g; my $numsize=undef; my $value=undef; my $base; if ($string =~ /'/) { ################################################# # it contains a base identifier # such as 3'b001 or 'hf92 or 3'o7 or 8'd9 # split it apart and decode it. # note that numsize is not required. ################################################# ($numsize, $value) = split(/'/, $string); #print "split numsize is $numsize \n"; $numsize = undef unless (length($numsize) > 0); $base = lc ( substr($value, 0, 1) ); $value = substr($value, 1, length($value)-1); if ($base eq 'h') { $value = $class->HexstrToBinstr($value); } elsif ($base eq 'o') { $value = $class->OctstrToBinstr($value); } elsif ($base eq 'd') { $value = $class->DecstrToBinstr($value); } elsif ($base eq 'b') { $value = $class->BinstrToBinstr($value); } } else { ################################################# # no base identifier given # assume number is a raw decimal number. ################################################# $value = $class->DecstrToBinstr($string); } if (defined($numsize)) { ################################################# # binary numbers must be exact widths. # all others get some slack because # 7'h4a # is actually valid, even though hexstrtobinstr will return 8 chars. ################################################# $value =~ /^0*(.*)/; my $msb_string = $1; my $msb_length = length($msb_string); my $total_bits = length($value); if ($msb_length > $numsize) { $class->Error( "specified length is too short for given value. Truncating ($string)." ); $value = substr($value, $total_bits-$numsize, $numsize); } elsif ($numsize > $total_bits) { $value = '0'x($numsize - $total_bits) . $value; } elsif ($total_bits > $numsize) { # do one last trimming for cases when # the value is 3'h2, # since hexstrtobinstr will return a 4 bit value $value =~ /(.{$numsize})$/; $value = $1; } } else { $numsize='u'; my $char; my $i; for($i=0; $i$numsize, 'binary'=>$value }; bless $r_hash, $class; return $r_hash; } ############################################################ sub copy { my ($obj)=@_; my $new = {}; my @keys = keys(%$obj); foreach my $key (@keys) { my $value = $obj->{$key}; $new->{$key} = $value; } my $class = ref($obj); bless $new, $class; return $new; } ############################################################ # assign and return width sub width { my ($obj,$width) = @_; $obj->{'width'} = $width if (@_ > 1); return shift->{'width'}; } ############################################################ # return number in binary format for display # no width indication, no base specifier sub binary { my ($obj,$binary) = @_; $obj->{'binary'} = $binary if (@_ > 1); return shift->{'binary'}; } ############################################################ ############################################################ my %hex_to_bin_char_converter = ( 'x' => 'xxxx', 'X' => 'xxxx', 'z' => 'zzzz', 'Z' => 'zzzz', '0' => '0000', '1' => '0001', '2' => '0010', '3' => '0011', '4' => '0100', '5' => '0101', '6' => '0110', '7' => '0111', '8' => '1000', '9' => '1001', 'a' => '1010', 'A' => '1010', 'b' => '1011', 'B' => '1011', 'c' => '1100', 'C' => '1100', 'd' => '1101', 'D' => '1101', 'e' => '1110', 'E' => '1110', 'f' => '1111', 'F' => '1111', ); #hex_digits : /[xXzZ0-9a-fA-F][xXzZ0-9a-fA-F_]*/ sub HexstrToBinstr { my ($obj,$hex) = @_; my $ret = ''; while(length($hex)) { $hex=~/(.)$/; my $char = $1; $hex =~ s/$char$//; next if ($char eq '_'); $ret = $hex_to_bin_char_converter{$char} . $ret; } return $ret; } my %oct_to_bin_char_converter = ( 'x' => 'xxx', 'X' => 'xxx', 'z' => 'zzz', 'Z' => 'zzz', '0' => '000', '1' => '001', '2' => '010', '3' => '011', '4' => '100', '5' => '101', '6' => '110', '7' => '111', ); #octal_digits : /[xXzZ0-7][xXzZ0-7_]*/ sub OctstrToBinstr { my ($obj, $oct) = @_; my $ret = ''; while(length($oct)) { $oct=~/(.)$/; my $char = $1; $oct =~ s/$char$//; next if ($char eq '_'); $ret = $oct_to_bin_char_converter{$char} . $ret; } return $ret; } #decimal_digits : /[0-9][0-9_]*/ sub DecstrToBinstr { my ($obj, $dec) = @_; my $ret; $dec = lc($dec); if($dec =~ /x/) { $ret = 'x'; } elsif($dec =~ /z/) { $ret = 'z'; } else { $ret = sprintf("%lx",$dec); $ret = $obj->HexstrToBinstr($ret); } return $ret; } #binary_digits : /[xXzZ01][xXzZ01_]*/ sub BinstrToBinstr { my ($obj, $bin) = @_; my $ret = ''; $bin =~ s/_//g; $ret = lc ($bin); return $ret; } ############################################################ sub Error { my ($obj, $string) = @_; print "\n\n\tERROR: $string \n\n"; return } ############################################################ sub dump { my ($obj)=@_; print "vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv\n"; my @keys = keys(%$obj); @keys = sort(@keys); foreach my $key (@keys) { my $value = $obj->{$key}; unless(defined($value)) {$value = 'undefined';} print "$key = $value \n"; } print "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n"; } my %bin_to_hex_block_converter = ( '0000' => '0', '0001' => '1', '0010' => '2', '0011' => '3', '0100' => '4', '0101' => '5', '0110' => '6', '0111' => '7', '1000' => '8', '1001' => '9', '1010' => 'a', '1011' => 'b', '1100' => 'c', '1101' => 'd', '1110' => 'e', '1111' => 'f', ); # return number in hexadecimal format for display # no width indication, no base specifier sub hexadecimal { my ($obj) = @_; my $bin = $obj->binary; # make it a multiple of 4 character long. my $rem = length($bin) % 4; my $replicate=0; if ($rem) {$replicate = 4 - $rem;} $bin = '0'x$replicate . $bin; my $hex = ''; my $block; my $char; while($bin) { $block = substr($bin,-4,4); $bin = substr($bin, 0, length($bin) - 4); if($block=~/x/) {$char = 'x';} elsif($block=~/z/) {$char = 'z';} else { $char = $bin_to_hex_block_converter{$block}; } $hex = $char . $hex; } return $hex; } my %bin_to_oct_block_converter = ( '000' => '0', '001' => '1', '010' => '2', '011' => '3', '100' => '4', '101' => '5', '110' => '6', '111' => '7', ); # return number in octal format for display # no width indication, no base specifier sub octal { my ($obj) = @_; my $bin = $obj->binary; # make it a multiple of 3 character long. my $rem = length($bin) % 3; my $replicate=0; if ($rem) {$replicate = 3 - $rem;} $bin = '0'x$replicate . $bin; my $oct = ''; my $block; my $char; while($bin) { $block = substr($bin,-3,3); $bin = substr($bin, 0, length($bin) - 3); if($block=~/x/) {$char = 'x';} elsif($block=~/z/) {$char = 'z';} else { $char = $bin_to_oct_block_converter{$block}; } $oct = $char . $oct; } return $oct; } # return number in decimal format for display # no width indication, no base specifier sub decimal { my ($obj) = @_; my $hex = $obj->hexadecimal; my $dec_str; if ($hex=~/x/) { $dec_str = 'x'; } elsif ($hex =~ /z/) { $dec_str = 'z'; } else { my $dec_val = hex($hex); $dec_str = sprintf("%d", $dec_val); } return $dec_str; } ############################################################ sub basedhexadecimal { my ($obj) = @_; return $obj->width . "'h" . $obj->hexadecimal ; } sub baseddecimal { my ($obj) = @_; return $obj->width . "'d" . $obj->decimal ; } sub basedoctal { my ($obj) = @_; return $obj->width . "'o" . $obj->octal ; } sub basedbinary { my ($obj) = @_; return $obj->width . "'b" . $obj->binary ; } ############################################################ ############################################################ ############################################################ ############################################################ ############################################################ # return the numeric value of the object, # you will need to check to see if all bits are valid first. sub numeric { my ($obj) = @_; my $hex_str = $obj->hexadecimal; my $num; if ( $hex_str=~ /[zx]/ ) { $num = 0; } else { $num = hex($hex_str); } return $num; } ############################################################ ############################################################ ############################################################ ############################################################ ############################################################ # look at the given width, and fix binary value to match sub trim { my ($obj)=@_; my $width=$obj->width; return if($width eq 'u'); my $binary=$obj->binary; my $length=length($binary); return if($width == $length); if($width > $length) {$obj->binary('0'x($width-$length).$binary);} else {$obj->binary(substr($binary, $length-$width, $width));} } ############################################################ ############################################################ ############################################################ ############################################################ ############################################################ sub unary_reduction_and { my ($obj)=@_; my $ret = $obj->copy; $ret->width(1); my $bin=$ret->binary; if($bin=~/[xz]/) {$ret->binary('x');} elsif($bin=~/0/) {$ret->binary('0');} else {$ret->binary('1');} return $ret; } sub unary_reduction_nand { my ($obj)=@_; my $ret = $obj->copy; $ret->width(1); my $bin=$ret->binary; if($bin=~/[xz]/) {$ret->binary('x');} elsif($bin=~/0/) {$ret->binary('1');} else {$ret->binary('0');} return $ret; } sub unary_reduction_or { my ($obj)=@_; my $ret = $obj->copy; $ret->width(1); my $bin=$ret->binary; if($bin=~/[xz]/) {$ret->binary('x');} elsif($bin=~/1/) {$ret->binary('1');} else {$ret->binary('0');} return $ret; } sub unary_reduction_nor { my ($obj)=@_; my $ret = $obj->copy; $ret->width(1); my $bin=$ret->binary; if($bin=~/[xz]/) {$ret->binary('x');} elsif($bin=~/1/) {$ret->binary('0');} else {$ret->binary('1');} return $ret; } sub unary_reduction_xor { my ($obj)=@_; my $ret = $obj->copy; $ret->width(1); my $bin=$ret->binary; if($bin=~/[xz]/) {$ret->binary('x');} else { my $running = substr($bin,0,1); my $len = length($bin); my $char; for(my $i=1;$i < $len; $i++) { $char = substr($bin,$i,1); if($running eq $char) { $running = '0'; } else { $running = '1'; } } $ret->binary($running); } return $ret; } sub unary_reduction_xnor { my ($obj)=@_; my $ret = $obj->copy; $ret->width(1); my $bin=$ret->binary; if($bin=~/[xz]/) {$ret->binary('x');} else { my $running = substr($bin,0,1); my $len = length($bin); my $char; for(my $i=1;$i < $len; $i++) { $char = substr($bin,$i,1); if($running eq $char) { $running = '1'; } else { $running = '0'; } } $ret->binary($running); } return $ret; } # evaluate the thing as a boolean sub unary_logical_boolean { my ($obj)=@_; my $ret = $obj->copy; $ret->width(1); my $bin=$ret->binary; if($bin=~/[xz]/) {$ret->binary('x');} elsif($bin=~/1/) {$ret->binary('1');} else {$ret->binary('0');} return $ret; } sub unary_logical_negation { my ($obj)=@_; my $ret = $obj->copy; $ret->width(1); my $bin=$ret->binary; if($bin=~/[xz]/) {$ret->binary('x');} elsif($bin=~/1/) {$ret->binary('0');} else {$ret->binary('1');} return $ret; } sub unary_bitwise_negation { my ($obj)=@_; my $ret = $obj->copy; my $bin=$ret->binary; $bin =~ tr/z/x/; $bin =~ tr/01/10/; $ret->binary($bin); return $ret; } # same as two's complement sub unary_minus { my ($obj)=@_; my $ret = $obj->unary_bitwise_negation; $ret = $ret->unary_plus_one; return $ret; } # add '1' to the input sub unary_plus_one { my ($obj)=@_; my $ret = $obj->copy; my $bin=$ret->binary; if($bin=~/[xz]/) {$ret->binary('x');} else { my $final=''; my $carry=1; my $len = length($bin); my $pair; for(my $i=$len-1;$i >= 0 ; $i--) { $pair = substr($bin,$i,1) . $carry; if($pair eq '11') { $final = '0' . $final; $carry = '1'; } elsif( ($pair eq '10') or ($pair eq '01') ) { $final = '1' . $final; $carry = '0'; } else # ($pair eq '00') { $final = '0' . $final; $carry = '0'; } } $ret->binary($final); } return $ret; } sub unary_plus { my ($obj)=@_; my $ret = $obj->copy; return $ret; } ############################################################ ############################################################ ############################################################ ############################################################ ############################################################ my %unary_operator_hash_table = ( '&' => \&unary_reduction_and, '~&' => \&unary_reduction_nand, '|' => \&unary_reduction_or, '~|' => \&unary_reduction_nor, '^' => \&unary_reduction_xor, '~^' => \&unary_reduction_xnor, '^~' => \&unary_reduction_xnor, '+' => \&unary_plus, '-' => \&unary_minus, '!' => \&unary_logical_negation, '~' => \&unary_bitwise_negation, ); ############################################################ ############################################################ ############################################################ ############################################################ ############################################################ sub unary_operator { #print "CALLING unary_operator\n"; my ($obj, $unary_operator) = @_ ; my $call = $unary_operator_hash_table{$unary_operator}; my $ret = &$call($obj); $ret->trim; return $ret; } ############################################################ ############################################################ ############################################################ ############################################################ ############################################################ sub binary_arithmetic_multiply { my ($obj_left, $obj_right)=@_; my $ret = ref($obj_left)->new; if( ($obj_left->width eq 'u') or ($obj_right->width eq 'u') ) { $ret->width('u'); } else { $ret->width( $obj_left->width + $obj_right->width ) }; if ( ($obj_left->binary =~ /[zx]/) or ($obj_right->binary =~ /[zx]/) ) { $ret->binary('x'); } else { my $num = ( $obj_left->numeric * $obj_right->numeric ); my $str = sprintf("%x", $num); $ret->binary($ret->HexstrToBinstr($str)); } return $ret; } sub binary_arithmetic_divide { my ($obj_left, $obj_right)=@_; my $ret = ref($obj_left)->new; if( ($obj_left->width eq 'u') or ($obj_right->width eq 'u') ) { $ret->width('u'); } else { $ret->width( $obj_left->width - $obj_right->width ); if($ret->width < 0) {$ret->width(0);} } if ( ($obj_left->binary =~ /[zx]/) or ($obj_right->binary =~ /[zx]/) ) { $ret->binary('x'); } else { my $num = ( $obj_left->numeric / $obj_right->numeric ); my $int = int ($num); my $str = sprintf("%x", $int); $ret->binary($ret->HexstrToBinstr($str)); } return $ret; } sub binary_arithmetic_add { #print "CALLING binary_arithmetic_add\n"; my ($obj_left, $obj_right)=@_; my $ret = ref($obj_left)->new; my $left_width = $obj_left->width; my $right_width = $obj_right->width; if( ($left_width eq 'u') or ($right_width eq 'u') ) { $ret->width('u'); } else { if ($left_width > $right_width) {$ret->width($left_width);} else {$ret->width($right_width);} } if ( ($obj_left->binary =~ /[zx]/) or ($obj_right->binary =~ /[zx]/) ) { $ret->binary('x'); } else { my $num = ( $obj_left->numeric + $obj_right->numeric ); my $str = sprintf("%x", $num); my $bin = $ret->HexstrToBinstr($str); $ret->binary($bin); $ret->trim; } return $ret; } sub binary_arithmetic_subtract { #print "CALLING binary_arithmetic_subtract\n"; my ($obj_left, $obj_right)=@_; my ($ret,$prep_left,$prep_right) = $obj_left->binary_prep($obj_right); my $neg_right = $prep_right->unary_minus; $ret = $prep_left->binary_arithmetic_add($neg_right); return $ret; } sub binary_arithmetic_modulus { my ($obj_left, $obj_right)=@_; my $ret = ref($obj_left)->new; if( ($obj_left->width eq 'u') or ($obj_right->width eq 'u') ) { $ret->width('u'); } else { $ret->width( $obj_right->width ); } if ( ($obj_left->binary =~ /[zx]/) or ($obj_right->binary =~ /[zx]/) ) { $ret->binary('x'); } else { my $num = ( $obj_left->numeric % $obj_right->numeric ); my $str = sprintf("%x", $num); $ret->binary($ret->HexstrToBinstr($str)); } return $ret; } sub binary_relational_greater_than { my ($obj_left, $obj_right)=@_; my $ret = ref($obj_left)->new; $ret->width(1); if ( ($obj_left->binary =~ /[zx]/) or ($obj_right->binary =~ /[zx]/) ) { $ret->binary('x'); } else { if( $obj_left->numeric > $obj_right->numeric ) { $ret->binary('1'); } else { $ret->binary('0'); } } } sub binary_relational_less_than { my ($obj_left, $obj_right)=@_; my $ret = ref($obj_left)->new; $ret->width(1); if ( ($obj_left->binary =~ /[zx]/) or ($obj_right->binary =~ /[zx]/) ) { $ret->binary('x'); } else { if( $obj_left->numeric < $obj_right->numeric ) { $ret->binary('1'); } else { $ret->binary('0'); } } } sub binary_relational_greater_than_or_equal_to { my ($obj_left, $obj_right)=@_; my $ret = ref($obj_left)->new; $ret->width(1); if ( ($obj_left->binary =~ /[zx]/) or ($obj_right->binary =~ /[zx]/) ) { $ret->binary('x'); } else { if( $obj_left->numeric >= $obj_right->numeric ) { $ret->binary('1'); } else { $ret->binary('0'); } } } sub binary_relational_less_than_or_equal_to { my ($obj_left, $obj_right)=@_; my $ret = ref($obj_left)->new; $ret->width(1); if ( ($obj_left->binary =~ /[zx]/) or ($obj_right->binary =~ /[zx]/) ) { $ret->binary('x'); } else { if( $obj_left->numeric <= $obj_right->numeric ) { $ret->binary('1'); } else { $ret->binary('0'); } } } sub binary_logical_and { my ($obj_left, $obj_right)=@_; my $ret = ref($obj_left)->new; $ret->width(1); my $bool_left = $obj_left->unary_logical_boolean; my $bool_right = $obj_right->unary_logical_boolean; my $pair = $bool_left->binary . $bool_right->binary; if ($pair =~ /[xz]/) {$ret->binary('x');} elsif ($pair eq '11') {$ret->binary('1');} else {$ret->binary('0');} return $ret; } sub binary_logical_or { my ($obj_left, $obj_right)=@_; my $ret = ref($obj_left)->new; $ret->width(1); my $bool_left = $obj_left->unary_logical_boolean; my $bool_right = $obj_right->unary_logical_boolean; my $pair = $bool_left->binary . $bool_right->binary; if ($pair =~ /[xz]/) {$ret->binary('x');} elsif ($pair eq '11') {$ret->binary('1');} else {$ret->binary('0');} return $ret; } sub binary_equality { my ($obj_left, $obj_right)=@_; my ($ret,$prep_left,$prep_right) = $obj_left->binary_prep($obj_right); $ret->width(1); my $bin_left = $prep_left->binary; my $bin_right = $prep_right->binary; if ( ($bin_left=~/[xz]/) or ($bin_right=~/[xz]/) ) {$ret->binary('x');} elsif ($bin_left eq $bin_right) {$ret->binary('1');} else {$ret->binary('0');} return $ret } sub binary_inequality { my ($obj_left, $obj_right)=@_; my ($ret,$prep_left,$prep_right) = $obj_left->binary_prep($obj_right); $ret->width(1); my $bin_left = $prep_left->binary; my $bin_right = $prep_right->binary; if ( ($bin_left=~/[xz]/) or ($bin_right=~/[xz]/) ) {$ret->binary('x');} elsif ($bin_left eq $bin_right) {$ret->binary('0');} else {$ret->binary('1');} return $ret } sub binary_case_equality { my ($obj_left, $obj_right)=@_; my ($ret,$prep_left,$prep_right) = $obj_left->binary_prep($obj_right); $ret->width(1); if ($prep_left->binary eq $prep_right->binary) {$ret->binary('1');} else {$ret->binary('0');} return $ret } sub binary_case_inequality { my ($obj_left, $obj_right)=@_; my ($ret,$prep_left,$prep_right) = $obj_left->binary_prep($obj_right); $ret->width(1); if ($prep_left->binary eq $prep_right->binary) {$ret->binary('0');} else {$ret->binary('1');} return $ret } sub binary_bitwise_and { my ($obj_left, $obj_right)=@_; my ($ret,$prep_left,$prep_right) = $obj_left->binary_prep($obj_right); my $bin_left =$prep_left->binary; my $bin_right=$prep_right->binary; my $pair; my $final=''; my $len = length($bin_left); for(my $i=0; $i<$len ; $i++) { $pair = substr($bin_left ,$i,1) . substr($bin_right,$i,1); if($pair =~ /[xz]/) { $final .= 'x'; } elsif( $pair eq '11' ) { $final .= '1'; } else { $final .= '0'; } } $ret->binary($final); return $ret; } sub binary_bitwise_or { my ($obj_left, $obj_right)=@_; my ($ret,$prep_left,$prep_right) = $obj_left->binary_prep($obj_right); my $bin_left =$prep_left->binary; my $bin_right=$prep_right->binary; my $pair; my $final=''; my $len = length($bin_left); for(my $i=0; $i<$len ; $i++) { $pair = substr($bin_left ,$i,1) . substr($bin_right,$i,1); if($pair =~ /[xz]/) { $final .= 'x'; } elsif( $pair =~ '1' ) { $final .= '1'; } else { $final .= '0'; } } $ret->binary($final); return $ret; } sub binary_bitwise_xor { my ($obj_left, $obj_right)=@_; my ($ret,$prep_left,$prep_right) = $obj_left->binary_prep($obj_right); my $bin_left =$prep_left->binary; my $bin_right=$prep_right->binary; my $pair; my $final=''; my $len = length($bin_left); for(my $i=0; $i<$len ; $i++) { $pair = substr($bin_left ,$i,1) . substr($bin_right,$i,1); if($pair =~ /[xz]/) { $final .= 'x'; } elsif( ($pair eq '01') or ($pair eq '10') ) { $final .= '1'; } else { $final .= '0'; } } $ret->binary($final); return $ret; } sub binary_bitwise_xnor { my ($obj_left, $obj_right)=@_; my ($ret,$prep_left,$prep_right) = $obj_left->binary_prep($obj_right); my $bin_left =$prep_left->binary; my $bin_right=$prep_right->binary; my $pair; my $final=''; my $len = length($bin_left); for(my $i=0; $i<$len ; $i++) { $pair = substr($bin_left ,$i,1) . substr($bin_right,$i,1); if($pair =~ /[xz]/) { $final .= 'x'; } elsif( ($pair eq '11') or ($pair eq '00') ) { $final .= '1'; } else { $final .= '0'; } } $ret->binary($final); return $ret; } sub binary_right_shift { my ($obj_left, $obj_right)=@_; my $ret=$obj_left->copy; if($obj_right->binary =~ /[zx]/) { $ret->binary('x'); } else { unless($obj_left->width eq 'u') { $ret->width($obj_left->width - $obj_right->numeric); $ret->width(0) if($ret->width < 0); } $ret->binary (substr ($obj_left->binary, 0, length($obj_left->binary)-$obj_right->numeric ) ); } return $ret; } sub binary_left_shift { my ($obj_left, $obj_right)=@_; my $ret=$obj_left->copy; if($obj_right->binary =~ /[zx]/) { $ret->binary('x'); } else { unless($obj_left->width eq 'u') { $ret->width($obj_left->width + $obj_right->numeric); } $ret->binary($obj_left->binary . '0'x($obj_right->numeric)); } return $ret; } ############################################################ ############################################################ ############################################################ ############################################################ ############################################################ ################################################### # most all binary operations will call this sub # it will take two objects, and return copies # of those objects, except that it will trim up # the widths of the data so that they match. # i.e. the calling subs shouldn't have to worry # if the widths dont match, or if the width # is undefined. this sub will handle it. ################################################### sub binary_prep { my ($obj_left,$obj_right) = @_; my $left = $obj_left->copy; my $right = $obj_right->copy; my $leftbin=$left->binary; my $leftwidth=$left->width; my $leftlength=length($leftbin); my $rightbin=$right->binary; my $rightwidth=$right->width; my $rightlength=length($rightbin); my $new_obj = $obj_left->copy; ######################################################### # if both undefined widths ######################################################### if ( ($leftwidth eq 'u') and ($rightwidth eq 'u') ) { # make lengths match the longest binary string if($leftlength > $rightlength) { $right->binary('0'x($leftlength-$rightlength) . $rightbin); $new_obj->width($leftlength); } else { $left->binary('0'x($rightlength-$leftlength) . $leftbin); $new_obj->width($rightlength); } } ######################################################### # if left has undefined width (and right is defined width) ######################################################### elsif ($leftwidth eq 'u') { # expand the undefined width (left) to match it. $left->binary('0'x($rightlength-$leftlength) . $leftbin); $left->width($rightwidth); $new_obj->width($rightwidth); } ######################################################### # if right has undefined width (and left is defined width) ######################################################### elsif ($rightwidth eq 'u') { # expand the undefined width (right) to match it. $right->binary('0'x($leftlength-$rightlength) . $rightbin); $right->width($leftwidth); $new_obj->width($leftwidth); } ######################################################### # if both widths defined, and both equal to each other ######################################################### elsif ($rightwidth eq $leftwidth) { $new_obj->width($leftwidth); } ######################################################### # otherwise, widths are defined, but not equal # check for left > right ######################################################### elsif ( $leftlength > $rightlength ) { $right->binary('0'x($leftlength-$rightlength) . $rightbin); $right->width($leftwidth); $new_obj->width($leftwidth); } ######################################################### # widths are defined, and right > left ######################################################### else { $left->binary('0'x($rightlength-$leftlength) . $leftbin); $left->width($rightwidth); $new_obj->width($rightwidth); } my @ret = ( $new_obj, $left, $right ); return @ret; } ############################################################ ############################################################ ############################################################ ############################################################ ############################################################ my %binary_operator_hash_table = ( '*' => \&binary_arithmetic_multiply, '/' => \&binary_arithmetic_divide, '+' => \&binary_arithmetic_add, '-' => \&binary_arithmetic_subtract, '%' => \&binary_arithmetic_modulus, '&&' => \&binary_logical_and, '||' => \&binary_logical_or, '>' => \&binary_relational_greater_than, '<' => \&binary_relational_less_than, '>=' => \&binary_relational_greater_than_or_equal_to, '<=' => \&binary_relational_less_than_or_equal_to, '==' => \&binary_equality, '!=' => \&binary_inequality, '===' => \&binary_case_equality, '!==' => \&binary_case_inequality, '&' => \&binary_bitwise_and, '|' => \&binary_bitwise_or, '^' => \&binary_bitwise_xor, '~^' => \&binary_bitwise_xnor, '^~' => \&binary_bitwise_xnor, '>>' => \&binary_right_shift, '<<' => \&binary_left_shift, ); ############################################################ ############################################################ ############################################################ ############################################################ ############################################################ sub binary_operator { #print "CALLING binary_operator \n"; my ($left, $binary_operator, $right) = @_ ; my $call = $binary_operator_hash_table{$binary_operator}; my $ret = &$call($left, $right); $ret->trim; return $ret; } ############################################################ ############################################################ ############################################################ ############################################################ ############################################################ # the conditional operator in verilog is: # expr1 ? cond1 : expr2 # note, that in verilog, expr2 could be another # conditional operator, which can result in # daisy chained conditionals, such as this: # output = # 8'd0 ? reset : # 8'd1 ? start : # 8'd2 ? state1 : # 8'd3 ? state2 : # 8'd4; sub conditional_operator { my ($expr1, $cond1, $expr2) = @_; my $ret; my $bool = $cond1->unary_logical_boolean; if ($bool->binary eq '1') { $ret = $expr1->copy; } else { $ret = $expr2->copy; } return $ret; } ############################################################ ############################################################ ############################################################ ############################################################ ############################################################ # highest priority priority # unary + - ~ ! xxx # multiply divide modulus * / % 100 # add subtract + - 90 # shift << >> 80 # relation < <= > >= 70 # equality == != === !== 60 # reduction and & ~& 50 # reduction xor ^ ~^ 40 # reduction or | ~| 30 # logical and && 20 # logical or || 10 # conditional ?: xxx # lowest priority # note that unary operators and # conditional (trinary ?:) operators # are handled separate from this level of priority, # therefore they are not included in the hash shown below. my %binary_priority_hash = ( '||' => 10, '&&' => 20, '|' => 30, '~|' => 30, '^' => 40, '~^' => 40, '&' => 50, '~&' => 50, '==' => 60, '!=' => 60, '===' => 60, '!==' => 60, '<' => 70, '<=' => 70, '>' => 70, '>=' => 70, '<<' => 80, '>>' => 80, '+' => 90, '-' => 90, '*' => 100, '/' => 100, '%' =>100, ); ############################################################ ############################################################ ############################################################ ############################################################ ############################################################ # given a chain of constant expressions, # separated by binary operators, # evaluate the result using the correct precedence. # 9 + 49 / -23 * 33 << 3 sub BinaryOperatorChain { # print "\n\n CALLING BinaryOperatorChain \n\n\n"; my ( $obj, @chain_list ) = @_; ############################################################ if(0) { print "\n\n\n DUMPING LIST \n\n\n"; foreach my $item (@chain_list) { if (ref($item)) { $item->dump; } else { print "operator is $item \n"; } } } ############################################################ ############################################################ # go through the chain_list and reduce it one operator at a time, # taking into account operator precedence and left to right # occurence of operators. ############################################################ my $i; my $binary_operator; my $current_priority; my $highest_operator_index; my $highest_priority_so_far; my $left; my $right; my $result; while( @chain_list > 1 ) { #################################################### # look at all the operators and find the one # with the highest priority. # go left to right so that left has higher priority #################################################### $highest_operator_index = 0; $highest_priority_so_far = 0; for($i=1; $i<@chain_list; $i=$i+2) { $binary_operator = $chain_list[$i]; $current_priority = $binary_priority_hash{$binary_operator}; if ($current_priority > $highest_priority_so_far) { $highest_operator_index = $i; } } #################################################### # $highest_operator_index points to the operator in # @chain_list with the highest priority. # take the operator and the two items that surround it, # and reduce it to a single value. # (i.e. three items in chain_list: '4' '+' '2' # are replaced with one item in chain_list: '6' ) #################################################### $left = $chain_list[$highest_operator_index - 1]; $binary_operator = $chain_list[$highest_operator_index ]; $right = $chain_list[$highest_operator_index + 1]; $result = $left->binary_operator($binary_operator, $right); splice(@chain_list, $highest_operator_index-1, 3, ($result)); } return $chain_list[0]; } ############################################################ ############################################################ ############################################################ ############################################################ ############################################################