PWCoreLichenImpl.mesa
Copyright Ó 1986, 1987 by Xerox Corporation. All rights reversed.
Created by Bertrand Serlet, July 14, 1986 5:49:00 pm PDT
Bertrand Serlet, May 5, 1988 10:40:04 pm PDT
Last tweaked by Mike Spreitzer on March 4, 1989 2:34:15 pm PST
Barth, April 16, 1987 11:54:55 pm PDT
Louis Monier March 30, 1988 11:55:43 am PST
Jean-Marc Frailong December 10, 1987 7:18:00 pm PST
Don Curry July 20, 1988 7:40:18 am PDT
DIRECTORY
AMBridge, AMModel, AMTypes, BackStop, BrineIO,
CD, CDOps, CDSequencer, CDSequencerExtras, CDViewer,
Core, CoreCDUser, CoreClasses, CoreFlat, CoreIO, CoreOps, CoreProperties, CoreStructuralComparison,
GList, Interpreter, InterpreterOps, IO, List, PopUpButtons, ProcessProps,
PWCLCoreFlatExtras, PWCore, PWCoreLichen,
RefTab, Rope, SimpleMailer, Sisyph, StructuredStreams, SymTab, TerminalIO, UnparserBuffer, UserProfile, ViewerClasses, ViewerTools, WorldVM;
PWCoreLichenImpl: CEDAR PROGRAM
IMPORTS AMBridge, AMModel, AMTypes, BackStop, BrineIO, CD, CDOps, CDSequencer, CDSequencerExtras, CDViewer, CoreCDUser, CoreClasses, CoreFlat, CoreIO, CoreOps, CoreProperties, CoreStructuralComparison, GList, InterpreterOps, IO, List, PopUpButtons, ProcessProps, PWCLCoreFlatExtras, PWCore, RefTab, Rope, SimpleMailer, Sisyph, StructuredStreams, SymTab, TerminalIO, UnparserBuffer, UserProfile, ViewerTools, WorldVM
EXPORTS PWCoreLichen
SHARES PWCore =
BEGIN OPEN CC:CoreClasses, CF:CoreFlat, CFE:PWCLCoreFlatExtras, CO:CoreOps, CP:CoreProperties, CSC:CoreStructuralComparison, PUB:PopUpButtons, PWCoreLichen, SS:StructuredStreams, UB:UnparserBuffer;
Types & globals
FlatWireRecList: TYPE = LIST OF CF.FlatWireRec;
Element: TYPE = CSC.Element;
ElementList: TYPE = CSC.ElementList;
ActualID: TYPE = CSC.ActualID;
Role: TYPE ~ CSC.Role;
TV: TYPE ~ AMBridge.TV;
BadRoot: PUBLIC SIGNAL [root: CellType] ~ CODE;
Message: PUBLIC SIGNAL [msg: ROPE] ~ CODE;
FoundProblems: PUBLIC SIGNAL [source, extracted: CellType] ~ CODE;
MsgFailed: SIGNAL ~ CODE;
SourceInfo: TYPE ~ REF SourceInfoPrivate;
SourceInfoPrivate: TYPE ~ RECORD [layout: CD.Object, sourceCT: CellType, liKey: INT, extractedToSource, sourceToExtracteds: RefTab.Ref];
CompareData: TYPE ~ REF CompareDataPrivate;
CompareDataPrivate: TYPE ~ RECORD [
context: Context,
vsp: CSC.ViewStatsPair,
hd: HighlightData,
extractedToSourceInfo: RefTab.Ref,
chops: REF INT,
chop: BOOLFALSE --stacked via lambda-binding--
];
wideTransistorTolerances: PUBLIC TransistorTolerances ~ CoreStructuralComparison.wideTransistorTolerances;
tightTransistorTolerances: PUBLIC TransistorTolerances ~ CoreStructuralComparison.tightTransistorTolerances;
FormatTolerances: PUBLIC PROC [ttols: TransistorTolerances] RETURNS [ROPE]
~ {RETURN CoreStructuralComparison.FormatTolerances[ttols]};
OtherRole: ARRAY Role OF Role = [A: B, B: A];
Extracted: Role ~ B; Source: Role ~ A;
We need them in this order because SourceSubtree would side-effect the data-structure created from extractedRoot if it came second.
RoleDescriptions: CSC.RoleNames ← ALL["uninitialized"];
HighlightData: TYPE = REF HighlightDataPrivate;
HighlightDataPrivate: TYPE = RECORD [
design: CD.Design ← NIL,
viewer: ViewerClasses.Viewer ← NIL,
ii: CD.Instance ← NIL
];
compareResults: ATOM = CoreIO.RegisterProperty[CP.RegisterUnprintableProperty[$PWCoreLichenCompareResults], WriteNil, ReadNil]; --I have to have a read proc because of expensive existing files with the written crock in them. I have to have a write proc because I can't register only a read proc, and there are expensive existing data structures (at November 19, 1987 5:31:56 pm PST).
globKeyProp: ATOM = CoreIO.RegisterProperty[CP.RegisterUnprintableProperty[$PWCoreLichenImplGlobalKey], WriteNil, ReadNil];
doneProp: ATOM ~ CP.RegisterUnprintableProperty[$PWCoreLichenImplDone];
kindName: ARRAY CSC.MismatchKind OF ROPE ~ ["Stuck", "Difference", "Transitor shape mismatch"];
transistorTolerancesProp: PUBLIC ATOM ~ CP.RegisterProperty[$LichenTransistorTolerances];
chopProp: ATOM ~ CP.RegisterProperty[$PWCoreLichenChop];
check: BOOLTRUE;
autoHack: BOOLFALSE;
mayQuitEarly: BOOLTRUE;
globKey: REF INTNEW [INT ← 1];
trace: PUBLIC SymTab.Ref ← SymTab.Create[case: TRUE];
ChipNDale command interface
StatsCmd: PROC [cmd: CDSequencer.Command] = {DisplayStats[]};
cmdTTols: TransistorTolerances ← wideTransistorTolerances;
CompareCmd: PROC [cmd: CDSequencer.Command] --CDSequencer.CommandProc-- = {
abort: REF BOOL ~ NEW [BOOLFALSE];
Inner: PROC ~ {
CompareOne: CoreCDUser.EachRootCellTypeProc ~ {
name: ROPE = CO.GetCellTypeName[root];
allOK: BOOL;
{TerminalIO.PutF["Extracting and comparing %g.\n", IO.rope[name]];
allOK ← Compare[root, [cmd.design], cmdTTols !BadRoot => GOTO badrut];
TerminalIO.PutRope["Done extracting and comparing "];
TerminalIO.PutRope[name];
TerminalIO.PutRope[IF allOK THEN "; no new errors discovered.\n" ELSE "; some new errors discovered.\n"];
EXITS badrut => TerminalIO.PutRope["... that's not a valid root (there's no layout attached)!\n"]};
quit ← abort^;
};
[] ← CoreCDUser.EnumerateSelectedCellTypes[cmd.design, CompareOne, Sisyph.mode];
};
CDSequencer.UseAbortFlag[cmd.design, abort];
TerminalIO.PutF["For PWCoreLichen, mayQuitEarly=%g, automorphismHack=%g, top transistor tolerances=%g\n", [boolean[GetQuitEarly[]]], [boolean[GetAutomorphismHack[]]], [rope[FormatTolerances[cmdTTols]]] ];
ProcessProps.AddPropList[List.PutAssoc[$AbortBool, abort, NIL], Inner];
};
ForgetCmd: PROC [cmd: CDSequencer.Command] --CDSequencer.CommandProc-- = {
abort: REF BOOL ~ NEW [BOOLFALSE];
Inner: PROC ~ {
ForgetOne: CoreCDUser.EachRootCellTypeProc ~ {
name: ROPE = CO.GetCellTypeName[root];
TerminalIO.PutF["Forgetting comparisons for subtree rooted at %g.\n", IO.rope[name]];
ForgetComparisons[root];
quit ← abort^;
};
[] ← CoreCDUser.EnumerateSelectedCellTypes[cmd.design, ForgetOne, Sisyph.mode];
};
CDSequencer.UseAbortFlag[cmd.design, abort];
ProcessProps.AddPropList[List.PutAssoc[$AbortBool, abort, NIL], Inner];
};
GlobalForgetCmd: PROC [cmd: CDSequencer.Command] --CDSequencer.CommandProc-- = {
GlobalForget[];
TerminalIO.PutF["All previous results from Lichen have been forgotten\n"];
RETURN};
SetAutomorphismHackCmd: PROC [cmd: CDSequencer.Command] ~ {
SELECT cmd.key FROM
$PWCoreLichenEnableAutomorphismHack => SetAutomorphismHack[TRUE];
$PWCoreLichenDisableAutomorphismHack => SetAutomorphismHack[FALSE];
ENDCASE => ERROR; -- invalid command key !!!
TerminalIO.PutF["Lichen now %gs automorphisms in graphs\n", IO.rope[IF GetAutomorphismHack[] THEN "accept" ELSE "reject"]];
};
SetQuitEarlyCmd: PROC [cmd: CDSequencer.Command] ~ {
SELECT cmd.key FROM
$PWCoreLichenEnableQuitEarly => SetQuitEarly[TRUE];
$PWCoreLichenDisableQuitEarly => SetQuitEarly[FALSE];
ENDCASE => ERROR; -- invalid command key !!!
TerminalIO.PutF["Lichen now %g quit early\n", IO.rope[IF GetQuitEarly[] THEN "may" ELSE "may not"]];
};
SetTolerancesCmd: PROC [cmd: CDSequencer.Command] ~ {
ENABLE TerminalIO.UserAbort => CONTINUE;
SELECT cmd.key FROM
$PWCoreLichenWideTolerances => cmdTTols ← wideTransistorTolerances;
$PWCoreLichenTightTolerances => cmdTTols ← tightTransistorTolerances;
$PWCoreLichenReadTolerances => {
rope: ROPE ~ TerminalIO.RequestRope["Top transistor tolerances (e.g., \"length: 1.5, width: 2.0\"): "];
cmdTTols ← RefineTolerances[rope, cmdTTols];
};
ENDCASE => ERROR; -- invalid command key !!!
TerminalIO.PutRope[Rope.Cat["PWCoreLichen top transistor tolerances are now ", FormatTolerances[cmdTTols], "\n"]];
};
PrintConstraintsCmd: PROC [cmd: CDSequencer.Command] --CDSequencer.CommandProc-- = {
abort: REF BOOL ~ NEW [BOOLFALSE];
Inner: PROC ~ {
PrintPerCellType: CoreCDUser.EachRootCellTypeProc ~ {
extracted: CellType ~ PWCore.LayoutInfo[root].extractedCT;
name: ROPE = CO.GetCellTypeName[extracted];
constraints: RefTab.Ref ~ GetConstraints[extracted];
PerConstraint: PROC [key, val: REF ANY] RETURNS [quit: BOOLFALSE] ~ {
constraint: RefTab.Ref ~ NARROW[val];
sep: ROPENIL;
PerWire: PROC [key, val: REF ANY] RETURNS [quit: BOOLFALSE] ~ {
wire: Core.Wire ~ NARROW[key];
TerminalIO.PutRope[sep];
TerminalIO.PutRope[CO.GetFullWireName[extracted.public, wire]];
sep ← ", ";
quit ← abort^};
TerminalIO.PutRope["\t"];
[] ← constraint.Pairs[PerWire];
TerminalIO.PutRope[";\n"];
quit ← abort^};
IF constraints=NIL OR constraints.GetSize=0
THEN TerminalIO.PutF["No commonality constraints for %g.\n", IO.rope[name]]
ELSE {
TerminalIO.PutF["Commonality constraints of %g:\n", IO.rope[name]];
[] ← constraints.Pairs[PerConstraint];
TerminalIO.PutRope["\t.\n"];
};
quit ← abort^};
[] ← CoreCDUser.EnumerateSelectedCellTypes[cmd.design, PrintPerCellType, Sisyph.mode];
};
CDSequencer.UseAbortFlag[cmd.design, abort];
ProcessProps.AddPropList[List.PutAssoc[$AbortBool, abort, NIL], Inner];
};
TraceProc: PROC [viewer: PUB.Viewer, instanceData, classData, key: REF ANY] --PUB.PopUpButtonProc-- ~ {
sel: ROPE ~ ViewerTools.GetSelectionContents[];
SELECT key FROM
$AddTrace => {
IF sel.Length[] = 0 THEN GOTO Usage;
[] ← trace.Store[sel, $trace];
TerminalIO.PutF["Lichen also tracing %g\n", [rope[sel]]]};
$RemTrace => {
IF sel.Length[] = 0 THEN GOTO Usage;
[] ← trace.Delete[sel];
TerminalIO.PutF["Lichen not tracing %g\n", [rope[sel]]]};
$ClearTraces => {
trace.Erase[];
TerminalIO.PutRope["Lichen not tracing anything\n"]};
$ListTraces => {
first: BOOLTRUE;
ListTrace: PROC [key, val: REF ANY] RETURNS [quit: BOOLFALSE] --SymTab.EachPairAction-- ~ {
name: ROPE ~ NARROW[key];
IF first THEN first ← FALSE ELSE TerminalIO.PutRope[", "];
TerminalIO.PutRope[name];
RETURN};
TerminalIO.PutF["Lichen tracing %g names: ", [integer[trace.GetSize]]];
IF trace.Pairs[ListTrace] THEN ERROR;
TerminalIO.PutRope["\n"]};
ENDCASE => ERROR; -- invalid command key !!!
EXITS
Usage => TerminalIO.PutRope["Invoke with a vertex name in the current Tioga selection\n"]
};
traceButtonClass: PUB.Class ~ PUB.MakeClass[[
proc: TraceProc,
choices: LIST[
[$AddTrace, "Add Tioga selection to the set of traced names"],
[$ListTraces, "List the set of traced names"],
[$RemTrace, "Remove Tioga selection from the set of traced names"],
[$ClearTraces, "Clear the set of traced names"],
]
]];
traceButton: PUB.Viewer ~ traceButtonClass.Instantiate[viewerInfo: [name: "PWCL Trace"]];
TrackProfile: PROC [reason: UserProfile.ProfileChangeReason] --UserProfile.ProfileChangedProc-- = {
cmdTTols ← RefineTolerances[UserProfile.Line["PWCoreLichen.TopTolerances", ""], wideTransistorTolerances];
RETURN};
Public Utilities
DisplayStats: PUBLIC PROC ~ {CSC.DisplayStats[]};
ForgetComparisons: PUBLIC PROC [root: CellType] ~ {
seen: RefTab.Ref ~ RefTab.Create[];
Clearit: PROC [cell: CellType, target: CF.FlatCellTypeRec ← CF.allFlatCells, flatCell: CF.FlatCellTypeRec ← [], instance: CF.CellInstance ← NIL, index: NATLAST[NAT], parent: CellType ← NIL, flatParent: CF.FlatCellTypeRec ← [], data: REF ANYNIL] --CF.UnboundFlatCellProc-- ~ {
IF seen.Fetch[cell].found THEN RETURN;
IF NOT seen.Insert[cell, $Seen] THEN ERROR;
CP.PutCellTypeProp[cell, compareResults, NIL];
CP.PutCellTypeProp[cell, doneProp, NIL];
IF cell.class.recast#NIL OR cell.class=CC.recordCellClass THEN CF.NextUnboundCellType[cell, target, flatCell, instance, index, parent, flatParent, data, Clearit];
};
Clearit[cell: root, flatCell: [[]]];
};
SetAutomorphismHack: PUBLIC PROC [enabled: BOOL] = {autoHack ← enabled};
GetAutomorphismHack: PUBLIC PROC RETURNS [enabled: BOOL] = {enabled ← autoHack};
SetQuitEarly: PUBLIC PROC [may: BOOL] ~ {mayQuitEarly ← may};
GetQuitEarly: PUBLIC PROC RETURNS [may: BOOL] ~ {may ← mayQuitEarly};
GlobalForget: PUBLIC PROC ~ {globKey ← NEW [INT ← globKey^+1]; RETURN};
The real stuff
Compare: PUBLIC PROC [root: CellType, context: Context, ttols: TransistorTolerances ← wideTransistorTolerances] RETURNS [allOK: BOOLTRUE] = {
ENABLE FoundProblems => allOK ← FALSE;
cd: CompareData ~ NEW [CompareDataPrivate ← [context, CSC.CreateViewStatsPair[], NEW [HighlightDataPrivate ← []], RefTab.Create[], NEW [INT ← 0] ]];
sent: BOOL;
info: SimpleMailer.SendMessageInfo;
RecursiveCompare[root: root, cd: cd, ttols: ttols];
[sent, info] ← SimpleMailer.SendMessage[
from: "PWCoreLichen",
to: LIST["Spreitzer.pa"],
subject: CO.GetCellTypeName[root].Concat[" View Statistics"],
body: IO.PutFR["chops: %g\n", [integer[cd.chops^]]]
.Concat[CSC.FmtViewStatsPair[cd.vsp, RoleDescriptions]]];
IF (NOT sent) OR info # ok THEN SIGNAL MsgFailed;
RETURN};
CompareForTheRestOfUs: PUBLIC PROC [root: CellType, design: CD.Design, precision: REAL ← 0.90] RETURNS [allOK: BOOLTRUE] = {
ttols: TransistorTolerances;
SELECT precision FROM
>=1.0 => ttols ← tightTransistorTolerances;
<= 0.000001 => ttols ← wideTransistorTolerances;
ENDCASE => {
ttols ← NEW[CoreStructuralComparison.TransistorTolerancesPrivate];
ttols[length] ← [precision, 1/precision];
ttols[width] ← [precision, 1/precision];
};
allOK ← Compare[root, [design], ttols];
};
RecursiveCompare: PROC [root: CellType, cd: CompareData, ttols: TransistorTolerances] = {
extractedToSourceInfo: RefTab.Ref ~ NARROW[CP.GetCellTypeProp[root, compareResults]];
sameKey: BOOL ~ WITH CP.GetCellTypeProp[root, globKeyProp] SELECT FROM
x: REF INT => x^ = globKey^,
ENDCASE => FALSE;
layoutAtom: ATOM = GetLayoutAtom[root];
abort: REF BOOL ~ WITH ProcessProps.GetProp[$AbortBool] SELECT FROM
x: REF BOOL => x,
ENDCASE => NEW [BOOLFALSE];
IF abort^ THEN RETURN;
SELECT TRUE FROM
sameKey AND extractedToSourceInfo#NIL => {
IF extractedToSourceInfo#cd.extractedToSourceInfo THEN MergeRefTabs[cd.extractedToSourceInfo, extractedToSourceInfo];
RETURN};
layoutAtom#NIL  => {
done: BOOL ~ SELECT CP.GetCellTypeProp[root, doneProp] FROM
NIL => FALSE,
$TRUE => TRUE,
ENDCASE => ERROR;
source: CellType ~ root;
extracted: CellType;
extractedToSource, sourceToExtracteds: RefTab.Ref;
layout: CD.Object;
liKey: INT;
IF abort^ THEN RETURN;
CSC.PushTimer[$PWCore];
{ENABLE UNWIND => CSC.PopTimer[$PWCore];
[layout: layout, extractedCT: extracted, extractedToSource: extractedToSource, key: liKey] ← PWCore.LayoutInfo[source];
sourceToExtracteds ← ReverseRefTab[extractedToSource]};
CSC.PopTimer[$PWCore];
IF NOT cd.extractedToSourceInfo.Insert[extracted, NEW [SourceInfoPrivate ← [layout, source, liKey, extractedToSource, sourceToExtracteds]]] THEN ERROR;
IF abort^ THEN RETURN;
IF cd.chop THEN cd.chops^ ← cd.chops^ + 1
ELSE IF done THEN cd.chop ← TRUE
ELSE MatchLeaves[extracted, source, sourceToExtracteds, layout, liKey, ttols, cd, abort];
IF abort^ THEN RETURN;
CP.PutCellTypeProp[root, compareResults, cd.extractedToSourceInfo];
CP.PutCellTypeProp[root, globKeyProp, globKey];
CP.PutCellTypeProp[root, doneProp, $TRUE];
};
root.class=CC.recordCellClass => SIGNAL BadRoot[root];
ENDCASE  => {
cd.context.path.recastCount ← cd.context.path.recastCount+1;
RecursiveCompare[CO.Recast[root], cd, ttols];
RETURN};
};
MergeRefTabs: PROC [to, from: RefTab.Ref] ~ {
MovePair: PROC [key, val: REF ANY] RETURNS [quit: BOOLFALSE] ~ {[] ← to.Insert[key, val]; RETURN};
[] ← from.Pairs[MovePair];
RETURN};
SourceSubtree: PROC [data: REF ANY, instance: CC.CellInstance, path: CF.InstancePath, ttols: TransistorTolerances] RETURNS [CSC.SubtreeAns] --CSC.SubtreeSpec-- = {
cd: CompareData ~ NARROW[data];
oldChop: BOOL ~ cd.chop;
ct: CellType ← instance.type;
recastCount: NAT ← 0;
DO
SELECT CP.GetCellTypeProp[ct, chopProp] FROM
NIL => NULL;
$TRUE => cd.chop ← TRUE;
ENDCASE => ERROR;
IF GetLayoutAtom[ct]#NIL THEN {
subData: CompareData ~ NEW [CompareDataPrivate ← cd^];
subData.context.path ← CFE.LowerFlatCell[[path, recastCount], cd.context.path];
RecursiveCompare[ct, subData, ttols !BadRoot => ERROR];
GOTO Dun};
IF ct.class.recast=NIL THEN {
IF ct.class#CC.recordCellClass THEN GOTO Dun;
RETURN [[NIL, [NIL], IF cd.chop AND ~oldChop THEN Pop ELSE NIL]]};
ct ← CO.Recast[ct];
REPEAT
Dun => RETURN [[ct, CSC.CreateParallelMapper[ct, instance.type], IF cd.chop AND ~oldChop THEN Pop ELSE NIL]];
ENDLOOP;
};
Pop: PROC [data: REF ANY]
~ {cd: CompareData ~ NARROW[data]; cd.chop ← FALSE; RETURN};
GetLayoutAtom: PROC [cellType: CellType] RETURNS [ATOM]
~ {RETURN [NARROW[CP.GetCellTypeProp[cellType, PWCore.layoutAtomProp]]]};
EnumData: TYPE ~ REF EnumDataPrivate;
EnumDataPrivate: TYPE ~ RECORD [
si: SourceInfo,
finish: RefTab.Ref ← NIL
];
ExtractedSubtree: PROC [data: REF ANY, instance: CC.CellInstance, path: CF.InstancePath, ttols: TransistorTolerances] RETURNS [CSC.SubtreeAns] --CSC.SubtreeSpec-- = {
cd: CompareData ~ NARROW[data];
ct: CellType ← instance.type;
DO
si: SourceInfo ~ NARROW[cd.extractedToSourceInfo.Fetch[ct].val];
IF check THEN TRUSTED {
source: CellType ~ LOOPHOLE[PWCore.GetPWCoreExtractedCTKey[ct]];
sourced: BOOL ~ source#LOOPHOLE[0] AND GetLayoutAtom[source]#NIL;
IF hardNosed AND source#LOOPHOLE[0] AND ~sourced THEN ct ← ct;
IF sourced # (si#NIL) THEN ERROR};
IF si#NIL THEN RETURN [[si.sourceCT, [EnumerateSourceToExtracteds, NEW [EnumDataPrivate ← [si, IF ct#instance.type THEN CO.CreateBindingTable[ct.public, instance.type.public] ELSE NIL]]]]];
IF ct.class.recast=NIL THEN {
IF ct.class#CC.recordCellClass THEN RETURN [[ct, CSC.CreateParallelMapper[ct, instance.type]]];
RETURN [[NIL, [NIL]]]};
ct ← CO.Recast[ct];
ENDLOOP;
};
hardNosed: BOOLFALSE;
EnumerateSourceToExtracteds: PROC [data: REF ANY, Consume: CSC.MapConsumer] ~ {
ed: EnumData ~ NARROW[data];
si: SourceInfo ~ ed.si;
seen: RefTab.Ref ~ RefTab.Create[];
PerFrom: PROC [wire: Wire] RETURNS [subWires: BOOLTRUE, quit: BOOLFALSE] --CO.EachWireProc-- ~ {
IF wire.size#0 THEN RETURN;
IF seen.Fetch[wire].found THEN RETURN;
IF NOT seen.Insert[wire, $T] THEN ERROR;
{extracteds: CSC.Element ~ NARROW[si.sourceToExtracteds.Fetch[wire].val];
ProduceTos: PROC [Consume: CSC.ToConsumer] ~ {
FOR dl: CSC.Element ← extracteds, dl.rest WHILE dl#NIL DO
WITH dl.first SELECT FROM
dw: CSC.DescendantWire => {
IF dw.flatCell.path.length#0 THEN ERROR;
{it: Wire ~ IF ed.finish=NIL THEN dw.wire ELSE NARROW[ed.finish.Fetch[dw.wire].val];
IF it=NIL THEN ERROR;
Consume[it]}};
ENDCASE => ERROR;
wire ← wire;
ENDLOOP;
RETURN};
IF extracteds=NIL THEN ERROR;
Consume[wire, ProduceTos];
RETURN}};
IF CO.VisitWireSeq[si.sourceCT.public, PerFrom] THEN ERROR;
RETURN};
namesOfCellTypesToIgnore: SymTab.Ref ← SymTab.Create[case: FALSE];
MatchLeaves: PROC [extractedRoot, sourceRoot: CellType, sourceToExtracteds: RefTab.Ref, layout: CD.Object, liKey: INT, ttols: TransistorTolerances, cd: CompareData, abort: REF BOOL] = {
GiveHints: PROC [Consume: PROC [ds: ARRAY CSC.Role OF CSC.Descendant]] --HintsGiver-- = {
PassPair: PROC [key, val: REF ANY] RETURNS [quit: BOOLFALSE] --EachPairProc-- = {
sourcePublic: Wire = NARROW[key];
extractedPublics: Element = NARROW[val];
ds: ARRAY CSC.Role OF CSC.Descendant;
ds[Extracted] ← extractedPublics.first;
ds[Source] ← NEW [CF.FlatWireRec ← [wireRoot: public, wire: sourcePublic]];
Consume[ds];
};
[] ← sourceToExtracteds.Pairs[PassPair];
};
ii: CD.Instance ← NIL;
didQuitEarly: REF BOOL ~ NEW [BOOLFALSE];
contexized: BOOLFALSE;
Contexize: PROC ~ {
IF contexized THEN RETURN;
contexized ← TRUE;
IF cd.hd.viewer=NIL THEN {
cd.hd.design ← CDOps.CreateDesign[CD.FetchTechnology[$cmosB]];
cd.hd.viewer ← CDViewer.CreateViewer[cd.hd.design];
};
IF cd.hd.ii=NIL OR cd.hd.ii.ob#layout THEN {
IF cd.hd.ii#NIL THEN CDOps.RemoveInstance[cd.hd.design, cd.hd.ii, FALSE];
CDOps.IncludeInstance[cd.hd.design, cd.hd.ii ← NEW [CD.InstanceRep ← [ob: layout]]];
cd ← cd};
CoreCDUser.SetDesignRootCellType[cd.context.design, sourceRoot];
CoreCDUser.SetDesignRootCellType[cd.hd.design, extractedRoot];
CoreCDUser.SetRootCellTypeDecoration[sourceRoot, Sisyph.mode.decoration];
CoreCDUser.SetRootCellTypeDecoration[extractedRoot, PWCore.extractMode.decoration];
RETURN};
PrintWork: PROC [to: IO.STREAM, which: Role, subset: BOOL, root, other: CellType, ds: ElementList, Ans: CSC.QueryAnswerer] = {
CutTest: PROC [cutData: REF ANY, root, cellType: CellType, flatCell: CF.FlatCellTypeRec, instance: CC.CellInstance] RETURNS [BOOL] --CFE.CutMembershipTester-- = {
RETURN [instance # NIL AND (SELECT which FROM Source => SourceSubtree, Extracted => ExtractedSubtree, ENDCASE => ERROR)[cd, instance, flatCell.path, ttols].leafType#NIL];
};
Annotate: PROC [subject: CFE.Descendant, to: IO.STREAM] = {
PrintDL: PROC [which: CellType, dls: Element, except: CSC.Descendant] = {
first: BOOLTRUE;
to.PutRope["{"];
FOR dls ← dls, dls.rest WHILE dls # NIL DO
IF except = NIL OR NOT CSC.DescendantEqual[except, dls.first] THEN {
IF first THEN first ← FALSE ELSE to.PutRope[", "];
to.PutRope[WITH dls.first SELECT FROM
x: CF.FlatWire => CF.WirePathRope[which, x^],
x: REF CF.InstancePath => CF.InstancePathRope[which, x^],
ENDCASE => ERROR];
};
ENDLOOP;
to.PutRope["}"];
};
WITH subject SELECT FROM
x: CF.FlatWire => NULL;
x: REF CF.InstancePath => NULL;
x: CF.FlatCellType => subject ← NEW [CF.InstancePath ← x.path];
ENDCASE => ERROR;
{dlp: CSC.Pair ~ IF Ans#NIL THEN Ans[which, subject] ELSE [NIL, NIL];
IF dlp # [NIL, NIL] THEN {
IF dlp[which].rest # NIL THEN {
to.PutRope["&"];
PrintDL[root, dlp[which], subject];
};
to.PutRope["~"];
PrintDL[other, dlp[OtherRole[which]], NIL];
};
}};
Printit: PROC = {
WithFilter: PROC [filter: CFE.Filter] ~ TRUSTED {
CFE.FlatPrint[root, filter, to, Annotate, [CutTest]];
};
to.PutRope[RoleDescriptions[which].Concat[": "]];
IF subset THEN CFE.MakeNeighborhoodFilter[ds, WithFilter] ELSE WithFilter[NIL];
};
PrintObject[to, Printit, always, " "];
to ← to;
};
PerMismatch: PROC
[
msg: ROPENIL,
kind: CSC.MismatchKind,
cts: CSC.CellTypePair,
colorElts: CSC.ColorElts,
Ans: CSC.QueryAnswerer
] --CSC.MismatchConsumer--
= {
cellTypeName: ROPE = CO.GetCellTypeName[cts[Source]];
PrintNeighborhood: PROC [to: IO.STREAM, which: Role, subset: BOOL] = {
other: Role ~ OtherRole[which];
PrintWork[to, which, subset, cts[which], cts[other], colorElts[which], Ans]};
PrintNeighborhoods: PROC [to: IO.STREAM] = {
IF NOT SS.IsAnSS[to] THEN to ← SS.Create[UB.NewInittedHandle[[margin: 50, output: [stream[to]]]]];
to.PutRope["Neighborhoods are: "];
PrintNeighborhood[to, Source, TRUE];
PrintNeighborhood[to, Extracted, TRUE];
};
PrintCircuits: PROC [to: IO.STREAM] = {
IF NOT SS.IsAnSS[to] THEN to ← SS.Create[UB.NewInittedHandle[[margin: 50, output: [stream[to]]]]];
to.PutRope["Circuits are: "];
PrintNeighborhood[to, Source, FALSE];
PrintNeighborhood[to, Extracted, FALSE];
};
Ignore: PROC = {IF cellTypeName.Length[] > 0 THEN [] ← namesOfCellTypesToIgnore.Store[cellTypeName, $TRUE] ELSE ERROR--can't do it, you turkey--};
IF cellTypeName.Length[] > 0 AND namesOfCellTypesToIgnore.Fetch[cellTypeName].found THEN RETURN;
CSC.PushTimer[$debug];
{ENABLE UNWIND => CSC.PopTimer[$debug];
IF didQuitEarly^ THEN {
FindType: PROC [elts: ElementList, root: CellType] RETURNS [ROPE] ~ {
WITH elts.first.first SELECT FROM
dw: CSC.DescendantWire => RETURN ["wire"];
di: CSC.DescendantCellInstance => RETURN [CO.GetCellTypeName[CF.ResolveFlatCellType[root, [di^]].cellType]];
ENDCASE => ERROR;
};
type: ROPE ~ IF colorElts[Source] # NIL THEN FindType[colorElts[Source], cts[Source]] ELSE FindType[colorElts[Extracted], cts[Extracted]];
TerminalIO.PutF["**** Doomed from the start in %g: you have %g layout %gs and %g in the source\n", [rope[cellTypeName]], [integer[GList.Length[colorElts[Extracted]]]], [rope[type]], [integer[GList.Length[colorElts[Source]]]]];
Message["Read 'em and weep"];
}
ELSE {
fws: FlatWireRecList ← NIL;
TerminalIO.PutF["*** %g %gbetween layout and source for %g:\n", IO.rope[kindName[kind]], [rope[IF msg # NIL THEN Rope.Cat["(", msg, ") "] ELSE NIL]], IO.rope[cellTypeName]];
TerminalIO.PutF["\tlayout\t=> %g\n", IO.rope[ElementsToRope[colorElts[Extracted], cts[Extracted]]]];
TerminalIO.PutF["\tsource\t=> %g\n", IO.rope[ElementsToRope[colorElts[Source], cts[Source]]]];
Contexize[];
FOR role: Role IN Role DO
clearFirst: BOOLTRUE;
FOR elts: ElementList ← colorElts[role], elts.rest UNTIL elts = NIL DO
FOR parts: Element ← elts.first, parts.rest UNTIL parts = NIL DO
WITH parts.first SELECT FROM
di: CSC.DescendantCellInstance => CoreCDUser.HighlightFlatCellType[root: roots[role], flatCell: [di^, 0], clearFirst: clearFirst];
dw: CSC.DescendantWire => CoreCDUser.HighlightFlatWire[root: roots[role], flatWire: dw^, clearFirst: clearFirst];
ENDCASE => ERROR;
clearFirst ← FALSE;
ENDLOOP;
ENDLOOP;
ENDLOOP;
Message["Joe Bob sez check it out"];
cd ← cd};
};
CSC.PopTimer[$debug]};
PerDroppedConnection: PROC [role: Role, subroot: Core.CellType, public, actual: CF.FlatWire] ~ {
PrintNeighborhood: PROC [to: IO.STREAM, subset: BOOL] = {
PrintWork[to, role, subset, subroot, NIL, LIST[LIST[actual]], NIL]};
Message[IO.PutFR["*** Dropped connection below %g %g between public %g and actual %g", IO.rope[RoleDescriptions[role]], [rope[CO.GetCellTypeName[subroot]]], [rope[CF.WirePathRope[subroot, public^]]], IO.rope[CF.WirePathRope[subroot, actual^]]]];
aok ← FALSE};
PerBogusMerge: PROC [role: Role, subroot: Core.CellType, w1, w2: CF.FlatWire, from: REF CF.InstancePath] ~ {
PrintNeighborhood: PROC [to: IO.STREAM, subset: BOOL] = {
PrintWork[to, role, subset, subroot, NIL, LIST[LIST[w1], LIST[w2]], NIL]};
Message[IO.PutFR["*** Wires %g and %g in %g %g not consistent with fusion by source at or below %g", [rope[CF.WirePathRope[subroot, w1^]]], [rope[CF.WirePathRope[subroot, w2^]]], [rope[RoleDescriptions[role]]], [rope[CO.GetCellTypeName[subroot]]], [rope[CF.InstancePathRope[subroot, from^]]] ]];
aok ← FALSE};
roots: CSC.CellTypePair ← ALL[extractedRoot];
stss: CSC.SubtreeSpecPair ← ALL[ExtractedSubtree];
mss: CSC.MergeSpecPair ← ALL[MergeByPinsAndFuseTransistors];
specVal: REF ANY ~ CP.InheritCellTypeProp[sourceRoot, transistorTolerancesProp];
oldChop: BOOL ~ cd.chop;
ttolsCopied: BOOLFALSE;
aok: BOOLTRUE;
roots[Source] ← sourceRoot;
stss[Source] ← SourceSubtree;
mss[Source] ← CSC.MergeNothing;
ttols ← RefineTolerances[specVal, ttols];
cd.chop ← FALSE;
IF NOT (CSC.FlattenAndCompare[RoleDescriptions, roots, stss, mss, GiveHints, NIL, PerMismatch, PerDroppedConnection, PerBogusMerge, NIL, trace, autoHack, mayQuitEarly, didQuitEarly, abort, ttols, cd.vsp, cd].isomorphic AND aok) THEN FoundProblems[sourceRoot, extractedRoot];
cd.chop ← oldChop;
RETURN};
RefineTolerances: PUBLIC PROC [propval: REF ANY, inherit: TransistorTolerances] RETURNS [ttols: TransistorTolerances] ~ {
IF propval=NIL THEN RETURN [inherit];
WITH propval SELECT FROM
x: ROPE => {
specRef: REF TolerancesSpec;
specTV: TV;
errorRope: ROPE;
noResult: BOOL;
[specTV, errorRope, noResult] ← Evaluate[Rope.Cat["[", x, "]"], CODE[TolerancesSpec]];
IF errorRope.Length#0 OR noResult THEN ERROR;
TRUSTED {specRef ← NARROW[AMBridge.RefFromTV[specTV]]};
ttols ← NEW [CSC.TransistorTolerancesPrivate ← [
length: IF specRef.length#0 THEN Normalize[specRef.length] ELSE inherit[length],
width: IF specRef.width#0 THEN Normalize[specRef.width] ELSE inherit[width]
]];
RETURN};
ENDCASE => ERROR};
Normalize: PROC [x: REAL] RETURNS [CSC.Tolerance] ~ {
RETURN [SELECT x FROM
>= 1.0 => [1.0/x, x],
> 0.0 => [x, 1.0/x],
ENDCASE => ERROR]};
MergeByPinsAndFuseTransistors: PROC
[
data: REF ANY,
parent: Core.CellType,
recastOfSubroot: BOOL,
EnumerateInstances: PROC [Consume: PROC [ci: CC.CellInstance] RETURNS [stop: BOOLFALSE]],
IdentifyActual: PROC [ci: CC.CellInstance, actual: Core.Wire, describe: BOOLFALSE] RETURNS [ActualID],
Consume: CSC.MergeConsumer,
Depublicize: CSC.Depublicizer] --MergeSpec-- =
{cd: CompareData ~ NARROW[data];
nFused: NAT ← 0;
si: SourceInfo ~ IF NOT recastOfSubroot THEN NARROW[cd.extractedToSourceInfo.Fetch[parent].val] ELSE NIL;
fkey: REF ~ NEW [ROPE ← "fused"];
FuseTransistors: PROC = {
Describe: PROC [ci: CC.CellInstance] RETURNS [description: ROPE] = {
ip: CF.InstancePath = CF.AddInstance[CF.nullInstancePath, ci, parent];
description ← CF.InstancePathRope[parent, ip];
};
CheckChild: PROC [ci: CC.CellInstance] RETURNS [stop: BOOLFALSE] = {
IF ci.type.class # CC.transistorCellClass THEN RETURN;
{ci1: CC.CellInstance = ci;
g1: ActualID = IdentifyActual[ci1, ci1.actual[0], debug];
c1a: ActualID = IdentifyActual[ci1, ci1.actual[1], debug];
c1b: ActualID = IdentifyActual[ci1, ci1.actual[2], debug];
CheckOthers: PROC [ci: CC.CellInstance] RETURNS [stop: BOOLFALSE] = {
IF ci.type.class # CC.transistorCellClass OR CP.GetCellInstanceProp[ci, fused] = fkey THEN RETURN;
IF ci = ci1 THEN RETURN [TRUE];
{ci2: CC.CellInstance = ci;
g2: ActualID = IdentifyActual[ci2, ci2.actual[0], debug];
IF g1.id = g2.id THEN {
c2a: ActualID = IdentifyActual[ci2, ci2.actual[1], debug];
c2b: ActualID = IdentifyActual[ci2, ci2.actual[2], debug];
IF c1a.id=c2a.id AND c1b.id=c2b.id OR c1a.id=c2b.id AND c1b.id=c2a.id THEN {
t1: CC.Transistor = NARROW [ci1.type.data];
t2: CC.Transistor = NARROW [ci2.type.data];
rl1: REF INT ~ NARROW[CoreProperties.GetCellTypeProp[ci1.type, CC.lengthProp]];
rl2: REF INT ~ NARROW[CoreProperties.GetCellTypeProp[ci2.type, CC.lengthProp]];
IF t1.type=t2.type AND (rl1=rl2 OR rl1#NIL AND rl2#NIL AND rl1^=rl2^) THEN {
IF debug THEN TerminalIO.PutF["%g || %g\n", [rope[Describe[ci1]]], [rope[Describe[ci2]]]];
CP.PutCellInstanceProp[ci2, fused, fkey];
nFused ← nFused + 1;
Consume[LIST[
NEW [CF.InstancePath ← CF.AddInstance[[], ci1, parent]],
NEW [CF.InstancePath ← CF.AddInstance[[], ci2, parent]]]];
}
ELSE IF debug THEN TerminalIO.PutF["%g # %g\n", [rope[Describe[ci1]]], [rope[Describe[ci2]]]];
}
ELSE IF debug THEN {
TerminalIO.PutF["%g[%g, %g] # ", [rope[Describe[ci1]]], [rope[c1a.description]], [rope[c1b.description]]];
TerminalIO.PutF["%g[%g, %g]\n", [rope[Describe[ci2]]], [rope[c2a.description]], [rope[c2b.description]]];
};
}
ELSE IF debug THEN TerminalIO.PutF["%g.gate~%g # %g.gate~%g\n", [rope[Describe[ci1]]], [rope[g1.description]], [rope[Describe[ci2]]], [rope[g2.description]]];
}};
EnumerateInstances[CheckOthers];
}};
EnumerateInstances[CheckChild];
IF nFused # 0 THEN {
TerminalIO.PutF["*** PWCoreLichen: %g pairwise transistor fusions in CellType %g.\n", [integer[nFused]], [rope[CO.GetCellTypeName[parent]]]];
};
};
FuseTransistors[];
IF check AND ((si#NIL) # (PWCore.GetPWCoreExtractedCTKey[parent]#0 AND NOT recastOfSubroot)) THEN ERROR;
IF si#NIL THEN {
MergeExtracted: PROC [key, val: REF ANY] RETURNS [quit: BOOLFALSE] --EachPairProc-- = {
sourcePublic: Wire = NARROW[key];
extractedPublics: Element = NARROW[val];
IF extractedPublics.rest # NIL THEN {
Consume[extractedPublics];
IF msgFusionBySource THEN {
sent: BOOL;
info: SimpleMailer.SendMessageInfo;
exts: ROPENIL;
FOR eps: Element ← extractedPublics, eps.rest WHILE eps#NIL DO
ep: CSC.DescendantWire ~ NARROW[eps.first];
IF ep.flatCell.path.length#0 THEN ERROR;
exts ← exts.Cat["\nExtracted public wire: ", FmtNames[parent.public, ep.wire]];
ENDLOOP;
[sent, info] ← SimpleMailer.SendMessage[from: "PWCoreLichen", to: LIST["Spreitzer.pa"], subject: "Fusion by source", otherHeader: IO.PutFR["Source cell type: %g\nExtracted cell type: %g\nSource public wire: %g%g", [rope[CO.InheritCellTypeName[si.sourceCT]]], [rope[CO.InheritCellTypeName[parent]]], [rope[FmtNames[si.sourceCT.public, sourcePublic]]], [rope[exts]] ]];
IF (NOT sent) OR info # ok THEN SIGNAL MsgFailed};
};
};
DiscardSpurriousPublic: PROC [ext: Core.Wire] ~ {
src: Core.Wire ~ NARROW[si.extractedToSource.Fetch[ext].val];
IF src=NIL THEN Depublicize[ext];
RETURN};
[] ← si.sourceToExtracteds.Pairs[MergeExtracted];
CO.VisitRootAtomics[parent.public, DiscardSpurriousPublic];
data ← data;
};
};
fused: ATOM = CoreIO.RegisterProperty[CP.RegisterUnprintableProperty[$PWCoreLichenTransistorFused], WriteNil, ReadNil];
debug: BOOLFALSE;
msgFusionBySource: BOOLFALSE;
FmtNames: PROC [root, wire: Core.Wire] RETURNS [fmt: ROPE] ~ {
n: INT ← 0;
FOR ns: LIST OF ROPECO.GetFullWireNames[root, wire], ns.rest WHILE ns#NIL DO
SELECT n ← n + 1 FROM
1 => fmt ← ns.first;
2 => fmt ← fmt.Cat["(aka ", ns.first];
>2 => fmt ← fmt.Cat[", ", ns.first];
ENDCASE => ERROR;
ENDLOOP;
IF n > 1 THEN fmt ← fmt.Concat[")"]};
ElementsToRope: PROC [elts: ElementList, root: CellType] RETURNS [rope: ROPENIL] = {
FmtPart: PROC [part: REF ANY] RETURNS [ROPE] ~ {
RETURN [WITH part SELECT FROM
fi: REF CF.InstancePath => CF.InstancePathRope[root, fi^],
fw: CF.FlatWire => CF.WirePathRope[root, fw^],
ENDCASE => ERROR]};
sep: ROPENIL;
WHILE elts#NIL DO
parts: Element ← elts.first;
this: ROPENIL;
IF parts.rest = NIL THEN this ← FmtPart[parts.first]
ELSE {
sep: ROPE ← "{";
WHILE parts # NIL DO
this ← this.Cat[sep, FmtPart[parts.first]];
parts ← parts.rest;
sep ← ", ";
ENDLOOP;
this ← this.Concat["}"];
};
rope ← rope.Cat[sep, this];
elts ← elts.rest;
sep ← ", ";
ENDLOOP;
rope ← rope;
};
ReverseRefTab: PROC [fwd: RefTab.Ref] RETURNS [bkwd: RefTab.Ref] = {
CopyPair: PROC [key, val: REF ANY] RETURNS [quit: BOOLFALSE] --RefTab.EachPairProc-- = {
wfrom: Wire = NARROW[key];
wto: Wire = NARROW[val];
lfrom: Element ← NARROW[bkwd.Fetch[wto].val];
lfromCONS[NEW [CF.FlatWireRec ← [wireRoot: public, wire: wfrom]], lfrom];
[] ← bkwd.Store[wto, lfrom];
};
bkwd ← RefTab.Create[];
[] ← fwd.Pairs[CopyPair];
};
CreateParallelTable: PROC [w1, w2: Wire] RETURNS [RefTab.Ref] ~ {
table: RefTab.Ref ~ RefTab.Create[];
PerPair: PROC [actualWire, publicWire: Wire] RETURNS [subWires: BOOLTRUE, quit: BOOLFALSE] ~ {
IF NOT table.Insert[actualWire, publicWire] THEN ERROR;
};
IF CO.VisitBinding[w1, w2, PerPair] THEN ERROR;
RETURN [table]};
GetConstraints: PUBLIC PROC [subroot: CellType] RETURNS [RefTab.Ref] ~ {RETURN CSC.GetConstraints[subroot]};
PrintObject: PROC [to: IO.STREAM, PrintIt: PROC, cond: UB.BreakCondition ← width, sep: ROPENIL] = {
SS.Bp[to, cond, step, sep];
SS.Begin[to];
PrintIt[!UNWIND => SS.End[to]];
SS.End[to];
};
step: NAT ← 3;
Interpreter interface (for tolerance specifications)
Evaluate: PROC
[
rope: ROPE,
target: AMTypes.Type ← AMTypes.nullType,
context: AMModel.Context ← NIL-- NIL means use AMModel.RootContext[LocalWorld[]]--,
symbolsList: Interpreter.SymbolsList ← NIL-- look here first for name to TV lookup--,
abort: Interpreter.AbortClosure ← [NIL, NIL]-- default is to never abort--]
RETURNS [result: TVNIL, errorRope: ROPENIL, noResult: BOOLFALSE] = {
inner: PROC = {
result ← InterpreterOps.Eval[
tree: InterpreterOps.ParseExpr[expr: Rope.Cat["& ← ", rope], errout: errorStream],
head: InterpreterOps.NewEvalHead[
context: context,
specials: symbolsList,
helpFatalClosure: [MyHelpFatal, errorStream],
abortClosure: abort],
target: target
! FatalInterpreterError => {errorStream.PutRope[msg]; CONTINUE}];
RETURN};
errorStream: IO.STREAMIO.ROS[];
IF context = NIL THEN TRUSTED {context ← AMModel.RootContext[WorldVM.LocalWorld[]]};
IF symbolsList = NIL THEN symbolsList ← LIST[SymTab.Create[]];
errorStream.PutRope[BackStop.Call[inner]];
errorRope ← IO.RopeFromROS[errorStream];
noResult ← (result = AMTypes.GetEmptyTV[]);
RETURN};
FatalInterpreterError: ERROR [msg: ROPE] ~ CODE;
MyHelpFatal: InterpreterOps.HelpFatal = {
PROC [data: REF, head: EvalHead, parent: Tree, msg: ROPE];
ERROR FatalInterpreterError[msg];
};
CoreFlat extensions (should be moved there)
HashPath: RefTab.HashProc ~ {
path: REF CF.InstancePath = NARROW[key];
RETURN[CF.InstancePathHash[path^]];
};
EqualPath: RefTab.EqualProc ~ {
p1: REF CF.InstancePath = NARROW[key1];
p2: REF CF.InstancePath = NARROW[key2];
RETURN[CF.InstancePathEqual[p1^, p2^]];
};
CoreIO hacks (to be removed someday)
WriteNil: CoreIO.PropWriteProc
~ {BrineIO.WriteID[stream, "a"]; BrineIO.WriteAtom[stream, $Foo]};
ReadNil: CoreIO.PropReadProc
~ {
id: ROPE ~ BrineIO.ReadID[stream];
a: ATOM ~ BrineIO.ReadAtom[stream];
IF NOT (id.Equal["a"] AND a=$Foo) THEN ERROR;
RETURN [NIL]};
Initialization
RoleDescriptions[Source] ← "source";
RoleDescriptions[Extracted] ← "layout";
UserProfile.CallWhenProfileChanges[TrackProfile];
CDSequencerExtras.RegisterCommand[key: $PWCoreLichenCompare, proc: CompareCmd, queue: doQueue];
CDSequencerExtras.RegisterCommand[key: $PWCoreLichenForgetComparison, proc: ForgetCmd, queue: doQueue];
CDSequencerExtras.RegisterCommand[key: $PWCoreLichenForgetAllComparisons, proc: GlobalForgetCmd, queue: doQueue];
CDSequencerExtras.RegisterCommand[key: $PWCoreLichenEnableAutomorphismHack, proc: SetAutomorphismHackCmd, queue: doQueue];
CDSequencerExtras.RegisterCommand[key: $PWCoreLichenDisableAutomorphismHack, proc: SetAutomorphismHackCmd, queue: doQueue];
CDSequencerExtras.RegisterCommand[key: $PWCoreLichenEnableQuitEarly, proc: SetQuitEarlyCmd, queue: doQueue];
CDSequencerExtras.RegisterCommand[key: $PWCoreLichenDisableQuitEarly, proc: SetQuitEarlyCmd, queue: doQueue];
CDSequencerExtras.RegisterCommand[key: $PWCoreLichenWideTolerances, proc: SetTolerancesCmd, queue: doQueue];
CDSequencerExtras.RegisterCommand[key: $PWCoreLichenTightTolerances, proc: SetTolerancesCmd, queue: doQueue];
CDSequencerExtras.RegisterCommand[key: $PWCoreLichenReadTolerances, proc: SetTolerancesCmd, queue: doQueue];
CDSequencerExtras.RegisterCommand[key: $PWCoreLichenDisplayStats, proc: StatsCmd, queue: doQueue];
CDSequencerExtras.RegisterCommand[key: $PWCoreLichenPrintConstraints, proc: PrintConstraintsCmd, queue: doQueue];
END.