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] ~ { 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] ~ { 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; }; WriteByte: PROC [file: File, b: BYTE] ~ { 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] ~ { 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] ~ { 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] ~ { 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; }; ReadByte: PROC [file: File] RETURNS [b: BYTE] ~ { b _ IO.GetByte[file.stream]; file.ckSum _ Basics.BITAND[file.ckSum+b, 000FFH]; }; ReadCard: PROC [file: File] RETURNS [c: CARD32] ~ { 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] ~ { 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] ~ { 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; }; Create: PUBLIC PROC [ct: Core.CellType, fileBase: Rope.ROPE] RETURNS [file: File] ~ { 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] ~ { 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] ~ { Record: PROC [wire: Core.Wire, level: Ports.Level, drive: Ports.Drive] ~ { 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 ~ { 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] ~ { 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]] ~ { 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 }; CreateCapture: PUBLIC PROC [ct: Core.CellType] RETURNS [capture: 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] ~ { 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 ~ { 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]; }; 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] ~ { 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] ~ { ERROR; }; MTSCaptureTP: PUBLIC RosemaryUser.TestProc ~ { 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 ~ { 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]; }; MakeFileName: PROC [source: Rope.ROPE, suffix: Rope.ROPE, local: BOOL] RETURNS [new: Rope.ROPE] ~ { 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] ~ { rb: REF BOOL _ NARROW [CoreProperties.GetCellTypeProp[ct, prop]]; RETURN [IF rb=NIL THEN default ELSE rb^]; }; RosemaryUser.RegisterTestProc["MTSCapture", MTSCaptureTP]; RosemaryUser.RegisterTestProc["MTSReplay", MTSReplayTP]; END. &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 Types & constants 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: ROPE _ NIL -- 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 Compute index in BOOL array from MTS pin number within board. This is due to the bit-level little-endian ordering on the PC ... Read the pin description file pinsFileName into pins. Syntax is: -- 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]. Build the wireToPins field of a file from dut.public & pins Physical file access details for write Write a byte into file and then update file checksum. Write a 32-bit integer in little-endian order (LSB first) Write header. It is assumed that file.block is empty at this point. The file pointer is moved to the end of the header Flush current block padded to nBytes with zeroes. Physical file access details for read Read a byte and compute checksum Read a 32-bit integer in little-endian order (LSB first) Read a NULL-padded rope of the required length. Strip padding on return. Read the header in and fill in adequate fields of the File structure Low-level client access to MTS files Create the handle and make it ready to accept vectors. Vectors are written into fileBase.mtsVectors, pin information is read from fileBase.mtsPins. Write file.vector at the end of the file Convert a Rosemary port into an MTS vector and write it into file Record the right information into the test vector [wire: Core.Wire, port: Port] RETURNS [subElements: BOOL _ TRUE, quit: BOOL _ FALSE] Flush out current block if any, write file header & close the file stream. Call back user for each vector that's read in High-level client access to MTS files Build the capture structure from CT properties, NIL means no capture. Setup the right information into the port [wire: Core.Wire, port: Port] RETURNS [subElements: BOOL _ TRUE, quit: BOOL _ FALSE] Conversion between MTS file format and ASCII format Convert base.mtsVectors into base.mtsAscii This version is not definitive... The format may still change, or even be dropped altogether. Convert base.mtsAscii into base.mtsVectors -- NOT YET IMPLEMENTED Test procedure(s) Extension of LogicRosemaryImpl.LogicTest to provide MTS vector capture Extension of LogicRosemaryImpl.LogicTest to provide MTS vector capture Odds and ends Extend source using suffix. Force file to be local is local is TRUE. Read a boolean property from a CT with the specified default Initialization �Κ Ά��– "cedar" style˜�codešœ™Kšœ<™<K™/—K™�K™2K™�šΟk ˜ KšœΕ˜Ε—K˜�KšΠbl œœ˜Kšœ΄˜»Kšœ ˜Kšœ ˜Kšœ œ ˜head™Jšœœ Οc?˜_Kšœœ Ÿ)˜IšœœŸ˜?K˜�Kšœœœ Ÿ.™FšœœœŸ™+Kšœ œŸ™/Kšœ œŸ8™NKšœœŸ2™CKšœœŸ1™EKšœœŸ%™6KšœœœŸ!™4Kšœ™KšœœY™`———™š Οn œœ œœœ˜7K™Kšœ!œ˜'K˜K˜�—šΠnt œœœœœœœ œ˜hKšœ5™5™ Kšœ Ÿ™#K™CK™—K™γKšœ œœœ ˜7Kšœ œœ˜Kšœœ˜ Kš œ œœœœ œœ˜7šœœœ˜Kš œœœ œ œ˜8Kšœ˜—KšœœœŸ˜<KšœœŸ%˜CK˜ š˜š œœ œ˜Kšœ œœ˜!Kšœ#œœ ˜DK˜—š œœ˜šœœ˜Kšœœœ ˜K˜Kšœ ˜—K˜—š  œœœœœ˜7šœ œ"œ˜>Kšœ œ˜'Kšœ˜—K˜—š  œœœœœ˜,šœ˜ Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ-˜4—K˜—š   œœœœ œ˜=Kšœœ˜šœ˜ Kšœ˜Kšœ˜Kšœ˜Kšœ˜šœ˜ Kšœ˜Kšœ ˜ Kšœ˜——Kšœiœ ˜xšœ œœœ˜AKšœ˜Kšœ ˜ Kšœ˜—Kšœ œ œœ˜Qšœ œ œ˜Kšœ˜Kšœ ˜ Kšœ˜—š˜Kšœ˜—K˜—Kšœœ˜Kšœœ˜Kšœ œœ˜Kšœœœ˜K˜ Kšœ œ˜Kšœ œ"œœ Ÿ˜TKšœœœ˜,Kšœœœœ˜JKšœ'˜+šœœ˜šœœŸ˜7Kšœœ˜Kšœœœ˜,K˜—Kšœ<˜<šœŸ˜(Kšœ$˜$Kšœ+˜+Kšœœœ˜,šœœœ˜,Kšœ œ˜Kšœœœ˜,Kšœ˜—K˜——šœœœ˜$K˜6šœ˜Kšœœœ˜,—Kšœ˜K˜—šœœœ˜Kšœœ‰Ÿ%˜·Kšœ œ˜Kšœœ ˜šœœœ˜Kšœœ˜9Kšœœ˜K˜—Kšœ˜—Kšœ˜—Kšœ˜KšœŸ8˜KKšœ œŸ˜5K˜K˜�—š  œœœœœ˜dK™;š œœœ˜-Kšœ œ˜Kšœ#œ œ˜HK˜—š  œœ˜(šœœ&œ˜2K˜JKšœ%œŸ˜FKšœ˜—K˜—Kšœœœ˜K˜šœœ˜K˜Kšœ5˜5K˜šœœ˜Kšœœ*˜2Kšœ,˜,Kšœ(œŸ ˜7Kšœ)˜0—Kšœ˜—K˜/Kšœœœ˜ K˜——™&š  œœœ˜)K™5Kšœœœ˜KšœœœŸ˜AKšœœ˜Kšœ˜Kšœœ˜1K˜K˜�—š  œœœ˜+Kšœ/œ™9K˜Kšœ ˜ J˜Jšœ˜Jšœ˜Jšœ˜K˜K˜�—š  œœœ œ˜;Kš œ&œ˜8Kšœ8œ˜UKš œœœœœ˜IK˜K˜�—š  œœ œ œ˜7Kšœ$Ÿ˜7K˜K˜�—š œœ˜)Kšœœ˜$Kšœœœœ˜:Kšœ˜Kšœ˜Kšœœœœ˜7Kšœ˜Kšœ"˜"Kšœ˜K˜�—š  œœ˜"K™vKšœ œŸ6˜PKšœ œŸ7˜IKšœœŸ ˜7Kšœ œ˜KšœœœŸ˜@KšœŸ˜7š œœœœœ˜5Kšœ/Ÿ˜FKšœ˜—Kšœœ0Ÿ˜XKšœ$˜$Kšœ˜Kšœ˜Kšœ#˜#Kšœ˜Kšœ!Ÿ-˜NKšœŸ0˜FKšœ˜Kšœœœœœœœ˜VKšœŸ˜3Kšœ˜KšœœŸ!˜VKšœ,˜,Kšœ%Ÿ+˜RKšœ"Ÿ˜3Kšœ*Ÿ˜HK˜K˜�—š  œœœ˜.K™1Kšœœœ˜KšœœœŸ˜2Kšœœœ˜:Kšœ)˜+Kšœ˜K˜——™%š œœœœ˜1K™ Kšœ˜Kšœœ˜1K˜K˜�—š œœœœ˜3Kšœ.œ™8K˜K˜Kšœ˜Kšœ˜Kšœ˜K˜ K˜K˜�—š  œœœœ œ˜CKšœœ=™HKšœœœ!˜.Kšœ œ˜šœœœ ˜Kšœœ˜Kšœœ˜&Kšœœ˜Kšœ˜—Kšœ+˜+K˜K˜�—š  œœ˜!K™DKšœœ˜Kšœœ˜Kšœ˜Kš œœ(œœŸ˜PKšœ˜Kšœ˜Kšœ˜KšœœœŸ˜CKšœ˜KšœŸ ˜ Kšœ˜Kšœ œ˜šœŸ˜Kšœ#œ˜(Kšœœ˜ Kšœ œ˜Kšœ œ ˜Kšœ˜KšœœœŸ(˜HKšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kš œœœœœœ˜<Kš œœœœœœ˜6Kšœ˜Kšœ˜Kšœ#˜#Jšœ œ˜"Kšœ˜—Kš œœ"œœŸ˜Rš œœœœ&˜9KšœŸ!˜6Kšœ˜—K˜——™$š  œœœ$œœ˜UK™6Kšœ\™\Kšœœ˜!Kšœ%Οt œ"œ’œ˜\Kšœ8˜8Kš œœœœœŸ˜JKšœœA˜^Kšœ œœœ ˜FKšœ˜Kšœ˜Kšœ,˜,Kšœœ˜,Kš œœœœœ œ˜MKšœœ1œ ˜PKšœ˜KšœŸ4˜DK˜K˜�—š  œœœ˜)K™(Kšœœ˜&Kšœ˜Kšœ4œ%˜_šœœœ˜'Kšœœ˜Kš œœœœ*œ˜IKš œœœœ&œ˜EKš œœœœ&œ˜EKšœ˜—K˜K˜�—š œœœ#˜CKšœ œ™Aš œœ>˜JK™1Kšœ œ&˜7Kšœœ˜ KšœœŸ*˜DKšœœœŸ˜?šœ˜šœ œ˜Kšœœ œ œŸ˜NKšœœ œ œŸ˜OKšœœ œ œŸ ˜SKšœœŸ˜"—Kšœœ œ œŸ˜SKšœœ œ œŸ ˜Všœ ˜ šœœœ˜'Kšœœ œ œŸ˜NKšœœ œ œŸ˜KKšœœ œ œŸ˜NKšœœŸ˜!—šœœ˜Kšœœ œ œŸ˜KKšœœ œ œŸ˜LKšœœ œ œŸ˜LKšœœŸ˜"—K˜—šœ œ˜Kšœœ œ œŸ˜JKšœœ œ œŸ˜KKšœœ œ œŸ ˜SKšœœŸ˜"—KšœœŸ@˜R—Kšœ˜Kšœ'˜'Kšœ!˜!Kšœ!˜!K˜—š œ ˜0Kš œœœœœœ™TKšœœŸ)˜>šœ˜Kšœ"˜"Kšœœœœ ˜3šœ˜š œœœœœ˜?Kšœ(˜(Kš˜—š œœœœ˜$Kšœ$˜$Kš˜—K˜—šœ˜š œœœœœ˜?Kšœœ œœ˜9Kš˜—š œœœœ˜$Kšœœ œœ ˜5Kš˜—K˜—KšœœŸ˜ KšœœŸ˜!KšœœŸ˜ KšœœŸ˜8KšœœŸ˜%—K˜—K˜K˜)š œœœœŸ(˜KKšœœœ˜%Kšœœœ˜"Kšœœœ˜!Kšœ˜—KšœBŸ˜SKšœŸ˜(K˜K˜�—š œœœ˜#K™JKšœœ˜Kšœœ%˜@Kšœ˜KšœŸ˜:šœœœŸ˜5Kšœœœ˜%Kšœ œ˜$Kšœ˜—KšœœœŸ9˜NKšœ˜K˜K˜�—š œ œ$œœ ˜WK™-š œœœœ˜&Kšœœ%˜,Kšœ˜K˜—Kšœœ˜Kšœ œ˜%Kšœœœ"˜1Kšœ œ%˜5Kšœœ1œ ˜OKšœ˜Kš œ œœœŸ ˜=šœœœŸY˜jKšœ8˜8Kš œœœœŸ ˜CK˜—Kšœœ˜,Kš œœœœœ œ˜MKšœ!˜!šœ˜šœ.œ˜6Kšœ œœ4˜HKšœœœŸ˜Cšœœœ˜'Kšœœœ˜'Kšœœ˜1Kšœ˜—Kšœ˜Kšœ˜—šœœœ˜'Kšœœ˜Kš œœœœ%œ˜DKš œœœœ!œ˜@Kš œœœœ!œ˜@Kšœ˜—KšœŸ˜#Kšœ˜—Kš œœœœŸ˜DK˜——™%š  œ œœ˜MKšœ!œ œ™Eš  œœ œœœ˜>Kšœ;œ ˜IK˜—Kšœœœ,˜JKšœœ˜$JšœC˜CK˜$Kšœ˜Jšœ œ˜Kšœœœœ/˜HKšœœœ$˜KKšœœX˜jKšœ8œœ+˜pKšœD˜DKšœœœ0˜TKšœ*œ˜0Kšœ\˜\Kšœ,˜,K˜K˜�—š œœœ˜3šœ œœ˜Kšœ:˜:Kšœ3˜3K˜—K˜K˜�—š  œ œ˜0Kšœ œœ˜+K˜K˜�—š œ œ0œ˜Pš œœ˜*š œœœ(˜NK™)Kšœ œ+˜<šœœ˜KšœŸ˜'Kšœ˜—šœ˜Kšœœ˜#KšœœŸ*˜DKšœ'˜'Kšœ!˜!Kšœ!˜!šœ˜šœ˜ KšœœŸ˜(KšœŸ˜ —š˜KšœœŸ˜(KšœŸ˜ ——š˜šœ˜ Kš œœœœœ Ÿ˜JKšœŸ˜"—š˜KšœœŸ˜)KšœŸ˜"——Kšœ˜—K˜—š œ ˜0Kš œœœœœœ™TKšœ œ+˜<Kš œœœœœ˜,KšœœŸ)˜>šœ˜Kšœ$˜$šœ˜Kšœ˜Kš œ œœœœœœ˜DKšœ˜—šœ˜K˜š œœœœœ˜?Kšœ*˜*Kš˜—š œœœœ˜$Kšœ#˜#Kšœœ ˜Kš œœ œœŸ ˜?Kš˜—K˜—šœ˜K˜#š œœœœœ˜?Kšœ#˜#Kš œ œœœœœœ˜EKš˜—š œœœœ˜$Kšœ˜Kš œ œœœœœœ˜EKšœœ ˜Kš œœ œœŸ ˜?Kš˜—K˜—KšœœŸ˜ KšœœŸ˜!KšœœŸ˜ KšœœŸ˜8KšœœŸ˜%—K˜—Kšœ9Ÿ˜HKšœ ˜ K˜—Kšœœ˜$šœœ˜Kšœ=˜=Kšœ˜Kšœ˜—Kšœ'˜'K˜——šœœœ™3š œœœœ˜7Kšœœ œ œ˜LKšœ!˜!Kšœ!˜!Kšœœ œ ˜6Kšœ!˜!š œœœœœ˜>K˜Kšœœ˜*Kšœœœ˜-šœ˜Jš œœœœœ ˜gKšœœœ˜.Kšœ˜Kšœ˜—Kšœ ˜ Kšœ˜—Kšœ!˜!Kšœœ œ ˜9Kšœ!˜!K˜K˜�—š  œœœ œ˜-Kšœ*™*JšΟb]™]š  œœ˜!Kšœ œ˜0Kšœœ˜Kšœœ ˜"šœœœ˜'Kšœœ˜šœœ ˜!Kšœ$˜$KšœœŸ*˜DKšœœ˜Kšœ#˜#Kšœ˜Kšœ˜šœ˜šœ˜ Kšœœœ˜—š˜Kšœœœ˜——š˜šœ˜ Kšœœœ˜—š˜Kšœœœ˜ ——Kšœ˜Kš œ œ œ œœ˜@Kšœ˜—Kšœ˜Kšœ˜—Kšœœ ˜"Kšœ ˜ K˜—Jšœ œœ˜J˜3Jšœœœ>˜LJšœœ˜Jšœ ˜Jšœ8œ˜?K˜K˜�—š  œœœ œ˜-Kšœ*™*Kšœ˜KšŸΠbc™K˜——™š  œœ˜.KšœF™FKšœ œ9˜GKšœœœ˜1Kšœ+˜+Kšœœ˜Kšœ˜š˜š œœœœœ˜BKšœ>œ œ˜]Kšœ˜—Jšœœ˜$Kšœ œœ˜9Jšœ œœ˜9Kšœ˜Kšœ˜—Kšœ œœ˜+K˜K˜�—š  œœ˜-Kšœ4œ™Fš  œœ˜Kšœ œœ˜9Kšœ œœ˜9K˜—Kšœœœ˜1Kšœ"˜"K˜——™ š  œœœœ œœ œ˜cKšœ?œ™DKšœœ˜Kšœ!œ˜&Kšœœ˜%Kšœ:˜:Kšœ1˜1Kšœ=˜=Kšœ4˜4šœœŸ1˜MKšœœ œ˜Kšœ˜—Kšœœœœ˜@K˜K˜�—š  œœœœ˜EKšœ œ0˜@Kšœœ'˜DK˜K˜�—š  œœœ œœœ˜OKšœœ™<Kšœœœœ,˜AKš œœœœ œ˜)K˜——™Kšœ:˜:Kšœ8˜8—K˜�K˜�Kšœ˜—�…—����aT��0��