TestarossaRuntimeImpl.mesa
Copyright Ó 1985, 1987 by Xerox Corporation. All rights reserved.
Barth, October 18, 1988 11:57:28 am PDT
DIRECTORY Atom, Basics, BitOps, CardTab, Convert, FS, GList, GPIB, IO, Process, Rope, RProperties, SymTab, TerminalIO, TestarossaRuntime, XBus;
TestarossaRuntimeImpl: CEDAR PROGRAM
IMPORTS Atom, Basics, BitOps, CardTab, Convert, FS, GList, GPIB, IO, Process, Rope, RProperties, SymTab, TerminalIO, XBus
EXPORTS TestarossaRuntime =
BEGIN OPEN TestarossaRuntime;
Error: PUBLIC ERROR [msg: ROPENIL] = CODE;
Parse
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 ROPELIST ["Parameters", "TimingGroups", "NameDutPinTimingGroup", "DutPinFixturePin", "FixturePinChipChannel", "ColumnNames", "Vectors", "End"];
keywordProcs: LIST OF ParseKeywordProc ← LIST[Parameters, 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[];
program.targetAddress ← 0;
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;
};
Parameters: ParseKeywordProc = {
ParseToken[pd];
DO
name: ROPE ← ParseName[pd, nextReserved];
value: REF ANY;
IF name=NIL THEN EXIT;
IF Rope.Fetch[pd.token] IN ['0..'9] THEN value ← NEW[INT ← Convert.IntFromRope[pd.token]]
ELSE value ← pd.token;
program.properties ← RProperties.PutProp[program.properties, Atom.MakeAtom[name], value];
ParseToken[pd];
ParseSeperator[pd];
ENDLOOP;
};
TimingGroups: ParseKeywordProc = {
ParseToken[pd];
DO
name: ROPENIL;
group: TimingGroup ← NEW[TimingGroupRec];
IF (name ← ParseName[pd, nextReserved])=NIL THEN EXIT;
Format
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: ROPENIL;
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 CARDNEW[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;
vectorLength ← vectorLength + 1;
};
};
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 => {
SELECT pd.char FROM
IO.CR => {
position ← 1;
ParseToken[pd];
ConsVector[];
};
'* => {
position ← position + pd.charsSkipped + 1;
ParseToken[pd];
program.targetAddress ← vectorLength;
};
ENDCASE => ERROR Error["Expecting carriage return in Vectors"];
};
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[];
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: ROPENARROW[CardTab.Fetch[program.columnNames, column].val];
groups: DutPinTimingGroups;
max: CARD ← 0;
min: CARDLAST[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: CARDSELECT 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: ROPENIL] RETURNS [name: ROPENIL] = {
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"]];
};
Transform
debug: BOOLTRUE;
minCopyLength: CARDINAL = 3;
minCopyLengthNoLiteral: CARDINAL = 2;
maxCopyLength: CARDINAL = 15;
maxLiteralLength: CARDINAL = 64;
bufferSize: CARD = 64;
BufferIndex: TYPE = [0..bufferSize);
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 CARDNARROW[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];
fixture.properties ← program.properties;
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
IF debug THEN TerminalIO.PutF["\nchip: %g", IO.int[chipIndex]];
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
chip.formatTiming[channel] ← NIL;
FOR imIndex: InhibitMaskIndex IN InhibitMaskIndex DO
chip.inhibitMask[imIndex][channel].inhibit ← FALSE;
chip.inhibitMask[imIndex][channel].mask ← FALSE;
ENDLOOP;
ENDLOOP;
chip.targetAddress ← 0;
chip.matchAddress ← vectorAddressesPerChip;
chip.firstFreeVector ← 0;
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] = {
LiteralLength: PROC RETURNS [length: NAT] = {
length ← LOOPHOLE[nextInsert-copyLength-literalPosition, CARDINAL] MOD bufferSize;
};
EmitLiteral: PROC [length: NAT] = {
IF length>0 THEN {
IF debug THEN TerminalIO.PutF["\nLit(%g):", IO.card[length]];
EmitByte10[IF length=maxLiteralLength THEN 0 ELSE length];
FOR index: NAT IN [0..length) DO
vector: BitOps.BitDWord ← buffer[literalPosition+index];
IF debug THEN TerminalIO.PutF[" %x", IO.int[LOOPHOLE[vector]]];
EmitByte10[BitOps.ECFD[vector, 0, 10, 20]];
EmitByte10[BitOps.ECFD[vector, 10, 10, 20]];
ENDLOOP;
literalPosition ← (literalPosition+length) MOD bufferSize
};
};
EmitCopy: PROC = {
IF copyLength>0 THEN {
position: NATLOOPHOLE[matchPosition+1-copyLength, CARDINAL] MOD bufferSize;
IF debug THEN TerminalIO.PutF["\nCopy l:%g, p: %g", IO.card[copyLength], IO.card[position]];
EmitByte10[BitOps.WordOR[BitOps.WordShift[copyLength, 6], position]];
copyLength ← 0;
literalPosition ← nextInsert;
};
};
EmitByte10: PROC [byte: Byte10] = {
IF chip.firstFreeVector=vectorAddressesPerChip AND firstFreeByte=bytesPerAddress THEN ERROR Error["Not enough storage for this vector set"];
chip.vectors[chip.firstFreeVector][firstFreeByte] ← byte;
firstFreeByte ← firstFreeByte + 1;
IF firstFreeByte=bytesPerAddress THEN {
firstFreeByte ← 0;
chip.firstFreeVector ← chip.firstFreeVector + 1;
};
};
AlignCodeWords: PROC [dontModifyBefore: CARDINAL] = {
PushDown: PROC [byteCount: CARDINAL] = {
IF byteCount>0 THEN {
getWord: BufferIndex ← chip.firstFreeVector;
getByte: ByteIndex ← firstFreeByte;
putWord: BufferIndex ← chip.firstFreeVector + ((byteCount + firstFreeByte) / bytesPerAddress);
putByte: ByteIndex ← (firstFreeByte + byteCount) MOD bytesPerAddress;
IF putWord>vectorAddressesPerChip OR (putWord=vectorAddressesPerChip AND putByte>0) THEN ERROR; -- should never happen since padding an existing word
firstFreeByte ← putByte;
chip.firstFreeVector ← putWord;
UNTIL getByte=getByteIndex AND getWord=getWordIndex DO
IF getByte=0 THEN {
getByte ← bytesPerAddress - 1;
getWord ← getWord - 1;
}
ELSE getByte ← getByte - 1;
IF putByte=0 THEN {
putByte ← bytesPerAddress - 1;
putWord ← putWord - 1;
}
ELSE putByte ← putByte - 1;
chip.vectors[putWord][putByte] ← chip.vectors[getWord][getByte];
ENDLOOP;
getByteIndex ← putByte;
getWordIndex ← putWord;
};
};
GetByte: PROC RETURNS [byte: Byte10] = {
IF getWordIndex=chip.firstFreeVector AND getByteIndex=firstFreeByte THEN ERROR Error["Couldn't align code words"];
byte ← chip.vectors[getWordIndex][getByteIndex];
getByteIndex ← (getByteIndex + 1) MOD bytesPerAddress;
IF getByteIndex=0 THEN getWordIndex ← getWordIndex + 1;
};
PutByte: PROC [byte: Byte10] = {
chip.vectors[putWordIndex][putByteIndex] ← byte;
putByteIndex ← (putByteIndex + 1) MOD bytesPerAddress;
IF putByteIndex=0 THEN putWordIndex ← putWordIndex + 1;
};
putWordIndex: BufferIndex ← dontModifyBefore;
getWordIndex: BufferIndex ← dontModifyBefore;
putByteIndex: ByteIndex ← 0;
getByteIndex: ByteIndex ← 0;
IF copyLength < minCopyLength THEN {
litLen: NAT;
copyLength ← 0;
litLen ← LiteralLength[];
IF litLen # 0 THEN EmitLiteral[litLen];
}
ELSE EmitCopy[];
WHILE firstFreeByte>0 DO
entryDelta: CARDINAL ← bytesPerAddress-firstFreeByte;
The following horrible hack is used to align the pretarget block when it cannot be aligned any other way.
IF dontModifyBefore=0 AND getWordIndex=chip.firstFreeVector AND getByteIndex=firstFreeByte THEN {
byte: Byte10;
getByteIndex ← 0;
getWordIndex ← 0;
byte ← GetByte[];
IF byte>=maxLiteralLength THEN ERROR; -- first not literal
PushDown[entryDelta+4];
putWordIndex ← 0;
putByteIndex ← 0;
PutByte[1]; -- Lit 1
PutByte[GetByte[]]; -- first vector byte
PutByte[GetByte[]]; -- second vector byte
THROUGH [0..entryDelta+1) DO
PutByte[BitOps.ICIW[0, BitOps.ICIW[1, 0, 0, 4, 10], 4, 6, 10]];
ENDLOOP;
PutByte[byte]; -- restore literal
IF firstFreeByte#0 THEN ERROR;
}
ELSE {
byte: Byte10;
putByteIndex ← getByteIndex;
putWordIndex ← getWordIndex;
byte ← GetByte[];
IF byte<maxLiteralLength THEN {
newLiteral: CARDINAL;
IF byte=0 THEN byte ← maxLiteralLength;
entryDelta ← MIN[entryDelta, byte-1];
PushDown[entryDelta];
THROUGH [0..entryDelta) DO
PutByte[1]; -- Lit 1
PutByte[GetByte[]]; -- first vector byte
PutByte[GetByte[]]; -- second vector byte
ENDLOOP;
newLiteral ← byte-entryDelta;
PutByte[newLiteral]; -- restore modified literal
getWordIndex ← getWordIndex + ((2*newLiteral + getByteIndex) / bytesPerAddress);
getByteIndex ← (getByteIndex + 2*newLiteral) MOD bytesPerAddress;
}
ELSE {
length: CARDINAL ← BitOps.ECFW[byte, 0, 4, 10];
position: CARDINAL ← BitOps.ECFW[byte, 4, 6, 10];
entryDelta ← MIN[entryDelta, length-1];
PushDown[entryDelta];
THROUGH [0..entryDelta) DO
PutByte[BitOps.ICIW[position, BitOps.ICIW[1, 0, 0, 4, 10], 4, 6, 10]];
position ← (position + 1) MOD bufferSize;
ENDLOOP;
PutByte[BitOps.ICIW[position, BitOps.ICIW[length-entryDelta, 0, 0, 4, 10], 4, 6, 10]]; -- restore modified copy
};
};
ENDLOOP;
};
firstFreeInhibitMask: CARDINAL ← 0;
firstFreeByte: CARDINAL ← 0;
inhibitMaskIndex: InhibitMaskIndex;
buffer: ARRAY BufferIndex OF BitOps.BitDWord ← ALL[BitOps.BitDWordZero];
matchBits: ARRAY BufferIndex OF BOOLALL[FALSE];
literalPosition: CARDINAL ← 0;
matchPosition: CARDINAL ← 0;
copyLength: CARDINAL ← 0;
nextInsert: CARDINAL ← 0;
FOR cycle: CARD IN [0..program.vectors.size) DO
Match: PROC [ignore: BOOL] RETURNS [match: BOOLFALSE] = {
address: NAT ← nextInsert;
previousMatch: BOOL ← matchBits[nextInsert];
DO
nextMatch: BOOL;
address ← (address + 1) MOD bufferSize;
nextMatch ← matchBits[address];
matchBits[address] ← value=buffer[address] AND (previousMatch OR ignore);
IF address=nextInsert THEN EXIT;
previousMatch ← nextMatch;
ENDLOOP;
DO
IF matchBits[address] THEN {
match ← TRUE;
matchPosition ← address;
EXIT;
};
address ← IF address=0 THEN bufferSize-1 ELSE address - 1;
IF address=nextInsert THEN EXIT;
ENDLOOP;
};
value: BitOps.BitDWord ← BitOps.BitDWordZero;
inhibitMask: InhibitMaskArray ← ALL[[FALSE, FALSE]];
vector: ParsedVector ← program.vectors[cycle];
IF cycle=program.targetAddress THEN {
AlignCodeWords[0];
chip.targetAddress ← chip.firstFreeVector;
};
FOR channelIndex: ChannelIndex IN ChannelIndex DO
tsi: TripleSequenceIndex ← map[channelIndex];
value ← BitOps.DoubleShift[value, 1];
IF tsi.assigned THEN {
triple: ParsedVectorTriple ← vector[tsi.triple];
IF triple.value[tsi.sequence] THEN value ← BitOps.DoubleOR[value, 1];
inhibitMask[channelIndex].inhibit ← triple.inhibit[tsi.sequence];
inhibitMask[channelIndex].mask ← triple.mask[tsi.sequence];
};
ENDLOOP;
FOR inhibitMaskIndex IN [0..firstFreeInhibitMask) DO
IF inhibitMask=chip.inhibitMask[inhibitMaskIndex] THEN EXIT;
REPEAT FINISHED => {
IF firstFreeInhibitMask=inhibitMaskPerChip THEN ERROR Error["Not enough inhibit/mask entries for this vector set"];
chip.inhibitMask[firstFreeInhibitMask] ← inhibitMask;
inhibitMaskIndex ← firstFreeInhibitMask;
firstFreeInhibitMask ← firstFreeInhibitMask + 1;
};
ENDLOOP;
value ← BitOps.DoubleOR[value, BitOps.DoubleShift[inhibitMaskIndex, 16]];
IF debug THEN TerminalIO.PutF["\nvalue: %x", IO.int[value]];
SELECT TRUE FROM
copyLength=0 => IF Match[TRUE] THEN copyLength ← 1;
Match[FALSE] => copyLength ← copyLength+1;
copyLength>=minCopyLength => {
EmitLiteral[LiteralLength[]];
EmitCopy[];
IF Match[TRUE] THEN copyLength ← 1;
};
LiteralLength[]=0 AND copyLength>=minCopyLengthNoLiteral => {
EmitCopy[];
IF Match[TRUE] THEN copyLength ← 1;
};
ENDCASE => {
copyLength ← 0;
IF LiteralLength[]>=maxLiteralLength THEN EmitLiteral[maxLiteralLength];
};
buffer[nextInsert] ← value;
nextInsert ← (nextInsert + 1) MOD bufferSize;
{
emitCopy: BOOL ← copyLength=maxCopyLength;
length: NAT ← LiteralLength[];
IF emitCopy OR length=maxLiteralLength THEN EmitLiteral[length];
IF emitCopy THEN EmitCopy[];
};
ENDLOOP;
AlignCodeWords[chip.targetAddress];
chip.matchAddress ← chip.firstFreeVector - 1;
};
PrintFixture: PUBLIC PROC [fixture: Fixture] = {
FOR chipIndex: CARDINAL IN [0..fixture.size) DO
GetByte: PROC RETURNS [byte: Byte10] = {
byte ← chip.vectors[vectorIndex][byteIndex];
byteIndex ← byteIndex + 1;
IF byteIndex=bytesPerAddress THEN {
byteIndex ← 0;
vectorIndex ← vectorIndex + 1;
}
};
chip: Chip ← fixture[chipIndex];
vectorIndex: CARDINAL ← 0;
byteIndex: CARDINAL ← 0;
TerminalIO.PutF["\nchip: %g", IO.card[chipIndex]];
TerminalIO.PutF["\nfirstFreeVector: %g", IO.card[chip.firstFreeVector]];
TerminalIO.PutF["\nInhibitMask"];
FOR inhibitMaskIndex: InhibitMaskIndex IN InhibitMaskIndex DO
TerminalIO.PutF["\n%g:", IO.card[inhibitMaskIndex]];
FOR channelIndex: ChannelIndex IN ChannelIndex DO
TerminalIO.PutF[" (%g, %g)", IO.card[IF chip.inhibitMask[inhibitMaskIndex][channelIndex].inhibit THEN 1 ELSE 0], IO.card[IF chip.inhibitMask[inhibitMaskIndex][channelIndex].mask THEN 1 ELSE 0]];
ENDLOOP;
ENDLOOP;
UNTIL vectorIndex>=chip.firstFreeVector DO
byte: Byte10 ← GetByte[];
IF byte<maxLiteralLength THEN {
IF byte=0 THEN byte ← maxLiteralLength;
TerminalIO.PutF["\nLit(%g):", IO.card[byte]];
THROUGH [0..byte) DO
upper: Byte10 ← GetByte[];
value: BitOps.BitDWord ← GetByte[];
value ← BitOps.ICID[upper, value, 0, 10, 20];
TerminalIO.PutF[" %x", IO.card[value]];
ENDLOOP;
}
ELSE TerminalIO.PutF["\nCopy l:%g, p: %g", IO.card[BitOps.ECFW[byte, 0, 4, 10]], IO.card[BitOps.ECFW[byte, 4, 6, 10]]];
ENDLOOP;
ENDLOOP;
};
PrintFixtureChannel: PUBLIC PROC [fixture: Fixture, channelIndex: ChannelIndex, chipIndex: CARDINAL ← 0] = {
GetByte: PROC RETURNS [byte: Byte10] = {
byte ← chip.vectors[vectorIndex][byteIndex];
byteIndex ← byteIndex + 1;
IF byteIndex=bytesPerAddress THEN {
byteIndex ← 0;
vectorIndex ← vectorIndex + 1;
};
};
PrintVector: PROC [value: CARD] = {
inhibitMaskIndex: InhibitMaskIndex;
bit: CARD;
inhibitMaskIndex ← BitOps.ECFD[value, 0, 4, 20];
bit ← BitOps.ECFD[value, channelIndex+4, 1, 20];
TerminalIO.PutF["\n %g %g %g", IO.card[bit], IO.card[IF chip.inhibitMask[inhibitMaskIndex][channelIndex].inhibit THEN 1 ELSE 0], IO.card[IF chip.inhibitMask[inhibitMaskIndex][channelIndex].mask THEN 1 ELSE 0]];
};
chip: Chip ← fixture[chipIndex];
vectorIndex: CARDINAL ← 0;
byteIndex: CARDINAL ← 0;
buffer: ARRAY BufferIndex OF BitOps.BitDWord ← ALL[BitOps.BitDWordZero];
nextInsert: CARDINAL ← 0;
UNTIL vectorIndex>=chip.firstFreeVector DO
byte: Byte10 ← GetByte[];
IF byte<maxLiteralLength THEN {
IF byte=0 THEN byte ← maxLiteralLength;
THROUGH [0..byte) DO
upper: Byte10 ← GetByte[];
value: BitOps.BitDWord ← GetByte[];
value ← BitOps.ICID[upper, value, 0, 10, 20];
buffer[nextInsert] ← value;
nextInsert ← (nextInsert + 1) MOD bufferSize;
PrintVector[value];
ENDLOOP;
}
ELSE {
length: CARDINAL ← BitOps.ECFW[byte, 0, 4, 10];
position: CARDINAL ← BitOps.ECFW[byte, 4, 6, 10];
THROUGH [0..length) DO
PrintVector[buffer[position]];
buffer[nextInsert] ← buffer[position];
position ← (position + 1) MOD bufferSize;
nextInsert ← (nextInsert + 1) MOD bufferSize;
ENDLOOP;
};
ENDLOOP;
};
Pickle
Pickle: PUBLIC PROC [fixture: Fixture, file: ROPE] = {
};
Unpickle: PUBLIC PROC [file: ROPE] RETURNS [fixture: Fixture] = {
};
Execute
Types
Timing: TYPE = {Sample, Width, Delay};
PEReg: TYPE = [0..16);
PEData: TYPE = [0..1024);
Ram: TYPE = {VRam, HRam, ERam, CRam};
VRamAdd: TYPE = [0..1024);
HalfClocks: TYPE = [0..20);
InvStages: TYPE = [0..20);
EdgeC: TYPE = [0..4);
EdgeF: TYPE = [0..6);
Delay: TYPE = [0..475); --nS
Width: TYPE = [5..475); --nS
Sample: TYPE = [0..480); --nS
Period: TYPE = [30..480); --nS
ClockDivisor: TYPE = {one, two, four, eight};
Phase: TYPE = {Even, Odd};
Edge: TYPE = {Leading, Trailing};
Transition: TYPE = {Up, Down};
DataPattern: TYPE = {p00, p11, p01, p10};
Constants
clock8112: GPIB.DeviceAddr = 12;
fineMap: ARRAY EdgeF OF [0..16) = [4, 2, 12, 10, 1, 9];
baseAdd: LONG POINTER = LOOPHOLE[LONG[4000h]];
Globals
gClkPeriod: Period ← 50; -- nS, only global for debugging, could be local to SetClock
gClkDivisor: ClockDivisor ← one; -- only global for debugging
gClockCtl: CARDINAL ← 0; -- shared amongst various procedures
gDebug: BOOLFALSE;
gRamDebug: BOOLFALSE;
hardwareEnabled: BOOLTRUE;
gCycOffset: Ns ← 0; -- shared by SetRefDelay, FindTZero
pause: NAT ← 100; -- hack for HoneEdge
CBus Addresses
Pin Electronics
pinElecBase: CARDINAL ~ 000h; -- up to 17Fh
Each DUT pin
Each Timing Generator (Sample, Width, Delay)
dsr0: CARDINAL ~ 000h;
dsr1: CARDINAL ~ 001h;
invCh0: CARDINAL ~ 002h;
invCh1: CARDINAL ~ 003h;
invCh2: CARDINAL ~ 004h;
risingEdge: CARDINAL ~ 005h;
fallingEdge: CARDINAL ~ 006h;
ioCtl: CARDINAL ~ 007h;
format: CARDINAL ~ 00fh;
Decompressor
decompBase: CARDINAL ~ 180h; -- up to 1FFh
writeLoopAdd: CARDINAL ~ 180h;
writeEndAdd: CARDINAL ~ 181h;
writeExtRamAdd: CARDINAL ~ 182h;
writeExtRamCtl: CARDINAL ~ 183h;
ramWD0: CARDINAL ~ 184h;
ramWD1: CARDINAL ~ 185h;
ramWD2: CARDINAL ~ 186h;
ramWD3: CARDINAL ~ 187h;
dramCtl: CARDINAL ~ 188h;
readDebug: CARDINAL ~ 180h;
readVRamAdd: CARDINAL ~ 181h;
readHRamAdd: CARDINAL ~ 182h;
readCmd: CARDINAL ~ 183h;
ramRD0 : CARDINAL ~ 184h;
ramRD1 : CARDINAL ~ 185h;
ramRD2 : CARDINAL ~ 186h;
ramRD3 : CARDINAL ~ 187h;
Ref Clock Generator (on Multibus card)
extCtlBase: CARDINAL ~ 200h; -- up to 3FFh
clkCtl: CARDINAL  ~ 200h;
refDelay: CARDINAL ~ 201h;
ioCtl Register Bits
phaseComp: CARDINAL ~ 001h;
sampleLate: CARDINAL ~ 002h;
analogSample: CARDINAL ~ 004h;
ttlMode: CARDINAL ~ 008h;
selLateData: CARDINAL ~ 010h;
format Register Bits
nrz: CARDINAL ~ 001h;
rz: CARDINAL ~ 002h;
ro: CARDINAL ~ 004h;
rc: CARDINAL ~ 008h;
rt: CARDINAL ~ 010h;
writeExtRamCtl Register Bits
writeCRam: CARDINAL ~ 101h;
writeERam: CARDINAL ~ 102h;
writeHRam: CARDINAL ~ 104h;
writeVRam: CARDINAL ~ 108h;
readCRam: CARDINAL ~ 110h;
readERam: CARDINAL ~ 120h;
readHRam: CARDINAL ~ 140h;
readVRam: CARDINAL ~ 180h;
extRamAccess: CARDINAL ~ 100h;
vramBypass: CARDINAL ~ 200h;
readDebug Register Bits
resetMinus1: CARDINAL ~ 001h;
wrHRamLow: CARDINAL ~ 002h;
wrHRamHigh: CARDINAL ~ 004h;
fetchCmd: CARDINAL ~ 008h;
prefetch: CARDINAL ~ 010h;
error: CARDINAL ~ 020h;
Procedures
Run: PUBLIC PROC [fixture: Fixture] = {
None of the routines are parameterized by the chip number!
InitSystem[];
Reset[];
SetClock[NARROW[RProperties.GetProp[fixture.properties, $Clock], REF INT]^];
IF hardwareEnabled THEN FindTZero[];
FOR chipIndex: CARDINAL IN [0..fixture.size) DO
chip: Chip ← fixture[chipIndex];
FOR ch: ChannelIndex IN ChannelIndex DO
tg: TimingGroup ← chip.formatTiming[ch];
IF tg#NIL THEN {
IF hardwareEnabled THEN SELECT tg.format FROM
NRZ => NRZMode[ch, tg.delay];
RZ => RZMode[ch, tg.delay, tg.width];
RO => ROMode[ch, tg.delay, tg.width];
RT => RTMode[ch, tg.delay, tg.width];
RC => RCMode[ch, tg.delay, tg.width];
ENDCASE => ERROR;
SetAnalogSample[ch, tg.variableThreshold];
SetTTLLevels[ch, tg.variableLevels];
};
ENDLOOP;
FOR vi: CARD IN [0..chip.firstFreeVector) DO
RamWrite[VRam, vi, chip.vectors[vi].elements[0], chip.vectors[vi].elements[1], chip.vectors[vi].elements[2], chip.vectors[vi].elements[3]];
ENDLOOP;
FOR imi: InhibitMaskIndex IN InhibitMaskIndex DO
data0, data1, data2, data3: Byte10 ← 0;
FOR ch: ChannelIndex IN ChannelIndex DO
SELECT ch FROM
IN [0..6) => {
data0 ← BitOps.IBIW[chip.inhibitMask[imi][ch].mask, data0, ch, 6];
data2 ← BitOps.IBIW[chip.inhibitMask[imi][ch].inhibit, data2, ch, 6];
};
IN [6..16) => {
data1 ← BitOps.IBIW[chip.inhibitMask[imi][ch].mask, data1, ch-6, 10];
data3 ← BitOps.IBIW[chip.inhibitMask[imi][ch].inhibit, data3, ch-6, 10];
};
ENDCASE => ERROR;
ENDLOOP;
RamWrite[CRam, imi, data0, data1, data2, data3];
ENDLOOP;
CBusWrite[writeEndAdd, chip.matchAddress];
CBusWrite[writeLoopAdd, chip.targetAddress];
ENDLOOP;
StartCtl[TRUE, TRUE, FALSE];
};
InitSystem: PROC ~ {
IF gDebug THEN TerminalIO.PutRope["\nInit\n"];
InitGPIBDevices[];
SetRefDelayAbs[0];
CBusWrite[clkCtl, 0];
gClockCtl ← 0;
SetClock[100];
};
Reset: PROC ~ {
IF gDebug THEN TerminalIO.PutRope["Reset\n"];
StartCtl[FALSE, FALSE, FALSE];
CBusWrite[writeLoopAdd, 0];
CBusWrite[writeEndAdd, 0];
CBusWrite[writeExtRamCtl, 0];
StartCtl[FALSE, FALSE, TRUE];
StartCtl[FALSE, FALSE, FALSE];
};
FindTZero: PROC ~ {
In RZ mode force the output of the pulse generator high so that only the transitions resulting from changes in the data pipeline are observed. Skew RefCk to measure the time when the data transition occurs. Measure both rising and falling edges and call the greater of the two times the beginning of the cycle.
biggest: ChannelIndex ← 0;
new: Ns ← 0;
gCycOffset ← 0;
ClearAllPE[];
SetDataPattern[p10];
FOR ch: ChannelIndex IN ChannelIndex DO
SetFormat[ch, RZ];
ForcePulseHigh[ch];
new ← MAX[FindOutputEdge[ch, Even, Up], FindOutputEdge[ch, Odd, Down]];
IF new>gCycOffset THEN {gCycOffset ← new; biggest ← ch};
ENDLOOP;
TerminalIO.PutF["Cycle Start Offset = %g, chan: %g\n", IO.int[gCycOffset], IO.int[biggest]];
};
NRZMode: PROC [ch: ChannelIndex, delay: Delay] ~ {
dhc, whc: HalfClocks ← 0;
dis, wis: InvStages ← 0;
delayFix: NAT ← 0;
TerminalIO.PutF["Chan: %g, NRZ, Delay: %g\n", IO.int[ch], IO.int[delay]];
[dhc, dis] ← CalibrateDL[ch, Delay, delay];
[whc, wis] ← CalibrateDL[ch, Width, delay+20]; --doesn't matter how wide
MakePulse[ch, NRZ, dhc, dis, whc, wis];
DO
WHILE HoneEdge[ch, Leading, Even, Up, delay, p10] DO
IF (delayFix ← delayFix+1) > 7 THEN ERROR;
[dhc, dis] ← CalibrateDL[ch, Delay, delay-delayFix];
MakePulse[ch, NRZ, dhc, dis, whc, wis];
ENDLOOP;
IF HoneEdge[ch, Leading, Odd, Up, delay, p01] THEN {
IF (delayFix ← delayFix+1) > 7 THEN ERROR;
[dhc, dis] ← CalibrateDL[ch, Delay, delay-delayFix];
MakePulse[ch, NRZ, dhc, dis, whc, wis];
LOOP;
};
EXIT;
ENDLOOP;
SetDataPattern[p10]; --Display the result
SetRefDelay[0];
};
RZMode: PROC [ch: ChannelIndex, delay: Delay, width: Width] ~ {
dhc, whc: HalfClocks ← 0;
dis, wis: InvStages ← 0;
delayFix, widthFix: NAT ← 0;
TerminalIO.PutF["Chan: %g, RZ, Delay: %g, Width: %g\n", IO.int[ch], IO.int[delay], IO.int[width]];
[dhc, dis] ← CalibrateDL[ch, Delay, delay];
[whc, wis] ← CalibrateDL[ch, Width, delay+width];
MakePulse[ch, RZ, dhc, dis, whc, wis];
DO
WHILE HoneEdge[ch, Leading, Even, Up, delay, p10] DO
IF (delayFix ← delayFix+1) > 7 THEN ERROR;
[dhc, dis] ← CalibrateDL[ch, Delay, delay-delayFix];
MakePulse[ch, RZ, dhc, dis, whc, wis];
ENDLOOP;
WHILE HoneEdge[ch, Trailing, Even, Down, delay+width, p10] DO
IF (widthFix ← widthFix+1) > 7 THEN ERROR;
[whc, wis] ← CalibrateDL[ch, Width, delay+width-widthFix];
MakePulse[ch, RZ, dhc, dis, whc, wis];
ENDLOOP;
IF HoneEdge[ch, Leading, Odd, Up, delay, p01] THEN {
IF (delayFix ← delayFix+1) > 7 THEN ERROR;
[dhc, dis] ← CalibrateDL[ch, Delay, delay-delayFix];
MakePulse[ch, RZ, dhc, dis, whc, wis];
LOOP;
};
IF HoneEdge[ch, Trailing, Odd, Down, delay+width, p01] THEN {
IF (widthFix ← widthFix+1) > 7 THEN ERROR;
[whc, wis] ← CalibrateDL[ch, Width, delay+width-widthFix];
MakePulse[ch, RZ, dhc, dis, whc, wis];
LOOP;
};
EXIT;
ENDLOOP;
SetDataPattern[p11]; --Display the result
SetRefDelay[0];
};
ROMode: PROC [ch: ChannelIndex, delay: Delay, width: Width] ~ {
dhc, whc: HalfClocks ← 0;
dis, wis: InvStages ← 0;
delayFix, widthFix: NAT ← 0;
TerminalIO.PutF["Chan: %g, RO, Delay: %g, Width: %g\n", IO.int[ch], IO.int[delay], IO.int[width]];
[dhc, dis] ← CalibrateDL[ch, Delay, delay];
[whc, wis] ← CalibrateDL[ch, Width, delay+width];
MakePulse[ch, RO, dhc, dis, whc, wis];
DO
WHILE HoneEdge[ch, Leading, Even, Down, delay, p01] DO
IF (delayFix ← delayFix+1) > 7 THEN ERROR;
[dhc, dis] ← CalibrateDL[ch, Delay, delay-delayFix];
MakePulse[ch, RO, dhc, dis, whc, wis];
ENDLOOP;
WHILE HoneEdge[ch, Trailing, Even, Up, delay+width, p01] DO
IF (widthFix ← widthFix+1) > 7 THEN ERROR;
[whc, wis] ← CalibrateDL[ch, Width, delay+width-widthFix];
MakePulse[ch, RO, dhc, dis, whc, wis];
ENDLOOP;
IF HoneEdge[ch, Leading, Odd, Down, delay, p10] THEN {
IF (delayFix ← delayFix+1) > 7 THEN ERROR;
[dhc, dis] ← CalibrateDL[ch, Delay, delay-delayFix];
MakePulse[ch, RO, dhc, dis, whc, wis];
LOOP;
};
IF HoneEdge[ch, Trailing, Odd, Up, delay+width, p10] THEN {
IF (widthFix ← widthFix+1) > 7 THEN ERROR;
[whc, wis] ← CalibrateDL[ch, Width, delay+width-widthFix];
MakePulse[ch, RO, dhc, dis, whc, wis];
LOOP;
};
EXIT;
ENDLOOP;
SetDataPattern[p00]; --Display the result
SetRefDelay[0];
};
RTMode: PROC [ch: ChannelIndex, delay: Delay, width: Width] ~ {
RZMode[ch, delay, width];
SetFormat[ch, RT];
};
RCMode: PROC [ch: ChannelIndex, delay: Delay, width: Width] ~ {
RZMode[ch, delay, width];
SetFormat[ch, RC];
};
MakePulse: PROC [ch: ChannelIndex, f: Format, dhc: HalfClocks, dis: InvStages, whc: HalfClocks, wis: InvStages] ~ {
SetFormat[ch, f];
SetTimingChan[ch, Delay, dhc, dis];
SetTimingChan[ch, Width, whc, wis];
};
FindHC: PROC [ch: ChannelIndex, tg: Timing, t: Transition, start: HalfClocks ← 0] RETURNS [hc: HalfClocks ← 0] ~ {
SetTimingChan[ch, tg, 0, 0];
hc ← start;
WHILE NOT EdgeIsEarly[ch, t] DO
SetTimingChan[ch, tg, (hc←hc+1), 0];
ENDLOOP;
WHILE EdgeIsEarly[ch, t] DO
SetTimingChan[ch, tg, (hc←hc+1), 0];
ENDLOOP;
};
FindIS: PROC [ch: ChannelIndex, tg: Timing, hc: HalfClocks, t: Transition] RETURNS [is: InvStages ← 0]~ {
SetTimingChan[ch, tg, hc, 0];
WHILE EdgeIsEarly[ch, t] DO
SetTimingChan[ch, tg, hc, (is←is+1)];
ENDLOOP;
};
CalibrateDL: PROC [ch: ChannelIndex, tg: Timing, ns: Ns] RETURNS [hc: HalfClocks, is: InvStages] ~ {
Cal: PROC [t: Transition] RETURNS [hc: HalfClocks ← 0, is: InvStages ← 0] ~ {
IF (hc ← FindHC[ch, tg, t]) = 0 THEN hc ← FindHC[ch, tg, t, 1];
hc ← hc-1;
IF (is ← FindIS[ch, tg, hc, t]) = 0 THEN
IF hc#0 THEN {
hc ← hc-1;
is ← FindIS[ch, tg, hc, t];
} ELSE {
hc ← FindHC[ch, tg, t, 2]-1;
is ← FindIS[ch, tg, hc, t];
IF is=0 THEN ERROR;
};
is←is-1;
SetTimingChan[ch, tg, hc, is];
};
hce, hco: HalfClocks;
ise, iso: InvStages;
SetRefPhase[Even];
SetDataPattern[p11];
SetFormat[ch, RZ];
IF tg=Delay THEN SetDelayPassThru[ch] ELSE SetWidthPassThru[ch];
SetRefDelay[ns];
[hce, ise] ← Cal[Up];
SetRefPhase[Odd];
[hco, iso] ← Cal[Down];
[hc, is] ← MinDelay[hce, ise, hco, iso];
IF gDebug THEN TerminalIO.PutF["Half Clocks: %g, Inv Stages: %g\n", IO.int[hc], IO.int[is]];
};
HoneEdge: PROC [ch: ChannelIndex, e: Edge, p: Phase, t: Transition, ns: Ns, pat: DataPattern] RETURNS [tooLate: BOOLFALSE] ~ {
ec: EdgeC ← 0;
SetRefPhase[p];
SetRefDelay[ns];
SetDataPattern[pat];
FOR i: NAT IN EdgeC DO
TweakEdge[ch, e, p, (ec ← i), 0];
Process.PauseMsec[pause];
IF NOT EdgeIsEarly[ch, t] THEN EXIT;
ENDLOOP;
IF ec#0 THEN TweakEdge[ch, e, p, (ec ← ec-1), 0] ELSE {
TerminalIO.PutF["Too early\n"];
RETURN[TRUE];
};
FOR i: NAT IN EdgeF DO
TweakEdge[ch, e, p, ec, i];
Process.PauseMsec[pause];
IF NOT EdgeIsEarly[ch, t] THEN GOTO foundIt;
REPEAT
foundIt => {TerminalIO.PutF[" %g %g EdgeC: %g, EdgeF: %g\n",
IO.rope[IF e=Leading THEN "Leading" ELSE "Trailing"],
IO.rope[IF p=Even THEN "Even" ELSE "Odd"],
IO.int[ec], IO.int[i]]};
FINISHED => TerminalIO.PutF["Exceeded range\n"];
ENDLOOP;
};
TweakEdge: PROCEDURE [ch: ChannelIndex, e: Edge, p: Phase, ec: EdgeC, ef: EdgeF] = {
PEWrite[ch, IF p=Odd THEN risingEdge ELSE fallingEdge, MapEdge[ec, ef], IF e=Leading THEN Delay ELSE Width];
};
ClearAllPE: PROC ~ {
FOR c: ChannelIndex IN ChannelIndex DO
ClearPEChan[c];
ENDLOOP;
};
ClearPEChan: PROC [c: ChannelIndex] ~ {
FOR t: Timing IN Timing DO
SetTimingChan[c, t, 0, 0];
ENDLOOP;
PEWrite[c, ioCtl, 0];
PEWrite[c, format, nrz];
};
MinDelay: PROC [hc0: HalfClocks, is0: InvStages, hc1: HalfClocks, is1: InvStages] RETURNS [hs: HalfClocks, is: InvStages] ~ {
SELECT TRUE FROM
hc0>hc1 => RETURN[hc1, is1];
hc0<hc1 => RETURN[hc0, is0];
hc0=hc1 AND is0>is1 => RETURN[hc1, is1];
hc0=hc1 AND is0<is1 => RETURN[hc0, is0];
ENDCASE => RETURN[hc0, is0];
};
SetTimingChan: PROC [chan: ChannelIndex, timing: Timing, halfClocks: HalfClocks, invStages: InvStages, upEdgeC: EdgeC ← 0, upEdgeF: EdgeF ← 0, dnEdgeC: EdgeC ← 0, dnEdgeF: EdgeF ← 0] ~ {
PEWrite[chan, dsr0, DSR[halfClocks].high, timing];
PEWrite[chan, dsr1, DSR[halfClocks].low, timing];
PEWrite[chan, invCh0, IC[invStages, FALSE, FALSE].high, timing];
PEWrite[chan, invCh1, IC[invStages, FALSE, FALSE].mid, timing];
PEWrite[chan, invCh2, IC[invStages, FALSE, FALSE].low, timing];
PEWrite[chan, risingEdge, MapEdge[upEdgeC, upEdgeF], timing];
PEWrite[chan, fallingEdge, MapEdge[dnEdgeC, dnEdgeF], timing];
};
DSR: PROC [halfClocks: HalfClocks] RETURNS [high, low: CARDINAL] ~ {
c: BitOps.BitDWord ← 0;
c ← BitOps.IBID[TRUE, c, halfClocks/2, 12];
c ← BitOps.IBID[TRUE, c, IF (halfClocks MOD 2)=0 THEN 10 ELSE 11, 12];
high ← BitOps.ECFD[c, 0, 8, 12];
low ← Basics.BITSHIFT[BitOps.ECFD[c, 8, 4, 12], 4]; --left justify result
};
IC: PROC [invStages: InvStages, forceH, forceL: BOOL] RETURNS [high, mid, low: CARDINAL] ~ {
c: BitOps.BitDWord ← 0;
c ← BitOps.IBID[TRUE, c, invStages, 22];
IF forceL THEN c ← BitOps.IBID[TRUE, c, 20, 22];
IF forceH THEN c ← BitOps.IBID[TRUE, c, 21, 22];
high ← BitOps.ECFD[c, 0, 8, 22];
mid ← BitOps.ECFD[c, 8, 8, 22];
low ← Basics.BITSHIFT[BitOps.ECFD[c, 16, 6, 22], 2]; --left justify result
};
MapEdge: PROC [edgeC: EdgeC, edgeF: EdgeF] RETURNS [CARDINAL] ~ {
RETURN[Basics.BITOR[Basics.BITSHIFT[80h, -edgeC], fineMap[edgeF]]];
};
RamWrite: PROC [which: Ram, add: Address, d0, d1, d2, d3: Byte10] ~ {
IF gRamDebug THEN TerminalIO.PutRope[IO.PutFLR["Write %g Add: %x, Data: %x, %x, %x, %x\n", LIST[IO.rope[
SELECT which FROM
VRam => "VRam",
HRam => "HRam",
ERam => "ERam",
CRam => "CRam",
ENDCASE => ERROR],
IO.card[add], IO.card[d0], IO.card[d1], IO.card[d2], IO.card[d3]]]];
CBusWrite[writeExtRamAdd, add];
LoadRamWD[d0, d1, d2, d3];
CBusWrite[writeExtRamCtl, extRamAccess];
CBusWrite[writeExtRamCtl, SELECT which FROM
VRam => writeVRam,
HRam => writeHRam,
ERam => writeERam,
CRam => writeCRam,
ENDCASE => ERROR];
CBusWrite[writeExtRamCtl, extRamAccess];
CBusWrite[writeExtRamCtl, 0];
};
RamRead: PROC [which: Ram, add: Address] RETURNS [d0, d1, d2, d3: Byte10] ~ {
CBusWrite[writeExtRamAdd, add];
CBusWrite[writeExtRamCtl, extRamAccess];
CBusWrite[writeExtRamCtl, SELECT which FROM
VRam => readVRam,
HRam => readHRam,
ERam => readERam,
CRam => readCRam,
ENDCASE => ERROR];
[d0, d1, d2, d3] ← FetchRamRD[];
CBusWrite[writeExtRamCtl, 0];
IF gRamDebug THEN TerminalIO.PutRope[IO.PutFLR["Read %g Add: %x, Data: %x, %x, %x, %x\n", LIST[IO.rope[
SELECT which FROM
VRam => "VRam",
HRam => "HRam",
ERam => "ERam",
CRam => "CRam",
ENDCASE => ERROR],
IO.card[add], IO.card[d0], IO.card[d1], IO.card[d2], IO.card[d3]]]];
};
LoadRamWD: PROC [d0, d1, d2, d3: Byte10] ~ {
CBusWrite[ramWD0, d0];
CBusWrite[ramWD1, d1];
CBusWrite[ramWD2, d2];
CBusWrite[ramWD3, d3];
};
FetchRamRD: PROC RETURNS [d0, d1, d2, d3: Byte10] ~ {
d0 ← CBusRead[ramRD0];
d1 ← CBusRead[ramRD1];
d2 ← CBusRead[ramRD2];
d3 ← CBusRead[ramRD3];
};
InitGPIBDevices: PROC ~ TRUSTED {
IF hardwareEnabled THEN {
IF ~GPIB.InitializeController[] THEN ERROR;
GPIB.InterfaceClear[];
GPIB.SelectedDeviceClear[clock8112];
GPIB.SelectedRemoteEnable[clock8112];
-- fixed edge rate, square wave output, 5V, 0V, output on
WriteGPIB[clock8112, "W1 DTY 50% HIL -0.5V LOL -2.0V D0"];
};
};
ForcePulseHigh: PROC [ch: ChannelIndex] = {
SetTimingChan[ch, Delay, 0, 0];
SetTimingChan[ch, Width, 0, 0];
PEWrite[ch, invCh0, 0, Delay];
PEWrite[ch, invCh2, IC[0, TRUE, FALSE].low, Delay];
PEWrite[ch, invCh0, 0, Width];
PEWrite[ch, invCh2, IC[0, FALSE, TRUE].low, Width];
};
ForcePulseLow: PROC [ch: ChannelIndex] = {
SetTimingChan[ch, Delay, 0, 0];
SetTimingChan[ch, Width, 0, 0];
PEWrite[ch, invCh0, 0, Delay];
PEWrite[ch, invCh2, IC[0, FALSE, TRUE].low, Delay];
PEWrite[ch, invCh0, 0, Width];
PEWrite[ch, invCh2, IC[0, TRUE, FALSE].low, Width];
};
SetDelayPassThru: PROC [ch: ChannelIndex] ~ {
IF gDebug THEN TerminalIO.PutF["Set Delay pass thru\n"];
SetTimingChan[ch, Delay, 0, 0];
SetTimingChan[ch, Width, 0, 0];
PEWrite[ch, invCh2, IC[0, FALSE, FALSE].low, Delay];
PEWrite[ch, invCh0, 0, Width];
PEWrite[ch, invCh2, IC[0, FALSE, TRUE].low, Width];
};
SetWidthPassThru: PROC [ch: ChannelIndex] ~ {
IF gDebug THEN TerminalIO.PutF["Set Width pass thru\n"];
SetTimingChan[ch, Delay, 0, 0];
SetTimingChan[ch, Width, 0, 0];
PEWrite[ch, invCh0, 0, Delay];
PEWrite[ch, invCh2, IC[0, FALSE, TRUE].low, Delay];
PEWrite[ch, invCh2, IC[0, FALSE, FALSE].low, Width];
};
SetDataPattern: PROC [dp: DataPattern] ~ {
Reset[];
IF gDebug THEN TerminalIO.PutF["Set pattern: %g\n", IO.rope[SELECT dp FROM p00 => "00", p01 => "01", p10 => "10", p11 => "11", ENDCASE => ERROR]];
RamWrite[CRam, 0, 03Fh, 3FFh, 0h, 0h]; --mask: FFFF, inhibit: 0000
SELECT dp FROM
p00 => {
RamWrite[VRam, 0, 2h, 0h, 0h, 0h]; --Lit 2, 00, 00, 00
RamWrite[VRam, 1, 0h, 080h, 080h, 080h]; -- 00,Cpy 2@0,Cpy 2@0,Cpy 2@0
RamWrite[VRam, 2, 2h, 0h, 0h, 0h]; --Lit 2, 00, 00, 00
RamWrite[VRam, 3, 0h, 080h, 080h, 080h]; -- 00,Cpy 2@0,Cpy 2@0,Cpy 2@0
};
p01 => {
RamWrite[VRam, 0, 2h, 0h, 0h, 3Fh]; --Lit 2, 00, 00, 3f
RamWrite[VRam, 1, 3FFh, 080h, 080h, 080h]; -- 3FF,Cpy 2@0,Cpy 2@0,Cpy 2@0
RamWrite[VRam, 2, 2h, 0h, 0h, 3Fh]; --Lit 2, 00, 00, 3f
RamWrite[VRam, 3, 3FFh, 080h, 080h, 080h]; -- 3FF,Cpy 2@0,Cpy 2@0,Cpy 2@0
};
p10 => {
RamWrite[VRam, 0, 2h, 3Fh, 3FFh, 0h]; --Lit 2, 3f, 3FF, 00
RamWrite[VRam, 1, 0h, 080h, 080h, 080h]; -- 00,Cpy 2@0,Cpy 2@0,Cpy 2@0
RamWrite[VRam, 2, 2h, 3Fh, 3FFh, 0h]; --Lit 2, 3f, 3FF, 00
RamWrite[VRam, 3, 0h, 080h, 080h, 080h]; -- 00,Cpy 2@0,Cpy 2@0,Cpy 2@0
};
p11 => {
RamWrite[VRam, 0, 2h, 3Fh, 3FFh, 3Fh]; --Lit 2, 3f, 3FF, 3f
RamWrite[VRam, 1, 3FFh, 080h, 080h, 080h]; -- 3FF,Cpy 2@0,Cpy 2@0,Cpy 2@0
RamWrite[VRam, 2, 2h, 3Fh, 3FFh, 3Fh]; --Lit 2, 3f, 3FF, 3f
RamWrite[VRam, 3, 3FFh, 080h, 080h, 080h]; -- 3FF,Cpy 2@0,Cpy 2@0,Cpy 2@0
};
ENDCASE => ERROR;
CBusWrite[writeLoopAdd, 0];
CBusWrite[writeEndAdd, 3];
StartCtl[TRUE, TRUE, FALSE];
};
FindOutputEdge: PROC [ch: ChannelIndex, p: Phase, t: Transition] RETURNS [time: Ns ← 0] ~ {
SetRefPhase[p];
SetRefDelayAbs[time];
WHILE NOT EdgeIsEarly[ch, t] DO
SetRefDelayAbs[(time ← time+1)];
ENDLOOP;
};
EdgeIsEarly: PROCEDURE [ch: ChannelIndex, t: Transition] RETURNS [BOOL] = {
trueCount, falseCount: NAT ← 0;
FOR i: NAT IN [0..7) DO
IF (t=Down) = (Basics.BITAND[PERead[ch, ioCtl], phaseComp]#0) THEN trueCount ← trueCount+1
ELSE falseCount ← falseCount + 1;
ENDLOOP;
IF gDebug THEN TerminalIO.PutF[" %g/%g ", IO.card[trueCount], IO.card[falseCount]];
RETURN[trueCount>falseCount];
};
SetFormat: PROC [ch: ChannelIndex, f: Format] = {
PEWrite[ch, format, SELECT f FROM NRZ => nrz, RZ => rz, RO => ro, RC=> rc, RT => rt, ENDCASE => ERROR];
IF gDebug THEN TerminalIO.PutF["Set format: %g\n", IO.rope[SELECT f FROM NRZ => "NRZ", RZ => "RZ", RO => "RO", RC=> "RC", RT => "RT", ENDCASE => ERROR]]
};
SetAnalogSample: PROC [ch: ChannelIndex, b: BOOL] = {
PEWrite[ch, ioCtl, BitOps.IBIW[b, PERead[ch, ioCtl], 2, 5]];
};
SetTTLLevels: PROC [ch: ChannelIndex, b: BOOL] = {
PEWrite[ch, ioCtl, BitOps.IBIW[b, PERead[ch, ioCtl], 1, 5]];
};
SetRefDelayAbs: PROC [d: Delay] ~ {
CBusWrite[refDelay, d];
IF gDebug THEN TerminalIO.PutF["Ref clock delay: %g\n", IO.card[d]];
};
SetRefDelay: PROC [d: Delay] ~ {
SetRefDelayAbs[d+gCycOffset];
};
SetClock: PROC [period: Period] ~ {
divisor: ClockDivisor;
SELECT TRUE FROM
period IN [30..70) => {divisor ← one; gClkPeriod ← period};
period IN [70..140) => {divisor ← two; gClkPeriod ← (period+1)/2};
period IN [140..280) => {divisor ← four; gClkPeriod ← (period+2)/4};
period IN [280..560) => {divisor ← eight; gClkPeriod ← (period+4)/8};
ENDCASE => ERROR;
SetClockDivisor[divisor];
WriteGPIB[clock8112, IO.PutFR["PER %gNS", IO.card[gClkPeriod]]];
IF gDebug THEN TerminalIO.PutF["Set clock to %g ns\n", IO.card[period]]
};
StartCtl: PROC [start, loop, reset: BOOLFALSE] ~ {
gClockCtl ← BitOps.IBIW[start, gClockCtl, 5, 8];
gClockCtl ← BitOps.IBIW[loop, gClockCtl, 4, 8];
gClockCtl ← BitOps.IBIW[reset, gClockCtl, 0, 8];
CBusWrite[clkCtl, gClockCtl];
};
SetRefPhase: PROC [p: Phase] ~ {
gClockCtl ← BitOps.IBIW[p=Even, gClockCtl, 2, 8];
CBusWrite[clkCtl, gClockCtl];
IF gDebug THEN TerminalIO.PutF["Set reference phase %g\n", IO.rope[IF p=Even THEN "Even" ELSE "Odd"]]
};
SetClockDivisor: PROC [cd: ClockDivisor] ~ {
gClockCtl ← BitOps.ICIW[ORD[cd], gClockCtl, 6, 2, 8];
CBusWrite[clkCtl, gClockCtl];
gClkDivisor ← cd;
};
PEWrite: PROC [chan: ChannelIndex, reg: PEReg, data: PEData, timing: Timing ← Sample] ~ {
To write the ioCtl and Format registers, use default: timing ← Sample since these regs are not asociated with any particular timing chain
CBusWrite[(chan*24)+(timing.ORD*8)+reg, data];
};
PERead: PROC [chan: ChannelIndex, reg: PEReg, timing: Timing ← Sample] RETURNS [data: PEData] ~ {
To read the ioCtl and Format registers, use default: timing ← Sample since these regs are not asociated with any particular timing chain
data ← Basics.BITAND[CBusRead[(chan*24)+(timing.ORD*8)+reg], 0FFh]; --PE only uses 8 bits
};
CBusRead: PROC [add: Address] RETURNS [data: Byte10 ← 0] ~ {
IF hardwareEnabled THEN data ← XBus.IORead[baseAdd + add*2];
};
CBusWrite: PROC [add: Address, data: Byte10] ~ {
IF hardwareEnabled THEN XBus.IOWrite[baseAdd + add*2, data];
};
WriteGPIB: PROCEDURE [device: GPIB.DeviceAddr, msg: Rope.ROPE] = TRUSTED {
IF hardwareEnabled THEN GPIB.WriteDevice[device, Rope.Concat[msg, "\n\l"]];
};
END.