OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

[/] [openrisc/] [tags/] [gnu-src/] [newlib-1.18.0/] [newlib-1.18.0-or32-1.0rc1/] [newlib/] [libc/] [machine/] [spu/] [spu_timer_slih.c] - Diff between revs 207 and 345

Only display areas with differences | Details | Blame | View Log

Rev 207 Rev 345
/*
/*
(C) Copyright IBM Corp. 2008
(C) Copyright IBM Corp. 2008
 
 
All rights reserved.
All rights reserved.
 
 
Redistribution and use in source and binary forms, with or without
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
modification, are permitted provided that the following conditions are met:
 
 
* Redistributions of source code must retain the above copyright notice,
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
documentation and/or other materials provided with the distribution.
* Neither the name of IBM nor the names of its contributors may be
* Neither the name of IBM nor the names of its contributors may be
used to endorse or promote products derived from this software without
used to endorse or promote products derived from this software without
specific prior written permission.
specific prior written permission.
 
 
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
POSSIBILITY OF SUCH DAMAGE.
*/
*/
 
 
/* Second Level Interrupt handler and related services for SPU timers.  */
/* Second Level Interrupt handler and related services for SPU timers.  */
#include "spu_timer_internal.h"
#include "spu_timer_internal.h"
/* Resets decrementer to the specified value. Also updates software timebase
/* Resets decrementer to the specified value. Also updates software timebase
   to account for the time between the last decrementer reset and now. There
   to account for the time between the last decrementer reset and now. There
   are two cases:
   are two cases:
    * Called by application to start a new timer.
    * Called by application to start a new timer.
    * Called by spu_clock to active the next timer.
    * Called by spu_clock to active the next timer.
   In both cases, the amount of time is the current interval timeout minus the
   In both cases, the amount of time is the current interval timeout minus the
   current decrementer value.  */
   current decrementer value.  */
void
void
__reset_spu_decr (int val)
__reset_spu_decr (int val)
{
{
 
 
  /* The interrupt occurs when the msb goes from 0 to 1 or when the decrementer
  /* The interrupt occurs when the msb goes from 0 to 1 or when the decrementer
     goes from 0 to -1.  To be precisely accurate we should set the timer to
     goes from 0 to -1.  To be precisely accurate we should set the timer to
     the intverval -1, unless the interval passed in is 0 in which case it
     the intverval -1, unless the interval passed in is 0 in which case it
     should be left at 0.  */
     should be left at 0.  */
  int enable_val = (__likely (val)) ? val - 1 : 0;
  int enable_val = (__likely (val)) ? val - 1 : 0;
 
 
  /* Decrementer must be stopped before writing it - minimize the time
  /* Decrementer must be stopped before writing it - minimize the time
     stopped.  */
     stopped.  */
  unsigned mask = __disable_spu_decr ();
  unsigned mask = __disable_spu_decr ();
 
 
  /* Perform tb correction before resettting the decrementer. the corrected
  /* Perform tb correction before resettting the decrementer. the corrected
     value is the current timeout value minus the current decrementer value.
     value is the current timeout value minus the current decrementer value.
     Occasionally the read returns 0 - a second read will clear this
     Occasionally the read returns 0 - a second read will clear this
     condition.  */
     condition.  */
  spu_readch (SPU_RdDec);
  spu_readch (SPU_RdDec);
  int decval = spu_readch (SPU_RdDec);
  int decval = spu_readch (SPU_RdDec);
  /* Restart decrementer with next timeout val.  */
  /* Restart decrementer with next timeout val.  */
  __enable_spu_decr (enable_val, mask);
  __enable_spu_decr (enable_val, mask);
 
 
  /* Update the timebase values before enabling for interrupts.  */
  /* Update the timebase values before enabling for interrupts.  */
  __spu_tb_val += __spu_tb_timeout - decval;
  __spu_tb_val += __spu_tb_timeout - decval;
  __spu_tb_timeout = enable_val;
  __spu_tb_timeout = enable_val;
}
}
 
 
/* Update software timebase and timeout value for the 'next to expire' timer.
/* Update software timebase and timeout value for the 'next to expire' timer.
   Called when starting a new timer so the timer list will have timeouts
   Called when starting a new timer so the timer list will have timeouts
   relative to the current time.  */
   relative to the current time.  */
