eu.Mesa
created by RoseTranslate 2.8.8 of January 28, 1985 2:26 pm PST from eu.Rose of June 10, 1985 12:11:20 pm PDT
created for gunther.pa
created at June 17, 1985 4:19:58 pm PDT
DIRECTORY
RoseTypes, RoseCreate, Rope, DragOpsCross, BitOps, Dragon, RoseRun, DragOpsCrossUtils;
eu: CEDAR PROGRAM
IMPORTS RoseCreate, BitOps, Dragon, RoseRun, RoseTypes, DragOpsCrossUtils =
BEGIN OPEN
RoseTypes, BitOps;
Signal Type decls
PBusFaults: TYPE = Dragon.PBusFaults;
ALULeftSources: TYPE = Dragon.ALULeftSources;
ALURightSources: TYPE = Dragon.ALURightSources;
Store2ASources: TYPE = Dragon.Store2ASources;
ALUOps: TYPE = Dragon.ALUOps;
CondSelects: TYPE = Dragon.CondSelects;
RegisterCells: PROC =
BEGIN
[] ← RoseCreate.RegisterCellType[name: "EU",
expandProc: NIL,
ioCreator: CreateEUIO, initializer: InitializeEU,
evals: [EvalSimple: EUEvalSimple],
tests: LIST[[name: "ExerciseEveryBlock", proc: EUTestExerciseEveryBlock, stateToo: FALSE]],
ports: CreateEUPorts[],
driveCreator: CreateEUDrive];
END;
otherss: SymbolTable ← RoseCreate.GetOtherss["eu.pass"];
CreateEUPorts: PROC RETURNS [ports: Ports] = {ports ← RoseCreate.PortsFromFile["eu.EU.rosePorts"]};
EUIORef: TYPE = REF EUIORec;
EUIORec: TYPE = MACHINE DEPENDENT RECORD [
PhA(0): BOOLEAN,
nPhA(1): BOOLEAN,
PhB(2): BOOLEAN,
nPhB(3): BOOLEAN,
Vdd(4): BOOLEAN,
Gnd(5): BOOLEAN,
PadVdd(6): BOOLEAN,
PadGnd(7): BOOLEAN,
MHold(8): BOOLEAN,
MnReset(9): BOOLEAN,
DShift(10): BOOLEAN,
DExecute(11): BOOLEAN,
DNSelect(12): BOOLEAN,
DDataIn(13): BOOLEAN,
DDataOut(14): BOOLEAN,
EPData(15): ARRAY [0..2) OF CARDINAL,
EPParityB(17): BOOLEAN,
EPRejectB(18): BOOLEAN,
EPFaultB(19): PBusFaults,
EPNPErrorB(20): BOOLEAN,
KBus(21): ARRAY [0..2) OF CARDINAL,
EUAluLeftSrc1BA(23): ALULeftSources,
EUAluRightSrc1BA(24): ALURightSources,
EUStore2ASrc1BA(25): Store2ASources,
EUAluOp2AB(26): ALUOps,
EUCondSel2AB(27): CondSelects,
EURes3AisCBus2BA(28): BOOLEAN,
EUSt3AisCBus2BA(29): BOOLEAN,
EUCondition2BA(30): BOOLEAN,
EURes3BisPBus3AB(31): BOOLEAN,
EUCheckParity3AB(32): BOOLEAN,
EULoadField3BA(33): BOOLEAN,
EUWriteToPBus3AB(34): BOOLEAN];
EUDriveRef: TYPE = REF EUDriveRec;
EUDriveRec: TYPE = RECORD [tag: DriveTagType, s: PACKED ARRAY EUPort OF DriveLevel];
EUPort: TYPE = MACHINE DEPENDENT {
PhA, nPhA, PhB, nPhB, Vdd, Gnd, PadVdd, PadGnd, MHold, MnReset, DShift, DExecute, DNSelect, DDataIn, DDataOut, EPData, EPParityB, EPRejectB, EPFaultB, EPNPErrorB, KBus, EUAluLeftSrc1BA, EUAluRightSrc1BA, EUStore2ASrc1BA, EUAluOp2AB, EUCondSel2AB, EURes3AisCBus2BA, EUSt3AisCBus2BA, EUCondition2BA, EURes3BisPBus3AB, EUCheckParity3AB, EULoadField3BA, EUWriteToPBus3AB, (39)};
CreateEUIO: PROC [ct: CellType] RETURNS [ioAsAny: REF ANY] --IOCreator-- = {
ioAsAny ← NEW[EUIORec];
};
CreateEUDrive: PROC [ct: CellType] RETURNS [driveAsAny: REF ANY] --DriveCreator-- = {
driveAsAny ← NEW[EUDriveRec];
};
EUStateRef: TYPE = REF EUStateRec;
EUStateRec: TYPE = RECORD [
-- 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
];
InitializeEU: Initializer = {
IF leafily THEN
BEGIN
ioRec: EUIORef ← NARROW[cell.realCellStuff.newIO];
state: EUStateRef ← NEW[EUStateRec];
cell.realCellStuff.state ← state;
BEGIN OPEN ioRec, state;
ram[Dragon.PRtoByte[euConstant]] ← 0; -- done by IFU
END;
END;
};
EUEvalSimple: CellProc =
BEGIN
newIO: EUIORef ← NARROW[cell.realCellStuff.newIO];
state: EUStateRef ← NARROW[cell.realCellStuff.state];
BEGIN OPEN newIO, state;
-- 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;
END;
END;
EUTestExerciseEveryBlock: CellTestProc =
BEGIN
instructions: EUIORef ← NARROW[io];
drive: EUDriveRef ← NARROW[driveAsAny];
BEGIN OPEN instructions;
-- All test code is in euTest.mesa
END;
END;
explicitly requested 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]]};
RegisterCells[];
END.