EU.rose
Copyright © 1984 by Xerox Corporation. All rights reserved.
Last edited by: Monier, June 10, 1985 12:11:19 pm PDT
??? shows something that has to be discussed and fixed
Directory Rope, DragOpsCross;
Imports BitOps, Dragon, RoseRun, RoseTypes, DragOpsCrossUtils;
Open BitOps;
CELLTYPE "EU"
PORTS [
-- Timing and housekeeping interface
PhA, nPhA, PhB, nPhB<BOOL,
Vdd, Gnd, PadVdd, PadGnd<BOOL,
-- Main memory interface
MHold, MnReset<BOOL, -- MHold must be high before testing (steal a cycle, no matter the state of the other bits, including DNSelectAB); seen at the end of 1, nothing during 2 but hold 3
-- Serial debugging interface
DShift<BOOL,   -- shift the shift register by 1 bit if ~DNSelectAB
DExecute<BOOL,   -- interpret the content of the shift register if ~DNSelectAB
DNSelect<BOOL,   -- if high, hold but don't Execute or Shift (same as DAddress decoded) needed for anything but hold
DDataIn<BOOL,   -- sampled 1 full cycle after a PhB that DShiftAB is Asserted
DDataOut>BOOL, -- 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<BOOL, -- received during PhB
EPFaultB<EnumType["Dragon.PBusFaults"], -- received during PhB, together with the last EPRejectB of a sequence. On the next PhA, the EU still freezes, but saves the Fault address in RAM. No instruction following a faulty Cache access should modify the carry!
EPNPErrorB=BOOL,
-- I interface
KBus= INT[32],
PhiB: a, b, and c ram addresses are multiplexed on KBus. a=[0..7], b=[8..15], c=[16..23],
PhiA: data moves from EU -> 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):
registers 0 through 127 constitute the stack
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
        ----
Total       172
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;
EPRejectB is valid at the end of PhiB but bogus during PhiA, so it must be latched at the end of PhiB. A current problem is that the source for result3BA depends upon EPRejectB, and the choice is made during the same PhiB as it is received. So this statement has to be first. I'll try to get rid of this pb by shooting all cases where EPRejectB is needed during PhiB.
faultBA ← EPFaultB#Dragon.None AND EPRejectB;
Updating the RAM addresses
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;
Data pipe
store2BA ← store2AB;
Alu and Field Unit computation
Set Default values of state
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
    NOTEBFL[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];
maskhole ← a cloud of zeros followed by fd.mask ones
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
Insert 32-fd.shift bits of aluLeft on the left of shiftout
shiftout ← MDTD
[Dragon.LTD[aluLeft], 32, fd.shift, 32-fd.shift, shiftout, 32, 0, 32-fd.shift];
Insert fd.shift bits of rightOp2AB on the right of shiftout
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 ← zeros, then fd.mask-fd.shift ones, then fd.shift zeros.
maskhole ← DNOT[DXOR[maskhole, mask2],32];
Merge shiftout and rightOp2AB using the mask
result ← WordOp[or, result, WordOp[and, rightOp2AB, Dragon.LFD[maskhole] ] ];
END;
END;
Returns a+b+carry and c32, where a and b are considered to be signed numbers
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: BOOLFALSE;
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;
Returns a-b-carry and c32, where a and b are considered to be signed numbers
Implemented as a+(~b)+1+(~carry)
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𡤍ragon.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𡤍ragon.LFD[IBID[lsb, MDTD[Dragon.LTD[w],32,1,31,[0,0],32,0,31],32,31]]};
-- To do
kernal ???
send address to cache during PhA ???
check what happens after a cc=TRUE