CDMEBESMainImpl.mesa
Copyright © 1985, 1987 by Xerox Corporation. All rights reserved.
written by E. McCreight, November 2, 1983 6:03 pm
McCreight, April 30, 1987 6:36:33 pm PDT
Last edited by: Christian Jacobi, May 7, 1987 5:11:20 pm PDT
Last Edited by: McCreight July 31, 1987 12:14:26 pm PDT
A package to output a ChipnDale design in MEBES format for a Perkin-Elmer Lithographic System. Files produced herein should conform to the MEBES Mode I/II, extended mode, or reticle mode pattern file format described in the MEBES Software Manual, document A900-0102C, Data Files, document A900-0024D, publication of
Perkin-Elmer
Electron Beam Technology
3392 Investment Boulevard
Hayward, CA 94545
DIRECTORY
AMBridge, AMTypes, Ascii, Atom, Basics, BasicTime, CD, CDBasics, CDCells, CDCommandOps, CDImports, CDMEBES, CDOps, CDProperties, CDSequencer, CDVArrow, CStitching, FS, IO, PrintTV, Process, Rope, TerminalIO;
CDMEBESMainImpl: CEDAR PROGRAM
IMPORTS AMBridge, Ascii, Atom, Basics, BasicTime, CD, CDBasics, CDCells, CDCommandOps, CDImports, CDMEBES, CDOps, CDProperties, CDSequencer, CDVArrow, CStitching, FS, IO, PrintTV, Process, Rope, TerminalIO
EXPORTS CDMEBES =
BEGIN OPEN CDMEBES;
Global Variables
stripesPerClump: PUBLIC INT ← 10;
defaultTadsPerNm: PUBLIC INT ← 1;
wDir: PUBLIC ROPE ← "///MEBES";
findBBoxByEnumeration: BOOLFALSE;
abortFlag: PUBLIC REF BOOLNEW[BOOL];
foundErrorLayer: BOOLFALSE;
today: Date;
Init: PROC ~ {
CDSequencer.ImplementCommand[key~$MEBESMask , proc~StartMEBESMask, queue~doQueue];
CDCommandOps.RegisterWithMenu[menu~$ProgramMenu, entry~"MEBES mask generation", key~$MEBESMask, proc~StartMEBESMask];
TerminalIO.PutRope["ChipNDale MEBES mask generator loaded\n"];
};
StartMEBESMask: PUBLIC PROC [comm: CDSequencer.Command] =
TRUSTED BEGIN
TerminalIO.PutRope["MEBES-mask generation\n"];
IF CDCells.IsPushedIn[comm.design] THEN {
TerminalIO.PutRope["**Design is pushed in; not done\n"];
RETURN
};
IF CDImports.HasUnloadedImports[comm.design].yes THEN {
TerminalIO.PutRope["**Design has non bound imports; not done\n"];
RETURN
};
TerminalIO.PutRope["Releases lock of design, but design MUST NOT be changed\n"];
abortFlag^ ← FALSE;
[] ← CDCommandOps.DoWithResource[proc: MEBESMask, comm: comm, resource: $MEBESMask];
END;
MEBESMask: PROC [ comm: CDSequencer.Command ] =
BEGIN
NotePunt: PROC = {TerminalIO.PutF["\n*** MEBES mask generation aborted at %g\n", IO.time[]]};
BEGIN ENABLE
BEGIN
ABORTED => GOTO Punt;
TerminalIO.UserAbort => GOTO Punt;
UNWIND => NotePunt[];
END;
MakeMasks[comm: comm, streamGen: SimpleStreamGen];
IF abortFlag^ THEN NotePunt[];
EXITS Punt => NotePunt[];
END;
END;
StreamGenProc: TYPE = PROC [ ms: MaskState ] RETURNS [ dest: IO.STREAM ];
SimpleStreamGen: PROC [ ms: MaskState ] RETURNS [ dest: IO.STREAM ] -- StreamGenProc -- =
BEGIN
dest ← FS.StreamOpen[
fileName: IO.PutFR[format: "%g%g/%g",
v1: IO.rope[wDir], v2: IO.rope[ms.maskSetName], v3: IO.rope[ms.patternFileNames.first]
],
accessOptions: create
];
END;
Prop: PROC [ms: MaskState, prop: REF, default: REF ← $Error] RETURNS [value: REF] = {
value ← CDProperties.GetDesignProp[ms.design, prop];
IF value = NIL THEN value ← CDProperties.GetTechnologyProp[ms.design.technology, prop];
IF value = NIL THEN value ← CDProperties.GetAtomProp[$CDMEBES, prop];
IF value = NIL THEN value ← default;
IF value = $Error THEN
BEGIN
TerminalIO.PutF[" .. no value for property %g.. ", IO.rope[ToRope[prop]]];
ERROR TerminalIO.UserAbort;
END;
};
NumericProp: PROC [ms: MaskState, prop: REF, default: REFNIL] RETURNS [value: INT] = {
value ← NARROW[Prop[ms, prop, default], REF INT]^;
};
MakeMasks: PROC [comm: CDSequencer.Command, streamGen: StreamGenProc ] =
BEGIN
ms: MaskState = NEW[ MaskStateRec ← [
design: comm.design,
drawCommand: NEW[DrawRectangle]]];
TRUSTED {Process.SetPriority[Process.priorityBackground]};
TerminalIO.PutF["\nMEBES Mask Generation\nPreliminary analysis begun at %g.\n", IO.time[]];
IF NOT abortFlag^ THEN
BEGIN ENABLE -- for ERRORs
BEGIN
UNWIND => ms.s ← AbortFile[ms.s];
END;
ComputeReticleClip: PROC [cdToNm: Rational] RETURNS [clip: NmRect] =
BEGIN
AdjustReticleSize: PROC [coord: ROPE, z1, z2: Nm, zSizeProp: ATOM, zOffset: Nm ← 0] RETURNS [newZ1, newZ2: Nm] =
BEGIN
zSize: Nm = NumericProp[ms, zSizeProp, NEW[Nm ← z2-z1]];
IF z2-z1 > zSize THEN
{TerminalIO.PutF[" .. design %g size of %d nanometers is larger than allowed die size of %d nanometers ..", IO.rope[coord], IO.int[z2-z1], IO.int[zSize]];
ERROR TerminalIO.UserAbort};
newZ1 ← (z1+z2-zSize)/2-zOffset;
newZ2 ← newZ1+zSize;
END;
smallestScribedDesignNm: NmRect ← Bloat[
r: ScaleRect[ms.designClip^, cdToNm],
deltaDiameter: 2*(maxBloat+activeToScribeCenter)];
[clip.x1, clip.x2] ← AdjustReticleSize["x", smallestScribedDesignNm.x1, smallestScribedDesignNm.x2, $CDMEBESNmDieSizeX, ms.dieOffset.x];
[clip.y1, clip.y2] ← AdjustReticleSize["y", smallestScribedDesignNm.y1, smallestScribedDesignNm.y2, $CDMEBESNmDieSizeY, ms.dieOffset.y];
END; -- of ComputeReticleClip
maxBloat: Nm ← NumericProp[ms, $CDMEBESNmMaxBloat, NEW[Nm ← 10000]];
activeToScribeCenter: Nm ← NumericProp[ms, $CDMEBESActiveToScribeCenter];
maskCount, otherCount: INT ← 0;
cf: REF CD.ContextFilter ← NEW[CD.ContextFilter ← ALL[TRUE]];
commentLayer: CD.Layer = CD.FetchLayer[t: comm.design.technology, uniqueKey: $comment];
today ← Today[];
ms.maskSetName ← (IF Prop[ms, $CDMEBESMaskSetName, $default] = $default THEN
TerminalIO.RequestRope["Project name? (9 chars, alphanum) "] ELSE ToRope[Prop[ms, $CDMEBESMaskSetName]]);
ms.toolingSpec ← NARROW[Prop[ms, $CDMEBESToolingSpec]];
ms.derivationSpec ← NARROW[Prop[ms, $CDMEBESDerivationSpec]];
ms.nmPerLambda ← NumericProp[ms, $CDMEBESNmPerLambda];
ComputeTadsPerNm[ms];
ms.dieOffset ← [x: NumericProp[ms, $CDMEBESNmDieOffsetX, NEW[Nm ← 0]],
y: NumericProp[ms, $CDMEBESNmDieOffsetY, NEW[Nm ← 0]]];
ms.dropInList ← NARROW[Prop[ms, $CDMEBESDropInList, NIL], LIST OF REF ANY];
ms.designClip ← NEW[CD.Rect ← [0, 0, 0, 0]];
FOR d: LIST OF REF ANY ← ms.derivationSpec, d.rest WHILE d#NIL DO
WITH d.first SELECT FROM
derivation: DerivationMaskSpec =>
MarkInterestingLayers[ms, derivation.cover];
ENDCASE => NULL;
ENDLOOP; -- d
IF commentLayer # CD.errorLayer THEN
cf[commentLayer] ← ms.interestingLayers[commentLayer];
foundErrorLayer ← FALSE;
IF CDCells.IsPushedIn[ms.design] AND
NOT TerminalIO.Confirm[IO.PutFR["WARNING: CDMEBES working on pushed cell %g in design %g. This probably isn't what you want. Confirm to continue...\n", IO.rope[ms.design.actual.first.desc], IO.rope[ms.design.name]]] THEN GOTO AbortMask;
ms.designClip^ ← CDOps.BoundingBox[ms.design];
IF findBBoxByEnumeration THEN {
ms.designClip^ ← [0, 0, 0, 0];
CDOps.DrawDesign[ms.design, CD.CreateDrawRef[[
interestClip~CDBasics.universe,
drawRect~NoteLayer,
stopFlag~abortFlag,
devicePrivate~ms,
contextFilter~cf,
symbolics~FALSE,
showErrors~FALSE,
design~ms.design
]]];
.. measure design in ChipNDale units, leaves result in ms.designClip
};
ms.nmReticleClip ← ComputeReticleClip[
cdToNm: ReduceRational[
[num: ms.nmPerLambda, denom: ms.design.technology.lambda]
]];
FOR d: LIST OF REF ANY ← ms.derivationSpec, d.rest WHILE d # NIL DO
WITH d.first SELECT FROM
derivation: DerivationMaskSpec => maskCount ← maskCount+1
ENDCASE => otherCount ← otherCount+1;
ENDLOOP;
TerminalIO.PutF["Preliminary analysis finished at %g. Making %d real masks and %d other things at %d (x) x %d (y) nm.\n",
IO.time[],
IO.int[maskCount],
IO.int[otherCount],
IO.int[CDBasics.SizeOfRect[ms.nmReticleClip].x],
IO.int[CDBasics.SizeOfRect[ms.nmReticleClip].y]
];
IF foundErrorLayer THEN
TerminalIO.PutRope[" WARNING: CDMEBES found geometry on error layer\n"];
FOR d: LIST OF REF ANY ← ms.derivationSpec, d.rest WHILE d#NIL DO
ms.data ← NIL;
WITH d.first SELECT FROM
derivation: DerivationMaskSpec => {
tooling: ToolingMaskSpec = NARROW[ms.toolingSpec.GetPropFromList[derivation.maskId]];
ms.curMask ← derivation;
IF tooling#NIL
THEN MakeMEBESMask[ms, derivation, tooling, streamGen
! TadTooLarge => {
ComputeTadsPerNm[ms, 2*ms.tadsPerNm];
RETRY
} ] -- a real mask
ELSE TerminalIO.PutF["Mask %g, a part of the derivation spec, is not part of the tooling spec.\n", IO.atom[derivation.maskId]];
};
spec: CoverSpec => -- we're doing analysis, probably
DoAnalysisPass[ms, spec];
ENDCASE => NULL;
IF abortFlag^ THEN GOTO AbortMask;
IF ms.data # NIL THEN
BEGIN
tv: AMTypes.TV;
rs: IO.STREAM = IO.ROS[];
rs.Put[IO.rope["\nAnalysis result .. "]];
TRUSTED {tv ← AMBridge.TVForReferent[ms.data]};
PrintTV.Print[tv: tv, put: rs, depth: 10, width: 1000];
rs.Put[IO.rope["\n"]];
TerminalIO.PutRope[IO.RopeFromROS[rs]];
END;
ENDLOOP; -- d
TerminalIO.PutF["\nMEBES mask generation finished at %g\n", IO.time[]];
EXITS
AbortMask => {ms.s ← AbortFile[ms.s]};
END;
END;
MarkInterestingLayers: PROC [ ms: MaskState, cover: CoverSpec ] = {
WITH cover SELECT FROM
lora: LIST OF REF ANY =>
FOR l: LIST OF REF ANY ← lora, l.rest WHILE l # NIL DO
MarkInterestingLayers[ms, l.first];
ENDLOOP;
layer: CDLayer => ms.interestingLayers[layer.source] ← TRUE;
ENDCASE => NULL;
};
NoteLayer: PROC [ pr: CD.DrawRef, r: CD.Rect, l: CD.Layer ] =
BEGIN
ms: MaskState = NARROW[pr.devicePrivate];
IF abortFlag^ THEN ERROR TerminalIO.UserAbort;
ms.designClip^ ← IF CDBasics.NonEmpty[ms.designClip^] THEN CDBasics.Surround[ms.designClip^, r] ELSE r;
IF l=CD.errorLayer THEN {
foundErrorLayer ← TRUE;
pr.contextFilter[CD.errorLayer] ← FALSE; -- found one, don't need more
};
END;
MakeMEBESMask: PROC [ms: MaskState, derivation: DerivationMaskSpec, tooling: ToolingMaskSpec, streamGen: StreamGenProc] =
BEGIN
RestateAddrUnit: PROC [ nm: Nm -- 10^-3 micrometer -- ] RETURNS [ extModeAddrUnits: INT -- 2^-28 micrometer -- ] = {
extModeAddrUnits ← nm/1000;
nm ← nm MOD 1000;
FOR i: INT IN [0..28) DO -- manual high-precision binary divide, (nm/1000)
extModeAddrUnits ← extModeAddrUnits+extModeAddrUnits;
nm ← nm+nm;
IF nm > 1000 THEN {
extModeAddrUnits ← extModeAddrUnits+1;
nm ← nm-1000;
};
ENDLOOP;
};
blockCountFileIndex: INT;
... index where the block count CARDINAL is stored in extended or reticle mode files
mebesReticleClip: MEBESRect = ScaleRect[ms.nmReticleClip,
[num: 1, denom: tooling.addrUnit -- nm/MEBES pixel --]];
nRows: NAT = Ceiling[[
num: CDBasics.SizeOfRect[mebesReticleClip].y -- MEBES pixels -- ,
denom: maskHeight -- MEBES pixels --
]];
nCols: NAT = Ceiling[[
num: CDBasics.SizeOfRect[mebesReticleClip].x -- MEBES pixels -- ,
denom: maskWidth -- MEBES pixels --]];
extModeAddrUnits: INT = RestateAddrUnit[tooling.addrUnit];
ms.mebesPixelPitch ← tooling.addrUnit;
ms.mode ← tooling.mode;
ms.scale ← ReduceRational[[num: ms.nmPerLambda*ms.tadsPerNm, denom: ms.design.technology.lambda]];
FOR col: NAT IN [0..nCols) DO
left: MEBESPixels = mebesReticleClip.x1+col*maskWidth;
FOR row: NAT IN [0..nRows) DO
bottom: MEBESPixels = mebesReticleClip.y1+row*maskHeight;
topmostStripe: INT = MIN[maxStripes, Ceiling[[num: CDBasics.SizeOfRect[mebesReticleClip].y-maskHeight*row, denom: stripeHeight]]];
IF ms.mode # reticle OR (row = 0 AND col = 0) THEN {
.. open a new pattern file and generate a header
ms.patternFileNames ← CONS[
GenPatternFileName[mode: ms.mode, maskSetName: ms.maskSetName,
maskName: tooling.maskNo, row: row, col: col],
ms.patternFileNames];
ms.s ← EBESOpen[streamGen[ms]];
TerminalIO.PutF["\nStarting mask %g at %g ", IO.rope[ms.patternFileNames.first], IO.time[]];
SendCommand[s: ms.s, comm: (SELECT tooling.mode FROM
oneTwo => NEW[Mode12StartDrawing ← [
addressCode: (SELECT tooling.addrUnit FROM
500 => Nm500,
250 => Nm250,
ENDCASE => ERROR),
cx: MIN[maskWidth, CDBasics.SizeOfRect[mebesReticleClip].x-col*maskWidth],
cy: MIN[maskHeight, CDBasics.SizeOfRect[mebesReticleClip].y-row*maskHeight],
moDayYr: today,
patternFileName: RopeToPatternFileName[ms.patternFileNames.first]
]],
extended => NEW[ExtAddrModeStartDrawing ← [
addrUnitHigh: Basics.HighHalf[extModeAddrUnits],
addrUnitLow: Basics.LowHalf[extModeAddrUnits],
cxLow: MIN[maskWidth, CDBasics.SizeOfRect[mebesReticleClip].x-col*maskWidth],
cyLow: MIN[maskHeight, CDBasics.SizeOfRect[mebesReticleClip].y-row*maskHeight],
moDayYr: today,
patternFileName: RopeToPatternFileName[ms.patternFileNames.first]
]],
reticle => NEW[ExtAddrModeStartDrawing ← [
addrUnitHigh: Basics.HighHalf[extModeAddrUnits],
addrUnitLow: Basics.LowHalf[extModeAddrUnits],
cxHigh: Basics.HighHalf[CDBasics.SizeOfRect[mebesReticleClip].x],
cxLow: Basics.LowHalf[CDBasics.SizeOfRect[mebesReticleClip].x],
cyHigh: Basics.HighHalf[CDBasics.SizeOfRect[mebesReticleClip].y],
cyLow: Basics.LowHalf[CDBasics.SizeOfRect[mebesReticleClip].y],
moDayYr: today,
patternFileName: RopeToPatternFileName[ms.patternFileNames.first],
field2Size: (SIZE[CARDINAL]+SIZE[SegDirectoryEntry]*nCols)*Basics.bytesPerWord/bytesPerMebesWord
]],
ENDCASE => ERROR)];
IF ms.mode # oneTwo THEN {
blockCountFileIndex ← ms.s.GetIndex[];
SendCommand[s: ms.s, comm: NEW[CARDINAL ← 0]];
.. to be filled in later with block count
IF ms.mode = reticle THEN {
.. leave room for the segment (column) directory
de: REF SegDirectoryEntry = NEW[SegDirectoryEntry];
FOR segment: INT IN [0..nCols) DO
SendCommand[s: ms.s, comm: de];
ENDLOOP;
};
};
};
IF ms.mode = reticle AND row = 0 THEN { -- fill in segment directory for new column
oldIndex: INT = ms.s.GetIndex[];
ms.s.SetIndex[blockCountFileIndex+(Basics.bytesPerWord*(SIZE[CARDINAL]+SIZE[SegDirectoryEntry]*col))];
SendCommand[s: ms.s, comm: NEW[SegDirectoryEntry ← [
firstBlock: 1+(oldIndex/mebesBlockSize),
firstWordWithinBlock: 1+(oldIndex MOD mebesBlockSize)/bytesPerMebesWord
]]];
ms.s.SetIndex[oldIndex];
SendCommand[s: ms.s, comm: NEW[StartSegment ← [segmentNumber: segmentOrigin+col]]];
};
FOR stripeBase: INT -- [0..maxStripes) -- ← 0, stripeBase+stripesPerClump WHILE stripeBase < topmostStripe DO
rects: Tesselation ← NIL;
IF abortFlag^ THEN RETURN;
ms.tadStripeClip ← ScaleRect[
CDBasics.Intersection[mebesReticleClip, [
x1: left,
y1: bottom+stripeBase*stripeHeight,
x2: left+maskWidth,
y2: bottom+(stripeBase+stripesPerClump)*stripeHeight
]],
[num: ms.mebesPixelPitch*ms.tadsPerNm, denom: 1]];
ms.designStripeClip ← ScaleTadToCD[ms,
CDBasics.Extend[ms.tadStripeClip, ms.mebesPixelPitch*ms.tadsPerNm]];
ms.viewerArrow ← CDBasics.Center[ms.designStripeClip];
CDVArrow.ShowArrow[design: ms.design, pos: ms.viewerArrow];
.. keep user happy
rects ← GenerateCover[ms, LIST[$Enlarge, NEW[Nm ← 2*tooling.skewPerSide], derivation.cover]];
AddScribe[ms, rects];
FOR stripe: INT IN [stripeBase..MIN[stripeBase+stripesPerClump, topmostStripe]) DO
ms.tadStripeClip ← ScaleRect[
CDBasics.Intersection[mebesReticleClip, [
x1: left,
y1: bottom+stripe*stripeHeight,
x2: left+maskWidth,
y2: bottom+(stripe+1)*stripeHeight
]],
[num: ms.mebesPixelPitch*ms.tadsPerNm, denom: 1]];
ms.stripeRectCount ← 0;
SendCommand[s: ms.s, comm: (SELECT ms.mode FROM
oneTwo => NEW[Mode12StartStripe ← [stripeNumber: stripe+stripeOrigin]],
extended => NEW[ExtAddrModeStartStripe ← [stripeNumber: stripe+stripeOrigin]],
reticle => NEW[ExtAddrModeStartStripe ←
[stripeNumber: row*maxStripes+stripe+stripeOrigin]],
ENDCASE => ERROR)];
FOR dil: LIST OF REF ANY ← ms.dropInList, dil.rest WHILE dil # NIL DO
di: DropIn = NARROW[dil.first];
ddi: Atom.PropList = NARROW[derivation.props.GetPropFromList[$DropIns]];
val: REF ANY ← InsertDropIn[ms: ms, rects: rects, di: di,
data: ddi.GetPropFromList[di.name]];
derivation.props ← derivation.props.PutPropOnList[
prop: $DropIns,
val: ddi.PutPropOnList[prop: di.name, val: val]
];
ENDLOOP;
[] ← rects.EnumerateArea[rect: ms.tadStripeClip,
eachTile: OutputTile, data: ms];
SendCommand[s: ms.s, comm: NEW[BasicCommand ← endStripe]];
TerminalIO.PutF[".%d", IO.int[ms.stripeRectCount]]; -- end of stripe
ENDLOOP; -- stripe
rects ← DisposeTesselation[rects];
ENDLOOP; -- stripeBase
IF ms.mode # reticle THEN {
SendCommand[s: ms.s, comm: NEW[BasicCommand ← endDrawing]];
ms.s.Flush[];
IF ms.mode # oneTwo THEN {
oldIndex: INT = ms.s.GetIndex[];
ms.s.SetIndex[blockCountFileIndex];
SendCommand[s: ms.s, comm: NEW[CARDINAL ← oldIndex/mebesBlockSize]];
.. fill in the number of blocks needed
ms.s.SetIndex[oldIndex];
};
ms.s.Close[];
ms.s ← NIL;
};
ENDLOOP; -- row
ENDLOOP; -- col
IF ms.mode = reticle THEN {
oldIndex: INT;
SendCommand[s: ms.s, comm: NEW[BasicCommand ← endDrawing]];
ms.s.Flush[];
oldIndex ← ms.s.GetIndex[];
ms.s.SetIndex[blockCountFileIndex];
SendCommand[s: ms.s, comm: NEW[CARDINAL ← oldIndex/mebesBlockSize]];
.. fill in the number of blocks needed
ms.s.SetIndex[oldIndex];
ms.s.Close[];
ms.s ← NIL;
};
END;
DoAnalysisPass: PROC [ ms: MaskState, spec: CoverSpec ] =
BEGIN
END;
GenerateCover: PUBLIC PROC [ ms: MaskState, spec: CoverSpec, extInfluenceDiameter: Tad ← 0 ] RETURNS [ rects: Tesselation ] =
BEGIN
WITH spec SELECT FROM
list: LIST OF REF ANY =>
BEGIN
(e.g., LIST[$Bletch, NEW[INT ← 2], NEW[CDLayer ← []], NEW[CDLayer ← []]] )
ref: REF ANY = Atom.GetProp[$CDMEBESCoverProcs, list.first];
rects ← NARROW[ref, REF CoverProc]^[ms, list.rest, extInfluenceDiameter];
END;
layer: CDLayer =>
SELECT layer.deltaDiameter FROM
>=0 =>
BEGIN
rects ← NewTesselation[NIL];
DrawLayersWithPosDeltas[ms: ms, rects: rects, layers: LIST[layer], extInfluenceDiameter: extInfluenceDiameter];
END;
ENDCASE => rects ← GenerateCover[
ms,
LIST[$Enlarge,
NEW[Nm ← layer.deltaDiameter],
NEW[CDLayerRec ← [source: layer.source, deltaDiameter: 0]]],
extInfluenceDiameter];
ENDCASE => IF spec = NIL THEN rects ← NewTesselation[NIL] ELSE ERROR;
END;
AddScribe: PROC [ms: MaskState, rects: Tesselation] =
BEGIN
IF NARROW[Prop[ms, $CDMEBESAddScribe, NEW[BOOLFALSE]], REF BOOL]^ THEN { -- insert the scribe line
scaleFactor: Rational = [num: ms.tadsPerNm, denom: 1]; -- from Nm to Tads
r: NmRect = ms.nmReticleClip;
rSize: NmPosition = CDBasics.SizeOfRect[r];
tooling: ToolingMaskSpec = NARROW[ms.toolingSpec.GetPropFromList[ms.curMask.maskId]];
outer: Nm = tooling.scribeOffset;
inner: Nm = tooling.scribeOffset+tooling.scribeWidth;
IF inner > outer THEN { -- nontrivial scribe structure
ChRect: PROC [b: NmRect] = {
FOR xRepeat: INT IN [-1..1] DO
FOR yRepeat: INT IN [-1..1] DO
offsetRect: NmRect = CDBasics.MoveRect[r: b,
offset: CDBasics.AddPoints[p: ms.dieOffset,
s: [x: xRepeat*rSize.x, y: yRepeat*rSize.y]]
];
clippedMebesRect: MEBESRect = ScaleRect[CDBasics.Intersection[offsetRect, r], scaleFactor];
IF CDBasics.NonEmpty[clippedMebesRect] THEN
rects.ChangeRect[clippedMebesRect, $covered];
ENDLOOP;
ENDLOOP;
};
ChRect[[x1: r.x1+outer, y1: r.y1+outer, x2: r.x1+inner, y2: r.y2-outer]]; -- left side
ChRect[[x1: r.x2-inner, y1: r.y1+outer, x2: r.x2-outer, y2: r.y2-outer]]; -- right side
ChRect[[x1: r.x1+outer, y1: r.y1+outer, x2: r.x2-outer, y2: r.y1+inner]]; -- bottom
ChRect[[x1: r.x1+outer, y1: r.y2-inner, x2: r.x2-outer, y2: r.y2-outer]]; -- top
};
};
END;
DrawInfo: TYPE = REF DrawInfoRec;
DrawInfoRec: TYPE = RECORD [
layers: ARRAY CD.Layer OF RECORD [ used: BOOLFALSE, deltaDiameter: Tad ← 0 ],
rects: Tesselation ← NIL,
ms: MaskState ← NIL ];
DrawLayersWithPosDeltas: PUBLIC PROC [ms: MaskState, rects: Tesselation, layers: LIST OF CDLayer, extInfluenceDiameter: Nm ← 0] =
BEGIN
IF layers # NIL THEN
BEGIN
biggestDelta: Nm ← 0;
di: DrawInfo;
cf: REF CD.ContextFilter ← NEW[CD.ContextFilter ← ALL[FALSE]];
dr: CD.DrawRef← CD.CreateDrawRef[[
stopFlag~abortFlag,
interestClip~Bloat[ms.designStripeClip, MIN[(ms.design.technology.lambda*(extInfluenceDiameter+biggestDelta))/ms.nmPerLambda, 0]],
drawRect~IncludeRectangle,
devicePrivate~(di ← NEW[DrawInfoRec ← [rects: rects, ms: ms]]),
contextFilter~cf,
symbolics~FALSE,
showErrors~FALSE,
design~ms.design
]];
FOR list: LIST OF CDLayer ← layers, list.rest WHILE list # NIL DO
layer: CDLayer ← list.first;
cf[layer.source] ← TRUE;
di.layers[layer.source] ← [used: TRUE, deltaDiameter: layer.deltaDiameter*ms.tadsPerNm];
IF di.layers[layer.source].deltaDiameter < 0 THEN ERROR;
biggestDelta ← MAX[biggestDelta, layer.deltaDiameter];
ENDLOOP;
CDOps.DrawDesign[ms.design, dr]; -- enumerate design rectangles into rects
END;
END;
IncludeRectangle: PROC [ pr: CD.DrawRef, r: CD.Rect, l: CD.Layer ] =
BEGIN
di: DrawInfo = NARROW[pr.devicePrivate];
IF di.layers[l].used AND CDBasics.NonEmpty[r] THEN
BEGIN
tr: TadRect ← ScaleCDToTad[ms: di.ms, cdr: r];
IF di.layers[l].deltaDiameter # 0 THEN
tr ← Bloat[tr, di.layers[l].deltaDiameter];
di.rects.ChangeRect[rect: tr, new: $covered];
END;
END;
OutputTile: PROCEDURE [tile: CStitching.Tile, data: REF ANY]
-- CStitching.PerTileProc -- =
BEGIN -- only called on tiles with non-NIL values
DoOutput: PROC =
BEGIN
ms: MaskState = NARROW[data];
tpp: Tad = ms.tadsPerNm*ms.mebesPixelPitch; -- tads per pixel
tr: TadRect = CDBasics.MoveRect[
r: CDBasics.Intersection[tile.Area, ms.tadStripeClip],
offset: CDBasics.NegOffset[CDBasics.BaseOfRect[ms.tadStripeClip]]
];
mr: MEBESRect = [x1: tr.x1/tpp, y1: tr.y1/tpp, x2: tr.x2/tpp, y2: tr.y2/tpp];
IF CDBasics.NonEmpty[mr] THEN
BEGIN
ms.drawCommand^ ← [
x: mr.x1,
y: mr.y1,
w: mr.x2-mr.x1,
h: mr.y2-mr.y1
];
... think very carefully about this ...
SendCommand[s: ms.s, comm: ms.drawCommand];
ms.stripeRectCount ← ms.stripeRectCount+1;
END;
END;
WITH tile.value SELECT FROM
a: ATOM =>
SELECT a FROM
$covered => DoOutput[];
ENDCASE => ERROR;
ENDCASE => ERROR;
END;
Today: PROC RETURNS [ d: Date ] =
TRUSTED BEGIN
time: BasicTime.Unpacked = BasicTime.Unpack[BasicTime.Now[]];
moDayYr: ROPE = IO.PutFR[format: "%02d%02d%02d",
v1: IO.card[LOOPHOLE[time.month, CARDINAL]+1], v2: IO.int[time.day], v3: IO.int[time.year MOD 100]];
FOR i: [0..6) IN [0..6) DO d[i] ← Rope.Fetch[moDayYr, i] ENDLOOP;
END;
GenPatternFileName: PROC [ mode: MEBESMode, maskSetName, maskName: ROPE, row, col: NAT ] RETURNS [ rfn: ROPE ] =
BEGIN
r: ROPE;
twoCharMaskName: ROPE = Rope.Substr[base: Rope.Cat[maskName, "xx"], len: 2];
SELECT mode FROM
oneTwo, extended => {
r ← IO.PutFR[format: "%s%s.%g%g",
v1: IO.rope[Rope.Substr[base: Rope.Cat[maskSetName, "xxxxxxx"], len: 7]],
v2: IO.rope[twoCharMaskName],
v3: IO.char[LOOPHOLE['A+col]], v4: IO.char[LOOPHOLE['A+row]]];
};
reticle => {
IF row # 0 OR col # 0 THEN ERROR;
r ← IO.PutFR[format: "%s.%s",
v1: IO.rope[Rope.Substr[base: Rope.Cat[maskSetName, "xxxxxxx"], len: 9]],
v2: IO.rope[twoCharMaskName]];
};
ENDCASE => ERROR;
rfn ← Rope.Translate[base: r, translator: ToUpperCase];
END;
ToUpperCase: PROC [old: CHAR] RETURNS [new: CHAR] -- Rope.TranslatorType -- =
{new ← Ascii.Upper[old]};
RopeToPatternFileName: PROC [ rfn: ROPE ] RETURNS [ fn: PatternFileName ] =
{FOR i: [0..12) IN [0..12) DO fn[i] ← Rope.Fetch[rfn, i] ENDLOOP};
AbortFile: PROC [s: IO.STREAM ] RETURNS [ IO.STREAM ] =
{IF s#NIL THEN s.Close[abort: TRUE]; RETURN[NIL]};
ComputeTadsPerNm: PROC [ ms: MaskState, minAllowable: Tad ← 1 ] = {
prop: ATOM = $CDMEBESTadsPerNm;
ms.tadsPerNm ← NumericProp[ms, prop, NEW[INT ← defaultTadsPerNm]];
IF ms.tadsPerNm < minAllowable THEN {
TerminalIO.PutF["\n\nSorry, your basic unit of measure of 1/%d nm was too large. I've made it smaller for you. Restarting conversion of this mask.\n", IO.int[ms.tadsPerNm]];
ms.tadsPerNm ← minAllowable;
defaultTadsPerNm ← MAX[defaultTadsPerNm, minAllowable];
IF Prop[ms, $CDMEBESTadsPerNm, NIL] # NIL THEN
CDProperties.PutDesignProp[ms.design, prop, NEW[Tad ← minAllowable]];
};
};
Module START code...
Init[];
END. -- of CDMEBESMainImpl