File: MakePLAImpl.mesa   
Copyright © 1984 by Xerox Corporation. All rights reserved.
Created by: Bob July 30, 1984 11:36:59 am PDT PDT
Last Edited by: Mayo, August 31, 1984 1:17:46 am PDT
Make a PLA. This module is documented in MakePLATileDoc.sil and in the user's manual.
General plan: Read our input table and create a description record. Use that record to build a PLA. The PLA is built by producing rows from top to bottom, inserting extra rows as needed. Each row is built left to right, and may have extra columns added as needed.
DIRECTORY
MakePLA,
Commander USING [CommandProc, Register],
Parquet, ExprRead, BoolOps, TerminalIO, AMTypes, Rope, IO, FS, SymTab, Atom, Convert;
MakePLAImpl: CEDAR PROGRAM    
IMPORTS Commander, Parquet, ExprRead, BoolOps, TerminalIO, Rope, FS, SymTab, Atom, Convert EXPORTS MakePLA = BEGIN
OPEN MakePLA;
maxCellSize: INT ~ 500; -- We will try to keep the second level core cells below this size.
maxChunkSize: INT ~ 50; -- We will try to keep the first level core cells below this size.
Error: PUBLIC ERROR[ec: ErrorCode, msg: Rope.ROPE] ~ CODE;
PLADescription: TYPE = REF PLADescriptionRec;
PLADescriptionRec: TYPE = RECORD [
context: Parquet.Context ← NIL,
inputNames, outputNames: LIST OF Rope.ROPENIL, -- every input and output MUST be named, ReadParameters and ReadTileSetOnly guarentee this
truthTable: BoolOps.TruthTable ← NIL,
extraRows, extraAndColumns, extraOrColumns: CARDINALCARDINAL.LAST-100, -- SPACING (in number of minterms) between extra ground rows and columns
optimize: BOOLTRUE,
printRows: CARDINAL ← 25, -- If the PLA has more than this many rows, then list them out on the screen as we assemble them.
dumpFile: Rope.ROPENIL -- if non-NIL then truth table (after optimization, if any) will be dumped to this file.
];
Create: PUBLIC PROC [context: Parquet.Context] RETURNS [Parquet.Module] = BEGIN
haveHorizontalExtras, haveVerticalOrExtras, haveVerticalAndExtras: BOOLTRUE;
numInputs, numOutputs, numPTerms: INT;
constructedPLA: Parquet.Module ← NIL;
startOfPreviousRow: Parquet.PlacedTile;
inputNames, outputNames: LIST OF Rope.ROPENIL;
ourTruthTable: BoolOps.TruthTable;
desc: PLADescription;
-- tiles outside of the core of the PLA, and extra tiles in the core
Aul, AUleft, ADleft, All, ALtop, ARtop, ALbot, ARbot: Parquet.Tile ← NIL;
AHleft, ALH, ARH, AHV, AVtop, AUV, ADV, AVbot: Parquet.Tile ← NIL;
Btop, BU, BD, BH, Bbot: Parquet.Tile ← NIL;
OLtop, ORtop, Our, OUright, ODright, Olr, ORbot, OLbot: Parquet.Tile ← NIL;
OVtop, OUV, ODV, OHV, OVbot, OLH, ORH, OHright: Parquet.Tile ← NIL;
-- major tiles in the core of the PLA
AUL1, AUL0, AULx, AUR1, AUR0, AURx: Parquet.Tile ← NIL;
ADL1, ADL0, ADLx, ADR1, ADR0, ADRx: Parquet.Tile ← NIL;
OUL1, OUL0, OUR1, OUR0: Parquet.Tile ← NIL;
ODL1, ODL0, ODR1, ODR0: Parquet.Tile ← NIL;
-- blocks of the PLA
AndBlock: Rope.ROPENIL;
OrBlock: Rope.ROPENIL;
LeftBlock: Rope.ROPE ← "PLA-left";
MidBlock: Rope.ROPE ← "PLA-mid";
RightBlock: Rope.ROPE ← "PLA-right";
TopAndBlock: Rope.ROPE ← "PLA-topAnd";
BotAndBlock: Rope.ROPE ← "PLA-botAnd";
TopOrBlock: Rope.ROPE ← "PLA-topOr";
BotOrBlock: Rope.ROPE ← "PLA-botOr";
ODD: PROC[i: INT] RETURNS[BOOL] = INLINE BEGIN RETURN[(i MOD 2 # 0)] END;
EVEN: PROC[i: INT] RETURNS[BOOL] = INLINE BEGIN RETURN[(i MOD 2 = 0)] END;
DumpTruthTable: PROC [tt: BoolOps.TruthTable, ttFile: Rope.ROPE] = BEGIN
-- dump truth table to file
ttStream: IO.STREAM;
message: Rope.ROPENIL;
ttStream ← FS.StreamOpen[ttFile, $create ! FS.Error => IF error.group = user THEN {ttStream ← NIL; CONTINUE}];
IF ttStream = NIL THEN ERROR Error[NoDump, Rope.Cat["Could not create truth table dump file '", ttFile, "'.\n"]];
BoolOps.TTToStream[tt, ttStream ! BoolOps.Error => {message ← msg; CONTINUE}];
IF message # NIL THEN ERROR Error[NoDump, Rope.Cat["Could not dump truth table to file '", ttFile, "', error was: ", message, "\n"]];
END;
LoadTiles: PROC[context: Parquet.Context] = BEGIN
anyTilesNotFound: BOOLFALSE;
anyErrorsMessages: Rope.ROPENIL;
anyErrors: BOOLFALSE;
GetTile: PROC[name1, name2, name3: Rope.ROPENIL, required: BOOL] RETURNS [Parquet.Tile] = BEGIN
tile: Parquet.Tile ← NIL;
tile ← Parquet.InstantiateTile[context, name1];
IF name2 # NIL AND tile = NIL THEN {
tile ← Parquet.InstantiateTile[context, name2];
IF name3 # NIL AND tile = NIL THEN {
tile ← Parquet.InstantiateTile[context, name3];
};
};
IF required AND tile = NIL THEN {
anyErrors ← TRUE;
anyErrorsMessages ← Rope.Cat[anyErrorsMessages, "MakePLA could not find tile '", name1];
IF name2 # NIL THEN {
IF name3 # NIL THEN
anyErrorsMessages ← Rope.Cat[anyErrorsMessages, " nor its alternates '", name2, "' and '", name3]
ELSE
anyErrorsMessages ← Rope.Cat[" nor its alternate '", name2];
};
anyErrorsMessages ← Rope.Cat[anyErrorsMessages, "'.\n"]
};
RETURN[tile];
END;
RequiredTile: PROC[name1, name2, name3: Rope.ROPENIL] RETURNS [Parquet.Tile] = BEGIN
RETURN[GetTile[name1, name2, name3, TRUE]];
END;
OptionalTile: PROC[name1, name2, name3: Rope.ROPENIL] RETURNS [Parquet.Tile] = BEGIN
RETURN[GetTile[name1, name2, name3, FALSE]];
END;
-- left side of AND plane
Aul ← OptionalTile["Aul"];
AUleft ← OptionalTile["AUleft", "Aleft"];
ADleft ← OptionalTile["ADleft", "Aleft"];
AHleft ← OptionalTile["AHleft"];
All ← OptionalTile["All"];
-- top and bottom of AND plane
ALbot ← OptionalTile["ALbot", "Abot"];
ARbot ← OptionalTile["ARbot", "Abot"];
AVbot ← OptionalTile["AVbot"];
ALtop ← OptionalTile["ALtop", "Atop"];
ARtop ← OptionalTile["ARtop", "Atop"];
AVtop ← OptionalTile["AVtop"];
-- core of AND plane
AUL0 ← RequiredTile["AUL0", "AL0", "A0"];
ADL0 ← RequiredTile["ADL0", "AL0", "A0"];
AUR0 ← RequiredTile["AUR0", "AR0", "A0"];
ADR0 ← RequiredTile["ADR0", "AR0", "A0"];
AUL1 ← RequiredTile["AUL1", "AL1", "A1"];
ADL1 ← RequiredTile["ADL1", "AL1", "A1"];
AUR1 ← RequiredTile["AUR1", "AR1", "A1"];
ADR1 ← RequiredTile["ADR1", "AR1", "A1"];
AULx ← RequiredTile["AUL-", "AL-", "A-"];
ADLx ← RequiredTile["ADL-", "AL-", "A-"];
AURx ← RequiredTile["AUR-", "AR-", "A-"];
ADRx ← RequiredTile["ADR-", "AR-", "A-"];
ALH ← OptionalTile["ALH", "AH"];
ARH ← OptionalTile["ARH", "AH"];
AUV ← OptionalTile["AUV", "AV"];
ADV ← OptionalTile["ADV", "AV"];
AHV ← OptionalTile["AHV"];
-- area between planes
Btop ← OptionalTile["Btop"];
BU ← RequiredTile["BU", "B"];
BD ← RequiredTile["BD", "B"];
BH ← OptionalTile["BH"];
Bbot ← OptionalTile["Bbot"];
-- top and bottom of OR plane
OLtop ← OptionalTile["OLtop", "Otop"];
ORtop ← OptionalTile["ORtop", "Otop"];
OVtop ← OptionalTile["OVtop"];
OLbot ← OptionalTile["OLbot", "Obot"];
ORbot ← OptionalTile["ORbot", "Obot"];
OVbot ← OptionalTile["OVbot"];
-- right side of OR plane
Olr ← OptionalTile["Olr"];
Our ← OptionalTile["Our"];
OUright ← OptionalTile["OUright", "Oright"];
ODright ← OptionalTile["ODright", "Oright"];
OHright ← OptionalTile["OHright"];
-- core of the OR plane
OUL0 ← RequiredTile["OUL0", "OU0"];
OUR0 ← RequiredTile["OUR0", "OU0"];
ODL0 ← RequiredTile["ODL0", "OD0"];
ODR0 ← RequiredTile["ODR0", "OD0"];
OUL1 ← RequiredTile["OUL1", "OU1"];
OUR1 ← RequiredTile["OUR1", "OU1"];
ODL1 ← RequiredTile["ODL1", "OD1"];
ODR1 ← RequiredTile["ODR1", "OD1"];
OLH ← OptionalTile["OLH", "OH"];
ORH ← OptionalTile["ORH", "OH"];
OUV ← OptionalTile["OUV", "OV"];
ODV ← OptionalTile["ODV", "OV"];
OHV ← OptionalTile["OHV"];
-- now take care of dependencies between tiles
IF ALH = NIL OR ARH = NIL OR BH = NIL OR OLH = NIL OR ORH = NIL THEN {
IF ALH # NIL OR ARH # NIL OR BH # NIL OR OLH # NIL OR ORH # NIL THEN {
anyErrorsMessages ← Rope.Cat[anyErrorsMessages, "Set of tiles must have all of the extra horizontal tiles or none of them.\n"];
anyErrors ← TRUE;
}
ELSE
haveHorizontalExtras ← FALSE;
};
IF AUV = NIL OR ADV = NIL THEN {
IF AUV # NIL OR ADV # NIL THEN {
anyErrorsMessages ← Rope.Cat[anyErrorsMessages, "Set of tiles must have all of the extra vertical AND plane tiles or none of them.\n"];
anyErrors ← TRUE;
}
ELSE
haveVerticalAndExtras ← FALSE;
};
IF OUV = NIL OR ODV = NIL THEN {
IF OUV # NIL OR ODV # NIL THEN {
anyErrorsMessages ← Rope.Cat[anyErrorsMessages, "Set of tiles must have all of the extra vertical OR plane tiles or none of them.\n"];
anyErrors ← TRUE;
}
ELSE
haveVerticalOrExtras ← FALSE;
};
IF haveHorizontalExtras THEN {
IF haveVerticalAndExtras AND AHV = NIL THEN {
anyErrorsMessages ← Rope.Cat[anyErrorsMessages, "Tile 'AHV' is missing.\n"];
anyErrors ← TRUE;
};
IF haveVerticalOrExtras AND OHV = NIL THEN {
anyErrorsMessages ← Rope.Cat[anyErrorsMessages, "Tile 'OHV' is missing.\n"];
anyErrors ← TRUE;
};
};
IF Aul # NIL AND AUleft = NIL THEN {
anyErrorsMessages ← Rope.Cat[anyErrorsMessages, "Tile 'Aul' exists but it connects to AUleft which is missing.\n"];
anyErrors ← TRUE;
};
IF All # NIL AND (AUleft = NIL OR ADleft = NIL) THEN {
anyErrorsMessages ← Rope.Cat[anyErrorsMessages, "Tile 'All' exists but it connects to AUleft or ADleft which is missing.\n"];
anyErrors ← TRUE;
};
IF Our # NIL AND OUright = NIL THEN {
anyErrorsMessages ← Rope.Cat[anyErrorsMessages, "Tile 'Our' exists but it connects to OUright which is missing.\n"];
anyErrors ← TRUE;
};
IF Olr # NIL AND (OUright = NIL OR ODright = NIL) THEN {
anyErrorsMessages ← Rope.Cat[anyErrorsMessages, "Tile 'Olr' exists but it connects to OUright or ODright which is missing.\n"];
anyErrors ← TRUE;
};
IF anyErrors THEN ERROR Error[TileSet, anyErrorsMessages];
END;
rowTabInitialized: BOOLFALSE;
rowTab: ARRAY BOOL --and plane-- OF
ARRAY BOOL --upper row-- OF
ARRAY BOOL -- left col-- OF
ARRAY BoolOps.TruthBit --bit-- OF Parquet.Tile;
OptAdjTile: PROC [oldTile: Parquet.PlacedTile, newTile: Parquet.Tile, dir: Parquet.Direction, orient: Parquet.Orientation, newBlock: Rope.ROPE] RETURNS[Parquet.PlacedTile] = INLINE BEGIN
pt: Parquet.PlacedTile;
oldBlock: Rope.ROPE;
IF newTile = NIL THEN RETURN[NIL];
oldBlock ← Parquet.GetCurrentBlock[constructedPLA];
Parquet.SwitchBlock[constructedPLA, newBlock];
pt ← Parquet.AdjacentTile[oldTile, newTile, dir, orient];
Parquet.SwitchBlock[constructedPLA, oldBlock];
RETURN[pt];
END;
PlaceExtra: PROC[startBelow: Parquet.PlacedTile] RETURNS[Parquet.PlacedTile] = BEGIN
prevTile, startRow: Parquet.PlacedTile;
tilesSinceExtra: INT;
tile: Parquet.Tile;
-- AND plane
Parquet.SwitchBlock[constructedPLA, AndBlock];
tilesSinceExtra ← 0;
FOR i: INT IN [0 .. numInputs) DO
IF tilesSinceExtra+1 >= desc.extraAndColumns AND haveVerticalAndExtras AND EVEN[i] THEN {
-- do extra vertical columns in AND plane
prevTile ← Parquet.AdjacentTile[prevTile, AHV, $rightOf, Parquet.IdentityOrient]
};
IF i = 0 THEN {
-- do first tile in core of row, and tiles to its left
startRow ← prevTile ← Parquet.AdjacentTile[startBelow, ALH, $below, Parquet.IdentityOrient];
[] ← OptAdjTile[prevTile, AHleft, $leftOf, Parquet.IdentityOrient, LeftBlock]
}
ELSE {
-- do other tiles in core of AND plane, and tiles above and below
IF EVEN[i] THEN tile ← ALH ELSE tile ← ARH;
prevTile ← Parquet.AdjacentTile[prevTile, tile, $rightOf, Parquet.IdentityOrient];
};
tilesSinceExtra ← tilesSinceExtra + 1;
ENDLOOP;
-- Between planes
Parquet.SwitchBlock[constructedPLA, MidBlock];
prevTile ← Parquet.AdjacentTile[prevTile, BH, $rightOf, Parquet.IdentityOrient];
-- OR plane
Parquet.SwitchBlock[constructedPLA, OrBlock];
tilesSinceExtra ← 0;
FOR i: INT IN [0 .. numOutputs) DO
IF tilesSinceExtra+1 >= desc.extraOrColumns AND haveVerticalOrExtras AND EVEN[i] THEN {
-- do extra vertical columns in OR plane
prevTile ← Parquet.AdjacentTile[prevTile, OHV, $rightOf, Parquet.IdentityOrient]
};
IF EVEN[i] THEN tile ← OLH ELSE tile ← ORH;
prevTile ← Parquet.AdjacentTile[prevTile, tile, $rightOf, Parquet.IdentityOrient];
IF i = numOutputs - 1 THEN {
t: Parquet.PlacedTile;
-- do tiles to the right of the last core tile
t ← OptAdjTile[prevTile, OHright, $rightOf, Parquet.IdentityOrient, RightBlock];
};
tilesSinceExtra ← tilesSinceExtra + 1;
ENDLOOP;
-- we're done with the row, return the start of it
RETURN[startRow];
END;
PlaceRow: PROC[startBelow: Parquet.PlacedTile, rowNum: INT] RETURNS[Parquet.PlacedTile] = BEGIN
topRow: BOOL ← (rowNum = 0);
botRow: BOOL ← (rowNum = numPTerms - 1);
prevTile, startRow: Parquet.PlacedTile;
tilesSinceExtra: INT;
tile: Parquet.Tile;
IF ~rowTabInitialized THEN {
-- up (U) rows of AND plane
rowTab[TRUE][TRUE][TRUE][$One] ← AUL1;
rowTab[TRUE][TRUE][TRUE][$Zero] ← AUL0;
rowTab[TRUE][TRUE][TRUE][$NC] ← AULx;
rowTab[TRUE][TRUE][FALSE][$One] ← AUR1;
rowTab[TRUE][TRUE][FALSE][$Zero] ← AUR0;
rowTab[TRUE][TRUE][FALSE][$NC] ← AURx;
-- down (D) rows of AND plane
rowTab[TRUE][FALSE][TRUE][$One] ← ADL1;
rowTab[TRUE][FALSE][TRUE][$Zero] ← ADL0;
rowTab[TRUE][FALSE][TRUE][$NC] ← ADLx;
rowTab[TRUE][FALSE][FALSE][$One] ← ADR1;
rowTab[TRUE][FALSE][FALSE][$Zero] ← ADR0;
rowTab[TRUE][FALSE][FALSE][$NC] ← ADRx;
-- up (U) rows of OR plane
rowTab[FALSE][TRUE][TRUE][$One] ← OUL1;
rowTab[FALSE][TRUE][TRUE][$Zero] ← OUL0;
rowTab[FALSE][TRUE][TRUE][$NC] ← OUL0;
rowTab[FALSE][TRUE][FALSE][$One] ← OUR1;
rowTab[FALSE][TRUE][FALSE][$Zero] ← OUR0;
rowTab[FALSE][TRUE][FALSE][$NC] ← OUR0;
-- down (D) rows of OR plane
rowTab[FALSE][FALSE][TRUE][$One] ← ODL1;
rowTab[FALSE][FALSE][TRUE][$Zero] ← ODL0;
rowTab[FALSE][FALSE][TRUE][$NC] ← ODL0;
rowTab[FALSE][FALSE][FALSE][$One] ← ODR1;
rowTab[FALSE][FALSE][FALSE][$Zero] ← ODR0;
rowTab[FALSE][FALSE][FALSE][$NC] ← ODR0;
rowTabInitialized ← TRUE;
};
-- AND plane
Parquet.SwitchBlock[constructedPLA, AndBlock];
tilesSinceExtra ← 0;
FOR i: INT IN [0 .. numInputs) DO
tile ← rowTab[TRUE][EVEN[rowNum]][EVEN[i]][ourTruthTable.pterms[rowNum].bits[i]];
IF tilesSinceExtra+1 >= desc.extraAndColumns AND haveVerticalAndExtras AND EVEN[i] THEN {
-- do extra vertical columns in AND plane
IF EVEN[rowNum] THEN
prevTile ← Parquet.AdjacentTile[prevTile, AUV, $rightOf, Parquet.IdentityOrient]
ELSE
prevTile ← Parquet.AdjacentTile[prevTile, ADV, $rightOf, Parquet.IdentityOrient];
-- upper tile
IF rowNum = 0 THEN [] ← OptAdjTile[prevTile, AVtop, $above, Parquet.IdentityOrient, TopAndBlock];
-- lower tile
IF rowNum = numPTerms - 1 THEN [] ← OptAdjTile[prevTile, AVbot, $below, Parquet.IdentityOrient, BotAndBlock];
};
IF i = 0 THEN {
t: Parquet.PlacedTile;
-- do first tile in core of row, and tiles to its left
startRow ← prevTile ← Parquet.AdjacentTile[startBelow, tile, $below, Parquet.IdentityOrient];
IF EVEN[rowNum] THEN
t ← OptAdjTile[prevTile, AUleft, $leftOf, Parquet.IdentityOrient, LeftBlock]
ELSE
t ← OptAdjTile[prevTile, ADleft, $leftOf, Parquet.IdentityOrient, LeftBlock];
-- upper left tile
IF rowNum = 0 THEN [] ← OptAdjTile[t, Aul, $above, Parquet.IdentityOrient, LeftBlock];
-- lower left tile
IF rowNum = numPTerms - 1 THEN [] ← OptAdjTile[t, All, $below, Parquet.IdentityOrient, LeftBlock];
}
ELSE {
-- do other tiles in core of AND plane, and tiles above and below
prevTile ← Parquet.AdjacentTile[prevTile, tile, $rightOf, Parquet.IdentityOrient];
};
IF rowNum = 0 THEN {
-- tiles above
IF EVEN[i] THEN
[] ← OptAdjTile[prevTile, ALtop, $above, Parquet.IdentityOrient, TopAndBlock]
ELSE
[] ← OptAdjTile[prevTile, ARtop, $above, Parquet.IdentityOrient, TopAndBlock];
};
IF rowNum = numPTerms - 1 THEN {
-- tiles below
IF EVEN[i] THEN
[] ← OptAdjTile[prevTile, ALbot, $below, Parquet.IdentityOrient, BotAndBlock]
ELSE
[] ← OptAdjTile[prevTile, ARbot, $below, Parquet.IdentityOrient, BotAndBlock];
};
tilesSinceExtra ← tilesSinceExtra + 1;
ENDLOOP;
-- Between planes
Parquet.SwitchBlock[constructedPLA, MidBlock];
IF EVEN[rowNum] THEN
prevTile ← Parquet.AdjacentTile[prevTile, BU, $rightOf, Parquet.IdentityOrient]
ELSE
prevTile ← Parquet.AdjacentTile[prevTile, BD, $rightOf, Parquet.IdentityOrient];
-- tile above
IF rowNum = 0 THEN [] ← OptAdjTile[prevTile, Btop, $above, Parquet.IdentityOrient, MidBlock];
-- tile below
IF rowNum = numPTerms - 1 THEN [] ← OptAdjTile[prevTile, Bbot, $below, Parquet.IdentityOrient, MidBlock];
-- OR plane
Parquet.SwitchBlock[constructedPLA, OrBlock];
tilesSinceExtra ← 0;
FOR i: INT IN [0 .. numOutputs) DO
tile ← rowTab[FALSE][EVEN[rowNum]][EVEN[i]][ourTruthTable.pterms[rowNum].bits[i + numInputs]];
IF tilesSinceExtra+1 >= desc.extraOrColumns AND haveVerticalOrExtras AND EVEN[i] THEN {
-- do extra vertical columns in OR plane
IF EVEN[rowNum] THEN
prevTile ← Parquet.AdjacentTile[prevTile, OUV, $rightOf, Parquet.IdentityOrient]
ELSE
prevTile ← Parquet.AdjacentTile[prevTile, ODV, $rightOf, Parquet.IdentityOrient];
-- upper tile
IF rowNum = 0 THEN [] ← OptAdjTile[prevTile, OVtop, $above, Parquet.IdentityOrient, TopOrBlock];
-- lower tile
IF rowNum = numPTerms - 1 THEN [] ← OptAdjTile[prevTile, OVbot, $below, Parquet.IdentityOrient, BotOrBlock];
};
prevTile ← Parquet.AdjacentTile[prevTile, tile, $rightOf, Parquet.IdentityOrient];
IF i = numOutputs - 1 THEN {
t: Parquet.PlacedTile;
-- do tiles to the right of the last core tile
IF EVEN[rowNum] THEN
t ← OptAdjTile[prevTile, OUright, $rightOf, Parquet.IdentityOrient, RightBlock]
ELSE
t ← OptAdjTile[prevTile, ODright, $rightOf, Parquet.IdentityOrient, RightBlock];
-- upper left tile
IF rowNum = 0 THEN [] ← OptAdjTile[t, Our, $above, Parquet.IdentityOrient, RightBlock];
-- lower left tile
IF rowNum = numPTerms - 1 THEN [] ← OptAdjTile[t, Olr, $below, Parquet.IdentityOrient, RightBlock];
};
IF rowNum = 0 THEN {
-- tiles above
IF EVEN[i] THEN
[] ← OptAdjTile[prevTile, OLtop, $above, Parquet.IdentityOrient, TopOrBlock]
ELSE
[] ← OptAdjTile[prevTile, ORtop, $above, Parquet.IdentityOrient, TopOrBlock];
};
IF rowNum = numPTerms - 1 THEN {
-- tiles below
IF EVEN[i] THEN
[] ← OptAdjTile[prevTile, OLbot, $below, Parquet.IdentityOrient, BotOrBlock]
ELSE
[] ← OptAdjTile[prevTile, ORbot, $below, Parquet.IdentityOrient, BotOrBlock];
};
tilesSinceExtra ← tilesSinceExtra + 1;
ENDLOOP;
-- we're done with the row, return the start of it
RETURN[startRow];
END;
-- start of main body of module generator --
IF context = NIL THEN ERROR Error[NoDescription, "No description was specified!"];
-- store parameters for tiles
{
ourParams: SymTab.Ref ← SymTab.Create[];
ExprRead.StoreInt[ourParams, "numInputs", numInputs];
ExprRead.StoreInt[ourParams, "numOutputs", numOutputs];
ExprRead.StoreInt[ourParams, "numProductTerms", numPTerms];
Parquet.CombineParameters[context, ourParams];
};
-- check & create input
desc ← ReadParameters[context];
IF desc.truthTable = NIL THEN ERROR Error[NoTruthTable, "No truth table was specified!"];
-- optimize
IF desc.optimize THEN
ourTruthTable ← BoolOps.TTOptimize[desc.truthTable]
ELSE
ourTruthTable ← desc.truthTable;
IF desc.dumpFile # NIL THEN DumpTruthTable[ourTruthTable, desc.dumpFile];
-- initialization
LoadTiles[context];
numInputs ← ourTruthTable.numInputs;
numOutputs ← ourTruthTable.numOutputs;
numPTerms ← ourTruthTable.numPTerms;
[constructedPLA, startOfPreviousRow] ← Parquet.NewModule[context];
-- create the PLA row by row, inserting extra ground rows as needed
{
noisy: BOOL ← (numPTerms > desc.printRows);
rowsSinceExtra: INT ← 0;
rowsSinceBlock: INT ← -1;
blocksSinceChunk: INT ← -1;
blockNumber: INT ← 1;
chunkNumber: INT ← 1;
rowsPerBlock: INTMAX[1, maxCellSize / MAX[numInputs, numOutputs, 1]];
blocksPerChunk: INT ← -1;
AndChunk, OrChunk: Rope.ROPENIL;
IF numPTerms / rowsPerBlock > 5 THEN
blocksPerChunk ← MAX[1, maxChunkSize / rowsPerBlock];
IF noisy THEN TerminalIO.WriteRope["Rows (product terms) placed: "];
FOR row: INT IN [0 .. numPTerms) DO
IF (blocksSinceChunk > blocksPerChunk OR blocksSinceChunk < 0) AND blocksPerChunk > 0 THEN {
AndChunk ← Rope.Concat["PLA-andBlock", Convert.RopeFromInt[chunkNumber, 10, FALSE]];
OrChunk ← Rope.Concat["PLA-orBlock", Convert.RopeFromInt[chunkNumber, 10, FALSE]];
chunkNumber ← chunkNumber + 1;
blocksSinceChunk ← 0;
};
IF rowsSinceBlock > rowsPerBlock OR rowsSinceBlock < 0 THEN {
AndBlock ← Rope.Concat["PLA-and", Convert.RopeFromInt[blockNumber, 10, FALSE]];
OrBlock ← Rope.Concat["PLA-or", Convert.RopeFromInt[blockNumber, 10, FALSE]];
IF ~Parquet.NestBlock[constructedPLA, AndBlock, AndChunk] THEN ERROR;
IF ~Parquet.NestBlock[constructedPLA, OrBlock, OrChunk] THEN ERROR;
blockNumber ← blockNumber + 1;
blocksSinceChunk ← blocksSinceChunk + 1;
rowsSinceBlock ← 0;
};
IF rowsSinceExtra+1 >= desc.extraRows AND haveHorizontalExtras AND EVEN[row] THEN {
startOfPreviousRow ← PlaceExtra[startBelow: startOfPreviousRow];
rowsSinceExtra ← 0;
};
startOfPreviousRow ← PlaceRow[startBelow: startOfPreviousRow, rowNum: row];
IF noisy THEN TerminalIO.WriteInt[row + 1];
rowsSinceExtra ← rowsSinceExtra + 1;
rowsSinceBlock ← rowsSinceBlock + 1;
ENDLOOP;
IF noisy THEN TerminalIO.WriteRope[" . . . that's all.\n"];
};
-- we're done!
RETURN[constructedPLA];
END;
-- get the truth table from a description
FetchTT: PUBLIC PROC [context: Parquet.Context] RETURNS [inputNames, outputNames: LIST OF Rope.ROPE, truthTable: BoolOps.TruthTable] = BEGIN
GetEQN: PROC[inNames, outNames: LIST OF Rope.ROPE] RETURNS[BoolOps.TruthTable] = BEGIN
tt: BoolOps.TruthTable;
problems: BOOLFALSE;
inAtoms, reverseAtoms: LIST OF ATOMNIL;
eqnTrees, reversedEqnTrees: LIST OF BoolOps.Tree ← NIL;
message: Rope.ROPE;
allErrorMessages: Rope.ROPENIL;
IF inNames = NIL OR outNames = NIL THEN
ERROR Error[ParameterErrors, "Both 'Inputs' and 'Outputs' must be specified."];
-- read equations one at a time, converting to sum-of-products as we go
FOR outs: LIST OF Rope.ROPE ← outNames, outs.rest WHILE outs # NIL DO
eqn: Rope.ROPENIL;
t: BoolOps.Tree ← NIL;
eqn ← ExprRead.FetchRope[context.parameters, outs.first, TRUE].val;
IF eqn = NIL THEN {
problems ← TRUE;
allErrorMessages ← Rope.Cat[allErrorMessages, "Equation for output '", outs.first, "' is missing.\n"];
};
FOR i: INT DECREASING IN [0 .. Rope.Length[eqn]) DO -- remove trailing semicolon
ch: CHAR ← Rope.Fetch[eqn, i];
IF ch # ' AND ch # '\t AND ch # '\n THEN {
IF ch = '; AND i > 0 THEN eqn ← Rope.Substr[eqn, 0, i];
EXIT;
};
ENDLOOP;
message ← NIL;
t ← BoolOps.RopeToTree[eqn ! BoolOps.Error => {message ← msg; CONTINUE}];
IF message # NIL THEN {
problems ← TRUE;
allErrorMessages ← Rope.Cat[allErrorMessages, "Could not translate equation for output '", outs.first, "', error was: ", message, "\n"];
}
ELSE {
message ← NIL;
t ← BoolOps.TreeToSOP[t ! BoolOps.Error => {message ← msg; CONTINUE}];
IF message # NIL THEN {
problems ← TRUE;
allErrorMessages ← Rope.Cat[allErrorMessages, "Could not translate output '", outs.first, "' to sum-of-products form, error was: ", message, "\n"];
}
ELSE {
reversedEqnTrees ← CONS[t, reversedEqnTrees];
};
};
ENDLOOP;
IF problems THEN ERROR Error[ParameterErrors, allErrorMessages];
-- reverse our list of equations since it is backwards
FOR trees: LIST OF BoolOps.Tree ← reversedEqnTrees, trees.rest WHILE trees # NIL DO
eqnTrees ← CONS[trees.first, eqnTrees];
ENDLOOP;
-- create a list of atoms from the input names
FOR ins: LIST OF Rope.ROPE ← inNames, ins.rest WHILE ins # NIL DO
reverseAtoms ← CONS[Atom.MakeAtom[ins.first], reverseAtoms];
ENDLOOP;
FOR ins: LIST OF ATOM ← reverseAtoms, ins.rest WHILE ins # NIL DO
inAtoms ← CONS[ins.first, inAtoms];
ENDLOOP;
-- make a truth table
message ← NIL;
tt ← BoolOps.SOPToTT[inAtoms, eqnTrees ! BoolOps.Error => {message ← msg; CONTINUE}];
IF message # NIL THEN
ERROR Error[ParameterErrors, Rope.Cat["Could not translate equations to truth table form, error was: ", message, "\n"]];
RETURN[tt];
END;
GetTTFile: PROC[] RETURNS[BoolOps.TruthTable] = BEGIN
tt: BoolOps.TruthTable;
ttFile: Rope.ROPE;
ttStream: IO.STREAM;
explanation: Rope.ROPE;
ttFile ← ExprRead.FetchRope[context.parameters, "TruthTableFile", TRUE].val;
IF ttFile = NIL THEN RETURN[NIL];
ttStream ← FS.StreamOpen[ttFile ! FS.Error => IF error.group = user THEN {ttStream ← NIL; CONTINUE}];
IF ttStream = NIL THEN
ERROR Error[ParameterErrors, Rope.Cat["Could not open truth table file '", ttFile, "'.\n"]];
tt ← BoolOps.StreamToTT[ttStream ! BoolOps.Error => {explanation ← msg; CONTINUE}];
IF explanation # NIL THEN
ERROR Error[ParameterErrors, Rope.Cat["Error in reading truth table: ", explanation, "\n"]];
RETURN[tt];
END;
GetTT: PROC[] RETURNS[BoolOps.TruthTable] = BEGIN
ref: REF ANY;
ref ← ExprRead.FetchRefAny[context.parameters, "TruthTable", TRUE].val;
IF ref = NIL THEN RETURN[NIL];
IF ~ISTYPE[ref, BoolOps.TruthTable] THEN ERROR Error[ParameterErrors, Rope.Cat["TruthTable option is not of type BoolOps.TruthTable!"]];
RETURN[NARROW[ref]];
END;
EnsureSize: PROC [list: LIST OF Rope.ROPE, quantity: INT, defaultName: Rope.ROPE] RETURNS [LIST OF Rope.ROPE] = BEGIN
forward, backward: LIST OF Rope.ROPENIL;
FOR i: INT IN [1..quantity] DO
IF list # NIL THEN {
backward ← CONS[list.first, backward];
list ← list.rest;
}
ELSE {
backward ← CONS[Rope.Concat[defaultName, Convert.RopeFromInt[from: i, showRadix: FALSE]], backward];
};
ENDLOOP;
FOR i: INT IN [1..quantity] DO
forward ← CONS[backward.first, forward];
backward ← backward.rest;
ENDLOOP;
RETURN[forward];
END;
-- get names of inputs and outputs
inputNames ← ExprRead.FetchListOfRope[context.parameters, "Inputs", TRUE].val;
outputNames ← ExprRead.FetchListOfRope[context.parameters, "Outputs", TRUE].val;
-- get a truth table
SELECT TRUE FROM
ExprRead.FetchRope[context.parameters, "TruthTableFile", TRUE].found =>
truthTable ← GetTTFile[];
ExprRead.FetchRefAny[context.parameters, "TruthTable", TRUE].found =>
truthTable ← GetTT[];
TRUE =>
truthTable ← GetEQN[inputNames, outputNames];
ENDCASE;
inputNames ← EnsureSize[inputNames, truthTable.numInputs, "input"];
outputNames ← EnsureSize[outputNames, truthTable.numOutputs, "output"];
END;
ReadParameters: PROC [context: Parquet.Context] RETURNS [options: PLADescription] = BEGIN
GetOptions: PROC[context: Parquet.Context] RETURNS[PLADescription] = BEGIN
opt: PLADescription ← NEW[PLADescriptionRec];
bool: BOOL;
card: CARDINAL;
found: BOOL;
-- get equations and names
[opt.inputNames, opt.outputNames, opt.truthTable] ← FetchTT[context];
-- optimize?
[found, bool] ← ExprRead.FetchBool[context.parameters, "Optimize", TRUE];
IF found THEN opt.optimize ← bool;
-- get the number of extra rows and columns
[found, card] ← ExprRead.FetchCardinal[context.parameters, "Extras", TRUE];
IF found THEN opt.extraRows ← opt.extraAndColumns ← opt.extraOrColumns ← card;
[found, card] ← ExprRead.FetchCardinal[context.parameters, "ExtraRows", TRUE];
IF found THEN opt.extraRows ← card;
[found, card] ← ExprRead.FetchCardinal[context.parameters, "ExtraColumns", TRUE];
IF found THEN opt.extraAndColumns ← opt.extraOrColumns ← card;
[found, card] ← ExprRead.FetchCardinal[context.parameters, "ExtraAndColumns", TRUE];
IF found THEN opt.extraAndColumns ← card;
[found, card] ← ExprRead.FetchCardinal[context.parameters, "ExtraOrColumns", TRUE];
IF found THEN opt.extraOrColumns ← card;
-- print rows
[found, card] ← ExprRead.FetchCardinal[context.parameters, "PrintRows", TRUE];
IF found THEN opt.printRows ← card;
-- dump file
opt.dumpFile ← ExprRead.FetchRope[context.parameters, "TruthTableDumpFile", TRUE].val;
RETURN[opt];
END;
-- get parameters
options ← GetOptions[context];
IF options = NIL THEN ERROR;
IF options.truthTable = NIL THEN ERROR;
options.context ← context;
RETURN[options];
END;
MakePLAProc: Parquet.ModuleGeneratorProc = BEGIN
-- PROC[context: Context] RETURNS [Module]
m: Parquet.Module;
errMsg: Rope.ROPENIL;
m ← Create[context ! Error => {errMsg ← msg; CONTINUE}];
IF errMsg # NIL THEN {
TerminalIO.WriteRope[errMsg];
RETURN[NIL];
};
RETURN[m];
END;
NoOp: Commander.CommandProc = BEGIN   
END;
-- register this generator with Parquet
[] ← Parquet.RegisterGenerator["MakePLA", MakePLAProc];
-- register a command so that we won't get ".load file failed to register command"
Commander.Register[key: "MakePLA", proc: NoOp, doc: "Does nothing"];
END.