*----------------------------------------------------------- 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;