SinixImpl.mesa
Copyright © 1985, 1986 by Xerox Corporation. All rights reversed.
Created by Bertrand Serlet August 15, 1985 2:09:54 pm PDT
Bertrand Serlet December 9, 1986 1:40:25 am PST
Pradeep Sindhu May 5, 1986 5:16:10 pm PDT
Barth, April 18, 1986 4:42:48 pm PST
DIRECTORY
Basics,
CD, CDBasics, CDDefaultProcs, CDDirectory, CDInstances, CDOrient, CDProperties, CDSatellites, CDSymbolicObjects,
CedarProcess,
Core, CoreClasses, CoreOps, CoreProperties,
CoreGeometry,
D2Intervals, GList, HashTable, IO, Process,
PW, PWObjects,
Rope, RopeList,
Sinix;
SinixImpl: CEDAR PROGRAM
IMPORTS Basics, CD, CDBasics, CDDefaultProcs, CDDirectory, CDInstances, CDOrient, CDProperties, CDSatellites, CDSymbolicObjects, CedarProcess, CoreClasses, CoreOps, CoreProperties, CoreGeometry, D2Intervals, GList, HashTable, IO, Process, PW, PWObjects, Rope, RopeList
EXPORTS Sinix
SHARES CoreGeometry =
BEGIN OPEN Sinix;
Fusion data structure and internal manipulation
InternalBug: PUBLIC SIGNAL [name: ROPE] = CODE;
CallerBug: PUBLIC SIGNAL [] = CODE;
FusionPropMismatch: PUBLIC SIGNAL [name: ROPE, prop: ATOM, value1, value2: REF] = CODE;
FusionStructureMismatch: PUBLIC SIGNAL [name: ROPE, wire1, wire2: Wire] = CODE;
StructureMismatch: PUBLIC SIGNAL [name: ROPE, actual, subPublic: Wire] = CODE;
FusionByNameMismatch: PUBLIC SIGNAL [name, msg: ROPE, wire: Wire] = CODE;
StructuralLoop: PUBLIC SIGNAL [name: ROPE, wire: Wire] = CODE;
ROPES: TYPE = LIST OF ROPE;
NameWire: TYPE = RECORD [name: ROPE, wire: Wire];
FusionData: TYPE = REF FusionDataRec;
FusionDataRec: TYPE = RECORD [
mode: Mode,      -- to avoid passing it around
nir: CD.Rect,      -- Rectangles for which geometry that is strictly inside (edge excluded) is not promoted public
name: ROPE,      -- name of the object currently extracted
isAbut: BOOL,     -- recording publics depend on this property
fused: HashTable.Table,  -- Association [fused -> root] [Wire -> Wire]. If root=NIL, then this wire is a root. Basically all the wires ever created belong to this table.
data: REF,       -- different information for Abut and Cell. For abuts it contains the next rects, (REF D2Intervals.Table). For Cell, it is a LIST OF NameWire. This list is not sorted at creation time, and same name may appear several times.
rects: SEQUENCE nbOfLayers: NAT OF D2Intervals.Table -- Association (per layer) [fusion geometry -> wire] [D2Intervals.Rect -> IW]. Wire may or may not be a root.
];
Geometry can be attached at all levels of a structured wire.
AbutData: TYPE = REF AbutDataRec;
AbutDataRec: TYPE = RECORD [
inX: BOOL,      -- to avoid passing it around
nextRects: SEQUENCE nbOfLayers: NAT OF D2Intervals.Table -- Association (per layer) [fusion geometry -> wire] [D2Intervals.Rect -> IW]. Wire may or may not be a root.
];
IW: TYPE = REF IWRec;
IWRec: TYPE = RECORD [instance: CD.Instance, wire: Wire];
RectToRect: PROC [cdRect: CD.Rect] RETURNS [rect: D2Intervals.Rect] = INLINE {
rect ← [[cdRect.x1, cdRect.x2], [cdRect.y1, cdRect.y2]];
};
InstanceToRect: PROC [inst: CD.Instance] RETURNS [rect: D2Intervals.Rect] = INLINE {
rect ← RectToRect[
IF inst.ob.class.interestRect=CDDefaultProcs.InterestRect -- simple rectangles !
THEN CDInstances.InstRectO[inst]
ELSE CDBasics.Surround[CDInstances.InstRectO[inst], CDInstances.InstRectI[inst]]
];
};
IWToRect: PROC [table: D2Intervals.Table, value: D2Intervals.Value] RETURNS [rect: D2Intervals.Rect] = {
iw: IWNARROW [value];
mode: Mode ← NARROW [table.userData];
rect ← InstanceToRect[iw.instance];
};
CreateRoot: PROC [fusionData: FusionData, size: NAT, props: Properties ← NIL] RETURNS [wire: Wire] = {
EachProperty: PROC [prop: ATOM, val: REF ANYNIL] = {
newVal: REF ANYSELECT prop FROM
fusionData.mode.decoration.pinsProp => NIL,
fusionData.mode.decoration.geometryProp => NIL,
CoreOps.nameProp => NIL,
ENDCASE => val;
CoreProperties.PutWireProp[wire, prop, newVal];
};
wire ← CoreOps.CreateWires[size: size];
CoreProperties.Enumerate[props, EachProperty];
[] ← HashTable.Store[fusionData.fused, wire, NIL];
};
copyGeometry indicates if this geometry is only going to be used for fusion and does not have to figure in the geometry and pins properties (real geometry versus geometry at the level below).
InsertInstances: PROC [fusionData: FusionData, transformation: CD.Instance, instances: LIST OF CD.Instance, wire: Wire, copyGeometry: BOOL] = {
pins: LIST OF CD.Instance ← CoreGeometry.GetPins[fusionData.mode.decoration, wire];
IF ~HashTable.Fetch[fusionData.fused, wire].found THEN SIGNAL InternalBug[fusionData.name];
IF RootWire[fusionData, wire]#wire THEN SIGNAL InternalBug[fusionData.name];
note that if copyGeometry AND NOT fusionData.isAbut, instances might be duplicated on pins and geometry
FOR is: LIST OF CD.Instance ← instances, is.rest WHILE is#NIL DO
inst: CD.Instance ← CoreGeometry.Transform[transformation, is.first];
iw: IWNEW [IWRec ← [instance: inst, wire: wire]];
layers: LayerRange ← fusionData.mode.instanceLayer[inst];
IF copyGeometry THEN CoreGeometry.PutGeometry[fusionData.mode.decoration, wire, CONS [inst, CoreGeometry.GetGeometry[fusionData.mode.decoration, wire]]];
FOR i: NAT IN [layers.min .. layers.max] DO D2Intervals.Insert[fusionData[i], iw] ENDLOOP;
IF NOT CoreGeometry.AtEdge[fusionData.nir, inst] THEN LOOP;
If a pin is found, either we are extracting an abut, and then we just note that this is a public wire, or we are extracting a cell and we physically add the pin [and that is the test for being public]
IF fusionData.isAbut
THEN CoreProperties.PutWireProp[wire, $Public, $Public]
ELSE pins ← CONS [inst, pins];
ENDLOOP;
CoreGeometry.PutPins[fusionData.mode.decoration, wire, pins];
};
Fusion external manipulation
Creates a DAG Wire from a HashTable, getting rid of wires that have their father also in the list.
CreateDAGWire: PROC [table: HashTable.Table] RETURNS [wire: Wire] = {
wires: LIST OF Wire ← NIL;
EachKeyDelete: HashTable.EachPairAction = {
WireDelete: PROC [wire: Wire] = {
IF ~HashTable.Delete[table, wire] THEN RETURN; -- already done
FOR i: NAT IN [0 .. wire.size) DO
WireDelete[wire[i]];
ENDLOOP;
};
wire: Wire ← NARROW [key];
FOR i: NAT IN [0 .. wire.size) DO
delete all the sons
WireDelete[wire[i]];
ENDLOOP;
};
index: NAT ← 0;
SummarizeWires: HashTable.EachPairAction = {
wire[index] ← NARROW [key];
index ← index+1;
};
[] ← HashTable.Pairs[table, EachKeyDelete];
wire ← CoreOps.CreateWires[HashTable.GetSize[table]];
[] ← HashTable.Pairs[table, SummarizeWires];
IF index#HashTable.GetSize[table] THEN SIGNAL InternalBug[NIL];
};
LayoutFusionByName: PROC [fusionData: FusionData] = {
refList: REF LIST OF NameWire = NARROW [fusionData.data];
nameToWire: HashTable.Table = HashTable.Create[mod: 11, equal: HashTable.RopeEqual, hash: HashTable.HashRope];
FOR nameWires: LIST OF NameWire ← refList^, nameWires.rest WHILE nameWires#NIL DO
name: ROPE = nameWires.first.name;
wire: Wire ← RootWire[fusionData, nameWires.first.wire];
previousName: ROPE = CoreOps.GetShortWireName[wire];
previousWire: Wire ← NARROW [HashTable.Fetch[nameToWire, name].value];
IF previousName#NIL AND ~Rope.Equal[name, previousName]
THEN SIGNAL FusionPropMismatch[fusionData.name, CoreOps.nameProp, name, previousName];
IF previousWire#NIL THEN {
previousWire ← RootWire[fusionData, previousWire];
IF previousWire#wire THEN {
wire ← StructuredFusion[fusionData, wire, previousWire];
PW.WriteF["Fusion by name for '%g' in cell '%g'.\n", IO.rope[name], IO.rope[fusionData.name]];
CoreProperties.PutWireProp[wire, $FusedByName, $FusedByName];
};
};
[] ← HashTable.Store[nameToWire, name, wire];
[] ← CoreOps.SetShortWireName[wire, name];
ENDLOOP;
};
NoFusionByName: PROC [fusionData: FusionData] = {
refList: REF LIST OF NameWire = NARROW [fusionData.data];
EachNameWire: HashTable.EachPairAction = {
uniqueID: INT ← 0;
name: ROPE = NARROW [key];
wires: Wires ← NARROW [value];
[] ← CoreOps.SetShortWireName[wires.first, name];
wires ← wires.rest;
IF wires=NIL THEN RETURN;
PW.WriteF["NO fusion by name for '%g' in cell '%g'.\n", IO.rope[name], IO.rope[fusionData.name]];
WHILE wires#NIL DO
[] ← CoreOps.SetShortWireName[wires.first, IO.PutFR["%g$%g$", IO.rope[name], IO.int[uniqueID]]];
uniqueID ← uniqueID + 1;
wires ← wires.rest;
ENDLOOP;
};
nameToWires: HashTable.Table = HashTable.Create[mod: 11, equal: HashTable.RopeEqual, hash: HashTable.HashRope]; -- contains (for every name) the list of roots that have it for name
FOR nameWires: LIST OF NameWire ← refList^, nameWires.rest WHILE nameWires#NIL DO
name: ROPE ← nameWires.first.name;
wire: Wire = RootWire[fusionData, nameWires.first.wire];
wires: Wires ← NARROW [HashTable.Fetch[nameToWires, name].value];
IF NOT CoreOps.Member[wires, wire] THEN wires ← CONS [wire, wires];
[] ← HashTable.Store[nameToWires, name, wires];
ENDLOOP;
[] ← HashTable.Pairs[nameToWires, EachNameWire];
};
SchematicsFusionByName: PROC [fusionData: FusionData] = {
refList: REF LIST OF NameWire = NARROW [fusionData.data];
nameToWire: HashTable.Table = HashTable.Create[mod: 11, equal: HashTable.RopeEqual, hash: HashTable.HashRope];
names: ROPESNIL;
We first fuse all the nameWire with same name, and at the same time prepare the list of all the names that have components
FOR nameWires: LIST OF NameWire ← refList^, nameWires.rest WHILE nameWires#NIL DO
name: ROPE = nameWires.first.name;
wire: Wire ← RootWire[fusionData, nameWires.first.wire];
previousName: ROPE = CoreOps.GetShortWireName[wire];
previousWire: Wire ← NARROW [HashTable.Fetch[nameToWire, name].value];
hasComponents: BOOL = CoreOps.ParseWireName[name].components#NIL;
IF previousName#NIL AND ~Rope.Equal[name, previousName]
THEN SIGNAL FusionPropMismatch[fusionData.name, CoreOps.nameProp, name, previousName];
IF previousWire#NIL THEN previousWire ← RootWire[fusionData, previousWire];
IF previousWire#NIL AND previousWire#wire
THEN wire ← StructuredFusion[fusionData, wire, previousWire]
ELSE IF hasComponents THEN names ← CONS [name, names];
[] ← HashTable.Store[nameToWire, name, wire];
IF NOT hasComponents THEN [] ← CoreOps.SetShortWireName[wire, name];
ENDLOOP;
We sort the names, using the trick that foo[2].mumble or foo[2][3] are after foo[2] in the lexicographic order
names ← RopeList.Sort[names, RopeList.Compare];
FOR list: ROPES ← RopeList.Sort[names, RopeList.Compare], list.rest WHILE list#NIL DO
name: ROPE ← list.first;
wire: Wire = RootWire[fusionData, NARROW [HashTable.Fetch[nameToWire, name].value]];
base: ROPE; components: ROPESNIL;
matchingWire: Wire;
[base, components] ← CoreOps.ParseWireName[name];
matchingWire ← NARROW [HashTable.Fetch[nameToWire, base].value];
WHILE components#NIL DO
index: INT ← -1;
IF matchingWire=NIL THEN SIGNAL FusionByNameMismatch[fusionData.name, IO.PutFR["Path name %g does not correspond to any wire", IO.rope[name]], wire];
matchingWire ← RootWire[fusionData, matchingWire];
FOR i: NAT IN [0 .. matchingWire.size) DO
subWireName: ROPE ← CoreOps.GetShortWireName[matchingWire[i]];
IF subWireName=NIL THEN subWireName ← IO.PutR1[IO.int[i]];
IF Rope.Equal[subWireName, components.first] THEN {
IF index=-1 THEN index ← i ELSE FusionByNameMismatch[fusionData.name, IO.PutFR["Current wire has 2 or more sub-wires with field `%g'", IO.rope[components.first]], wire];
};
ENDLOOP;
IF index=-1 THEN SIGNAL FusionByNameMismatch[fusionData.name, IO.PutFR["Current wire does not have a sub-wire with field `%g'", IO.rope[components.first]], wire];
matchingWire ← matchingWire[index];
components ← components.rest;
ENDLOOP;
[] ← StructuredFusion[fusionData, matchingWire, wire];
ENDLOOP;
};
AddNameWire: PROC [fusionData: FusionData, name: ROPE, wire: Wire] = {
nameWires: REF LIST OF NameWire ← NARROW [fusionData.data];
nameWires^ ← CONS [[name: name, wire: wire], nameWires^];
};
The following PROC implements Fisher-Galler fusion, not for efficiency, but for simplicity
RootWire: PROC [fusionData: FusionData, wire: Wire] RETURNS [rootWire: Wire] = {
IF wire=NIL THEN SIGNAL InternalBug[fusionData.name];
rootWire ← NARROW [HashTable.Fetch[fusionData.fused, wire].value];
IF rootWire=NIL THEN RETURN [wire];
IF rootWire=wire THEN SIGNAL InternalBug[fusionData.name];
rootWire ← RootWire[fusionData, rootWire];
[] ← HashTable.Replace[fusionData.fused, wire, rootWire];
};
This mechanism could be exported if we wanted that.
PropFusionProc: TYPE = PROC [prop: ATOM, value1, value2: REF ANY, name: ROPE] RETURNS [value: REF ANY];
NameFusion: PropFusionProc = {
rope1: ROPENARROW [value1];
rope2: ROPENARROW [value2];
IF rope1#NIL AND rope2#NIL AND ~Rope.Equal[rope1, rope2] THEN SIGNAL FusionPropMismatch[name, prop, rope1, rope2];
value ← IF rope1=NIL THEN rope2 ELSE rope1;
};
InstancesFusion: PropFusionProc = {
instances1: LIST OF CD.Instance ← NARROW [value1];
instances2: LIST OF CD.Instance ← NARROW [value2];
value ← CDInstances.AppendToList[instances1, instances2];
};
DefaultFusion: PropFusionProc = {
IF value1#NIL AND value2#NIL AND value1#value2 THEN SIGNAL FusionPropMismatch[name, prop, value1, value2];
value ← IF value1=NIL THEN value2 ELSE value1;
};
Appends the properties of fused to the ones of root, but treating short names specially
FuseProperties: PUBLIC PROC [mode: Sinix.Mode, fused, root: Wire, name: ROPE] = {
EachProperty: PROC [prop: ATOM, val: REF ANY] = {
rootValue: REF ← CoreProperties.GetWireProp[root, prop];
SELECT prop FROM
mode.decoration.pinsProp => CoreGeometry.PutPins[mode.decoration, root, CDInstances.AppendToList[CoreGeometry.GetPins[mode.decoration, root], CoreGeometry.GetPins[mode.decoration, fused]]];
mode.decoration.geometryProp => CoreGeometry.PutGeometry[mode.decoration, root, CDInstances.AppendToList[CoreGeometry.GetGeometry[mode.decoration, root], CoreGeometry.GetGeometry[mode.decoration, fused]]];
CoreOps.nameProp   => CoreProperties.PutWireProp[root, prop, NameFusion[prop, val, rootValue, name]];
ENDCASE      => CoreProperties.PutWireProp[root, prop, DefaultFusion[prop, val, rootValue, name]];
};
CoreProperties.Enumerate[fused.properties, EachProperty];
};
This proc really does the fusion of fused and root, by wiping fused of some tables, and adding interesting properties of fused to root.
DeleteFusioned: PROC [fusionData: FusionData, fused, root: Wire] = {
IF ~HashTable.Fetch[fusionData.fused, fused].found THEN SIGNAL InternalBug[fusionData.name];
IF RootWire[fusionData, fused]#fused THEN SIGNAL InternalBug[fusionData.name];
IF ~HashTable.Fetch[fusionData.fused, root].found THEN SIGNAL InternalBug[fusionData.name];
IF RootWire[fusionData, root]#root THEN SIGNAL InternalBug[fusionData.name];
IF fused=root THEN SIGNAL InternalBug[fusionData.name]; -- should never occur
FuseProperties[fusionData.mode, fused, root, fusionData.name];
fused.properties ← NIL; -- to help GC
[] ← HashTable.Store[fusionData.fused, fused, root];
};
StructuredFusion: PROC [fusionData: FusionData, wire1, wire2: Wire] RETURNS [wire: Wire] = {
wire1 ← RootWire[fusionData, wire1];
wire2 ← RootWire[fusionData, wire2];
IF wire1=wire2 THEN RETURN [wire1];
SELECT TRUE FROM
wire1=wire2    => wire ← wire1;
wire1.size=0     => {DeleteFusioned[fusionData, wire1, wire2]; wire ← wire2};
wire2.size=0    => {DeleteFusioned[fusionData, wire2, wire1]; wire ← wire1};
wire1.size=wire2.size => {
wire ← CreateRoot[fusionData, wire1.size];
FOR i: NAT IN [0 .. wire.size) DO
wire[i] ← StructuredFusion[fusionData, wire1[i], wire2[i]];
ENDLOOP;
DeleteFusioned[fusionData, wire1, wire];
DeleteFusioned[fusionData, wire2, wire];
};
ENDCASE     => SIGNAL FusionStructureMismatch[fusionData.name, wire1, wire2];
};
FindTouchingWires: PROC [fusionData: FusionData, transformation: CD.Instance, geometry: LIST OF CD.Instance] RETURNS [touchingWires: LIST OF Wire ← NIL] = {
FOR insts: LIST OF CD.Instance ← geometry, insts.rest WHILE insts#NIL DO
inst: CD.Instance ← CoreGeometry.Transform[transformation, insts.first];
layers: LayerRange ← fusionData.mode.instanceLayer[inst];
rect: D2Intervals.Rect ← InstanceToRect[inst];
InternalFindTouchingWires: PROC [table: D2Intervals.Table, value: D2Intervals.Value] RETURNS [quit: BOOLFALSE] = {
iw: IWNARROW [value];
instance: CD.Instance ← iw.instance;
wire: Wire ← RootWire[fusionData, iw.wire];
IF ~HashTable.Fetch[fusionData.fused, wire].found THEN SIGNAL InternalBug[fusionData.name];
IF CoreOps.Member[touchingWires, wire] THEN RETURN;
IF fusionData.mode.touchProc[instance, inst] THEN touchingWires ← CONS [wire, touchingWires];
};
FOR i: NAT IN [layers.min .. layers.max] DO
[] ← D2Intervals.Enumerate[fusionData[i], InternalFindTouchingWires, rect];
ENDLOOP;
ENDLOOP;
};
Other Internal utilities
FusionGeometry: PROC [fusionData: FusionData, transformation: CD.Instance, geometry: LIST OF CD.Instance, copyGeometry: BOOL] RETURNS [wire: Wire ← NIL] = {
touchingWires: LIST OF Wire ← FindTouchingWires[fusionData, transformation, geometry];
IF touchingWires=NIL THEN {
wire ← CreateRoot[fusionData, 0];
InsertInstances[fusionData, transformation, geometry, wire, copyGeometry];
RETURN;
};
wire ← touchingWires.first;
InsertInstances[fusionData, transformation, geometry, wire, copyGeometry];
touchingWires ← touchingWires.rest;
WHILE touchingWires#NIL DO
fused: Wire ← touchingWires.first;
wire ← StructuredFusion[fusionData, fused, wire];
touchingWires ← touchingWires.rest;
ENDLOOP;
};
The complexity of this proc is partly due to the fact that we have a DAG
FusionWire: PROC [fusionData: FusionData, dagTable: HashTable.Table, public: Wire, transformation: CD.Instance, copyProps: BOOL] RETURNS [actual: Wire] = {
prevActual: Wire ← NARROW [HashTable.Fetch[dagTable, public].value];
structActual: Wire;
publicName: ROPE ← CoreOps.GetShortWireName[public];
pins: LIST OF CD.Instance ← CoreGeometry.GetPins[fusionData.mode.decoration, public];
IF prevActual#NIL THEN RETURN [prevActual];
structActual ← CreateRoot[fusionData, public.size, IF copyProps THEN public.properties ELSE NIL];
FOR i: NAT IN [0 .. public.size) DO
structActual[i] ← FusionWire[fusionData, dagTable, public[i], transformation, copyProps];
ENDLOOP;
actual ← IF pins=NIL
THEN structActual
ELSE StructuredFusion[
fusionData, structActual,
FusionGeometry[fusionData, transformation, pins, copyProps]
];
IF copyProps AND publicName#NIL THEN AddNameWire[fusionData, publicName, actual];
[] ← HashTable.Store[dagTable, public, actual];
};
FusionWires: PROC [fusionData: FusionData, public: Wire, transformation: CD.Instance] RETURNS [actual: Wire] = {
dagTable: HashTable.Table ← HashTable.Create[3];
actual ← CoreOps.CreateWires[public.size];
FOR i: NAT IN [0 .. actual.size) DO
actual[i] ← FusionWire[fusionData, dagTable, public[i], transformation, FALSE];
ENDLOOP;
};
SortInstances: PROC [instances: LIST OF CD.Instance] RETURNS [sorted: LIST OF CD.Instance ← NIL] = {
Eval: PROC [inst: CD.Instance] RETURNS [INT] = {
pos: CD.Position ← CDBasics.BaseOfRect[CDInstances.InstRectI[inst]];
RETURN [pos.x+pos.y];
};
Compare: GList.CompareProc = {
RETURN [Basics.CompareINT[Eval[NARROW [ref1]], Eval[NARROW [ref2]]]];
};
sorted ← NARROW [GList.Sort[CDInstances.AppendToList[instances, NIL], Compare]]; -- it is important to copy instances, because GList.Sort modifies physically its argument
};
Extraction
cacheProp: PUBLIC ATOMPW.RegisterProp[$SinixCache, FALSE, TRUE];
Extract: PUBLIC ExtractProc = {
cache: Cache ← NARROW [CDProperties.GetObjectProp[obj, cacheProp]];
CDProperties.PutObjectProp[obj, satellitesProp, CDSatellites.GetSatelliteRopes[obj]];
IF cache#NIL AND cache.mode=mode AND mode.equalProc[obj, cache.properties, cache.userData, properties, userData] THEN RETURN [result: cache.result, props: cache.props];
BEGIN
priority: CedarProcess.Priority ← CedarProcess.GetPriority[];
atom: ATOMNARROW [CDProperties.GetListProp[properties, mode.extractProcProp]];
extractProc: ExtractProc;
IF atom=NIL THEN atom ← NARROW [CDProperties.GetObjectProp[obj, mode.extractProcProp]];
IF atom=NIL THEN atom ← NARROW [CDProperties.GetProp[obj.class, mode.extractProcProp]];
IF atom=NIL THEN extractProc ← ExtractExpand ELSE {
refProc: REF ExtractProc ← NARROW [HashTable.Fetch[registeredExtractProcs, atom].value];
IF refProc=NIL THEN {
PW.WriteF["*** ExtractProc $%g not registered. You must run the program defining it.\n", IO.atom[atom]]; SIGNAL CallerBug[];
};
extractProc ← refProc^;
};
CedarProcess.CheckAbort[];
CedarProcess.SetPriority[background];
Process.Yield[];
[result, props] ← extractProc[obj, mode, properties, userData];
We detect trivial causes of bugs!
WITH result SELECT FROM
wire: Wire    => {};
wires: Wires    => {};
cellType: CellType  => IF NOT CoreGeometry.HasIR[mode.decoration, cellType] THEN SIGNAL CallerBug[]; -- IR decoration missing!
ENDCASE    => IF result#NIL THEN SIGNAL CallerBug[]; -- probably some ExtractProc is grossly wrong
CedarProcess.SetPriority[priority];
CDProperties.PutObjectProp[obj, cacheProp, NEW [CacheRec ← [mode: mode, properties: properties, userData: userData, result: result, props: props]]];
END;
};
registeredExtractProcs: HashTable.Table ← HashTable.Create[];
RegisterExtractProc: PUBLIC PROC [key: ATOM, extractProc: ExtractProc] = {
IF NOT HashTable.Store[registeredExtractProcs, key, NEW [ExtractProc ← extractProc]] THEN PW.WriteF["ExtractProc overwritten for $%g.\n", IO.atom[key]];
};
ExtractCell: PUBLIC ExtractProc = {
cellType: CellType; -- the result
ir: CD.Rect ← CD.InterestRect[obj];
publics: HashTable.Table ← HashTable.Create[3];
internals: HashTable.Table ← HashTable.Create[3];
cellPtr: CD.CellPtr ← NARROW [obj.specificRef];
cdInstances: LIST OF CD.Instance ← SortInstances[cellPtr.contents];
fusionData: FusionData ← NEW [FusionDataRec[mode.nbOfLayers]];
currentInstances: LIST OF CoreClasses.CellInstance ← NIL;
count: NAT ← 0;
FOR list: LIST OF CD.Instance ← cdInstances, list.rest WHILE list#NIL DO count ← count+1 ENDLOOP;
PW.WriteF["Extracting [%g] cell %g (size: [%g, %g], instances: %g)\n", IO.rope[IF mode.fusionByName=none THEN IO.PutFR["%g[%g]", IO.rope[mode.decoration.name], IO.int[NARROW [userData, REF INT]^]] ELSE mode.decoration.name], IO.rope[CDDirectory.Name[obj]], IO.int[obj.size.x], IO.int[obj.size.y], IO.int[count]];
fusionData.mode ← mode; fusionData.name ← CDDirectory.Name[obj];
fusionData.nir ← IF mode.fusionByName=none
THEN CDBasics.Extend[ir, - NARROW [userData, REF INT]^] ELSE ir;
fusionData.fused ← HashTable.Create[3]; fusionData.data ← NEW [LIST OF NameWire ← NIL]; fusionData.isAbut ← FALSE;
FOR i: NAT IN [0 .. mode.nbOfLayers) DO
fusionData[i] ← D2Intervals.Create[
range: RectToRect[CDBasics.Surround[CDBasics.RectAt[[0, 0], obj.size], ir]],
valueRect: IWToRect,
userData: mode];
ENDLOOP;
CDProperties.PutObjectProp[obj, satellitesProp, CDSatellites.GetSatelliteRopes[obj]]; -- to reinforce invariantes on the object (restriction of CDSatellites)
WHILE cdInstances#NIL DO
cdInstance: CD.Instance ← cdInstances.first;
subUserData: REF ← userData;
subResult: REF; subProps: Properties;
CDProperties.PutInstanceProp[cdInstance, satellitesProp, CDSatellites.GetSatelliteRopes[cdInstance]];
IF mode.fusionByName=none THEN {
UnionOverlap: PROC [rect: CD.Rect] = {
interSize: CD.Position;
IF overlap+overlap>bbox.x2-bbox.x1 OR overlap+overlap>bbox.y2-bbox.y1 THEN RETURN;
interSize ← CDBasics.SizeOfRect[CDBasics.Intersection[rect, CDBasics.Extend[bbox, -overlap]]];
overlap ← MAX [overlap, MIN [interSize.x, interSize.y]];
IF overlap+overlap>bbox.x2-bbox.x1 OR overlap+overlap>bbox.y2-bbox.y1 THEN RETURN;
interSize ← CDBasics.SizeOfRect[CDBasics.Intersection[rect, CDBasics.Extend[bbox, -overlap]]];
overlap ← MAX [overlap, MIN [interSize.x, interSize.y]];
};
Compute the right overlap of theses cells on this instance
bbox: CD.Rect ← CDInstances.InstRectI[cdInstance];
overlap: INT ← 0;
UnionOverlap[[FIRST[INT], FIRST[INT], fusionData.nir.x1, LAST[INT]]];
UnionOverlap[[fusionData.nir.x2, FIRST[INT], LAST[INT], LAST[INT]]];
UnionOverlap[[FIRST[INT], FIRST[INT], LAST[INT], fusionData.nir.y1]];
UnionOverlap[[FIRST[INT], fusionData.nir.y2, LAST[INT], LAST[INT]]];
FOR list: LIST OF CD.Instance ← cellPtr.contents, list.rest WHILE list#NIL DO
IF list.first#cdInstance THEN UnionOverlap[CDInstances.InstRectO[list.first]];
ENDLOOP;
subUserData ← NEW [INT ← overlap];
};
[subResult, subProps] ← Extract[cdInstance.ob, mode, cdInstance.properties, subUserData];
IF subResult#NIL THEN WITH subResult SELECT FROM
subWire: Wire    => {
[] ← FusionWire[fusionData, HashTable.Create[1], subWire, cdInstance, TRUE];
};
subWires: Wires    => {
dagTable: HashTable.Table ← HashTable.Create[3];
WHILE subWires#NIL DO
[] ← FusionWire[fusionData, dagTable, subWires.first, cdInstance, TRUE];
subWires ← subWires.rest;
ENDLOOP;
};
subCellType: CellType => {
bbox: CD.Rect ← CDInstances.BoundingRectO[cdInstances.rest];
instance: CoreClasses.CellInstance ← CoreClasses.CreateInstance[
actual: FusionWires[fusionData, subCellType.public, cdInstance],
type: subCellType, props: subProps
];
it is impossible here to check for conformance, since fusion might have been done differently in different parts of the DAG
CoreGeometry.PutTransf[mode.decoration, instance, cdInstance];
currentInstances ← CONS [instance, currentInstances];
We try to simplify fusionData by getting rid of all pieces of geometry which are outside the bounding box for sure
FOR i: NAT IN [0 .. mode.nbOfLayers) DO
DeleteOutsideRange: PROC [table: D2Intervals.Table, value: D2Intervals.Value] RETURNS [quit: BOOLFALSE] = {
valueInst: CD.Instance ← NARROW [value, IW].instance;
IF ~CoreGeometry.Intersect[bbox, valueInst] THEN D2Intervals.Delete[fusionData[i], value];
};
[] ← D2Intervals.Enumerate[fusionData[i], DeleteOutsideRange, [[FIRST[INT], bbox.x1], D2Intervals.universe.y]];
[] ← D2Intervals.Enumerate[fusionData[i], DeleteOutsideRange, [[bbox.x2, LAST[INT]], D2Intervals.universe.y]];
[] ← D2Intervals.Enumerate[fusionData[i], DeleteOutsideRange, [D2Intervals.universe.x, [FIRST[INT], bbox.y1]]];
[] ← D2Intervals.Enumerate[fusionData[i], DeleteOutsideRange, [D2Intervals.universe.x, [bbox.y2, LAST[INT]]]];
ENDLOOP;
};
ENDCASE    => SIGNAL InternalBug[fusionData.name];
cdInstances ← cdInstances.rest;
ENDLOOP;
Time to do fusion by name!
SELECT mode.fusionByName FROM
layout  => LayoutFusionByName[fusionData];
none   => NoFusionByName[fusionData];
schematics => SchematicsFusionByName[fusionData];
ENDCASE  => CallerBug[]; --no way to do fusionByName in this mode!
We replace all components of structured wires by their roots. We compute internals and publics on the way!
BEGIN
InternalDagIfy: HashTable.EachPairAction = {
WireDagIfy: PROC [wire: Wire, maxIter: NAT ← 32] = {
32 should be enough to avoid 815
IF RootWire[fusionData, wire]#wire THEN SIGNAL InternalBug[fusionData.name];
FOR i: NAT IN [0 .. wire.size) DO
wire[i] ← RootWire[fusionData, wire[i]];
IF maxIter=0 THEN SIGNAL StructuralLoop[fusionData.name, wire];-- probably loop in the Wire structure
WireDagIfy[wire[i], maxIter-1];
ENDLOOP;
};
wire: Wire ← NARROW [key];
IF value#NIL THEN RETURN; -- we dagify only the roots
[] ← HashTable.Store[internals, wire, NIL];
IF CoreGeometry.HasPins[mode.decoration, wire]
THEN [] ← HashTable.Store[publics, wire, NIL];
WireDagIfy[wire];
};
[] ← HashTable.Pairs[fusionData.fused, InternalDagIfy];
END;
We replace in all instances wires by their roots
FOR list: LIST OF CoreClasses.CellInstance ← currentInstances, list.rest WHILE list#NIL DO
instance: CoreClasses.CellInstance ← list.first;
actual: Wire ← instance.actual;
FOR i: NAT IN [0 .. actual.size) DO
wire: Wire ← RootWire[fusionData, actual[i]];
IF ~CoreOps.CorrectConform[wire, instance.type.public[i]] THEN SIGNAL StructureMismatch[fusionData.name, wire, instance.type.public[i]];
actual[i] ← wire;
ENDLOOP;
ENDLOOP;
We build the CellType
cellType ← CoreClasses.CreateRecordCell[
public: CreateDAGWire[publics],
internal: CreateDAGWire[internals],
instances: currentInstances,
name: CDDirectory.Name[obj],
giveNames: TRUE
];
CoreGeometry.PutIR[mode.decoration, cellType, ir];
Let us recover for circular data structures
FOR i: NAT IN [0 .. fusionData.nbOfLayers) DO D2Intervals.Erase[fusionData[i]] ENDLOOP;
result ← cellType;
};
AbutFusionWire: PROC [fusionData: FusionData, dagTable: HashTable.Table, public: Wire, transformation: CD.Instance] RETURNS [actual: Wire] = {
RecordEachPin: CoreGeometry.EachInstanceProc = {
sides: CoreGeometry.Sides ← CoreGeometry.GetSides[subIr, instance];
IF (~inX AND sides[top]) OR (inX AND sides[right])
THEN thesePins ← CONS [instance, thesePins];
IF (~inX AND sides[bottom]) OR (inX AND sides[left]) THEN {
inst: CD.Instance ← CoreGeometry.Transform[transformation, instance];
iw: IWNEW [IWRec ← [instance: inst, wire: structActual]];
layers: LayerRange ← mode.instanceLayer[inst];
FOR i: NAT IN [layers.min .. layers.max] DO D2Intervals.Insert[abutData[i], iw] ENDLOOP;
};
IF CoreGeometry.TransfedNotAtEdge[transformation, nir, instance] THEN RETURN;
CoreProperties.PutWireProp[structActual, $Public, $Public];
};
prevActual: Wire ← NARROW [HashTable.Fetch[dagTable, public].value];
structActual: Wire;
abutData: AbutData ← NARROW [fusionData.data];
thesePins: LIST OF CD.Instance ← NIL; -- not transformed
mode: Mode ← fusionData.mode;
inX: BOOL ← abutData.inX;
nir: CD.Rect ← fusionData.nir;
subIr: CD.Rect ← CD.InterestRect[transformation.ob]; -- in the transformation coordonnate system
IF prevActual#NIL THEN RETURN [prevActual];
structActual ← CreateRoot[fusionData, public.size];
[] ← CoreGeometry.EnumeratePins[fusionData.mode.decoration, public, RecordEachPin];
FOR i: NAT IN [0 .. public.size) DO
structActual[i] ← AbutFusionWire[fusionData, dagTable, public[i], transformation];
ENDLOOP;
actual ← IF thesePins=NIL
THEN structActual
ELSE StructuredFusion[
fusionData, structActual,
FusionGeometry[fusionData, transformation, thesePins, FALSE]
];
[] ← HashTable.Store[dagTable, public, actual];
};
AbutFusionWires: PROC [fusionData: FusionData, public: Wire, transformation: CD.Instance] RETURNS [actual: Wire] = {
dagTable: HashTable.Table ← HashTable.Create[3];
actual ← CoreOps.CreateWires[public.size];
FOR i: NAT IN [0 .. actual.size) DO
actual[i] ← AbutFusionWire[fusionData, dagTable, public[i], transformation];
ENDLOOP;
};
ExtractAbut: PUBLIC ExtractProc = {
cellType: CellType; -- the result
publics: HashTable.Table ← HashTable.Create[3];
internals: HashTable.Table ← HashTable.Create[3];
ir: CD.Rect ← CD.InterestRect[obj];
expanded: CD.Object ← CDDirectory.Expand[obj].new;
cellPtr: CD.CellPtr ← NARROW [expanded.specificRef];
cdInstances: LIST OF CD.Instance ← cellPtr.contents;
range: D2Intervals.Rect ← RectToRect[CDBasics.Surround[CDBasics.RectAt[[0, 0], expanded.size], ir]];
fusionData: FusionData ← NEW [FusionDataRec[mode.nbOfLayers]];
abutData: AbutData ← NEW [AbutDataRec[mode.nbOfLayers]];
currentInstances: LIST OF CoreClasses.CellInstance ← NIL;
count: NAT ← 0;
fusionData.mode ← mode; fusionData.name ← CDDirectory.Name[obj]; fusionData.fused ← HashTable.Create[3]; fusionData.data ← abutData; fusionData.isAbut ← TRUE;
fusionData.nir ← IF mode.fusionByName=none
THEN CDBasics.Extend[ir, - NARROW [userData, REF INT]^] ELSE ir;
abutData.inX ← obj.class=PWObjects.abutXClass;
FOR i: NAT IN [0 .. mode.nbOfLayers) DO
abutData[i] ← D2Intervals.Create[range: range, valueRect: IWToRect, userData: mode];
ENDLOOP;
FOR i: NAT IN [0 .. mode.nbOfLayers) DO
fusionData[i] ← D2Intervals.Create[range: range, valueRect: IWToRect, userData: mode];
ENDLOOP;
FOR list: LIST OF CD.Instance ← cdInstances, list.rest WHILE list#NIL DO count ← count+1 ENDLOOP;
PW.WriteF["Extracting [%g] abut %g (size: [%g, %g], instances: %g)\n", IO.rope[mode.decoration.name], IO.rope[CDDirectory.Name[obj]], IO.int[obj.size.x], IO.int[obj.size.y], IO.int[count]];
cdInstances ← SortInstances[cdInstances]; -- we know they are already in reverse order
WHILE cdInstances#NIL DO
cdInstance: CD.Instance ← cdInstances.first;
subResult: REF; subCellType: CellType; subProps: Properties;
instance: CoreClasses.CellInstance;
[subResult, subProps] ← Extract[cdInstance.ob, mode, cdInstance.properties, userData];
subCellType ← NARROW [subResult];
instance ← CoreClasses.CreateInstance[
actual: AbutFusionWires[fusionData, subCellType.public, cdInstance],
type: subCellType, props: subProps
];
IF ~CoreOps.CorrectConform[instance.actual, subCellType.public] THEN SIGNAL StructureMismatch[fusionData.name, instance.actual, subCellType.public];
FOR i: NAT IN [0 .. mode.nbOfLayers) DO
table: D2Intervals.Table ← fusionData[i];
fusionData[i] ← abutData[i]; abutData[i] ← table; D2Intervals.Erase[table];
ENDLOOP;
CoreGeometry.PutTransf[mode.decoration, instance, cdInstance];
currentInstances ← CONS [instance, currentInstances];
cdInstances ← cdInstances.rest;
ENDLOOP;
We replace all components of structured wires by their roots. We compute internals and publics on the way!
BEGIN
InternalDagIfy: HashTable.EachPairAction = {
WireDagIfy: PROC [wire: Wire, maxIter: NAT ← 32] = {
32 should be enough to avoid 815
IF maxIter=0 THEN SIGNAL StructuralLoop[fusionData.name, wire]; -- probably loop in the Wire structure
IF RootWire[fusionData, wire]#wire THEN SIGNAL InternalBug[fusionData.name];
FOR i: NAT IN [0 .. wire.size) DO
wire[i] ← RootWire[fusionData, wire[i]];
WireDagIfy[wire[i], maxIter-1];
ENDLOOP;
};
wire: Wire ← NARROW [key];
IF value#NIL THEN RETURN; -- we dagify only the roots
[] ← HashTable.Store[internals, wire, NIL];
IF CoreProperties.GetWireProp[wire, $Public]#NIL
THEN [] ← HashTable.Store[publics, wire, NIL];
WireDagIfy[wire];
};
[] ← HashTable.Pairs[fusionData.fused, InternalDagIfy];
END;
We replace in all instances wires by their roots
FOR list: LIST OF CoreClasses.CellInstance ← currentInstances, list.rest WHILE list#NIL DO
instance: CoreClasses.CellInstance ← list.first;
actual: Wire ← instance.actual;
FOR i: NAT IN [0 .. actual.size) DO
wire: Wire ← RootWire[fusionData, actual[i]];
IF ~CoreOps.CorrectConform[wire, instance.type.public[i]] THEN SIGNAL StructureMismatch[fusionData.name, wire, instance.type.public[i]];
actual[i] ← wire;
ENDLOOP;
ENDLOOP;
We build the CellType
cellType ← CoreClasses.CreateRecordCell[
public: CreateDAGWire[publics],
internal: CreateDAGWire[internals],
instances: currentInstances,
name: CDDirectory.Name[expanded],
giveNames: TRUE
];
CoreGeometry.PutIR[mode.decoration, cellType, ir];
Enable the retrieval of pins
BEGIN
SetLazyPins: PROC [wire: Wire] = {
CoreGeometry.PutRecordLazyPins[mode.decoration, wire, cellType];
};
ClearPublic: CoreOps.EachWireProc = {
CoreProperties.PutWireProp[wire, $Public, NIL];
};
CoreOps.VisitRootAtomics[cellType.public, SetLazyPins];
[] ← CoreOps.VisitWireSeq[cellType.public, ClearPublic];
END;
result ← cellType;
Let us recover for circular data structures
FOR i: NAT IN [0 .. fusionData.nbOfLayers) DO D2Intervals.Erase[fusionData[i]] ENDLOOP;
FOR i: NAT IN [0 .. fusionData.nbOfLayers) DO D2Intervals.Erase[abutData[i]] ENDLOOP;
};
ExtractExpand: PUBLIC ExtractProc = {
newObj: CD.Object ← CDDirectory.Expand[obj, NIL, NIL].new;
PW.WriteF["Expanding [%g] object %g of class %g\n", IO.rope[mode.decoration.name], IO.rope[CDDirectory.Name[obj]], IO.atom[obj.class.objectType]];
IF newObj=NIL THEN SIGNAL CallerBug[]; -- no expand proc found for this object that we do not know how to extract!
RETURN Extract[newObj, mode, CDProperties.DAppendProps[obj.properties, properties], userData];
};
NameFromSatellites: PROC [obj: Object, properties: CD.PropList] RETURNS [name: ROPENIL] = {
objName: ROPE = CDDirectory.Name[obj];
name ← NARROW [CDProperties.GetListProp[properties, $InstanceName]];
FOR ropes: ROPESNARROW [CDProperties.GetListProp[properties, satellitesProp]], ropes.rest WHILE ropes#NIL DO
IF name#NIL AND NOT Rope.Equal[ropes.first, name] THEN SIGNAL FusionPropMismatch[objName, CoreOps.nameProp, name, ropes.first];
name ← ropes.first;
ENDLOOP;
FOR ropes: ROPESNARROW [CDProperties.GetObjectProp[obj, satellitesProp]], ropes.rest WHILE ropes#NIL DO
IF name#NIL AND NOT Rope.Equal[ropes.first, name] THEN SIGNAL FusionPropMismatch[objName, CoreOps.nameProp, name, ropes.first];
name ← ropes.first;
ENDLOOP;
};
ExtractRect: PUBLIC ExtractProc = {
wire: Wire;
IF obj.layer=CD.shadeLayer OR obj.layer=CD.errorLayer OR obj.layer=CD.backgroundLayer OR obj.layer=CD.outlineLayer OR obj.layer=CD.selectionLayer OR obj.layer=CD.commentLayer THEN RETURN [NIL];
wire ← CoreOps.CreateWire[name: NameFromSatellites[obj, properties]];
CoreGeometry.PutPin[mode.decoration, wire, CDInstances.NewInst[obj]];
result ← wire;
};
ExtractPin: PUBLIC ExtractProc = {
wire: Wire;
instance: CD.Instance ← CDInstances.NewInst[ob: obj, properties: properties];
pinName: ROPE ← CDSymbolicObjects.GetName[instance];
IF NameFromSatellites[obj, properties]#NIL THEN SIGNAL CallerBug[];
wire ← CoreOps.CreateWire[name: pinName];
CoreGeometry.PutPin[mode.decoration, wire, instance];
result ← wire;
};
ExtractAtomic: PUBLIC ExtractProc = {
wire: Wire;
wire ← CoreOps.CreateWire[name: NameFromSatellites[obj, properties]];
CoreGeometry.PutPin[mode.decoration, wire, CDInstances.NewInst[obj]];
result ← wire;
};
ExtractNull: PUBLIC ExtractProc = {result ← NIL};
Extraction mode procs
DefaultInstanceLayer: PUBLIC PROC [CD.Instance] RETURNS [LayerRange] = {RETURN [[0, 0]]};
AlwaysTrue: PUBLIC PROC [obj: CD.Object, properties1: CD.PropList, userData1: REF, properties2: CD.PropList, userData2: REF] RETURNS [BOOLTRUE] = {};
AlwaysFalse: PUBLIC PROC [obj: CD.Object, properties1: CD.PropList, userData1: REF, properties2: CD.PropList, userData2: REF] RETURNS [BOOLFALSE] = {};
CompareProps: PUBLIC PROC [obj: CD.Object, properties1: CD.PropList, userData1: REF, properties2: CD.PropList, userData2: REF] RETURNS [BOOL] = {
FOR p1: CD.PropList ← properties1, p1.rest WHILE p1#NIL DO
IF CDProperties.GetListProp[properties2, p1.first.key]#p1.first.val THEN RETURN [FALSE];
ENDLOOP;
FOR p2: CD.PropList ← properties2, p2.rest WHILE p2#NIL DO
IF CDProperties.GetListProp[properties1, p2.first.key]#p2.first.val THEN RETURN [FALSE];
ENDLOOP;
RETURN [TRUE];
};
satellitesProp: PUBLIC ATOMPW.RegisterProp[$SinixSatellites];
Initialization
RegisterExtractProc[$ExtractCell, ExtractCell];
RegisterExtractProc[$ExtractAbut, ExtractAbut];
RegisterExtractProc[$ExtractExpand, ExtractExpand];
RegisterExtractProc[$ExtractRect, ExtractRect];
RegisterExtractProc[$ExtractPin, ExtractPin];
RegisterExtractProc[$ExtractAtomic, ExtractAtomic];
RegisterExtractProc[$ExtractNull, ExtractNull];
END.