<> <> <> <<>> DIRECTORY Basics, CardTab, Convert, FS, GList, IO, Rope, Sage, SymTab; SageImpl: CEDAR PROGRAM IMPORTS Basics, CardTab, Convert, FS, GList, IO, Rope, SymTab EXPORTS Sage = BEGIN OPEN Sage; Error: PUBLIC ERROR [msg: ROPE _ NIL] = CODE; <> ParseData: TYPE = REF ParseDataRec; ParseDataRec: TYPE = RECORD [ streams: LIST OF IO.STREAM, names: LIST OF ROPE, noticeCR: BOOL, token: ROPE, char: CHAR, kind: IO.TokenKind, charsSkipped: INT]; ParseKeywordProc: TYPE = PROC [pd: ParseData, program: Programming, nextReserved: ROPE]; Parse: PUBLIC PROC [file: ROPE] RETURNS [program: Programming] = { keywords: LIST OF ROPE _ LIST ["TimingGroups", "NameDutPinTimingGroup", "DutPinFixturePin", "FixturePinChipChannel", "ColumnNames", "Vectors", "End"]; keywordProcs: LIST OF ParseKeywordProc _ LIST[TimingGroups, NameDutPinTimingGroup, DutPinFixturePin, FixturePinChipChannel, ColumnNames, Vectors]; pd: ParseData _ NEW[ParseDataRec]; pd.streams _ LIST[FS.StreamOpen[file]]; pd.names _ LIST[file]; pd.noticeCR _ FALSE; program _ NEW[ProgrammingRec]; program.timingGroups _ SymTab.Create[]; program.nameDutPinTimingGroup _ SymTab.Create[]; program.dutPinFixturePin _ CardTab.Create[]; program.fixturePinChipChannel _ CardTab.Create[]; program.columnNames _ CardTab.Create[]; ParseToken[pd]; -- prime the pump DO IF pd.kind#tokenID THEN ERROR Error["Missing keyword"]; IF NOT Rope.Equal[pd.token, keywords.first, FALSE] THEN ERROR Error["Unknown keyword"]; keywordProcs.first[pd, program, keywords.rest.first]; keywords _ keywords.rest; keywordProcs _ keywordProcs.rest; IF keywordProcs=NIL THEN EXIT; ENDLOOP; }; TimingGroups: ParseKeywordProc = { ParseToken[pd]; DO name: ROPE _ NIL; group: TimingGroup _ NEW[TimingGroupRec]; IF (name _ ParseName[pd, nextReserved])=NIL THEN EXIT; <> IF pd.kind#tokenID THEN ERROR Error["Expecting timing group format"]; group.format _ SELECT TRUE FROM Rope.Equal[pd.token, "NRZ", FALSE] => NRZ, Rope.Equal[pd.token, "RZ", FALSE] => RZ, Rope.Equal[pd.token, "RO", FALSE] => RO, Rope.Equal[pd.token, "RC", FALSE] => RC, Rope.Equal[pd.token, "RT", FALSE] => RT, ENDCASE => ERROR Error["Unknown timing group format"]; ParseToken[pd]; group.delay _ ParseNs[pd]; group.width _ ParseNs[pd]; group.sample _ ParseNs[pd]; group.variableThreshold _ ParseBool[pd]; group.variableLevels _ ParseBool[pd]; ParseSeperator[pd]; IF NOT SymTab.Store[program.timingGroups, name, group] THEN ERROR Error["Multiply defined timing group"]; ENDLOOP; }; NameDutPinTimingGroup: ParseKeywordProc = { ParseToken[pd]; DO name: ROPE _ NIL; group: DutPinTimingGroup _ NEW[DutPinTimingGroupRec]; groups: DutPinTimingGroups; IF (name _ ParseName[pd, nextReserved])=NIL THEN EXIT; IF pd.kind=tokenSINGLE THEN { IF pd.char#'[ THEN ERROR Error["Bad bus index"]; ParseToken[pd]; group.index _ ParseNumber[pd]; IF pd.char#'] THEN ERROR Error["Bad bus index"]; ParseToken[pd]; } ELSE group.index _ 0; group.dutPin _ ParseNumber[pd]; group.timingGroup _ ParseName[pd]; ParseSeperator[pd]; groups _ NARROW[SymTab.Fetch[program.nameDutPinTimingGroup, name].val]; FOR s: DutPinTimingGroups _ groups, s.rest UNTIL s=NIL DO IF s.first.index=group.index THEN ERROR Error["Multiply defined signal"]; ENDLOOP; groups _ CONS[group, groups]; [] _ SymTab.Store[program.nameDutPinTimingGroup, name, groups]; ENDLOOP; }; DutPinFixturePin: ParseKeywordProc = { ParseToken[pd]; DO dutPin: CARD _ 0; fixturePin: REF CARD _ NEW[CARD]; IF pd.kind#tokenDECIMAL THEN EXIT; dutPin _ ParseNumber[pd]; fixturePin^ _ ParseNumber[pd]; ParseSeperator[pd]; IF NOT CardTab.Store[program.dutPinFixturePin, dutPin, fixturePin] THEN ERROR Error["Multiply defined Dut Pin"]; ENDLOOP; }; FixturePinChipChannel: ParseKeywordProc = { ParseToken[pd]; DO fixturePin: CARD _ 0; chipChannel: ChannelAddress _ NEW[ChannelAddressRec]; IF pd.kind#tokenDECIMAL THEN EXIT; fixturePin _ ParseNumber[pd]; chipChannel.chip _ ParseNumber[pd]; chipChannel.channel _ ParseNumber[pd]; ParseSeperator[pd]; IF NOT CardTab.Store[program.fixturePinChipChannel, fixturePin, chipChannel] THEN ERROR Error["Multiply defined Fixture Pin"]; ENDLOOP; }; ColumnNames: ParseKeywordProc = { position: CARD _ 0; pd.noticeCR _ TRUE; ParseToken[pd]; DO SELECT pd.kind FROM tokenSINGLE => { IF pd.char#IO.CR THEN ERROR Error["Expecting carriage return in ColumnNames"]; position _ 0; ParseToken[pd]; }; tokenID => { name: ROPE; IF Rope.Equal[pd.token, nextReserved, FALSE] THEN EXIT; IF Rope.Length[pd.token]#1 THEN ERROR Error["Expecting only one character in ColumnNames"]; position _ position + pd.charsSkipped + 1; name _ NARROW[CardTab.Fetch[program.columnNames, position].val]; name _ Rope.Concat[name, Rope.FromChar[pd.char]]; [] _ CardTab.Store[program.columnNames, position, name]; ParseToken[pd]; }; ENDCASE => ERROR Error["Expecting character in ColumnNames"]; ENDLOOP; pd.noticeCR _ FALSE; }; Vectors: ParseKeywordProc = { ConsVector: PROC = { IF triples#NIL THEN { triplesLength: CARD _ GList.Length[triples]; vectors _ CONS[NEW[ParsedVectorRec[triplesLength]], vectors]; FOR index: CARD DECREASING IN [0..triplesLength) DO vectors.first[index] _ triples.first; triples _ triples.rest; ENDLOOP; }; }; vectors: LIST OF ParsedVector _ NIL; triples: LIST OF ParsedVectorTriple _ NIL; position: CARD _ 0; vectorLength: CARD _ 0; pd.noticeCR _ TRUE; ParseToken[pd]; DO SELECT pd.kind FROM tokenSINGLE => { IF pd.char#IO.CR THEN ERROR Error["Expecting carriage return in Vectors"]; position _ 1; ParseToken[pd]; ConsVector[]; }; tokenDECIMAL, tokenID => { triple: ParsedVectorTriple; skip: CARD; size: CARD; IF Rope.Equal[pd.token, nextReserved, FALSE] THEN EXIT; triple _ NEW[ParsedVectorTripleRec]; skip _ pd.charsSkipped + Rope.Length[pd.token]; size _ VectorColumnToBusSize[program, position + pd.charsSkipped]; triple.value _ ConvertBoolSequence[pd.token, size]; ParseToken[pd]; position _ position + skip; [triple.inhibit, skip] _ ParseBoolSequence[pd, size]; position _ position + skip; [triple.mask, skip] _ ParseBoolSequence[pd, size]; position _ position + skip; triples _ CONS[triple, triples]; }; ENDCASE => ERROR Error["Unknown token type in Vectors"]; ENDLOOP; pd.noticeCR _ FALSE; ConsVector[]; vectorLength _ GList.Length[vectors]; program.vectors _ NEW[ParsedVectorsRec[vectorLength]]; FOR index: CARD DECREASING IN [0..vectorLength) DO program.vectors[index] _ vectors.first; vectors _ vectors.rest; ENDLOOP; }; VectorColumnToBusSize: PROC [program: Programming, column: CARD] RETURNS [size: CARD] = { name: ROPE _ NARROW[CardTab.Fetch[program.columnNames, column].val]; groups: DutPinTimingGroups; max: CARD _ 0; min: CARD _ LAST[CARD]; IF name=NIL THEN ERROR Error["Some column is not properly named"]; groups _ NARROW[SymTab.Fetch[program.nameDutPinTimingGroup, name].val]; IF groups=NIL THEN ERROR Error["Some signal has vectors but is not defined in NameDutPinTimingGroup"]; UNTIL groups=NIL DO max _ MAX[groups.first.index, max]; min _ MIN[groups.first.index, min]; groups _ groups.rest; ENDLOOP; size _ max-min+1; }; ParseBool: PROC [pd: ParseData] RETURNS [bool: BOOL] = { IF pd.kind#tokenID THEN ERROR Error["Expecting boolean"]; IF Rope.Length[pd.token]#1 THEN ERROR Error["Expecting only T or F for boolean"]; bool _ SELECT pd.char FROM 'T, 't => TRUE, 'F, 'f => FALSE, ENDCASE => ERROR Error["Expecting only T or F for boolean"]; ParseToken[pd]; }; ParseBoolSequence: PROC [pd: ParseData, size: CARD] RETURNS [bools: BoolSequence, skip: CARD] = { IF NOT (pd.kind=tokenDECIMAL OR pd.kind=tokenID) THEN ERROR Error["Expecting hexadecimal number in Vectors"]; bools _ ConvertBoolSequence[pd.token, size]; ParseToken[pd]; skip _ pd.charsSkipped + Rope.Length[pd.token]; }; ConvertBoolSequence: PROC [token: ROPE, size: CARD] RETURNS [bools: BoolSequence] = { boolIndex: CARD _ size; bools _ NEW[BoolSequenceRec[size]]; FOR index: CARD DECREASING IN [0..LOOPHOLE[Rope.Length[token]]) DO char: CHAR _ Rope.Fetch[token, index]; charValue: CARD _ SELECT TRUE FROM char IN ['0..'9] => char-'0, char IN ['a..'f] => char-'a+10, char IN ['A..'F] => char-'A+10, ENDCASE => ERROR Error["Bad hexadecimal digit in Vectors"]; THROUGH [1..4] DO boolIndex _ boolIndex - 1; bools[boolIndex] _ IF charValue MOD 2=1 THEN TRUE ELSE FALSE; IF boolIndex=0 THEN EXIT; charValue _ charValue / 2; ENDLOOP; IF boolIndex=0 AND index#0 THEN ERROR Error["Too many digits"]; ENDLOOP; }; ParseName: PROC [pd: ParseData, nextReserved: ROPE _ NIL] RETURNS [name: ROPE _ NIL] = { IF pd.kind#tokenID THEN ERROR Error["Expecting name or keyword"]; IF nextReserved#NIL AND Rope.Equal[pd.token, nextReserved, FALSE] THEN RETURN; name _ pd.token; ParseToken[pd]; }; ParseNumber, ParseNs: PROC [pd: ParseData] RETURNS [ns: Ns] = { IF pd.kind#tokenDECIMAL THEN ERROR Error["Expecting number"]; ns _ Convert.CardFromRope[pd.token]; ParseToken[pd]; }; ParseSeperator: PROC [pd: ParseData] = { IF pd.kind#tokenSINGLE THEN ERROR Error["Expecting seperator"]; IF pd.char#'; THEN ERROR Error["Bad seperator"]; ParseToken[pd]; }; ParseToken: PROC [pd: ParseData] = { DO { ParseOneToken[pd ! IO.EndOfStream => GOTO PopFile]; EXIT; EXITS PopFile => { IO.Close[pd.streams.first]; pd.streams _ pd.streams.rest; pd.names _ pd.names.rest; IF pd.streams=NIL THEN ERROR; -- more pops than includes? LOOP; }; }; ENDLOOP; }; ParseOneToken: PROC [pd: ParseData] = { ParseOnlyOneToken[pd]; IF pd.kind=tokenID AND Rope.Equal[pd.token, "Include", FALSE] THEN { file: ROPE; [] _ IO.SkipWhitespace[pd.streams.first ! IO.EndOfStream => GOTO BadInclude]; file _ IO.GetLineRope[pd.streams.first ! IO.EndOfStream => GOTO BadInclude]; pd.streams _ CONS[FS.StreamOpen[file], pd.streams]; pd.names _ CONS[file, pd.names]; ParseOnlyOneToken[pd]; EXITS BadInclude => ERROR Error["No file name for include"]; }; }; ParseOnlyOneToken: PROC [pd: ParseData] = { [pd.token, pd.charsSkipped] _ IO.GetTokenRope[pd.streams.first, IF pd.noticeCR THEN BreaksCR ELSE Breaks]; pd.char _ Rope.Fetch[pd.token]; pd.kind _ SELECT TRUE FROM Rope.Length[pd.token]=1 AND (IF pd.noticeCR THEN BreaksCR ELSE Breaks)[pd.char]=break => tokenSINGLE, pd.char IN ['0..'9] => tokenDECIMAL, ENDCASE => tokenID; }; BreaksCR: IO.BreakProc = { RETURN[SELECT char FROM IO.CR => break, IN [IO.NUL .. IO.SP] => sepr, '[, '], ';, '. => break, IN ['0..'9], IN ['a..'z], IN ['A..'Z] => other, ENDCASE => ERROR Error["Illegal character"]]; }; Breaks: IO.BreakProc = { RETURN[SELECT char FROM IN [IO.NUL .. IO.SP] => sepr, '[, '], ';, '. => break, IN ['0..'9], IN ['a..'z], IN ['A..'Z] => other, ENDCASE => ERROR Error["Illegal character"]]; }; <> historyDepth: CARD = 64; HistoryIndex: TYPE = [0..historyDepth); DoubleByte: TYPE = RECORD [first, second: Byte10]; ColumnNameList: TYPE = LIST OF ColumnName; ColumnName: TYPE = REF ColumnNameRec; ColumnNameRec: TYPE = RECORD [ column: CARD, name: ROPE]; TripleSequence: TYPE = REF TripleSequenceRec; TripleSequenceRec: TYPE = RECORD [elements: SEQUENCE size: CARDINAL OF ChipTripleSequence]; -- chip ChipTripleSequence: TYPE = ARRAY ChannelIndex OF TripleSequenceIndex; TripleSequenceIndex: TYPE = RECORD [ assigned: BOOL, triple: CARDINAL, sequence: CARDINAL]; Transform: PUBLIC PROC [program: Programming] RETURNS [fixture: Fixture] = { FindMaxChip: CardTab.EachPairAction = { ca: ChannelAddress _ NARROW[val]; chips _ MAX[ca.chip+1, chips]; }; BuildColumnNames: CardTab.EachPairAction = { columnNames _ CONS[NEW[ColumnNameRec], columnNames]; columnNames.first.column _ key; columnNames.first.name _ NARROW[val]; }; CompareColumns: GList.CompareProc = { first: ColumnName _ NARROW[ref1]; second: ColumnName _ NARROW[ref2]; RETURN[Basics.CompareCard[first.column, second.column]]; }; FillFormatTiming: SymTab.EachPairAction = { FOR groups: DutPinTimingGroups _ NARROW[val], groups.rest UNTIL groups=NIL DO group: DutPinTimingGroup _ groups.first; fixturePin: REF CARD _ NARROW[CardTab.Fetch[program.dutPinFixturePin, group.dutPin].val]; chipChannel: ChannelAddress; timingGroup: TimingGroup; triplet: CARD _ 0; IF fixturePin=NIL THEN ERROR Error["No fixture pin assigned for dut pin"]; chipChannel _ NARROW[CardTab.Fetch[program.fixturePinChipChannel, fixturePin^].val]; IF chipChannel=NIL THEN ERROR Error["No chip and channel assigned for fixture pin"]; timingGroup _ NARROW[SymTab.Fetch[program.timingGroups, group.timingGroup].val]; IF timingGroup=NIL THEN ERROR; fixture[chipChannel.chip].formatTiming[chipChannel.channel] _ timingGroup; FOR names: ColumnNameList _ columnNames, names.rest UNTIL names=NIL DO IF Rope.Equal[names.first.name, key] THEN EXIT; triplet _ triplet + 1; REPEAT FINISHED => ERROR; ENDLOOP; map[chipChannel.chip][chipChannel.channel] _ [TRUE, triplet, group.index]; ENDLOOP; }; chips: CARD _ 0; map: TripleSequence; columnNames: ColumnNameList _ NIL; [] _ CardTab.Pairs[program.fixturePinChipChannel, FindMaxChip]; fixture _ AllocateFixture[chips]; map _ NEW[TripleSequenceRec[chips]]; FOR chipIndex: CARD IN [0..chips) DO FOR channelIndex: CARD IN [0..channelsPerChip) DO map[chipIndex][channelIndex] _ [FALSE, 0, 0]; ENDLOOP; ENDLOOP; [] _ CardTab.Pairs[program.columnNames, BuildColumnNames]; columnNames _ NARROW[GList.Sort[columnNames, CompareColumns]]; [] _ SymTab.Pairs[program.nameDutPinTimingGroup, FillFormatTiming]; FOR chipIndex: CARD IN [0..chips) DO FillMaskInhibitVector[program, fixture[chipIndex], map[chipIndex]]; ENDLOOP; }; AllocateFixture: PROC [chips: CARD] RETURNS [fixture: Fixture] = { fixture _ NEW[FixtureRec[chips]]; FOR chipIndex: CARD IN [0..chips) DO chip: Chip _ NEW[ChipRec[vectorAddressesPerChip]]; fixture[chipIndex] _ chip; FOR channel: ChannelIndex IN ChannelIndex DO FOR imIndex: InhibitMaskIndex IN InhibitMaskIndex DO chip.inhibitMask[imIndex][channel].inhibit _ FALSE; chip.inhibitMask[imIndex][channel].mask _ FALSE; ENDLOOP; ENDLOOP; FOR vectorIndex: CARD IN [0..vectorAddressesPerChip) DO FOR byteIndex: ByteIndex IN ByteIndex DO chip.vectors[vectorIndex][byteIndex] _ 0; ENDLOOP; ENDLOOP; ENDLOOP; }; FillMaskInhibitVector: PROC [program: Programming, chip: Chip, map: ChipTripleSequence] = { history: ARRAY HistoryIndex OF CARD32; FOR historyIndex: HistoryIndex IN HistoryIndex DO history[historyIndex] _ 0; ENDLOOP; FOR cycle: CARD IN [0..program.vectors.size) DO current: CARD32 _ 0; vector: ParsedVector _ program[cycle]; FOR channelIndex: ChannelIndex IN ChannelIndex DO tsi: TripleSequenceIndex _ map[channelIndex]; current _ Basics.BITSHIFT[current, 1]; IF map[channelIndex].assigned THEN IF vector[tsi.triple][tsi.sequence] THEN current _ Basics.BITOR[current, 1]; ENDLOOP; ENDLOOP; }; <> Pickle: PUBLIC PROC [fixture: Fixture, file: ROPE] = { }; Unpickle: PUBLIC PROC [file: ROPE] RETURNS [fixture: Fixture] = { }; END.