:IF[With3MB]; *************************************** TITLE[XWTask]; *3 MB Ethernet microcode % Ed Fiala 19 May 1982: Add PFetch4 at XOTimerDone+4 to fix bug reported by Hal Murray. Ed Fiala 10 May 1982: Bum 2 mi (at XIIndexOK+6 and XINops+4); introduce XW1stQW0 quadword. Ed Fiala 1 April 1982: Reformat and absorb XWInit.Mc and XWDefs.Mc into XWTask.Mc; eliminate separate input init for 2nd board; arrange register definitions for reasonable printout in Pilot1.Regs; change XWRandomReg from Refr to ClockLo; bum 3b mi; assemble only if With3MB. Ed Fiala 8 October 1981: Crank time between successive outputs down to 500 us from 1 ms and eliminate unnecessary waits; reformat file according to standard conventions; bum 31b mi. HGM 17 June 1981: Notice new input buffer sooner. HGM 16 June 1981: Interlock XWIndex and XWBSetup. Jim Frandeen October 6, 1980 12:01 PM: Try extra Nop after IOStore4 for 736 problem. Jim Frandeen September 2, 1980 2:31 PM: Change DoInt to NotifyInterrupt. HGM June 28, 1980 10:55 PM: Ether/XW things, fix AR 3253 HGM April 28, 1980 9:47 PM: Nops for H4PE vs IOStore4 HGM April 24, 1980 4:04 PM: Interlock Output Jim Frandeen January 20, 1980 11:04 AM: cleanup for D0Lang Version 6. Jim Frandeen December 22, 1979 9:53 PM: cleanup for D0Lang Version 5. Hal Murray on September 21, 1979 3:01 AM Hal Murray on September 15, 1979 Base reg changes Hal Murray on September 13, 1979 7:47 PM Written by Roy Ogus (early 79). This module contains basic code for a single controller. File XWSIO2 contains StartIO code for two boards. This version has Ethernet address recognition. Data buffers must be quadaligned, and at least 4 words long. Prefetching of CSB at end of packet. RM ALLOCATION R0 XWTemp1 - Temporary register; used by DoInt R1 XWTemp2 - Temporary register; used by DoInt R2 XWTemp - Temporary register R3 XWCompletion - Completion word R4 XWIndex - Buffer displacement R5 XWCount - Buffer word counter (quadword buffer) R6 XWPtr - Buffer ptr (low) (current IOCB) R7 XWPtrHi - Buffer ptr (high) (current IOCB) R10 XWIOCB - Base register for current IOCB (low) R11 XWIOCBHi - Base register for current IOCB (high) R12 XWCSB - Base register for CSB (low) R13 XWCSBHi - Base register for CSB (high) R14 XW1stQW0 - First quadword of message (input only) R15 XW1stQW1 R16 XW1stQW2 R17 XW1stQW3 RM registers that overlay normal use R4 XWCSB1 - first word of CSB (not currently used) R5 XWNextIOCB - Short pointer to next IOCB R6 XIHALo - Host address (low - input only) R7 XIHAHi - Host address (high - input only) H4PE Hardware Problem NOPs in the code are because the input hardware generates H4PE's. Even on the Rev P boards they squeak through occasionally. The fault handler simply resumes after any H4PE (A flag in FFault allows crashing, if you prefer). Successful recovery requires several mi after Inputs or IOStore4s for several reasons: 1) In the sequence Input (or IOStore4) then another memory reference, the MC1 microcode does a wild branch after an H4PE. 3 non-memory mi are needed after an Input and 4 mi after an IOStore4 to avoid this. Interlocking an Input allows another reference on the mi following the interlock (?or is one more intervening mi required?; if a PStore tries to store the data which just got H4PE, is that fatal?). 2) An H4PE will abort the 6th mi following an IOStore4 or the 4th mi following Input. LoadPage may occur in the 1st mi after a tasking return, and the mi after the LoadPage must not be aborted, so at least 5 mi following IOStore4 and 3 following Input must occur before tasking. INPUT MICROCODE Input CSB 00 (reserved for emulator control) 01 Short pointer to First IOCB 02 Host address (low word) 03 Host address (high word) 04 Input wakeup bit mask 05 Packets missed (for debugging) 06-07 (unused) 10-13 quadword scratch bufer 14-17 Used by test program Input IOCB 00 Link to next IOCB 01 (unused) 02 Command word 03 Completion word 04 Amount used in buffer 05 Length of buffer 06 Low pointer to buffer 07 High pointer to buffer % *Use to write state from Task 0 MC[XOWriteState,Or[LShift[XOTask,4],0]]; MC[XIWriteState,Or[LShift[XITask,4],0]]; MC[XOWriteState2,Or[LShift[XOTask2,4],0]]; MC[XIWriteState2,Or[LShift[XITask2,4],0]]; *I/O Address Registers Set[XIData,3]; *Input data *Set[XIHost,1]; *Host ID (3mb Ethernet only) Set[XWStatus,2]; *Status/State register Set[XOData,1]; *Output data Set[XWReadState,2]; *State register read Set[XWWriteState,0]; *State register write %D0Lang foils assembly of this module because assigned tasks are 4/5 and 10/11 rather than being in the same group of four tasks. To keep the current task assignment, we assemble the code as task 0. However, we then run afoul because SRCDES=0 is interpreted by the hardware as a STACK reference in PFetch/PStore, and we would otherwise use register 0 as the target for memory references. % *These macros define xw#1 for use by the code and xo1#1, xo2#1, xi1#1, and *xi2#1 for the Pilot1.Regs file. Macro[XIReg,( SetTask[xiTask] RV[xi1#1,Add[LShift[And[xiTask,3],4],#2]], SetTask[xiTask2] RV[xi2#1,Add[LShift[And[xiTask2,3],4],#2]], SetTask[0] RV[xw#1,#2])]; Macro[XOReg,( SetTask[xoTask] RV[xo1#1,Add[LShift[And[xoTask,3],4],#2]], SetTask[xoTask2] RV[xo2#1,Add[LShift[And[xoTask2,3],4],#2]])]; Macro[XWReg,(XIReg[#1,#2],XOReg[#1,#2])]; *An identical set of these task-specific registers exists for both the *input and the output task. *Note: reg. 0 and 1 used by DoInt. XWReg[InitialCSB,0]; *CSB ptr passed in this reg from Initialize.mc *These 4 registers are used as a quadword by the input task. XWReg[Temp1,0]; XWReg[Temp2,1]; XWReg[Temp,2]; XWReg[Completion,3]; *Not used very often *These must match the layout in the IOCB XWReg[Index,4]; XWReg[Count,5]; XWReg[Ptr,6]; XWReg[PtrHi,7]; *Register definitions for first 4 words of CSB (overlay others) XWReg[CSB1,4]; XWReg[NextIOCB,5]; XIReg[HALo,6]; *Input only XIReg[HAHi,7]; *Input only XWReg[IOCB,10]; XWReg[IOCBHi,11]; XWReg[CSB,12]; XWReg[CSBHi,13]; XIReg[1stQW0,14]; XIReg[1stQW1,15]; XIReg[1stQW2,16]; XIReg[1stQW3,17]; *State Register command words MC[XWSetPurgeMode,260]; *Sets: Enable Input, PurgeMode MC[XWSetOutputEOP,107]; *Sets: Enable Output, OutputEOP, JamEnable MC[XWEnableInput,220]; *Sets: Enable Input MC[XWEnableOutput,103]; *Sets: Enable Output, JamEnable MC[XWDisableInputOutput,300]; *Clears: Input and Output MC[XWDisableInput,200]; *Clears: Input state register MC[XWDisableOutput,100]; *Clears: Output state register MC[XWPream,252]; *Xerox Wire preamble *Status bit definitions: as read with Read State/status command. *Status bits (Ethernet) Set[XSIJam,100000]; *Receiver-detected collision (Jam) Set[XSOURun,40000]; *Output Underrun Set[XSIORun,20000]; *Input overrun Set[XSOCOLL,10000]; *Transmitter-detected collision (Collision) Set[XSICRC,4000]; *Input Bad CRC Set[XSOFAULT,2000]; *Output Data Fault Set[XSOPAR,1000]; *Output Bad Parity Set[XSIBA,400]; *Input Bad Alignment MC[XISMASK,Or[XSIJam,XSIORun,XSICRC,XSIBA]]; *Status bits reported for input command MC[XOSMASK,Or[XSOURun,XSOCOLL,XSOFAULT,XSOPAR]]; *Output Status bits MC[XOCollisionMask,XSOCOLL]; *Completion codes MC[XWGoodPacket,040000]; MC[XWErrorBadHWPkt,070000]; MC[XWErrorZeroBuf,064000]; *On input (length<3) and output (length=0) MC[XWPktBufOverrun,061000]; *Input only MC[XWErrorCountOV,062000]; *Output only *Timer definitions Set[XWTimerRunning,5]; *State 5 is simple timer Set[XWTimerIdle,4]; *State 4 is idle MC[XWTimerMask,LShift[XWTimerRunning,14]]; MC[XWIdleTimer,LShift[XWTimerIdle,14]]; MC[pXWRandomReg,IP[ClockLo]]; *Pointer to register containing random no. *Indices into blocks *IOCB's MC[XWIndexNext,0]; *(Short) Pointer to next IOCB MC[XWIndexTransInt,1]; *Retransmission Mask MC[XWIndexCompletion,3]; MC[XWIndexBuffer,4]; *Long pointer and length (Quadword aligned) *CSB's MC[XWIndexHeader,0]; *First 4 words of CSB MC[XWIndexPtr,1]; *Pointer to current IOCB MC[XWIndexWake,4]; *Wakup Bitmask MC[XWIndexNoICBCount,5]; *Count of times when no ICB chain (For test only) MC[XWIndexScratch,10]; *Quadword scratch area SetTask[0]; *3 MB ETHERNET controller INITIALIZATION. *Will only be called if there is an Ethernet board in the machine. OnPage[xwInitPage]; xiInit: Call[xwSetup], At[xwInInitLoc]; StkP _ xwTemp, Return; *Restore Stkp *Compute value for xoNotify register, used for notify after timer wakeup. xoInit: xwTemp _ IP[xoNotify]C, Call[xwSetup], At[xwOutInitLoc]; Stack _ HiA[xoTimerDoneLoc,xoTask]; xoInitx: Stack _ (Stack) or (LoA[xoTimerDoneLoc]); StkP _ xwTemp, Return; *Restore Stkp *Second Board *Compute value for XONotify2 register, used for notify after timer wakeup. xoInit2: xwTemp _ IP[xoNotify2]C, Call[xwSetup], At[xwOutInitLoc2]; Stack _ HiA[xoTimerDoneLoc,XOTask2], GoTo[xoInitx]; xwSetup: T _ (SStkP&NStkP) xor (377C); StkP _ xwTemp, xwTemp _ T, NoRegILockOK; xwCSBHi _ 0C; xwIOCBHi _ 0C; T _ xwInitialCSB, UseCTask; xwCSB _ T, Return; OnPage[XWPage]; *Input microcode is notified at XIStart to initialize. XIStart: XWTemp _ XWDisableInput, Call[XWOutState], At[XIStartLoc]; *ADDRESS RECOGNITION: Ethernet encapsulation assumed. * Check if the packet, * is explicitly for this host (dest=us), * or is broadcast (dest=0), * or if the host is promiscuous (us=0). *There are two wakeup points. * XIBegin: No prefetch of CSB has been done. * XIFastBegin: Prefetch of CSB has been done. XIBegin: PFetch4[XWCSB,XWCSB1,XWIndexHeader!]; *Fetch 1st 4 words of CSB XIFastBegin: *Read 4 words into CSB scratch area IOStore4[XWCSB,XIData,XWIndexScratch!], Call[XINops]; LU _ RHMask[XWHALo]; PFetch4[XWCSB,XW1stQW0,XWIndexScratch!], Skip[ALU#0]; T _ XWNextIOCB, GoTo[XIForMe]; *We are promiscuous T _ RSh[XW1stQW0,10]; *Right justify dest host in T LU _ (RHMask[XWHALo]) xor T, Skip[ALU#0]; T _ XWNextIOCB, GoTo[XIForMe]; *Broadcast T _ XWNextIOCB, GoTo[XIForMe,ALU=0]; *Packet explicitly for me *Packet unaccepted. Tell hardware to ignore (i.e., purge) rest of packet. XIPurge: XWTemp _ XWSetPurgeMode, Call[XWOutState]; *Return here after wakeup LU _ XWNextIOCB; DblGoTo[XIFastBegin,XIBegin,ALU#0]; *Get buffer started. Check for no IOCB available (in case it was a slow wakeup). XIForMe: XWIOCB _ T, GoTo[XIIndexOK,ALU#0]; *ALU=0 => No IOCB ***No IOCB. Bump counter in CSB. Very bad tasking here. Predicted execution ***small numbers of times per hour. PFetch1[XWCSB,XWTemp,XWIndexNoICBCount!]; XWTemp _ (XWTemp) + 1; PStore1[XWCSB,XWTemp,XWIndexNoICBCount!], GoTo[XIPurge]; *Check for runt packets (<= 3 words, excluding CRC). *IOATTEN=1 => EOP => small packet XIIndexOK: Skip[IOAtten']; GoTo[XIPurge]; Call[XWBSetup]; XWCount _ (XWCount) - (4C); Skip[Carry]; T _ XWCompletion _ XWErrorZeroBuf, GoTo[XIMark]; XWCount _ (XWCount) - (4C), Task; *Leaves TPC setup PStore4[XWPtr,XW1stQW0,0]; ***This better not be the bypass kludge??? *Main loop-checks for end of buffer, then checks ATTEN for end-of-packet. XWCount _ (XWCount) - (4C), GoTo[XIQuadFull,R<0]; T _ (XWIndex) _ (XWIndex) + (4C), Skip[IOAtten']; Input[XWTemp,XWStatus], GoTo[XIAttn]; IOStore4[XWPtr,XIData]; XINops: Nop; Nop; Nop; Nop, GoTo[XWRet]; *H4PE aborts the mi immediately after the Return. *Get here when there is no more room for quadwords in the buffer. *Check ATTEN to see if end of packet. XIQuadFull: *Number of singles remaining in buffer = XWCount+8. *Set up XWCount as (No. singles-1), and read in singles. XWCount _ (XWCount) + (7C); XWIndex _ (XWIndex) + (3C), Call[.+1]; *Loop here. XWCount _ (XWCount) - 1, GoTo[XIBufFull,R<0]; T _ (XWIndex) _ (XWIndex) + 1, Skip[IOAtten']; Input[XWTemp,XWStatus], GoTo[XIAttn]; Input[XWTemp,XIData]; LU _ XWTemp; ** H4PE timing Nop; PStore1[XWPtr,XWTemp], Return; *We get here when the input buffer is exactly full. *Check if ATTEN for EOP, indicating that the last word was the CRC. XIBufFull: XWIndex _ (XWIndex) + 1, Skip[IOAtten']; Input[XWTemp,XWStatus], GoTo[XIAttn]; *Check if there is only one more word (i.e. the CRC); otherwise, mark the *packet buffer overrun. Input[XWTemp,XIData]; *, Call[XWRet]; LU _ XWTemp; ** H4PE timing XWIndex _ (XWIndex) + 1, Skip[IOAtten']; Input[XWTemp,XWStatus], GoTo[XIAttn]; *EOP, so only 1 more word (CRC) T _ XWCompletion _ XWPktBufOverrun, GoTo[XIMark]; *FINISH UP PACKET *Arrive here after an IOAtten/IOAtten' is detected, *indicating an end of packet. *XWIndex has the number of words stored in the current buffer. *XWTemp has status word XIAttn: T _ LdF[XWTemp,10,2]; *Get excess count XWIndex _ (XWIndex) - T - 1, Call[XWStoreIndex]; *CRC too XWTemp _ (XWTemp) and (XISMask); *Isolate InStatus XWCompletion _ XWErrorBadHWPkt, Skip[ALU#0]; T _ XWCompletion _ XWGoodPacket, GoTo[XIMark]; *Hardware indicated problems T _ RSh[XWTemp,10]; *Right justify Ethernet status XIMark: XWCompletion _ (XWCompletion) or T, Call[XWStoreStatus]; Call[XWNext]; *Now check if there is a buffer. GoTo[XIPurge]; %OUTPUT MICROCODE Output CSB 00 (reserved for emulator control) 01 Short pointer to First IOCB 02-03 (unused) 04 Output wakeup bit mask 5-17 (unused) Output IOCB 00 Link to next IOCB 01 Initial Retransmission Interval 02 Command word 03 Completion word 04 (Unused) 05 Length of buffer 06 Low pointer to buffer 07 High pointer to buffer % *Output microcode is notified at XOStart to disable and initialize TPC. XOStart: XWIOCB _ 0C, At[XOStartLoc]; XONoWork: XWTemp1 _ XWIdleTimer, Call[XWLoadTimer]; *Idle state of output microcode has TPC pointing here. Software executes an *Output opcode with argument XWEnableOutput to reenable interrupts and wake *the microcode. XWIOCB, GoTo[XWOutBlock,R<0]; *Fetch pointer to 1st IOCB PFetch4[XWCSB,XWCSB1,XWIndexHeader!], GoTo[XOGO1]; XOCheck: XWIOCB _ T, Skip[ALU#0]; *ALU = 0 => no IOCB *Block until reenabled by software if nothing to do. GoTo[XONoWork]; Call[XWBSetup]; XWCount _ (XWCount) - (4C), Skip[R>=0]; T _ XWCompletion _ XWErrorZeroBuf, GoTo[XOMark]; *Buffer empty *Check if old transmission interval has overflowed PFetch1[XWIOCB,XWTemp2,XWIndexTransInt!]; XWTemp2 _ (LSh[XWTemp2,1]) + 1, Skip[R>=0]; *R<0 => overflow T _ XWCompletion _ XWErrorCountOV, GoTo[XOMark]; PStore1[XWIOCB,XWTemp2,XWIndexTransInt!]; *Initialize displacement to -4 XWIndex _ (XWIndex) - (4C), Call[XWRet]; *Main loop-checks for end of buffer, then checks IOAtten for end-of-pkt. XWCount _ (XWCount) - (4C), GoTo[XOQuadEmpty,R<0]; T _ XWIndex _ (XWIndex) + (4C), GoTo[XOAbort,IOAtten]; IOFetch4[XWPtr,XOData], Return; *Normal exit from Output Loop is here *Number of singles remaining in buffer = XWCount+8. *Set up XWCount as (No. singles-1), and output singles. XOQuadEmpty: T _ XWIndex _ (XWIndex) + (4C); XWCount _ (XWCount) + (7C), Call[.+1]; *Loop here XWCount _ (XWCount) - 1, GoTo[XOEnd,R<0]; PFetch1[XWPtr,XWTemp], Skip[IOAtten']; GoTo[XOAbort]; T _ (Zero) + T + 1; *Update T Output[XWTemp,XOData], GoTo[XWRet]; *Hack to generate lots of JAMs -- Just wait for an UnderRun * Call[.+1]; * Nop; * Skip[IOAtten']; * GoTo[XOAbort]; * Return; *END OF PACKET XOEnd: XWTemp _ XWSetOutputEOP, Call[XWOutState]; *Set OutputEOP bit *Wait for end-of-packet wakeup XOAbort: Input[XWTemp,XWStatus]; *Read Status XWTemp _ (XWTemp) and (XOSMask); *Isolate OutStatus LU _ (XWTemp) xor (XOCollisionMask), GoTo[XOPacketOK,ALU=0]; *Error reported by the hardware T _ RSh[XWTemp,10], Skip[ALU=0]; XWCompletion _ XWErrorBadHWPkt, GoTo[XOMark]; %COLLISION ONLY. Choose random retransmission interval. A tick size of ~38 us is wanted (because Altos used that value historically). Dolphin timers have a basic tick of 64 cycles, or 6.4 us at 100 ns/cycle. 6*6.4 is 38.4 which is very close, so 6*x = 2*(2*x + x) is computed below. % T _ (SStkP&NStkP) xor (377C); *Save Stkp XWTemp2 _ pXWRandomReg; *point to "random" register (Refr) StkP _ XWTemp2, XWTemp2 _ T, NoRegILockOK; *Form new transmission interval mask. Xor with CTask adds randomization when *the 2nd ethernet board is on the same ethernet as the 1st board during checkout. T _ RSh[Stack,2]; *Get random number StkP _ XWTemp2; *Restore StkP T _ (CTask) xor T; *Mask random number with old value of retransmission mask T _ XWTemp1 _ (RSh[XWTemp1,1]) and T; ***Next branch implies 0 wait, but maybe 500 us is needed? XWTemp1 _ (LSh[XWTemp1,1]) + T, GoTo[XOGO,ALU=0]; XWTemp1 _ LSh[XWTemp1,1], GoTo[XOCountDown]; *GOOD PACKET status: XOPacketOK: T _ XWCompletion _ XWGoodPacket; *Mark packet good XOMark: XWCompletion _ (XWCompletion) or T, Call[XWStoreStatus]; %To help hosts with half duplex interfaces (Altos) avoid missing closely-spaced packets, enforce ~ 500 us delay after each output by starting a timer here and not allowing the next output until the timer has run out. % XWTemp1 _ 116C, Call[XWNext]; *78d ~ 500/6.4; setup next IOCB XOCountDown: T _ (LdF[XWTemp1,7,2]) - 1; XWTemp2 _ T, Task; *Save high part -1 in XWTemp2 XWTemp1 _ LdF[XWTemp1,11,7]; *Low 7 bits in XWTemp1 *Start simple timer 7-bit number in XWTemp1. XOLoadTimer: XWTemp1 _ LSh[XWTemp1,4]; *align data part XWIOCB _ 100000C; *Indicate timer running XWTemp1 _ (XWTemp1) or (XWTimerMask), Call[XWLoadTimer]; *Wakeup here if the driver enables interrupts while we are waiting. In that *event, block until the timer expires. XWTemp _ XWDisableOutput, GoTo[XWOutState]; *Timer has expired. XOTimerDone: LU _ XWIOCB, Skip[R<0], At[XOTimerDoneLoc]; Return; *Timer expired as output was disabled--block. *Check for more time to elapse before start of transmission (High part of *random number >=0). XWTemp2 _ (XWTemp2) - 1, Skip[R<0]; XWTemp1 _ 177C, GoTo[XOLoadTimer]; *Setup maximum timer value *Countdown interval is over. Enable output wakeups. Hardware transmission *will start when .gr. 10 words are in the buffer or the OutputEOP bit is set *(for a packet less than 12 words) XWTemp _ XWEnableOutput, Call[XWOutState]; XOGO: PFetch4[XWCSB,XWCSB1,XWIndexHeader!]; XOGO1: T _ XWNextIOCB, GoTo[XOCheck]; *SUBROUTINES *Get buffer descriptor pointed to in T, and fix pointer (in XWPtr, XWPtrHi). *XWPtrHi goes from (0, x) to (x, x+1) *Clears XWIndex XWBSetup: PFetch4[XWIOCB,XWIndex,XWIndexBuffer!]; XWIndex _ (XWIndex) and (0C); T _ LSh[XWPtrHi,10]; XWPtrHi _ (RHMask[XWPtrHi]) + T + 1, Return; XWStoreIndex: PStore1[XWIOCB,XWIndex,XWIndexBuffer!], GoTo[XWRet]; *Chain to next IOCB and update pointer in CSB *Current Drivers depend upon this action being ATOMIC. XWNext: PFetch4[XWCSB,XWCSB1,XWIndexHeader!]; PFetch1[XWIOCB,XWNextIOCB,XWIndexNext!]; PStore4[XWCSB,XWCSB1,XWIndexHeader!], Return; *Notify the driver by generating appropiate interrupts. *This could be merged with ENNext, but that would be too long between TASKs. XWStoreStatus: PFetch1[XWCSB,XWTemp,XWIndexWake!]; *Fetch wakeup bitmask PStore1[XWIOCB,XWCompletion,XWIndexCompletion!]; LoadPage[NotifyInterruptPage]; T _ XWTemp, GoToP[NotifyInterrupt]; *Mask in T *Load timer from data in XWTemp1, and disable output hardware. *Note: There must be at least 7 mi between consecutive LoadTimer/AddToTimer. *This code must be coordinated with Timer.Mc and anything else that loads *timers. Note that we ABORT for a long time after the OUTPUT. XWLoadTimer: T _ CTask; *Timer slot is same as output task number. XWTemp1 _ (XWTemp1) or T; LoadTimer[XWTemp1]; XWOutBlock: XWTemp _ XWDisableOutput; XWOutState: Output[XWTemp,XWWriteState]; XWRet: XWTemp _ XWTemp, Return; *Avoid Output-Output-PStore4 gotcha END[XWTask]; :ELSE; ********************************************** TITLE[No.3.MB.Ethernet.Microcode]; :ENDIF; ********************************************* (1792)\f5