RosemaryExample.mesa
Copyright © 1987 by Xerox Corporation. All rights reserved.
Last Edited by: Louis Monier January 5, 1987 8:15:51 pm PST
Barth, January 30, 1987 2:50:55 pm PST
-- This is an example of simulation using Rosemary; three parts:
-- how to get a cellType (by extracting an icon in a CD design);
-- how to define a high-level behavioral simulation procedure for this cellType;
-- how to write a test procedure for this a cellType;
-- Generic advice: go take a look at the Ports and BitOps interfaces.
DIRECTORY BitOps, Core, CoreFlat, Ports, PW, Rope, Rosemary, RosemaryUser, Sisyph, TerminalIO;
RosemaryExample:
CEDAR
PROGRAM
IMPORTS CoreFlat, Ports, PW, Rosemary, RosemaryUser, Sisyph, TerminalIO
= BEGIN
Convenient thingies
myDesignName: Rope.
ROPE = "MyDesign";
-- the name of the ChipNDale file MyCell resides in
myCx: Sisyph.Context ← Sisyph.Create[
PW.OpenDesign[myDesignName]];
-- the Sisyph context derived from this design; needed for extraction
Error:
PROC [msg: Rope.
ROPE] ~ {TerminalIO.PutRopes[msg, "\n"];
ERROR};
-- The generic error
MyCell
-- This binds a pair of procs (initialization and simulation) with the name under which a cellType will be registered
MyCellName: Rope.ROPE = Rosemary.Register[roseClassName: "MyCell", init: MyCellInit, evalSimple: MyCellSimple];
-- This proc returns the cellType;
-- We suppose that there is a cell "MyCell.icon" in the design "MyDesign".
MyCell:
PROC
RETURNS [ct: Core.CellType] = {
ct ← Sisyph.
ES[name: "MyCell", cx: myCx];
-- extract the cell by name
-- we supoose that the public wires are "Vdd", "Gnd", "Clock", Seq["In", 4], Seq["Out", 4];
[] ← Rosemary.BindCellType[cellType: ct, roseClassName: MyCellName];
-- register this cellType with Rosemary;
-- the pair of procs cited above can now be used for simulation
Ports.InitPorts[ct, l, none, "Vdd", "Gnd", "Clock"]; -- level (H, L, X), and inputs only
Ports.InitPorts[ct, ls, none, "In"]; -- level sequence, and input only
Ports.InitPorts[ct, ls, drive, "Out"];
-- level sequence, and output only
-- specify the type and direction of wires for simulation
};
-- This is the safest way to proceed: for every public wire, you need an index in order to get the the value on the coprresponding port; nothing garantees the order in which wires appear, so if you hide these indexes in the global frame, you might get a stale value. The best place is in this record. It is a pain to redefine such a record for every cellType you simulate, but it is sooooo much safer...
MyCellState: TYPE = REF MyCellStateRec;
MyCellStateRec:
TYPE =
RECORD [
in, out, vdd, gnd, ck:
NAT ←
LAST[
NAT],
-- with LAST[NAT] you'll catch non-initializes values
previousClock: Ports.Level ← L,
master, slave: Ports.LevelSequence
-- the state associated with the cell: in this case two LevelSequence
];
-- This proc is called once before the simulation starts; it was registered above
MyCellInit: Rosemary.InitProc = {
-- PROC [cellType: Core.CellType, p: Ports.Port, oldStateAny: REF ANY ← NIL, steady: BOOL ← FALSE] RETURNS [stateAny: REF ANY ← NIL]n: NAT ← NARROW[CoreProperties.GetCellTypeProp[cellType, $n], REF NAT]^
state: MyCellState ←
IF oldStateAny#
NIL
THEN
NARROW[oldStateAny]
ELSE
NEW[MyCellStateRec];
-- create a new state or reuse an old one
[state.in, state.out, state.vdd, state.gnd, state.ck] ← Ports.PortIndexes[cellType.public, "In", "Out", "Vdd", "Gnd", "Clock"];
-- finds the index of public wires in the cellType
state.master ← NEW[Ports.LevelSequenceRec[4]];
state.slave ← NEW[Ports.LevelSequenceRec[4]];
Ports.SetLS[state.master, X];
Ports.SetLS[state.slave, X];
-- initialize the state
stateAny ← state;
-- you have to return the state as a REF ANY, so don't forget this
};
-- This proc is called by Rosemary whenever ... (RICK, HELP!!!!)
-- This is an example of edge-triggered latch where the output is always a copy of `slave': when the clock is low, it samples the input into master; on an up-transition of the clock, it copies the value of master into the slave and stop sampling the input.
-- Rosemary can call this proc whenever it pleases, so we need to keep in the state a value of the previous clock in order to differentiate.
MyCellSimple: Rosemary.EvalProc = {
-- PROC [p: Ports.Port, stateAny: REF ANY]
state: MyCellState ← NARROW[stateAny];
IF p[state.vdd].l#H
OR p[state.gnd].l#L
THEN Error["Turn on the power supply, please"];
-- can't hurt!
SELECT p[state.ck].l
FROM
L => Ports.CopyLS[from: p[state.in].ls, to: state.master];
-- level sequences are refs, so you need this proc to copy the values
H =>
IF state.previousClock=L
THEN Ports.CopyLS[from: state.master, to: state.slave];
-- up transition
ENDCASE => NULL;
Ports.CopyLS[from: state.slave, to: p[state.out].ls]; -- always
state.previousClock ← p[state.ck].l;
};
-- This proc is called once per Eval[]
MyCellTest: RosemaryUser.TestProc = {
--
PROC [cellType: Core.CellType, p: Ports.Port, Eval: PROC]
in, out, vdd, gnd, ck: NAT ← LAST[NAT];
[in, out, vdd, gnd, ck] ← Ports.PortIndexes[cellType.public, "In", "Out", "Vdd", "Gnd", "Clock"];
-- port indexes, again
[] ← Rosemary.SetFixedWire[cellType.public[vdd], H];
[] ← Rosemary.SetFixedWire[cellType.public[gnd], L];
-- power on
[] ← Ports.InitPort[cellType.public[ck], l];
[] ← Ports.InitTesterDrive[cellType.public[ck], force];
-- the clock is a level (one bit, values among L, H, X), and the tester forces its value
[] ← Ports.InitPort[cellType.public[in], ls];
[] ← Ports.InitTesterDrive[cellType.public[in], force];
-- the input is a level sequence, and the tester forces its value
[] ← Ports.InitPort[cellType.public[out], ls];
[] ← Ports.InitTesterDrive[cellType.public[out], none];
-- the output is a sequence, and the tester ignores its value initially
Ports.LCToLS[0FH, p[in].ls]; -- initial value, in hex notation
p[ck].l ← L;
p[out].d ← none;
Eval[];
p[ck].l ← H;
Eval[];
-- the register now contains 0FH; output is ignored (because of `none')
p[ck].l ← L;
p[out].d ← none;
Eval[];
p[ck].l ← H;
Ports.LCToLS[0FH, p[out].ls]; -- the expected value
p[out].d ← expect;
Eval[];
-- Because of expect, the computed value will be compared with the value we specify.
-- If the output is incorrect, an error window will open.
};
-- Final call: this creates a tester panel
CreateSimulation:
PROC [ct: Core.CellType, cutSets:
LIST
OF Rope.
ROPE ←
NIL] = {
tester: RosemaryUser.Tester ← RosemaryUser.TestProcedureViewer[
cellType: ct,
testButtons: LIST["OnOfTheManyPossibleTests"],
name: "MyCellTest",
displayWires: RosemaryUser.DisplayPortLeafWires[ct],
cutSet: CoreFlat.CreateCutSet[cellTypes: cutSets]
];
Rosemary.Initialize[simulation: tester.display.simulation, steady: FALSE]
};
RosemaryUser.RegisterTestProc["MyCellTest", MyCellTest];
-- associates a test proc with a button
[] ← CreateSimulation[ct: MyCell[], cutSets:
LIST[MyCellName]];
-- creates a simulation; the cutset specifies to use the simulation proc defined for this cell
END.
MyCellName: Rope.ROPE = Rosemary.Register[roseClassName: "MyCell", init: MyCellInit, evalSimple: MyCellSimple];
MyCell
:
PROC
RETURNS [
ct
: Core.CellType] = {
ct ← either extract the cell or calls a proc;
[] ← Rosemary.BindCellType[cellType: ct, roseClassName: MyCellName];
Ports.InitPorts[ct, l, ls, b, c, lc, none, drive, "Vdd", "Gnd", "Clock"];
};
MyCellState: TYPE = REF MyCellStateRec;
MyCellStateRec
:
TYPE =
RECORD [
in, out, vdd, gnd, ck: NAT ← LAST[NAT],
any other state: any type, including Ports.Level and Ports.LevelSequence
];
MyCellInit
: Rosemary.InitProc = {
state: MyCellState ← IF oldStateAny#NIL THEN NARROW[oldStateAny] ELSE NEW[MyCellStateRec];
[state.in, state.out] ← Ports.PortIndexes[cellType.public, "In", "Out"];
stateAny ← state;
-- you have to return the state as a REF ANY, so don't forget this
};
MyCellSimple
: Rosemary.EvalProc = {
state: MyCellState ← NARROW[stateAny];
body of the proc
};
MyCellTest: RosemaryUser.TestProc = {
--
PROC [cellType: Core.CellType, p: Ports.Port, Eval: PROC]
in, out: NAT ← Ports.PortIndex[cellType.public, "In", "Out"];
[] ← Rosemary.SetFixedWire[cellType.public[vdd], H];
[] ← Rosemary.SetFixedWire[cellType.public[gnd], L];
[] ← Ports.InitPort[cellType.public[ck], l, ls, b, c, lc];
[] ← Ports.InitTesterDrive[cellType.public[ck], none, force, expect];
set values and drives on ports
Eval[];
set values and drives on ports
Eval[];
};
CreateSimulation:
PROC [ct: CellType, cutSets:
LIST
OF Rope.
ROPE ←
NIL] = {
tester ← RosemaryUser.TestProcedureViewer[
cellType: ct,
testButtons: LIST["OnOfTheManyPossibleTests"],
name: "MyCellTest",
displayWires: RosemaryUser.DisplayPortLeafWires[ct],
cutSet: CoreFlat.CreateCutSet[cellTypes: cutSets]
];
Rosemary.Initialize[simulation: tester.display.simulation, steady: FALSE]
};
RosemaryUser.RegisterTestProc["MyCellTest", MyCellTest];
[] ← CreateSimulation[ct: MyCell[], cutSets: LIST[MyCellName]];