# ========== Copyright Header Begin ==========================================
# 
# OpenSPARC T2 Processor File: TSB.pm
# Copyright (C) 1995-2007 Sun Microsystems, Inc. All Rights Reserved
# 4150 Network Circle, Santa Clara, California 95054, U.S.A.
#
# * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 
#
# 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; version 2 of the License.
#
# 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, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
# For the avoidance of doubt, and except that if any non-GPL license 
# choice is available it will apply instead, Sun elects to use only 
# the General Public License version 2 (GPLv2) at this time for any 
# software where a choice of GPL license versions is made 
# available with the language indicating that GPLv2 or any later version 
# may be used, or where a choice of which version of the GPL is applied is 
# otherwise unspecified. 
#
# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, 
# CA 95054 USA or visit www.sun.com if you need additional information or 
# have any questions. 
# 
# ========== Copyright Header End ============================================
# -*- perl -*-

package Midas::TSB;
use strict;

use TRELoad 'BitFieldTie';
use Midas::Command;
use Midas::Error;
use Midas::Globals;
use Midas::MMU;

use fields qw(
	      name
	      config_register

	      base
	      sizebits
	      is_split
	      numentries
	      page_size

	      tsblinkname
	      tsblinkobj
	      force_ctx_zero
	      ttefmt
	      way

	      srcline
	      srcfile
	      srclinestop
	      srcfilestop

	      touched
	      length
	     );

###############################################################################

sub new {
  my $this = shift;
  my %args = @_;

  unless (ref $this) {
    $this = fields::new($this);
  }

  $this->{touched} = 0;
  $this->{force_ctx_zero} = undef;
  $this->{page_size} = undef;
  $this->{way} = undef;

  foreach my $key (keys %args) {
    $this->{$key} = $args{$key};
  }

  return $this;
}

###############################################################################

sub new_from_line {
  my $class = shift;
  my $startline = shift;
  my $fh        = shift;
  my $srcfile   = shift;
  my $srcline   = shift;
  my $mmu       = shift;

  my $srcfile_start = $srcfile;
  my $srcline_start = $srcline;

  local ($_);
  $_ = $startline;


  fatal "Badly formatted TSB line=$srcline, file=$srcfile:\n$_", M_TSBSYNTAX
    unless /^\s*MIDAS_TSB\s*(\S+)\s*((0[xX])?([\da-fA-F]+))\s*(.*)\s*$/;

  my $tsbname = lc $1;
  my $register = $2;
  my $rest = $5;

  my $reg_bf = string2bf($register, 64);
  fatal "TSB config register '$register' is not a number at file=$srcfile, line=$srcline\n", M_NOTNUM unless defined $reg_bf;


  my $force_ctx_zero = undef;
  my $page_size = undef;
  my $ttefmt = undef;
  my $way = undef;
  my $tsblinkname;
  if($rest =~ s/force_ctx_zero//i) {
    $force_ctx_zero = 1;
  }
  if($rest =~ s/link\s*=\s*(\S+)//i) {
    $tsblinkname = lc $1;
  }
  if($rest =~ s/page_size\s*=\s*(\d+)//i) {
    $page_size = $1;
  }
  if($rest =~ s/way\s*=\s*(\d+)//i) {
    $way = $1;
  }
  if($rest =~ s/ttefmt\s*=(\S+)//i) {
    $ttefmt = lc $1;
    if($ttefmt ne 'sun4u' and $ttefmt ne 'sun4v') {
      fatal "Illegal ttefmt '$1' on TSB line line=$srcline, file=$srcfile.\n",
	M_TSBSYNTAX;
    }
  }

  my $this = $class->new
    (
     name            => $tsbname,
     tsblinkname     => $tsblinkname,
     config_register => $reg_bf,
     srcfile         => $srcfile_start,
     srcline         => $srcline_start,
     srcfilestop     => $srcfile,
     srclinestop     => $srcline,
    );


  $this->config_from_register($mmu);

  $this->{force_ctx_zero} = $force_ctx_zero if defined $force_ctx_zero;
  $this->{page_size}      = $page_size      if defined $page_size;
  $this->{ttefmt}         = $ttefmt         if defined $ttefmt;
  $this->{way}            = hex($way)       if defined $way;

  if($this->{is_split} and not defined $this->{way}) {
    fatal "TSB '$tsbname' is split but does not\n". 
      "  define way!\n" .
      "  line=$srcline, file=$srcfile.\n", M_TSBSYNTAX;
  }

  if(defined $this->{way} and not ($this->{way} == 0 or $this->{way} == 1)) {
    fatal "TSB '$tsbname' defines way that isn't\n".
      "  zero or one!\n".
      "  line=$srcline, file=$srcfile.\n", M_TSBSYNTAX;
  }

  if($this->{is_split} and $this->{way} == 1) {
    my $newBase = BitFieldTie->new($PASIZE, $this->{base});
    my $offset = $this->{numentries} * 16; # 16 bytes per entry
    $newBase->add($offset);
    $this->{base} = $newBase;
  }


  return $this;

}

###############################################################################

