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

Subversion Repositories eco32

[/] [eco32/] [trunk/] [sim/] [timer.c] - Rev 268

Go to most recent revision | Compare with Previous | Blame | View Log

/*
 * timer.c -- timer simulation
 */
 
 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <setjmp.h>
 
#include "common.h"
#include "console.h"
#include "error.h"
#include "except.h"
#include "cpu.h"
#include "timer.h"
 
 
#define TIME_WRAP	1000000		/* avoid overflow of current time */
 
 
/*
 * data structure for simulation timer
 */
typedef struct timer {
  struct timer *next;
  int alarm;
  void (*callback)(int param);
  int param;
} Timer;
 
 
/*
 * data structure for timer/counter device
 */
typedef struct {
  Word ctrl;
  Word divisor;
  Word counter;
  int irq;
} TimerCounter;
 
 
static Bool debug = false;
 
static Timer *activeTimers = NULL;
static Timer *freeTimers = NULL;
static int currentTime = 0;		/* measured in clock cycles */
 
static TimerCounter timerCounters[NUMBER_TMRCNT];
 
 
Word timerRead(Word addr) {
  int dev, reg;
  Word data;
 
  if (debug) {
    cPrintf("\n**** TIMER READ from 0x%08X", addr);
  }
  dev = addr >> 12;
  if (dev >= NUMBER_TMRCNT) {
    /* illegal device */
    throwException(EXC_BUS_TIMEOUT);
  }
  reg = addr & 0x0FFF;
  if (reg == TIMER_CTRL) {
    data = timerCounters[dev].ctrl;
  } else
  if (reg == TIMER_DIVISOR) {
    data = timerCounters[dev].divisor;
  } else
  if (reg == TIMER_COUNTER) {
    data = timerCounters[dev].counter;
  } else {
    /* illegal register */
    throwException(EXC_BUS_TIMEOUT);
  }
  if (debug) {
    cPrintf(", data = 0x%08X ****\n", data);
  }
  return data;
}
 
 
void timerWrite(Word addr, Word data) {
  int dev, reg;
 
  if (debug) {
    cPrintf("\n**** TIMER WRITE to 0x%08X, data = 0x%08X ****\n",
            addr, data);
  }
  dev = addr >> 12;
  if (dev >= NUMBER_TMRCNT) {
    /* illegal device */
    throwException(EXC_BUS_TIMEOUT);
  }
  reg = addr & 0x0FFF;
  if (reg == TIMER_CTRL) {
    if (data & TIMER_IEN) {
      timerCounters[dev].ctrl |= TIMER_IEN;
    } else {
      timerCounters[dev].ctrl &= ~TIMER_IEN;
    }
    if (data & TIMER_EXP) {
      timerCounters[dev].ctrl |= TIMER_EXP;
    } else {
      timerCounters[dev].ctrl &= ~TIMER_EXP;
    }
    if ((timerCounters[dev].ctrl & TIMER_IEN) != 0 &&
        (timerCounters[dev].ctrl & TIMER_EXP) != 0) {
      /* raise timer interrupt */
      cpuSetInterrupt(timerCounters[dev].irq);
    } else {
      /* lower timer interrupt */
      cpuResetInterrupt(timerCounters[dev].irq);
    }
  } else
  if (reg == TIMER_DIVISOR) {
    timerCounters[dev].divisor = data;
    timerCounters[dev].counter = data;
  } else {
    /* illegal register */
    throwException(EXC_BUS_TIMEOUT);
  }
}
 
 
void timerTick(void) {
  Timer *timer;
  void (*callback)(int param);
  int param;
  int i;
 
  /* increment current time */
  currentTime += CC_PER_INSTR;
  /* avoid overflow */
  if (currentTime >= TIME_WRAP) {
    currentTime -= TIME_WRAP;
    timer = activeTimers;
    while (timer != NULL) {
      timer->alarm -= TIME_WRAP;
      timer = timer->next;
    }
  }
  /* check whether any simulation timer expired */
  while (activeTimers != NULL &&
         currentTime >= activeTimers->alarm) {
    timer = activeTimers;
    activeTimers = timer->next;
    callback = timer->callback;
    param = timer->param;
    timer->next = freeTimers;
    freeTimers = timer;
    (*callback)(param);
  }
  /* decrement counters and check if an interrupt must be raised */
  for (i = 0; i < NUMBER_TMRCNT; i++) {
    if (timerCounters[i].counter <= CC_PER_INSTR) {
      timerCounters[i].counter += timerCounters[i].divisor - CC_PER_INSTR;
      timerCounters[i].ctrl |= TIMER_EXP;
      if (timerCounters[i].ctrl & TIMER_IEN) {
        /* raise timer interrupt */
        cpuSetInterrupt(timerCounters[i].irq);
      }
    } else {
      timerCounters[i].counter -= CC_PER_INSTR;
    }
  }
}
 
 
void timerStart(int usec, void (*callback)(int param), int param) {
  Timer *timer;
  Timer *p;
 
  if (freeTimers == NULL) {
    error("out of timers");
  }
  timer = freeTimers;
  freeTimers = timer->next;
  timer->alarm = currentTime + usec * CC_PER_USEC;
  timer->callback = callback;
  timer->param = param;
  if (activeTimers == NULL ||
      timer->alarm < activeTimers->alarm) {
    /* link into front of active timers queue */
    timer->next = activeTimers;
    activeTimers = timer;
  } else {
    /* link elsewhere into active timers queue */
    p = activeTimers;
    while (p->next != NULL &&
           p->next->alarm <= timer->alarm) {
      p = p->next;
    }
    timer->next = p->next;
    p->next = timer;
  }
}
 
 
void timerReset(void) {
  Timer *timer;
  int i;
 
  cPrintf("Resetting Timer...\n");
  while (activeTimers != NULL) {
    timer = activeTimers;
    activeTimers = timer->next;
    timer->next = freeTimers;
    freeTimers = timer;
  }
  for (i = 0; i < NUMBER_TMRCNT; i++) {
    timerCounters[i].ctrl = 0x00000000;
    timerCounters[i].divisor = 0xFFFFFFFF;
    timerCounters[i].counter = 0xFFFFFFFF;
    timerCounters[i].irq = IRQ_TIMER_0 + i;
  }
}
 
 
void timerInit(void) {
  Timer *timer;
  int i;
 
  for (i = 0; i < NUMBER_TIMERS; i++) {
    timer = malloc(sizeof(Timer));
    if (timer == NULL) {
      error("cannot allocate simulation timers");
    }
    timer->next = freeTimers;
    freeTimers = timer;
  }
  timerReset();
}
 
 
void timerExit(void) {
  Timer *timer;
 
  while (activeTimers != NULL) {
    timer = activeTimers;
    activeTimers = timer->next;
    timer->next = freeTimers;
    freeTimers = timer;
  }
  while (freeTimers != NULL) {
    timer = freeTimers;
    freeTimers = timer->next;
    free(timer);
  }
}
 

Go to most recent revision | Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

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