P Interface
EPData=INT[32], -- address/data to cache/FP during PhA, data to/from cache/FP during PhB
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!
EPParityB=BOOL, -- during PhB
EPNPErrorB=BOOL, -- parity error
I interface
Changes
LeftOpSources: TYPE = {aBus(0), rBus(1), cBus(2), reserve3(3)};
RightOpSources: TYPE = {bBus(0), rBus(1), cBus(2), kBus(3)};
Store2ASources: TYPE = {bBus(0), rBus(1), cBus(2), reserve3(3)};
EUStore3AGetscBusB
EURes3AGetscBusB
EURes3BGetsPB
KBus= INT[32], -- a, b, and c ram addresses are multiplexed on KBus. a=[0..7], b=[8..15], c=[16..23], addresses during PhiB, data during PhiA, data direction is EU -> IFU if cAdr in [ifuXBus..ifuLast] during the previous PhiB, otherwise it is IFU -> EU or FP.
The following signals should change during PhiB and be stable during PhiA.
EUAluLeftisR1BA, EUAluLeftisR3BA < BOOL, -- same timing as RAM addresses: 1B
EUAluRightisKBA, EUAluRightisR1BA, EUAluRightisR3BA < BOOL, -- same timing as RAM addresses: 1B
EUS1isR1BA, EUS1isR3BA < BOOL, -- same timing as RAM addresses: 1B
EUR2isR3BA < BOOL, -- sent when data is in result1: 2B
EUS3isR3BA < BOOL, -- sent when data is in store2: 2B
EUHoldCarryBA < BOOL, -- received on PhB following a CCTrap or an EUCache Fault, and prevents any carry change on next PhA.
The following signals should change during PhiA and be stable during PhiB.
EUR3isR2AB < BOOL, -- multiplexor control on R3: 3A
EUWriteToPBusAB < BOOL, -- sent by the IFU during 3A, stable during 3B; specifies read/write to Cache on 3B. Used for Store instructions, and issued every instruction following an EPrejectB. This is not necessary, but so far OK and simple.
EUCheckPParityAB < BOOL, -- sent by the IFU during 3A, stable during 3B; instructs EU to Check parity of cache reads.
The following signals are sent by the IFU during 1A, stable during 1B and are used on the next cycle.
EUAluOpAB< EnumType["Dragon.ALUOps"], -- sent on 1A, used during 2B. Not latched by EU.
EUCondSelAB< EnumType["Dragon.CondSelects"], -- idem for timing
Trap is sent by the IFU during 2B and held during 3A.
EUTrapBA< BOOL, -- if selected condition is true then trap
EUConditionBA> BOOL, -- selected condition sent to IFU during 2B, latched during PhA to hide precharge in the carry propagator and other things.
InstrCountAB<INT[32],
Serial debugging interface
All the following signals change during PhA and PhB, giving an entire clock cycle for them to change, are sampled during PhB, and are applied to the data path during the following PhA.
DShiftAB<BOOL, -- shift the shift register by 1 bit if ~DNSelectAB
DExecuteAB<BOOL, -- interpret the content of the shift register if ~DNSelectAB
DNSelectAB<BOOL, -- if high, hold but don't Execute or Shift
DHoldAB<BOOL, -- must be high before testing
DDataInAB<BOOL, -- sampled during each PhB following a PhB that DShiftAB is dragon.Asserted
DDataOutAB=BOOL -- changes during each PhA following a PhB that DShiftAB is dragon.Asserted, continues to be driven through the PhB following the PhA it changes
State
euLogRef: REF REF,
Pipeline registers
aluLeft, aluRight, aluOut: Dragon.HexWord,
result1, result2, result3: Dragon.HexWord,
store1, store2, store3: Dragon.HexWord,
RAM, RAM addresses and various aliased registers
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
131: euField
132 through 147 are auxilliary registers
148 thru 151: (currently spare)
152 through 163 are constants
Any aAdr larger than 163 is illegal
aAdr, bAdr, cAdr: Dragon.HexByte,
ram: ARRAY Dragon.HexByte OF Dragon.HexWord,
field: Dragon.HexWord, -- a copy is kept as RAM[fieldAdr], another copy is in the field unit. Any write is performed to both physical locations, any read is from the most convenient location.
Control bits and register whose useful life does not extend beyond individual macro instructions and therefore need not be pampered
mqAB, mqBA: Dragon.HexWord,
mulcandSAB, mulcandSBA: BOOL,
mulProdSAB, mulProdSBA: BOOL,
mulSubAB, mulSubBA: BOOL,
divisorSAB, divisorSBA: BOOL,
divendSAB, divendSBA: BOOL,
divZeroAB, divZeroBA: BOOL,
divRmsbAB, divRmsbBA: BOOL,
divRCorrAB, divRCorrBA: BOOL,
divQIncrAB, divQIncrBA: BOOL,
mar: Dragon.HexWord, -- contains the faulty address after a cache access
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.
conditionB: BOOL,
overflow: BOOL, -- temporary state, does not survive through a whole cycle
Other pieces from the Control pipeline
rejectBA: BOOL, -- a copy of EPRejectB stable during PhiA
faultBA: BOOL, -- a latched veriosn of EPFaultB # None
parityResult3, parityStore3: BOOL -- parity associated to the register
EvalSimple
log ← NARROW[euLogRef^];
ram[PRtoByte[euConstant]] ← 0; -- done by IFU
PhiA phase. Note that rejectBA alone inhibits almost any state change during PhiA
IF PhA
THEN {
Dragon.Assert[aAdr>=0 AND aAdr<164];
No simultaneous bypass control signals on
Dragon.Assert[NOT (EUAluLeftisR1BA AND EUAluLeftisR3BA)];
Dragon.Assert[NOT Dragon.MoreThanOneOf[EUAluRightisR1BA, EUAluRightisR3BA, EUAluRightisKBA]];
Dragon.Assert[NOT (EUS1isR1BA AND EUS1isR3BA)];
Dragon.Assert[NOT (EUAluLeftisR1BA AND EUAluLeftisR3BA)];
The last cycle of a faulty cache access: rejectBA and EPFaultB are both high
IF rejectBA AND faultBA THEN {ram[marAdr] ← result3}; -- save the faulty address
IF
NOT (EUHoldCarryBA
OR rejectBA
OR (EUTrapBA
AND EUConditionBA))
THEN
{carryAB ← carryBA};
These state bits should not require the special protection given carry since they are not shared between macro instructions
mqAB ← mqBA;
mulcandSAB ← mulcandSBA;
mulProdSAB ← mulProdSBA;
mulSubAB ← mulSubBA;
divisorSAB ← divisorSBA;
divendSAB ← divendSBA;
divZeroAB ← divZeroBA;
divRmsbAB ← divRmsbBA;
divRCorrAB ← divRCorrBA;
divQIncrAB ← divQIncrBA;
rejected ← rejectBA;
IF
NOT rejectBA
THEN {
kBusAB ← Dragon.LFD[KBus];
aluLeft ←
SELECT
TRUE
FROM
EUAluLeftisR1BA => result1,
EUAluLeftisR3BA => result3,
ENDCASE => ram[aAdr]; -- watch out for illegal addresses
aluRight ←
SELECT
TRUE
FROM
EUAluRightisR1BA => result1,
EUAluRightisR3BA => result3,
EUAluRightisKBA => Dragon.LFD[KBus],
ENDCASE => ram[bAdr]; -- watch out for illegal addresses
store1 ←
SELECT
TRUE
FROM
EUS1isR1BA => result1,
EUS1isR3BA => result3,
ENDCASE => ram[bAdr]; -- watch out for illegal addresses
result2 ←
SELECT
TRUE
FROM
EUR2isR3BA => result3,
ENDCASE => result1;
Always send address to Cache during PhiA
EPData ← Dragon.LTD[result1];
store3 ←
SELECT
TRUE
FROM
EUS3isR3BA => result3,
ENDCASE => store2;
parityStore3 ← IF EUS3isR3BA THEN parityResult3 ELSE CacheOps.Parity32[store2];
It is particularly important that no store in the RAM happens during rejectBA
RejectBA should run thru the cAdr decoder
SELECT
TRUE
FROM
cAdr IN 0 to 163
cAdr IN [PRtoByte[euStack] .. PRtoByte[euConstant]+12) => ram[cAdr] ← result3;
cAdr IN 240 to 255
cAdr
IN [PRtoByte[ifuXBus] .. PRtoByte[ifuLast]] =>
KBus ← Dragon.LTD[result3];
ENDCASE => Dragon.Assert[FALSE, "EU cAdr out of range"];
phBLastFLAG ← FALSE;
};
};
PhiB phase. Most of the computations take place during PhiB
IF PhB
THEN {
-- EPFaultB ???
PlusOrMinusAluRight: Dragon.HexWord; -- temporary used for subtraction
c32: BOOL;
IF
NOT phBLastFLAG
AND log #
IO.noWhereStream
THEN {
reset ← resetting AND NOT ResetAB;
EULog[InstrCountAB,aAdr,aluLeft,bAdr,aluRight,store1,cAdr,result3];
resetting ← ResetAB};
phBLastFLAG ← TRUE;
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 result3 depends upon EPRejectB, and the choice is made during the same PhiB as it is received. So this statement has to be first.
faultBA ← EPFaultB#None;
Updating the RAM addresses
aAdr ← BitOps.ECFD[KBus, 32, 0, 8];
bAdr ← BitOps.ECFD[KBus, 32, 8, 8];
cAdr ← BitOps.ECFD[KBus, 32, 16, 8];
PBus: notice that in case of reject during a store, we keep sending the data even though it is useless
EUWriteToPBusAB => EUR3isR2AB
Dragon.Assert[NOT EUWriteToPBusAB OR EUR3isR2AB];
IF EUWriteToPBusAB
THEN {
-- store in progress
EPData ← Dragon.LTD[store3]; -- send data to Cache (Store)
EPParityB ← CacheOps.Parity32[store3]; -- EMM, was parityStore3
result3 ← result2}
save the address in result3, done normally since IFU issues EUR3isR2AB
ELSE {
-- either a fetch, an op, or a move in progress
IF rejectBA
-- Fetch with reject => save address in result3 and don't listen to IFU
THEN result3 ← result2
ELSE
-- Fetch without reject
--
IF EUR3isR2AB
THEN result3 ← result2 -- op or move
ELSE {
result3 ← Dragon.LFD[EPData];
parityResult3 ← EPParityB;
IF EUCheckPParityAB
AND CacheOps.Parity32[result3]#parityResult3
THEN EPNPErrorB ← FALSE
} };
Data pipe
store2 ← store1;
Alu and Field Unit computation
Set Default values of state
carryBA ← carryAB;
mqBA ← mqAB;
divisorSBA ← divisorSAB;
divendSBA ← divendSAB;
divZeroBA ← divZeroAB;
divRmsbBA ← divRmsbAB;
divRCorrBA ← divRCorrAB;
divQIncrBA ← divQIncrAB;
mulcandSBA ← mulcandSAB;
mulProdSBA ← mulProdSAB;
mulSubBA ← mulSubAB;
SELECT EUAluOpAB
FROM
SAdd => {
[aluOut, c32] ← DoubleADD[aluLeft, aluRight, carryAB];
result1 ← aluOut;
carryBA ← FALSE};
SSub => {
[aluOut, c32] ← DoubleSUB[aluLeft, aluRight, carryAB];
result1 ← aluOut;
carryBA ← FALSE};
UAdd => {
[aluOut, c32] ← DoubleADD[aluLeft, aluRight, carryAB];
result1 ← aluOut;
carryBA ← c32};
USub => {
[aluOut, c32] ← DoubleSUB[aluLeft, aluRight, carryAB];
result1 ← aluOut;
carryBA ← NOT c32};
VAdd => {
[aluOut, c32] ← DoubleADD[aluLeft, aluRight, FALSE];
result1 ← aluOut};
VSub => {
[aluOut, c32] ← DoubleSUB[aluLeft, aluRight, FALSE];
result1 ← aluOut};
LAdd => {
[aluOut, c32] ← DoubleADD[aluLeft, aluRight, FALSE];
result1 ← aluOut;
carryBA ← FALSE};
LSub => {
[aluOut, c32] ← DoubleSUB[aluLeft, aluRight, FALSE];
result1 ← aluOut;
carryBA ← FALSE};
FOP => {
-- field descriptor provided by Field
result1 ← aluOut ← FieldOp[aluLeft, aluRight, field]};
FOPK => {
-- field descriptor provided by kBusAB. Otherwise identical to FOP
result1 ← aluOut ← FieldOp[aluLeft, aluRight, kBusAB]};
And => {
result1 ← aluOut ← WordOp[and, aluLeft, aluRight]};
Or => {
result1 ← aluOut ← WordOp[or, aluLeft, aluRight]};
Xor => {
result1 ← aluOut ← WordOp[xor, aluLeft, aluRight]};
BndChk => {
[aluOut, c32] ← DoubleSUB[aluLeft, aluRight, FALSE];
result1 ← aluLeft};
MulLdS => {
aluOut ← aluLeft; -- typically, aluOut will be tested for 0
result1 ← 0;
mqBA ← aluRight;
mulcandSBA ← EBFL[aluLeft, 0];
mulProdSBA ← FALSE;
mulSubBA ← FALSE};
MulLdU => {
aluOut ← aluLeft; -- typically, aluOut will be tested for 0
result1 ← 0;
mqBA ← aluRight;
mulcandSBA ← FALSE;
mulProdSBA ← FALSE;
mulSubBA ← FALSE};
MulStep => {
tempR1, tempMQ: Dragon.HexWord;
zero:
BOOL ←
EBFL[mqAB, 30] AND EBFL[mqAB, 31] AND mulSubAB OR
~EBFL[mqAB, 30] AND ~EBFL[mqAB, 31] AND ~mulSubAB;
two:
BOOL ←
EBFL[mqAB, 30] AND ~EBFL[mqAB, 31] AND ~mulSubAB OR
~EBFL[mqAB, 30] AND EBFL[mqAB, 31] AND mulSubAB;
one: BOOL ← ~zero AND ~two;
mulSubBA ← EBFL[mqAB, 30];
log.PutF["\nEU Multiply operation: %g %g times the multiplicand",
IO.rope[ (IF EBFL[mqAB, 30] THEN "Subtract" ELSE "Add") ],
IO.card[ (IF two THEN 2 ELSE IF one THEN 1 ELSE 0 ] ];
SELECT
TRUE
FROM
zero => {
aluOut ← aluLeft;
mulProdSBA ← mulProdSAB;
[tempR1, tempMQ] ← DblShiftRt[mulProdSBA, aluOut, mqAB];
[result1, mqBA] ← DblShiftRt[mulProdSBA, tempR1, tempMQ]};
one => {
IF mulSubBA
THEN [aluOut, carryBA] ← DoubleSUB [aluLeft, aluRight, FALSE]
ELSE [aluOut, carryBA] ← DoubleADD [aluLeft, aluRight, FALSE];
mulProdSBA ← mulSubBA#mulcandSAB;
[tempR1, tempMQ] ← DblShiftRt[mulProdSBA, aluOut, mqAB];
[result1, mqBA] ← DblShiftRt[mulProdSBA, tempR1, tempMQ]};
two => {
[tempR1, tempMQ] ← DblShiftRt[mulProdSAB, aluLeft, mqAB];
IF mulSubBA
THEN [aluOut, carryBA] ← DoubleSUB [tempR1, aluRight, FALSE]
ELSE [aluOut, carryBA] ← DoubleADD [tempR1, aluRight, FALSE];
mulProdSBA ← mulSubBA#mulcandSAB;
[result1, mqBA] ← DblShiftRt[mulProdSBA, aluOut, tempMQ]};
ENDCASE => ERROR};
MulAdj => {
IF mulSubAB
THEN [aluOut, ] ← DoubleADD [aluLeft, aluRight, FALSE]
ELSE aluOut ← aluLeft;
result1 ← aluOut};
RdMQ => {
result1 ← aluOut ← mqAB};
Divide Step 0
DivLdDbl => {
result1 ← aluOut ← aluLeft;
mqBA ← aluRight };
Divide Step 1
DivLdU => {
aluOut ← aluLeft;
carryBA ← FALSE;
divendSBA ← FALSE;
divisorSBA ← FALSE;
divZeroBA ← aluOut=0;
result1 ← aluOut };
DivLdS => {
aluOut ← aluLeft;
carryBA ← EBFL[mqAB, 0];
divendSBA ← EBFL[aluLeft, 0];
divisorSBA ← EBFL[aluRight, 0];
divZeroBA ← aluOut=0 AND ~EBFL[mqAB,0];
[divRmsbBA, result1, mqBA] ← DblShiftLt[aluOut, mqAB,
EBFL[aluRight,0]=EBFL[aluLeft,0]] };
Divide Step 2
DivCkU => {
-- Check for carry on first subtract
[aluOut, carryBA] ← DoubleSUB[aluLeft, aluRight, FALSE]; -- Trap if carry
divZeroBA ← aluOut=0 AND ~EBFL[mqAB,0];
[divRmsbBA, result1, mqBA] ← DblShiftLt[aluOut, mqAB, FALSE] };
DivCkS => {
-- Check aluOut on first operation after a shift - result thrown away
IF
EBFL[mqAB,31]
THEN [aluOut, ] ← DoubleSUB [aluLeft, aluRight, carryAB] -- note carry-in
ELSE [aluOut, ] ← DoubleADD [aluLeft, aluRight, carryAB]; -- from DivLdS
result1 ← aluLeft }; -- aluOut will be tested here - see DivOvFl.
Divide Step 3..33
DivStep => {
notLocked: BOOL ← divRmsbAB = (divisorSAB = EBFL[mqAB,31]); -- Rmsb = SignRem
IF
EBFL[mqAB,31]
THEN [aluOut, carryBA] ← DoubleSUB [aluLeft, aluRight, FALSE]
ELSE [aluOut, carryBA] ← DoubleADD [aluLeft, aluRight, FALSE];
divZeroBA ← ~
EBFL[mqAB,0]
AND (divZeroAB
OR
(notLocked AND carryBA AND aluOut=0)); -- new zero only possible if not locked
[divRmsbBA, result1, mqBA] ← DblShiftLt[aluOut, mqAB,
(EBFL[mqAB,31] # (carryBA = divRmsbAB) )] }; -- reverse op if Rmsb = carry
DivAdjM => {
notLocked: BOOL ← divRmsbAB = (divisorSAB = EBFL[mqAB,31]); -- Rmsb = SignRem
newRemS: BOOL;
IF
EBFL[mqAB,31]
THEN [aluOut, carryBA] ← DoubleSUB [aluLeft, aluRight, FALSE]
ELSE [aluOut, carryBA] ← DoubleADD [aluLeft, aluRight, FALSE];
divZeroBA ← ((notLocked AND carryBA AND aluOut=0) OR divZeroAB);
newRemS ← notLocked # carryBA;
divRCorrBA ←
~divisorSAB AND newRemS OR
divisorSAB AND ~newRemS AND ~divZeroBA OR
newRemS AND divZeroBA;
divQIncrBA ← divisorSAB AND divZeroBA;
mqBA ← ShiftLt[mqAB, (EBFL[mqAB,31] # (carryBA = divRmsbAB) )];
result1 ← aluOut };
DivAdjR => {
notLocked: BOOL ← divRmsbAB = (divisorSAB = EBFL[mqAB,31]); -- Rmsb = SignRem
newRemS: BOOL;
IF
EBFL[mqAB,31]
THEN [aluOut, carryBA] ← DoubleSUB [aluLeft, aluRight, FALSE]
ELSE [aluOut, carryBA] ← DoubleADD [aluLeft, aluRight, FALSE];
divZeroBA ← ((notLocked AND carryBA AND aluOut=0) OR divZeroAB);
newRemS ← notLocked # carryBA;
divRCorrBA ←
~divendSAB AND newRemS OR
divendSAB AND ~newRemS AND ~divZeroBA OR
newRemS AND divZeroBA;
divQIncrBA ←
~divendSAB AND divisorSAB OR
divendSAB AND ~divisorSAB AND ~divZeroBA OR
divisorSAB AND divZeroBA;
mqBA ← ShiftLt[mqAB, (EBFL[mqAB,31] # (carryBA = divRmsbAB) )];
result1 ← aluOut };
DivAdj => {
result1Alt: Dragon.HexWord ← IF divRCorrAB THEN aluRight ELSE 0;
IF
EBFL[mqAB,31]
THEN [aluOut, ] ← DoubleSUB [aluLeft, result1Alt, FALSE]
ELSE [aluOut, ] ← DoubleADD [aluLeft, result1Alt, FALSE];
carryBA ← divQIncrAB;
mqBA ← mqAB;
result1 ← aluOut};
ENDCASE => ERROR Stop["Invalid ALU Operation"];
Here we compute the overflow by checking the high-order bits of the operands (aluLeft and aluRight or - aluRight) and of the result
SELECT EUAluOpAB
FROM
SSub, USub, VSub, LSub =>
[PlusOrMinusAluRight, ] ← DoubleSUB[0, aluRight, FALSE];
ENDCASE => PlusOrMinusAluRight ← aluRight;
overflow ← (EBFL[aluLeft, 0] = EBFL[PlusOrMinusAluRight, 0]) AND (EBFL[aluLeft, 0] # EBFL[aluOut, 0]);
Condition and trap generation
conditionB ←
SELECT EUCondSelAB
FROM
EZ => aluOut=0, -- aluOut=0
LZ => EBFL[aluOut, 0], -- aluOut<0 by checking the high-order bit
LE => (aluOut=0) OR EBFL[aluOut, 0], -- aluOut<=0,
OvFl => overflow,
BC => NOT (NOT EBFL[aluLeft, 0] -- 0<=arg -- AND EBFL[aluOut, 0] -- arg-limit<0 --),
IL => (EBFL[aluLeft, 0]#EBFL[aluLeft, 1]) OR (EBFL[aluRight, 0]#EBFL[aluRight, 1]) OR (EBFL[aluOut, 0]#EBFL[aluOut, 1]),
DivOvFl => (
SELECT EUAluOpAB
FROM
DivCkU => carryBA,
DivCkS => (
IF aluOut=0
THEN (divendSAB = divisorSAB)
ELSE (divendSAB = (aluOut>=20000000000B))),
ENDCASE => ERROR Stop["Invalid ALU Operation for DivMul condition"]),
False => FALSE,
NE => aluOut#0, -- aluOut#0
GE => NOT EBFL[aluOut, 0], -- aluOut>=0 by checking the high-order bit
GZ => NOT ((aluOut=0) OR EBFL[aluOut, 0]), -- aluOut>0,
NotOvFl => NOT overflow,
NotBC => NOT EBFL[aluLeft, 0] -- 0<=arg -- AND EBFL[aluOut, 0] -- arg-limit<0 --,
NotIL => NOT ((EBFL[aluLeft, 0]#EBFL[aluLeft, 1]) OR (EBFL[aluRight, 0]#EBFL[aluRight, 1]) OR (EBFL[aluOut, 0]#EBFL[aluOut, 1])),
True => TRUE,
ENDCASE => ERROR Stop["Invalid EUConditionBA Code"];
EUConditionBA ← conditionB };