<> <> <> <<>> <> <<>> 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; <> 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 <> <> <> <> <> <> <> <> <<];>> <> <> RankFromNum: PROC [pinNum: NAT] RETURNS [rank: NAT] ~ { <> rank _ 8*(pinNum/8) + 7 - pinNum MOD 8; }; ReadPinsFile: PROC [pinsFileName: Rope.ROPE] RETURNS [pins: LIST OF Pin, groups: INT, id: Rope.ROPE] ~ { <> <> << -- 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.STREAM _ FS.StreamOpen[pinsFileName, read]; someFailed: BOOL _ FALSE; 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.ROPE _ NIL] ~ { 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: BOOL _ FALSE; notConnected: BOOL _ FALSE; 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: BOOL _ FALSE; 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: BOOL _ TRUE, quit: BOOL _ FALSE]>> 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: BYTE _ IO.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: NAT _ IO.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: BOOL _ TRUE, quit: BOOL _ FALSE]>> 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: BOOL _ TRUE; 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 BOOL _ NARROW [CoreProperties.GetCellTypeProp[ct, prop]]; RETURN [IF rb=NIL THEN default ELSE rb^]; }; <<Initialization>> RosemaryUser.RegisterTestProc["MTSCapture", MTSCaptureTP]; RosemaryUser.RegisterTestProc["MTSReplay", MTSReplayTP]; END.