1 /* sim_timer.c: simulator timer library
3 Copyright (c) 1993-2008, Robert M Supnik
5 Permission is hereby granted, free of charge, to any person obtaining a
6 copy of this software and associated documentation files (the "Software"),
7 to deal in the Software without restriction, including without limitation
8 the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 and/or sell copies of the Software, and to permit persons to whom the
10 Software is furnished to do so, subject to the following conditions:
12 The above copyright notice and this permission notice shall be included in
13 all copies or substantial portions of the Software.
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 Except as contained in this notice, the name of Robert M Supnik shall not be
23 used in advertising or otherwise to promote the sale, use or other dealings
24 in this Software without prior written authorization from Robert M Supnik.
26 27-May-08 RMS Fixed bug in Linux idle routines (from Walter Mueller)
27 18-Jun-07 RMS Modified idle to exclude counted delays
28 22-Mar-07 RMS Added sim_rtcn_init_all
29 17-Oct-06 RMS Added idle support (based on work by Mark Pizzolato)
30 Added throttle support
31 16-Aug-05 RMS Fixed C++ declaration and cast problems
32 02-Jan-04 RMS Split out from SCP
34 This library includes the following routines:
36 sim_timer_init - initialize timing system
37 sim_rtc_init - initialize calibration
38 sim_rtc_calb - calibrate clock
39 sim_timer_init - initialize timing system
40 sim_idle - virtual machine idle
41 sim_os_msec - return elapsed time in msec
42 sim_os_sleep - sleep specified number of seconds
43 sim_os_ms_sleep - sleep specified number of milliseconds
45 The calibration, idle, and throttle routines are OS-independent; the _os_
52 t_bool sim_idle_enab
= FALSE
; /* global flag */
54 static uint32 sim_idle_rate_ms
= 0;
55 static uint32 sim_throt_ms_start
= 0;
56 static uint32 sim_throt_ms_stop
= 0;
57 static uint32 sim_throt_type
= 0;
58 static uint32 sim_throt_val
= 0;
59 static uint32 sim_throt_state
= 0;
60 static int32 sim_throt_wait
= 0;
61 extern int32 sim_interval
, sim_switches
;
63 extern UNIT
*sim_clock_queue
;
65 t_stat
sim_throt_svc (UNIT
*uptr
);
67 UNIT sim_throt_unit
= { UDATA (&sim_throt_svc
, 0, 0) };
69 /* OS-dependent timer and clock routines */
76 #define sys$gettim SYS$GETTIM
80 #include <lib$routines.h>
83 const t_bool rtc_avail
= TRUE
;
87 uint32 quo
, htod
, tod
[2];
90 sys$
gettim (tod
); /* time 0.1usec */
92 /* To convert to msec, must divide a 64b quantity by 10000. This is actually done
93 by dividing the 96b quantity 0'time by 10000, producing 64b of quotient, the
94 high 32b of which are discarded. This can probably be done by a clever multiply...
98 for (i
= 0; i
< 64; i
++) { /* 64b quo */
99 htod
= (htod
<< 1) | ((tod
[1] >> 31) & 1); /* shift divd */
100 tod
[1] = (tod
[1] << 1) | ((tod
[0] >> 31) & 1);
101 tod
[0] = tod
[0] << 1;
102 quo
= quo
<< 1; /* shift quo */
103 if (htod
>= 10000) { /* divd work? */
104 htod
= htod
- 10000; /* subtract */
105 quo
= quo
| 1; /* set quo bit */
111 void sim_os_sleep (unsigned int sec
)
117 uint32
sim_os_ms_sleep_init (void)
120 return 10; /* VAX/VMS is 10ms */
122 return 1; /* Alpha/VMS is 1ms */
126 uint32
sim_os_ms_sleep (unsigned int msec
)
128 uint32 stime
= sim_os_msec ();
130 int32 nsfactor
= -10000;
131 static int32 zero
= 0;
133 lib$
emul (&msec
, &nsfactor
, &zero
, qtime
);
134 sys$
setimr (2, qtime
, 0, 0);
136 return sim_os_msec () - stime
;
141 #elif defined (_WIN32)
145 const t_bool rtc_avail
= TRUE
;
147 uint32
sim_os_msec ()
149 if (sim_idle_rate_ms
) return timeGetTime ();
150 else return GetTickCount ();
153 void sim_os_sleep (unsigned int sec
)
159 void sim_timer_exit (void)
161 timeEndPeriod (sim_idle_rate_ms
);
165 uint32
sim_os_ms_sleep_init (void)
169 if (timeGetDevCaps (&timers
, sizeof (timers
)) != TIMERR_NOERROR
)
171 if ((timers
.wPeriodMin
== 0) || (timers
.wPeriodMin
> SIM_IDLE_MAX
))
173 if (timeBeginPeriod (timers
.wPeriodMin
) != TIMERR_NOERROR
)
175 atexit (sim_timer_exit
);
181 return timers
.wPeriodMin
; /* sim_idle_rate_ms */
184 uint32
sim_os_ms_sleep (unsigned int msec
)
186 uint32 stime
= sim_os_msec();
189 return sim_os_msec () - stime
;
192 /* OS/2 routines, from Bruce Ray */
194 #elif defined (__OS2__)
196 const t_bool rtc_avail
= FALSE
;
198 uint32
sim_os_msec ()
203 void sim_os_sleep (unsigned int sec
)
208 uint32
sim_os_ms_sleep_init (void)
213 uint32
sim_os_ms_sleep (unsigned int msec
)
218 /* Metrowerks CodeWarrior Macintosh routines, from Ben Supnik */
220 #elif defined (__MWERKS__) && defined (macintosh)
223 #include <Mactypes.h>
226 #include <siouxglobals.h>
227 #define NANOS_PER_MILLI 1000000
228 #define MILLIS_PER_SEC 1000
230 const t_bool rtc_avail
= TRUE
;
232 uint32
sim_os_msec (void)
234 unsigned long long micros
;
235 UnsignedWide macMicros
;
236 unsigned long millis
;
238 Microseconds (&macMicros
);
239 micros
= *((unsigned long long *) &macMicros
);
240 millis
= micros
/ 1000LL;
241 return (uint32
) millis
;
244 void sim_os_sleep (unsigned int sec
)
250 uint32
sim_os_ms_sleep_init (void)
255 uint32
sim_os_ms_sleep (unsigned int milliseconds
)
257 uint32 stime
= sim_os_msec ();
258 struct timespec treq
;
260 treq
.tv_sec
= milliseconds
/ MILLIS_PER_SEC
;
261 treq
.tv_nsec
= (milliseconds
% MILLIS_PER_SEC
) * NANOS_PER_MILLI
;
262 (void) nanosleep (&treq
, NULL
);
263 return sim_os_msec () - stime
;
271 #include <sys/time.h>
273 #define NANOS_PER_MILLI 1000000
274 #define MILLIS_PER_SEC 1000
275 #define sleep1Samples 100
277 const t_bool rtc_avail
= TRUE
;
279 uint32
sim_os_msec ()
285 gettimeofday (&cur
, &foo
);
286 msec
= (((uint32
) cur
.tv_sec
) * 1000) + (((uint32
) cur
.tv_usec
) / 1000);
290 void sim_os_sleep (unsigned int sec
)
296 uint32
sim_os_ms_sleep_init (void)
298 #if defined (_POSIX_SOURCE) /* POSIX-compliant */
300 struct timespec treq
;
303 if (clock_getres (CLOCK_REALTIME
, &treq
) != 0)
305 msec
= (treq
.tv_nsec
+ (NANOS_PER_MILLI
- 1)) / NANOS_PER_MILLI
;
306 if (msec
> SIM_IDLE_MAX
) return 0;
311 uint32 i
, t1
, t2
, tot
, tim
;
313 for (i
= 0, tot
= 0; i
< sleep1Samples
; i
++) {
319 tim
= (tot
+ (sleep1Samples
- 1)) / sleep1Samples
;
320 if (tim
== 0) tim
= 1;
321 else if (tim
> SIM_IDLE_MAX
) tim
= 0;
327 uint32
sim_os_ms_sleep (unsigned int milliseconds
)
329 uint32 stime
= sim_os_msec ();
330 struct timespec treq
;
332 treq
.tv_sec
= milliseconds
/ MILLIS_PER_SEC
;
333 treq
.tv_nsec
= (milliseconds
% MILLIS_PER_SEC
) * NANOS_PER_MILLI
;
334 (void) nanosleep (&treq
, NULL
);
335 return sim_os_msec () - stime
;
340 /* OS independent clock calibration package */
342 static int32 rtc_ticks
[SIM_NTIMERS
] = { 0 }; /* ticks */
343 static int32 rtc_hz
[SIM_NTIMERS
] = { 0 }; /* tick rate */
344 static uint32 rtc_rtime
[SIM_NTIMERS
] = { 0 }; /* real time */
345 static uint32 rtc_vtime
[SIM_NTIMERS
] = { 0 }; /* virtual time */
346 static uint32 rtc_nxintv
[SIM_NTIMERS
] = { 0 }; /* next interval */
347 static int32 rtc_based
[SIM_NTIMERS
] = { 0 }; /* base delay */
348 static int32 rtc_currd
[SIM_NTIMERS
] = { 0 }; /* current delay */
349 static int32 rtc_initd
[SIM_NTIMERS
] = { 0 }; /* initial delay */
351 void sim_rtcn_init_all (void)
355 for (i
= 0; i
< SIM_NTIMERS
; i
++) {
356 if (rtc_initd
[i
] != 0) sim_rtcn_init (rtc_initd
[i
], i
);
361 int32
sim_rtcn_init (int32 time
, int32 tmr
)
363 if (time
== 0) time
= 1;
364 if ((tmr
< 0) || (tmr
>= SIM_NTIMERS
)) return time
;
365 rtc_rtime
[tmr
] = sim_os_msec ();
366 rtc_vtime
[tmr
] = rtc_rtime
[tmr
];
367 rtc_nxintv
[tmr
] = 1000;
370 rtc_based
[tmr
] = time
;
371 rtc_currd
[tmr
] = time
;
372 rtc_initd
[tmr
] = time
;
376 int32
sim_rtcn_calb (int32 ticksper
, int32 tmr
)
378 uint32 new_rtime
, delta_rtime
;
381 if ((tmr
< 0) || (tmr
>= SIM_NTIMERS
)) return 10000;
382 rtc_hz
[tmr
] = ticksper
;
383 rtc_ticks
[tmr
] = rtc_ticks
[tmr
] + 1; /* count ticks */
384 if (rtc_ticks
[tmr
] < ticksper
) return rtc_currd
[tmr
]; /* 1 sec yet? */
385 rtc_ticks
[tmr
] = 0; /* reset ticks */
386 if (!rtc_avail
) return rtc_currd
[tmr
]; /* no timer? */
387 new_rtime
= sim_os_msec (); /* wall time */
388 if (new_rtime
< rtc_rtime
[tmr
]) { /* time running backwards? */
389 rtc_rtime
[tmr
] = new_rtime
; /* reset wall time */
390 return rtc_currd
[tmr
]; /* can't calibrate */
392 delta_rtime
= new_rtime
- rtc_rtime
[tmr
]; /* elapsed wtime */
393 rtc_rtime
[tmr
] = new_rtime
; /* adv wall time */
394 rtc_vtime
[tmr
] = rtc_vtime
[tmr
] + 1000; /* adv sim time */
395 if (delta_rtime
> 30000) /* gap too big? */
396 return rtc_initd
[tmr
]; /* can't calibr */
397 if (delta_rtime
== 0) /* gap too small? */
398 rtc_based
[tmr
] = rtc_based
[tmr
] * ticksper
; /* slew wide */
399 else rtc_based
[tmr
] = (int32
) (((double) rtc_based
[tmr
] * (double) rtc_nxintv
[tmr
]) /
400 ((double) delta_rtime
)); /* new base rate */
401 delta_vtime
= rtc_vtime
[tmr
] - rtc_rtime
[tmr
]; /* gap */
402 if (delta_vtime
> SIM_TMAX
) delta_vtime
= SIM_TMAX
; /* limit gap */
403 else if (delta_vtime
< -SIM_TMAX
) delta_vtime
= -SIM_TMAX
;
404 rtc_nxintv
[tmr
] = 1000 + delta_vtime
; /* next wtime */
405 rtc_currd
[tmr
] = (int32
) (((double) rtc_based
[tmr
] * (double) rtc_nxintv
[tmr
]) /
406 1000.0); /* next delay */
407 if (rtc_based
[tmr
] <= 0) rtc_based
[tmr
] = 1; /* never negative or zero! */
408 if (rtc_currd
[tmr
] <= 0) rtc_currd
[tmr
] = 1; /* never negative or zero! */
409 return rtc_currd
[tmr
];
412 /* Prior interfaces - default to timer 0 */
414 int32
sim_rtc_init (int32 time
)
416 return sim_rtcn_init (time
, 0);
419 int32
sim_rtc_calb (int32 ticksper
)
421 return sim_rtcn_calb (ticksper
, 0);
424 /* sim_timer_init - get minimum sleep time available on this host */
426 t_bool
sim_timer_init (void)
428 sim_idle_enab
= FALSE
; /* init idle off */
429 sim_idle_rate_ms
= sim_os_ms_sleep_init (); /* get OS timer rate */
430 return (sim_idle_rate_ms
!= 0);
433 /* sim_idle - idle simulator until next event or for specified interval
436 tmr = calibrated timer to use
438 Must solve the linear equation
440 ms_to_wait = w * ms_per_wait
443 w = ms_to_wait / ms_per_wait
446 t_bool
sim_idle (uint32 tmr
, t_bool sin_cyc
)
448 uint32 cyc_ms
, w_ms
, w_idle
, act_ms
;
451 if ((sim_clock_queue
== NULL
) || /* clock queue empty? */
452 ((sim_clock_queue
->flags
& UNIT_IDLE
) == 0)) { /* event not idle-able? */
453 if (sin_cyc
) sim_interval
= sim_interval
- 1;
456 cyc_ms
= (rtc_currd
[tmr
] * rtc_hz
[tmr
]) / 1000; /* cycles per msec */
457 if ((sim_idle_rate_ms
== 0) || (cyc_ms
== 0)) { /* not possible? */
458 if (sin_cyc
) sim_interval
= sim_interval
- 1;
461 w_ms
= (uint32
) sim_interval
/ cyc_ms
; /* ms to wait */
462 w_idle
= w_ms
/ sim_idle_rate_ms
; /* intervals to wait */
463 if (w_idle
== 0) { /* none? */
464 if (sin_cyc
) sim_interval
= sim_interval
- 1;
467 act_ms
= sim_os_ms_sleep (w_idle
); /* wait */
468 act_cyc
= act_ms
* cyc_ms
;
469 if (sim_interval
> act_cyc
)
470 sim_interval
= sim_interval
- act_cyc
;
471 else sim_interval
= 1;
475 /* Set idling - implicitly disables throttling */
477 t_stat
sim_set_idle (UNIT
*uptr
, int32 val
, char *cptr
, void *desc
)
479 if (sim_idle_rate_ms
== 0) return SCPE_NOFNC
;
480 if ((val
!= 0) && (sim_idle_rate_ms
> (uint32
) val
))
482 sim_idle_enab
= TRUE
;
483 if (sim_throt_type
!= SIM_THROT_NONE
) {
484 sim_set_throt (0, NULL
);
485 printf ("Throttling disabled\n");
486 if (sim_log
) fprintf (sim_log
, "Throttling disabled\n");
493 t_stat
sim_clr_idle (UNIT
*uptr
, int32 val
, char *cptr
, void *desc
)
495 sim_idle_enab
= FALSE
;
501 t_stat
sim_show_idle (FILE *st
, UNIT
*uptr
, int32 val
, void *desc
)
503 fprintf (st
, sim_idle_enab
? "idle enabled": "idle disabled");
507 /* Throttling package */
509 t_stat
sim_set_throt (int32 arg
, char *cptr
)
515 if ((cptr
!= 0) && (*cptr
!= 0)) return SCPE_ARG
;
516 sim_throt_type
= SIM_THROT_NONE
;
519 else if (sim_idle_rate_ms
== 0) return SCPE_NOFNC
;
521 val
= strtotv (cptr
, &tptr
, 10);
522 if (cptr
== tptr
) return SCPE_ARG
;
523 c
= toupper (*tptr
++);
524 if (*tptr
!= 0) return SCPE_ARG
;
525 if (c
== 'M') sim_throt_type
= SIM_THROT_MCYC
;
526 else if (c
== 'K') sim_throt_type
= SIM_THROT_KCYC
;
527 else if ((c
== '%') && (val
> 0) && (val
< 100))
528 sim_throt_type
= SIM_THROT_PCT
;
529 else return SCPE_ARG
;
531 printf ("Idling disabled\n");
532 if (sim_log
) fprintf (sim_log
, "Idling disabled\n");
533 sim_clr_idle (NULL
, 0, NULL
, NULL
);
535 sim_throt_val
= (uint32
) val
;
540 t_stat
sim_show_throt (FILE *st
, DEVICE
*dnotused
, UNIT
*unotused
, int32 flag
, char *cptr
)
542 if (sim_idle_rate_ms
== 0)
543 fprintf (st
, "Throttling not available\n");
545 switch (sim_throt_type
) {
548 fprintf (st
, "Throttle = %d megacycles\n", sim_throt_val
);
552 fprintf (st
, "Throttle = %d kilocycles\n", sim_throt_val
);
556 fprintf (st
, "Throttle = %d%%\n", sim_throt_val
);
560 fprintf (st
, "Throttling disabled\n");
564 if (sim_switches
& SWMASK ('D')) {
565 fprintf (st
, "Wait rate = %d ms\n", sim_idle_rate_ms
);
566 if (sim_throt_type
!= 0)
567 fprintf (st
, "Throttle interval = %d cycles\n", sim_throt_wait
);
573 void sim_throt_sched (void)
577 sim_activate (&sim_throt_unit
, SIM_THROT_WINIT
);
581 void sim_throt_cancel (void)
583 sim_cancel (&sim_throt_unit
);
588 Throttle service has three distinct states
590 0 take initial measurement
591 1 take final measurement, calculate wait values
592 2 periodic waits to slow down the CPU
595 t_stat
sim_throt_svc (UNIT
*uptr
)
600 switch (sim_throt_state
) {
602 case 0: /* take initial reading */
603 sim_throt_ms_start
= sim_os_msec ();
604 sim_throt_wait
= SIM_THROT_WST
;
605 sim_throt_state
++; /* next state */
606 break; /* reschedule */
608 case 1: /* take final reading */
609 sim_throt_ms_stop
= sim_os_msec ();
610 delta_ms
= sim_throt_ms_stop
- sim_throt_ms_start
;
611 if (delta_ms
< SIM_THROT_MSMIN
) { /* not enough time? */
612 if (sim_throt_wait
>= 100000000) { /* too many inst? */
613 sim_throt_state
= 0; /* fails in 32b! */
616 sim_throt_wait
= sim_throt_wait
* SIM_THROT_WMUL
;
617 sim_throt_ms_start
= sim_throt_ms_stop
;
619 else { /* long enough */
620 a_cps
= ((double) sim_throt_wait
) * 1000.0 / (double) delta_ms
;
621 if (sim_throt_type
== SIM_THROT_MCYC
) /* calc desired cps */
622 d_cps
= (double) sim_throt_val
* 1000000.0;
623 else if (sim_throt_type
== SIM_THROT_KCYC
)
624 d_cps
= (double) sim_throt_val
* 1000.0;
625 else d_cps
= (a_cps
* ((double) sim_throt_val
)) / 100.0;
626 if (d_cps
>= a_cps
) {
630 sim_throt_wait
= (int32
) /* time between waits */
631 ((a_cps
* d_cps
* ((double) sim_idle_rate_ms
)) /
632 (1000.0 * (a_cps
- d_cps
)));
633 if (sim_throt_wait
< SIM_THROT_WMIN
) { /* not long enough? */
638 // fprintf (stderr, "Throttle values a_cps = %f, d_cps = %f, wait = %d\n",
639 // a_cps, d_cps, sim_throt_wait);
643 case 2: /* throttling */
648 sim_activate (uptr
, sim_throt_wait
); /* reschedule */