MTSVectorImpl.mesa
Copyright Ó 1987 by Xerox Corporation. All rights reserved.
Jean-Marc Frailong March 7, 1988 1:36:34 pm PST
Test vector generation for the PC-based MTS tester
DIRECTORY
Basics, BasicTime, Convert, Core, CoreFlat, CoreOps, CoreProperties, FS, IO, Ports, RefTab, RefText, Rosemary, RosemaryUser, RosemaryVector, Rope, TerminalIO, TiogaFileOps, TiogaStreams, MTSVector;
MTSVectorImpl: CEDAR PROGRAM
IMPORTS Basics, BasicTime, Convert, CoreFlat, CoreOps, CoreProperties, FS, IO, Ports, RefTab, RefText, Rosemary, RosemaryUser, RosemaryVector, Rope, TerminalIO, TiogaFileOps, TiogaStreams
EXPORTS MTSVector
SHARES MTSVector
~ BEGIN OPEN MTSVector;
Types & constants
vectorBlockLength: NAT ← 16384; -- number of bytes in a block of test vectors (end up with 0's)
testFileHeaderSize: NAT ← 2048; -- minimum number of bytes in file header
headerSignature: Rope.ROPE ← "MTSX0002"; -- signature in header
TFH: TYPE ~ REF TFHRep; -- the data structure does not really exist...
TFHRep: TYPE ~ RECORD [ -- Test File Header
signature: ROPE ← "MTSX0002", -- always 8 bytes
cgOffset: CARD32 ← 0, -- byte index of 1st block in file, starting from byte 0
nCG: CARD32 ← 0, -- number of channel groups (ngrps in all vectors)
blkLen: CARD32 ← 0, -- number of bytes in a block, equal to vecBlkLen
ckSum: BYTE ← 0, -- including this, the file sums to 0
testID: ROPENIL -- information, 120 bytes long...
];
The TFH sould be padded with 0's to reach 2K bytes, the vectors should start on a page boundary.
Pin management
RankFromNum: PROC [pinNum: NAT] RETURNS [rank: NAT] ~ {
Compute index in BOOL array from MTS pin number within board. This is due to the bit-level little-endian ordering on the PC ...
rank ← 8*(pinNum/8) + 7 - pinNum MOD 8;
};
ReadPinsFile: PROC [pinsFileName: Rope.ROPE] RETURNS [pins: LIST OF Pin, groups: INT, id: Rope.ROPE] ~ {
Read the pin description file pinsFileName into pins.
Syntax is:
<Title line> -- a full line of text
<wire name> ":" <group><connector><pin in connector> [<pullup>] ";"
<wire name> ":" "NC" ";"
where <group> is digit 0..9, <connector> is A, B or C, <pin in connector> is an integer in 0..19 and <pullup> is an optionnal token "PullUp". It is also permissible to use as pin-in-connector the syntax 0/<n> with n in [1..64].
pinFile: IO.STREAMFS.StreamOpen[pinsFileName, read];
someFailed: BOOLFALSE;
failed: BOOL;
usedPins: ARRAY [0..9] OF PACKED ARRAY [0..64) OF BOOL;
FOR i: NAT IN [0..9] DO
FOR j: NAT IN [0..64) DO usedPins[i][j] ← FALSE ENDLOOP;
ENDLOOP;
[] ← IO.SkipWhitespace[pinFile, TRUE]; -- skip over comments
id ← IO.GetLineRope[pinFile]; -- use the 1st file line as a comment
groups ← 0;
DO
Fail: PROC [msg: Rope.ROPE] ~ {
failed ← TRUE; someFailed ← TRUE;
TerminalIO.PutF["Wire %g : %g.\n", IO.rope[wireName], IO.rope[msg]];
};
WordProc: IO.BreakProc ~ {
RETURN [SELECT char FROM
IN [IO.NUL .. IO.SP] => sepr,
':, '; => break,
ENDCASE => other];
};
GetToken: PROC [] RETURNS [result: Rope.ROPENIL] ~ {
result ← IO.GetTokenRope[pinFile, WordProc ! IO.EndOfStream =>
{Fail["premature end of file"]; REJECT}
].token;
};
Group: PROC [c: CHAR] RETURNS [grp: INT] ~ {
SELECT c FROM
'0 => grp ← 0;
'1 => grp ← 1;
'2 => grp ← 2;
'3 => grp ← 3;
'4 => grp ← 4;
'5 => grp ← 5;
'6 => grp ← 6;
'7 => grp ← 7;
'8 => grp ← 8;
'9 => grp ← 9;
ENDCASE => {Fail["invalid group number"]; grp ← -1};
};
PinOnBoard: PROC [token: Rope.ROPE] RETURNS [pinNum: INT] ~ {
connector, pinInConnector: INT;
SELECT Rope.Fetch[token, 0] FROM
'/ => connector ← -1;
'A => connector ← 0;
'B => connector ← 1;
'C => connector ← 2;
ENDCASE => {
Fail["invalid connector"];
GOTO invalid;
};
pinInConnector ← Convert.CardFromDecimalLiteral[token, 1 ! Convert.Error => {Fail["invalid pin number"]; GOTO invalid}];
IF connector>0 AND (pinInConnector<1 OR pinInConnector>20) THEN {
Fail["invalid pin number"];
GOTO invalid;
};
pinNum ← IF connector<0 THEN pinInConnector-1 ELSE pinInConnector+20*connector-1;
IF pinNum<0 OR pinNum>63 THEN {
Fail["invalid pin number"];
GOTO invalid;
};
EXITS
invalid => pinNum ← -1;
};
wireName, token: Rope.ROPE;
group, pinNum: INT;
hasPullup: BOOLFALSE;
notConnected: BOOLFALSE;
pin: Pin;
failed ← FALSE;
wireName ← IO.GetTokenRope[pinFile, WordProc ! IO.EndOfStream => EXIT].token; -- EOF
token ← GetToken[ ! IO.EndOfStream => EXIT];
IF Rope.Equal[token, ":"] THEN token ← GetToken[ ! IO.EndOfStream => EXIT]
ELSE Fail["missing colon after wire name"];
SELECT TRUE FROM
Rope.Equal[token, "NC", FALSE] => { -- unconnected wire
notConnected ← TRUE;
token ← GetToken[ ! IO.EndOfStream => EXIT];
};
Rope.Length[token]<3 => Fail["pin specification too short"];
ENDCASE => { -- normal pin specification
group ← Group[Rope.Fetch[token, 0]];
pinNum ← PinOnBoard[Rope.Substr[token, 1]];
token ← GetToken[ ! IO.EndOfStream => EXIT];
IF Rope.Equal[token, "PullUp", FALSE] THEN {
hasPullup ← TRUE;
token ← GetToken[ ! IO.EndOfStream => EXIT];
};
};
IF NOT Rope.Equal[token, ";"] THEN {
Fail["missing semicolon, skipping to next semicolon"];
UNTIL Rope.Equal[token, ";"] DO
token ← GetToken[ ! IO.EndOfStream => EXIT];
ENDLOOP;
};
IF NOT failed THEN {
pin ← NEW [PinRep ← [name: wireName, notConnected: notConnected, hasPullup: hasPullup, group: group, rank: RankFromNum[pinNum], pinNum: pinNum]]; -- rank in bit-level little-endian...
groups ← MAX [groups, group];
pins ← CONS [pin, pins];
IF NOT notConnected THEN {
IF usedPins[group][pinNum] THEN Fail["pin already used"];
usedPins[group][pinNum] ← TRUE;
}
};
ENDLOOP;
IO.Close[pinFile];
groups ← groups+1; -- this is the number of groups, i.e. highest group+1...
IF someFailed THEN groups ← -1; -- failure indication
};
MapPublicToPins: PROC [public: Core.WireSeq, pins: LIST OF Pin] RETURNS [wireToPins: RefTab.Ref] ~ {
Build the wireToPins field of a file from dut.public & pins
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
};
};
failed: BOOLFALSE;
wireToPins ← RefTab.Create[];
UNTIL pins=NIL DO
pin: Pin = pins.first;
wire: Core.Wire = CoreOps.FindWire[public, pin.name];
pins ← pins.rest;
SELECT TRUE FROM
wire=NIL => Fail[pin.name, "not found in public"];
wire.size#0 => Fail[pin.name, "not atomic"];
RefTab.Insert[wireToPins, wire, pin] => NULL; -- all OK
ENDCASE => Fail[pin.name, "wire defined twice"];
ENDLOOP;
CoreOps.VisitRootAtomics[public, CheckDefined];
IF failed THEN wireToPins ← NIL;
};
Physical file access details for write
WriteByte: PROC [file: File, b: BYTE] ~ {
Write a byte into file and then update file checksum.
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];
};
WriteCard: PROC [file: File, c: CARD32] ~ {
Write a 32-bit integer in little-endian order (LSB first)
n: Basics.LongNumber;
n.lc ← c;
WriteByte[file, n.ll];
WriteByte[file, n.lh];
WriteByte[file, n.hl];
WriteByte[file, n.hh];
};
WriteRope: PROC [file: File, r: Rope.ROPE, length: NAT] ~ {
EachChar: Rope.ActionType ~ { WriteByte[file, ORD[c]] };
[] ← Rope.Map[base: r, action: EachChar, start: 0, len: MIN[length, Rope.Length[r]]];
FOR i: INT IN [Rope.Length[r] .. length) DO WriteByte[file, 00H] ENDLOOP;
};
PinRepLength: PROC [pin: Pin] RETURNS [nBytes: NAT] ~ {
nBytes ← Rope.Length[pin.name] + 8; -- for extra fields
};
WritePin: PROC [file: File, pin: Pin] ~ {
length: INT = Rope.Length[pin.name];
WriteByte[file, (IF pin.notConnected THEN 0FFH ELSE 00H)];
WriteByte[file, pin.group];
WriteByte[file, pin.pinNum];
WriteByte[file, (IF pin.hasPullup THEN 0FFH ELSE 00H)];
WriteCard[file, length];
WriteRope[file, pin.name, length];
};
WriteHeader: PROC [file: File] ~ {
Write header. It is assumed that file.block is empty at this point. The file pointer is moved to the end of the header
headerSize: CARD32 ← 256; -- an upper bound on the fixed part of the header size
ckSumOffset: INT; -- this is where the checksum will go once completed...
endOfBlockOffset: INT; -- this is where the header ends
ckSumByte: BYTE;
IF file.block.length#0 THEN ERROR; -- block has not been flushed
IO.SetIndex[file.stream, 0]; -- backup to start of file
FOR p: LIST OF Pin ← file.pins, p.rest UNTIL p=NIL DO
headerSize ← headerSize+PinRepLength[p.first]; -- for extra pin fields
ENDLOOP;
headerSize ← 1024*((MAX[headerSize+1, testFileHeaderSize]+1023)/1024); -- round up to 1K
WriteRope[file, headerSignature, 8];
WriteCard[file, headerSize];
WriteCard[file, file.nGroups];
WriteCard[file, vectorBlockLength];
WriteCard[file, file.nVects];
ckSumOffset ← file.block.length; -- note down where checksum should go finally
WriteByte[file, 00H]; -- this is the checksum position, patched at end
WriteRope[file, file.id, 120];
FOR p: LIST OF Pin ← file.pins, p.rest UNTIL p=NIL DO WritePin[file, p.first] ENDLOOP;
WriteByte[file, 080H]; -- end of pin list indicator
FlushBlock[file, headerSize];
ckSumByte ← Basics.BITAND[0100H-file.ckSum, 000FFH]; -- recompute checksum for total 0
endOfBlockOffset ← IO.GetIndex[file.stream];
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, endOfBlockOffset]; -- get back to end of header
};
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 WriteByte[file, 00H] ENDLOOP;
IO.PutBlock[file.stream, block, 0, nBytes];
block.length ← 0;
};
Physical file access details for read
ReadByte: PROC [file: File] RETURNS [b: BYTE] ~ {
Read a byte and compute checksum
b ← IO.GetByte[file.stream];
file.ckSum ← Basics.BITAND[file.ckSum+b, 000FFH];
};
ReadCard: PROC [file: File] RETURNS [c: CARD32] ~ {
Read a 32-bit integer in little-endian order (LSB first)
n: Basics.LongNumber;
n.ll ← ReadByte[file];
n.lh ← ReadByte[file];
n.hl ← ReadByte[file];
n.hh ← ReadByte[file];
c ← n.lc;
};
ReadRope: PROC [file: File, 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 = ReadByte[file];
buf ← RefText.AppendChar[buf, VAL[b]];
IF b#00H THEN lastNonPad ← i;
ENDLOOP;
r ← Rope.FromRefText[buf, 0, lastNonPad+1];
};
ReadHeader: PROC [file: File] ~ {
Read the header in and fill in adequate fields of the File structure
signature: Rope.ROPE;
headerSize, blockLength: NAT;
signature ← ReadRope[file, 8];
IF NOT Rope.Equal[signature, headerSignature] THEN ERROR; -- invalid file format
headerSize ← ReadCard[file];
file.nGroups ← ReadCard[file];
blockLength ← ReadCard[file];
IF blockLength#vectorBlockLength THEN ERROR; -- invalid file format
file.nVects ← ReadCard[file];
[] ← ReadByte[file]; -- checksum
file.id ← ReadRope[file, 120];
file.pins ← NIL;
DO -- read pins until no more
grp, num, hasPullup, notConnected: BYTE;
len: INT;
name: Rope.ROPE;
pin: Pin ← NEW [PinRep];
notConnected ← ReadByte[file];
IF notConnected=080H THEN EXIT; -- end of header significant information
grp ← ReadByte[file];
num ← ReadByte[file];
hasPullup ← ReadByte[file];
len ← ReadCard[file];
name ← ReadRope[file, len];
pin.name ← name;
pin.notConnected ← IF notConnected=00H THEN FALSE ELSE TRUE;
pin.hasPullup ← IF hasPullup=00H THEN FALSE ELSE TRUE;
pin.group ← grp;
pin.pinNum ← num;
pin.rank ← RankFromNum[pin.pinNum];
file.pins ← CONS [pin, file.pins];
ENDLOOP;
IF IO.GetIndex[file.stream]>headerSize THEN ERROR; -- header longer than announced
FOR i: INT IN [IO.GetIndex[file.stream] .. headerSize) DO
[] ← ReadByte[file]; -- to compute checksum on the fly
ENDLOOP;
};
Low-level client access to MTS files
Create: PUBLIC PROC [ct: Core.CellType, fileBase: Rope.ROPE] RETURNS [file: File] ~ {
Create the handle and make it ready to accept vectors.
Vectors are written into fileBase.mtsVectors, pin information is read from fileBase.mtsPins.
file ← NEW [FileRep ← [dut: ct]];
[file.pins, file.nGroups, file.id] ← ReadPinsFile[MakeFileName[fileBase, "mtsPins", FALSE]];
file.wireToPins ← MapPublicToPins[ct.public, file.pins];
IF file.nGroups<0 OR file.wireToPins=NIL THEN ERROR; -- incorrect pin file
IF Rope.IsEmpty[file.id] THEN file.id ← Rope.Cat["Vectors for ", CoreOps.GetCellTypeName[ct]];
file.id ← IO.PutFR["%g -- Generated %g", IO.rope[file.id], IO.time[]];
file.ckSum ← 0;
file.nVects ← 0;
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[fileBase, "mtsVectors", TRUE], create];
WriteHeader[file];
file.ckSum ← 0; -- ignore previous data, will be rewritten anyway...
};
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 WriteByte[file, group.tristate.bytes[i]] ENDLOOP;
FOR i: NAT IN [0..8) DO WriteByte[file, group.data.bytes[i]] ENDLOOP;
FOR i: NAT IN [0..8) DO WriteByte[file, group.mask.bytes[i]] ENDLOOP;
ENDLOOP;
};
WriteVectorFromPort: PUBLIC PROC [file: File, port: Ports.Port] ~ {
Convert a Rosemary port into an MTS vector and write it into file
Record: PROC [wire: Core.Wire, level: Ports.Level, drive: Ports.Drive] ~ {
Record the right information into the test vector
pin: Pin ← NARROW [RefTab.Fetch[wireToPins, wire].val];
group: CG;
source, data, mask: BOOL; -- the 3 bits defining a test vector "bit"
IF pin.notConnected THEN RETURN; -- pin not connected to tester
SELECT drive FROM
expect => SELECT level FROM
H => {source ← FALSE; data ← TRUE; mask ← TRUE}; -- H: don't drive, expect a 1
L => {source ← FALSE; data ← FALSE; mask ← TRUE}; -- L: don't drive, expect a 0
X => {source ← FALSE; data ← FALSE; mask ← FALSE}; -- X: don't drive, ignore result
ENDCASE => ERROR; -- cannot happen
inspect => {source ← TRUE; data ← FALSE; mask ← FALSE}; -- F: don't drive, check TS
none => {source ← FALSE; data ← FALSE; mask ← FALSE}; -- X: don't drive, ignore result
force => {
IF pin.hasPullup THEN SELECT level 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 level 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
};
drive => SELECT level 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; -- unsupported drive (c.f. RosemaryVector.SettleToBoundTestPort)
group ← vector[pin.group];
group.tristate.bits[pin.rank] ← source;
group.data.bits[pin.rank] ← data;
group.mask.bits[pin.rank] ← mask;
};
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 => Record[wire, port.l, port.d];
b => Record[wire, IF port.b THEN H ELSE L, port.d];
ls => {
IF port.driveType=separate THEN FOR i: NAT IN [0..wire.size) DO
Record[wire[i], port.ls[i], port.ds[i]];
ENDLOOP
ELSE FOR i: NAT IN [0..wire.size) DO
Record[wire[i], port.ls[i], port.d];
ENDLOOP
};
bs => {
IF port.driveType=separate THEN FOR i: NAT IN [0..wire.size) DO
Record[wire[i], IF port.bs[i] THEN H ELSE L, port.ds[i]];
ENDLOOP
ELSE FOR i: NAT IN [0..wire.size) DO
Record[wire[i], IF port.bs[i] THEN H ELSE L, port.d];
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
};
vector: Vector ← file.vector;
wireToPins: RefTab.Ref ← file.wireToPins;
FOR i: NAT IN [0..vector.ngrps) DO -- force uninitialized bits to Drive Low
vector[i].tristate.bits ← ALL [TRUE];
vector[i].data.bits ← ALL [FALSE];
vector[i].mask.bits ← ALL [TRUE];
ENDLOOP;
[] ← Ports.VisitBinding[file.dut.public, port, EachWirePortPair]; -- 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];
WriteHeader[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, fileBase: 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]];
file.stream ← FS.StreamOpen[MakeFileName[fileBase, "mtsVectors", FALSE], read];
ReadHeader[file];
IF file.pins=NIL THEN ERROR; -- something wrong with the pins
IF ct#NIL THEN { -- ct may be omitted, but then there is no wire->pin mapping and some checks are bypassed
file.wireToPins ← MapPublicToPins[ct.public, file.pins];
IF file.wireToPins=NIL THEN ERROR; -- something wrong with the pins
};
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;
THROUGH [0..file.nVects) DO
IF blockOffset+bytesPerVector>vectorBlockLength THEN {
nBytesRead: NATIO.GetBlock[file.stream, block, 0, vectorBlockLength];
IF nBytesRead#vectorBlockLength THEN ERROR; -- last block truncated
FOR i: NAT IN [0..vectorBlockLength) DO
b: BYTE = ORD[RefText.Fetch[block, i]];
file.ckSum ← Basics.BITAND[file.ckSum+b, 000FFH];
ENDLOOP;
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 Basics.BITAND[file.ckSum, 0FFH]#0 THEN ERROR; -- invalid checksum
};
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.
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: Rope.ROPE = GetVectors[ct];
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;
IF Rope.IsEmpty[vectors] THEN RETURN[Failed["Unable to find name for vector file"]];
capture.port ← Ports.CreatePort[targetCT, TRUE];
capture.target ← RosemaryVector.CreateTarget[handle.simulation, targetFlatCT, capture.port];
capture.mtsFile ← Create[targetCT, vectors];
};
CaptureAndWrite: PUBLIC PROC [capture: Capture] ~ {
IF capture#NIL THEN {
RosemaryVector.SampleTarget[capture.target, capture.port];
WriteVectorFromPort[capture.mtsFile, capture.port];
};
};
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
pin: Pin = NARROW [RefTab.Fetch[file.wireToPins, wire].val];
IF pin.notConnected THEN {
lvl ← X; drv ← inspect; -- who cares...
}
ELSE {
group: CG = file.vector[pin.group];
source, data, mask: BOOL; -- the 3 bits defining a test vector "bit"
source ← group.tristate.bits[pin.rank];
data ← group.data.bits[pin.rank];
mask ← group.mask.bits[pin.rank];
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 ← IF pin.hasPullup THEN force ELSE expect} -- H
ELSE {lvl ← X; drv ← inspect} -- ?
ELSE
IF mask THEN {lvl ← L; drv ← expect} -- L
ELSE {lvl ← X; drv ← inspect} -- X
};
};
EachWirePortPair: Ports.EachWirePortPairProc ~ {
[wire: Core.Wire, port: Port] RETURNS [subElements: BOOLTRUE, quit: BOOLFALSE]
pin: Pin = NARROW [RefTab.Fetch[file.wireToPins, wire].val];
IF pin#NIL AND pin.notConnected THEN RETURN;
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 = GetVectors[ct];
IF Rope.IsEmpty[vectors] THEN {
TerminalIO.PutF["*** Unable to find MTS vector file name\n"];
ERROR;
};
Read[ct, vectors, ConvertVectorToPort];
};
Conversion between MTS file format and ASCII format
WriteAsciiHeader: PROC [file: File, out: IO.STREAM] ~ {
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, "%lPins:%l", IO.rope["b"], IO.rope["B"]];
TiogaStreams.ChangeDepth[out, 1];
FOR pins: LIST OF Pin ← file.pins, pins.rest UNTIL pins=NIL DO
pin: Pin = pins.first;
IO.PutF[out, "%g : ", IO.rope[pin.name]];
IF pin.notConnected THEN IO.PutF[out, "NC ;"]
ELSE {
IO.PutF[out, "%g%g%g ", IO.int[pin.group], IO.char['A+(pin.pinNum/20)], IO.int[(pin.pinNum MOD 20)+1]];
IF pin.hasPullup THEN IO.PutF[out, "PullUp "];
IO.PutF[out, ";"];
};
TiogaStreams.EndNode[out, same];
ENDLOOP;
TiogaStreams.EndNode[out, reset];
IO.PutF[out, "%lVectors:%l", IO.rope["b"], IO.rope["B"]];
TiogaStreams.ChangeDepth[out, 1];
};
MTSToAscii: PUBLIC PROC [base: Rope.ROPE] ~ {
Convert base.mtsVectors 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 pinNum: [0..64) IN [0..64) DO
rank: [0..64) = RankFromNum[pinNum];
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 pinNum=19 OR pinNum=39 OR pinNum=59 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]];
};
AsciiToMTS: PUBLIC PROC [base: Rope.ROPE] ~ {
Convert base.mtsAscii into base.mtsVectors
ERROR;
-- NOT YET IMPLEMENTED
};
Test procedure(s)
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;
Eval[memory: memory, clockEval: TRUE, checkPorts: FALSE];
Eval[memory: memory, clockEval: FALSE, checkPorts: TRUE];
CaptureAndWrite[capture];
ENDLOOP;
IF capture#NIL THEN Close[capture.mtsFile];
};
MTSReplayTP: PUBLIC RosemaryUser.TestProc ~ {
Extension of LogicRosemaryImpl.LogicTest to provide MTS vector capture
EachVector: PROC [] ~ {
Eval[memory: memory, clockEval: TRUE, checkPorts: FALSE];
Eval[memory: memory, clockEval: FALSE, checkPorts: TRUE];
};
memory: BOOL = GetBool[cellType, $Memory, FALSE];
ReadPort[cellType, p, 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]];
};
GetVectors: PROC [ct: Core.CellType] RETURNS [vectors: Rope.ROPE] ~ {
vectors ← NARROW [CoreProperties.GetCellTypeProp[ct, $Vectors]];
IF Rope.IsEmpty[vectors] THEN vectors ← CoreOps.GetCellTypeName[ct];
};
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^];
};
Initialization
RosemaryUser.RegisterTestProc["MTSCapture", MTSCaptureTP];
RosemaryUser.RegisterTestProc["MTSReplay", MTSReplayTP];
END.