##################################################################
# 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<length($value); $i++)
{
$char = substr($value,$i,1);
#print "i=$i char = $char \n";
last if ($char eq '1');
}
$numsize = length($value) - $i;
}
my $r_hash = { 'width'=>$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];
}
############################################################
############################################################
############################################################
############################################################
############################################################