:TITLE[Timer];*Last edited: 15 October 1981 by Fiala

* Placed MXRclk, added With10MB switch to table July 6, 1982 2:27 PM

%Occurrences of LoadTimer and AddToTimer must be separated by 7 mi, and branch
conditions are illegal with LoadToTimer and AddToTimer. Here the 7 mi
requirement is fulfilled with 3 mi before LoadTimer/AddToTimer and 4 mi after.
OTHER MODULES MUST GUARANTEE 3 MI FROM WAKEUP TO LOADTIMER/ADDTOTIMER AND 4 MI
AFTER LOADTIMER/ADDTOTIMER BEFORE TASKING TO BE CONSISTENT.

In LoadTimer/AddToTimer, 0:3 are the new state, 4:11 new data, and 12:15
are the slot number.

Refresh must cycle fast enough to maintain storage. 16k RAMs are configured
as 128 rows x 128 columns; one Dolphin refresh reference refreshes four rows,
so 32 references are required per cycle; 64k RAMs are configured as 256 x 256
and would require 64 references per cycle. Both 16k and 64k RAMs specify a
2 ms cycle at maximum operating temperature (80 deg C).

The primary leakage phenomenon necessitating refresh is exponential in
temperature, about exp(-44*(300/T)), so required refresh rate goes up by a
factor of 2 for every increase of 5 degrees C. Refresh at a 2 ms period
is undesirable because about 4 percent of all cycles would be consumed by
the refresh timer; code below presently implements an 8 ms period, which
gives up about 10 degrees in temperature margin, possibly requiring some
marginal RAMs to be replaced, but the manufacturer’s specification is
believed very conservative and we do not intend to operate anywhere near the
maximum operating temperature. Parameters for other periods are given in the
table below.

Initialization must be particularly careful about refresh. Initial uses a
640 cycle timer with the Refr register as a state variable for a period of
2 ms. After putting AMesa and its overlays in storage, Initial exits with
LoadRAM. During LoadRAM, inline refresh is done once every 8 mi (i.e.,
slightly faster than the 640 cycle timer), but xfTemp1 is used as the Refresh
base register, so an unrefreshed period of almost 4 ms might be experienced
by some RAMs, if xfTemp1 and Refr are maximally out of sync--however, this
is still less than the 8 ms refresh period used by AMesa. AMesa immediately
starts a 2560 cycle refresh timer with Refr as the state variable;
Initialize.Mc sets Refr to the value in xfTemp1 immediately after this code
returns, so there is no significant refresh hickup between the end of
Initial’s LoadRAM and AMesa’s normal refresh. LoadRAM calls during AMesa
init always allow normal refresh to avoid discontinuities in the refresh
cycle. However, HRam init in DisplayInit.Mc goes about 7500 cycles without
tasking.

When a timer wakeup is deferred too long, wakeup and AddToTimer don’t work
properly. If a timer is able to occupy the wakeup slot, then, no matter how
long tasking is deferred, wakeup will happen as soon as tasking occurs.
However, if some other timer occupies the slot, then 8192 cycles after it
expired, the timer’s data field will become positive and cease requesting a
wakeup--8192 cycles after that it will go negative again, etc. So any
deferral of 8193 to 16384 cycles will delay wakeup at least 16384 cycles.
Even if an expiring timer immediately occupies the wakeup slot, AddToTimer
may leave the data field with an 8192 cycle wait for next time, if tasking
is defferred too long.

Slot 17b is used for refresh and realtime clock update. The clock constant
should produce carries out of RTCLow at the rate expected by the Alto clock
software. Since the 26d-bit Alto RTClock ticks every 38.09524 us, the
constant added each tick for a N-us clock should be N/38.09524; since this is
stored in a doubleword with 6 bits to the right of the low order RTClock bit,
the fraction added is (N/38.09524)*(2↑6) = (4*2↑6*C)/(H*38.09524) =
6.720000*C/H, where H is the processor clock crystal frequency in mhz and C
is the timer period in cycles. Since timer periods are denominated in
processor cycles, microcode can control either the number of cycles between
timer wakeups or the constant added to the simulated RTClock each tick.

