<> <> <> <> <> 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; <<-- 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: BOOL _ TRUE, -- if false we won't mirror PLAs to save space noStretch: BOOL _ FALSE, -- if true we won't stretch along the stretch lines inputNames, outputNames: LIST OF Rope.ROPE _ NIL, dumpFileName: Rope.ROPE _ NIL, -- master name for the dump files outputLocations: LIST OF INT _ NIL -- 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: BOOL _ FALSE; outList: LIST OF INT; first: BOOL _ TRUE; lastRightSide: INT _ 0; outputOffset: INT _ 0; dumpFileName: Rope.ROPE _ NIL; 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.ROPE _ NIL; 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 + <<-- stretch only by whole lambda>> outLoc _ outLoc + (amount * endLoc _ endLoc + (amount * [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.ROPE _ NIL; 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.ROPE _ NIL; placeInts: LIST OF INT _ NIL; 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: BOOL _ FALSE; FOR i: INT IN [0..options.truthTable.numOutputs) DO goofed: BOOL _ FALSE; 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.