static inline void
static inline void
__update_spu_tb_val (void)
__update_spu_tb_val (void)
{
{
  int elapsed = __spu_tb_timeout - spu_readch (SPU_RdDec);
  int elapsed = __spu_tb_timeout - spu_readch (SPU_RdDec);
#ifdef SPU_TIMER_DEBUG
#ifdef SPU_TIMER_DEBUG
  if (elapsed < 0)
  if (elapsed < 0)
    ABORT ();
    ABORT ();
#endif
#endif
  __spu_tb_val += elapsed;
  __spu_tb_val += elapsed;
 
 
  /* Adjust the timeout for the timer next to expire. Note this could cause
  /* Adjust the timeout for the timer next to expire. Note this could cause
     the timeout to go negative, if it was just about to expire when we called
     the timeout to go negative, if it was just about to expire when we called
     spu_timer_start.  This is OK, since this can happen any time interrupts
     spu_timer_start.  This is OK, since this can happen any time interrupts
     are disabled. We just schedule an immediate timeout in this case.  */
     are disabled. We just schedule an immediate timeout in this case.  */
  if (__spu_timers_active)
  if (__spu_timers_active)
    {
    {
      __spu_timers_active->tmout -= elapsed;
      __spu_timers_active->tmout -= elapsed;
      if (__spu_timers_active->tmout < 0)
      if (__spu_timers_active->tmout < 0)
        __spu_timers_active->tmout = 0;
        __spu_timers_active->tmout = 0;
    }
    }
}
}
 
 
/* Add an allocated timer to the active list. The active list is sorted by
/* Add an allocated timer to the active list. The active list is sorted by
   timeout value. The timer at the head of the list is the timer that will
   timeout value. The timer at the head of the list is the timer that will
   expire next.  The rest of the timers have a timeout value that is relative
   expire next.  The rest of the timers have a timeout value that is relative
   to the timer ahead of it on the list.  This relative value is determined
   to the timer ahead of it on the list.  This relative value is determined
   here, when the timer is added to the active list. When its position in the
   here, when the timer is added to the active list. When its position in the
   list is found, the timer's timeout value is set to its interval minus the
   list is found, the timer's timeout value is set to its interval minus the
   sum of all the timeout values ahead of it.  The timeout value for the timer
   sum of all the timeout values ahead of it.  The timeout value for the timer
   following the newly added timer is then adjusted to a new relative value. If
   following the newly added timer is then adjusted to a new relative value. If
   the newly added timer is at the head of the list, the decrementer is reset.
   the newly added timer is at the head of the list, the decrementer is reset.
   This function is called by SLIH to restart multiple timers (reset == 0) or
   This function is called by SLIH to restart multiple timers (reset == 0) or
   by spu_timer_start() to start a single timer (reset == 1).  */
   by spu_timer_start() to start a single timer (reset == 1).  */
void
void
__spu_timer_start (int id, int reset)
__spu_timer_start (int id, int reset)
{
{
  spu_timer_t *t;
  spu_timer_t *t;
  spu_timer_t **pn;
  spu_timer_t **pn;
  spu_timer_t *start = &__spu_timers[id];
  spu_timer_t *start = &__spu_timers[id];
  unsigned tmout_time = 0;
  unsigned tmout_time = 0;
  unsigned my_intvl = start->intvl;
  unsigned my_intvl = start->intvl;
  unsigned was_enabled = spu_readch (SPU_RdMachStat) & 0x1;
  unsigned was_enabled = spu_readch (SPU_RdMachStat) & 0x1;
 
 
  spu_idisable ();
  spu_idisable ();
 
 
  t = __spu_timers_active;
  t = __spu_timers_active;
  pn = &__spu_timers_active;
  pn = &__spu_timers_active;
 
 
  /* If the active list is empty, just add the timer with the timeout set to
  /* If the active list is empty, just add the timer with the timeout set to
     the interval. Otherwise find the place in the list for the timer, setting
     the interval. Otherwise find the place in the list for the timer, setting
     its timeout to its interval minus the sum of timeouts ahead of it.  */
     its timeout to its interval minus the sum of timeouts ahead of it.  */
  start->state = SPU_TIMER_ACTIVE;
  start->state = SPU_TIMER_ACTIVE;
  if (__likely (!t))
  if (__likely (!t))
    {
    {
      __spu_timers_active = start;
      __spu_timers_active = start;
      start->next = NULL;
      start->next = NULL;
      start->tmout = my_intvl;
      start->tmout = my_intvl;
    }
    }
  else
  else
    {
    {
 
 
      /* Update swtb and timeout val of the next timer, so all times are
      /* Update swtb and timeout val of the next timer, so all times are
         relative to now.  */
         relative to now.  */
      if (reset)
      if (reset)
        __update_spu_tb_val ();
        __update_spu_tb_val ();
 
 
      while (t && (my_intvl >= (tmout_time + t->tmout)))
      while (t && (my_intvl >= (tmout_time + t->tmout)))
        {
        {
          tmout_time += t->tmout;
          tmout_time += t->tmout;
          pn = &t->next;;
          pn = &t->next;;
          t = t->next;
          t = t->next;
        }
        }
      start->next = t;
      start->next = t;
      start->tmout = my_intvl - tmout_time;
      start->tmout = my_intvl - tmout_time;
      *pn = start;
      *pn = start;
 
 
      /* Adjust timeout for timer after us.  */
      /* Adjust timeout for timer after us.  */
      if (t)
      if (t)
        t->tmout -= start->tmout;
        t->tmout -= start->tmout;
    }
    }
 
 
  if (reset && (__spu_timers_active == start))
  if (reset && (__spu_timers_active == start))
    __reset_spu_decr (__spu_timers_active->tmout);
    __reset_spu_decr (__spu_timers_active->tmout);
 
 
  if (__unlikely (was_enabled))
  if (__unlikely (was_enabled))
    spu_ienable ();
    spu_ienable ();
}
}
 
 
/* SLIH for decrementer.  Manages software timebase and timers.
/* SLIH for decrementer.  Manages software timebase and timers.
   Called by SPU FLIH. Assumes decrementer is still running
   Called by SPU FLIH. Assumes decrementer is still running
   (event not yet acknowledeged).  */
   (event not yet acknowledeged).  */