The table below shows values for 40 mhz (100 ns cycle), 44.5 mhz (89.886 ns
cycle), and 50 mhz (80 ns cycle) main system clocks. Note that clock periods
larger than 5120 cyc require a double timer. Equivalent constants for the
three frequencies using a 640 cycle timer are [4300.8/H] 107.52 (40 mhz),
96.6472d (44.5 mhz), and 86.0106d (50 mhz).

40 mhz
Refresh
Timer RTimer 40 mhz 44.5 mhz50 mhz
Period
Period Value RConstantHi/Lo RConstantHi/Lo RConstantHi/Lo
2 ms
640 cyc 50257b 153/102436b 140/122657b 126/001266b
4 ms
1280 cyc 50517b 327/005075b 301/045536b 254/002554b
8 ms
2560 cyc 51217b 656/012172b 602/113274b 530/005330b
16 ms
5120 cyc 52417b 1534/024363b 1405/026570b 1260/012660b
%

SetTask[0];

OnPage[XMiscPage];

*Common subroutine for Mesa/Alto ReadClock. Takes 430b in T, returns
*(VM 430) in RTemp, RTCLow in T and RTemp1.
MXRClk:
PFetch1[MDS,RTemp], at[mxRClkLoc];****** placed for Lisp
RTemp1 ← IP[RTCLow]C;
T ← (SStkP&NStkP) xor (377C);
StkP ← RTemp1, RTemp1 ← T, NoRegILockOK;
***It is probably better not to zero the bits here.
T ← (Stack) and not (77C);*Return Most Significant 10d bits
RestSP:
StkP ← RTemp1, RTemp1 ← T, NoRegILockOK, Return;*Restore StkP

SetTask[TTask];

