MTSVectorImpl.mesa
Copyright Ó 1987 by Xerox Corporation. All rights reserved.
Jean-Marc Frailong October 11, 1988 6:35:19 pm PDT
Don Curry September 6, 1988 5:53:51 pm PDT
Test vector generation for the PC-based MTS tester
DIRECTORY
Basics, BasicTime, Core, CoreFlat, CoreOps, CoreProperties, FS, IO, Ports, RefTab, RefText, Rosemary, RosemaryUser, RosemaryVector, Rope, SymTab, TerminalIO, TiogaFileOps, TiogaStreams, MTSVector;
MTSVectorImpl: CEDAR PROGRAM
IMPORTS Basics, BasicTime, CoreFlat, CoreOps, CoreProperties, FS, IO, Ports, RefTab, RefText, Rosemary, RosemaryUser, RosemaryVector, Rope, SymTab, TerminalIO, TiogaFileOps, TiogaStreams
EXPORTS MTSVector
SHARES MTSVector
~ BEGIN OPEN MTSVector;
Types & constants
bitMask: ARRAY [0..8) OF BYTE;
vectorBlockLength: NAT ← 16384; -- number of bytes in a block of test vectors (end up with 0's)
testFileHeaderSize: CARD16 ← 2048; -- minimum number of bytes in file header
headerSignature: Rope.ROPE ← "MTSX003"; -- signature in header
Meaning of the codes:
Code Resp Mask Data Function
0 1 1 0 Issue 0 & check it
L 0 1 0 Check 0 emitted by DUT
1 1 1 1 Issue 1 & check it
H 0 1 1 Check 1 emitted by DUT
T 1 0 1 Issue 1, don't check
F 1 0 0 Issue 0, don't check
? 0 0 1 data 1, don't drive or sense
X 0 0 0 data 0, don't drive or sense
Pin management
MapPublicToPackage: PROC [public: Core.WireSeq, package: Package] RETURNS [wireToPins: RefTab.Ref] ~ {
Build the wireToPins field of a file from dut.public & package
Fail: PROC [name, explanation: Rope.ROPE] ~ {
failed ← TRUE;
TerminalIO.PutF["*** Wire %g : %g.\n", IO.rope[name], IO.rope[explanation]];
};
CheckDefined: PROC [wire: Core.Wire] ~ {
IF NOT RefTab.Fetch[wireToPins, wire].found THEN {
Fail[CoreOps.GetFullWireName[public, wire], "not described in pins file"];
[] ← RefTab.Insert[wireToPins, wire, NIL]; -- to avoid double messages
};
};
Define: SymTab.EachPairAction ~ {
wd: WireDescription ← NARROW [val];
wire: Core.Wire = CoreOps.FindWire[public, key];
SELECT TRUE FROM
wire=NIL => Fail[key, "not found in public"];
wire.size#0 => Fail[key, "not atomic"];
RefTab.Insert[wireToPins, wire, wd] => NULL; -- all OK
ENDCASE => Fail[key, "wire defined twice"];
};
failed: BOOLFALSE;
wireToPins ← RefTab.Create[];
[] ← SymTab.Pairs[package.signals, Define]; -- lookup all signals in the public
CoreOps.VisitRootAtomics[public, CheckDefined];
IF failed THEN ERROR; -- DUT pins do not match package pins
};
Physical file access details for write
PutHeader: PROC [file: File] ~ {
Write header. The file pointer is moved to the end of the header. The first call to WriteHeader is a dummy in order to ascertain the header size and fill the file up to the start point of the first vector block. The real header is written only at closing time (this simplifies a lot of things). File position on entry is ignored, and is set to start pont of 1st vector block on exit (always a multiple of 1K for efficiency on PC).
Header format is known by PC software and should not be changed.
PutByte: PROC [b: BYTE] ~ {
Write a byte into file and then update file checksum (not buffered).
IO.PutByte[file.stream, b];
file.ckSum ← Basics.BITAND[file.ckSum+b, 000FFH];
};
PutCard16: PROC [c: CARD16] ~ {
Write a 16-bit integer in little-endian order (LSB first)
PutByte[Basics.LowByte[c]];
PutByte[Basics.HighByte[c]];
};
PutInt32: PROC [i: INT32] ~ {
Write a 32-bit integer in little-endian order (LSB first)
n: Basics.LongNumber;
n.li ← i;
PutByte[n.ll];
PutByte[n.lh];
PutByte[n.hl];
PutByte[n.hh];
};
PutCard: PROC [c: CARD32] ~ {
Write a 32-bit integer in little-endian order (LSB first)
n: Basics.LongNumber;
n.lc ← c;
PutByte[n.ll];
PutByte[n.lh];
PutByte[n.hl];
PutByte[n.hh];
};
PutCRope: PROC [r: Rope.ROPE] ~ {
Write the rope C-style (NULL terminated).
EachChar: Rope.ActionType ~ { PutByte[ORD[c]] };
[] ← Rope.Map[base: r, action: EachChar];
PutByte[00H];
};
PutRope: PROC [r: Rope.ROPE, length: INT] ~ {
Write the rope extended to length characters with NULLs (or truncated if too long)
EachChar: Rope.ActionType ~ { PutByte[ORD[c]] };
[] ← Rope.Map[base: r, action: EachChar, start: 0, len: MIN[length, Rope.Length[r]]];
THROUGH [Rope.Length[r] .. length) DO PutByte[00H] ENDLOOP;
};
PutWireDescription: SymTab.EachPairAction ~ {
wd: WireDescription = NARROW [val];
PutByte[ORD[wd.flavor]]; -- will be 0FFH for termination flag
PutCRope[wd.name];
FOR pins: LIST OF PinDescription ← wd.pins, pins.rest UNTIL pins=NIL DO
pin: PinDescription = pins.first;
PutByte[pin.group]; -- will be 0xFF for terminator
PutByte[pin.byte];
PutByte[pin.mask];
PutByte[pin.bit];
PutCRope[pin.pkgName];
PutCRope[pin.conName];
ENDLOOP;
PutByte[0FFH]; -- end of pin descriptions list
};
ckSumOffset: INT; -- this is where the checksum will go once completed...
ckSumByte: BYTE;
IO.SetIndex[file.stream, 0]; -- backup to start of file
PutRope[headerSignature, 8];
PutInt32[file.hdrSize]; -- garbage on 1st pass...
PutCard16[file.nGroups];
PutCard16[vectorBlockLength];
PutInt32[file.nVects];
PutInt32[LOOPHOLE[file.timeStamp]];
ckSumOffset ← IO.GetIndex[file.stream]; -- note down where checksum should go finally
PutByte[00H]; -- this is the checksum position, patched at end
PutRope[file.id, 120]; -- comment for display on the PC
PutCRope[file.package.name]; -- start of package/fixture description
PutCRope[file.package.fixtureName];
[] ← SymTab.Pairs[file.package.signals, PutWireDescription];
PutByte[0FFH]; -- end of wire descriptions list
THROUGH [(IO.GetIndex[file.stream] MOD 1024) .. 1024) DO PutByte[00H] ENDLOOP; -- add adequate padding
file.hdrSize ← IO.GetIndex[file.stream];
IF ( file.hdrSize MOD 1024 ) # 0 THEN ERROR; -- internal bug
ckSumByte ← Basics.BITAND[0100H-file.ckSum, 000FFH]; -- recompute checksum for total 0
IO.SetIndex[file.stream, ckSumOffset]; -- get back to where the checksum should be
IO.PutByte[file.stream, ckSumByte]; -- and write it
IO.SetIndex[file.stream, file.hdrSize]; -- get back to end of header
};
WriteBByte: PROC [file: File, b: BYTE] ~ {
Write byte into file buffer and update file checksum (physically written only on flush).
block: REF TEXT ← file.block;
IF block.length>=block.maxLength THEN ERROR; -- buffer overflowed
block[block.length] ← VAL[b];
block.length ← block.length+1;
file.ckSum ← Basics.BITAND[file.ckSum+b, 000FFH];
};
FlushBlock: PROC [file: File, nBytes: NAT] ~ {
Flush current block padded to nBytes with zeroes.
block: REF TEXT ← file.block;
IF block.length>nBytes THEN ERROR; -- internal bug
UNTIL block.length=nBytes DO WriteBByte[file, 00H] ENDLOOP;
IO.PutBlock[file.stream, block, 0, nBytes];
block.length ← 0;
};
Physical file access details for read
GetHeader: PROC [file: File] ~ {
Read the header in and fill in adequate fields of the File structure
Header format is known by PC software and should not be changed.
GetByte: PROC [] RETURNS [b: BYTE] ~ {
Read a byte and compute checksum
b ← IO.GetByte[file.stream];
file.ckSum ← Basics.BITAND[file.ckSum+b, 000FFH];
};
GetInt32: PROC [] RETURNS [i: INT32] ~ {
Read a 32-bit integer in little-endian order (LSB first)
n: Basics.LongNumber;
n.ll ← GetByte[];
n.lh ← GetByte[];
n.hl ← GetByte[];
n.hh ← GetByte[];
i ← n.li;
};
GetCard16: PROC [] RETURNS [c: CARD16] ~ {
Read a 16-bit integer in little-endian order (LSB first)
n: Basics.ShortNumber;
n.lo ← GetByte[];
n.hi ← GetByte[];
c ← n.sc;
};
GetRope: PROC [length: NAT] RETURNS [r: Rope.ROPE] ~ {
Read a NULL-padded rope of the required length. Strip padding on return.
buf: REF TEXT ← RefText.ObtainScratch[length];
lastNonPad: INT ← -1;
FOR i: NAT IN [0..length) DO
b: BYTE = GetByte[];
buf ← RefText.AppendChar[buf, VAL[b]];
IF b#00H THEN lastNonPad ← i;
ENDLOOP;
r ← Rope.FromRefText[buf, 0, lastNonPad+1];
RefText.ReleaseScratch[buf];
};
GetCRope: PROC [] RETURNS [r: Rope.ROPE] ~ {
Read a NULL-terminated rope. Strip padding on return.
buf: REF TEXT ← RefText.ObtainScratch[256]; -- quite reasonable length
DO
b: BYTE = GetByte[];
IF b=00H THEN EXIT; -- rope terminator found
buf ← RefText.AppendChar[buf, VAL[b]];
ENDLOOP;
r ← Rope.FromRefText[buf];
RefText.ReleaseScratch[buf];
};
signature: Rope.ROPE;
blockLength: NAT;
file.ckSum ← 0;
signature ← GetRope[8];
IF NOT Rope.Equal[signature, headerSignature] THEN ERROR; -- invalid file format
file.hdrSize ← GetInt32[];
file.nGroups ← GetCard16[];
blockLength ← GetCard16[];
IF blockLength#vectorBlockLength THEN ERROR; -- invalid file format
file.nVects ← GetInt32[];
file.timeStamp ← LOOPHOLE[GetInt32[]];
[] ← GetByte[]; -- checksum
file.id ← GetRope[120];
file.package ← NEW [PackageRep ← [signals: SymTab.Create[]]];
file.package.name ← GetCRope[];
file.package.fixtureName ← GetCRope[];
DO -- loop until wire description list exhausted
b: BYTE ← GetByte[];
wd: WireDescription ← NEW [WireDescriptionRep];
IF b=0FFH THEN EXIT; -- end of wire description list
wd.flavor ← VAL [b];
wd.name ← GetCRope[];
DO -- loop until pin description list for current wire exhausted
pd: PinDescription;
b ← GetByte[];
IF b=0FFH THEN EXIT; -- end of pin description list
pd ← NEW [PinDescriptionRep];
pd.group ← b;
pd.byte ← GetByte[];
pd.mask ← GetByte[];
pd.bit ← GetByte[];
pd.pkgName ← GetCRope[];
pd.conName ← GetCRope[];
wd.pins ← CONS [pd, wd.pins]; -- append (well, prepend) to list
ENDLOOP;
IF NOT SymTab.Insert[file.package.signals, wd.name, wd] THEN ERROR; -- incorrect file
ENDLOOP;
THROUGH [IO.GetIndex[file.stream] .. file.hdrSize) DO [] ← GetByte[] ENDLOOP;
IF IO.GetIndex[file.stream]#file.hdrSize THEN ERROR; -- incorrect file
UNTIL IO.EndOf[file.stream] DO [] ← GetByte[] ENDLOOP; -- to compute checksum
IF Basics.BITAND[file.ckSum, 000FFH]#0 THEN ERROR; -- invalid checksum
IO.SetIndex[file.stream, file.hdrSize]; -- back to the first vector
};
Low-level client access to MTS files
Create: PUBLIC PROC [ct: Core.CellType, package: Rope.ROPE, vectors: Rope.ROPE, title: Rope.ROPENIL] RETURNS [file: File] ~ {
Create the handle and make it ready to accept vectors.
Vectors are written into fileBase.xtv, pin information is read from fileBase.mtsPins.
HighestBoard: SymTab.EachPairAction ~ {
Compute the highest used board number for signals that are driven by the tester during simulation (i.e. exclude power and special pins)
wd: WireDescription = NARROW [val];
SELECT wd.flavor FROM
signal, pullup => {
FOR pins: LIST OF PinDescription ← wd.pins, pins.rest UNTIL pins=NIL DO
file.nGroups ← MAX [pins.first.group, file.nGroups];
ENDLOOP
};
gnd, vdd, ignore => NULL;
ENDCASE => ERROR; -- incorrect value ...
};
file ← NEW [FileRep ←
[dut: ct, nGroups: -1, ckSum: 0, nVects: 0, timeStamp: BasicTime.Now[]]];
file.package ← ReadPackage[MakeFileName[package, "mtsPins", FALSE]];
file.wireToPins ← MapPublicToPackage[ct.public, file.package];
[] ← SymTab.Pairs[file.package.signals, HighestBoard];
IF file.nGroups<0 THEN ERROR; -- incorrect pin file
file.nGroups ← file.nGroups + 1; -- was index of highest board, not number of boards
IF Rope.IsEmpty[title] THEN title ← IO.PutFR["%g on %g", IO.rope[CoreOps.GetCellTypeName[ct]], IO.rope[file.package.name]];
file.id ← IO.PutFR["%g -- %g", IO.rope[title], IO.time[]];
file.block ← RefText.New[vectorBlockLength];
file.vector ← NEW [VectorRep[file.nGroups]];
FOR i: NAT IN [0..file.vector.ngrps) DO file.vector[i] ← NEW [CGRep] ENDLOOP;
file.stream ← FS.StreamOpen[MakeFileName[vectors, "xtv", TRUE], create];
TerminalIO.PutF["MTS vectors saved on %g\n", IO.rope[FS.GetName[FS.OpenFileFromStream[file.stream]].fullFName]];
PutHeader[file];
file.ckSum ← 0; -- ignore previous chksum, header will be rewritten ...
};
WriteVector: PUBLIC PROC [file: File] ~ {
Write file.vector at the end of the file
bytesPerVector: NAT = 24*file.nGroups;
file.nVects ← file.nVects+1;
IF file.block.length+bytesPerVector>vectorBlockLength THEN FlushBlock[file, vectorBlockLength];
FOR g: NAT IN [0..file.vector.ngrps) DO
group: CG = file.vector[g];
FOR i: NAT IN [0..8) DO WriteBByte[file, group.tristate.bytes[i]] ENDLOOP;
FOR i: NAT IN [0..8) DO WriteBByte[file, group.data.bytes[i]] ENDLOOP;
FOR i: NAT IN [0..8) DO WriteBByte[file, group.mask.bytes[i]] ENDLOOP;
ENDLOOP;
};
WriteVectorFromPorts: PUBLIC PROC [file: File, before: Ports.Port, after: Ports.Port] ~ {
Convert a pair of Rosemary ports into an MTS vector and write it into file.
Note that this version does NOT support force...
Record: PROC [wire: Core.Wire, levelBefore: Ports.Level, driveBefore: Ports.Drive, levelAfter: Ports.Level, driveAfter: Ports.Drive, mode: REF ANY] ~ {
Record the right information into the test vector
wd: WireDescription ← NARROW [RefTab.Fetch[wireToPins, wire].val];
source, data, mask: BOOL; -- the 3 bits defining a test vector "bit"
useEarlyDrive: BOOLTRUE; -- If driven before, use that value
IF wd.flavor=ignore THEN RETURN; -- pin not connected to tester, all default to FALSE
SELECT mode FROM
NIL => NULL;
$After => useEarlyDrive ← FALSE;
ENDCASE => ERROR; -- Unknown value of MTSOption property
IF useEarlyDrive AND driveBefore=drive THEN {
SELECT levelBefore FROM
H => {source ← TRUE; data ← TRUE; mask ← TRUE}; -- 1: drive and expect a 1
L => {source ← TRUE; data ← FALSE; mask ← TRUE}; -- 0: drive and expect a 0
X => {source ← FALSE; data ← FALSE; mask ← FALSE}; -- X: don't drive, ignore result
ENDCASE => ERROR; -- cannot happen
}
ELSE { -- driveBefore # drive
SELECT driveAfter FROM
expect => SELECT levelAfter FROM
H => {source ← FALSE; data ← TRUE; mask ← TRUE}; -- H: don't drive, expect 1
L => {source ← FALSE; data ← FALSE; mask ← TRUE}; -- L: don't drive, expect 0
X => {source ← FALSE; data ← FALSE; mask ← FALSE}; -- X: don't drive, ignore result
ENDCASE => ERROR; -- cannot happen
inspect => {
IF wd.flavor=pullup THEN SELECT levelAfter FROM
H => {source ← FALSE; data ← TRUE; mask ← TRUE}; -- H: don't drive, expect a 1
L => {source ← TRUE; data ← FALSE; mask ← TRUE}; -- 0: drive and expect a 0
X => {source ← FALSE; data ← TRUE; mask ← TRUE}; -- H: don't drive, expect a 1
ENDCASE => ERROR -- cannot happen
ELSE SELECT levelAfter FROM
H => {source ← TRUE; data ← TRUE; mask ← FALSE}; -- T: drive 1 and check TS
L => {source ← TRUE; data ← FALSE; mask ← FALSE}; -- F: drive 0 and check TS
X => {source ← TRUE; data ← FALSE; mask ← FALSE}; -- F: drive 0 and check TS
ENDCASE => ERROR; -- cannot happen
};
none => {source ← FALSE; data ← FALSE; mask ← FALSE}; -- X: don't drive, ignore result
drive => {
IF useEarlyDrive THEN { -- didn't drive before, does after ???
source ← FALSE; data ← FALSE; mask ← FALSE; -- X: don't drive, ignore result
}
ELSE { -- special wire: always use the late drive value ...
SELECT levelAfter FROM
H => {source ← TRUE; data ← TRUE; mask ← TRUE}; -- 1: drive and expect a 1
L => {source ← TRUE; data ← FALSE; mask ← TRUE}; -- 0: drive and expect a 0
X => {source ← FALSE; data ← FALSE; mask ← FALSE}; -- X: don't drive, ignore result
ENDCASE => ERROR; -- cannot happen
};
};
ENDCASE => ERROR; -- Incorrect capture
};
IF wd.flavor=vdd AND NOT ((NOT source) AND data AND mask) THEN ERROR; -- bug
IF wd.flavor=gnd AND NOT ((NOT source) AND (NOT data) AND mask) THEN ERROR; -- bug
FOR pins: LIST OF PinDescription ← wd.pins, pins.rest UNTIL pins=NIL DO
pin: PinDescription = pins.first;
group: CG ← vector[pin.group];
group.tristate.bits[pin.bit] ← source;
group.data.bits[pin.bit] ← data;
group.mask.bits[pin.bit] ← mask;
ENDLOOP;
};
VisitDUT: PROC [wire: Core.Wire, before: Ports.Port, after: Ports.Port] ~ {
mode: REF ANY ← CoreProperties.GetWireProp[wire, $MTSOption];
IF before.levelType#after.levelType THEN ERROR;
IF before.driveType#after.driveType THEN ERROR;
SELECT before.levelType FROM
l => Record[wire, before.l, before.d, after.l, after.d, mode];
b => Record[wire, IF before.b THEN H ELSE L, before.d, IF after.b THEN H ELSE L, after.d, mode];
ls => {
IF before.driveType=separate THEN FOR i: NAT IN [0..wire.size) DO
Record[wire[i], before.ls[i], before.ds[i], after.ls[i], after.ds[i], mode];
ENDLOOP
ELSE FOR i: NAT IN [0..wire.size) DO
Record[wire[i], before.ls[i], before.d, after.ls[i], after.d, mode];
ENDLOOP
};
bs => {
IF before.driveType=separate THEN FOR i: NAT IN [0..wire.size) DO
Record[wire[i], IF before.bs[i] THEN H ELSE L, before.ds[i], IF after.bs[i] THEN H ELSE L, after.ds[i], mode];
ENDLOOP
ELSE FOR i: NAT IN [0..wire.size) DO
Record[wire[i], IF before.bs[i] THEN H ELSE L, before.d, IF after.bs[i] THEN H ELSE L, after.d, mode];
ENDLOOP
};
c => ERROR; -- not yet supported
lc => ERROR; -- not yet supported
q => ERROR; -- not yet supported
composite => { -- Explore sub-wires
FOR i: NAT IN [0..wire.size) DO
VisitDUT[wire[i], before[i], after[i]];
ENDLOOP;
};
ENDCASE => ERROR; -- unexpected value
};
vector: Vector ← file.vector;
wireToPins: RefTab.Ref ← file.wireToPins;
FOR i: NAT IN [0..vector.ngrps) DO -- force uninitialized bits to Ignore
vector[i].tristate.bits ← ALL [FALSE];
vector[i].data.bits ← ALL [FALSE];
vector[i].mask.bits ← ALL [FALSE];
ENDLOOP;
[] ← VisitDUT[file.dut.public, before, after]; -- compute vector
WriteVector[file]; -- and add it to file
};
Close: PUBLIC PROC [file: File] ~ {
Flush out current block if any, write file header & close the file stream.
sum: CARD16 ← 0;
IF file.block.length#0 THEN FlushBlock[file, vectorBlockLength];
TerminalIO.PutF["%g vectors captured during simulation\n", IO.int[file.nVects]];
PutHeader[file];
IO.SetIndex[file.stream, 0]; -- backtrack to start of file
UNTIL IO.EndOf[file.stream] DO -- check file checksum
byte: BYTEIO.GetByte[file.stream];
sum ← Basics.BITAND[sum+byte, 0FFH];
ENDLOOP;
IF sum#0 THEN ERROR; -- implementation bug: file did not checksum properly !!!
IO.Close[file.stream];
};
Read: PUBLIC PROC [ct: Core.CellType, vectors: Rope.ROPE, eachVector: PROC [File]] ~ {
Call back user for each vector that's read in
GetByte: PROC [] RETURNS [b: BYTE] ~ {
b ← ORD [RefText.Fetch[block, blockOffset]];
blockOffset ← blockOffset+1;
};
bytesPerVector: NAT;
blockOffset: NAT ← vectorBlockLength;
block: REF TEXT ← RefText.New[vectorBlockLength];
file: File ← NEW [FileRep ← [dut: ct, block: block, ckSum: 0, timeStamp: BasicTime.nullGMT]];
file.stream ← FS.StreamOpen[MakeFileName[vectors, "xtv", FALSE], read];
GetHeader[file];
IF ct#NIL THEN
file.wireToPins ← MapPublicToPackage[ct.public, file.package]; -- ct=NIL for ASCII
file.vector ← NEW [VectorRep[file.nGroups]];
FOR i: NAT IN [0..file.vector.ngrps) DO file.vector[i] ← NEW [CGRep] ENDLOOP;
bytesPerVector ← 24*file.nGroups;
TerminalIO.PutF["MTS vectors read from %g (%g vectors).\n", IO.rope[FS.GetName[FS.OpenFileFromStream[file.stream]].fullFName], IO.int[file.nVects]];
THROUGH [0..file.nVects) DO
IF blockOffset+bytesPerVector>vectorBlockLength THEN {
nBytesRead: NATIO.GetBlock[file.stream, block, 0, vectorBlockLength];
-- Note that there is no need to verify the checksum since GetHeader did it...
IF nBytesRead#vectorBlockLength THEN ERROR; -- last block truncated
blockOffset ← 0;
};
FOR g: NAT IN [0..file.vector.ngrps) DO
group: CG = file.vector[g];
FOR i: NAT IN [0..8) DO group.tristate.bytes[i] ← GetByte[] ENDLOOP;
FOR i: NAT IN [0..8) DO group.data.bytes[i] ← GetByte[] ENDLOOP;
FOR i: NAT IN [0..8) DO group.mask.bytes[i] ← GetByte[] ENDLOOP;
ENDLOOP;
eachVector[file]; -- call back user
ENDLOOP;
IF NOT IO.EndOf[file.stream] THEN ERROR; -- what is trailing behind the vectors ???
IO.Close[file.stream];
};
High-level client access to MTS files
CreateCapture: PUBLIC PROC [ct: Core.CellType] RETURNS [capture: Capture] ~ {
Build the capture structure from CT properties, NIL means no capture.
Properties are:
- On top level cell:
DUT: <rope> -- CoreFlat path to the device under test
Title: <rope> -- title of the generated MTS file
CutSet: <CoreFlat.CutSet> (c.f. RosemaryUser)
Tests: LIST ["MTSCapture"] (seen by RosemaryUser, but required for this procedure...)
- On the DUT:
Package: <rope> -- name of the file describing the package, and hence the fixture.
Vectors: <rope> -- name of the file where vectors will be stored.
Failed: PROC [msg: Rope.ROPE] RETURNS [nil: Capture ← NIL] ~ {
TerminalIO.PutF["*** %g, vectors will not be captured.\n", IO.rope[msg]];
};
targetRope: Rope.ROPE = NARROW [CoreProperties.GetCellTypeProp[ct, $DUT]];
vectors, package, title: Rope.ROPE;
handle: RosemaryUser.RoseDisplay = RosemaryUser.RoseDisplayFor[ct];
targetFlatCT: CoreFlat.FlatCellType;
targetCT: Core.CellType;
capture ← NEW [CaptureRep];
IF handle=NIL THEN RETURN[Failed["Unable to recover Rosemary display"]];
IF Rope.IsEmpty[targetRope] THEN RETURN[Failed["Missing DUT description"]];
targetFlatCT ← NEW [CoreFlat.FlatCellTypeRec ← CoreFlat.ParseCellTypePath[ct, targetRope, handle.cutSet]];
IF CoreFlat.BelowCutSet[ct, targetFlatCT^, handle.cutSet] THEN RETURN[Failed["DUT is below simulation cutset"]];
targetCT ← CoreFlat.ResolveFlatCellType[ct, targetFlatCT^].cellType;
vectors ← NARROW [CoreProperties.GetCellTypeProp[targetCT, $Vectors]];
package ← NARROW [CoreProperties.GetCellTypeProp[targetCT, $Package]];
title ← NARROW [CoreProperties.GetCellTypeProp[targetCT, $Title]];
IF vectors=NIL THEN vectors ← package;
IF package=NIL THEN package ← vectors;
IF Rope.IsEmpty[vectors] THEN RETURN[Failed["Unable to find name for vector file"]];
IF Rope.IsEmpty[package] THEN RETURN[Failed["Unable to find name for package file"]];
capture.before ← Ports.CreatePort[targetCT, TRUE];
capture.after ← Ports.CreatePort[targetCT, TRUE];
capture.target ← RosemaryVector.CreateTarget[handle.simulation, targetFlatCT, capture.before];
capture.mtsFile ← Create[targetCT, package, vectors, title];
};
EvalAndCapture: PUBLIC PROC [capture: Capture, Eval: RosemaryUser.TestEvalProc, memory: BOOLTRUE, useClockEval: BOOLTRUE, checkPorts: BOOLTRUE] ~ {
Capture state before Eval, then do eval once or twice (for clockEval hack), then capture state again. Derive vector from before & after state, then write it.
IF useClockEval THEN {
Eval[memory: memory, clockEval: TRUE, checkPorts: FALSE];
IF capture#NIL THEN RosemaryVector.SampleTarget[capture.target, capture.before];
Eval[memory: memory, clockEval: FALSE, checkPorts: checkPorts];
IF capture#NIL THEN {
RosemaryVector.SampleTarget[capture.target, capture.after];
WriteVectorFromPorts[capture.mtsFile, capture.before, capture.after];
};
}
ELSE {
Eval[memory: memory, checkPorts: checkPorts];
IF capture#NIL THEN {
RosemaryVector.SampleTarget[capture.target, capture.after];
WriteVectorFromPorts[capture.mtsFile, capture.after, capture.after];
};
};
};
CloseCapture: PUBLIC PROC [capture: Capture] ~ {
IF capture#NIL THEN Close[capture.mtsFile];
};
ReadPort: PUBLIC PROC [ct: Core.CellType, p: Ports.Port, eachVector: PROC[]] ~ {
ConvertVectorToPort: PROC [file: File] ~ {
Setup: PROC [wire: Core.Wire] RETURNS [lvl: Ports.Level, drv: Ports.Drive] ~ {
Setup the right information into the port
wd: WireDescription = NARROW [RefTab.Fetch[file.wireToPins, wire].val];
IF wd.flavor=ignore THEN {
lvl ← L; drv ← inspect; -- ignore port contents, supplied externally
}
ELSE {
group: CG = file.vector[wd.pins.first.group];
bit: [0..60) = wd.pins.first.bit;
source, data, mask: BOOL; -- the 3 bits defining a test vector "bit"
source ← group.tristate.bits[bit];
data ← group.data.bits[bit];
mask ← group.mask.bits[bit];
IF wd.flavor=pullup THEN { -- simulate the presence of an external pull-up
IF source THEN
IF data THEN
IF mask THEN {lvl ← H; drv ← drive} -- 1
ELSE {lvl ← H; drv ← driveWeak} -- T
ELSE
IF mask THEN {lvl ← L; drv ← drive} -- 0
ELSE {lvl ← L; drv ← force} -- F, should not happen with pullup
ELSE
IF data THEN
IF mask THEN {lvl ← H; drv ← driveWeak} -- H
ELSE {lvl ← H; drv ← driveWeak} -- ?
ELSE
IF mask THEN {lvl ← L; drv ← expect} -- L
ELSE {lvl ← H; drv ← driveWeak} -- X
}
ELSE {
IF source THEN
IF data THEN
IF mask THEN {lvl ← H; drv ← drive} -- 1
ELSE {lvl ← H; drv ← force} -- T
ELSE
IF mask THEN {lvl ← L; drv ← drive} -- 0
ELSE {lvl ← L; drv ← force} -- F
ELSE
IF data THEN
IF mask THEN {lvl ← H; drv ← expect} -- H
ELSE {lvl ← X; drv ← none} -- ?
ELSE
IF mask THEN {lvl ← L; drv ← expect} -- L
ELSE {lvl ← X; drv ← none} -- X
ELSE {lvl ← X; drv ← drive} -- X
}
};
};
EachWirePortPair: Ports.EachWirePortPairProc ~ {
[wire: Core.Wire, port: Port] RETURNS [subElements: BOOLTRUE, quit: BOOLFALSE]
subElements ← FALSE; -- presume that we won't explore children
SELECT port.levelType FROM
l => [port.l, port.d] ← Setup[wire];
b => {
[port.l, port.d] ← Setup[wire];
port.b ← SELECT port.l FROM H => TRUE, L => FALSE, ENDCASE => ERROR;
};
ls => {
drv: Ports.Drive;
IF port.driveType=separate THEN FOR i: NAT IN [0..wire.size) DO
[port.ls[i], port.ds[i]] ← Setup[wire[i]];
ENDLOOP
ELSE FOR i: NAT IN [0..wire.size) DO
[port.ls[i], drv] ← Setup[wire[i]];
IF i=0 THEN port.d ← drv
ELSE IF port.d#drv THEN ERROR; -- should be separate drives ...
ENDLOOP
};
bs => {
lvl: Ports.Level; drv: Ports.Drive;
IF port.driveType=separate THEN FOR i: NAT IN [0..wire.size) DO
[lvl, port.ds[i]] ← Setup[wire[i]];
port.bs[i] ← SELECT lvl FROM H => TRUE, L => FALSE, ENDCASE => ERROR;
ENDLOOP
ELSE FOR i: NAT IN [0..wire.size) DO
[lvl, drv] ← Setup[wire[i]];
port.bs[i] ← SELECT lvl FROM H => TRUE, L => FALSE, ENDCASE => ERROR;
IF i=0 THEN port.d ← drv
ELSE IF port.d#drv THEN ERROR; -- should be separate drives ...
ENDLOOP
};
c => ERROR; -- not yet supported
lc => ERROR; -- not yet supported
q => ERROR; -- not yet supported
composite => subElements ← TRUE; -- explore sub-elements
ENDCASE => ERROR; -- unexpected value
};
[] ← Ports.VisitBinding[ct.public, p, EachWirePortPair]; -- compute port
eachVector[];
};
vectors: Rope.ROPE = NARROW [CoreProperties.GetCellTypeProp[ct, $Vectors]];
IF Rope.IsEmpty[vectors] THEN {
TerminalIO.PutF["*** MTS vector file name not specified.\n"];
ERROR;
};
Read[ct, vectors, ConvertVectorToPort];
};
PrintPort: PROC [ct: Core.CellType, p: Ports.Port] ~ {
This is flaky debugging code, activate only if needed...
Print in clear the contents of a port on the terminal
EachWirePortPair: Ports.EachWirePortPairProc ~ {
[wire: Core.Wire, port: Port] RETURNS [subElements: BOOLTRUE, quit: BOOLFALSE]
subElements ← port.levelType=composite;
IF subElements THEN RETURN; -- no need to print in that case
TerminalIO.PutF["%g:(%g)", IO.rope[CoreOps.GetFullWireName[ct.public, wire]], IO.rope[Ports.driveNames[port.d]]];
SELECT port.levelType FROM
l => TerminalIO.PutF["%g ", IO.rope[Ports.levelNames[port.l]]];
b => TerminalIO.PutF["%g ", IO.rope[IF port.b THEN "H" ELSE "L"]];
ls => {
IF port.driveType=separate THEN ERROR -- not supported yet for debug ...
ELSE TerminalIO.PutF["%g ", IO.rope[Ports.LSToRope[port.ls, port.ls.size]]];
};
bs => ERROR; -- not supported yet for debug ...
c => ERROR; -- not yet supported
lc => ERROR; -- not yet supported
q => ERROR; -- not yet supported
composite => subElements ← TRUE; -- explore sub-elements
ENDCASE => ERROR; -- unexpected value
};
[] ← Ports.VisitBinding[ct.public, p, EachWirePortPair]; -- explore port
TerminalIO.PutRope["\n"];
};
Conversion between MTS file format and ASCII format
WriteAsciiHeader: PROC [file: File, out: IO.STREAM] ~ {
EachWD: SymTab.EachPairAction ~ {
wd: WireDescription = NARROW [val];
IO.PutRope[out, wd.name];
SELECT wd.flavor FROM
signal => IO.PutF[out, " : "];
pullup => IO.PutF[out, " /PU : "];
gnd => IO.PutF[out, " /0V : "];
vdd => IO.PutF[out, " /5V : "];
ignore => IF wd.pins#NIL THEN IO.PutF[out, " /NC : "] ELSE IO.PutF[out, " /NC"];
ENDCASE => ERROR;
FOR pins: LIST OF PinDescription ← wd.pins, pins.rest UNTIL pins=NIL DO
pin: PinDescription = pins.first;
IO.PutF[out, " %g:%g", IO.rope[pin.pkgName], IO.rope[pin.conName]];
IO.PutF[out, "[G:%g,B:%g,M:%x,b:%g]", IO.int[pin.group], IO.int[pin.byte], IO.int[pin.mask], IO.int[pin.bit]];
ENDLOOP;
IO.PutF[out, ";"];
TiogaStreams.EndNode[out, same];
};
IO.PutF[out, "%lTitle:%l %g", IO.rope["b"], IO.rope["B"], IO.rope[file.id]];
TiogaStreams.ChangeDepth[out, 1];
TiogaStreams.EndNode[out, reset];
IO.PutF[out, "%lFixture:%l %g", IO.rope["b"], IO.rope["B"], IO.rope[file.package.fixtureName]];
TiogaStreams.EndNode[out, reset];
IO.PutF[out, "%lSignals:%l", IO.rope["b"], IO.rope["B"]];
TiogaStreams.ChangeDepth[out, 1];
[] ← SymTab.Pairs[file.package.signals, EachWD];
TiogaStreams.EndNode[out, reset];
IO.PutF[out, "%lVectors:%l", IO.rope["b"], IO.rope["B"]];
TiogaStreams.ChangeDepth[out, 1];
};
MTSToAscii: PROC [base: Rope.ROPE] ~ {
Convert base.xtv into base.mtsAscii
This version is not definitive... The format may still change, or even be dropped altogether.
EachVector: PROC [file: File] ~ {
IF firstVector THEN WriteAsciiHeader[file, out];
firstVector ← FALSE;
IO.PutF[out, "%l", IO.rope["sf"]];
FOR i: NAT IN [0..file.vector.ngrps) DO
group: CG = file.vector[i];
FOR rank: [0..60) IN [0..60) DO
source, data, mask: BOOL; -- the 3 bits defining a test vector "bit"
c: CHAR;
source ← group.tristate.bits[rank];
data ← group.data.bits[rank];
mask ← group.mask.bits[rank];
IF source THEN
IF data THEN
IF mask THEN c ← '1 ELSE c ← 'T
ELSE
IF mask THEN c ← '0 ELSE c ← 'F
ELSE
IF data THEN
IF mask THEN c ← 'H ELSE c ← '?
ELSE
IF mask THEN c ← 'L ELSE c ← 'X;
IO.PutChar[out, c];
IF rank=15 OR rank=31 OR rank=47 THEN IO.PutChar[out, ' ];
ENDLOOP;
IO.PutChar[out, ' ];
ENDLOOP;
IO.PutF[out, "%l", IO.rope["SF"]];
TiogaStreams.EndNode[out, same];
};
firstVector: BOOLTRUE;
root: TiogaFileOps.Ref ← TiogaFileOps.CreateRoot[];
out: IO.STREAM ← TiogaStreams.CreateOutput[to: root, defaultFormat: "code"];
Read[NIL, base, EachVector];
IO.Close[out];
TiogaFileOps.Store[root, MakeFileName[base, "mtsAscii", TRUE]];
};
EstimatePacking: PROC [base: Rope.ROPE] RETURNS [before: INT, after: INT] ~ {
Estimate how many vectors would be left after compaction
EachVector: PROC [file: File] ~ {
same: BOOLTRUE;
IF previousVector=NIL THEN {
previousVector ← NEW [VectorRep[file.vector.ngrps]];
before ← file.nVects;
same ← FALSE;
}
ELSE {
FOR i: NAT IN [0..file.vector.ngrps) DO
FOR j: NAT IN [0..8) DO
same ← same AND
(file.vector[i].tristate.bytes[j]=previousVector[i].tristate.bytes[j]) AND
(file.vector[i].data.bytes[j]=previousVector[i].data.bytes[j]) AND
(file.vector[i].mask.bytes[j]=previousVector[i].mask.bytes[j]);
ENDLOOP;
ENDLOOP;
};
IF NOT same THEN {
after ← after+1;
FOR i: NAT IN [0..file.vector.ngrps) DO
FOR j: NAT IN [0..8) DO
previousVector[i].tristate.bytes[j] ← file.vector[i].tristate.bytes[j];
previousVector[i].data.bytes[j] ← file.vector[i].data.bytes[j];
previousVector[i].mask.bytes[j] ← file.vector[i].mask.bytes[j];
ENDLOOP;
ENDLOOP;
};
};
previousVector: Vector ← NIL;
before ← 0;
after ← 0;
Read[NIL, base, EachVector];
};
AsciiToMTS: PROC [base: Rope.ROPE] ~ {
Convert base.mtsAscii into base.xtv
ERROR;
-- NOT YET IMPLEMENTED
};
Vector capture
MTSCaptureTP: PUBLIC RosemaryUser.TestProc ~ {
Extension of LogicRosemaryImpl.LogicTest to provide MTS vector capture
logicTime: NAT = Ports.PortIndex[cellType.public, "RosemaryLogicTime"];
memory: BOOL = GetBool[cellType, $Memory, FALSE];
capture: Capture ← CreateCapture[cellType];
p[logicTime].b ← TRUE;
p[logicTime].d ← drive;
DO
ENABLE Rosemary.Stop => IF reason=$BoolWireHasX THEN REJECT ELSE {
TerminalIO.PutF["Simulation completed; msg: %g; reason %g\n", IO.rope[msg], IO.atom[reason]];
EXIT};
p[logicTime].b ← NOT p[logicTime].b;
EvalAndCapture[capture: capture, Eval: Eval, memory: memory];
ENDLOOP;
IF capture#NIL THEN Close[capture.mtsFile];
};
OldMTSReplayTP: PUBLIC RosemaryUser.TestProc ~ {
Extension of LogicRosemaryImpl.LogicTest to provide MTS vector capture
EachVector: PROC [] ~ {
Ports.CopyPortValue[from: vectorPort, to: p];
Eval[memory: memory, clockEval: TRUE, checkPorts: FALSE];
Ports.CopyPortValue[from: vectorPort, to: p];
Eval[memory: memory, clockEval: FALSE, checkPorts: TRUE];
};
memory: BOOL = GetBool[cellType, $Memory, FALSE];
vectorPort: Ports.Port;
vectorPort ← Ports.CreatePort[cellType, TRUE];
ReadPort[cellType, vectorPort, EachVector];
};
Odds and ends
MakeFileName: PROC [source: Rope.ROPE, suffix: Rope.ROPE, local: BOOL] RETURNS [new: Rope.ROPE] ~ {
Extend source using suffix. Force file to be local is local is TRUE.
cp: FS.ComponentPositions;
server, dir, subDirs, base: Rope.ROPE;
[source, cp] ← FS.ExpandName[source];
server ← source.Substr[cp.server.start, cp.server.length];
dir ← source.Substr[cp.dir.start, cp.dir.length];
subDirs ← source.Substr[cp.subDirs.start, cp.subDirs.length];
base ← source.Substr[cp.base.start, cp.base.length];
IF server.Length[]#0 THEN { -- force file name to be within working directory
dir ← NIL; subDirs ← NIL;
};
new ← FS.ConstructFName[[NIL, dir, subDirs, base, suffix, NIL]];
};
GetBool: PROC [ct: Core.CellType, prop: ATOM, default: BOOL] RETURNS [BOOL] ~ {
Read a boolean property from a CT with the specified default
rb: REF BOOLNARROW [CoreProperties.GetCellTypeProp[ct, prop]];
RETURN [IF rb=NIL THEN default ELSE rb^];
};
Specification descriptions
Syntax:
Colon tokens are ignored. ; : / are special characters. : token is always ignored.
Fixture
Name <name> ;
Connector <name> <boardNum> <portNum (T1, T2, T3)>;
Pin <name> <connector> <number (2, 4, ... 40)>;
Package
Name <name> ;
Fixture <name>;
Wire [ / option ] <name> <pin> <pin> ... <pin> ;
Option is one of:
0V : signal is power ground. Check before starting test, TS it afterwards
5V : signal is power +5V. Check before starting test, TS it afterwards
PU : signal has a pullup. Check presence before starting test
NC : signal is not connected to the tester. There may be no pins on this signal (i.e. it is really a no-connect), or there may be some pins. In the latter case, the tester will never drive or sample the values on those pins (typically strange power supplies). To ensure that the tester does not burn up, power supplies outside of the range 0V-5V should be connected to the DUT without any connection to the tester itself.
not specified: normal signal
Fixture: TYPE ~ REF FixtureRep;
FixtureRep: TYPE ~ RECORD [
name: Rope.ROPE,
connectors: SymTab.Ref, -- name -> ConnectorDescription
pins: SymTab.Ref -- name -> PinDescription
];
ConnectorDescription: TYPE ~ REF ConnectorDescriptionRep;
ConnectorDescriptionRep: TYPE ~ RECORD [
name: Rope.ROPE,
board: [0..8),
port: [1..3]
];
Token: IO.BreakProc ~ { -- stop on whitespace and /;, ignore ,:
RETURN[SELECT char FROM
IN [IO.NUL .. IO.SP], ',, ': => sepr,
'/, '; => break,
ENDCASE => other]
};
SyntaxError: PUBLIC SIGNAL [msg: Rope.ROPE, at: INT, s: IO.STREAM] ~ CODE;
Complain: PROC [msg: Rope.ROPE, s: IO.STREAM] ~ {
pos: INTIO.GetIndex[s];
SIGNAL SyntaxError[msg, pos, s];
};
EndOfStatement: PROC [s: IO.STREAM, skip: BOOLFALSE] ~ {
buffer: REF TEXT ← RefText.ObtainScratch[256];
DO
kind: IO.TokenKind; token: REF TEXT;
[tokenKind: kind, token: token] ← IO.GetCedarToken[s, buffer];
SELECT TRUE FROM
kind=tokenEOF => {Complain["End of file in the middle of a statement", s]; EXIT};
kind=tokenSINGLE AND token[0]='; => EXIT;
skip => NULL;
ENDCASE => Complain["Extraneous information ignored", s];
skip ← TRUE;
ENDLOOP;
RefText.ReleaseScratch[buffer];
};
AssignPin: PROC [port: [1..3], index: [1..20]] RETURNS [bit: [0..60), byte: [0..8), mask: BYTE] ~ {
Compute the bit position, byte position & mask given the conector port and index in the port (pin number on the board connector is twice the index as odd-numbered wires are grounded for noise immunity).
bit ← 20*(port-1)+index-1;
byte ← bit/8;
mask ← bitMask[bit MOD 8];
};
ReadFixture: PUBLIC PROC [file: Rope.ROPE] RETURNS [f: Fixture] ~ {
Connectors are checked to have unique names. Connectors are checked not to use twice the same (board, port) combination;
s: IO.STREAM = FS.StreamOpen[MakeFileName[file, "mtsFixture", FALSE], read];
tester: ARRAY [0..8) OF RECORD [ -- to check that physical resources are used only once
connectors: ARRAY [1..3] OF Rope.ROPE,
pins: ARRAY [0..60) OF Rope.ROPE
];
FOR i: NAT IN [0..8) DO
FOR j: NAT IN [1..3] DO tester[i].connectors[j] ← NIL ENDLOOP;
FOR j: NAT IN [0..60) DO tester[i].pins[i] ← NIL ENDLOOP;
ENDLOOP;
f ← NIL;
DO
ENABLE IO.EndOfStream => EXIT;
id: Rope.ROPE; n: INT;
id ← IO.GetTokenRope[s, Token].token;
SELECT TRUE FROM
Rope.Equal[id, "Name", FALSE] => {
ENABLE IO.EndOfStream => GOTO PrematureEOF;
name: Rope.ROPEIO.GetTokenRope[s, Token].token;
IF f#NIL THEN Complain["Name of fixture already defined", s]
ELSE f ← NEW [FixtureRep ← [name: name, connectors: SymTab.Create[], pins: SymTab.Create[]]];
EndOfStatement[s];
};
Rope.Equal[id, "Connector", FALSE] => {
ENABLE IO.EndOfStream => GOTO PrematureEOF;
connector: ConnectorDescription ← NEW [ConnectorDescriptionRep];
connector.name ← IO.GetTokenRope[s, Token].token; -- of connector
IF NOT SymTab.Insert[f.connectors, connector.name, connector] THEN Complain["Connector already defined", s];
n ← IO.GetInt[s];
IF n NOT IN [0..8) THEN Complain["Board number not in [0..8)", s]
ELSE connector.board ← n;
id ← IO.GetTokenRope[s, Token].token;
SELECT TRUE FROM
Rope.Equal[id, "T1"] => connector.port ← 1;
Rope.Equal[id, "T2"] => connector.port ← 2;
Rope.Equal[id, "T3"] => connector.port ← 3;
ENDCASE => Complain["Invalid port identification", s];
id ← tester[connector.board].connectors[connector.port]; -- previously allocated?
IF id#NIL THEN Complain[Rope.Cat["Connector slot already in use under the name ", id], s]
ELSE tester[connector.board].connectors[connector.port] ← connector.name;
EndOfStatement[s];
};
Rope.Equal[id, "Pin", FALSE] => {
ENABLE IO.EndOfStream => GOTO PrematureEOF;
pin: PinDescription = NEW [PinDescriptionRep];
connector: ConnectorDescription ← NIL;
pin.pkgName ← IO.GetTokenRope[s, Token].token; -- of pin on package
IF NOT SymTab.Insert[f.pins, pin.pkgName, pin] THEN Complain["Pin already defined", s];
connector ← NARROW [SymTab.Fetch[f.connectors, IO.GetTokenRope[s, Token].token].val];
IF connector=NIL THEN Complain["Connector has not been defined", s];
pin.group ← connector.board;
n ← IO.GetInt[s];
IF n NOT IN [1..20] THEN Complain["Pin number on connector not in [1..20]", s]
ELSE {
pin.conName ← IO.PutFR["%g.%02g", IO.rope[connector.name], IO.int[n]];
[pin.bit, pin.byte, pin.mask] ← AssignPin[connector.port, n];
};
id ← tester[pin.group].pins[pin.bit];
IF id#NIL THEN Complain[Rope.Cat["Pin slot already in use under the name ", id], s]
ELSE tester[pin.group].pins[pin.bit] ← pin.pkgName;
EndOfStatement[s];
};
ENDCASE => {
Complain["Unknown statement type", s];
EndOfStatement[s, TRUE];
};
REPEAT
PrematureEOF => Complain["End of file in the middle of a statement", s];
ENDLOOP;
};
ReadPackage: PUBLIC PROC [file: Rope.ROPE] RETURNS [p: Package] ~ {
s: IO.STREAMFS.StreamOpen[MakeFileName[file, "mtsPackage", FALSE], read];
pinToWire: SymTab.Ref ← SymTab.Create[]; -- to check each pin is used at most once
fixture: Fixture ← NIL;
p ← NIL;
DO
ENABLE IO.EndOfStream => EXIT;
id: Rope.ROPE;
id ← IO.GetID[s];
SELECT TRUE FROM
Rope.Equal[id, "Name", FALSE] => {
ENABLE IO.EndOfStream => GOTO PrematureEOF;
name: Rope.ROPEIO.GetTokenRope[s, Token].token;
IF p#NIL THEN Complain["Name of package already defined", s]
ELSE p ← NEW [PackageRep ← [name: name, signals: SymTab.Create[]]];
EndOfStatement[s];
};
Rope.Equal[id, "Fixture", FALSE] => {
ENABLE IO.EndOfStream => GOTO PrematureEOF;
id ← IO.GetTokenRope[s, Token].token; -- of fixture, allow . and / inside ...
fixture ← ReadFixture[id];
p.fixtureName ← fixture.name;
EndOfStatement[s];
};
Rope.Equal[id, "Wire", FALSE] => {
ENABLE IO.EndOfStream => GOTO PrematureEOF;
wire: WireDescription = NEW [WireDescriptionRep ← [flavor: signal]];
wire.name ← IO.GetTokenRope[s, Token].token; -- of pin
IF NOT SymTab.Insert[p.signals, wire.name, wire] THEN Complain["Wire already defined", s];
id ← IO.GetTokenRope[s, Token].token; -- pin name or '/
IF Rope.Equal[id, "/"] THEN {
id ← IO.GetTokenRope[s, Token].token; -- option token
SELECT TRUE FROM
Rope.Equal[id, "0V", FALSE] => wire.flavor ← gnd;
Rope.Equal[id, "5V", FALSE] => wire.flavor ← vdd;
Rope.Equal[id, "PU", FALSE] => wire.flavor ← pullup;
Rope.Equal[id, "NC", FALSE] => wire.flavor ← ignore;
ENDCASE => Complain["Incorrect option", s];
IF wire.flavor#signal THEN id ← IO.GetTokenRope[s, Token].token;
};
UNTIL Rope.Equal[id, ";"] DO
pin: PinDescription = NARROW [SymTab.Fetch[fixture.pins, id].val];
IF pin=NIL THEN Complain["Pin not available in package", s]
ELSE {
wire.pins ← CONS [pin, wire.pins];
id ← NARROW [SymTab.Fetch[pinToWire, pin.pkgName].val];
IF id#NIL THEN Complain[Rope.Cat["Pin already connected to wire ", id], s]
ELSE [] ← SymTab.Insert[pinToWire, pin.pkgName, wire.name];
};
id ← IO.GetTokenRope[s, Token].token; -- pin name or ;
ENDLOOP;
IF wire.flavor#ignore AND wire.pins=NIL THEN Complain["No pins specified for this wire", s];
};
ENDCASE => {
Complain["Unknown statement type", s];
EndOfStatement[s, TRUE];
};
REPEAT
PrematureEOF => Complain["End of file in the middle of a statement", s];
ENDLOOP;
};
Fixture dumping
DumpFixture: PROC [in: Rope.ROPE, out: Rope.ROPENIL] ~ {
Reformat the fixture description so that the PC can scan it very easily.
Format is {<pkgName> <conName> <board> <byte> <mask>}*
EachPin: SymTab.EachPairAction ~ {
pd: PinDescription = NARROW [val];
IO.PutF[s, "%s %s %g %g %g\n", IO.rope[pd.pkgName], IO.rope[pd.conName], IO.int[pd.group], IO.int[pd.byte], IO.int[pd.mask]];
};
f: Fixture = ReadFixture[in];
s: IO.STREAM;
IF out=NIL THEN out ← in;
s ← FS.StreamOpen[MakeFileName[out, "fxt", TRUE], create];
[] ← SymTab.Pairs[f.pins, EachPin];
IO.Close[s];
};
Fixture setup description
WriteFixtureSetup: PROC [packageName: IO.ROPE] ~ {
PinDesc:  TYPE = RECORD[name: IO.ROPE, flavor: WireFlag, pin: PinDescription];
TwoPinDesc: TYPE = RECORD[pd1, pd2: PinDesc];
root:   TiogaFileOps.Ref ← TiogaFileOps.CreateRoot[];
out:   IO.STREAM ← TiogaStreams.CreateOutput[to: root];
pins:   LIST OF PinDesc ← NIL;
package:  Package ← ReadPackage[packageName];
eachWire: SymTab.EachPairAction = {
wd: WireDescription     ← NARROW[val];
list: LIST OF PinDescription ← wd.pins;
FOR list ← list, list.rest WHILE list#NIL DO
pins ← CONS[[wd.name, wd.flavor, list.first], pins] ENDLOOP};
[] ← SymTab.Pairs[package.signals, eachWire];
DO
done: BOOLTRUE;
FOR list: LIST OF PinDesc ← pins, list.rest WHILE list#NIL AND list.rest#NIL DO
SELECT Rope.Compare[list.first.pin.conName, list.rest.first.pin.conName] FROM
less  => LOOP;
greater => {
[list.first, list.rest.first] ← TwoPinDesc[list.rest.first, list.first];
done ← FALSE};
ENDCASE => ERROR;
ENDLOOP;
IF done THEN EXIT ENDLOOP;
out.PutF["Package: %g", IO.rope[package.name]];
TiogaStreams.EndNode[out, same];
out.PutF["Fixture: %g", IO.rope[package.fixtureName]];
TiogaStreams.EndNode[out, same];
out.PutF["Pins:"];
TiogaStreams.ChangeDepth[out, 1];
FOR list: LIST OF PinDesc ← pins, list.rest WHILE list#NIL DO
out.PutF["%l%g ", IO.rope["f"], IO.rope[list.first.pin.conName]];
SELECT list.first.flavor FROM
signal  => out.PutRope[" "];
pullup => out.PutRope["PU "];
gnd  => out.PutRope["0V "];
vdd  => out.PutRope["5V "];
ignore => out.PutRope["NC "];
ENDCASE => ERROR;
out.PutF["%g", IO.rope[list.first.name]];
TiogaStreams.EndNode[out, same];
ENDLOOP;
out.Close[];
TiogaFileOps.Store[root, Rope.Cat[packageName, ".mtsFixtureSetup"]]};
Vector replay
PortFromVectorProc: TYPE ~ PROC [file: File, wire: Core.Wire] RETURNS [lvl: Ports.Level, drv: Ports.Drive];
PortFromVector: PROC [file: File, port: Ports.Port, EachSignal: PortFromVectorProc] ~ {
Call EachSignal on each wire used in the port to setup information
EachWirePortPair: Ports.EachWirePortPairProc ~ {
[wire: Core.Wire, port: Port] RETURNS [subElements: BOOLTRUE, quit: BOOLFALSE]
subElements ← FALSE; -- presume that we won't explore children
SELECT port.levelType FROM
l => [port.l, port.d] ← EachSignal[file, wire];
b => {
[port.l, port.d] ← EachSignal[file, wire];
port.b ← SELECT port.l FROM H => TRUE, L => FALSE, ENDCASE => ERROR;
};
ls => {
drv: Ports.Drive;
IF port.driveType=separate THEN FOR i: NAT IN [0..wire.size) DO
[port.ls[i], port.ds[i]] ← EachSignal[file, wire[i]];
ENDLOOP
ELSE FOR i: NAT IN [0..wire.size) DO
[port.ls[i], drv] ← EachSignal[file, wire[i]];
IF i=0 THEN port.d ← drv
ELSE IF port.d#drv THEN ERROR; -- should be separate drives ...
ENDLOOP
};
bs => {
lvl: Ports.Level; drv: Ports.Drive;
IF port.driveType=separate THEN FOR i: NAT IN [0..wire.size) DO
[lvl, port.ds[i]] ← EachSignal[file, wire[i]];
port.bs[i] ← SELECT lvl FROM H => TRUE, L => FALSE, ENDCASE => ERROR;
ENDLOOP
ELSE FOR i: NAT IN [0..wire.size) DO
[lvl, drv] ← EachSignal[file, wire[i]];
port.bs[i] ← SELECT lvl FROM H => TRUE, L => FALSE, ENDCASE => ERROR;
IF i=0 THEN port.d ← drv
ELSE IF port.d#drv THEN ERROR; -- should be separate drives ...
ENDLOOP
};
c => ERROR; -- not yet supported
lc => ERROR; -- not yet supported
q => ERROR; -- not yet supported
composite => subElements ← TRUE; -- explore sub-elements
ENDCASE => ERROR; -- unexpected value
};
[] ← Ports.VisitBinding[file.dut.public, port, EachWirePortPair];
};
VerifyPublicState: PROC [file: File, simulation: Rosemary.Simulation, port: Ports.Port, EachSignal: PortFromVectorProc] ~ {
Verify that the values in port match current values in simulation. Port is for the simulation public (this is intended for replay purposes only). Drive values are not used. H and L indicate expect those values, X means don't care.
VerifyPublic: Ports.EachWirePortPairProc ~ {
PROC [wire: Core.Wire, port: Port] RETURNS [subElements: BOOLTRUE, quit: BOOLFALSE]
Complain: PROC [] ~ {
Issue an error message explaining the error
msg: IO.STREAMIO.ROS[];
nbits: NAT;
SELECT port.levelType FROM
l, b => nbits ← 1;
ls => nbits ← port.ls.size;
bs => nbits ← port.bs.size;
ENDCASE => ERROR;
IO.PutF[msg, "Wire %g expected ", IO.rope[CoreOps.GetFullWireName[simulation.cellType.public, wire]]];
SELECT port.levelType FROM
l => IO.PutRope[msg, Ports.levelNames[port.l]];
b => IO.PutRope[msg, IF port.b THEN "1" ELSE "0"];
ls => IO.PutRope[msg, Ports.LSToRope[port.ls]];
bs => {
FOR bit: NAT IN [0..port.bs.size) DO
IO.PutRope[msg, IF port.bs[bit] THEN "1" ELSE "0"]
ENDLOOP;
};
ENDCASE => ERROR;
IO.PutF[msg, ", but was %g", IO.rope[Ports.LSToRope[ls, nbits]]];
SIGNAL Ports.CheckError[IO.RopeFromROS[msg]];
};
IF port.levelType#composite THEN { -- otherwise, continue recursive descent
firstFreeBit: NAT ← 0;
subElements ← FALSE;
flatWire.wire ← wire;
FOR vals: Rosemary.RoseValues ← Rosemary.GetValues[simulation, flatWire], vals.rest UNTIL vals=NIL DO
currentValue: Ports.LevelSequence ← vals.first.roseWire.currentValue;
IF currentValue=NIL THEN {
ls[firstFreeBit] ← vals.first.roseWire.wireLevel;
firstFreeBit ← firstFreeBit + 1;
}
ELSE {
firstBit: NAT ← vals.first.fieldStart;
FOR bit: NAT IN [0..vals.first.fieldWidth) DO
ls[firstFreeBit + bit] ← currentValue[firstBit+bit];
ENDLOOP;
firstFreeBit ← firstFreeBit + vals.first.fieldWidth;
};
ENDLOOP;
SELECT port.levelType FROM
l => IF port.l#X AND port.l#ls[0] THEN Complain[];
b => SELECT ls[0] FROM
H => IF NOT port.b THEN Complain[];
L => IF port.b THEN Complain[];
ENDCASE => Complain[];
ls => FOR i: NAT IN [0..port.ls.size) DO
IF port.ls[i]#X AND port.ls[i]#ls[i] THEN {
Complain[];
EXIT;
};
ENDLOOP;
bs => FOR i: NAT IN [0..port.bs.size) DO
SELECT ls[i] FROM
H => IF NOT port.bs[i] THEN {Complain[]; EXIT};
L => IF port.bs[i] THEN {Complain[]; EXIT};
ENDCASE => {Complain[]; EXIT};
ENDLOOP;
ENDCASE => ERROR; -- only l, b, ls, bs ports are supported
};
};
ls: Ports.LevelSequence ← NEW [Ports.LevelSequenceRec[1024]];
flatWire: CoreFlat.FlatWire ← NEW [CoreFlat.FlatWireRec];
flatWire.flatCell ← CoreFlat.rootCellType;
flatWire.wireRoot ← public;
PortFromVector[file, port, EachSignal];
[] ← Ports.VisitBinding[simulation.cellType.public, port, VerifyPublic];
};
StandardDriving: PortFromVectorProc ~ {
[file: File, wire: Core.Wire] RETURNS [lvl: Ports.Level, drv: Ports.Drive]
Simulates normal driving phase of tester
wd: WireDescription = NARROW [RefTab.Fetch[file.wireToPins, wire].val];
group: CG = file.vector[wd.pins.first.group];
bit: [0..60) = wd.pins.first.bit;
SELECT TRUE FROM
group.tristate.bits[bit] => {
drv ← drive;
lvl ← IF group.data.bits[bit] THEN H ELSE L;
};
wd.flavor=pullup => { -- simulate pullup when tester is not driving
drv ← driveWeak;
lvl ← H;
};
ENDCASE => {
drv ← none;
lvl ← L;
};
};
StandardChecking: PortFromVectorProc ~ {
[file: File, wire: Core.Wire] RETURNS [lvl: Ports.Level, drv: Ports.Drive]
Simulates normal verification phase of tester (used by VerifyPublicState)
wd: WireDescription = NARROW [RefTab.Fetch[file.wireToPins, wire].val];
group: CG = file.vector[wd.pins.first.group];
bit: [0..60) = wd.pins.first.bit;
IF group.tristate.bits[bit] OR group.mask.bits[bit]THEN {
drv ← expect;
lvl ← IF group.data.bits[bit] THEN H ELSE L;
}
ELSE {
drv ← none;
lvl ← X;
};
};
MTSReplayTP: PUBLIC RosemaryUser.TestProc ~ {
[simulation: Rosemary.Simulation, cellType: Core.CellType, p: Ports.Port, Eval: TestEvalProc]
Replay proc at transistor level (without ClockEval) without tri-state verification
EachVector: PROC [file: File] ~ {
PortFromVector[file, p, StandardDriving]; -- get new driving info
Eval[memory: memory, clockEval: FALSE, checkPorts: FALSE];
VerifyPublicState[file, simulation, p, StandardChecking]; -- verify state
};
memory: BOOL = GetBool[cellType, $Memory, FALSE];
vectors: Rope.ROPE = NARROW [CoreProperties.GetCellTypeProp[cellType, $Vectors]];
IF Rope.IsEmpty[vectors] THEN {
TerminalIO.PutF["*** MTS vector file name not specified.\n"];
ERROR;
};
TerminalIO.PutF["Replaying MTS vectors from %g on %g at transistor level.\n", IO.rope[vectors], IO.rope[CoreOps.GetCellTypeName[cellType]]];
Read[cellType, vectors, EachVector];
};
MTSReplayGateTP: RosemaryUser.TestProc ~ {
[simulation: Rosemary.Simulation, cellType: Core.CellType, p: Ports.Port, Eval: TestEvalProc]
Replay proc at gate level (with ClockEval) without tri-state verification
EachVector: PROC [file: File] ~ {
PortFromVector[file, p, StandardDriving]; -- get new driving info
Eval[memory: memory, clockEval: TRUE, checkPorts: FALSE];
PortFromVector[file, p, StandardDriving]; -- get new driving info (same as previous)
Eval[memory: memory, clockEval: FALSE, checkPorts: FALSE];
VerifyPublicState[file, simulation, p, StandardChecking]; -- verify state
};
memory: BOOL = GetBool[cellType, $Memory, FALSE];
vectors: Rope.ROPE = NARROW [CoreProperties.GetCellTypeProp[cellType, $Vectors]];
IF Rope.IsEmpty[vectors] THEN {
TerminalIO.PutF["*** MTS vector file name not specified.\n"];
ERROR;
};
TerminalIO.PutF["Replaying MTS vectors from %g on %g at gate level.\n", IO.rope[vectors], IO.rope[CoreOps.GetCellTypeName[cellType]]];
Read[cellType, vectors, EachVector];
};
MTSReplayTSTP: RosemaryUser.TestProc ~ {
[simulation: Rosemary.Simulation, cellType: Core.CellType, p: Ports.Port, Eval: TestEvalProc]
Replay proc at transistor level (without ClockEval) with tri-state verification
ERROR; -- Function is not yet implemented
};
MTSReplayTSGateTP: RosemaryUser.TestProc ~ {
[simulation: Rosemary.Simulation, cellType: Core.CellType, p: Ports.Port, Eval: TestEvalProc]
Replay proc at transistor level (with ClockEval) with tri-state verification
ERROR; -- Function is not yet implemented
};
Initialization
Init: PROC [] RETURNS [] ~ {
Various initializations
bitMask[0] ← 080H;
bitMask[1] ← 040H;
bitMask[2] ← 020H;
bitMask[3] ← 010H;
bitMask[4] ← 008H;
bitMask[5] ← 004H;
bitMask[6] ← 002H;
bitMask[7] ← 001H;
RosemaryUser.RegisterTestProc["MTSCapture", MTSCaptureTP];
RosemaryUser.RegisterTestProc["MTSCapture2", MTSCaptureTP2];
RosemaryUser.RegisterTestProc["MTSReplay", MTSReplayTP];
RosemaryUser.RegisterTestProc["MTSReplayGate", MTSReplayGateTP];
RosemaryUser.RegisterTestProc["MTSReplayTS", MTSReplayTSTP];
RosemaryUser.RegisterTestProc["MTSReplayTSGate", MTSReplayTSGateTP];
RosemaryUser.RegisterTestProc["OldMTSReplay", OldMTSReplayTP];
};
Init[];
END.
Jean-Marc Frailong September 14, 1988 2:31:52 pm PDT
Minor aesthetic modifications
changes to: Fail (local of MapPublicToPackage) changed mesage, Read added vector count print out, ReadPort changed message
Jean-Marc Frailong September 16, 1988 12:23:01 pm PDT
Need hack to take in account X on gates ...
changes to: Record (local of WriteVectorFromPort)