unsigned int
unsigned int
spu_clock_slih (unsigned status)
spu_clock_slih (unsigned status)
{
{
  int decr_reset_val;
  int decr_reset_val;
  spu_timer_t *active, *handled;
  spu_timer_t *active, *handled;
  unsigned was_enabled = spu_readch (SPU_RdMachStat) & 0x1;
  unsigned was_enabled = spu_readch (SPU_RdMachStat) & 0x1;
 
 
  status &= ~MFC_DECREMENTER_EVENT;
  status &= ~MFC_DECREMENTER_EVENT;
 
 
  spu_idisable ();
  spu_idisable ();
 
 
  /* The decrementer has now expired.  The decrementer event was acknowledged
  /* The decrementer has now expired.  The decrementer event was acknowledged
     in the FLIH but not disabled. The decrementer will continue to run while
     in the FLIH but not disabled. The decrementer will continue to run while
     we're running the clock/timer handler. The software clock keeps running,
     we're running the clock/timer handler. The software clock keeps running,
     and accounts for all the time spent running handlers. Add the current
     and accounts for all the time spent running handlers. Add the current
     timeout to the software timebase and set the timeout to DECR_MAX. This
     timeout to the software timebase and set the timeout to DECR_MAX. This
     allows the "clock read" code to continue to work while we're in here, and
     allows the "clock read" code to continue to work while we're in here, and
     gives us the most possible time to finish before another underflow.  */
     gives us the most possible time to finish before another underflow.  */
  __spu_tb_val += __spu_tb_timeout;
  __spu_tb_val += __spu_tb_timeout;
  __spu_tb_timeout = DECR_MAX;
  __spu_tb_timeout = DECR_MAX;
 
 
  /* For all timers that have the current timeout value, move them from the
  /* For all timers that have the current timeout value, move them from the
     active list to the handled list and call their handlers. Note that the
     active list to the handled list and call their handlers. Note that the
     handled/stopped lists may be manipulated by the handlers if they wish to
     handled/stopped lists may be manipulated by the handlers if they wish to
     stop/free the timers. Note that only the first expired timer will reflect
     stop/free the timers. Note that only the first expired timer will reflect
     the real timeout value; the rest of the timers that had the same timeout
     the real timeout value; the rest of the timers that had the same timeout
     value will have a relative value of zero.  */
     value will have a relative value of zero.  */
  if (__spu_timers_active)
  if (__spu_timers_active)
    {
    {
      __spu_timers_active->tmout = 0;
      __spu_timers_active->tmout = 0;
      while ((active = __spu_timers_active)
      while ((active = __spu_timers_active)
             && (active->tmout <= TIMER_INTERVAL_WINDOW))
             && (active->tmout <= TIMER_INTERVAL_WINDOW))
        {
        {
          __spu_timers_active = active->next;
          __spu_timers_active = active->next;
          active->next = __spu_timers_handled;
          active->next = __spu_timers_handled;
          __spu_timers_handled = active;
          __spu_timers_handled = active;
          active->state = SPU_TIMER_HANDLED;
          active->state = SPU_TIMER_HANDLED;
          (*active->func) (active->id);
          (*active->func) (active->id);
        }
        }
    }
    }
 
 
  /* put the handled timers back on the list and restart decrementer.  */
  /* put the handled timers back on the list and restart decrementer.  */
  while ((handled = __spu_timers_handled) != NULL)
  while ((handled = __spu_timers_handled) != NULL)
    {
    {
      __spu_timers_handled = handled->next;
      __spu_timers_handled = handled->next;
      __spu_timer_start (handled->id, 0);
      __spu_timer_start (handled->id, 0);
    }
    }
 
 
  /* Reset the decrementer before returning. If we have any active timers, we
  /* Reset the decrementer before returning. If we have any active timers, we
     set it to the timeout value for the timer at the head of the list, else
     set it to the timeout value for the timer at the head of the list, else
     the default clock value.  */
     the default clock value.  */
  decr_reset_val = __spu_timers_active ? __spu_timers_active->tmout : CLOCK_START_VALUE;
  decr_reset_val = __spu_timers_active ? __spu_timers_active->tmout : CLOCK_START_VALUE;
 
 
  __reset_spu_decr (decr_reset_val);
  __reset_spu_decr (decr_reset_val);
 
 
  if (__likely (was_enabled))
  if (__likely (was_enabled))
    spu_ienable ();
    spu_ienable ();
 
 
  return status;
  return status;
}
}
 
 

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.