:TITLE[Timer]; *Last edited: 15 October 1981 by Fiala %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 mhz 50 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. MXRClk: PFetch1[MDS,RTemp]; 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, 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 SetFault, GoTo[.], At[TimerTable,10]; *7 *Ethernet Output Notify slot (Add copies with EOTask1, EOTask2 ... if *multiple controllers). TimerTemp _ HiA[EOTimerDoneLoc,EOTask], GoTo[TEONotify], At[TimerTable,11]; 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]; *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]; *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];e6(1795)\7650f1 26f0 599f1 143f0