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