LogicRosemaryImpl.mesa
Copyright © 1986 by Xerox Corporation. All rights reserved.
Created by: Louis Monier December 30, 1986 7:40:13 pm PST
Last Edited by: Louis Monier April 27, 1987 12:22:55 pm PDT
Pradeep Sindhu March 27, 1987 5:11:11 pm PST
McCreight March 19, 1987 1:12:54 pm PST
Hoel, February 20, 1987 6:11:31 pm PST
Barth, February 20, 1987 5:27:10 pm PST
This package provides the basic user-interface for interactive simulation using Rosemary, and a set of primitives: clock generator, oracle, rom, ... These primitives must be used for simulation only and do not produce any layout.
DIRECTORY BitOps, CDEnvironment, CDProperties, CDSequencer, Core, CoreClasses, CoreCreate, CoreFlat, CoreOps, CoreProperties, FileNames, FileViewerOps, FS, HashTable, IO, Logic, LogicUtils, Ports, Rope, RopeList, Rosemary, RosemaryUser, SinixOps, Sisyph, Static;
LogicRosemaryImpl: CEDAR PROGRAM
IMPORTS BitOps, CDEnvironment, CDProperties, CDSequencer, CoreClasses, CoreCreate, CoreFlat, CoreOps, CoreProperties, FileNames, FileViewerOps, FS, HashTable, IO, LogicUtils, Ports, Rope, RopeList, Rosemary, RosemaryUser, SinixOps, Sisyph, Static
EXPORTS LogicUtils, Logic
= BEGIN OPEN LogicUtils, CoreCreate;
Oracle
-- oracle name -> file name; default is oracle name=file name
oracleBindings: HashTable.Table ← HashTable.Create[equal: HashTable.RopeEqual, hash: HashTable.HashRope];
-- called from the interpreter by a user to bind a new file to an oracle
SetOracleFileName: PUBLIC PROC [id, fileName: ROPE] ~ {
[] ← HashTable.Store[oracleBindings, id, fileName];
};
-- called from the interpreter by a user to find out which file is bound to an oracle
GetOracleFileName: PUBLIC PROC [id: ROPE] RETURNS [fileName: ROPE]~ {
ref: REF ← HashTable.Fetch[oracleBindings, id].value;
RETURN[IF ref=NIL THEN id ELSE NARROW[ref]];
};
OracleName: IO.ROPE = Rosemary.Register[roseClassName: "Oracle", init: OracleInit, evalSimple: OracleSimple];
Oracle: PUBLIC PROC [in, out, name: ROPE, log: BOOLFALSE] RETURNS [ct: CellType] = {
-- syntax accepted: in ← " 0 2 32 (0 2) 1 (2 4 (3 3))"
-- 0 means atomic wire
-- 8 means a byte, ...
-- (w1 w2 w3) means a composite wire made of ...
WireFromRope: PROC [descr, name: ROPE, driveLevel: Ports.Drive] RETURNS [wire: Wire] = {
s: IO.STREAM = IO.RIS[IO.PutFR["(%s)", IO.rope[descr]]];
ref: REF ANY = IO.GetRefAny[s];
IO.Close[s];
wire ← WireFromRef[ref, driveLevel];
IF name#NIL THEN wire ← CoreOps.SetShortWireName[wire, name];
};
WireFromRef: PROC [ref: REF, driveLevel: Ports.Drive] RETURNS [wire: Wire] = {
WireListFromRefList: PROC [lora: LIST OF REF ANY] RETURNS [wl: LIST OF WR] = {
wl ← IF lora=NIL THEN NIL ELSE CONS[WireFromRef[lora.first, driveLevel],
WireListFromRefList[lora.rest]];
};
WITH ref SELECT FROM
refSize: REF INT => {
size: INT = refSize^;
wire ← CoreCreate.Seq[size: size];
IF size=0 THEN [] ← Ports.InitPort[wire: wire, levelType: l, driveType: aggregate, initDrive: driveLevel]
ELSE [] ← Ports.InitPort[wire: wire, levelType: ls, driveType: seperate, initDrive: driveLevel];
CoreProperties.PutWireProp[wire, oracleValueProp, oracleValueProp]; --non-NIL
-- I tag the wires which will receive sequences of values
};
lora: LIST OF REF ANY =>
wire ← CoreCreate.WireList[wrs: WireListFromRefList[lora]]; -- no l or ls
ENDCASE => ERROR;
};
inWire, outWire: Wire;
IF out=NIL OR in=NIL OR name=NIL THEN Error["Missing parameter on oracle"];
inWire ← WireFromRope[in, "In", none]; -- also sets the ports
outWire ← WireFromRope[out, "Out", force]; -- also sets the ports
ct ← CoreClasses.CreateUnspecified[name: OracleName, public: Wires["CK", inWire, outWire]];
CoreProperties.PutCellTypeProp[ct, $oracle, name];
CoreProperties.PutCellTypeProp[ct, $log, NEW[BOOL ← log]];
[] ← Rosemary.BindCellType[cellType: ct, roseClassName: OracleName];
[] ← CoreFlat.CellTypeCutLabels[ct, logicCutSet, bottomCutSet];
Ports.InitPorts[ct, l, none, "CK"];
};
OracleState: TYPE = REF OracleStateRec;
OracleStateRec: TYPE = RECORD [
oracleName: ROPENIL, -- entry in `oracleBindings' table
clk, in, out: NATLAST[NAT],
inWire, outWire: Wire,
stopAfterOneRun: BOOLFALSE,
maxNbCycle: INT ← 0,
log: BOOL,
prevClk: Ports.Level ← L,
sLog: IO.STREAMNIL,
cycle: INT ← -1
];
OracleInit: Rosemary.InitProc = {
state: OracleState = NEW[OracleStateRec];
clk, in, out: NATLAST[NAT];
id: ROPENARROW[CoreProperties.GetCellTypeProp[cellType, $oracle]];
log: BOOLNARROW[CoreProperties.GetCellTypeProp[cellType, $log], REF BOOL]^;
path: LIST OF ROPE = LIST [CDEnvironment.GetWorkingDirectory[]];
state.oracleName ← FileNames.FileWithSearchRules[root: GetOracleFileName[id], defaultExtension: ".oracle", requireExtension: FALSE, requireExact: TRUE, searchRules: path].fullPath;
[state.clk, state.in, state.out] ← Ports.PortIndexes[cellType.public, "CK", "In", "Out"];
state.inWire ← cellType.public[state.in];
state.outWire ← cellType.public[state.out];
[state.stopAfterOneRun, state.maxNbCycle] ← ParseFileAndDecorateWires[state.oracleName, state.inWire, state.outWire];
state.log ← log;
state.prevClk ← L;
IF log THEN state.sLog ← FS.StreamOpen[IO.PutFR["///Temp/%g.bugs", IO.rope[id]], $create];
state.cycle ← -1;
stateAny ← state;
};
-- Wrong value during the simulation
Disagreement: SIGNAL [shouldBe, is: Ports.Level] = CODE;
OracleSimple: Rosemary.EvalProc = {
LogMismatch: PROC [sLog: IO.STREAM, shouldBe, is: Ports.Level, index: CARD] ~ {
PortToRope: PROC [level: Ports.Level] RETURNS [r: ROPE] ~ {
r ← SELECT level FROM L => "L", H => "H", X => "X", ENDCASE => ERROR;
};
IO.PutF[sLog, "At %g should have been %g but was %g\n", IO.int[index], IO.rope[PortToRope[shouldBe]], IO.rope[PortToRope[is]]];
};
Mismatch: PROC [oracleName: ROPE, shouldBe, is: Ports.Level, index: CARD] ~ {
IF state.log THEN LogMismatch[state.sLog, shouldBe, is, index]
ELSE {PointAt[oracleName, index]; Disagreement[shouldBe, is]};
};
-- bit-wise comparison; if L or H, check; if X, ignore
Compare: PROC [wire: Wire, cycle: CARD, p: Ports.Port] ~ {
CompareLevel: PROC [shouldBe, is: Ports.Level, index: CARD] ~ {
IF shouldBe#X AND shouldBe#is THEN Mismatch[state.oracleName, shouldBe, is, index];
};
CompareLS: PROC [shouldBe, is: Ports.LevelSequence, index: CARD] ~ {
FOR i: NAT IN [0..shouldBe.size) DO CompareLevel[shouldBe[i], is[i], index] ENDLOOP;
};
ref: REF ← CoreProperties.GetWireProp[wire, oracleValueProp];
IF ref=NIL THEN FOR i: NAT IN [0..wire.size) DO Compare[wire[i], cycle, p[i]] ENDLOOP
ELSE {
values: Values ← NARROW[ref];
IF wire.size=0 THEN CompareLevel[values[cycle].ls[0], p.l, values[cycle].filePosition]
ELSE CompareLS[values[cycle].ls, p.ls, values[cycle].filePosition];
};
};
-- if L or H, drive; if X, tristate (drive←none)
SetPort: PROC [wire: Wire, cycle: NAT, p: Ports.Port] ~ {
ref: REF ← CoreProperties.GetWireProp[wire, oracleValueProp];
IF ref=NIL THEN FOR i: NAT IN [0..wire.size) DO SetPort[wire[i], cycle, p[i]] ENDLOOP
ELSE {
values: Values ← NARROW[ref];
IF wire.size=0 THEN {  -- special case for atomic
p.l ← values[cycle].ls[0];
p.d ← IF p.l=X THEN none ELSE drive;
}
ELSE {
Ports.CopyLS[from: values[cycle].ls, to: p.ls];
FOR i: NAT IN [0..p.ls.size) DO
p.ds[i] ← IF p.ls[i]=X THEN none ELSE drive;
ENDLOOP;
};
};
};
state: OracleState ← NARROW[stateAny];
curClk: Ports.Level = p[state.clk].l;
IF state.prevClk=L AND curClk=H THEN { -- up-going transition
state.cycle ← (state.cycle+1) MOD state.maxNbCycle;
};
IF state.cycle=-1 THEN RETURN;
-- put values on "out" ports for current cycle
FOR i: NAT IN [0..state.outWire.size) DO
SetPort[state.outWire[i], state.cycle, p[state.out][i]];
ENDLOOP;
IF state.prevClk=H AND curClk=L THEN { -- down-transition: check inputs
-- compare values on "in" ports for cycle n
FOR i: NAT IN [0..state.inWire.size) DO
Compare[state.inWire[i], state.cycle, p[state.in][i]];
ENDLOOP;
IF state.cycle=state.maxNbCycle-1 THEN
SELECT TRUE FROM
state.log AND state.stopAfterOneRun => {
IO.Close[state.sLog];
Rosemary.Stop[msg: "Oracle completed; look at ///Temp/*.bugs"]};
state.log AND ~state.stopAfterOneRun =>
IO.PutF[state.sLog, "Processed the file entirely at cycle %g\n", IO.int[state.cycle]];
~state.log AND state.stopAfterOneRun =>
Rosemary.Stop[msg: "Oracle completed successfully"];
ENDCASE => NULL;
};
IF curClk#X THEN state.prevClk ← curClk;
};
PointAt: PROC [oracleName: ROPE, index: CARD] = {
FileViewerOps.OpenSource[fileName: oracleName, index: index, chars: 1];
};
Bug: ERROR [msg: ROPE] = CODE;
CrashAndPointTo: PROC [oracleName, msg: ROPE, index: CARD] = {
PointAt[oracleName, index];
Bug[msg];
};
oracleValueProp: ATOM = $OracleValueProp; -- of type Values
Values: TYPE = REF ValuesRec;
ValuesRec: TYPE = RECORD[seq: SEQUENCE size: NAT OF Value];
used during simulation
Value: TYPE = REF ValueRec;
ValueRec: TYPE = RECORD[
ls: Ports.LevelSequence ← NIL, -- if atomic wire, use ls[0]
filePosition: CARD ← 0
];
-- Parses the file and decorates every sub-wire of "In" and "Out" with the values
-- Values are coded in hex, but digits are extended to accomodate X
-- Hex digits are 0, 1, 2, ..., 9, A, B, C, D, E , F, X, or (b b b b) with b=0, 1, or X
-- Examples of values: 37, FFEA7B, 7AXX2, 2BAD, XXXX
-- If not enough digits are specified, the value is right-justified and the msb are zero-extended
-- Atomic wires are restricted to 0, 1, X
-- Parser always absorbs any white space before the token
ParseFileAndDecorateWires: PROC [oracleName: ROPE, in, out: Wire] RETURNS [stopAfterOneRun: BOOLFALSE, numberOfCycles: NAT ← 0] ~ {
-- skip white space, then get a char and check it
Absorb: PROC [source: IO.STREAM, c: CHAR, msg: ROPE] ~ {
[] ← IO.SkipWhitespace[source];
IF IO.GetChar[source] # c THEN
CrashAndPointTo[oracleName, msg, IO.GetIndex[source]-1];
};
-- a hack to get the number of vectors
CountBars: PROC [source: IO.STREAM] RETURNS [length: NAT ← 0] ~ {
DO IF IO.GetChar[source ! IO.EndOfStream => GOTO Done] = '| THEN length ← length+1;
REPEAT Done => NULL;
ENDLOOP;
};
GetLevel: PROC [] RETURNS [level: Ports.Level] ~ {
SELECT IO.GetChar[source] FROM
'0 => level ← L;
'1 => level ← H;
'X, 'x => level ← X;
ENDCASE => CrashAndPointTo[oracleName, "Not a valid level", IO.GetIndex[source]-1];
};
GetHexDigit: PROC [] RETURNS [h: Ports.LevelSequence] ~ {
h ← NEW[Ports.LevelSequenceRec[4]];
SELECT IO.PeekChar[source] FROM
'( => {
[] ← IO.GetChar[source];  -- absorb the '(
h[0] ← GetLevel[];
h[1] ← GetLevel[];
h[2] ← GetLevel[];
h[3] ← GetLevel[];
Absorb[source, '), "Missing )"];
};
ENDCASE => {
c: CHAR;
SELECT c ← IO.GetChar[source] FROM
IN ['0 .. '9] => Ports.LCToLS[ORD[c]-ORD['0], h];
IN ['A .. 'F] => Ports.LCToLS[ORD[c]-ORD['A]+10, h];
'X, 'x => Ports.SetLS[h, X];
ENDCASE => CrashAndPointTo[oracleName, "not a hex digit", IO.GetIndex[source]];
};
};
IsTerminator: PROC [c: CHAR] RETURNS [BOOL] ~ {
RETURN [c IN [IO.NUL .. IO.SP] OR c='|]};
GetToken: PROC [nbBits: NAT] RETURNS [ls: Ports.LevelSequence]= {
digitIndex: NAT ← 100; -- we can extend on the left with 100 zeros
lsSize: NATMAX[nbBits, 1]; -- atomic => 0, but represented as ls
ls ← NEW[Ports.LevelSequenceRec[lsSize]];
[] ← IO.SkipWhitespace[source];
IF IsTerminator[IO.PeekChar[source]] THEN CrashAndPointTo[oracleName, "not a value", IO.GetIndex[source]];
WHILE NOT IsTerminator[IO.PeekChar[source]] DO
h: Ports.LevelSequence ← GetHexDigit[];
FOR i: NAT IN [0..4) DO
bigValue[digitIndex+i] ← h[i];
ENDLOOP;
digitIndex ← digitIndex+4;
ENDLOOP;
-- parsed value is now left-justified in bigValue
FOR i: NAT IN [0..lsSize) DO
ls[i] ← bigValue[digitIndex-lsSize+i];
ENDLOOP;
Ports.SetLS[bigValue, L]; -- clean-up
};
-- We know at this point how many tokens to find on a line
ParseOneLine: PROC [cycle: NAT] ~ {
values: Values;
val: Value;
PutValuesOnLeavesLtoR: PROC [wire: Wire, level: NAT] ~ {
ref: REF ← CoreProperties.GetWireProp[wire, oracleValueProp];
IF ref#NIL THEN {
values ← NARROW[ref];
val ← NEW[ValueRec ← [
ls: GetToken[nbBits: wire.size],
filePosition: IO.GetIndex[source]-1]];
values[cycle] ← val;
}
ELSE {
IF level>0 THEN Absorb[source, '(, "Missing ("]; -- get the "("
FOR i: NAT IN [0..wire.size) DO PutValuesOnLeavesLtoR[wire[i], level+1] ENDLOOP;
IF level>0 THEN Absorb[source, '), "Missing )"]; -- get the ")"
};
};
PutValuesOnLeavesLtoR[out, 0]; -- read left side (stimuli)
Absorb[source, '|, "Missing |"];
PutValuesOnLeavesLtoR[in, 0]; -- read right side (correct answers)
};
InitValueSequences: PROC [wire: Wire, nb: NAT] ~ {
IF CoreProperties.GetWireProp[wire, oracleValueProp]#NIL THEN
CoreProperties.PutWireProp[wire, oracleValueProp, NEW[ValuesRec[nb]]]
ELSE FOR i: NAT IN [0..wire.size) DO InitValueSequences[wire[i], nb] ENDLOOP;
};
-- Get the file and find the number of vectors
source: IO.STREAMFS.StreamOpen[oracleName];
bigValue: Ports.LevelSequence ← NEW[Ports.LevelSequenceRec[300]]; -- for parser
numberOfCycles ← CountBars[source];
Ports.SetLS[bigValue, L];
IO.SetIndex[source, 0]; -- return to beginning of test
-- Initialize values on In and Out wires
InitValueSequences[in, numberOfCycles];
InitValueSequences[out, numberOfCycles];
-- Parse the file
FOR cycle: NAT IN [0..numberOfCycles) DO ParseOneLine[cycle] ENDLOOP;
[] ← IO.SkipWhitespace[source];
SELECT TRUE FROM
IO.EndOf[source] => stopAfterOneRun ← FALSE;
IO.PeekChar[source]='. => stopAfterOneRun ← TRUE;
ENDCASE => CrashAndPointTo[oracleName, "What is this???", IO.GetIndex[source]];
};
Waveform generator
The waveform generator provides the capability to generate irregular level sequences for schematic-level Rosemary simulations. The icon (WaveForm.icon) has 3 parameters:
- val: a rope of 0's, 1's and X's specifying the sequence of levels to be affected to the output port
- freq: the requency of change (default is 2, the default period of the clock generator)
- firstEdge: the instant in time of the first transition (default is 1, as for the clock generator)
The output wire will stay at the first element of the level sequence until firstEdge, and then will progress circularly in the sequence every freq cycles (remember that the clock generator has a period of 2!). It should be noticed that the first value will be used up to firstEdge, and then for freq cycles more.
Wave: TYPE ~ REF WaveRec;
WaveRec: TYPE ~ RECORD [index: NAT ← 0, ls: Ports.LevelSequence ← NIL];
WaveFormName: ROPE = Rosemary.Register[roseClassName: "WaveForm", init: WaveFormInit, evalSimple: WaveFormSimple];
WaveForm: PUBLIC PROC [val: ROPE, freq: NAT, firstEdge: INT] RETURNS [ct: CellType] ~ {
Analyze: Rope.ActionType ~ {
SELECT c FROM
'0, 'L => ls[index] ← L;
'1, 'H => ls[index] ← H;
'X, 'x => ls[index] ← X;
ENDCASE => quit ← TRUE;
IF quit THEN RETURN;
index ← index+1;
};
wave: Wave ← NEW[WaveRec];
index: NAT ← 0;
ls: Ports.LevelSequence ← NEW[Ports.LevelSequenceRec[Rope.Length[val]]];
IF Rope.Length[val]=0 THEN LogicUtils.Error["The waveform value is missing"];
IF Rope.Map[base: val, action: Analyze] THEN LogicUtils.Error["The waveform value contains illegal characters"];
ct ← CoreClasses.CreateUnspecified[
name: WaveFormName,
public: CoreCreate.Wires["RosemaryLogicTime", "Out"]];
CoreProperties.PutCellTypeProp[ct, $ls, ls];
CoreProperties.PutCellTypeProp[ct, $freq, NEW[NAT ← freq]];
CoreProperties.PutCellTypeProp[ct, $firstEdge, NEW[INT ← firstEdge]];
[] ← Rosemary.BindCellType[cellType: ct, roseClassName: WaveFormName];
[] ← CoreFlat.CellTypeCutLabels[ct, logicCutSet, bottomCutSet];
Ports.InitPorts[ct, l, drive, "Out"];
Ports.InitPorts[ct, l, none, "RosemaryLogicTime"];
};
WaveState: TYPE = REF WaveStateRec;
WaveStateRec: TYPE = RECORD [
out, time: NATLAST[NAT],
index: NAT ← 0,
ls: Ports.LevelSequence,
freq: NAT ← 0,
counter: INT ← 0,
lastTime: Ports.Level ← H];
WaveFormInit: Rosemary.InitProc = {
state: WaveState ← IF oldStateAny=NIL THEN NEW[WaveStateRec] ELSE NARROW[oldStateAny];
firstEdge: INT = NARROW[CoreProperties.GetCellTypeProp[cellType, $firstEdge], REF INT]^;
[state.out, state.time] ← Ports.PortIndexes[cellType.public, "Out", "RosemaryLogicTime"];
state.index ← 0;
state.ls ← NARROW[CoreProperties.GetCellTypeProp[cellType, $ls]];
state.freq ← NARROW[CoreProperties.GetCellTypeProp[cellType, $freq], REF NAT]^;
state.counter ← 0-state.freq-firstEdge;
state.lastTime ← p[state.time].l;
stateAny ← state;
};
WaveFormSimple: Rosemary.EvalProc = {
state: WaveState ← NARROW[stateAny];
IF state.lastTime=p[state.time].l THEN RETURN;
state.counter ← state.counter+1;
IF state.counter>=state.freq THEN {
state.counter ← 0;
state.index ← state.index+1;
IF state.index>=state.ls.size THEN state.index ← 0;
};
p[state.out].l ← state.ls[state.index];
state.lastTime ← p[state.time].l;
};
Clock Generator
ClockGenName: ROPE = Rosemary.Register[roseClassName: "ClockGen", init: ClockGenInit, evalSimple: ClockGenSimple];
ClockGen: PUBLIC PROC [up, dn, firstEdge: INT, initLow: BOOL] RETURNS [ct: CellType] ~ {
ct ← CoreClasses.CreateUnspecified[
name: ClockGenName,
public: Wires["Clock", "RosemaryLogicTime"]];
CoreProperties.PutCellTypeProp[ct, $up, NEW[INT ← up]];
CoreProperties.PutCellTypeProp[ct, $dn, NEW[INT ← dn]];
CoreProperties.PutCellTypeProp[ct, $firstEdge, NEW[INT ← firstEdge]];
CoreProperties.PutCellTypeProp[ct, $initLow, NEW[BOOL ← initLow]];
CoreProperties.PutCellTypeProp[ct, $DAUserIgnoreForSelection, $Exists];
[] ← Rosemary.BindCellType[cellType: ct, roseClassName: ClockGenName];
[] ← CoreFlat.CellTypeCutLabels[ct, logicCutSet, bottomCutSet];
Ports.InitPorts[ct, l, drive, "Clock"];
Ports.InitPorts[ct, l, driveStrong, "Clock"];
Ports.InitPorts[ct, l, none, "RosemaryLogicTime"];
};
ClockState: TYPE = REF ClockStateRec;
ClockStateRec: TYPE = RECORD [
ck, time: NATLAST[NAT],
up, dn, firstEdge: INT,
initLow: BOOL,
counter: INT ← 0,
lastTime: Ports.Level ← H];
ClockGenInit: Rosemary.InitProc = {
state: ClockState ← NEW[ClockStateRec];
{OPEN state;
infinity: INTLAST[INT]/4; -- to avoid overflow
[ck, time] ← Ports.PortIndexes[cellType.public, "Clock", "RosemaryLogicTime"];
initLow ← NARROW[CoreProperties.GetCellTypeProp[cellType, $initLow], REF BOOL]^;
up ← NARROW[CoreProperties.GetCellTypeProp[cellType, $up], REF INT]^;
dn ← NARROW[CoreProperties.GetCellTypeProp[cellType, $dn], REF INT]^;
firstEdge ← NARROW[CoreProperties.GetCellTypeProp[cellType, $firstEdge], REF INT]^;
IF up=-1 THEN up ← infinity;
IF dn=-1 THEN dn ← infinity;
IF firstEdge=-1 THEN firstEdge ← infinity;
lastTime ← p[time].l};
stateAny ← state;
};
ClockGenSimple: Rosemary.EvalProc = {
state: ClockState ← NARROW[stateAny];
{OPEN state;
t0, normTime: INT;
state: ClockState ← NARROW[stateAny];
IF p[time].l=X THEN {p[ck].l ← state.lastTime ← L; RETURN}; -- only at the beginning
IF state.lastTime=p[time].l THEN RETURN;
state.counter ← state.counter+1;
t0 ← IF state.initLow THEN state.firstEdge+state.up ELSE state.firstEdge;
normTime ← (state.counter-t0+state.up+state.dn) MOD (state.up+state.dn);
p[ck].l ← SELECT TRUE FROM
state.counter<state.firstEdge => IF state.initLow THEN L ELSE H,
normTime< state.dn => L,
ENDCASE => H;
state.lastTime ← p[time].l;
}};
Assertion Checking
StopName: ROPE = Rosemary.Register[roseClassName: "Stop", init: StopInit, evalSimple: StopSimple];
Stop: PUBLIC PROC [] RETURNS [ct: CellType] ~ {
ct ← CoreClasses.CreateUnspecified[
name: StopName,
public: Wires["ShouldBeFalse", "RosemaryLogicTime"]];
[] ← Rosemary.BindCellType[cellType: ct, roseClassName: StopName];
[] ← CoreFlat.CellTypeCutLabels[ct, logicCutSet, bottomCutSet];
Ports.InitPorts[ct, l, none, "ShouldBeFalse", "RosemaryLogicTime"];
};
StopState: TYPE = REF StopStateRec;
StopStateRec: TYPE = RECORD [
in, time: NATLAST[NAT],
lastTime: Ports.Level ← H];
StopInit: Rosemary.InitProc = {
state: StopState ← NEW[StopStateRec];
[state.in, state.time] ← Ports.PortIndexes[cellType.public, "ShouldBeFalse", "RosemaryLogicTime"];
state.lastTime ← p[state.time].l;
stateAny ← state;
};
StopSimple: Rosemary.EvalProc = {
state: StopState ← NARROW[stateAny];
{OPEN state;
IF p[in].l#L AND p[time].l#lastTime THEN Rosemary.Stop[msg: "User-defined assertion is wrong", data: p, reason: $UserDefined];
lastTime ← p[time].l;
}};
Rom (only for simulation; at most 10 words)
RomName: ROPE = Rosemary.Register[roseClassName: "Rom", init: RomInit, evalSimple: RomSimple];
RomRep: TYPE = ARRAY [0..10) OF INT; -- a hack
Rom: PROC [n, b: NAT, v: RomRep] RETURNS [ct: CellType] ~ {
a: NAT ← BitOps.NBits[n];
ct ← CoreClasses.CreateUnspecified[name: RomName,
public: Wires[Seq["ad", a], Seq["out", b]]];
CoreProperties.PutCellTypeProp[ct, $value, NEW[RomRep ← v]];
[] ← Rosemary.BindCellType[cellType: ct, roseClassName: RomName];
[] ← CoreFlat.CellTypeCutLabels[ct, logicCutSet, bottomCutSet];
Ports.InitPorts[ct, ls, none, "ad"];
Ports.InitPorts[ct, ls, drive, "out"];
};
RomState: TYPE = REF RomStateRec;
RomStateRec: TYPE = RECORD [
ad, out: NATLAST[NAT],
val: ARRAY [0..10) OF Ports.LevelSequence];
RomInit: Rosemary.InitProc = {
state: RomState ← NEW[RomStateRec];
val: RomRep ← NARROW[CoreProperties.GetCellTypeProp[cellType, $value], REF RomRep]^;
b: NAT;
[state.ad, state.out] ← Ports.PortIndexes[cellType.public, "ad", "out"];
b ← p[state.out].ls.size;
FOR i: NAT IN [0..10) DO
state.val[i] ← NEW[Ports.LevelSequenceRec[b]];
Ports.LCToLS[val[i], state.val[i]];
ENDLOOP;
stateAny ← state;
};
RomSimple: Rosemary.EvalProc = {
state: RomState ← NARROW[stateAny];
IF LogicUtils.HasX[p[state.ad].ls] THEN Ports.SetLS[p[state.out].ls, X]
ELSE {ad: CARD ← Ports.LSToC[p[state.ad].ls];
Ports.CopyLS[from: state.val[ad], to: p[state.out].ls]
};
};
Utilities
-- These cutsets are used by Rosemary
logicCutSet: PUBLIC ROPE ← "Logic";    -- for standard cells
macroCutSet: PUBLIC ROPE ← "LogicMacro";  -- for composite cells (e.g. adder, counter, ...)
bottomCutSet: PUBLIC ROPE ← "LogicBottom";  -- for unspecified (e.g. clock, oracle, ...)
-- These belong in Sch, but Sch must be redone, so in the mean time...
RoseProbeProp: ATOM ← $RoseProbe; -- only value: $Yes
SetObjectPort: PUBLIC PROC [cx: Sisyph.Context, initType: Ports.LevelType ← b, initDrive: Ports.Drive ← none] = {
Sisyph.Eval[cx, "coreProps ← CoreProperties.PutProp[coreProps, $PortData,
NEW[Ports.PortDataRec ← [
type: initType,
drive: initDrive]]]"];
};
SetObjectTesterDrive: PUBLIC PROC [cx: Sisyph.Context, initDrive: Ports.Drive ← none] = {
Sisyph.Eval[cx, "coreProps ← CoreProperties.PutProp[coreProps, $PortTesterDrive,
NEW[Ports.Drive ← initDrive]]"];
};
LogicTest: RosemaryUser.TestProc ~ {
-- Wires have no memory unless the $Memory property is set to $Yes (the only recognized value).
logicTime: NAT ← Ports.PortIndex[cellType.public, "RosemaryLogicTime"];
memory: BOOLFALSE;
IF CoreProperties.GetCellTypeProp[cellType, $Memory]#NIL THEN memory ← TRUE;
p[logicTime].b ← TRUE;
p[logicTime].d ← drive;
DO
p[logicTime].b ← NOT p[logicTime].b;
Eval[memory];
ENDLOOP;
};
ExtractSelectedObjAndRunRosemary: PROC [comm: CDSequencer.Command] = {
globalNames: Sisyph.ROPES ← Sisyph.GetGlobalNames[Sisyph.Create[comm.design]];
-- a wire is innocent until proven guilty
UnnamedOrGlobal: PROC [wire: Wire] RETURNS [BOOL] ~ {
name: ROPE = CoreOps.GetShortWireName[wire];
RETURN [name=NIL OR RopeList.Memb[globalNames, name]];
};
Unconnected: PROC [wire: Wire] RETURNS [BOOL] ~ {
RETURN [CoreProperties.GetWireProp[wire, Static.staticCutSetProp]#NIL];
};
NotBus: PROC [wire: Wire] RETURNS [BOOL] ~ {
FOR i: NAT IN [0..wire.size) DO -- Skipped if wire is atomic
IF wire.elements[i].size#0 THEN RETURN[TRUE];
ENDLOOP;
RETURN [FALSE];
};
WorthGraphing: CoreOps.EachWireProc ~ {
SELECT TRUE FROM
UnnamedOrGlobal[wire] => RETURN [subWires: FALSE]; -- don't even consider sons
Unconnected[wire] => RETURN [subWires: FALSE]; -- don't even consider sons
NotBus[wire] => RETURN [subWires: TRUE]; -- will have interesting sons
ENDCASE => graphWires ← CONS[NEW [CoreFlat.FlatWireRec ← [wire: wire]], graphWires];
};
AtomicsInGraph: PROC [wire: Wire] ~ {
graphWires ← CONS[NEW[CoreFlat.FlatWireRec ← [flatCell: CoreFlat.rootCellType, wire: wire]], graphWires];
};
logicVdd, logicGnd: NAT;
graphWires: CoreFlat.FlatWires ← NIL;
root, cellType: CellType;
internal: Core.WireSeq;
labels: LIST OF ROPE;
[root: root, cell: cellType] ← SinixOps.SelectedCellType[comm.design, Sisyph.mode];
IF root=NIL THEN RETURN; -- Extraction ended in error, message already printed
-- Let's make sure we have something to simulate
IF cellType.class#CoreClasses.recordCellClass THEN Error["I can't simulate this thing"];
-- Find out which wires to display: top-level only
internal ← NARROW[cellType.data, CoreClasses.RecordCellType].internal;
[] ← CoreOps.VisitWireSeq[internal, WorthGraphing];
-- Prepare the cell for simulation
logicVdd ← CoreOps.GetWireIndex[cellType.public, "Vdd"]; -- -1 means not found
logicGnd ← CoreOps.GetWireIndex[cellType.public, "Gnd"];
IF logicVdd#-1 THEN [] ← Rosemary.SetFixedWire[cellType.public[logicVdd], H];
IF logicGnd#-1 THEN [] ← Rosemary.SetFixedWire[cellType.public[logicGnd], L];
labels ← SELECT CoreProperties.GetCellTypeProp[from: cellType, prop: $Simulation] FROM
$Fast => LIST[macroCutSet, logicCutSet], -- gate and macro level
$Transistors => LIST[bottomCutSet],  -- transistor level, except for oracles, ...
ENDCASE => LIST[logicCutSet];  -- gate level
CDProperties.PutDesignProp[comm.design, $DAUserRoseDisplay, RosemaryUser.TestProcedureViewer[
cellType: cellType,
testButtons: LIST["Logic Test"],
name: CoreOps.GetCellTypeName[cellType],
displayWires: graphWires,
graphWires: graphWires,
cutSet: CoreFlat.CreateCutSet[labels: labels]].display];
};
myDefaultGlobalNames: Sisyph.ROPES = LIST ["Vdd", "Gnd", "RosemaryLogicTime"];
Sisyph.defaultGlobalNames ← myDefaultGlobalNames;
-- Entry goes in Sisyph Menu
CDSequencer.ImplementCommand[key: $CoreRosemaryExtractSelectedObjAndRunRosemary, proc: ExtractSelectedObjAndRunRosemary, queue: doQueue];
RosemaryUser.RegisterTestProc["Logic Test", LogicTest];
END.