Oracle
Impl:
CEDAR
PROGRAM
IMPORTS Atom, CoreCreate, CoreClasses, CoreFlat, CoreOps, CoreProperties, FileViewerOps, FS, IO, Logic, Ports, Rosemary
EXPORTS Oracle
= BEGIN -- OPEN Oracle;
Oracle
WireFromRope:
PROC [ r:
IO.
ROPE, name:
IO.
ROPE ]
RETURNS [ w: Core.Wire ] =
{
s: IO.STREAM = IO.RIS[IO.PutFR["(%s)", IO.rope[r]]];
ref: REF ANY = s.GetRefAny[];
s.Close[];
w ← NARROW[WireFromRef[ref]];
IF name # NIL THEN [] ← CoreOps.SetShortWireName[w, name];
};
WireFromRef:
PROC [ ref:
REF
ANY ]
RETURNS [ w: CoreCreate.
WR ] =
{
WITH ref
SELECT
FROM
size:
REF
INT =>
w ← CoreCreate.Seq[size: size^];
lora:
LIST
OF
REF
ANY => {
w ← CoreCreate.WireList[wrs: WireListFromRefList[lora]];
};
ENDCASE => ERROR;
};
WireListFromRefList:
PROC [ lora:
LIST
OF
REF
ANY ]
RETURNS [ wl:
LIST
OF CoreCreate.
WR ] =
{
wl ← (IF lora=NIL THEN NIL ELSE CONS[WireFromRef[lora.first], WireListFromRefList[lora.rest]]);
};
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 [ wire: Core.Wire ] = {
SELECT wire.size
FROM
0 => [] ← Ports.InitPort[wire, l, aggregate, none];
ENDCASE => {
FOR i:
NAT
IN [0..wire.size)
DO
DoInitPort[wire[i]];
ENDLOOP;
};
};
ct ← CoreClasses.CreateUnspecified[name: OracleName,
public: CoreCreate.Wires["Vdd", "Gnd", "Clk",
WireFromRope[out, "Out"],
WireFromRope[in, "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[CoreOps.FindWire[ct.public, "Out"]];
DoInitPort[CoreOps.FindWire[ct.public, "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 ] = {
ScanLParen:
PROC [ ]
RETURNS [ found:
BOOL ] = {
[] ← s.SkipWhitespace[];
found ← s.PeekChar[ ! IO.EndOfStream => { found ← FALSE; CONTINUE } ] = '(;
IF found THEN { [] ← s.GetChar[]; [] ← s.SkipWhitespace[] };
};
ScanRParen:
PROC [ ]
RETURNS [ found:
BOOL ] = {
[] ← s.SkipWhitespace[];
found ← s.PeekChar[ ! IO.EndOfStream => { found ← FALSE; CONTINUE } ] = ');
IF found THEN { [] ← s.GetChar[]; [] ← s.SkipWhitespace[] };
};
ScanBar:
PROC [ ] = {
[] ← s.SkipWhitespace[];
IF s.GetChar[ !
IO.EndOfStream =>
GOTO Failure ] # '|
THEN GOTO Failure;
[] ← s.SkipWhitespace[];
EXITS Failure => ERROR CantParse;
};
SkipToVisible:
PROC [ ] = {
[] ← s.SkipWhitespace[];
[] ← s.PeekChar[]; -- force EndOfStream if no more characters
};
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 ], portName:
IO.
ROPE ←
NIL, depth:
NAT ← 0 ] = {
Complain:
PROC [ i, j:
NAT, shouldBe, is: Ports.Level ] = {
levelName: ARRAY Ports.Level OF CHAR = [L: '0, H: '1, X: 'X];
reason: IO.ROPE;
position: INT = s.GetIndex[]-1;
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 => {
IF ScanLParen[] THEN parenDepth ← parenDepth+1;
FOR i:
NAT
IN [0..wire.size)
DO
[] ← s.SkipWhitespace[];
DoWire[wire[i], port[i], proc, IO.PutFR["%s[%d]", IO.rope[portName], IO.int[i]], depth+1];
ENDLOOP;
IF ScanRParen[]
THEN {
IF parenDepth>0 THEN parenDepth ← parenDepth-1 ELSE ERROR CantParse;
};
IF parenDepth>depth THEN ERROR CantParse;
};
};
parenDepth: NAT ← 0;
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, "Out"];
ScanBar[];
DoWire[state.oracleProps.ct.public[state.in], p[state.in], ScanIn, "In"];
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;
};