*---------------------------------------------------------------------------- Title[PilotEther.Mc...September 23, 1982 1:49 PM...Taft]; * Ethernet microcode for Pilot, handles "multicast" on input (Swinehart) *---------------------------------------------------------------------------- *---------------------------------------------------------------------------- * Data structures *---------------------------------------------------------------------------- * This microcode supports a "multicast" input regimen. The client can specify from * 0 to 255 host numbers, using a bit vector. Packets directed to any of the specified * hosts will be accepted by the microcode. It is up to the client to sort out the results. * The feature is enabled by setting the ICSB.host field to -1. Then the ICSB.hosts vector * will be used to decide which packets to accept. Note that the client can disable the * acceptance of broadcast packets. Setting ICSB.host to a valid host returns to the * conventional single-host mode. The ICSB.hosts vector should have the proper value * before the ICSB.host field is set to -1. * ICSB: LONG POINTER TO InputControllerStatusBlock = LOOPHOLE[177600B]; * InputControllerStatusBlock: TYPE = MACHINE DEPENDENT RECORD [ MC[ICSB.next, 177600]; * next: POINTER TO IOControlBlock, MC[ICSB.interruptMask, 177601]; * interruptMask: WORD, MC[ICSB.host, 177602]; * host: CARDINAL, -- if >=0, conventional one-host style MC[ICSB.missed, 177603]; * missed: CARDINAL, * skipOverOCSB: ARRAY [4..30B) OF CARDINAL, MC[ICSB.hosts, 177630]; * hosts: PACKED ARRAY [0..256) OF BOOL, * ... ] -- Other stuff microcode doesn't use * OCSB: LONG POINTER TO OutputControllerStatusBlock = LOOPHOLE[177610B]; * OutputControllerStatusBlock: TYPE = MACHINE DEPENDENT RECORD [ MC[OCSB.next, 177610]; * next: POINTER TO IOControlBlock, MC[OCSB.interruptMask, 177611]; * interruptMask: WORD, MC[OCSB.minPacketSpacing, 177612]; * minPacketSpacing: CARDINAL, -- 32us units * ... ] -- Other stuff microcode doesn't use * IOControlBlock: TYPE = MACHINE DEPENDENT RECORD [ MC[IOCB.next, 0]; * next: POINTER TO IOControlBlock, MC[IOCB.completion, 1]; * completion: CompletionStatus, MC[IOCB.wordsUsed, 2]; * wordsUsed: CARDINAL, MC[IOCB.load, 3]; * load: CARDINAL, MC[IOCB.length, 4]; * length: CARDINAL, MC[IOCB.buffer, 5]; * buffer: LONG POINTER] * CompletionStatus: TYPE = MACHINE DEPENDENT RECORD [ * microcode: {busy, done, bufferOverflow, loadOverflow, (377B)}, * hardware: [0..377B]] * Hardware status for input is status word received through receive fifo; * for output is status word read from EControl. * Microcode completion status codes: MC[CS.done, 400]; MC[CS.bufferOverflow, 1000]; MC[CS.loadOverflow, 1400]; *---------------------------------------------------------------------------- * Emulator Task -- ResetEther instruction. * Branched to from Mesa emulator to reset Ethernet hardware and tasks. *---------------------------------------------------------------------------- Set[XTask, IP[EMU]]; * Emulator mode TopLevel; DontKnowRBase; ResetEther: T← EControl; TIOA← T; TaskingOff; * Smash the hardware off T← TurnOffRx, Call[OutputGetsT]; T← TurnOffTx, Call[OutputGetsT]; Call[EITInitPC]; * Reset the tasks LdTPC← T, Wakeup[EIT]; Call[EOTInitPC]; LdTPC← T, Wakeup[EOT]; T← A0, TaskingOn; TIOA← T, IFUJump[0]; *---------------------------------------------------------------------------- * Initialization code for Input Task *---------------------------------------------------------------------------- Set[XTask, IP[EIT]]; Subroutine; EITInitPC: T← EIT, CoReturn; TopLevel; T← EControl; TIOA← T, Block, Branch[EIIdle]; * full TIOA for initialization *---------------------------------------------------------------------------- * Initialization code for Output Task *---------------------------------------------------------------------------- Set[XTask, IP[EOT]]; Subroutine; EOTInitPC: T← EOT, CoReturn; TopLevel; RBase← RBase[EORegs]; T← 33000C; * Init random number generator RConst← T+(31C); * Constant (33031B = 13849) T← EControl; TIOA← T, Block, Branch[EOIdle]; * full TIOA for initialization *---------------------------------------------------------------------------- * Input Task *---------------------------------------------------------------------------- Set[XTask, IP[EIT]]; * Non-emulator mode * Idle state of the Ethernet input task. * Wake up here when first word of a new packet arrives. EIIdle: MemBase← IOBR; T← ICSB.next; Fetch← T, T← ICSB.host; PD← MD, RBase← RBase[EIRegs]; * Is there an IOCB? EICB← MD, TIOA[EData], Branch[EINoCB, ALU=0]; * Branch if no IOCB Fetch← T, EITemp2← T← Input, * Fetch ICSB.host, save first word of packet Branch[EIPLZ, IOAtten]; * Branch if zero-length packet * Address filtering. PD← NOT MD, TIOA[EControl]; * (Done upside-down for placement) T← A0, Branch[MultiTest, ALU>=0]; * Use multicast method if ICSB.host is negative * Traditional method: test for single host, broadcast, or promiscuous. T← RCY[T, EITemp2, 10], Call[EAdrCk]; * T← destination host, test for zero PD← T-MD, Call[EAdrCk]; * Test for destination host = me PD← MD, Call[EAdrCk]; * Test for me = 0 (promiscuous) * Packet not accepted by filter. * Tell hardware to ignore the rest of this packet. EIIgn: T← WaitForBOP; * Wakeup at start of next packet EILast: Output← T, Block, Branch[EIIdle]; Subroutine; EAdrCk: Branch[Accept, ALU=0]; Return; TopLevel; * Multicast method: use destination as an index into the ICSB.hosts array. MultiTest: T← RSH[EITemp2, 14]; * T ← Word offset T← T+(ICSB.hosts); Fetch← T, EITemp1← EITemp2; T← MD, EIPtr← ShC; EITemp1← T, ShC← EITemp1; * Shift count ← low 4 bits of destination host * (EITemp1[4:7]); R/T select bits get garbage PD← ShiftNoMask[EITemp1]; * Left-justify selected bit (from EITemp1 or T) ShC← EIPtr, Branch[EIIgn, ALU>=0]; * Reject if selected bit is zero Nop; * Packet accepted by filter. Set up buffer pointer and count. Accept: EIPtr← A0, Call[EIOCBSetup]; T← EITemp2, TIOA[EData], Block; * Recover first data word, await next * Input task (cont'd) * Input main loop. * Each iteration stores the word previously input from the interface * and inputs the next word. This facilitates end-of-packet handling. * EIPtr has negative of number of words remaining in the buffer. * IOAtten branches if the data word being read is the end-of-packet status. EIPtr← (Store← EIPtr)+1, DBuf← T, Branch[EIEnd, IOAtten]; T← Input, Block, Branch[.-1, ALU#0]; * Get here when buffer is exactly full (EIPtr=0). * We know that the word in T is not EOP status since we would have taken * the IOAtten branch if it were. If IOAtten is now true, then the word in T * is the CRC (which we must discard) and the next input is the status word. * If IOAtten is false, the buffer has overflowed. EIPtr← EITemp1; * Set packet length = buffer length T← Input, Branch[EIEnd1, IOAtten]; * Branch if end-of-packet T← CS.bufferOverflow, Branch[EIPost]; * Normal end-of-packet exit from main loop. * One extra word (the CRC) has been stored in the buffer and must not be * included in the count. The next input is the status word. EIEnd: T← (EITemp1)-1; * Initial buffer length EIPtr← (EIPtr)+T; * Actual packet length T← Input; * Read status word EIEnd1: T← T OR (CS.done); * Done completion status * Post input completion status now in T. EIPost: EITemp1← ICSB.next; Call[EPost]; Block, Branch[EIIdle]; * Await next packet * Here if packet length zero, i.e., first word input was end-of-packet status. * This should happen only if receiver-detected collision reporting is turned * on, since runt packets are ordinarily filtered out by the phase decoder. EIPLZ: EIPtr← A0, Branch[EIEnd1]; * Length← 0, post done status * Discard packet because IOCB queue is empty. EINoCB: T← T+1, TIOA[EControl]; * Increment ICSB.missed Fetch← T; EITemp1← MD+1; Store← T, DBuf← EITemp1, Branch[EIIgn]; * Ignore rest of packet *---------------------------------------------------------------------------- * Output Task *---------------------------------------------------------------------------- Set[XTask, IP[EOT]]; * Idle state of the Ethernet output task. TIOA=EControl. * Wake up here at end of previous packet or when poked by software. EOIdle: MemBase← IOBR; T← OCSB.next; * Fetch OCSB.next T← (Fetch← T)+(Sub[OCSB.minPacketSpacing!, OCSB.next!]C); RBase← RBase[EORegs]; Fetch← T, EOCB← PD← MD; * Fetch OCSB.minPacketSpacing T← (EOCB)+(IOCB.load), Branch[EONoCB, ALU=0]; * Go shut off transmitter if no IOCB * Test and update load Fetch← T, EOTemp2← MD; * Fetch IOCB.load EOTemp1← MD+MD+1; * Load lsh 1 +1 Store← T, DBuf← EOTemp1, EOTemp1← MD, * Store new load, save current Call[Random]; * T← random number T← RSH[T, 10]; * Use leftmost 8 bits * Test for load overflow (old load has sign bit set). * Then mask countdown with old load and test for nonzero result. * Note that the countdown clock ticks every 16 us, but we want to count in * units of 32 us, so we double the count before using it. T← ((EOTemp1) AND T) LSH 1, Branch[ELodOv, R<0]; PD← T-T, Branch[EWaitC, ALU#0]; * Carry← 1; branch if wait required * On first transmission attempt, enforce minimum inter-packet spacing. * Note: RTClock counts 32-usec intervals. EOTemp2 = OCSB.minPacketSpacing T← EOTime, RBase← RBase[RTClock]; * Time last transmission ended T← (RTClock)-T, RBase← RBase[EORegs]; * Inter-packet spacing T← ((EOTemp2)-T-1) LSH 1; * Compare with min permitted EWaitC: EOTemp1← T-1, Branch[EOGo, Carry']; * Start now if exceeded min * Must wait before retransmitting. EOTemp1 has wait time -1 in 16-usec units. * CountDown turns off data wakeups and requests a wakeup at next * tick of countdown clock. EWait: T← CountDown; EOTemp1← (EOTemp1)-1, Output← T, Block, Branch[.-1, R>=0]; * Now time to transmit packet. Set up buffer pointer and count. EOGo: EOPtr← T-T-1, Call[EIOCBSetup]; EOPtr← (Fetch← EOPtr)+1; * Pre-fetch the first word TIOA[EData], Branch[EOEnd, ALU=0]; * Output task (cont'd) * Output main loop. * Data transfer is pipelined: each iteration outputs a data word * fetched in the previous iteration and starts the fetch of the data for * the next iteration. This way, cache misses generally occur while our * task is blocked, and lower-priority tasks are permitted to run while * the cache is being loaded. * EOPtr is the negative of the number of words remaining to be fetched. * IOAtten branches if a collision, data late, or Fifo PE has aborted output. EOPtr← (Fetch← EOPtr)+1, T← MD, Branch[EOAbrt, IOAtten]; Output← T, Block, Branch[.-1, ALU#0]; * Next-to-last word has been output and last word has been fetched EOEnd: T← EOTemp1, Output← MD; * Output last word EOPtr← T, TIOA[EControl]; * Set words used = buffer length T← SendEOP; * Declare end of packet Output← T, Block; * Wake up here only when packet completely sent or collision has occurred. EOTemp1← Input, Branch[EOAbr1, IOAtten]; * Branch if aborted, get status RBase← RBase[RTClock]; * Remember current time T← RTClock, RBase← RBase[EORegs]; EOTime← T; EODone: T← (EOTemp1) AND (377C); * Just the status bits T← T OR (CS.done); * Done completion status * Post ending status and reset interface for next packet. EOPost: EOTemp1← OCSB.next; Call[EPost]; * Post status in T T← TurnOffTx, Branch[EORest]; * Turn transmitter off and on again * Here if collision or other error occurred. * If a collision, just try again. If not, post output done. EOAbrt: T← (EOTemp1)-1; * Initial buffer length EOPtr← (EOPtr)+T, TIOA[EControl]; * Words actually sent EOTemp1← Input; * Read status * If not a collision, post output done with whatever status bits we got. EOAbr1: PD← (EOTemp1) AND (TxCollision); * Collision? T← TurnOffTx, Branch[EODone, ALU=0]; * Branch if not EORest: Output← T; * Turn off to reset collision bit T← TurnOnTx, Branch[EOLast]; * Turn on and try again * Load overflow, post error status ELodOv: T← CS.loadOverflow, Branch[EOPost]; * Load overflow post code * Shut off hardware because IOCB queue is empty. EONoCB: T← TurnOffTx, Branch[EOLast]; EOLast: Output← T, Block, Branch[EOIdle]; *---------------------------------------------------------------------------- * Task-independent Subroutines *---------------------------------------------------------------------------- *---------------------------------------------------------------------------- EIOCBSetup: * Set up IOCB pointers and counts * Enter: ExCB = IOCB being worked on * ExPtr = 0 for input, -1 for output * MemBase = IOBR * Exit: ExPtr = negative word count * MemBase = ExBR * ExBR = base register properly set up for indexing by ExPtr * Points the base register beyond the end of the buffer -2↑16, and * addresses the buffer with a negative index in ExPtr. * Clobbers ExTemp1, T *---------------------------------------------------------------------------- Subroutine; T← (ExCB)+(IOCB.length); T← (Fetch← T)+1; * Fetch IOCB.length T← (Fetch← T)+1, ExTemp1← MD; * Fetch low buffer pointer Fetch← T, T← MD, * Fetch high buffer pointer ExPtr, Branch[.+2, R<0]; * Which base register? T← T+(ExTemp1), MemBase← EIBR, Branch[.+2]; * Low pointer + length T← T+(ExTemp1), MemBase← EOBR; * Low pointer + length T← MD, BRLo← T; T← T-1, XorSavedCarry; * Adjust high for negative indexing T← ExTemp1, BRHi← T; ExPtr← (0S)-T, Return; * ExPtr← negative count *---------------------------------------------------------------------------- EPost: * Post command completion. * Enter: T = completion status word * ExPtr = actual packet length * ExCB = IOCB being worked on * ExTemp1 = ICSB or OCSB pointer (IOBR relative) * Exit: MemBase = IOBR * Clobbers T, ExCB, ExTemp1, RBase *---------------------------------------------------------------------------- Subroutine; MemBase← IOBR; ExCB← (Fetch← ExCB)+1; * Fetch IOCB.next T← (Store← ExCB)+1, DBuf← T; * Store IOCB.completion Store← T, DBuf← ExPtr, T← MD; * Store IOCB.wordsUsed ExTemp1← (Store← ExTemp1)+1, DBuf← T; * Store xCSB.next Fetch← ExTemp1; * Fetch xCSB.interruptMask RBase← RBase[NWW]; * Initiate interrupts NWW← (NWW) OR MD, Reschedule, Return; *---------------------------------------------------------------------------- Random: * Generate random number and return it in T. * Must have RBase[RNum] (=EORegs). * Uses algorithm: * R← (2↑11 + 2↑2 + 2↑0)* R + 13849 *---------------------------------------------------------------------------- Subroutine; KnowRBase[RNum]; T← LSH[RNum, 11]; * T← 2↑9 * R T← T+(RNum); * (2↑9 + 2↑0)* R T← LSH[T, 2]; * (2↑11 + 2↑2)* R T← T+(RNum); * (2↑11 + 2↑2 + 2↑0)* R T← RNum← T+(RConst), Return; * +13849 TopLevel;