=
BEGIN
Simulation Globals
debugOn: BOOL ← FALSE;
asynchBehaviour: BOOL ← FALSE; --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: BOOL ← FALSE,
sentAddr: BOOL ← FALSE,
dataReady: BOOL ← FALSE,
getData: BOOL ← FALSE,
sendData: BOOL ← FALSE,
dataOut: BOOL ← FALSE,
dataIn: BOOL ← FALSE,
processFrame: BOOL ← FALSE,
resetPending: BOOL ← FALSE,
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;
};
};
}; --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: BOOL ← FALSE,
sentAddr: BOOL ← FALSE,
dataReady: BOOL ← FALSE,
getData: BOOL ← FALSE,
sendData: BOOL ← FALSE,
dataOut: BOOL ← FALSE,
dataIn: BOOL ← FALSE,
processFrame: BOOL ← FALSE,
resetPending: BOOL ← FALSE,
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];
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: BOOL ← FALSE,
hardDiskTask: BOOL ← FALSE,
serialCommTask: BOOL ← FALSE,
--flags
intrptStatus: IntrptStatus ← none,
busGranted: BOOL ← FALSE,
addrCycle: BOOL ← FALSE,
dynaRequest: BOOL ← FALSE,
addrReady: BOOL ← FALSE,
dataReady: BOOL ← FALSE,
driveData: BOOL ← FALSE,
latchData: BOOL ← FALSE,
dataOut: BOOL ← FALSE,
dataIn: BOOL ← FALSE,
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:
BOOL ←
FALSE] = {
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[];