Example #1
0
signed64
hw_event_queue_time (struct hw *me)
{
  return sim_events_time (hw_system (me));
}
Example #2
0
/* Deliver a clock tick to the counter. */
static void
deliver_tx3904tmr_tick (struct hw *me,
			void *data)
{
  struct tx3904tmr *controller = hw_data (me);
  SIM_DESC sd = hw_system (me);
  signed_8 this_ticks = sim_events_time(sd);

  signed_8 warp;
  signed_8 divisor;
  signed_8 quotient, remainder;

  /* compute simulation ticks between last tick and this tick */
  if(controller->last_ticks != 0)
    warp = this_ticks - controller->last_ticks + controller->roundoff_ticks;
  else
    {
      controller->last_ticks = this_ticks; /* initialize */
      warp = controller->roundoff_ticks;
    }

  if(controller->event != NULL)
    hw_event_queue_deschedule(me, controller->event);
  controller->event = NULL;

  /* Check whether the timer ticking is enabled at this moment.  This
     largely a function of the TCE bit, but is also slightly
     mode-dependent. */
  switch((int) GET_TCR_TMODE(controller))
    {
    case 0: /* interval */
      /* do not advance counter if TCE = 0 or if holding at count = CPRA */
      if(GET_TCR_TCE(controller) == 0 ||
	 controller->trr == controller->cpra)
	return;
      break;

    case 1: /* pulse generator */
      /* do not advance counter if TCE = 0 */
      if(GET_TCR_TCE(controller) == 0)
	return;
      break;

    case 2: /* watchdog */
      /* do not advance counter if TCE = 0 and WDIS = 1 */
      if(GET_TCR_TCE(controller) == 0 &&
	 GET_WTMR_WDIS(controller) == 1)
	return;
      break;

    case 3: /* disabled */
      /* regardless of TCE, do not advance counter */
      return;
    }

  /* In any of the above cases that return, a subsequent register
     write will be needed to restart the timer.  A tick event is
     scheduled by any register write, so it is more efficient not to
     reschedule dummy events here. */


  /* find appropriate divisor etc. */ 
  if(GET_TCR_CCS(controller) == 0) /* internal system clock */
    {
      /* apply internal clock divider */
      if(GET_TCR_CCDE(controller)) /* divisor circuit enabled? */
	divisor = controller->clock_ticks * (1 << (1 + GET_CCDR_CDR(controller)));
      else
	divisor = controller->clock_ticks;
    }
  else
    {
      divisor = controller->ext_ticks;
    }

  /* how many times to increase counter? */
  quotient = warp / divisor;
  remainder = warp % divisor;

  /* NOTE: If the event rescheduling code works properly, the quotient
     should never be larger than 1.  That is, we should receive events
     here at least as frequently as the simulated counter is supposed
     to decrement.  So the remainder (-> roundoff_ticks) will slowly
     accumulate, with the quotient == 0.  Once in a while, quotient
     will equal 1. */

  controller->roundoff_ticks = remainder;
  controller->last_ticks = this_ticks;
  while(quotient > 0) /* Is it time to increment counter? */
    {
      /* next 24-bit counter value */
      unsigned_4 next_trr = (controller->trr + 1) % (1 << 24);
      quotient --;
      
      switch((int) GET_TCR_TMODE(controller))
	{
	case 0: /* interval timer mode */
	  {
	    /* Current or next counter value matches CPRA value?  The
	       first case covers counter holding at maximum before
	       reset.  The second case covers normal counting
	       behavior. */
	    if(controller->trr == controller->cpra ||
	       next_trr == controller->cpra)
	      {
		/* likely hold CPRA value */
		if(controller->trr == controller->cpra)
		  next_trr = controller->cpra;

		SET_TISR_TIIS(controller);

		/* Signal an interrupt if it is enabled with TIIE,
		   and if we just arrived at CPRA.  Don't repeatedly
		   interrupt if holding due to TZCE=0 */
		if(GET_ITMR_TIIE(controller) &&
		   next_trr != controller->trr)
		  {
		    hw_port_event(me, INT_PORT, 1);
		  }

		/* Reset counter? */
		if(GET_ITMR_TZCE(controller))
		  {
		    next_trr = 0;
		  }
	      }
	  }
	break;

	case 1: /* pulse generator mode */
	  {
	    /* first trip point */
	    if(next_trr == controller->cpra)
	      {
		/* flip flip-flop & report */
		controller->ff ^= 1;
		hw_port_event(me, FF_PORT, controller->ff);
		SET_TISR_TPIAS(controller);

		/* signal interrupt */
		if(GET_PMGR_TPIAE(controller))
		  {
		    hw_port_event(me, INT_PORT, 1);
		  }

	      }
	    /* second trip point */
	    else if(next_trr == controller->cprb)
	      {
		/* flip flip-flop & report */
		controller->ff ^= 1;
		hw_port_event(me, FF_PORT, controller->ff);
		SET_TISR_TPIBS(controller);

		/* signal interrupt */
		if(GET_PMGR_TPIBE(controller))
		  {
		    hw_port_event(me, INT_PORT, 1);
		  }

		/* clear counter */
		next_trr = 0;
	      }
	  }
	break;

	case 2: /* watchdog timer mode */
	  {
	    /* watchdog timer expiry */
	    if(next_trr == controller->cpra)
	      {
		SET_TISR_TWIS(controller);

		/* signal interrupt */
		if(GET_WTMR_TWIE(controller))
		  {
		    hw_port_event(me, INT_PORT, 1);
		  }

		/* clear counter */
		next_trr = 0;
	      }
	  }
	break;

	case 3: /* disabled */
	default:
	  break;
	}

      /* update counter and report */
      controller->trr = next_trr;
      /* HW_TRACE ((me, "counter trr %ld tisr %lx",
	 (long) controller->trr, (long) controller->tisr)); */
    } /* end quotient loop */

  /* Reschedule a timer event in near future, so we can increment the
     counter again.  Set the event about 75% of divisor time away, so
     we will experience roughly 1.3 events per counter increment. */
  controller->event = hw_event_queue_schedule(me, divisor*3/4, deliver_tx3904tmr_tick, NULL);
}