/* sigma_rtc.c: Sigma clocks | |
Copyright (c) 2007, Robert M. Supnik | |
Permission is hereby granted, free of charge, to any person obtaining a | |
copy of this software and associated documentation files (the "Software"), | |
to deal in the Software without restriction, including without limitation | |
the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
and/or sell copies of the Software, and to permit persons to whom the | |
Software is furnished to do so, subject to the following conditions: | |
The above copyright notice and this permission notice shall be included in | |
all copies or substantial portions of the Software. | |
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER | |
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | |
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
Except as contained in this notice, the name of Robert M Supnik shall not be | |
used in advertising or otherwise to promote the sale, use or other dealings | |
in this Software without prior written authorization from Robert M Supnik. | |
rtc clocks | |
The real-time clock includes an internal scheduler for events which need to | |
be driven at multiples of the clock frequency, such as console and multiplexor | |
polling. Other devices can "register" with the clock module to receive service | |
callbacks at a timed interval. This replaces the standard SimH event queue | |
mechanism for real-time synchronous events. | |
*/ | |
#include "sigma_io_defs.h" | |
#define RTC_HZ_BASE 500 | |
#define RTC_TICKS_DFLT 500 | |
/* Timed events data structures */ | |
uint8 rtc_indx[RTC_NUM_EVNTS]; /* index into rtc_tab */ | |
uint8 rtc_cntr[RTC_NUM_EVNTS]; /* timer ticks left */ | |
uint8 rtc_xtra[RTC_NUM_EVNTS]; /* extra counter */ | |
UNIT *rtc_usrv[RTC_NUM_EVNTS]; /* unit servers */ | |
/* Real-time clock counter frequencies */ | |
uint16 rtc_tps[RTC_NUM_CNTRS] = { | |
RTC_HZ_OFF, RTC_HZ_OFF, RTC_HZ_500, RTC_HZ_500 | |
}; | |
/* Frequency descriptors. The base clock runs at 500Hz. To get submultiples, | |
an event uses a tick counter. If the frequency is not an even submultiple, the | |
event can specify an "extra" counter. Every "extra" ticks of the event counter, | |
the event counter is increased by one. Thus, 60Hz counts as 8-8-9, providing | |
3 clock ticks for every 25 base timer ticks. */ | |
typedef struct { | |
uint32 hz; | |
uint32 cntr_reset; | |
uint32 xtra_reset; | |
} rtcdef_t; | |
static rtcdef_t rtc_tab[RTC_NUM_HZ] = { | |
{ 0, 0, 0 }, | |
{ 500, 1, 0 }, | |
{ 50, 10, 0 }, | |
{ 60, 8, 3 }, | |
{ 100, 5, 0 }, | |
{ 2, 250, 0 }, | |
}; | |
t_stat rtc_svc (UNIT *uptr); | |
t_stat rtc_cntr_svc (UNIT *uptr); | |
t_stat rtc_reset (DEVICE *dptr); | |
t_stat rtc_show_events (FILE *of, UNIT *uptr, int32 val, void *desc); | |
/* Clock data structures | |
rtc_dev RTC device descriptor | |
rtc_unit RTC unit | |
rtc_reg RTC register list | |
*/ | |
UNIT rtc_unit = { UDATA (&rtc_svc, 0, 0), RTC_TICKS_DFLT }; | |
UNIT rtc_cntr_unit[RTC_NUM_CNTRS] = { | |
{ UDATA (&rtc_cntr_svc, 0, 0) }, | |
{ UDATA (&rtc_cntr_svc, 0, 0) }, | |
{ UDATA (&rtc_cntr_svc, 0, 0) }, | |
{ UDATA (&rtc_cntr_svc, 0, 0) } | |
}; | |
REG rtc_reg[] = { | |
{ BRDATA (TPS, rtc_tps, 10, 10, RTC_NUM_CNTRS), REG_HRO }, | |
{ BRDATA (INDX, rtc_indx, 10, 4, RTC_NUM_EVNTS), REG_HRO }, | |
{ BRDATA (CNTR, rtc_cntr, 10, 6, RTC_NUM_EVNTS), REG_HRO }, | |
{ BRDATA (XTRA, rtc_xtra, 10, 6, RTC_NUM_EVNTS), REG_HRO }, | |
{ NULL } | |
}; | |
MTAB rtc_mod[] = { | |
{ MTAB_XTD|MTAB_VDV|MTAB_NMO, RTC_C1, "C1", "C1", | |
&rtc_set_tps, &rtc_show_tps, NULL }, | |
{ MTAB_XTD|MTAB_VDV|MTAB_NMO, RTC_C2, "C2", "C2", | |
&rtc_set_tps, &rtc_show_tps, NULL }, | |
{ MTAB_XTD|MTAB_VDV|MTAB_NMO, RTC_C3, "C3", "C3", | |
&rtc_set_tps, &rtc_show_tps, NULL }, | |
{ MTAB_XTD|MTAB_VDV|MTAB_NMO, RTC_C4, "C4", NULL, | |
NULL, &rtc_show_tps, NULL }, | |
{ MTAB_XTD|MTAB_VDV|MTAB_NMO, 0, "EVENTS", NULL, | |
NULL, &rtc_show_events, NULL }, | |
{ 0 } | |
}; | |
DEVICE rtc_dev = { | |
"RTC", &rtc_unit, rtc_reg, rtc_mod, | |
1, 16, 8, 1, 16, 8, | |
NULL, NULL, &rtc_reset, | |
NULL, NULL, NULL | |
}; | |
/* Master timer service routine */ | |
t_stat rtc_svc (UNIT *uptr) | |
{ | |
uint32 i, idx; | |
int32 t; | |
t_stat st; | |
t = sim_rtcn_calb (RTC_HZ_BASE, TMR_RTC); /* calibrate clock */ | |
sim_activate (uptr, t); /* reactivate unit */ | |
for (i = 0; i < RTC_NUM_EVNTS; i++) { /* loop thru events */ | |
if (rtc_cntr[i] != 0) { /* event active? */ | |
rtc_cntr[i] = rtc_cntr[i] - 1; /* decrement */ | |
if (rtc_cntr[i] == 0) { /* expired? */ | |
idx = rtc_indx[i]; | |
rtc_cntr[i] = rtc_tab[idx].cntr_reset; /* reset counter */ | |
if (rtc_xtra[i] != 0) { /* fudge factor? */ | |
rtc_xtra[i] = rtc_xtra[i] - 1; /* decr fudge cntr */ | |
if (rtc_xtra[i] == 0) { /* expired? */ | |
rtc_cntr[i]++; /* extra tick */ | |
rtc_xtra[i] = rtc_tab[idx].xtra_reset; /* reset fudge cntr */ | |
} /* end fudge = 0 */ | |
} /* end fudge active */ | |
if ((rtc_usrv[i] == NULL) || /* registered? */ | |
(rtc_usrv[i]->action == NULL)) | |
return SCPE_IERR; /* should be */ | |
st = rtc_usrv[i]->action (rtc_usrv[i]); /* callback */ | |
if (st != SCPE_OK) /* error */ | |
return st; | |
} /* end cntr = 0 */ | |
} /* end event active */ | |
} /* end event loop */ | |
return SCPE_OK; | |
} | |
/* Callback for a system timer */ | |
t_stat rtc_cntr_svc (UNIT *uptr) | |
{ | |
uint32 cn = uptr - rtc_cntr_unit; | |
io_sclr_req (INTV (INTG_OVR, cn), 1); /* set cntr intr */ | |
return SCPE_OK; | |
} | |
/* Register a timer */ | |
t_stat rtc_register (uint32 tm, uint32 idx, UNIT *uptr) | |
{ | |
if ((tm >= RTC_NUM_EVNTS) || /* validate params */ | |
(idx >= RTC_NUM_HZ) || | |
(uptr == NULL) || | |
(uptr->action == NULL)) | |
return SCPE_IERR; | |
rtc_usrv[tm] = uptr; | |
rtc_indx[tm] = idx; | |
rtc_cntr[tm] = rtc_tab[idx].cntr_reset; /* init event */ | |
rtc_xtra[tm] = rtc_tab[idx].xtra_reset; | |
return SCPE_OK; | |
} | |
/* Set timer ticks */ | |
t_stat rtc_set_tps (UNIT *uptr, int32 val, char *cptr, void *desc) | |
{ | |
uint32 newval, i; | |
t_stat r; | |
if (val >= RTC_NUM_EVNTS) /* validate params */ | |
return SCPE_IERR; | |
if (cptr == NULL) /* must have arg */ | |
return SCPE_ARG; | |
newval = get_uint (cptr, 10, 10000, &r); | |
if ((r != SCPE_OK) || /* error? */ | |
((newval == 0) && (val >= 2))) /* can't turn off 3,4 */ | |
return SCPE_ARG; | |
for (i = 0; i < RTC_NUM_HZ; i++) { /* loop thru freqs */ | |
if (newval == rtc_tab[i].hz) { /* found freq? */ | |
rtc_tps[val] = i; | |
rtc_indx[val] = i; /* save event vals */ | |
rtc_cntr[val] = rtc_tab[i].cntr_reset; | |
rtc_xtra[val] = rtc_tab[i].xtra_reset; | |
return SCPE_OK; | |
} | |
} | |
return SCPE_ARG; | |
} | |
/* Show timer ticks */ | |
t_stat rtc_show_tps (FILE *of, UNIT *uptr, int32 val, void *desc) | |
{ | |
uint32 idx; | |
if (val >= RTC_NUM_EVNTS) | |
return SCPE_IERR; | |
idx = rtc_tps[val]; /* ptr to clk defs */ | |
if (rtc_tab[idx].hz == 0) | |
fprintf (of, "off\n"); | |
else fprintf (of, "%dHz\n", rtc_tab[idx].hz); | |
return SCPE_OK; | |
} | |
/* Reset routine */ | |
t_stat rtc_reset (DEVICE *dptr) | |
{ | |
uint32 i; | |
sim_rtcn_init (rtc_unit.wait, TMR_RTC); /* init base clock */ | |
sim_activate_abs (&rtc_unit, rtc_unit.wait); /* activate unit */ | |
for (i = 0; i < RTC_NUM_EVNTS; i++) { /* clear counters */ | |
if (i < RTC_NUM_CNTRS) { | |
rtc_cntr[i] = 0; | |
rtc_xtra[i] = 0; | |
rtc_indx[i] = 0; | |
rtc_usrv[i] = NULL; | |
if (rtc_register (i, rtc_tps[i], &rtc_cntr_unit[i]) != SCPE_OK) | |
return SCPE_IERR; | |
} | |
else if ((rtc_usrv[i] != NULL) && | |
(rtc_register (i, rtc_indx[i], rtc_usrv[i]) != SCPE_OK)) | |
return SCPE_IERR; | |
} | |
return SCPE_OK; | |
} | |
/* Show events */ | |
t_stat rtc_show_events (FILE *of, UNIT *uptr, int32 val, void *desc) | |
{ | |
uint32 i; | |
fprintf (of, "Event Status Frequency Ticks Extra\n"); | |
for (i = 0; i < RTC_NUM_EVNTS; i++) { | |
if (rtc_cntr[i]) | |
fprintf (of, " %d on %3dHz %3d %d\n", | |
i, rtc_tab[rtc_indx[i]].hz, rtc_cntr[i], rtc_xtra[i]); | |
else fprintf (of, " %d off\n", i); | |
} | |
return SCPE_OK; | |
} |