*----------------------------------------------------------- Title[Junk.mc...February 23, 1983 5:46 PM...Taft]; *----------------------------------------------------------- % Junk task and related subroutines. This code presently maintains the real time clock and event counters, and optionally samples the emulator task's PC and keeps a histogram. The main loop does the following: 1. Acknowledge junk wakeup. 2. Maintain the real time clock. Alto mode uses a DDA technique to approximate the effect of updating RTClock at ~38.09524 us intervals, though the Junk task actually wakes up every 32 us. PrincOps mode just increments RTClock every 32 us. 3. Call UpdateCounters every EventUpdateInterval times (about once/ms). 4. Samples the emulator's PC, if sampling is enabled. 5. Generates interval timer wakeups (PrincOps only). For Alto mode, the real time clock is represented as follows: VM 430: high 16 bits of clock (Alto convention). RTClock[0:9]: low 10 bits of clock (Alto-I convention). RTClock[10:15]: high 6 bits of DDA fraction (bits undefined on Alto). RTCFrac: low 16 bits of DDA fraction. Hence the DDA fraction is 22 bits long, and the cumulative error introduced by the approximation is guaranteed less than 1 / 2^22, or about 1 second / 48 days. For PrincOps mode, the clock is simply a 32-bit counter. RTClock is the low-order word, and RTC430 is the high-order word. Caution: the RTClock cell must be updated once and only once per wakeup of the Junk task, and must always assume a legal value (B[0:9] monotonically increasing). This is because RTClock is used for timing by the Ethernet output task, which is higher-priority than Junk. Derivation of RTCDelta: Junk wakeup interval = 32 us. RTClock interval = 224 / 5.88E6 = 38.09524 us. Therefore Delta = 32 / 38.09524 = 0.84000 Multiplying by 2^22 yields 3523215 = [65B ,, 141217B] % :If[AltoMode]; ********** Alto version ********** MC[RTCDeltaHi, 65]; MC[RTCDeltaLoLeft, 141000]; MC[RTCDeltaLoRight, 217]; * Microcode depends on this being odd :Else; ******** PrincOps version ******** MC[ITWakeupMask, 177422]; * Mask of wakeups to generate for interval timer :EndIf; ********************************** * Interval between calls to UpdateCounters MC[EventUpdateInterval, 37]; * Times 32 us = ~1 ms. Set[XTask, IP[JNK]]; *----------------------------------------------------------- * Junk task *----------------------------------------------------------- Subroutine; JNKInitPC: T_ JNK, CoReturn; TopLevel; JunkTaskStart: T_ A0, RBase_ RBase[Events]; TIOA_ T; EventUpdateTimer_ T-T-1, MemBase_ IOBR; :If[AltoMode]; ********** Alto version ********** RTCDeltaLo_ RTCDeltaLoLeft; RTCDeltaLo_ (RTCDeltaLo) OR (RTCDeltaLoRight), Branch[JunkTaskLoop]; * Main loop consumes 6 cycles per wakeup in the normal case * (no RTClock overflow or counter update or PC sampling). * RTCDeltaLo is normally odd (required for AckJunkTW_). * RTCDeltaLow even => PC sampling enabled. JunkUpdateCounters: Call[UpdateCounters]; JunkTaskLoop: T_ RTCDeltaLo, Branch[SamplePC, R even]; JunkTaskCont: RTCFrac_ (RTCFrac)+(AckJunkTW_ T); T_ RTCDeltaHi; RTClock_ (RTClock)+T, XorSavedCarry; EventUpdateTimer_ (EventUpdateTimer)+1, Branch[RTCCarry, Carry]; Block, DblBranch[JunkUpdateCounters, JunkTaskLoop, ALU>=0]; * Here when RTClock carries out. Increment VM 430. RTCCarry: T_ 400C; T_ T OR (30C); Fetch_ T; * VM 430 RTC430_ MD+1; * RTC430 is for Lisp performance stuff Store_ T, DBuf_ RTC430, Block, Branch[JunkUpdateCounters]; :Else; ******** PrincOps version ******** RTCDeltaLo_ 1C, Branch[JunkTaskLoop]; * Main loop consumes 4 cycles per wakeup in the normal case * (no counter update or PC sampling). * RTCDeltaLo normally contains 1 (required for AckJunkTW_). * RTCDeltaLow=0 => PC sampling enabled. JunkUpdateCounters: Call[UpdateCounters]; JunkTaskLoop: T_ RTCDeltaLo, Branch[SamplePC, R even]; JunkTaskCont: RTClock_ T_ (RTClock)+(AckJunkTW_ T); PD_ (WakeupTime)#T; RTC430_ A_ RTC430, XorSavedCarry, Branch[RTCWakeup, ALU=0]; EventUpdateTimer_ (EventUpdateTimer)+1, Block, DblBranch[JunkUpdateCounters, JunkTaskLoop, R>=0]; * Here to generate interval timer wakeup RTCWakeup: T_ ITWakeupMask; Fetch_ T; RBase_ RBase[NWW]; NWW_ (NWW) OR MD, Reschedule, Block, Branch[JunkTaskStart]; * Reset RBase before resuming :EndIf; ********************************** KnowRBase[Events]; * Here to sample the emulator's PC and maintain a histogram. * Histogram format is an array of LONG CARDINALs pointed to by PCHistBR. SamplePC: T_ A0, TaskingOff; RdTPC_ T, TaskingOn; T_ NOT (Link); * TPC data is inverted T_ T+T, MemBase_ PCHistBR; * 2 words per histogram slot Fetch_ T; * Increment histogram entry for PC JunkTemp_ MD+1; T_ (Store_ T)+1, DBuf_ JunkTemp, Branch[.-2, Carry]; T_ (RTCDeltaLo)+1, MemBase_ IOBR, Branch[JunkTaskCont]; *----------------------------------------------------------- UpdateCounters: * Subroutine to update RM shadow of event counters. * Called from Junk and Emulator tasks. Emulator must call with TaskingOff. * Entry: RBase=RBase[Events] * Exit: Event*Lo, EventHi1, EventHi0 updated; T clobbered. * Event*Prev remembers the previous value of EventCnt*', a non-resettable * register. Event*Lo maintains the count since the last restart. *----------------------------------------------------------- Subroutine; KnowRBase[Events]; * Update event count A. T_ (EventAPrev)-(EventCntA'); EventALo_ (EventALo)+T; EventAPrev_ (EventAPrev)-T, Branch[.+3, Carry']; EventAHi1_ (EventAHi1)+1; EventAHi0_ A_ EventAHi0, XorSavedCarry; * Update event count B. T_ (EventBPrev)-(EventCntB'); EventBLo_ (EventBLo)+T; EventBPrev_ (EventBPrev)-T, Branch[.+3, Carry']; EventBHi1_ (EventBHi1)+1; EventBHi0_ A_ EventBHi0, XorSavedCarry; * Reset the timer for Junk task calls. EventUpdateTimer_ NOT (EventUpdateInterval), Return; * Subroutines called only from the Emulator. Set[XTask, IP[EMU]]; *----------------------------------------------------------- SetPCHistAddr: * Subroutine to set the PC histogram address and enable/disable * sampling of the emulator task's PC. * Entry: T,,Q = LONG POINTER to table (or 0,,0 to disable) * RBase=Events * Exit: RBase=Events * MemBase=PCHistBR * Table is ARRAY [0..4095] OF LONG CARDINAL. * The emulator's PC is used as an index into the array, and the appropriate * word is incremented every 32 microseconds. *----------------------------------------------------------- Subroutine; KnowRBase[Events]; RTCDeltaLo_ (RTCDeltaLo) OR (1C); * Disable sampling EventTemp0_ Q, MemBase_ PCHistBR; PD_ (BRLo_ EventTemp0) OR T; BRHi_ T, Branch[.+2, ALU=0]; RTCDeltaLo_ (RTCDeltaLo)-1; * Enable sampling Return; *----------------------------------------------------------- StartCounters: * Subroutine to enable event counters and reset them to zero. * Entry: T=event counter enable word (for InsSetOrEvent_), RBase=Events. * Exit: event counters zeroed, T unchaged, RBase=Events. *----------------------------------------------------------- Subroutine; * Enable hardware counters as specified in the call. InsSetOrEvent_ T; * Now reset the software counters. Must do this with TaskingOff to prevent * interference from the junk task, and must not read event counters until * at least 3 cycles after InsSetOrEvent_ or we might read trash. EventALo_ A0, TaskingOff; EventAHi1_ A0; EventAHi0_ A0; EventAPrev_ EventCntA'; EventBLo_ A0; EventBHi1_ A0; EventBHi0_ A0, TaskingOn; EventBPrev_ EventCntB', Return; *----------------------------------------------------------- ReadCounters: * Subroutine to read current values of counters into memory. * Entry: T=pointer to CounterValues record, RBase=Events. * CounterValues: TYPE = MACHINE DEPENDENT RECORD [ * eventALo, eventAHi1, eventAHi0: CARDINAL, -- event counterA * eventBLo, eventBHi1, eventBHi0: CARDINAL]; -- event counterB * Exit: T, Q, and EventTemp0 clobbered. *----------------------------------------------------------- Subroutine; Fetch_ T, Q_ T; * Minimize waiting while TaskingOff T_ MD, EventTemp0_ Link; TopLevel; TaskingOff, Call[UpdateCounters]; * Capture up-to-date state T_ (Store_ Q)+1, DBuf_ EventALo; T_ (Store_ T)+1, DBuf_ EventAHi1; T_ (Store_ T)+1, DBuf_ EventAHi0; T_ (Store_ T)+1, DBuf_ EventBLo; T_ (Store_ T)+1, DBuf_ EventBHi1; T_ (Store_ T)+1, DBuf_ EventBHi0; Link_ EventTemp0; Subroutine; TaskingOn, Return; TopLevel;