DraculaOpsImpl.mesa
Copyright Ó 1987, 1988 by Xerox Corporation. All rights reserved.
Christian Le Cocq September 27, 1988 3:52:57 pm PDT
Jean-Marc Frailong December 28, 1987 3:38:44 pm PST
Last Edited by: Ross March 20, 1987 5:23:14 pm PST
Don Curry July 6, 1988 7:38:58 pm PDT
DIRECTORY
CedarProcess,
CifToCD USING [ReadFile],
CD,
CDCells USING [IsPushedIn],
CDDirectory USING [Name],
CDImports USING [HasUnloadedImports],
CDIO,
CDOps,
CDSequencer USING [Command],
CDSequencerExtras USING [RegisterCommand],
CDToCif USING [WriteCIF],
CDViewer,
Commander, CommandTool,
DraculaOps,
FS,
IO,
Process,
Real,
RealFns,
RefTab,
Rope,
Rsh,
SunAuthUnix USING [FixNameForUnix],
TerminalIO,
ViewerClasses, ViewerOps,
UnixRemoteFile,
UserCredentials USING [Get];
DraculaOpsImpl: CEDAR PROGRAM
IMPORTS CedarProcess, CifToCD, CDCells, CDDirectory, CDImports, CDIO, CDOps, CDSequencerExtras, CDToCif, CDViewer, Commander, CommandTool, FS, IO, Process, Real, RealFns, RefTab, Rope, Rsh, SunAuthUnix, TerminalIO, ViewerOps, UnixRemoteFile, UserCredentials
EXPORTS DraculaOps
~ BEGIN
ROPE: TYPE = Rope.ROPE;
DRCData: TYPE = REF DRCDataRec;
DRCDataRec: TYPE = RECORD [instance: CD.Instance, design: CD.Design, rules: Rope.ROPE];
these four are Unix side dependant
fileHost: ROPE ← "velantia-NFS";
cpuHost: ROPE ← "velantia";
remoteServerDir: ROPE ← "/tmp/";
ecadCmd: ROPE ← "/usr/ecad/bin/dracula ";
description: ROPE ← "
*DESCRIPTION
PRIMARY = %g
WINDOW = %d %d %d %d
PROGRAM-DIR = /usr/ecad/
SYSTEM = CIF
SCALE = 0.01 MIC
RESOLUTION = 0.25 MIC
FLAG-OFFGRID = YES
INDISK = %g.cif
OUTDISK = %g.errors.cif
KEEPDATA = SMART
MODE = EXEC NOW
SYSOUT = CIF
SCALEOUT = 0.25 MIC
PRINTFILE = drclog
*END
";
description: ROPE ← "\l*DESCRIPTION\lPRIMARY = %g\lWINDOW = %d %d %d %d\lPROGRAM-DIR = /usr/ecad/\lSYSTEM = CIF\lSCALE = 0.01 MIC\lRESOLUTION = 0.25 MIC\lFLAG-OFFGRID = YES\lINDISK = %g.cif\lOUTDISK = %g.errors.cif\lKEEPDATA = SMART\lMODE = EXEC NOW\lSYSOUT = CIF\lSCALEOUT = 0.25 MIC\lPRINTFILE = drclog\l*END\l";
overlap: REAL ← 50.0; -- microns
eachJobSize: REAL ← 5.0e7; -- = 0.5cm2
drcTab: RefTab.Ref ← RefTab.Create[];
defaultRulesName: ROPE ← "VTI";
cifExt: ROPE ← ".cif";
descrExt: ROPE ← ".des";
errorsExt: ROPE ← ".errors.cif";
logFile: ROPE ← "DRCLOG.SUM"; --highly flexible name
cifPerLambda: INT ← 100;
flattenAtomics: BOOLEANTRUE;
ecadKey: ATOM ← $EcadErrorsCif; --to identify the set of cif conventions
DRC control
DRCInstance: PUBLIC PROC [instance: CD.Instance, design: CD.Design, rules: Rope.ROPE] RETURNS [reportMsg: Rope.ROPENIL, hasErrors: BOOLEANTRUE] ~ {
drcData: DRCData ← NEW[DRCDataRec ← [instance, design, rules]];
reportMsg ← NARROW[DoDRCInstance[drcData]];
hasErrors ← reportMsg.Length[]#0};
DoDRCInstance: PROC [data: REF] RETURNS [result: REFNIL] ~ {
reportMsg: Rope.ROPE;
instance: CD.Instance;
design: CD.Design;
rules: Rope.ROPE;
fileServer: UnixRemoteFile.UnixServerHandle ← UnixRemoteFile.CreateHandle[fileHost];
myName: Rope.ROPE ← SunAuthUnix.FixNameForUnix[UserCredentials.Get[].name];
workingDir: Rope.ROPE ← Rope.Cat[remoteServerDir, myName];
[instance, design, rules] ← NARROW[data, DRCData]^;
{
ENABLE ABORTED, Process.InvalidProcess => {
reportMsg ← "DRC ABORTED";
GOTO Failed
};
CatchOne: UnixRemoteFile.EachNameProc ~ {
IF Rope.Fetch[name, 0]#'. THEN victimList ← CONS[Rope.Cat[workingDir, "/", name], victimList];
};
victimList: LIST OF ROPE;
topCellName, fileName: ROPE;
remoteFile, log: IO.STREAM;
sizeX, sizeY, minX, minY, maxX, maxY: REAL;
nX, nY: NAT;
hasErrors: BOOLEANFALSE;
lambda: REAL ← design.technology.lambda; -- conversion from INT
dx: REAL ← (instance.ob.bbox.x2-instance.ob.bbox.x1)/lambda;
ox: REAL ← instance.ob.bbox.x1/lambda;
dy: REAL ← (instance.ob.bbox.y2-instance.ob.bbox.y1)/lambda;
oy: REAL ← instance.ob.bbox.y1/lambda;
topCellName ← CDDirectory.Name[instance.ob, design];
IF topCellName=NIL THEN {reportMsg ← "Impossible to DRC non named objects"; GOTO Failed;};
UnixRemoteFile.MkDir[fileServer, [workingDir], 0666B ! UnixRemoteFile.Error => {
IF code=$exist THEN {
TerminalIO.PutRope[Rope.Cat["Dracula Warning: ", fileHost, ":", workingDir, " already exists\n"]];
CONTINUE;
}
ELSE {
reportMsg ← IO.PutFR["Dracula Error: %g (%g)\n", IO.rope[msg], IO.atom[code]];
GOTO Failed;
}
}];
fileName ← Rope.Cat[workingDir, "/", topCellName, cifExt];
remoteFile ← UnixRemoteFile.OpenWriteStream[fileServer, [fileName], 0666B ! UnixRemoteFile.Error =>
{reportMsg ← IO.PutFR["WriteError: %g (%g)\n", IO.rope[msg], IO.atom[code]]; GOTO Failed;}];
TerminalIO.PutRopes["Dracula DRC. CIF file: ", fileName, "\n"];
reportMsg ← CDToCif.WriteCIF[design, instance, remoteFile, cifPerLambda, flattenAtomics];
IO.Close[remoteFile];
IF reportMsg#NIL THEN GOTO Failed;
[sizeX, sizeY, nX, nY] ← GetJobSize[dx, dy];
minX ← ox-overlap;
maxX ← ox+sizeX+overlap;
THROUGH [0..nX) DO
minY ← oy-overlap;
maxY ← oy+sizeY+overlap;
THROUGH [0..nY) DO
fileName ← Rope.Cat[workingDir, "/", topCellName, descrExt];
remoteFile ← UnixRemoteFile.OpenWriteStream[fileServer, [fileName], 0666B ! UnixRemoteFile.Error =>
{reportMsg ← IO.PutFR["WriteError: %g (%g)\n", IO.rope[msg], IO.atom[code]]; GOTO Failed;}];
IO.PutFL[stream: remoteFile, format: description, list: LIST[
[rope[topCellName]], -- PRIMARY = cellName
[real[minX]], [real[minY]], [real[maxX]], [real[maxY]], --WINDOW x1 y1 x2 y2
[rope[topCellName]], -- INDISK = %g.cif
[rope[topCellName]] ]]; -- OUTDISK = %g.errors.cif
IO.Close[remoteFile];
log ← FS.StreamOpen[fileName: Rope.Cat[topCellName, ".log"], accessOptions: create];
IF nX#1 OR nY#1 THEN TerminalIO.PutF["Dracula: Now DRCing [%d, %d, %d, %d]\n", [real[minX]], [real[minY]], [real[maxX]], [real[maxY]]];
reportMsg ← Rsh.RSH[
remoteMachine: cpuHost,
command: Rope.Cat[Rope.Cat["cd ", workingDir, "; "], ecadCmd, topCellName, " ", rules], --i.e. cd /tmp/lecocq/; /usr/ecad/bin/dracula thisCell VTI
in: IO.noInputStream, out: log, error: TerminalIO.TOS[]
];
IO.Close[log];
IF reportMsg#NIL THEN GOTO Failed;
fileName ← Rope.Cat[workingDir, "/", logFile];
remoteFile ← UnixRemoteFile.OpenReadStream[fileServer, [fileName] ! UnixRemoteFile.Error =>
{reportMsg ← IO.PutFR["ReadError: %g (%g)\n", IO.rope[msg], IO.atom[code]]; GOTO Failed;}];
IF CheckForErrors[remoteFile ! IO.Error, IO.EndOfStream => {reportMsg ← "Bad or no DRCSUM file";GOTO Failed;}] THEN {
fileName ← Rope.Cat[workingDir, "/", topCellName, errorsExt];
remoteFile ← UnixRemoteFile.OpenReadStream[fileServer, [fileName] ! UnixRemoteFile.Error =>
{reportMsg ← IO.PutFR["ReadError: %g (%g)\n", IO.rope[msg], IO.atom[code]]; CONTINUE;}];
IF reportMsg#NIL THEN GOTO Failed;
reportMsg ← CifToCD.ReadFile[remoteFile, design, ecadKey, instance.trans];
IF reportMsg#NIL THEN GOTO Failed;
hasErrors ← TRUE;
};
minY ← minY+sizeY;
maxY ← maxY+sizeY;
ENDLOOP;
minX ← minX+sizeX;
maxX ← maxX+sizeX;
ENDLOOP;
IF hasErrors THEN CDOps.RedrawInstance[design, instance]
ELSE TerminalIO.PutRope["\n*\n* Dracula: No DRC Errors !\n*\n"];
UnixRemoteFile.Enumerate[fileServer, [workingDir], "*", CatchOne];
UNTIL victimList=NIL DO
UnixRemoteFile.Delete[fileServer, [victimList.first]];
victimList ← victimList.rest;
ENDLOOP;
UnixRemoteFile.RmDir[fileServer, [workingDir]]
EXITS Failed => {TerminalIO.PutRope[reportMsg]; result ← reportMsg;}
};
UnixRemoteFile.DestroyHandle[fileServer];
[] ← RefTab.Delete[drcTab, instance];
};
GetJobSize: PROC [dx, dy: REAL] RETURNS [sizeX, sizeY: REAL, nX, nY: NAT] ~ {
bigFactor, smallFactor, nTimes: NAT;
nTimes ← Real.Ceiling[dx*dy/eachJobSize];
smallFactor ← GetDiv[nTimes];
bigFactor ← nTimes/smallFactor;
IF dx<dy THEN {
sizeX ← dx/smallFactor;
sizeY ← dy/bigFactor;
nX ← smallFactor;
nY ← bigFactor;
}
ELSE {
sizeX ← dx/bigFactor;
sizeY ← dy/smallFactor;
nX ← bigFactor;
nY ← smallFactor;
};
};
GetDiv: PROC [n: NAT] RETURNS [div: NAT] ~ {
sqrt: NAT ← Real.Floor[RealFns.SqRt[n]];
FOR i: NAT DECREASING IN [2..sqrt] DO
IF n MOD i = 0 THEN RETURN[i];
ENDLOOP;
RETURN[1];
};
IsEOL: IO.BreakProc = {
[char: CHAR] RETURNS [IO.CharClass]
RETURN [SELECT char FROM
'\n, '\l => sepr,
ENDCASE => other]
};
MyGetLineRope: PROC [s: IO.STREAM] RETURNS [r: ROPE] ~ INLINE {
RETURN[IO.GetTokenRope[stream: s, breakProc: IsEOL].token]
};
CheckForErrors: PROC [logFile: IO.STREAM] RETURNS [hasErrors: BOOLEANFALSE] ~ {
startText: ROPE ← "(LINE SEGMENTS)";
stopText: ROPE ← "PRIMARY CELL :";
l: Rope.ROPE ← NIL;
UNTIL Rope.Find[s1: l, s2: startText]#-1 DO
l MyGetLineRope[logFile];
ENDLOOP;
[] ← IO.SkipWhitespace[logFile];
l MyGetLineRope[logFile];
UNTIL Rope.Find[s1: l, s2: stopText]#-1 DO
hasErrors ← TRUE;
TerminalIO.PutRopes[l, "\n"];
[] ← IO.SkipWhitespace[logFile];
l MyGetLineRope[logFile];
ENDLOOP;
};
CommandTool Command
doc: IO.ROPE ← "DraculaDRC designName [rules]\n";
DraculaDRCProc: Commander.CommandProc = {
Fail: PROC[msg: IO.ROPE] RETURNS[atom: ATOM ← $Failure] =
{cmd.out.PutF["%g\n%g\n", IO.rope[msg], IO.rope[doc]]};
name:   IO.ROPE  ← CommandTool.ArgN[cmd, 1];
design:  CD.Design ← GetDesign[name];
insts:   CD.InstanceList;
rules:   IO.ROPE  ← "VTI";
reportMsg: IO.ROPE  ← NIL;
hasErrors: BOOL   ← TRUE;
name ← name.Substr[0, name.Index[0, "."]];
IF design=NIL THEN {cmd.out.PutRope[doc]; RETURN[$Failure]};
IF design#NIL THEN insts ← CDOps.InstList[design];
SELECT TRUE FROM
design=NIL  => {RETURN[Fail["*** Design not found."]]};
insts=NIL   => {RETURN[Fail["*** Design empty."]]};
insts.rest#NIL => {RETURN[Fail["*** Design has multiple instances."]]};
ENDCASE;
IF rules.Length[]=0 THEN rules ← "VTI";
[reportMsg, hasErrors] ← DRCInstance[insts.first, design, rules];
IF hasErrors
THEN cmd.out.PutF["*** DRC Errors\n %g\n", IO.rope[reportMsg]]
ELSE cmd.out.PutRope[" No DRC Errors\n\n"]};
GetDesign: PUBLIC PROC [name: IO.ROPE] RETURNS [design: CD.Design] = {
viewer: ViewerClasses.Viewer;
design ← CDViewer.FindDesign[name];
IF design#NIL THEN RETURN[design];
IF name.Length[]=0 THEN RETURN[NIL];
design ← CDIO.ReadDesign[name];
CDOps.SetMutability[design];
viewer�Viewer.CreateViewer[design, FALSE];
ViewerOps.CloseViewer[viewer]};
CD Commands
DraculaDRCCmd: PROC [comm: CDSequencer.Command] ~ {
Called by ChipNDale upon activation of the command.
drcData: DRCData;
mainInst: CD.Instance;
IF CDCells.IsPushedIn[comm.design] THEN {
TerminalIO.PutRope["**Design is pushed in\n"];
GOTO Exit
};
IF CDImports.HasUnloadedImports[comm.design].yes THEN {
TerminalIO.PutRope["**Design has unbound imports\n"];
GOTO Exit
};
mainInst ← CDOps.TheInstance[comm.design, "cif generation\n"];
IF mainInst=NIL THEN {
TerminalIO.PutRope["**CIF generation needs single selected object\n"];
GOTO Exit
};
drcData ← NEW[DRCDataRec ← [mainInst, comm.design, defaultRulesName]];
[] ← RefTab.Store[drcTab, mainInst,
CedarProcess.Fork[DoDRCInstance, drcData,[background, TRUE, TRUE]]];
TerminalIO.PutRope["Dracula started\n"];
EXITS Exit => {};
};
DraculaAbortCmd: PROC [comm: CDSequencer.Command] ~ {
Called by ChipNDale upon activation of the command.
GetIt: RefTab.EachPairAction ~ {
found ← TRUE;
value ← val;
};
found: BOOLEAN ← FALSE;
value: RefTab.Val;
mainInst: CD.Instance ← CDOps.TheInstance[comm.design, "drc delete\n"];
IF mainInst#NIL THEN [found, value] ← RefTab.Fetch[x: drcTab, key: mainInst];
IF NOT found AND RefTab.GetSize[drcTab]=1 THEN [] ← RefTab.Pairs[drcTab, GetIt];
IF found THEN {
process: CedarProcess.Process ← NARROW[value];
IF process.status=busy THEN TRUSTED {Process.Abort[process.process]};
TerminalIO.PutRope[" DRC Abort requested\n"];
}
ELSE {
IF RefTab.GetSize[drcTab]=0 THEN TerminalIO.PutRope[" no DRC pending\n"]
ELSE TerminalIO.PutRope[" multiple DRCs pending, select one instance\n"];
}
};
CDSequencerExtras.RegisterCommand[key: $DraculaDrc, proc: DraculaDRCCmd, queue: doQueue];
CDSequencerExtras.RegisterCommand[key: $DraculaDel, proc: DraculaAbortCmd, queue: doQueue];
TerminalIO.PutRope["DraculaOps loaded\n"];
Commander.Register[key:"DraculaDRC", proc: DraculaDRCProc, doc: doc];
END.