Oracle
Impl:
CEDAR
PROGRAM
IMPORTS Atom, CoreCreate, CoreClasses, CoreFlat, CoreOps, CoreProperties, FileViewerOps, FS, IO, Logic, Ports, Rosemary
EXPORTS Oracle
= BEGIN -- OPEN Oracle;
Oracle
NatSeq: TYPE = REF NatSeqRec ← NIL;
NatSeqRec: TYPE = RECORD [ SEQUENCE size: NAT OF NAT ];
SizesFromRope:
PROC [ sRope:
IO.
ROPE ]
RETURNS [ sizes: NatSeq ] = {
s: IO.STREAM = IO.RIS[sRope];
len: NAT;
FOR len ← 0, len+1
DO
[] ← s.GetCard[ ! IO.EndOfStream => GOTO Done ];
ENDLOOP;
s.SetIndex[0];
sizes ← NEW[NatSeqRec[len]];
FOR len ← 0, len+1
DO
sizes[len] ← s.GetCard[ ! IO.EndOfStream => GOTO Done ];
ENDLOOP;
s.Close[];
};
WireFromSizes:
PROC [ sizes: NatSeq, wireName:
IO.
ROPE ]
RETURNS [ w: CoreCreate.
WR ] =
{
SELECT sizes.size
FROM
0 => ERROR;
1 => w ← CoreCreate.Seq[wireName, sizes[0]];
ENDCASE => {
wrs: LIST OF CoreCreate.WR ← NIL;
FOR i:
NAT
DECREASING
IN [0..sizes.size)
DO
fieldName: IO.ROPE = IO.PutFR["x%d", IO.int[i]];
wrs ← CONS[CoreCreate.Seq[fieldName, sizes[i]], wrs];
ENDLOOP;
w ← CoreCreate.WireList[wrs, wireName];
};
};
OracleName: IO.ROPE = Rosemary.Register[roseClassName: "Oracle", init: OracleInit, evalSimple: OracleSimple];
Oracle: PUBLIC PROC [out, in, c: IO.ROPE, id: INT] RETURNS [ct: Core.CellType] = {
DoInitPort:
PROC [ name:
IO.
ROPE ] = {
wire: Core.Wire = CoreOps.FindWire[ct.public, name];
SELECT wire.size
FROM
0 => [] ← Ports.InitPort[wire, l, aggregate, none];
ENDCASE => {
FOR i:
NAT
IN [0..wire.size)
DO
wi: Core.Wire = wire[i];
SELECT wi.size
FROM
0 => [] ← Ports.InitPort[wi, l, aggregate, none];
ENDCASE => {
FOR j:
NAT
IN [0..wi.size)
DO
[] ← Ports.InitPort[wi[j], l, aggregate, none];
ENDLOOP;
};
ENDLOOP;
};
};
outSizes: NatSeq = SizesFromRope[out];
inSizes: NatSeq = SizesFromRope[in];
ct ← CoreClasses.CreateUnspecified[name: OracleName,
public: CoreCreate.Wires["Vdd", "Gnd", "Clk",
WireFromSizes[outSizes, "Out"],
WireFromSizes[inSizes, "In"]
]];
CoreProperties.PutCellTypeProp[ct, $oracle,
NEW[OraclePropRec ← [
ct: ct,
id: Atom.MakeAtom[IO.PutFR["%s-%xx", IO.rope[c], IO.int[id]]]
]]];
[] ← Rosemary.BindCellType[cellType: ct, roseClassName: OracleName];
[] ← CoreFlat.CellTypeCutLabels[ct, Logic.logicCutSet];
Ports.InitPorts[ct, l, none, "Vdd", "Gnd"];
Ports.InitPorts[ct, b, none, "Clk"];
DoInitPort["Out"];
DoInitPort["In"];
};
OracleProp: TYPE = REF OraclePropRec ← NIL;
OraclePropRec:
TYPE =
RECORD [
ct: Core.CellType ← NIL,
id: ATOM ← NIL
];
LCS: TYPE = RECORD [ SEQUENCE size: NAT OF LONG CARDINAL ];
File: TYPE = RECORD [ name: IO.ROPE ← NIL, stream: IO.STREAM ← NIL ];
OracleState: TYPE = REF OracleStateRec;
OracleStateRec:
TYPE =
RECORD [
clk, out, in: NAT ← LAST[NAT],
prevClk: BOOL ← TRUE,
cycle: INT ← 0,
oracleProps: OracleProp
];
OracleInit: Rosemary.InitProc = {
clk: NAT = Ports.PortIndex[cellType.public, "Clk"];
in: NAT = Ports.PortIndex[cellType.public, "In"];
out: NAT = Ports.PortIndex[cellType.public, "Out"];
state: OracleState = NEW[OracleStateRec];
oracleProps: REF OraclePropRec ← NARROW[CoreProperties.GetCellTypeProp[cellType, $oracle], REF OraclePropRec];
source: REF ANY = Atom.GetProp[$RosemaryOracle, oracleProps.id];
WITH source
SELECT
FROM
s: IO.STREAM => s.SetIndex[0];
r: IO.ROPE => Atom.PutProp[$RosemaryOracle, oracleProps.id, IO.RIS[r]];
f:
REF File => {
IF f.stream # NIL THEN { f.stream.Close[]; f.stream ← NIL };
f.stream ← FS.StreamOpen[f.name];
};
ENDCASE => {
-- no oracle definition --
ERROR;
};
state^ ← [
clk: clk,
in: in,
out: out,
oracleProps: oracleProps
];
stateAny ← state;
};
Disagreement: SIGNAL [ shouldBe, is: Ports.Level ] = CODE;
CantParse: ERROR = CODE;
OracleSimple: Rosemary.EvalProc = {
DoValues:
PROC [ s:
IO.
STREAM ] = {
ScanBar:
PROC [ ] = {
[] ← s.SkipWhitespace[];
IF s.GetChar[] # '| THEN ERROR CantParse;
[] ← s.SkipWhitespace[];
};
SkipToVisible:
PROC [ ] = {
[] ← s.SkipWhitespace[];
[] ← s.PeekChar[];
};
SendOut:
PROC [ port: Ports.Port ] = {
c: CHAR ← s.GetChar[];
SELECT c
FROM
'0, 'F, 'f, 'L, 'l => {port.l ← L; port.d ← drive};
'1, 'T, 't, 'H, 'h => {port.l ← H; port.d ← drive};
'-, '., 'x, 'X => {port.d ← none};
ENDCASE => ERROR CantParse;
};
ScanIn:
PROC [ port: Ports.Port ] = {
c: CHAR ← s.GetChar[];
SELECT c
FROM
'0, 'F, 'f, 'L, 'l =>
IF port.l # L
THEN
SIGNAL Disagreement[L, port.l];
'1, 'T, 't, 'H, 'h =>
IF port.l # H
THEN
SIGNAL Disagreement[H, port.l];
'-, '., 'x, 'X => NULL;
ENDCASE => ERROR CantParse;
};
DoWire:
PROC [ wire: Core.Wire, port: Ports.Port, proc:
PROC [ Ports.Port ] ] = {
Complain:
PROC [ i, j:
NAT, shouldBe, is: Ports.Level ] = {
levelName: ARRAY Ports.Level OF CHAR = [L: '0, H: '1, X: 'X];
portName: IO.ROPE ← "In";
reason: IO.ROPE;
position: INT = s.GetIndex[]-1;
IF i<wire.size
THEN {
portName ← IO.PutFR["%s[%d]", IO.rope[portName], IO.int[i]];
IF j<wire[i].size
THEN
portName ← IO.PutFR["%s[%d]", IO.rope[portName], IO.int[j]];
};
reason ←
IO.PutFR["At position %d in oracle file %s, port %s should have been %s, but was %s instead.",
IO.int[position], IO.atom[state.oracleProps.id], IO.rope[portName],
IO.char[levelName[shouldBe]], IO.char[levelName[is]]];
ShowPlaceInStream[s];
Rosemary.Stop[msg: reason];
};
SELECT wire.size
FROM
0 => proc[port ! Disagreement =>
{Complain[0, 0, shouldBe, is]; RESUME}];
ENDCASE => {
FOR i:
NAT
IN [0..wire.size)
DO
wi: Core.Wire = wire[i];
[] ← s.SkipWhitespace[];
SELECT wi.size
FROM
0 => proc[port[i] ! Disagreement =>
{Complain[i, 0, shouldBe, is]; RESUME}];
ENDCASE => {
FOR j:
NAT
IN [0..wi.size)
DO
proc[port[i][j] ! Disagreement =>
{Complain[i, j, shouldBe, is]; RESUME}];
ENDLOOP;
};
ENDLOOP;
};
};
firstTry:
BOOL ←
TRUE;
{
ENABLE CantParse => GOTO FormatError;
[] ← SkipToVisible[ !
IO.EndOfStream =>
IF firstTry
THEN {
firstTry ← FALSE;
s.SetIndex[0]; -- return to beginning of test
RETRY;
}
ELSE GOTO FormatError ];
DoWire[state.oracleProps.ct.public[state.out], p[state.out], SendOut];
ScanBar[];
DoWire[state.oracleProps.ct.public[state.in], p[state.in], ScanIn];
EXITS FormatError => {
position: INT = s.GetIndex[]-1;
reason:
IO.
ROPE =
IO.PutFR["At position %d in oracle file %s, couldn't parse it.",
IO.int[position], IO.atom[state.oracleProps.id]];
ShowPlaceInStream[s];
Rosemary.Stop[msg: reason];
};
};
};
state: OracleState ← NARROW[stateAny];
curClk: BOOL = p[state.clk].b;
IF curClk
AND
NOT state.prevClk
THEN {
ref: REF ANY = Atom.GetProp[$RosemaryOracle, state.oracleProps.id];
WITH ref
SELECT
FROM
s: IO.STREAM => DoValues[s];
f: REF File => DoValues[f.stream];
ENDCASE => ERROR;
state.cycle ← state.cycle+1;
};
state.prevClk ← curClk;
};
ShowPlaceInStream:
PROC [ s:
IO.
STREAM, index:
INT ← -1 ] = {
file: FS.OpenFile = FS.OpenFileFromStream[s ! IO.Error => GOTO DoesntWork];
IF index < 0 THEN index ← s.GetIndex[]+index;
FileViewerOps.OpenSource[fileName: FS.GetName[file].fullFName, index: index, chars: 1];
EXITS DoesntWork => NULL;
};