# See copyright, etc in below POD section.
######################################################################
package SystemC::Vregs::Register;
use SystemC::Vregs::Number;
use SystemC::Vregs::Type;
use Bit::Vector::Overload;
use strict;
use vars qw ($VERSION);
use base qw (SystemC::Vregs::Subclass);
$VERSION = '1.464';
# Fields:
# {name} Field name (Subclass)
# {nor_name} Name w/o leading R_
# {at} File/line number (Subclass)
# {pack} Parent SystemC::Vregs ref
# {typeref} Owning SystemC::Vregs::Type ref
# {addrtext}
# {addr} Beginning SystemC::Vregs::Addr
# {addr_end} Ending SystemC::Vregs::Addr (exclusive < end).
# {spacingtext}
# {range} Range text
# {range_high} SystemC::Vregs::Addr
# {range_low} SystemC::Vregs::Addr
# {range_ents} Number of range entries, SystemC::Vregs::Addr
######################################################################
# Accessors
sub new {
my $class = shift; $class = ref $class if ref $class;
my $self = $class->SUPER::new(@_);
$self->{pack} or die; # Should have been passed as parameter
$self->{pack}{regs}{$self->{name}} = $self;
return $self;
}
sub delete {
my $self = shift;
$self->{pack} or die;
$self->{deleted} = 1; # So can see in any dangling refs.
delete $self->{pack}{regs}{$self->{name}};
}
sub attribute_value {
my $self = shift;
my $attr = shift;
return $self->{attributes}{$attr} if defined $self->{attributes}{$attr};
return $self->{typeref}->attribute_value($attr);
}
######################################################################
sub dewildcard {
my $self = shift;
#print ::Dumper($self);
return if (($self->{name}||"") !~ /\*/);
my $inh = $self->{typeref}->inherits();
print "Reg Wildcard $self->{name} $inh\n" if $SystemC::Vregs::Debug;
(my $regexp = $inh) =~ s/[*]/\.\*/g;
#(my $defbase = $inh) =~ s/[*]/Base/g;
#(my $defname = $defbase) =~ s/^R_//g;
#my $defref = new SystemC::Vregs::Define::Value
# (pack => $self->{pack},
# name => "RA_".$defname,
# rst => $self->{addrtext},
# desc => "Base address from wildcarded register range",
# );
my $gotone;
foreach my $matchref ($self->{pack}->find_reg_regexp("^$regexp")) {
$gotone = 1;
my $newname = SystemC::Vregs::three_way_replace
($self->{name}, $inh, $matchref->{name});
my $typeref = $self->{pack}->find_type($newname) or die;
my $addr = $self->{addrtext} ."|". $matchref->{addrtext};
print " Wildcarded $matchref->{name} to $newname\n" if $SystemC::Vregs::Debug;
my $newref = $self->new (name=>$newname,
pack=>$self->{pack},
addrtext => $addr,
spacingtext => $matchref->{spacingtext},
range => $matchref->{range},
typeref => $typeref,
);
$newref->copy_attributes_from($matchref);
$newref->copy_attributes_from($self);
}
$gotone or $self->warn ("No types matching wildcarded type: ",$self->inherits(),"\n");
$self->delete();
}
######################################################################
sub check_name {
my $regref = shift;
my $field = $regref->{name};
($field =~ /^R_[A-Z][a-zA-Z0-9]*$/)
or $regref->warn ("Register mnemonics must match R_[capitals][alphanumerics]\n");
($field =~ /cnfg[0-9]+$/i) and $regref->warn ("Abbreviate CNFG (Configuration) as Cfg\n"); #Dan Lussier'ism
($regref->{nor_name} = $field) =~ s/^[RC]_//;
}
sub check_addrtext {
my $regref = shift;
my $addrtext = $regref->{addrtext};
$addrtext =~ s/\s+$//;
my $inher_min;
if ($addrtext =~ s/\s*[|]\s*\b(R_[0-9a-zA-Z_]+)\b//) {
my $orin_name = $1;
my $orin_ref = $regref->{pack}->find_reg($orin_name);
if (!$orin_ref) {
$regref->warn ("Address contains | of unknown register: $addrtext\n");
} else {
my $text = $orin_ref->{addrtext};
$text =~ s/-.*//;
$inher_min = $regref->{pack}->addr_text_to_vec($text);
defined $inher_min or $orin_ref->warn("Can't parse address text: $text\n");
}
}
if ($addrtext =~ s/^.*(0x[0-9a-f_]+)\s*-\s*(0x[0-9a-f_]+)\s*[|]\s*//i) {
my $mintext = $1; my $maxtext = $2;
$inher_min = $regref->{pack}->addr_text_to_vec($mintext);
$regref->{addr_end_wildcard} = $regref->{pack}->addr_text_to_vec($maxtext);
}
($addrtext !~ /[|]/) or $regref->warn ("Address cannot contain |'s, or needs complete range: ", $addrtext,"\n");
my $endtext = "";
if ($addrtext =~ s/^(0x[0-9a-f_]+)\s*-\s*(0x[0-9a-f_]+)$/$1/i) {
$endtext = $2;
$regref->{addr_end_user} = $regref->{pack}->addr_text_to_vec($endtext);
}
($addrtext =~ /^0x[0-9a-f_]+$/i)
or $regref->warn ("Strange address format '$addrtext'\n");
$regref->{addr} = $regref->{pack}->addr_text_to_vec($addrtext);
if ($inher_min) {
$regref->{addr}->add( $regref->{addr}, $inher_min, 0);
$regref->{addr_end_user}->add( $regref->{addr_end_user}, $inher_min, 0) if $regref->{addr_end_user};
}
}
sub check_range_spacing {
my $regref = shift;
my $range = $regref->{range};
if (!defined $regref->{spacing}) {
$regref->{spacing} = $regref->{pack}->addr_text_to_vec($regref->{spacingtext});
(defined $regref->{spacing}) or $regref->warn ("Strange spacing value $regref->{spacingtext}\n");
}
my $spacing = $regref->{spacing};
if ($range) {
$range =~ /^\[([^\]:]+):([^\]:]+)\]$/
or $regref->warn ("Strange range $range\n");
my $htext = $1; my $ltext = $2;
$regref->{range_high} = $regref->{pack}->addr_text_to_vec($htext);
$regref->{range_low} = $regref->{pack}->addr_text_to_vec($ltext);
(defined $regref->{range_high}) or $regref->warn ("Can't parse $htext in range $range\n");
(defined $regref->{range_low}) or $regref->warn ("Can't parse $htext in range $range\n");
($regref->{range_low} < $regref->{range_high}) or $regref->warn ("Register range specified in the wrong order. Use regname[high:low] instead.\n");
($spacing->Lexicompare($regref->{pack}->addr_const_vec($regref->{typeref}->numbytes)) >= 0)
or $regref->warn ("Strange address spacing $spacing\n");
}
else { # No range
($spacing->equal($regref->{pack}->addr_const_vec(0)))
or $regref->warn ("Address spacing set to $spacing, but no range specified\n");
$regref->{range_low}
= $regref->{range_high}
= $regref->{pack}->addr_const_vec(0);
}
$regref->{range_ents} = $regref->{pack}->addr_const_vec(1);
$regref->{range_ents}->add( $regref->{range_high}, $regref->{range_ents}, 0);
$regref->{range_ents}->subtract ($regref->{range_ents}, $regref->{range_low}, 0);
$regref->{range_high_p1} = $regref->{pack}->addr_const_vec(1);
$regref->{range_high_p1}->add( $regref->{range_high_p1}, $regref->{range_high}, 0);
}
sub check {
my $regref = shift;
#print ::Dumper($regref);
$regref->check_name();
$regref->check_addrtext();
$regref->check_range_spacing();
# Computes after all checks
$regref->computes();
$regref->check_end();
}
sub remove_if_mismatch {
my $self = shift;
my $test_cb = shift;
if (($self->{typeref} && $self->{typeref}{deleted})
|| $test_cb->($self)) {
$self->delete;
}
}
sub computes {
my $regref = shift;
# Computes rely on check() being correct
{
# addr_end = addr + 4 + ((spacing * (ents - 1)))
my $inc = $regref->{pack}->addr_const_vec(1);
$inc->subtract($regref->{range_ents}, $inc, 0);
$inc->Multiply($regref->{spacing}, $inc);
$regref->{ent_size} = $regref->{pack}->addr_const_vec($regref->{typeref}->numbytes)->Clone();
$inc->add($inc, $regref->{ent_size}, 0);
$inc->add($regref->{addr}, $inc, 0);
$regref->{addr_end} = $inc->Clone();
}
{
# addr_end_inclusive = addr + ((spacing * (ents - 1)))
my $inc = $regref->{pack}->addr_const_vec(1);
$inc->subtract($regref->{range_ents}, $inc, 0);
$inc->Multiply($regref->{spacing}, $inc);
$inc->add($regref->{addr}, $inc, 0);
$regref->{addr_end_inclusive} = $inc->Clone();
# If register is of size 4 spacing 8, allow either XXXX_XXX0 or XXXX_XXX3.
$inc->add($regref->{addr_end_inclusive}, $regref->{ent_size}, 0);
$inc->subtract($inc, $regref->{pack}->addr_const_vec(1), 0);
$regref->{addr_end_inclusive_alt} = $inc->Clone();
# Or XXXX_XXXF
$inc->add($regref->{addr_end_inclusive}, $regref->{spacing}, 0);
$inc->subtract($inc, $regref->{pack}->addr_const_vec(1), 0);
$regref->{addr_end_inclusive_alt2} = $inc->Clone();
}
#print "-A $regref->{addr} AE $regref->{addr_end} SP $regref->{spacing} EC $regref->{addr_end_inclusive}\n";
}
sub check_end {
my $regref = shift;
if ($regref->{addr_end_wildcard}) {
($regref->{addr_end}->Lexicompare($regref->{addr_end_wildcard}) < 0)
or $regref->warn ("Register exceeds upper boundary in declaration: ",
$regref->{addr},"-",$regref->{addr_end}," > ", $regref->{addr_end_wildcard}, "\n");
} else {
(!$regref->{addr_end_user}
|| $regref->{addr_end_user}->equal($regref->{addr_end_inclusive})
|| $regref->{addr_end_user}->equal($regref->{addr_end_inclusive_alt})
|| $regref->{addr_end_user}->equal($regref->{addr_end_inclusive_alt2}))
or $regref->warn ("Ending address specified as '$regref->{addrtext}' does not match calculated end $regref->{addr_end_inclusive} or $regref->{addr_end_inclusive_alt}.\n");
}
}
sub dump {
my $self = shift;
my $fh = shift || \*STDOUT;
my $indent = shift||" ";
print $fh +($indent,"Reg: ",$self->{name},
" addr:",$self->{addrtext}||'',
"\n");
}
######################################################################
#### Package return
1;
__END__
=pod
=head1 NAME
SystemC::Vregs::Register - Register object
=head1 SYNOPSIS
use SystemC::Vregs;
=head1 DESCRIPTION
This package contains a blessed hash object for each register
definition.
=head1 FIELDS
These fields may be specified with the new() function, and accessed
via the self hash: $self->{field}.
=over 4
=item addrtext
Textual form of the address of the register.
=item spacing
Spacing of each register in a range, normally 4 bytes.
=item range
Entry range a ram covers, for example [7:0].
=item name
Name of the object.
=item pack
Reference to the package (SystemC::Vregs) object self is a member of.
=back
=head1 DERIVED FIELDS
These fields are valid only after check() is called.
=over 4
=item addr
Address of the register.
=item addr_end
Ending address of the register.
=back
=head1 METHODS
=over 4
=item new
Creates a new register object.
=item check
Checks the object for errors, and parses to create derived fields.
=back
=head1 DISTRIBUTION
Vregs is part of the L free Verilog software tool
suite. The latest version is available from CPAN and from
L. /www.veripool.org/>.
Copyright 2001-2010 by Wilson Snyder. This package is free software; you
can redistribute it and/or modify it under the terms of either the GNU
Lesser General Public License Version 3 or the Perl Artistic License Version 2.0.
=head1 AUTHORS
Wilson Snyder
=head1 SEE ALSO
L
=cut