RosemaryVectorImpl.mesa
Copyright © 1985, 1986 by Xerox Corporation. All rights reserved.
Barth, January 12, 1988 10:42:18 am PST
Jean-Marc Frailong September 30, 1988 11:40:09 am PDT
DIRECTORY Core, CoreFlat, CoreOps, FS, IO, Ports, RefTab, Rope, Rosemary, RosemaryVector, TerminalIO;
RosemaryVectorImpl: CEDAR PROGRAM
IMPORTS CoreFlat, CoreOps, FS, IO, Ports, RefTab, Rope, Rosemary, TerminalIO
EXPORTS RosemaryVector = BEGIN OPEN RosemaryVector;
OpenVectorFile: PUBLIC PROC [fileName: Rope.ROPE, port: Ports.Port, read: BOOLTRUE] RETURNS [vectorFile: VectorFile] = {
vectorFile ← NEW [VectorFileRec];
vectorFile.port ← port;
vectorFile.stream ← FS.StreamOpen[fileName, IF read THEN $read ELSE $create];
};
CloseVectorFile: PUBLIC PROC [vectorFile: VectorFile] = {
IO.Close[vectorFile.stream];
};
WriteVector: PUBLIC PROC [vectorFile: VectorFile, neverSayExpect: BOOLFALSE] = {
WriteDrive: PROC [d: Ports.Drive] = {
IO.PutRope[vectorFile.stream, Ports.driveNames[IF neverSayExpect AND d=expect THEN none ELSE d]];
IO.PutRope[vectorFile.stream, " "];
};
WriteLevel: PROC [l: Ports.Level] = {
IO.PutRope[vectorFile.stream, Ports.levelNames[l]];
IO.PutRope[vectorFile.stream, " "];
};
WriteBool: PROC [b: BOOL] = {
IO.PutRope[vectorFile.stream, IF b THEN "T " ELSE "F "];
};
WritePort: PROC [p: Ports.Port] = {
IF p.levelType=composite THEN FOR i: NAT IN [0..p.size) DO
WritePort[p[i]];
ENDLOOP
ELSE {
IO.PutRope[vectorFile.stream, Ports.driveTypeNames[p.driveType]];
IO.PutRope[vectorFile.stream, " "];
SELECT p.driveType FROM
aggregate => WriteDrive[p.d];
separate => FOR i: NAT IN [0..p.ds.size) DO
WriteDrive[p.ds[i]];
ENDLOOP;
ENDCASE => ERROR;
SELECT p.levelType FROM
l => WriteLevel[p.l];
ls => FOR i: NAT IN [0..p.ls.size) DO
WriteLevel[p.ls[i]];
ENDLOOP;
b => WriteBool[p.b];
bs => FOR i: NAT IN [0..p.bs.size) DO
WriteBool[p.bs[i]];
ENDLOOP;
c => IO.PutF[vectorFile.stream, "%g ", IO.card[p.c]];
lc => IO.PutF[vectorFile.stream, "%g ", IO.card[p.lc]];
q => FOR i: NAT IN [0..4) DO
IO.PutF[vectorFile.stream, "%g ", IO.card[p.q[i]]];
ENDLOOP;
ENDCASE => ERROR;
};
};
WritePort[vectorFile.port];
};
ReadVector: PUBLIC PROC [vectorFile: VectorFile] = {
ReadDrive: PROC RETURNS [d: Ports.Drive] = {
name: Rope.ROPEIO.GetID[vectorFile.stream];
d ← Ports.FindDrive[name];
};
ReadLevel: PROC RETURNS [l: Ports.Level] = {
name: Rope.ROPEIO.GetID[vectorFile.stream];
l ← Ports.FindLevel[name];
};
ReadBool: PROC RETURNS [b: BOOL] = {
name: Rope.ROPEIO.GetID[vectorFile.stream];
b ← SELECT TRUE FROM
Rope.Equal[name, "T"] => TRUE,
Rope.Equal[name, "F"] => FALSE,
ENDCASE => ERROR;
};
ReadPort: PROC [p: Ports.Port] = {
IF p.levelType=composite THEN FOR i: NAT IN [0..p.size) DO
ReadPort[p[i]];
ENDLOOP
ELSE {
name: Rope.ROPEIO.GetID[vectorFile.stream];
p.driveType ← Ports.FindDriveType[name];
SELECT p.driveType FROM
aggregate => p.d ← ReadDrive[];
separate => FOR i: NAT IN [0..p.ds.size) DO
p.ds[i] ← ReadDrive[];
ENDLOOP;
ENDCASE => ERROR;
SELECT p.levelType FROM
l => p.l ← ReadLevel[];
ls => FOR i: NAT IN [0..p.ls.size) DO
p.ls[i] ← ReadLevel[];
ENDLOOP;
b => p.b ← ReadBool[];
bs => FOR i: NAT IN [0..p.bs.size) DO
p.bs[i] ← ReadBool[];
ENDLOOP;
c => p.c ← IO.GetCard[vectorFile.stream];
lc => p.lc ← IO.GetCard[vectorFile.stream];
q => FOR i: NAT IN [0..4) DO
p.q[i] ← IO.GetCard[vectorFile.stream];
ENDLOOP;
ENDCASE => ERROR;
};
};
ReadPort[vectorFile.port];
};
CheckDUTConnections: PROC [root: Core.CellType, target: Target] RETURNS [failed: BOOL] ~ {
Verify that the public of the DUT does not have it's DAGness altered by the way it is inserted in the simulation CellType.
EachAtomicPublic: PROC [wire: Core.Wire] ~ {
canonized: CoreFlat.FlatWire ← NEW[CoreFlat.FlatWireRec];
otherWire: Core.Wire;
canonized^ ← CoreFlat.CanonizeWire[root, [flatCell: target.flatCell^, wireRoot: public, wire: wire]];
otherWire ← NARROW [RefTab.Fetch[table, canonized].val];
SELECT TRUE FROM
otherWire=NIL => [] ← RefTab.Insert[table, canonized, wire];
otherWire=wire => [] ← RefTab.Insert[table, canonized, wire]; -- public is DAG
ENDCASE => { -- A different public maps to the same canonized wire
IF NOT failed THEN TerminalIO.PutF["*** DUT publics may not be connected during a capture simulation:\n"];
TerminalIO.PutF["*** %g connected to %g\n",
IO.rope[CoreOps.GetFullWireName[target.dut.public, wire]],
IO.rope[CoreOps.GetFullWireName[target.dut.public, otherWire]]];
failed ← TRUE;
};
};
table: RefTab.Ref ← RefTab.Create[equal: CoreFlat.FlatWireEqual, hash: CoreFlat.FlatWireHash];
failed ← FALSE;
CoreOps.VisitRootAtomics[target.dut.public, EachAtomicPublic];
};
CreateTarget: PUBLIC PROC [simulation: Rosemary.Simulation, flatCell: CoreFlat.FlatCellType, test: Ports.Port] RETURNS [target: Target] = {
VisitPort: Ports.EachWirePortPairProc = {
IF RefTab.Fetch[visitTable, port].found THEN RETURN;
IF NOT RefTab.Insert[visitTable, port, $Visited] THEN ERROR;
IF port.levelType=composite THEN RETURN;
subElements ← FALSE;
flatWire.wire ← wire;
FOR values: Rosemary.RoseValues ← Rosemary.GetValues[simulation, flatWire], values.rest UNTIL values=NIL DO
revValues ← CONS[values.first, revValues];
ENDLOOP;
max ← MAX[max, CoreOps.WireBits[wire]];
};
revValues: Rosemary.RoseValues ← NIL;
visitTable: RefTab.Ref ← RefTab.Create[];
max: INT ← 0;
flatWire: CoreFlat.FlatWire ← NEW[CoreFlat.FlatWireRec];
flatWire.flatCell ← flatCell^;
flatWire.wireRoot ← public;
target ← NEW [TargetRep ← [
flatCell: flatCell,
dut: CoreFlat.ResolveFlatCellType[simulation.cellType, flatCell^].cellType
]];
IF Ports.VisitBinding[target.dut.public, test, VisitPort] THEN ERROR;
FOR values: Rosemary.RoseValues ← revValues, values.rest UNTIL values=NIL DO
target.portRoseValues ← CONS[values.first, target.portRoseValues];
ENDLOOP;
target.scratchValue ← NEW[Ports.LevelSequenceRec[max]];
target.insideDrive ← NEW[Ports.DriveSequenceRec[max]];
target.outsideDrive ← NEW[Ports.DriveSequenceRec[max]];
target.compositeDrive ← NEW[Ports.DriveSequenceRec[max]];
target.mayBeTS ← NEW[Ports.BoolSequenceRec[max]];
IF CheckDUTConnections[simulation.cellType, target] THEN ERROR;
};
Inside: PROC [child: CoreFlat.FlatCellTypeRec, parent: CoreFlat.FlatCellTypeRec] RETURNS [BOOL] = {
IF child.path.length<parent.path.length THEN RETURN[FALSE];
FOR pathIndex: CoreFlat.InstancePathIndex IN [0..parent.path.length) DO
IF child.path.bits[pathIndex]#parent.path.bits[pathIndex] THEN RETURN [FALSE];
ENDLOOP;
RETURN [TRUE];
};
WireValue: TYPE ~ RECORD [lvl: Ports.Level, drv: Ports.Drive];
Boundary: TYPE ~ {inside, outside, both};
ComputeWireValue: PROC [wire: Rosemary.RoseWire, boundary: Boundary, target: Target, xIsOff: BOOL] RETURNS [val: WireValue] ~ {
Compute value of wire (use only specified boundary condition). Transistor with X gates are assumed on or off according to xIsOff.
IF wire.wireDrive=infinite THEN RETURN [val: [lvl: wire.wireLevel, drv: infinite]];
val ← [X, none];
First, compute contributions from ports writing into this wire
IF wire.currentValue#NIL THEN ERROR; -- Not yet supported (c.f. RecomputeValue)
IF wire.connections#NIL THEN FOR writers: CARDINAL IN [0..wire.connections.size) DO
writer: Rosemary.Field ← wire.connections[writers];
writerLevel: Ports.Level ← writer.currentValue[0];
writerDrive: Ports.Drive ← IF writer.currentDrive=NIL THEN writer.portBinding.currentDrive ELSE writer.currentDrive[0];
IF boundary#both THEN {
connectionInside: BOOLIF writer.portBinding.instance=NIL THEN FALSE ELSE Inside[writer.portBinding.instance.instance, target.flatCell^];
IF connectionInside # (boundary=inside) THEN LOOP;
};
SELECT TRUE FROM
writerDrive > val.drv => val ← [lvl: writerLevel, drv: writerDrive];
writerDrive = val.drv => IF val.lvl#writerLevel AND writerDrive>none THEN val.lvl ← X;
ENDCASE => NULL;
ENDLOOP;
Then, add-in transistor network contributions
wire.mark ← TRUE; -- to prevent re-exploring this wire
FOR t: CARDINAL IN [0..wire.validNotOffChannels) DO
tran: Rosemary.RoseTransistor ← wire.notOffChannels[t];
otherWire: Rosemary.RoseWire;
mod: WireValue; -- result of analysis for otherWire
IF xIsOff AND tran.type#nD AND tran.gate.wireLevel=X THEN LOOP; -- discard if off
SELECT TRUE FROM
tran.ch1=wire => otherWire ← tran.ch2;
tran.ch2=wire => otherWire ← tran.ch1;
ENDCASE => ERROR; -- wire is supposed to be on transistor channel
IF otherWire.mark THEN LOOP; -- already visiting this wire...
IF boundary#both THEN { -- Ignore transistor if not in correct boundary side
IF Inside[tran.instance, target.flatCell^] # (boundary=inside) THEN LOOP;
};
mod ← ComputeWireValue[otherWire, both, target, xIsOff]; -- deeper levels do not apply boundary
mod.drv ← MIN [mod.drv, tran.conductivity];
SELECT TRUE FROM
mod.drv > val.drv => val ← mod;
mod.drv = val.drv => IF mod.lvl#val.lvl AND mod.drv>none THEN val.lvl ← X;
ENDCASE => NULL;
ENDLOOP;
wire.mark ← FALSE;
};
SampleTarget: PUBLIC PROC [target: Target, test: Ports.Port] = {
VisitPort: PROC [port: Ports.Port] = {
IF RefTab.Fetch[visitTable, port].found THEN RETURN;
IF NOT RefTab.Insert[visitTable, port, $Visited] THEN ERROR;
IF port.levelType=composite THEN FOR i: NAT IN [0..port.size) DO
VisitPort[port[i]];
ENDLOOP
ELSE {
scratchValue: Ports.LevelSequence = target.scratchValue;
insideDrive: Ports.DriveSequence = target.insideDrive;
outsideDrive: Ports.DriveSequence = target.outsideDrive;
compositeDrive: Ports.DriveSequence = target.compositeDrive;
mayBeTS: Ports.BoolSequence = target.mayBeTS;
portBits: NATSELECT port.levelType FROM
l, b => 1,
ls => port.ls.size,
bs => port.bs.size,
c => 16-port.fieldStart,
lc => 32-port.fieldStart,
q => 64-port.fieldStart,
ENDCASE => ERROR;
firstFreeBit: NAT ← 0;
FOR i: NAT IN [0..insideDrive.size) DO
insideDrive[i] ← outsideDrive[i] ← none;
mayBeTS[i] ← FALSE;
ENDLOOP;
UNTIL firstFreeBit>=portBits DO
roseWire: Rosemary.RoseWire ← roseValues.first.roseWire;
connections: Rosemary.Fields ← roseWire.connections;
currentValue: Ports.LevelSequence ← roseWire.currentValue;
IF connections#NIL THEN FOR connectionIndex: NAT IN [0..connections.size) DO
portBinding: Rosemary.PortBinding ← connections[connectionIndex].portBinding;
connectionInside: BOOLIF portBinding.instance=NIL THEN FALSE ELSE Inside[portBinding.instance.instance, target.flatCell^];
IF portBinding.clientPort.driveType=aggregate THEN {
IF connectionInside THEN FOR i: NAT IN [0..roseValues.first.fieldWidth) DO
insideDrive[firstFreeBit+i] ← MAX [portBinding.clientPort.d, insideDrive[firstFreeBit+i]];
ENDLOOP
ELSE FOR i: NAT IN [0..roseValues.first.fieldWidth) DO
outsideDrive[firstFreeBit+i] ← MAX [portBinding.clientPort.d, outsideDrive[firstFreeBit+i]];
ENDLOOP;
}
ELSE {
FOR fieldIndex: NAT IN [0..portBinding.fields.size) DO
field: Rosemary.Field ← portBinding.fields[fieldIndex];
IF field.roseWire=roseWire THEN {
firstBit: NAT ← roseValues.first.fieldStart;
FOR bit: NAT IN [0..roseValues.first.fieldWidth) DO
IF connectionInside THEN insideDrive[firstFreeBit+bit] ← MAX [field.currentDrive[firstBit+bit], insideDrive[firstFreeBit+bit]]
ELSE outsideDrive[firstFreeBit+bit] ← MAX [field.currentDrive[firstBit+bit], outsideDrive[firstFreeBit+bit]];
ENDLOOP;
};
ENDLOOP;
};
ENDLOOP;
IF roseWire.channels#NIL THEN { -- atomic wire with transistors
insideOff, insideOn, outsideOff, outsideOn: WireValue;
insideOff ← ComputeWireValue[roseWire, inside, target, TRUE];
insideOn ← ComputeWireValue[roseWire, inside, target, FALSE];
outsideOff ← ComputeWireValue[roseWire, outside, target, TRUE];
outsideOn ← ComputeWireValue[roseWire, outside, target, FALSE];
SELECT TRUE FROM
outsideOff#outsideOn => { -- Outside is unreasonable ...
IF roseWire.wireLevel=X THEN { -- give up
insideDrive[firstFreeBit] ← inspect; -- means completely unknown
outsideDrive[firstFreeBit] ← outsideOn.drv;
}
ELSE ERROR; -- How to handle such a crazy situation ???
};
insideOff#insideOn => { -- outside is reasonable, not inside
IF roseWire.wireLevel=X THEN { -- give up
insideDrive[firstFreeBit] ← inspect; -- means completely unknown
outsideDrive[firstFreeBit] ← outsideOn.drv;
}
ELSE { -- fake it so that the tester drives & checks the observed value
insideDrive[firstFreeBit] ← none;
outsideDrive[firstFreeBit] ← drive;
};
};
ENDCASE => { -- fully-known state
IF insideOff.drv=none THEN mayBeTS[firstFreeBit] ← TRUE;
insideDrive[firstFreeBit] ← insideOff.drv;
outsideDrive[firstFreeBit] ← outsideOff.drv;
};
};
IF currentValue=NIL THEN {
IF roseWire.wireDrive=infinite THEN { -- power is special, fake it...
insideDrive[firstFreeBit] ← infinite; -- this is to force an expect situation...
outsideDrive[firstFreeBit] ← none;
};
scratchValue[firstFreeBit] ← roseWire.wireLevel;
firstFreeBit ← firstFreeBit + 1;
}
ELSE {
firstBit: NAT ← roseValues.first.fieldStart;
FOR bit: NAT IN [0..roseValues.first.fieldWidth) DO
scratchValue[firstFreeBit + bit] ← currentValue[firstBit+bit];
ENDLOOP;
firstFreeBit ← firstFreeBit + roseValues.first.fieldWidth;
};
roseValues ← roseValues.rest;
ENDLOOP;
IF NOT firstFreeBit=portBits THEN ERROR;
FOR i: NAT IN [0..portBits) DO
in: Ports.Drive = insideDrive[i];
out: Ports.Drive = outsideDrive[i];
SELECT TRUE FROM
in=inspect => compositeDrive[i] ← none;
in=none AND out=none => compositeDrive[i] ← inspect;
in=none AND out>none => compositeDrive[i] ← IF mayBeTS[i] THEN force ELSE drive;
in=none AND out>none => compositeDrive[i] ← drive;
in>=out AND out>=none => compositeDrive[i] ← expect;
out>in => compositeDrive[i] ← drive;
ENDCASE => ERROR; -- unsupported case
ENDLOOP;
IF port.driveType=aggregate THEN {
port.d ← compositeDrive[0];
FOR i: NAT IN [0..portBits) DO
IF port.d#compositeDrive[i] THEN ERROR; -- different drive values for the aggregate
ENDLOOP;
}
ELSE FOR i: NAT IN [0..port.ds.size) DO port.ds[i] ← compositeDrive[i] ENDLOOP;
SELECT port.levelType FROM
l => port.l ← scratchValue[0];
ls => FOR i: NAT IN [0..port.ls.size) DO
port.ls[i] ← scratchValue[i];
ENDLOOP;
b => port.b ← Ports.ToBool[scratchValue[0]];
bs => FOR i: NAT IN [0..port.bs.size) DO
port.bs[i] ← Ports.ToBool[scratchValue[i]];
ENDLOOP;
c => port.c ← Ports.LSToC[scratchValue];
lc => port.lc ← Ports.LSToLC[scratchValue];
q => port.q ← Ports.LSToQ[scratchValue];
ENDCASE => ERROR;
};
};
roseValues: Rosemary.RoseValues ← target.portRoseValues;
visitTable: RefTab.Ref ← RefTab.Create[];
VisitPort[test];
};
END.
Jean-Marc Frailong September 13, 1988 6:24:11 pm PDT
Corrected bug in computation of path to power: did not prevent reexploration of level 0
changes to: PathToPower (PT001)
Jean-Marc Frailong September 14, 1988 2:35:21 pm PDT
Corrected drive computation (was using aggregate case always)
changes to: TraverseGraph (local of PathToPower) changed clientPort.d to drv
Jean-Marc Frailong September 14, 1988 6:28:28 pm PDT
Corrected drive computation (must ignore transistors with X gates)
changes to: TraverseGraph (local of PathToPower), VisitPort (local of SampleTarget)
Jean-Marc Frailong September 23, 1988 6:48:45 pm PDT
Added check that public of DUT does not have any wire connected together
changes to: CheckDUTConnections New function, CreateTarget Call CheckDUTConnections