*----------------------------------------------------------------------------
Title[PilotDual3Ether.Mc...March 31, 1988 7:26:35 pm PST...Willie-Sue];
* 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.
* ICSBD3: LONG POINTER TO InputControllerStatusBlock = LOOPHOLE[177700B];
* InputControllerStatusBlock: TYPE = MACHINE DEPENDENT RECORD [
MC[ICSBD3.next, 177700]; * next: POINTER TO IOControlBlock,
MC[ICSBD3.interruptMask, 177701]; * interruptMask: WORD,
MC[ICSBD3.host, 177702]; * host: CARDINAL, -- if >=0, conventional one-host style
MC[ICSBD3.missed, 177703]; * missed: CARDINAL,
* skipOverOCSB: ARRAY [4..30B) OF CARDINAL,
MC[ICSBD3.hosts, 177730]; * hosts: PACKED ARRAY [0..256) OF BOOL,
* ... ] -- Other stuff microcode doesn't use
* OCSBD3: LONG POINTER TO OutputControllerStatusBlock = LOOPHOLE[177710B];
* OutputControllerStatusBlock: TYPE = MACHINE DEPENDENT RECORD [
MC[OCSBD3.next, 177710]; * next: POINTER TO IOControlBlock,
MC[OCSBD3.interruptMask, 177711]; * interruptMask: WORD,
MC[OCSBD3.minPacketSpacing, 177712]; * minPacketSpacing: CARDINAL, -- 32us units
* ... ] -- Other stuff microcode doesn't use
* IOControlBlock: TYPE = MACHINE DEPENDENT RECORD [
MC[IOCBD3.next, 0]; * next: POINTER TO IOControlBlock,
MC[IOCBD3.completion, 1]; * completion: CompletionStatus,
MC[IOCBD3.wordsUsed, 2]; * wordsUsed: CARDINAL,
MC[IOCBD3.load, 3]; * load: CARDINAL,
MC[IOCBD3.length, 4]; * length: CARDINAL,
MC[IOCBD3.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];
BR[D3EIBR, 16]; * Dual 3 MB Ethernet Input base register
BR[D3EOBR, 17]; * Dual 3 MB Ethernet Output base register
*----------------------------------------------------------------------------
* TASK assignments
*----------------------------------------------------------------------------
TaskN[D3EIT,12];
TaskN[D3EOT,10];
*----------------------------------------------------------------------------
* TIOA assignments
*----------------------------------------------------------------------------
Device[D3EData, 25]; * 10 Megabit Ethernet data input/output
Device[D3EControl, 26]; * NS Ethernet control reg [Tx,Rx,Test]
*----------------------------------------------------------------------------
* Register assignments
*----------------------------------------------------------------------------
Set[!D3EIRegs, !FInRegion];
Set[!D3EORegs, !LTRegion];
SetRMRegion[D3EIRegs];
RVN[D3EICB]; * Pointer to IOCB being worked on
RVN[D3EIPtr]; * Input main loop pointer/count
RVN[D3EITemp1]; * Input temporaries
RVN[D3EITemp2];
SetRMRegion[D3EORegs]; * Used by output task -- first 3 must parallel EIRegs
RVN[D3EOCB]; * Pointer to IOCB being worked on
RVN[D3EOPtr]; * Output main loop pointer/count
RVN[D3EOTemp1]; * Output temporary
RVN[D3EOTemp2]; * Output temporary
RVN[D3EOTime]; * Time at end of last packet successfully transmitted
RVN[D3RNum]; * State for random number generator
RVN[D3RConst]; * Constant (13849) for random number generator
RVRel[D3ExCB, And[IP[D3EICB], 17]];
RVRel[D3ExPtr, And[IP[D3EIPtr], 17]];
RVRel[D3ExTemp1, And[IP[D3EITemp1], 17]];
*----------------------------------------------------------------------------
* Emulator Task -- ResetEther instruction.
* Branched to from Mesa emulator to reset Ethernet hardware and tasks.
*----------------------------------------------------------------------------
Set[XTask, IP[EMU]]; * Emulator mode
TopLevel;
DontKnowRBase;
D3ResetEther:
T← D3EControl;
TIOA← T;
TaskingOff; * Smash the hardware off
T← TurnOffRx, Call[OutputGetsT];
T← TurnOffTx, Call[OutputGetsT];
Call[D3EITInitPC]; * Reset the tasks
LdTPC← T, Wakeup[D3EIT];
Call[D3EOTInitPC];
LdTPC← T, Wakeup[D3EOT];
T← A0, TaskingOn;
TIOA← T, IFUJump[0];
*----------------------------------------------------------------------------
* Initialization code for Input Task
*----------------------------------------------------------------------------
Set[XTask, IP[D3EIT]];
Subroutine;
D3EITInitPC: T← D3EIT, CoReturn;
TopLevel;
T← D3EControl;
TIOA← T, Block, Branch[D3EIIdle]; * full TIOA for initialization
*----------------------------------------------------------------------------
* Initialization code for Output Task
*----------------------------------------------------------------------------
Set[XTask, IP[D3EOT]];
Subroutine;
D3EOTInitPC:
T← D3EOT, CoReturn;
TopLevel;
RBase← RBase[D3EORegs];
T← 33000C; * Init random number generator
D3RConst← T+(31C); * Constant (33031B = 13849)
T← D3EControl;
TIOA← T, Block, Branch[D3EOIdle]; * full TIOA for initialization
*----------------------------------------------------------------------------
* Input Task
*----------------------------------------------------------------------------
Set[XTask, IP[D3EIT]]; * Non-emulator mode
* Idle state of the Ethernet input task.
* Wake up here when first word of a new packet arrives.
D3EIIdle: MemBase← IOBR;
T← ICSBD3.next;
Fetch← T, T← ICSBD3.host;
PD← MD, RBase← RBase[D3EIRegs]; * Is there an IOCB?
D3EICB← MD, TIOA[D3EData],
Branch[D3EINoCB, ALU=0]; * Branch if no IOCB
Fetch← T, D3EITemp2← T← Input, * Fetch ICSBD3.host, save first word of packet
Branch[D3EIPLZ, IOAtten]; * Branch if zero-length packet
* Address filtering.
PD← NOT MD, TIOA[D3EControl]; * (Done upside-down for placement)
T← A0, Branch[D3MultiTest, ALU>=0]; * Use multicast method if ICSBD3.host is negative
* Traditional method: test for single host, broadcast, or promiscuous.
T← RCY[T, D3EITemp2, 10], Call[D3EAdrCk]; * T← destination host, test for zero
PD← T-MD, Call[D3EAdrCk]; * Test for destination host = me
PD← MD, Call[D3EAdrCk]; * Test for me = 0 (promiscuous)
* Packet not accepted by filter.
* Tell hardware to ignore the rest of this packet.
D3EIIgn: T← WaitForBOP; * Wakeup at start of next packet
D3EILast: Output← T, Block, Branch[D3EIIdle];
Subroutine;
D3EAdrCk: Branch[D3Accept, ALU=0];
Return;
TopLevel;
* Multicast method: use destination as an index into the ICSB.hosts array.
D3MultiTest:
T← RSH[D3EITemp2, 14]; * T ← Word offset
T← T+(ICSBD3.hosts);
Fetch← T, D3EITemp1← D3EITemp2;
T← MD, D3EIPtr← ShC;
D3EITemp1← T, ShC← D3EITemp1; * Shift count ← low 4 bits of destination host
* (D3EITemp1[4:7]); R/T select bits get garbage
PD← ShiftNoMask[D3EITemp1]; * Left-justify selected bit (from D3EITemp1 or T)
ShC← D3EIPtr, Branch[D3EIIgn, ALU>=0]; * Reject if selected bit is zero
Nop;
* Packet accepted by filter. Set up buffer pointer and count.
D3Accept: D3EIPtr← A0, Call[D3EIOCBSetup];
T← D3EITemp2, TIOA[D3EData], Block; * Recover first data word, await next
* 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.
D3EIPtr← (Store← D3EIPtr)+1, DBuf← T, Branch[D3EIEnd, 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.
D3EIPtr← D3EITemp1; * Set packet length = buffer length
T← Input, Branch[D3EIEnd1, IOAtten]; * Branch if end-of-packet
T← CS.bufferOverflow, Branch[D3EIPost];
* 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.
D3EIEnd: T← (D3EITemp1)-1; * Initial buffer length
D3EIPtr← (D3EIPtr)+T; * Actual packet length
T← Input; * Read status word
D3EIEnd1: T← T OR (CS.done); * Done completion status
* Post input completion status now in T.
D3EIPost: D3EITemp1← ICSBD3.next;
Call[D3EPost];
Block, Branch[D3EIIdle]; * 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.
D3EIPLZ: D3EIPtr← A0, Branch[D3EIEnd1]; * Length← 0, post done status
* Discard packet because IOCB queue is empty.
D3EINoCB: T← T+1, TIOA[D3EControl]; * Increment ICSBD3.missed
Fetch← T;
D3EITemp1← MD+1;
Store← T, DBuf← D3EITemp1, Branch[D3EIIgn]; * Ignore rest of packet
*----------------------------------------------------------------------------
* Output Task
*----------------------------------------------------------------------------
Set[XTask, IP[D3EOT]];
* Idle state of the Ethernet output task. TIOA=EControl.
* Wake up here at end of previous packet or when poked by software.
D3EOIdle: MemBase← IOBR;
T← OCSBD3.next; * Fetch OCSB.next
T← (Fetch← T)+(Sub[OCSBD3.minPacketSpacing!, OCSBD3.next!]C);
RBase← RBase[D3EORegs];
Fetch← T, D3EOCB← PD← MD; * Fetch OCSBD3.minPacketSpacing
T← (D3EOCB)+(IOCBD3.load),
Branch[D3EONoCB, ALU=0]; * Go shut off transmitter if no IOCB
* Test and update load
Fetch← T, D3EOTemp2← MD; * Fetch IOCB.load
D3EOTemp1← MD+MD+1; * Load lsh 1 +1
Store← T, DBuf← D3EOTemp1, D3EOTemp1← MD, * Store new load, save current
Call[D3Random]; * 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← ((D3EOTemp1) AND T) LSH 1, Branch[D3ELodOv, R<0];
PD← T-T, Branch[D3EWaitC, ALU#0]; * Carry← 1; branch if wait required
* On first transmission attempt, enforce minimum inter-packet spacing.
* Note: RTClock counts 32-usec intervals. D3EOTemp2 = OCSB.minPacketSpacing
T← D3EOTime, RBase← RBase[RTClock]; * Time last transmission ended
T← (RTClock)-T, RBase← RBase[D3EORegs]; * Inter-packet spacing
T← ((D3EOTemp2)-T-1) LSH 1; * Compare with min permitted
D3EWaitC: D3EOTemp1← T-1, Branch[D3EOGo, 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.
D3EWait: T← CountDown;
D3EOTemp1← (D3EOTemp1)-1, Output← T, Block, Branch[.-1, R>=0];
* Now time to transmit packet. Set up buffer pointer and count.
D3EOGo: D3EOPtr← T-T-1, Call[D3EIOCBSetup];
D3EOPtr← (Fetch← D3EOPtr)+1; * Pre-fetch the first word
TIOA[D3EData], Branch[D3EOEnd, ALU=0];
* 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.
D3EOPtr← (Fetch← D3EOPtr)+1, T← MD, Branch[D3EOAbrt, IOAtten];
Output← T, Block, Branch[.-1, ALU#0];
* Next-to-last word has been output and last word has been fetched
D3EOEnd: T← D3EOTemp1, Output← MD; * Output last word
D3EOPtr← T, TIOA[D3EControl]; * 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.
D3EOTemp1← Input, Branch[D3EOAbr1, IOAtten]; * Branch if aborted, get status
RBase← RBase[RTClock]; * Remember current time
T← RTClock, RBase← RBase[D3EORegs];
D3EOTime← T;
D3EODone: T← (D3EOTemp1) AND (377C); * Just the status bits
T← T OR (CS.done); * Done completion status
* Post ending status and reset interface for next packet.
D3EOPost: D3EOTemp1← OCSBD3.next;
Call[D3EPost]; * Post status in T
T← TurnOffTx, Branch[D3EORest]; * Turn transmitter off and on again
* Here if collision or other error occurred.
* If a collision, just try again. If not, post output done.
D3EOAbrt: T← (D3EOTemp1)-1; * Initial buffer length
D3EOPtr← (D3EOPtr)+T, TIOA[D3EControl]; * Words actually sent
D3EOTemp1← Input; * Read status
* If not a collision, post output done with whatever status bits we got.
D3EOAbr1: PD← (D3EOTemp1) AND (TxCollision); * Collision?
T← TurnOffTx, Branch[D3EODone, ALU=0]; * Branch if not
D3EORest: Output← T; * Turn off to reset collision bit
T← TurnOnTx, Branch[D3EOLast]; * Turn on and try again
* Load overflow, post error status
D3ELodOv: T← CS.loadOverflow, Branch[D3EOPost]; * Load overflow post code
* Shut off hardware because IOCB queue is empty.
D3EONoCB: T← TurnOffTx, Branch[D3EOLast];
D3EOLast: Output← T, Block, Branch[D3EOIdle];
*----------------------------------------------------------------------------
* Task-independent Subroutines
*----------------------------------------------------------------------------
*----------------------------------------------------------------------------
D3EIOCBSetup: * Set up IOCB pointers and counts
* Enter: ExCB = IOCBD3 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← (D3ExCB)+(IOCBD3.length);
T← (Fetch← T)+1; * Fetch IOCBD3.length
T← (Fetch← T)+1, D3ExTemp1← MD; * Fetch low buffer pointer
Fetch← T, T← MD, * Fetch high buffer pointer
D3ExPtr, Branch[.+2, R<0]; * Which base register?
T← T+(D3ExTemp1), MemBase← D3EIBR, Branch[.+2]; * Low pointer + length
T← T+(D3ExTemp1), MemBase← D3EOBR; * Low pointer + length
T← MD, BRLo← T;
T← T-1, XorSavedCarry; * Adjust high for negative indexing
T← D3ExTemp1, BRHi← T;
D3ExPtr← (0S)-T, Return; * ExPtr← negative count
*----------------------------------------------------------------------------
D3EPost: * 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;
D3ExCB← (Fetch← D3ExCB)+1; * Fetch IOCBD3.next
T← (Store← D3ExCB)+1, DBuf← T; * Store IOCB.completion
Store← T, DBuf← D3ExPtr, T← MD; * Store IOCB.wordsUsed
D3ExTemp1← (Store← D3ExTemp1)+1, DBuf← T; * Store xCSB.next
Fetch← D3ExTemp1; * Fetch xCSBD3.interruptMask
RBase← RBase[NWW]; * Initiate interrupts
NWW← (NWW) OR MD, Reschedule, Return;
*----------------------------------------------------------------------------
D3Random: * Generate random number and return it in T.
* Must have RBase[D3RNum] (=D3EORegs).
* Uses algorithm:
* R← (2^11 + 2^2 + 2^0)* R + 13849
*----------------------------------------------------------------------------
Subroutine;
KnowRBase[D3RNum];
T← LSH[D3RNum, 11]; * T← 2^9 * R
T← T+(D3RNum); * (2^9 + 2^0)* R
T← LSH[T, 2]; * (2^11 + 2^2)* R
T← T+(D3RNum); * (2^11 + 2^2 + 2^0)* R
T← D3RNum← T+(D3RConst), Return; * +13849
TopLevel;