*Initialize Notifies here to set up timers (this is throwaway code)
InitTimers:
TimerTemp ← 100000C, At[TimerInitLoc];
ClrTimers:
TimerTemp ← (LoadTimer[TimerTemp]) + 1;
RSImage ← T ← 0C;
*Zero RConstant now preventing storage update; RConstant will be corrected to
*a value appropriate for 40 mhz processor clock by Initialize after storage
*init. DisplayInit (if there is a UTVFC board and CSLOverlay=1) will
*determine the determine the processor clock crystal frequency and setup
*RConstantLo/Hi to a value appropriate for 40, 44.5, or 50 mhz crystals.
RConstantLo ← T, ResetMemErrs;*Clear any pending memory errors
RConstantHi ← RS232 ← T;*Zap RS232
***This mi isn’t doing anything useful.
Refr ← (Refr) and not (17C);*Fixup refresh pointer
TimerBase ← HiA[430];*Partly init TOD base reg.
LU ← Timer;*Clear pending wakeup.
*Single precision clock error is about .00019 or 16.2 sec/day for this 40 mhz
*value; that’s why we use double precision.
RTimer ← HiA[51217];*Set up the Refresh time
LU ← (TimerTemp) and (17C);*clear 20b timers
RTimer ← (RTimer) or (LoA[51217]), GoTo[ClrTimers,ALU#0];

LU ← Timer, LoadPage[TimerPage];*read to clear pending wakeups
LoadTimer[RTimer], GoToP[TimerSetTPC];*load refresh timer

OnPage[TimerPage];

TimerSetTPC:
TimerBase ← (TimerBase) or (LoA[430]), Call[TimerRet];
TimerWakeup:
*Timer wakeups come here
Dispatch[Timer,14,4];
*Since Refresh timer has the greatest effect on performance, do something
*useful for it in this mi.
T ← RConstantLo, Disp[.+1];
*Timer dispatch table for task 16. Unused table entries are overwritten
*by MicOverlay. NOTE: LoadPage cannot be used in TimerTable because this
*is the 3rd mi after wakeup, and a page fault might happen. Code in
*MicOverlay overwrites the unused TimerTable entries.
Refresh[Refr], GoTo[RefreshNext], At[TimerTable,0];*slot 17
SetFault, GoTo[.], At[TimerTable,1];*16 (Jasmine scan start)
SetFault, GoTo[.], At[TimerTable,2];*15 (fast stage of scan start)
SetFault, GoTo[.], At[TimerTable,3];*14 (Jasmine ScanCB)
SetFault, GoTo[.], At[TimerTable,4];*13 (Jasmine PixelTimer)
SetFault, GoTo[.], At[TimerTable,5];*12
SetFault, GoTo[.], At[TimerTable,6];*11
SetFault, GoTo[.], At[TimerTable,7];*10
:IF[With10MB]; ***************************************
IFE[enxTask,7,,ER[Fixup.needed.for.TimerTable.enxTask]];
APCTask&APC ← enxNotify, GoTo[TimerRet], At[TimerTable,10];
:ELSE;
SetFault, GoTo[.], At[TimerTable,10];*7
:ENDIF; **********************************************
:IF[With3MB]; ***************************************
*Ethernet Output Notify slot (Add copies with EOTask1, EOTask2 ... if
*multiple controllers).
TimerTemp ← HiA[EOTimerDoneLoc,EOTask], GoTo[TEONotify], At[TimerTable,11]; *6
:ELSE;
SetFault, GoTo[.], At[TimerTable,11];*6
:ENDIF; **********************************************
SetFault, GoTo[.], At[TimerTable,12];*5
SetFault, GoTo[.], At[TimerTable,13];*4
SetFault, GoTo[.], At[TimerTable,14];*3
SetFault, GoTo[.], At[TimerTable,15];*2 (Audio)
SetFault, GoTo[.], At[TimerTable,16];*1
SetFault, GoTo[.], At[TimerTable,17];*0

*Reserve locations that can be overwritten by non-resident overlays which
*use timers (reserve TimerTable 20 and 21 here; since 22 to 25 are used,
*26 is the next one available if more are needed).
IMReserve[0,Add[TimerTable,20],2];

:IF[With3MB]; ***************************************
*Notify for Ethernet output (Only one copy required)
TEONotify:
*Notify Ethernet output task, don’t restart timer
TimerTemp ← (TimerTemp) or (LoA[EOTimerDoneLoc]);
APCTask&APC ← TimerTemp, GoTo[TimerRet];
:ENDIF; **********************************************

*NOTE: Return must not occur sooner than 4th mi after AddToTimer.
RefreshNext:
AddToTimer[RTimer];
RTCLowLow ← (RTCLowLow) + T;
T ← (RConstantHi) + 1, UseCOutAsCIn;
RTCLow ← (RTCLow) + T;
Refr ← (Refr) + (20C), Skip[Carry];
TimerRet: Return, At[TimerTable,24];
*MouseHalt+1 for proper restart.
*Note: MC1 is busy for 20 cycles after Refresh, so the PFetch1 below will be
*held; hence the MouseHalt check is free.
:IF[WithMidas]; *******************************
T ← (FFault) and (10000C);*Midas present
LU ← (Printer) and T;*and requesting interrupt?
PFetch1[TimerBase,TimerTemp,0], Skip[ALU=0];*Increment VM 430
*Midas mouse halt is a task 16 breakpoint not set by the user.
*Midas continues at MouseHalt+1 (i.e., at TimerTable+24).
MouseHalt:
SetFault, GoTo[.], At[TimerTable,23];
:ELSE; ****************************************
PFetch1[TimerBase,TimerTemp,0];*Increment VM 430
:ENDIF; ***************************************
TimerTemp ← (TimerTemp) + 1;
PStore1[TimerBase,TimerTemp,0], GoTo[TimerRet];

:END[Timer];