SloIODeviceImpl.mesa
Copyright © 1986 by Xerox Corporation. All rights reserved.
Created by Neil Gunther, August 14, 1986 2:05:36 pm PDT
Last Edited by: Neil Gunther August 26, 1986 10:54:32 am PDT
DIRECTORY
CoreCreate, CoreProperties, IO, Logic, LogicUtils, Ports, Random, Rope, Rosemary, SloIODevice, TerminalIO;
SloIODeviceImpl: CEDAR PROGRAM
IMPORTS CoreCreate, IO, LogicUtils, Random, Rope, Rosemary, TerminalIO
EXPORTS SloIODevice
= BEGIN
Simulation Globals
debugOn: BOOLFALSE;
asynchBehaviour: BOOLFALSE; --Only set in simulation mode
Emulation of Intel 82586 Ethernet Controller
ENetName: CoreCreate.ROPE = Rosemary.Register[roseClassName: "EthernetController", init: ENetInit, evalSimple: ENetMaxModeSimple];
eNetVdd,
eNetGnd,
eNetNotS0, --DMA status bits
eNetNotS1,
eNetNotMX, --Maximum mode (tied low)
eNetRESET,
eNetCA, --82586 Channel Attention (input)
eNetINT, --Interrupt IOP controller (output)
eNetHOLD, --SloBus request (output)
eNetHLDA, --SloBus grant (input)
eNetREADY, --(=ALE) DMA transfer begin (input)
eNetARDY, --Asynch ready line
eNetCLK,
eNetNotBHE,
eNetA16,
eNetA17,
eNetA18,
eNetA19,
eNetA20,
eNetA21,
eNetA22,
eNetA23,
eNetADBus: NAT;
eNetADBits: NAT ← 16;
EthernetController: PUBLIC PROC RETURNS [ct: CoreCreate.CellType] = {
ct ← CoreCreate.Cell[
name: ENetName,
public: CoreCreate.Wires["Vdd", "Gnd",
Rope.Cat["nS0", "nS1", "nMX", "RESET", "CA"],
"INT", "HOLD", "HLDA", "READY", "ARDY", "CLK", "nBHE",
CoreCreate.Seq["ADBus", eNetADBits]],
instances: NIL
];
[] ← Rosemary.BindCellType[cellType: ct, roseClassName: ENetName];
[eNetVdd, eNetGnd, eNetCLK] ← LogicUtils.PortIndexes[ct.public, "Vdd", "Gnd", "CLK"];
[eNetNotS0, eNetNotS1, eNetNotMX, eNetRESET, eNetCA, eNetINT, eNetHOLD, eNetHLDA, eNetREADY, eNetARDY, eNetNotBHE, eNetADBus] ← LogicUtils.PortIndexes[ct.public,
"nS0", "nS1", "nMX", "RESET", "CA", "INT", "HOLD", "HLDA", "READY", "ARDY", "nBHE", "ADBus"];
--Inputs
LogicUtils.InitPorts[ct, l, none, "Vdd", "Gnd", "RESET", "CA", "HLDA", "READY", "nMX", "CLK"];
--Outputs
LogicUtils.InitPorts[ct, l, drive, "INT", "HOLD", "nS0", "nS1", "nBHE"];
--16-bit bidirectional muxed Address/Data
LogicUtils.InitPorts[ct, c, none, "ADBus"];
};
IntrptStatus: TYPE = {readReq, writeReq, seenCA, none};
ENetState: TYPE = REF ENetStateRec;
ENetStateRec: TYPE = RECORD [
--internal flags
intrptStatus: IntrptStatus ← none,
busGranted: BOOLFALSE,
sentAddr: BOOLFALSE,
dataReady: BOOLFALSE,
getData: BOOLFALSE,
sendData: BOOLFALSE,
dataOut: BOOLFALSE,
dataIn: BOOLFALSE,
processFrame: BOOLFALSE,
resetPending: BOOLFALSE,
dataWCycle: NAT ← 0,
dataRCycle: NAT ← 0,
frameClocks: NAT ← 0,
tick: NAT 𡤀,
prevActivity: ATOM ← $None,
--registers
addrReg: CARDINAL ← 60000,
dataReg: CARDINAL ← 0,
val: Ports.LevelSequence
];
ENetInit: Rosemary.InitProc = {
state: ENetState ← NEW[ENetStateRec];
state.prevActivity ← $None;
state.val ← NEW[Ports.LevelSequenceRec[eNetADBits]];
LogicUtils.SetLS[state.val, X];
stateAny ← state;
};
ENetMaxModeSimple: Rosemary.EvalProc = {
Purports to model DMA transfers on the SloBus.
state: ENetState ← NARROW[stateAny]; {
p[eNetADBus].d ← none; {
LoCLK: PROC RETURNS [lo: BOOL] = {
lo ← p[eNetCLK].l = L;
};
HiCLK: PROC RETURNS [hi: BOOL] = {
hi ← p[eNetCLK].l = H;
};
GenAddress: PROC = {
p[eNetADBus].d ← drive;
p[eNetADBus].c ← state.addrReg;
};
GenData: PROC = {
p[eNetADBus].d ← drive;
p[eNetADBus].c ← state.dataReg;
};
DataInCycle: PROC = {
p[eNetADBus].d ← drive;
state.dataReg ← p[eNetADBus].c;
p[eNetADBus].d ← none; --tri-state
};
DataOutCycle: PROC = {
p[eNetADBus].d ← drive;
p[eNetADBus].c ← state.dataReg ← 4000;
p[eNetADBus].d ← none;
};
AcquireSloBus: PROC = {
NoteActivity[$RequestingSloBus];
IF NOT state.busGranted THEN p[eNetHOLD].l ← H;
IF p[eNetHLDA].l = H THEN {
state.busGranted ← TRUE;
}
ELSE RETURN;
};
ReleaseSloBus: PROC = {
NoteActivity[$ReleasingSloBus];
p[eNetHOLD].d ← drive;
p[eNetHOLD].l ← L;
state.busGranted ← FALSE;
};
ReadTransfer: PROC = {
NoteActivity[$DMARead];
SELECT TRUE FROM
p[eNetREADY].l = L => {
IF NOT state.sentAddr THEN { --t=t0
GenAddress[];
state.dataRCycle ← state.dataRCycle.SUCC; --t=t1
state.sentAddr ← TRUE;
}
ELSE RETURN; --Wait state
};
p[eNetREADY].l = H => {
IF state.sentAddr THEN {
--Tri-state the bus drivers
p[eNetADBus].d ← none;
state.dataRCycle ← state.dataRCycle.SUCC; --t=t2, t3
IF state.dataRCycle=3 THEN {
state.sentAddr ← FALSE;
state.getData ← TRUE; --sample on t=t4 only
};
};
IF state.getData THEN {
IF state.dataRCycle=3 THEN {
DataInCycle[];
state.getData ← FALSE;
state.dataIn ← TRUE;
state.dataRCycle ← state.dataRCycle.SUCC; --t=t4
};
};
};
ENDCASE => NULL;
}; --ReadTransfer
WriteTransfer: PROC = {
NoteActivity[$DMAWrite];
SELECT TRUE FROM
p[eNetREADY].l = L => {
IF NOT state.sentAddr THEN { --t=t0
GenAddress[];
state.dataWCycle ← state.dataWCycle.SUCC; --t=t1
state.sentAddr ← TRUE;
};
};
p[eNetREADY].l = H => {
IF state.sendData THEN {
DataOutCycle[];
state.dataWCycle ← state.dataWCycle.SUCC; --t=t3 (t4 termination on HiCLK)
};
IF state.sentAddr THEN {
--Tri-state the bus drivers
p[eNetADBus].d ← none;
state.dataWCycle ← state.dataWCycle.SUCC; --t=t2
state.sentAddr ← FALSE;
state.sendData ← TRUE;
};
};
ENDCASE => NULL;
}; --WriteTransfer
CheckFrameInterrupt: PROC = {
IF state.processFrame THEN RETURN
ELSE
SELECT Random.ChooseInt[rs: rs, min: 1, max: 6] FROM
1, 3, 5 => state.processFrame ← TRUE; --set false during LoCLK
ENDCASE => NULL;
};
WatchDogTiming: PROC [forTicks: NAT ← 0] RETURNS [watching: BOOL FALSE] = {
IF state.tick=forTicks+1 THEN RETURN
ELSE {
state.tick ← state.tick.SUCC;
watching ← TRUE;
};
};
ResetWatchDog: PROC = {
state.tick ← 0;
};
NoteActivity: PROC [activity: ATOM] = {
IF NOT debugOn THEN RETURN;
IF activity#state.prevActivity THEN {
TerminalIO.WriteF1["\nENC: %g", IO.atom[activity]];
state.prevActivity ← activity;
}
ELSE RETURN;
};
ResetENetController: PROC = {
-- Reset ports --
p[eNetCA].d ← drive; p[eNetCA].l ← L;
p[eNetINT].d ← drive; p[eNetINT].l ← L;
-- Reset flags --
state.intrptStatus ← none;
state.busGranted ← FALSE;
state.sentAddr ← FALSE;
state.dataReady ← FALSE;
state.getData ← FALSE;
state.sendData ← FALSE;
state.dataOut ← FALSE;
state.dataIn ← FALSE;
state.processFrame ← FALSE;
state.resetPending ← FALSE;
state.dataWCycle ← 0;
state.dataRCycle ← 0;
state.frameClocks ← 0;
state.tick ← 0;
state.dataReg ← 0;
NoteActivity[$NowReset];
};
All interrupts triggered on low level
IF LoCLK[] THEN {
Priority ordering
SELECT TRUE FROM
p[eNetRESET].l = H AND NOT state.resetPending => {
IF NOT WatchDogTiming[forTicks: 4] THEN {
state.resetPending ← TRUE;
ResetWatchDog[];
};
};
state.resetPending => {
NoteActivity[$ResetPending];
IF NOT WatchDogTiming[forTicks: 6] THEN {
ResetENetController[];
}
ELSE IF state.processFrame THEN state.resetPending ← FALSE;
};
state.processFrame => { -- from the 82586 RU
NoteActivity[$FrameProcessing];
p[eNetINT].d ← drive; p[eNetINT].l ← H;
IF state.frameClocks < 173+4 THEN {
IF NOT state.busGranted THEN AcquireSloBus[];
*** Intel 80586: 66/173 cycles are unused for read/write altho' bus is held
WriteTransfer[]; --frame
state.frameClocks ← state.frameClocks.SUCC;
}
ELSE {
ReleaseSloBus[];
state.getData ← state.dataIn ← state.processFrame ← FALSE;
state.dataRCycle ← state.frameClocks ← 0;
};
};
p[eNetCA].l = H => {-- generates DMA Read request
IOP must assert CA for at least one cycle
IF NOT WatchDogTiming[forTicks: 1] THEN {
state.intrptStatus ← readReq;
p[eNetCA].d ← drive; p[eNetCA].l ← L; --override IOP signal
p[eNetINT].l ← L; --protocol requires this
ResetWatchDog[];
};
};
state.intrptStatus=readReq => {
SELECT TRUE FROM
NOT state.busGranted => AcquireSloBus[];
state.dataIn => {
ReleaseSloBus[]; --drops HOLD
state.intrptStatus ← none;
state.getData ← state.dataIn ← FALSE;
state.dataRCycle ← 0;
};
ENDCASE => ReadTransfer[];
};
state.intrptStatus=writeReq => {
p[eNetINT].d ← drive;
p[eNetINT].l ← H;
SELECT TRUE FROM
NOT state.busGranted => AcquireSloBus[];
state.dataOut => {
ReleaseSloBus[];
state.intrptStatus ← none;
state.dataOut ← FALSE;
state.dataWCycle ← 0;
};
ENDCASE => WriteTransfer[];
};
ENDCASE => NULL;
}; --end of LoCLK branch
IF HiCLK[] THEN {
IF p[eNetREADY].l = H AND state.sendData AND state.dataWCycle = 3 THEN {
state.dataOut ← TRUE;
state.sendData ← FALSE; --terminates on first half of t4
};
IF asynchBehaviour THEN CheckFrameInterrupt[]; --sets flag
}; --end of HiCLK branch
}}}; --ENetMaxModeSimple
Emulation of Am9580 Rigid Disk Controller
HardDiskName: CoreCreate.ROPE = Rosemary.Register[roseClassName: "HardDiskController", init: HDiskInit, evalSimple: HDiskSimple];
hDiskVdd,
hDiskGnd,
hDiskRESET,
hDiskNotCS, --Chip select
hDiskINTR, --Interrupt IOP controller (output)
hDiskBREQ, --SloBus request (output)
hDiskBACK, --SloBus grant (input)
hDiskNotREADY, --DMA transfer begin (input)
hDiskNotBHE,
hDiskALE,
hDiskALEN, --Upper ALE
hDiskNotRD,
hDiskNotWR,
hDiskDTNotR, --Data Tx/Rx
hDiskNotDEN, --Data enable
hDiskBNotW,
hDiskANotS, --Asynch/synch
hDiskCLK,
hDiskA0, --Slave mode only (selects odd/even byte of internal regs)
hDiskA1, --Slave mode (1-3; selects internal regs)
hDiskA2,
hDiskA3,
ADBus: NAT;
hDiskADBits: NAT ← 16;
HardDiskController: PUBLIC PROC RETURNS [ct: CoreCreate.CellType] = {
ct ← CoreCreate.Cell[
name: HardDiskName,
public: CoreCreate.Wires["Vdd", "Gnd", "RESET", "nCS", "INTR", "BREQ", "BACK", "nREADY", "CLK", CoreCreate.Seq["ADBus", hDiskADBits]],
instances: NIL
];
[] ← Rosemary.BindCellType[cellType: ct, roseClassName: HardDiskName];
[hDiskVdd, hDiskGnd, hDiskRESET, hDiskNotCS, hDiskINTR, hDiskBREQ, hDiskBACK, hDiskNotREADY, hDiskCLK, ADBus] ← LogicUtils.PortIndexes[ct.public, "Vdd", "Gnd", "RESET", "nCS", "INTR", "BREQ", "BACK", "nREADY", "CLK", "ADBus"];
LogicUtils.InitPorts[ct, l, none, "Vdd", "Gnd", "RESET", "nCS", "BACK", "nREADY", "CLK"]; --inputs
LogicUtils.InitPorts[ct, l, drive, "INTR", "BREQ"]; --outputs
LogicUtils.InitPorts[ct, c, none, "ADBus"]; --16-bit; bidirectional muxed Address/Data
};
HDiskState: TYPE = REF HDiskStateRec;
HDiskStateRec: TYPE = RECORD [
--internal flags
intrptStatus: IntrptStatus ← none,
busGranted: BOOLFALSE,
sentAddr: BOOLFALSE,
dataReady: BOOLFALSE,
getData: BOOLFALSE,
sendData: BOOLFALSE,
dataOut: BOOLFALSE,
dataIn: BOOLFALSE,
processFrame: BOOLFALSE,
resetPending: BOOLFALSE,
dataWCycle: NAT ← 0,
dataRCycle: NAT ← 0,
frameClocks: NAT ← 0,
tick: NAT 𡤀,
prevActivity: ATOM ← $None,
--registers
addrReg: CARDINAL ← 50000,
dataReg: CARDINAL ← 0,
val: Ports.LevelSequence
];
HDiskInit: Rosemary.InitProc = {
state: HDiskState ← NEW[HDiskStateRec];
state.prevActivity ← $None;
state.val ← NEW[Ports.LevelSequenceRec[hDiskADBits]];
LogicUtils.SetLS[state.val, X];
stateAny ← state;
};
HDiskSimple: Rosemary.EvalProc = {{{
}}};
Emulation of Z8530 Serial Communications Controller
SerialCommName: CoreCreate.ROPE = Rosemary.Register[roseClassName: "SerialCommController", init: SerialCommInit, evalSimple: SerialCommSimple];
SerialCommController: PUBLIC PROC RETURNS [ct: CoreCreate.CellType] = {
};
SerialCommInit: Rosemary.InitProc = {
};
SerialCommSimple: Rosemary.EvalProc = {{{
}}};
Emulation of Z8036/8536 Counter/timer & Parallel IO Unit
CIOName: CoreCreate.ROPE = Rosemary.Register[roseClassName: "CounterIOUnit", init: CIOInit, evalSimple: CIOSimple];
CounterIOUnit: PUBLIC PROC RETURNS [ct: CoreCreate.CellType] = {
};
CIOInit: Rosemary.InitProc = {
};
CIOSimple: Rosemary.EvalProc = {{{
}}};
Emulation of Dragon I/O processor
IOPName: CoreCreate.ROPE = Rosemary.Register[roseClassName: "IOPController", init: IOPInit, evalSimple: IOPSimple];
sloVdd,
sloGnd,
sloReset,
sloInt, --Interrupt device (output)
sloIntr, --Interrupt service request (input)
sloHold, --SloBus request (input)
sloHlda, --SloBus grant (output)
sloReady, --DMA transfer begin (output)
sloClk,
sloADBus: NAT;
sloBits: NAT ← 16;
IOPController: PUBLIC PROC RETURNS [ct: CoreCreate.CellType] = {
ct ← CoreCreate.Cell[
name: IOPName,
public: CoreCreate.Wires["Vdd", "Gnd", "iopRESET", "iopINT", "iopINTR", "iopHOLD", "iopHLDA", "iopREADY", "iopCLK", CoreCreate.Seq["ADBUS", sloBits]],
instances: NIL
];
[] ← Rosemary.BindCellType[cellType: ct, roseClassName: IOPName];
[sloVdd, sloGnd, sloReset, sloInt, sloIntr, sloHold, sloHlda, sloReady, sloClk, sloADBus] ← LogicUtils.PortIndexes[ct.public, "Vdd", "Gnd", "iopRESET", "iopINT", "iopINTR", "iopHOLD", "iopHLDA", "iopREADY", "iopCLK", "ADBUS"];
LogicUtils.InitPorts[ct, l, none, "Vdd", "Gnd", "iopINTR", "iopHOLD"]; --inputs
LogicUtils.InitPorts[ct, l, force, "iopRESET", "iopINT", "iopHLDA", "iopREADY", "iopCLK"]; --outputs
LogicUtils.InitPorts[ct, c, none, "ADBUS"]; --16-bit; bidirectional muxed Address/Data
};
IOPState: TYPE = REF IOPStateRec;
IOPStateRec: TYPE = RECORD [
--control tasks
eNetTask: BOOLFALSE,
hardDiskTask: BOOLFALSE,
serialCommTask: BOOLFALSE,
--flags
intrptStatus: IntrptStatus ← none,
busGranted: BOOLFALSE,
addrCycle: BOOLFALSE,
dynaRequest: BOOLFALSE,
addrReady: BOOLFALSE,
dataReady: BOOLFALSE,
driveData: BOOLFALSE,
latchData: BOOLFALSE,
dataOut: BOOLFALSE,
dataIn: BOOLFALSE,
dataWCycle: NAT ← 0,
dataRCycle: NAT ← 0,
tick: NAT 𡤀,
prevActivity: ATOM ← $Reset,
--registers
iopAddrReg: CARDINAL ← 0, --generated by IO devices
iopDataReg: CARDINAL ← 0,
val: Ports.LevelSequence
];
IOPInit: Rosemary.InitProc = {
state: IOPState ← NEW[IOPStateRec];
state.prevActivity ← $Reset;
state.val ← NEW[Ports.LevelSequenceRec[sloBits]];
LogicUtils.SetLS[state.val, X];
stateAny ← state;
};
IOPSimple: Rosemary.EvalProc = {
state: IOPState ← NARROW[stateAny]; {
p[sloADBus].d ← none; {
junkAddr: CARDINAL ← 0;
SloClockLo: PROC RETURNS [lo: BOOL] = {
lo ← p[sloClk].l = L;
};
SloClockHi: PROC RETURNS [hi: BOOL] = {
hi ← p[sloClk].l = H;
};
SloTiming: PROC [forTicks: NAT ← 0] RETURNS [timing: BOOLFALSE] = {
IF state.tick=forTicks+1 THEN RETURN
ELSE {
state.tick ← state.tick.SUCC;
timing ← TRUE;
};
};
ResetSloTimer: PROC = {
state.tick ← 0;
};
IF SloClockHi[] THEN { --activation goes out on high; latched by device on low
SELECT TRUE FROM
state.eNetTask => {
IMPORTANT: Ensure that asynchBehaviour is enabled in ENetMaxModeSimple
Random framing activity from ENC will trigger INTR request.
SELECT TRUE FROM
p[sloIntr].l = H AND NOT state.addrCycle => { --initiates write transfer
IF p[sloHold].l = H THEN {
IF NOT SloTiming[5] THEN { --busy for 5 cycles
p[sloHlda].d ← drive; --grant SloBus
p[sloHlda].l ← H;
p[sloADBus].d ← none; --tristate drivers
state.addrCycle ← TRUE;
ResetSloTimer[];
};
};
};
state.dynaRequest => {
state.driveData ← TRUE;
};
state.addrCycle => {
IF NOT SloTiming[1] THEN { --wait 1 bus cycle
Get address
p[sloADBus].d ← force; --enable drivers
state.iopAddrReg ← p[sloADBus].c;
If valid address & not busy then assert sloReady
p[sloReady].l ← H;
state.addrCycle ← FALSE;
state.latchData ← TRUE;
};
};
state.latchData => {
p[sloADBus].d ← force;
state.iopDataReg ← p[sloADBus].c;
p[sloADBus].d ← none; --tri-state ENC drivers
state.latchData ← FALSE;
};
state.driveData => {
p[sloADBus].d ← force;
p[sloADBus].c ← state.iopDataReg;
p[sloADBus].d ← none; --tri-state ENC drivers
state.driveData ← FALSE;
};
p[sloHold].l = L => {
p[sloHlda].l ← L;
p[sloReady].l ← L;
};
ENDCASE => NULL;
}; --end of eNetTask
state.hardDiskTask => {
To be defined
}; --end of hardDiskTask
state.serialCommTask => {
To be defined
}; --end of sccTask
ENDCASE => NULL;
};
IF SloClockLo[] THEN {
Don't do anything for now
};
}}}; --IOPSimple
rs: Random.RandomStream ← Random.Create[];
END.