sub config_from_register {
  my $this = shift;
  my $mmu  = shift;

  my $base = BitFieldTie->new($PASIZE, $this->{config_register});
  $base->store(12, 0, 0); # zero out bits 12:0;

  $this->{base} = $base;
  $this->{sizebits} = $this->{config_register}->extract(3,0);
  $this->{is_split} = $this->{config_register}->extract(12);
  $this->{numentries} = 512 * (1 << $this->{sizebits});

  my $base_lo = 13 + $this->{sizebits};
  if($base_lo > 13) {
    my $overlap = $base->extract($base_lo, 13);

    if($overlap) {
      if($CONFIG{allow_misaligned_tsb_base}) {
	$base->store($base_lo, 13, 0);
      } else {
	fatal "TSB '$this->{name}' has base $base, which\n".
	  "   is misaligned for size=$this->{sizebits}\n" .
	  "   Illegal unless -allow_misalgined_tsb_base is defined.\n" .
	  "   line=$this->{srcline}, file=$this->{srcfile}\n",
	    M_TSBSYNTAX;
      }
    }

  }
}

###############################################################################

sub get_ttefmt {
  my $this = shift;

  return $this->{ttefmt} if defined $this->{ttefmt};
  return $CONFIG{ttefmt};
}

###############################################################################

sub touch {
  my $this = shift;
  $this->{touched} = 1;

  if(defined $this->{tsblinkobj}) {
    $this->{tsblinkobj}->touch();
  }
}

###############################################################################

sub is_touched {
  my $this = shift;
  return $this->{touched} || $CONFIG{gen_all_tsbs};
}

###############################################################################

sub is_force_ctx_zero {
  my $this = shift;
  return $this->{force_ctx_zero};
}

###############################################################################

sub get_tsblinkname {
  my $this = shift;
  return $this->{tsblinkname};
}

###############################################################################

sub set_tsblinkobj {
  my $this = shift;
  my $obj  = shift;
  $this->{tsblinkobj} = $obj;
}

###############################################################################

sub page_size {
  my $this = shift;
  return $this->{page_size};
}

###############################################################################

sub write_to_goldfinger_file {
  my $this = shift;
  my $ofh  = shift;
  my $indent = shift;

  $indent = '' unless defined $indent;

  print $ofh "${indent}TSB $this->{name}\n";
  print $ofh "${indent}  src_file    = \"$this->{srcfile}\";\n";
  print $ofh "${indent}  src_line    = $this->{srcline};\n";
  print $ofh "${indent}  start_addr  = 0x$this->{base};\n";
  print $ofh "${indent}  size_bits   = $this->{sizebits};\n";
  print $ofh "${indent}  split       = $this->{is_split};\n";
  print $ofh "${indent}  num_entries = $this->{numentries};\n";
  print $ofh "${indent}  link_area   = $this->{tsblinkname};\n"
    if defined $this->{tsblinkname};

  print $ofh "${indent}END TSB\n";
  print $ofh "\n";
}

###############################################################################


###############################################################################
###############################################################################

package Midas::TSBLink;
use strict;

use TRELoad 'BitFieldTie';
use Midas::Command;
use Midas::Error;
use Midas::Globals;

use fields qw(
	      name

	      base
	      touched
	      length

	      srcline
	      srcfile
	      srclinestop
	      srcfilestop

	     );

###############################################################################

sub new {
  my $this = shift;
  my %args = @_;

  unless (ref $this) {
    $this = fields::new($this);
  }

  $this->{touched} = 0;

  foreach my $key (keys %args) {
    $this->{$key} = $args{$key};
  }

  return $this;
}

###############################################################################

sub new_from_line {
  my $class = shift;
  my $startline = shift;
  my $fh        = shift;
  my $srcfile   = shift;
  my $srcline   = shift;
  my $mmu       = shift;

  my $srcfile_start = $srcfile;
  my $srcline_start = $srcline;

  local ($_);
  $_ = $startline;


  while($_ =~ /\\$/) {
    $_ =~ s/\\\n$/ /;
    $_ .= <$fh>;
    $srcline++;
  }


  fatal "Badly formatted TSB_LINK line=$srcline, file=$srcfile:\n$_",
    M_TSBSYNTAX
    unless /^\s*MIDAS_TSB_LINK\s*(\S+)\s*((0[xX])?([\da-fA-F]+))\s*$/;

  my $tsblinkname = lc $1;
  my $addr = $2;

  my $addr_bf = string2bf($addr, $PASIZE);
  fatal "TSB_LINK addr '$addr' is not a number at ".
    "file=$srcfile, line=$srcline\n", M_NOTNUM unless defined $addr_bf;

  my $this = Midas::TSBLink->new
    (
     name         => $tsblinkname,
     base         => $addr_bf,
     srcfile      => $srcfile_start,
     srcline      => $srcline_start,
     srcfilestop  => $srcfile,
     srclinestop  => $srcline,
    );


  return $this;
}

###############################################################################

sub touch {
  my $this = shift;
  $this->{touched} = 1;
}

###############################################################################

sub is_touched {
  my $this = shift;
  return $this->{touched} || $CONFIG{gen_all_tsbs};
}

###############################################################################

sub set_len {
  my $this = shift;
  my $length_bf = shift;

  $this->{length} = $length_bf;
}

###############################################################################

sub write_to_goldfinger_file {
  my $this = shift;
  my $ofh  = shift;
  my $indent = shift;

  $indent = '' unless defined $indent;

  print $ofh "${indent}TSB_LINK $this->{name}\n";
  print $ofh "${indent}  src_file    = \"$this->{srcfile}\";\n";
  print $ofh "${indent}  src_line    = $this->{srcline};\n";
  print $ofh "${indent}  start_addr  = 0x$this->{base};\n";
  print $ofh "${indent}END TSB_LINK\n";
  print $ofh "\n";
}

###############################################################################

###############################################################################
1;
