File: MakeCPImpl.mesa   
Copyright © 1984 by Xerox Corporation. All rights reserved.
Created by: Bob July 30, 1984 11:36:59 am PDT
Last Edited by: Mayo, August 31, 1984 1:15:37 am PDT
Make a Control Path. A control path is just a bunch of 1-OR-term PLAs pitch matched to a datapath.
DIRECTORY
MakeCP,
MakePLA USING [Create, Error, FetchTT],
Commander USING [CommandProc, Register],
Parquet,
ExprRead USING [FetchListOfRope, FetchBool, FetchRope, StoreRefAny, StoreRope, Error],
BoolOps USING [TruthTable, TruthTableRec, PTerm],
TerminalIO, Rope, IO, FS, SymTab, Atom, Convert,
CD USING [lambda];
MakeCPImpl: CEDAR PROGRAM    
IMPORTS Commander, Parquet, ExprRead, SymTab, MakePLA, TerminalIO, Rope, Convert EXPORTS MakeCP = BEGIN
OPEN MakeCP;
Error: PUBLIC ERROR[ec: ErrorCode, msg: Rope.ROPE] ~ CODE;
l: INT ~ CD.lambda;
-- 2 orientations for the base PLAs
NormalOrient: Parquet.Orientation ~ Parquet.IdentityOrient;
MirroredOrient: Parquet.Orientation ← Parquet.MirrorY;
CPDescription: TYPE = REF CPDescriptionRec;
CPDescriptionRec: TYPE = RECORD [
context: Parquet.Context ← NIL,
truthTable: BoolOps.TruthTable ← NIL, -- before separation
truthTables: LIST OF BoolOps.TruthTable ← NIL, -- after separation
mirrorOK: BOOLTRUE,  -- if false we won't mirror PLAs to save space
noStretch: BOOLFALSE,  -- if true we won't stretch along the stretch lines
inputNames, outputNames: LIST OF Rope.ROPENIL,
dumpFileName: Rope.ROPENIL, -- master name for the dump files
outputLocations: LIST OF INTNIL -- location of left edge of each output, in lambda. Must be listed in same order as outputNames, with left at the head of the list and right at the tail.
];
Create: PUBLIC PROC [context: Parquet.Context] RETURNS [Parquet.Module] = BEGIN
desc: CPDescription ← NEW[CPDescriptionRec];
constructedCP: Parquet.Module ← NIL;
lastPlace: Parquet.PlacedTile;
outputNames: LIST OF Rope.ROPE;
mismatch: BOOLFALSE;
outList: LIST OF INT;
first: BOOLTRUE;
lastRightSide: INT ← 0;
outputOffset: INT ← 0;
dumpFileName: Rope.ROPENIL;
DoOnePLA: PROC [pt: Parquet.PlacedTile,
oldTop: INT,  -- top of last tile after stretching
tt: BoolOps.TruthTable,
name: Rope.ROPE,
outputLoc: INT,   -- desired output location after stretching
dontPosition: BOOL]
RETURNS
[virtualOutputLoc: INT, -- actual output location after stretching
virtualTop: INT, -- top of this tile after stretching
newPt: Parquet.PlacedTile] = BEGIN
ENABLE
MakePLA.Error => {ERROR Error[BadPLA, Rope.Cat["Could not create base PLA: ", msg]]};
InsertName: PROC [base, insert: Rope.ROPE] RETURNS [Rope.ROPE] = BEGIN
dotPos: INT ← -1;
IF base = NIL OR insert = NIL THEN RETURN[base];
DO
t: INT ← Rope.Find[base, ".", dotPos+1];
IF t = -1 THEN EXIT;
dotPos ← t;
ENDLOOP;
RETURN[Rope.Cat[Rope.Substr[base, 0, dotPos], "-", insert, Rope.Substr[base, dotPos]]];
END;
OrientOption: TYPE = RECORD [
orient: Parquet.Orientation,
endLoc, stretch: INT
];
errorMsg: Rope.ROPENIL;
errors: INT;
plaName: Rope.ROPE ← Rope.Cat["PLA", name];
aPLA: Parquet.Module;
aTile: Parquet.Tile;
normal, mirror, chosen: OrientOption;
mark: Parquet.Mark;
-- Set up options just as a user would
[] ← SymTab.Delete[desc.context.parameters, "TruthTableFile"];
ExprRead.StoreRefAny[desc.context.parameters, "TruthTable", tt];
ExprRead.StoreRope[desc.context.parameters, "TruthTableDumpFile", InsertName[desc.dumpFileName, name]];
-- make a PLA
aPLA ← MakePLA.Create[desc.context];
[errors, aTile] ← Parquet.ModuleIntoTile[module: aPLA, name: plaName, context: context, bbox: TRUE];
IF errors > 0 THEN
TerminalIO.WriteRope[Rope.Cat["Warning: ", Convert.RopeFromInt[errors], " error(s) found in PLA '", plaName, "', area(s) in question are indicated by highlighting.\n"]];
-- find the output and decide if we want to mirror the module
mark ← Parquet.GetMark[aTile, "outputBottom"];
IF mark = NIL THEN {
TerminalIO.WriteRope[Rope.Cat["Error: could not find mark called 'outputBottom' in the base PLA.\n"]];
mark ← Parquet.GetMark[aTile, "lr"];
};
normal.orient ← NormalOrient;
normal.endLoc ← outputLoc + (aTile.obj.size.y - mark.pos.y);
normal.stretch ← outputLoc - (oldTop + mark.pos.y);
IF desc.mirrorOK THEN {
mark ← Parquet.GetMark[aTile, "outputTop"];
IF mark = NIL THEN {
TerminalIO.WriteRope[Rope.Cat["Error: could not find mark called 'outputTop' in the base PLA.\n"]];
mark ← Parquet.GetMark[aTile, "lr"];
};
mirror.orient ← MirroredOrient;
mirror.endLoc ← outputLoc + mark.pos.y;
mirror.stretch ← outputLoc - (oldTop + (aTile.obj.size.y - mark.pos.y));
}
ELSE {
mirror ← normal;
};
SELECT TRUE FROM
(dontPosition OR (mirror.stretch >= 0 AND normal.stretch >= 0)) => {
-- Either will work, pick the one which extends up the least.
IF mirror.endLoc < normal.endLoc THEN chosen ← mirror ELSE chosen ← normal;
};
(mirror.stretch >= 0) => chosen ← mirror;
(normal.stretch >= 0) => chosen ← normal;
(mirror.stretch < 0 AND normal.stretch < 0) => {
-- neither will work, pick the closest
IF mirror.stretch > normal.stretch THEN chosen ← mirror ELSE chosen ← normal;
};
ENDCASE => ERROR;
newPt ← Parquet.AdjacentTileCorners[pt, aTile, above, chosen.orient];
-- now place a stretch line
IF dontPosition OR chosen.stretch < 0 THEN {
RETURN[outputLoc - chosen.stretch, chosen.endLoc - chosen.stretch, newPt];
}
ELSE {
oldY, minX, maxX: INT;
info: Parquet.StretchData;
outLoc: INT ← outputLoc;
endLoc: INT ← chosen.endLoc;
amount: INT ← (chosen.stretch + l - 1) / l;
-- stretch only by whole lambda
outLoc ← outLoc + (amount * l - chosen.stretch);
endLoc ← endLoc + (amount * l - chosen.stretch);
[ymax: oldY, xmin: minX, xmax: maxX] ← Parquet.PlacedTileLoc[pt];
info ← [point: [minX, oldY], length: maxX - minX, direction: up, type: pointed, label: Convert.RopeFromInt[from: amount, showRadix: FALSE]];
Parquet.PlaceStretch[into: pt.owner, data: info];
RETURN[outLoc, endLoc, newPt];
};
END;
Stretch: PROC [mod: Parquet.Module] RETURNS [Parquet.Module] = BEGIN
errors: INT;
aTile: Parquet.Tile;
newMod: Parquet.Module;
place: Parquet.PlacedTile;
[errors, aTile] ← Parquet.ModuleIntoTile[mod, "MakeCP", context];
IF errors > 0 THEN
TerminalIO.WriteRope[Rope.Cat["Warning: ", Convert.RopeFromInt[errors], " error(s) found in resulting module, area(s) in question are indicated by highlighting.\n"]];
[newMod, place] ← Parquet.NewModule[context];
[] ← Parquet.AdjacentTile[place, aTile, rightOf];
RETURN[newMod];
END;
{
errorMsg: Rope.ROPENIL;
desc ← ReadParameters[context ! Error => {errorMsg ← msg; CONTINUE}];
IF errorMsg # NIL THEN {
TerminalIO.WriteRope[errorMsg];
RETURN[NIL];
};
};
outputNames ← desc.outputNames;
[constructedCP, lastPlace] ← Parquet.NewModule[context];
outList ← desc.outputLocations;
TerminalIO.WriteRope["Outputs generated: "];
FOR tt: LIST OF BoolOps.TruthTable ← desc.truthTables, tt.rest WHILE tt # NIL DO
name: Rope.ROPE;
outputLocation, actualLoc: INT;
IF outputNames # NIL THEN {
name ← outputNames.first;
outputNames ← outputNames.rest;
}
ELSE
name ← "";
IF outList # NIL THEN
outputLocation ← outList.first + outputOffset
ELSE
outputLocation ← outputOffset;
[actualLoc, lastRightSide, lastPlace] ← DoOnePLA[lastPlace, lastRightSide, tt.first, name, outputLocation, first];
TerminalIO.WriteRope[Rope.Concat[" ", name]];
IF first THEN {
first ← FALSE;
outputOffset ← actualLoc - outputLocation;
}
ELSE {
IF actualLoc # outputLocation THEN mismatch ← TRUE;
};
IF outList # NIL THEN outList ← outList.rest;
ENDLOOP;
-- we're done!
IF ~desc.noStretch THEN {
TerminalIO.WriteRope[" done.\nStretching result to pitch-match... "];
constructedCP ← Stretch[constructedCP];
};
TerminalIO.WriteRope[" done.\n"];
IF mismatch THEN {
TerminalIO.WriteRope["Warning: Some output lines could not be matched up properly (maybe they are too close together, or on half-lambda grid).\n"];
};
RETURN[constructedCP];
END;
-- Converts a truth table with N outputs to N truth tables with 1 output each. Each truth table uses all of the inputs in the original order. The list of truth tables has the leftmost output (in the table sense) at the head, and the rightmost at the tail.
TTConvert: PUBLIC PROC [tt: BoolOps.TruthTable] RETURNS [LIST OF BoolOps.TruthTable] = BEGIN
ttList: LIST OF BoolOps.TruthTable ← NIL;
FOR outs: INT IN [0..tt.numOutputs) DO
-- make one truth table
oneTt: BoolOps.TruthTable ← NEW[BoolOps.TruthTableRec[tt.numPTerms]]; -- pesemistic size
oneTt.numInputs ← tt.numInputs;
oneTt.numPTerms ← 0; -- we will increment this
oneTt.numOutputs ← 1;
FOR pterms: INT IN [0..tt.numPTerms) DO
p: REF BoolOps.PTerm;
IF tt.pterms[pterms][tt.numInputs+tt.numOutputs-outs-1] # $One THEN LOOP;
-- make one term
p ← NEW[BoolOps.PTerm[tt.numInputs + 1]];
FOR ins: INT IN [0..tt.numInputs) DO
p[ins] ← tt.pterms[pterms][ins];
ENDLOOP;
p[tt.numInputs] ← $One; -- the only output
oneTt.pterms[oneTt.numPTerms] ← p;
oneTt.numPTerms ← oneTt.numPTerms + 1;
ENDLOOP;
IF oneTt.numPTerms = 0 THEN {
-- output is always false, make a truth table for consistencies sake
oneTt.numPTerms ← 1;
oneTt.pterms[0] ← NEW[BoolOps.PTerm[tt.numInputs + 1]];
FOR ins: INT IN [0..tt.numInputs) DO
oneTt.pterms[0][ins] ← $NC;
ENDLOOP;
oneTt.pterms[0][tt.numInputs] ← $Zero;
};
ttList ← CONS[oneTt, ttList];
ENDLOOP;
RETURN[ttList];
END;
ReadParameters: PROC [context: Parquet.Context] RETURNS [CPDescription] = BEGIN
options: CPDescription ← NEW[CPDescriptionRec];
GetOptions: PROC[params: SymTab.Ref] = BEGIN
found, bool: BOOL;
rope: Rope.ROPE;
placeRopes: LIST OF Rope.ROPENIL;
placeInts: LIST OF INTNIL;
errorMsg: Rope.ROPE;
-- output locations
[found, placeRopes] ← ExprRead.FetchListOfRope[params, "OutputLocations", TRUE ! ExprRead.Error => {errorMsg ← msg; CONTINUE}];
IF errorMsg # NIL THEN ERROR Error[ParameterErrors, errorMsg];
{  -- convert the output locations
val: INT ← 0;
didMsg: BOOLFALSE;
FOR i: INT IN [0..options.truthTable.numOutputs) DO
goofed: BOOLFALSE;
IF placeRopes = NIL THEN {
IF ~didMsg THEN
TerminalIO.WriteRope["Warning: Not enough output locations were specified, I'll make some up.\n"];
didMsg ← TRUE;
-- leave val at its previous value
}
ELSE {
val ← Convert.IntFromRope[placeRopes.first ! Convert.Error => {goofed ← TRUE; CONTINUE}];
IF goofed THEN ERROR Error[ParameterErrors, "Could not convert one of the output locations to an integer.\n"];
placeRopes ← placeRopes.rest;
};
placeInts ← CONS[val, placeInts];
ENDLOOP;
FOR place: LIST OF INT ← placeInts, place.rest WHILE place # NIL DO
options.outputLocations ← CONS[place.first, options.outputLocations];
ENDLOOP;
};
-- mirror ok
[found, bool] ← ExprRead.FetchBool[params, "MirrorOK", TRUE];
IF found THEN options.mirrorOK ← bool;
-- no stretch
[found, bool] ← ExprRead.FetchBool[params, "NoStretch", TRUE];
IF found THEN options.noStretch ← bool;
-- dump file name
[found, rope] ← ExprRead.FetchRope[params, "TruthTableDumpFile", TRUE];
IF found THEN options.dumpFileName ← rope;
END;
[options.inputNames, options.outputNames, options.truthTable] ← MakePLA.FetchTT[context];
options.truthTables ← TTConvert[options.truthTable];
GetOptions[context.parameters];
options.context ← context;
RETURN[options];
END;
MakeCPProc: Parquet.ModuleGeneratorProc = BEGIN
-- PROC [context: Context] RETURNS [Module]
CP: Parquet.Module;
CP ← Create[context];
RETURN[CP];
END;
NoOp: Commander.CommandProc = BEGIN   
END;
-- register this generator with Parquet
[] ← Parquet.RegisterGenerator["MakeCP", MakeCPProc];
-- register a command so that we won't get ".load file failed to register command"
Commander.Register[key: "MakeCP", proc: NoOp, doc: "Does nothing"];
END.