<> <> <> <> <<>> <<-- 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 <> 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>> <> <<-- 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]];