<> <> <> <<>> <> Directory Rope, DragOpsCross; Imports BitOps, Dragon, RoseRun, RoseTypes, DragOpsCrossUtils; Open BitOps; CELLTYPE "EU" PORTS [ <<>> <<-- Timing and housekeeping interface>> PhA, nPhA, PhB, nPhB> <<-- Main memory interface>> MHold, MnReset> <<-- Serial debugging interface>> DShiftBOOL, -- Tristate; changes in PhA after a PhB that DShiftAB is Asserted <<>> <<-- P Interface>> EPData=INT[32], -- address/data to cache/FP during PhA, data to/from cache/FP during PhB EPParityB=BOOL, EPRejectB> KBus= INT[32], <> < IFU iff cAdr in [ifuXBus..ifuLast] during the previous PhiB, otherwise IFU may be using bus to pass data to EU or control to FP.>> <<>> EUAluLeftSrc1BA < EnumType["Dragon.ALULeftSources"], EUAluRightSrc1BA < EnumType["Dragon.ALURightSources"], EUStore2ASrc1BA < EnumType["Dragon.Store2ASources"], EUAluOp2AB < EnumType["Dragon.ALUOps"], EUCondSel2AB < EnumType["Dragon.CondSelects"], EURes3AisCBus2BA < BOOL, -- multiplexor control to result3AB EUSt3AisCBus2BA < BOOL, -- multiplexor control to store3AB EUCondition2BA > BOOL, -- latched to hide precharge in the carry propagator etc EURes3BisPBus3AB < BOOL, -- multiplexor control to result3BA EUCheckParity3AB < BOOL, EULoadField3BA < BOOL, -- means cAdr=euField, received at the same time as the cAdr EUWriteToPBus3AB < BOOL -- Tells EU to drive PBus on 3B. Used for Store instructions, and issued every instruction following an EPrejectB. This is not necessary, but so far OK and simple. <<>> ] State <<-- Pipeline registers>> leftOp2AB, rightOp2AB, store2AB, result2BA, store2BA, result3AB, store3AB, result3BA: Dragon.HexWord, <<-- RAM, RAM addresses and various aliased registers>> <<-- The RAM is actually 128 bits wide.>> <<-- The RAM is organised as follows (Ref DragOpsCross):>> <> <<128: euJunk i.e. no write>> <<129: (currently spare)>> <<130: euMAR, stores the address causing a cache fault, one cycle after the fault>> <<131: euField aliased with field (when writing in euField, IFU also sends EULoadField3BA)>> <<132 through 143 are FP registers>> <<144 through 155 are constants>> <<156 through 159 (currently spare)>> <<160 through 175 are auxilliary>> <<176 through 239: not legal>> <<240 through 255: not in EU but on cAdr trigger drive of KBus; easily detected as 1111xxxx>> <<-- Registers physically present in the EU:>> <<0..127 128 Stack>> <<128, 129 130,131 4 junk, spare, MAR, field>> <<132..143 12 FP regs>> <<144..155 12 constants>> <<160..175 16 auxilliary>> << ---->> <> <<>> aAdr, bAdr, cAdr: Dragon.HexByte, ram: ARRAY Dragon.HexByte OF Dragon.HexWord, field: Dragon.HexWord, -- Field descriptor for the field unit. A copy is kept as RAM[fieldAdr], another copy is in the field unit. Any write is performed to both physical locations (specified by the EULoadField3BA only), any read is from the most convenient location: RAM for usual reads, field for field unit control. <<>> <<-- Bits and pieces for the ALU and the Field Unit>> kBusAB: Dragon.HexWord, -- a register to hold KBus for use in the field unit if required carryAB, carryBA: BOOL, -- carryBA is the output of the adder modified by the opcode of the previous operation and latched if no trap; carryAB is a copy of carryBA if there is no reject or fault. <<-- Other pieces from the Control pipeline>> rejectBA: BOOL, -- a copy of EPRejectB stable during PhiA faultBA: BOOL, -- a latched version of EPFaultB#None AND rejectBA; it carries a heavy responsibility: on the following cycle, the IFU promises to send cAdr=euMAR, and the EU promises to preserve the carry. parityStore3AB: BOOL, hold1BA, hold2AB, hold2BA, hold3AB: BOOL Initializer ram[Dragon.PRtoByte[euConstant]] _ 0; -- done by IFU EvalSimple <<-- PhA phase. Note that rejectBA alone inhibits almost any state change during PhA>> <<>> <<-- DBus update>> IF PhA THEN {hold2AB _ hold1BA; hold3AB _ hold2BA;}; <<>> IF PhA AND ~hold2BA THEN BEGIN <<>> -- Cycle carry if no reject and no trap in sight IF NOT (rejectBA OR EUCondition2BA) THEN carryAB _ carryBA; <<-- Always send address to Cache during PhiA (reject???)>> EPData _ Dragon.LTD[result2BA]; <<-- Loading field>> IF EULoadField3BA THEN field _ result3BA; IF NOT rejectBA THEN BEGIN kBusAB _ Dragon.LFD[KBus]; leftOp2AB _ SELECT EUAluLeftSrc1BA FROM aBus => ram[aAdr], rBus => result2BA, cBus => result3BA, ENDCASE => ERROR; rightOp2AB _ SELECT EUAluRightSrc1BA FROM bBus => ram[bAdr], rBus => result2BA, cBus => result3BA, kBus => Dragon.LFD[KBus], ENDCASE => ERROR; store2AB _ SELECT EUStore2ASrc1BA FROM bBus => ram[bAdr], cBus => result3BA, rBus => result2BA, ENDCASE => ERROR; result3AB _ SELECT EURes3AisCBus2BA FROM TRUE => result3BA, ENDCASE => result2BA; store3AB _ SELECT EUSt3AisCBus2BA FROM TRUE => result3BA, ENDCASE => store2BA; <<-- It is particularly important that no store in the RAM happens during rejectBA: rejectBA should run through the cAdr decoder>> SELECT TRUE FROM <<-- cAdr IN 0 to 175>> cAdr IN [Dragon.PRtoByte[euStack] .. Dragon.PRtoByte[euBogus]) => ram[cAdr] _ result3BA; <<-- cAdr IN 240 to 255>> cAdr IN [Dragon.PRtoByte[ifuXBus] .. Dragon.PRtoByte[ifuLast]] => KBus _ Dragon.LTD[result3BA]; ENDCASE => Dragon.Assert[FALSE, "EU cAdr out of range"]; END; END; <<>> <<>> <<-- PhiB phase. Most of the computations take place during PhiB>> <<-- DBus update>> IF PhB THEN {hold1BA _ MHold; hold2BA _ hold2AB;}; IF PhB AND ~hold3AB THEN -- EPFaultB ??? BEGIN -- Temporary PlusOrMinusAluRight, aluOut: Dragon.HexWord; c32, overflow, conditionB: BOOL; rejectBA _ EPRejectB; <> faultBA _ EPFaultB#Dragon.None AND EPRejectB; <> aAdr _ ECFD[KBus, 32, 0, 8]; bAdr _ ECFD[KBus, 32, 8, 8]; cAdr _ ECFD[KBus, 32, 16, 8]; -- aAdr in proper range (push in finer simulation???) Dragon.Assert[ aAdr IN [0..128) -- stack OR aAdr IN [130..135] -- euMAR, euField, and FP add OR aAdr IN [140..143] -- FP mult OR aAdr IN [144..155] -- constants OR aAdr IN [160..176)]; -- aux -- bAdr in proper range Dragon.Assert[ bAdr IN [0..128) -- stack OR bAdr IN [130..135] -- euMAR, euField, and FP add OR bAdr IN [140..143] -- FP mult OR bAdr IN [144..155] -- constants OR bAdr IN [160..176)]; -- aux -- cAdr in proper range Dragon.Assert[ -- EUMar cannot be written using cAdr cAdr IN [0..128] -- stack+euJunk OR cAdr IN [131..135] -- no MAR, but euField and FP add OR cAdr IN [140..143] -- FP mult OR cAdr IN [144..155] -- constants OR cAdr IN [160..176] -- aux OR cAdr IN [240..255]]; -- IFU regs <<-- PBus: notice that in case of reject during a store, we keep sending the data even though it is useless>> <<-- The default source for result3BA is result3AB; the only case where we update that register with the value found on the PBus is when a fetch terminates with no reject.>> Dragon.Assert[NOT (EUWriteToPBus3AB AND EURes3BisPBus3AB)]; -- just for simulation IF EUWriteToPBus3AB THEN -- store in progress BEGIN EPData _ Dragon.LTD[store3AB]; -- send data to Cache (Store) result3BA _ result3AB -- save the address in result3BA, done normally since NOT EURes3BisPBus3AB END ELSE -- either a fetch, an op, or a move in progress, but no write on the PBus SELECT TRUE FROM ~rejectBA AND EURes3BisPBus3AB => -- Fetch without reject result3BA _ Dragon.LFD[EPData]; rejectBA OR (~rejectBA AND ~EURes3BisPBus3AB) => -- Fetch with reject, op, move result3BA _ result3AB; ENDCASE => ERROR; <> store2BA _ store2AB; <> <> carryBA _ carryAB; -- ??? SELECT EUAluOp2AB FROM SAdd => { [aluOut, c32] _ DoubleADD[leftOp2AB, rightOp2AB, carryAB]; result2BA _ aluOut; carryBA _ FALSE}; SSub => { [aluOut, c32] _ DoubleSUB[leftOp2AB, rightOp2AB, carryAB]; result2BA _ aluOut; carryBA _ FALSE}; UAdd => { [aluOut, c32] _ DoubleADD[leftOp2AB, rightOp2AB, carryAB]; result2BA _ aluOut; carryBA _ c32}; USub => { [aluOut, c32] _ DoubleSUB[leftOp2AB, rightOp2AB, carryAB]; result2BA _ aluOut; carryBA _ NOT c32}; VAdd, VAdd2 => { [aluOut, c32] _ DoubleADD[leftOp2AB, rightOp2AB, FALSE]; result2BA _ aluOut}; VSub => { [aluOut, c32] _ DoubleSUB[leftOp2AB, rightOp2AB, FALSE]; result2BA _ aluOut}; LAdd => { [aluOut, c32] _ DoubleADD[leftOp2AB, rightOp2AB, FALSE]; result2BA _ aluOut; carryBA _ FALSE}; LSub => { [aluOut, c32] _ DoubleSUB[leftOp2AB, rightOp2AB, FALSE]; result2BA _ aluOut; carryBA _ FALSE}; FOP => { -- field descriptor provided by Field result2BA _ aluOut _ FieldOp[leftOp2AB, rightOp2AB, field]}; FOPK => { -- field descriptor provided by kBusAB. Otherwise identical to FOP result2BA _ aluOut _ FieldOp[leftOp2AB, rightOp2AB, kBusAB]}; And => { result2BA _ aluOut _ WordOp[and, leftOp2AB, rightOp2AB]}; Or => { result2BA _ aluOut _ WordOp[or, leftOp2AB, rightOp2AB]}; Xor => { result2BA _ aluOut _ WordOp[xor, leftOp2AB, rightOp2AB]}; BndChk => { [aluOut, c32] _ DoubleSUB[leftOp2AB, rightOp2AB, FALSE]; result2BA _ leftOp2AB}; MulLd, MulStep, RdMQ, DivLdDbl, DivCk, DivStep, DivAdjQ, DivAdjR => {NULL}; -- ??? ENDCASE => ERROR Stop["Invalid ALU Operation"]; <<-- Here we compute the overflow by checking the high-order bits of the operands (leftOp2AB and rightOp2AB or - rightOp2AB) and of the result>> SELECT EUAluOp2AB FROM SSub, USub, VSub, LSub => [PlusOrMinusAluRight, ] _ DoubleSUB[0, rightOp2AB, FALSE]; ENDCASE => PlusOrMinusAluRight _ rightOp2AB; overflow _ (EBFL[leftOp2AB, 0] = EBFL[PlusOrMinusAluRight, 0]) AND (EBFL[leftOp2AB, 0] # EBFL[aluOut, 0]); <<-- Condition and trap generation>> conditionB _ SELECT EUCondSel2AB FROM False => FALSE, EZ => aluOut=0, -- not necessarily after a sub!!! LZ => EBFL[aluOut, 0], -- aluOut<0 by checking the high-order bit LE => (aluOut=0) OR EBFL[aluOut, 0], -- aluOut<=0, True => TRUE, NE => aluOut#0, GE => NOT EBFL[aluOut, 0], GZ => NOT ((aluOut=0) OR EBFL[aluOut, 0]), -- aluOut>0, OvFl => overflow, BC => EBFL[leftOp2AB, 0] OR -- arg < 0 NOT EBFL[aluOut, 0], -- arg-limit >= 0 IL => (EBFL[leftOp2AB, 0]#EBFL[leftOp2AB, 1]) OR (EBFL[rightOp2AB, 0]#EBFL[rightOp2AB, 1]) OR (EBFL[aluOut, 0]#EBFL[aluOut, 1]), -- No: 3 bits!!! DivOvFl => FALSE, -- for now... NotOvFl => NOT overflow, NotBC => NOT EBFL[leftOp2AB, 0] -- 0<=arg -- AND EBFL[aluOut, 0] -- arg-limit<0 --, NotIL => NOT ((EBFL[leftOp2AB, 0]#EBFL[leftOp2AB, 1]) OR (EBFL[rightOp2AB, 0]#EBFL[rightOp2AB, 1]) OR (EBFL[aluOut, 0]#EBFL[aluOut, 1])), Kernal => ECFD[Dragon.LTD[aluOut], 32, 0, 4]=0, -- msb(aluOut, 4)=0 ENDCASE => ERROR Stop["Invalid EUCondition2BA Code"]; EUCondition2BA _ conditionB; END; <<>> Test ExerciseEveryBlock BlackBox <<>> <<-- All test code is in euTest.mesa>> <<>> ENDCELLTYPE; Cedar fieldAdr: INTEGER = Dragon.PRtoByte[euField]; marAdr: INTEGER = Dragon.PRtoByte[euMAR]; fpAluClear: INTEGER = Dragon.PRtoByte[fpAluClear]; fpAluSgl: INTEGER = Dragon.PRtoByte[fpAluSgl]; fpAluLsw: INTEGER = Dragon.PRtoByte[fpAluLsw]; fpAluMsw: INTEGER = Dragon.PRtoByte[fpAluMsw]; fpMultClear: INTEGER = Dragon.PRtoByte[fpMultClear]; fpMultSgl: INTEGER = Dragon.PRtoByte[fpMultSgl]; fpMultLsw: INTEGER = Dragon.PRtoByte[fpMultLsw]; fpMultMsw: INTEGER = Dragon.PRtoByte[fpMultMsw]; Remark: PROC [ message: Rope.ROPE ] = {RoseRun.DisableableStop[Remark, message]}; <<--Replace by straight logic functions; no case statements>> FieldOp: PROC[aluLeft, rightOp2AB, fieldDesc: Dragon.HexWord] RETURNS [result:Dragon.HexWord] = BEGIN fd: DragOpsCross.FieldDescriptor _ DragOpsCrossUtils.CardToFieldDescriptor[fieldDesc MOD 65536]; shiftout: BitDWord _ [0,0]; -- to begin cleanly maskhole: BitDWord _ doubleMasks[fd.mask]; <> Dragon.Assert[fd.shift < 33]; Dragon.Assert[fd.mask < 33]; SELECT fd.shift FROM 0 => shiftout _ Dragon.LTD[aluLeft]; -- no shift 32 => shiftout _ Dragon.LTD[rightOp2AB]; -- full shift IN (0..32) => BEGIN <> shiftout _ MDTD [Dragon.LTD[aluLeft], 32, fd.shift, 32-fd.shift, shiftout, 32, 0, 32-fd.shift]; <> shiftout _ MDTD [Dragon.LTD[rightOp2AB], 32, 0, fd.shift, shiftout, 32, 32-fd.shift, fd.shift]; END; ENDCASE => ERROR; result _ Dragon.LFD[DAND[shiftout, maskhole]]; -- mask shiftout IF fd.insert THEN BEGIN mask2: BitDWord _ doubleMasks[fd.shift]; -- another intermediate mask <> maskhole _ DNOT[DXOR[maskhole, mask2],32]; <> result _ WordOp[or, result, WordOp[and, rightOp2AB, Dragon.LFD[maskhole] ] ]; END; END; <> DoubleADD: PROC[al, bl: Dragon.HexWord, carry: BOOL] RETURNS [sl: Dragon.HexWord, c32: BOOL] = BEGIN Xor: PROC[x, y: BOOL] RETURNS [z: BOOL] = {RETURN[~x=y]}; ai, bi: BOOL; s: BOOL _ FALSE; c: BOOL _ carry; i: INTEGER; sum: BitDWord _ [0,0]; FOR i IN [1..32] DO ai _ EBFL[al, 32-i]; bi _ EBFL[bl, 32-i]; s _ Xor[ai, Xor[bi, c]]; c _ (ai AND bi) OR (bi AND c) OR (ai AND c); sum _ IBID[s, sum, 32, 32-i]; ENDLOOP; RETURN[Dragon.LFD[sum], c]; END; <> <> DoubleSUB: PROC[a, b: Dragon.HexWord, carry: BOOL] RETURNS [dif: Dragon.HexWord, c32: BOOL] = {[dif, c32] _ DoubleADD[a, WordOp[not,b], ~carry]}; WordOp: PROC[op: {not, or, and, xor}, left, right: Dragon.HexWord _ 0] RETURNS[result: Dragon.HexWord] = { RETURN[Dragon.LFD[SELECT op FROM not => DNOT [Dragon.LTD[left], 32], or => DOR [Dragon.LTD[left], Dragon.LTD[right]], and => DAND [Dragon.LTD[left], Dragon.LTD[right]], xor => DXOR [Dragon.LTD[left], Dragon.LTD[right]], ENDCASE => ERROR]] }; EBFL: PROC[word: Dragon.HexWord, index: CARDINAL] RETURNS[BOOL] = { RETURN[ EBFD[Dragon.LTD[word], 32, index]] }; DblShiftRt: PROC[msb: BOOL, ltIn, rtIn: Dragon.HexWord] RETURNS[ltOut, rtOut: Dragon.HexWord] = {rtOut _ ShiftRt[EBFL[ltIn, 31], rtIn]; ltOut _ ShiftRt[msb, ltIn]}; ShiftRt: PROC[msb: BOOL, w: Dragon.HexWord] RETURNS[r: Dragon.HexWord] = {r_Dragon.LFD[IBID[msb, MDTD[Dragon.LTD[w],32,0,31,[0,0],32,1,31],32,0]]}; DblShiftLt: PROC[ltIn, rtIn: Dragon.HexWord, lsb: BOOL] RETURNS[cry: BOOL, ltOut, rtOut: Dragon.HexWord] = {cry _ EBFL[ltIn,0]; ltOut _ ShiftLt[ltIn, EBFL[rtIn, 0]]; rtOut _ ShiftLt[rtIn, lsb]}; ShiftLt: PROC[w: Dragon.HexWord, lsb: BOOL] RETURNS[r: Dragon.HexWord] = {r_Dragon.LFD[IBID[lsb, MDTD[Dragon.LTD[w],32,1,31,[0,0],32,0,31],32,31]]}; <<-- To do>> <> <> <>