DIRECTORY
CD, CDAtomicObjects, CDBasics, CDCells, CDDirectory, CDInstances, CDLayers, CDOrient, CDProperties, CDRects, CDSymbolicObjects,
CedarProcess,
Core, CoreClasses, CoreOps, CoreProperties,
D2Intervals, HashTable, IO, Process,
PW, PWObjects, PWPins,
RedBlackTree, Rope,
Sinix;
SinixImpl:
CEDAR
PROGRAM
IMPORTS CD, CDBasics, CDCells, CDDirectory, CDInstances, CDLayers, CDOrient, CDProperties, CDRects, CDSymbolicObjects, CedarProcess, CoreClasses, CoreOps, CoreProperties, D2Intervals, HashTable, IO, Process, PW, PWObjects, PWPins, RedBlackTree, Rope
EXPORTS Sinix
SHARES CDCells, CDRects =
BEGIN OPEN Sinix;
Operations for an easy management of properties
AppendPinsProp:
PUBLIC
PROC [mode: Mode, wire: Wire, pins:
LIST
OF
CD.Instance] = {
WHILE pins#NIL DO AddPinsProp[mode, wire, pins.first]; pins ← pins.rest ENDLOOP;
};
AddPinsProp:
PUBLIC
PROC [mode: Mode, wire: Wire, pin:
CD.Instance] = {
PutPinsProp[mode, wire, CONS [pin, GetPinsProp[mode, wire]]];
};
PutPinsProp:
PUBLIC
PROC [mode: Mode, wire: Wire, geometry:
LIST
OF
CD.Instance] = {
CoreProperties.PutWireProp[wire, mode.pinsProp, geometry];
};
GetPinsProp:
PUBLIC
PROC [mode: Mode, wire: Wire]
RETURNS [geometry:
LIST
OF
CD.Instance] = {
value: REF ← CoreProperties.GetWireProp[wire, mode.pinsProp];
IF value=NIL THEN RETURN [NIL];
WITH value
SELECT
FROM
geom: LIST OF CD.Instance => geometry ← geom;
lazyPinsData: LazyPinsData => geometry ← lazyPinsData.getLazyPins[mode, wire, lazyPinsData.ir, lazyPinsData.data];
ENDCASE => ERROR;
};
PutLazyPinsProp:
PUBLIC
PROC [mode: Mode, wire: Wire, lazyPinsData: LazyPinsData] = {
CoreProperties.PutWireProp[wire, mode.pinsProp, lazyPinsData];
};
AppendInstancePins:
PROC [prevPins:
LIST
OF
CD.Instance, mode: Mode, wire: Wire, ir:
CD.Rect, instance: CoreClasses.CellInstance]
RETURNS [pins:
LIST
OF
CD.Instance, count:
NAT ← 0, subPublic: Wire] = {
transf: CD.Instance ← GetInstanceTransformationProp[mode, instance];
EachBind: CoreOps.EachWirePairProc = {
IF actualWire#wire THEN RETURN;
subPublic ← publicWire;
FOR subGeometry:
LIST
OF
CD.Instance ← GetPinsProp[mode, publicWire], subGeometry.rest
WHILE subGeometry#
NIL
DO
inst: CD.Instance ← Transform[transf, subGeometry.first];
IF ~CDBasics.Inside[CDBasics.Extend[CDInstances.InstRectO[inst], mode.clipDistance], CDBasics.Extend[ir, -1]] THEN {count ← count+1; pins ← CONS [inst, pins]};
ENDLOOP;
};
pins ← prevPins;
[] ← CoreOps.VisitBinding[instance.actual, instance.type.public, EachBind];
};
RecordGetLazyPins:
PUBLIC
PROC [mode: Mode, wire: Wire, ir:
CD.Rect, data:
REF]
RETURNS [pins:
LIST
OF
CD.Instance] = {
cellType: CellType ← NARROW [data];
recData: CoreClasses.RecordCellType ← NARROW [cellType.data];
countInstanceWire: NAT ← 0;
countInstances: NAT ← 0;
onlyInstance: CoreClasses.CellInstance;
onlySubPublic: Wire;
FOR i:
NAT
IN [0 .. recData.size)
DO
instance: CoreClasses.CellInstance ← recData[i];
subPublic: Wire;
count: NAT ← 0;
[pins, count, subPublic] ← AppendInstancePins[pins, mode, wire, ir, instance];
IF count#0
THEN {
countInstances ← countInstances + count;
countInstanceWire ← countInstanceWire + 1;
onlyInstance ← instance; onlySubPublic ← subPublic;
};
ENDLOOP;
IF countInstanceWire=1 THEN PutLazyPinsProp[mode, wire, NEW [LazyPinsDataRec ← [ir: ir, data: NEW [InstanceWireRec ← [transf: GetInstanceTransformationProp[mode, onlyInstance], subPublic: onlySubPublic]], getLazyPins: InstanceWireGetLazyPins]]];
IF countInstances<=2 THEN PutPinsProp[mode, wire, pins];
};
InstanceWire: TYPE = REF InstanceWireRec;
InstanceWireRec: TYPE = RECORD [transf: CD.Instance, subPublic: Wire];
InstanceWireGetLazyPins:
PROC [mode: Mode, wire: Wire, ir:
CD.Rect, data:
REF]
RETURNS [geometry:
LIST
OF
CD.Instance] = {
instanceWire: InstanceWire ← NARROW [data];
transf: CD.Instance ← instanceWire.transf;
FOR subGeometry:
LIST
OF
CD.Instance ← GetPinsProp[mode, instanceWire.subPublic], subGeometry.rest
WHILE subGeometry#
NIL
DO
inst: CD.Instance ← Transform[transf, subGeometry.first];
IF ~CDBasics.Inside[CDBasics.Extend[CDInstances.InstRectO[inst], mode.clipDistance], CDBasics.Extend[ir, -1]] THEN geometry ← CONS [inst, geometry];
ENDLOOP;
};
AppendWireGeometryProp:
PUBLIC
PROC [mode: Mode, wire: Wire, geometry:
LIST
OF
CD.Instance] = {
WHILE geometry#
NIL
DO
AddWireGeometryProp[mode, wire, geometry.first];
geometry ← geometry.rest;
ENDLOOP;
};
AddWireGeometryProp:
PUBLIC
PROC [mode: Mode, wire: Wire, cdInstance:
CD.Instance] = {
PutWireGeometryProp[mode, wire, CONS [cdInstance, GetWireGeometryProp[mode, wire]]];
};
PutWireGeometryProp:
PUBLIC
PROC [mode: Mode, wire: Wire, geometry:
LIST
OF
CD.Instance] = {
CoreProperties.PutWireProp[wire, mode.wireGeometryProp, geometry];
};
GetWireGeometryProp:
PUBLIC
PROC [mode: Mode, wire: Wire]
RETURNS [geometry:
LIST
OF
CD.Instance] = {
geometry ← NARROW [CoreProperties.GetWireProp[wire, mode.wireGeometryProp]];
};
PutInstanceTransformationProp:
PUBLIC
PROC [mode: Mode, instance: CoreClasses.CellInstance, transf:
CD.Instance] = {
CoreProperties.PutCellInstanceProp[instance, mode.instanceProp, transf];
};
GetInstanceTransformationProp:
PUBLIC
PROC [mode: Mode, instance: CoreClasses.CellInstance]
RETURNS [transf:
CD.Instance] = {
transf ← NARROW [CoreProperties.GetCellInstanceProp[instance, mode.instanceProp]];
};
Fusion data structure and internal manipulation
Signal: PUBLIC SIGNAL [cause: ATOM, data: LIST OF REF ← NIL] = CODE;
FusionData: TYPE = REF FusionDataRec;
FusionDataRec:
TYPE =
RECORD [
mode: Mode, -- to avoid passing it around
ir: CD.Rect, -- Interest Rect of obj (for finding publics)
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 RedBlackTree.Table, Association [name -> Wires] [ROPE -> LIST OF Wire]
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 [mode: Mode, cdRect:
CD.Rect]
RETURNS [rect: D2Intervals.Rect] = {
clip: INT ← mode.clipDistance;
rect ← [[cdRect.x1-clip, cdRect.x2+clip], [cdRect.y1-clip, cdRect.y2+clip]];
};
InstanceToRect:
PROC [mode: Mode, inst:
CD.Instance]
RETURNS [rect: D2Intervals.Rect] = {
rect ← RectToRect[mode, CDBasics.Surround[CDInstances.InstRectO[inst], CDInstances.InstRectI[inst]]];
};
IWToRect:
PROC [table: D2Intervals.Table, value: D2Intervals.Value]
RETURNS [rect: D2Intervals.Rect] = {
iw: IW ← NARROW [value];
mode: Mode ← NARROW [table.userData];
rect ← InstanceToRect[mode, iw.instance];
};
CreateRoot:
PROC [fusionData: FusionData, size:
NAT, props: Properties ←
NIL]
RETURNS [wire: Wire] = {
EachProperty:
PROC [prop:
ATOM, val:
REF
ANY ←
NIL] = {
newVal:
REF
ANY ←
SELECT prop
FROM
fusionData.mode.pinsProp => NIL,
fusionData.mode.wireGeometryProp => 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] = {
geometry: LIST OF CD.Instance ← GetWireGeometryProp[fusionData.mode, wire];
pins: LIST OF CD.Instance ← GetPinsProp[fusionData.mode, wire];
IF ~HashTable.Fetch[fusionData.fused, wire].found THEN Signal[$InternalBug];
IF RootWire[fusionData, wire]#wire THEN Signal[$InternalBug];
FOR is:
LIST
OF
CD.Instance ← instances, is.rest
WHILE is#
NIL
DO
inst: CD.Instance ← Transform[transformation, is.first];
IF
NOT CDBasics.Inside[CDBasics.Extend[CDInstances.InstRectO[inst], fusionData.mode.clipDistance], CDBasics.Extend[fusionData.ir, -1]]
THEN {
CoreProperties.PutWireProp[wire, $Public, $Public];
IF copyGeometry THEN pins ← CONS [inst, pins];
};
ENDLOOP;
IF copyGeometry THEN geometry ← CDInstances.AppendToList[TransformList[transformation, instances], geometry];
note that if condition true, geometry is duplicated
PutWireGeometryProp[fusionData.mode, wire, geometry];
PutPinsProp[fusionData.mode, wire, pins];
FOR is:
LIST
OF
CD.Instance ← instances, is.rest
WHILE is#
NIL
DO
inst: CD.Instance ← Transform[transformation, is.first];
iw: IW ← NEW [IWRec ← [instance: inst, wire: wire]];
layers: LayerRange ← (IF fusionData.mode.instanceLayer=NIL THEN [0, fusionData.nbOfLayers-1] ELSE fusionData.mode.instanceLayer[inst]);
FOR i: NAT IN [layers.min .. layers.max] DO D2Intervals.Insert[fusionData[i], iw] ENDLOOP;
ENDLOOP;
};
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];
};
NameWire: TYPE = REF NameWireRec;
NameWireRec: TYPE = RECORD [name: ROPE, wire: Wire];
EachNameWireProc: TYPE = PROC [name: ROPE, wire: Wire];
GetKey: RedBlackTree.GetKey = {
nameWire: NameWire ← NARROW [data];
RETURN [nameWire.name];
};
Compare: RedBlackTree.Compare = {
nameWire: NameWire ← NARROW [data];
RETURN [Rope.Compare[NARROW [k], nameWire.name]];
};
AddNameWire:
PROC [fusionData: FusionData, name:
ROPE, wire: Wire]
RETURNS [newWire: Wire] = {
nameTable: RedBlackTree.Table ← NARROW [fusionData.data];
data: RedBlackTree.UserData ← RedBlackTree.Lookup[nameTable, name];
IF data=
NIL
THEN {
RedBlackTree.Insert[nameTable, NEW [NameWireRec ← [name: name, wire: wire]], name];
newWire ← wire;
} ELSE {
nameWire: NameWire ← NARROW [data];
newWire ← StructuredFusion[fusionData, wire, nameWire.wire];
nameWire.wire ← newWire;
};
};
EnumerateIncreasing:
PROC [fusionData: FusionData, eachNameWire: EachNameWireProc] = {
nameTable: RedBlackTree.Table ← NARROW [fusionData.data];
EachNode: RedBlackTree.EachNode = {
nameWire: NameWire ← NARROW [data];
eachNameWire[nameWire.name, nameWire.wire];
};
RedBlackTree.EnumerateIncreasing[nameTable, EachNode];
};
The following PROC implements Fisher-Galler fusion, not for efficiency, but for simplicity
RootWire:
PROC [fusionData: FusionData, wire: Wire]
RETURNS [rootWire: Wire] = {
rootWire ← NARROW [HashTable.Fetch[fusionData.fused, wire].value];
IF rootWire=NIL THEN RETURN [wire];
IF rootWire=wire THEN Signal[$InternalBug];
rootWire ← RootWire[fusionData, rootWire];
[] ← HashTable.Replace[fusionData.fused, wire, rootWire];
};
PropFusionProc: TYPE = PROC [prop: ATOM, value1, value2: REF ANY] RETURNS [value: REF ANY];
NameFusion: PropFusionProc = {
rope1: ROPE ← NARROW [value1];
rope2: ROPE ← NARROW [value2];
IF rope1#
NIL
AND rope2#
NIL
AND ~Rope.Equal[rope1, rope2]
THEN {
PW.WriteF["Two wires that do not have the same name are fused: %g and %g.\n", IO.rope[rope1], IO.rope[rope2]];
Signal[$FusionPropError, LIST [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 {
PW.WriteF["Two wires that do not have the same %g property value are fused: %g and %g.\n", IO.atom[prop], IO.refAny[value1], IO.refAny[value2]];
Signal[$FusionPropError, LIST [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
FusionProperties:
PROC [fusionData: FusionData, fused, root: Wire] = {
EachProperty:
PROC [prop:
ATOM, val:
REF
ANY ←
NIL] = {
rootValue: REF ← CoreProperties.GetWireProp[root, prop];
refFusionProc: REF PropFusionProc ← NARROW [CoreProperties.GetProp[CoreProperties.FetchProperties[prop], $PropFusionProc]];
fusionProc: PropFusionProc ←
SELECT prop
FROM
fusionData.mode.pinsProp => InstancesFusion,
fusionData.mode.wireGeometryProp => InstancesFusion,
CoreOps.nameProp => NameFusion,
ENDCASE => DefaultFusion;
CoreProperties.PutWireProp[root, prop, fusionProc[prop, val, rootValue]];
};
CoreProperties.Enumerate[fused.properties, EachProperty];
fused.properties ← NIL; -- to help the GC!
};
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];
IF RootWire[fusionData, fused]#fused THEN Signal[$InternalBug];
IF ~HashTable.Fetch[fusionData.fused, root].found THEN Signal[$InternalBug];
IF RootWire[fusionData, root]#root THEN Signal[$InternalBug];
IF fused=root THEN Signal[$InternalBug]; -- should never occur
FusionProperties[fusionData, fused, root]; -- name, pins, geometry
[] ← 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[$StructureMismatch];
};
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 ← Transform[transformation, insts.first];
layers: LayerRange ← (IF fusionData.mode.instanceLayer=NIL THEN [0, fusionData.nbOfLayers-1] ELSE fusionData.mode.instanceLayer[inst]);
rect: D2Intervals.Rect ← InstanceToRect[fusionData.mode, inst];
InternalFindTouchingWires:
PROC [table: D2Intervals.Table, value: D2Intervals.Value]
RETURNS [quit:
BOOL ←
FALSE] = {
iw: IW ← NARROW [value];
instance: CD.Instance ← iw.instance;
wire: Wire ← RootWire[fusionData, iw.wire];
IF ~HashTable.Fetch[fusionData.fused, wire].found THEN Signal[$InternalBug];
IF CoreOps.Member[touchingWires, wire] THEN RETURN;
IF Touch[fusionData.mode, 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 ← CreateRoot[fusionData, public.size, IF copyProps THEN public.properties ELSE NIL];
publicName: ROPE ← CoreOps.GetShortWireName[public];
pins: LIST OF CD.Instance ← GetPinsProp[fusionData.mode, public];
IF prevActual#NIL THEN RETURN [prevActual];
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 actual ← 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];
};
IF instances=NIL THEN RETURN [NIL];
sorted ← LIST [instances.first]; instances ← instances.rest;
WHILE instances#
NIL
DO
inst: CD.Instance ← instances.first;
eval: INT ← Eval[inst];
aux: LIST OF CD.Instance ← sorted;
instances ← instances.rest;
IF eval>=Eval[sorted.first] THEN {sorted ← CONS [inst, sorted]; LOOP};
WHILE aux.rest#NIL AND eval<Eval[aux.rest.first] DO aux ← aux.rest ENDLOOP;
aux.rest ← CONS [inst, aux.rest];
ENDLOOP;
};
Extraction
cachePropertiesProp: ATOM ← PW.RegisterProp[$SinixPropertiesCache, TRUE, TRUE];
cacheUserDataProp: ATOM ← PW.RegisterProp[$SinixUserDataCache, TRUE, TRUE];
Extract:
PUBLIC ExtractProc = {
result ← CDProperties.GetObjectProp[obj, mode.cacheProp];
IF result#
NIL
AND mode.equalProc[
obj,
NARROW [CDProperties.GetObjectProp[obj, cachePropertiesProp]],
NARROW [CDProperties.GetObjectProp[obj, cacheUserDataProp]],
properties,
userData
]
THEN {
props ← NARROW [CDProperties.GetObjectProp[obj, mode.cachePropsProp]];
RETURN;
};
BEGIN
priority: CedarProcess.Priority ← CedarProcess.GetPriority[];
atom: ATOM ← NARROW [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];
CedarProcess.SetPriority[priority];
CDProperties.PutObjectProp[obj, mode.cacheProp, result];
CDProperties.PutObjectProp[obj, mode.cachePropsProp, props];
CDProperties.PutObjectProp[obj, cachePropertiesProp, properties];
CDProperties.PutObjectProp[obj, cacheUserDataProp, userData];
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[mode.name], IO.rope[CDDirectory.Name[obj]], IO.int[obj.size.x], IO.int[obj.size.y], IO.int[count]];
fusionData.mode ← mode; fusionData.ir ← ir; fusionData.fused ← HashTable.Create[3]; fusionData.data ← RedBlackTree.Create[GetKey, Compare];
FOR i:
NAT
IN [0 .. mode.nbOfLayers)
DO
fusionData[i] ← D2Intervals.Create[
range: RectToRect[mode, CDBasics.Surround[CDBasics.RectAt[[0, 0], obj.size], ir]],
valueRect: IWToRect,
userData: mode];
ENDLOOP;
WHILE cdInstances#
NIL
DO
cdInstance: CD.Instance ← cdInstances.first;
subResult: REF; subProps: Properties;
[subResult, subProps] ← Extract[cdInstance.ob, mode, cdInstance.properties, userData];
IF subResult#
NIL
THEN
WITH subResult
SELECT
FROM
subWire: Wire => {
wire: Wire ← FusionWire[fusionData, HashTable.Create[1], subWire, cdInstance, TRUE];
};
subCellType: CellType => {
bbox: CD.Rect ← CDInstances.BoundingRectO[cdInstances.rest];
instance: CoreClasses.CellInstance ← CoreClasses.CreateInstance[
actual: FusionWires[fusionData, subCellType.public, cdInstance],
type: subCellType, props: subProps
];
IF ~CoreOps.Conform[instance.actual, subCellType.public] THEN Signal[$StructureMismatch];
PutInstanceTransformationProp[mode, 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:
BOOL ←
FALSE] = {
valueInst: CD.Instance ← NARROW [value, IW].instance;
IF ~IntersectRect[mode, bbox, CDInstances.InstRectO[valueInst]] THEN D2Intervals.Delete[fusionData[i], value];
};
[] ← D2Intervals.Enumerate[fusionData[i], DeleteOutsideRange, [[FIRST[INT], bbox.x1-mode.clipDistance], D2Intervals.universe.y]];
[] ← D2Intervals.Enumerate[fusionData[i], DeleteOutsideRange, [[bbox.x2+mode.clipDistance, LAST[INT]], D2Intervals.universe.y]];
[] ← D2Intervals.Enumerate[fusionData[i], DeleteOutsideRange, [D2Intervals.universe.x, [FIRST[INT], bbox.y1-mode.clipDistance]]];
[] ← D2Intervals.Enumerate[fusionData[i], DeleteOutsideRange, [D2Intervals.universe.x, [bbox.y2+mode.clipDistance, LAST[INT]]]];
ENDLOOP;
};
ENDCASE => Signal[$InternalBug];
cdInstances ← cdInstances.rest;
ENDLOOP;
Time to do fusion by name!
BEGIN
EachNameWire: EachNameWireProc = {
AppendMatchingWires: HashTable.EachPairAction = {
wire: Wire ← NARROW [key];
wire ← RootWire[fusionData, wire];
IF ~Rope.Equal[CoreOps.GetShortWireName[wire], base] THEN RETURN;
IF ~CoreOps.Member[matchingWires, wire] THEN matchingWires ← CONS [wire, matchingWires];
};
base: ROPE ← name; components: LIST OF ROPE ← NIL;
matchingWires: LIST OF Wire ← NIL;
matchingWire: Wire;
wire ← RootWire[fusionData, wire];
IF ~mode.flatNameSpace THEN [base, components] ← CoreOps.ParseWireName[name];
[] ← HashTable.Pairs[fusionData.fused, AppendMatchingWires];
matchingWire ← IF matchingWires=NIL THEN NIL ELSE matchingWires.first;
IF matchingWires#NIL AND matchingWires.rest#NIL THEN Signal[$InternalBug]; -- at a given time 2 wires should not have the same name
IF components=
NIL
THEN {
previousWireName: ROPE ← CoreOps.GetShortWireName[wire];
IF previousWireName#
NIL
AND ~Rope.Equal[name, previousWireName]
THEN {
cell: CD.Object ← Cellize[obj];
HighLightWire[fusionData, cell, wire];
PW.WriteF["*** Sinix: fusing two wires having name %g and %g\n", IO.rope[name], IO.rope[previousWireName]];
[] ← PW.Draw[cell];
Signal[$FusionPropError, LIST [CoreOps.nameProp, name, previousWireName]]; -- wire has 2 names
};
[] ← CoreOps.SetShortWireName[wire, name];
IF matchingWire#NIL THEN [] ← StructuredFusion[fusionData, matchingWire, wire];
} ELSE {
WHILE components#
NIL
DO
subWires: LIST OF Wire ← NIL;
IF matchingWire=NIL THEN Signal[$FusionByNameError, LIST [name]]; -- path name do not correspond to any wire
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 subWires ← CONS [matchingWire[i], subWires];
ENDLOOP;
IF subWires=NIL THEN Signal[$FusionByNameError, LIST [name]]; -- currentWire does not have the component with name fieldName
IF subWires.rest#NIL THEN Signal[$FusionByNameError, LIST [name]]; -- currentWire has 2 or more components with name fieldName
matchingWire ← RootWire[fusionData, subWires.first];
components ← components.rest;
ENDLOOP;
[] ← StructuredFusion[fusionData, matchingWire, wire];
};
};
EnumerateIncreasing[fusionData, EachNameWire];
END;
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]; -- probably loop in the Wire structure
IF RootWire[fusionData, wire]#wire THEN Signal[$InternalBug];
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.Conform[wire, instance.type.public[i]] THEN Signal[$StructureMismatch]; -- probably because of structure
actual[i] ← wire;
ENDLOOP;
ENDLOOP;
We build the CellType
cellType ← CoreClasses.CreateRecordCell[
public: CreateDAGWire[publics],
internal: CreateDAGWire[internals],
instances: currentInstances,
name: CDDirectory.Name[obj]
];
Enable the retrieval of pins
BEGIN
lazyPinsData: LazyPinsData ← NEW [LazyPinsDataRec ← [ir: ir, getLazyPins: RecordGetLazyPins, data: cellType]];
SetLazyPins: CoreOps.EachWireProc = {
pins: LIST OF CD.Instance ← GetPinsProp[mode, wire];
IF pins=NIL THEN {PutLazyPinsProp[mode, wire, lazyPinsData]; RETURN};
FOR insts:
LIST
OF CoreClasses.CellInstance ← currentInstances, insts.rest
WHILE insts#
NIL
DO
pins ← AppendInstancePins[pins, mode, wire, ir, insts.first].pins;
ENDLOOP;
PutPinsProp[mode, wire, pins];
};
ClearPublic: CoreOps.EachWireProc = {
CoreProperties.PutWireProp[wire, $Public, NIL];
};
[] ← CoreOps.VisitWire[cellType.public, SetLazyPins];
[] ← CoreOps.VisitWire[cellType.public, ClearPublic];
END;
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] = {
prevActual: Wire ← NARROW [HashTable.Fetch[dagTable, public].value];
structActual: Wire ← CreateRoot[fusionData, public.size];
abutData: AbutData ← NARROW [fusionData.data];
thesePins: LIST OF CD.Instance ← NIL; -- not transformed
nextPins: LIST OF CD.Instance ← NIL; -- not transformed
mode: Mode ← fusionData.mode;
inX: BOOL ← abutData.inX;
ir: CD.Rect ← fusionData.ir;
IF prevActual#NIL THEN RETURN [prevActual];
FOR pins:
LIST
OF
CD.Instance ← GetPinsProp[fusionData.mode, public], pins.rest
WHILE pins#
NIL
DO
pin: CD.Instance ← pins.first;
pinRect: CD.Rect ← CDInstances.InstRectO[pins.first]; -- in the transformation coordonnate system
subIr: CD.Rect ← CD.InterestRect[transformation.ob]; -- in the transformation coordonnate system
top: BOOL ← IntersectRect[mode, pinRect, [subIr.x1, subIr.y2-1, subIr.x2, subIr.y2]];
left: BOOL ← IntersectRect[mode, pinRect, [subIr.x1, subIr.y1, subIr.x1+1, subIr.y2]];
bottom: BOOL ← IntersectRect[mode, pinRect, [subIr.x1, subIr.y1, subIr.x2, subIr.y1+1]];
right: BOOL ← IntersectRect[mode, pinRect, [subIr.x2-1, subIr.y1, subIr.x2, subIr.y2]];
IF (~inX AND top) OR (inX AND right) THEN thesePins ← CONS [pin, thesePins];
IF (~inX AND bottom) OR (inX AND left) THEN nextPins ← CONS [pin, nextPins];
IF ~CDBasics.Inside[CDBasics.Extend[CDInstances.InstRectO[Transform[transformation, pin]], mode.clipDistance], CDBasics.Extend[ir, -1]] THEN CoreProperties.PutWireProp[structActual, $Public, $Public];
ENDLOOP;
FOR is:
LIST
OF
CD.Instance ← nextPins, is.rest
WHILE is#
NIL
DO
inst: CD.Instance ← Transform[transformation, is.first];
iw: IW ← NEW [IWRec ← [instance: inst, wire: structActual]];
layers: LayerRange ← (IF mode.instanceLayer=NIL THEN [0, fusionData.nbOfLayers-1] ELSE mode.instanceLayer[inst]);
FOR i: NAT IN [layers.min .. layers.max] DO D2Intervals.Insert[abutData[i], iw] ENDLOOP;
ENDLOOP;
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[mode, 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.ir ← ir; fusionData.fused ← HashTable.Create[3]; fusionData.data ← abutData;
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.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.Conform[instance.actual, subCellType.public] THEN Signal[$StructureMismatch];
FOR i:
NAT
IN [0 .. mode.nbOfLayers)
DO
table: D2Intervals.Table ← fusionData[i];
fusionData[i] ← abutData[i]; abutData[i] ← table; D2Intervals.Erase[table];
ENDLOOP;
PutInstanceTransformationProp[mode, 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]; -- probably loop in the Wire structure
IF RootWire[fusionData, wire]#wire THEN Signal[$InternalBug];
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.Conform[wire, instance.type.public[i]] THEN Signal[$StructureMismatch]; -- probably because of structure
actual[i] ← wire;
ENDLOOP;
ENDLOOP;
We build the CellType
cellType ← CoreClasses.CreateRecordCell[
public: CreateDAGWire[publics],
internal: CreateDAGWire[internals],
instances: currentInstances,
name: CDDirectory.Name[expanded]
];
Enable the retrieval of pins
BEGIN
lazyPinsData: LazyPinsData ← NEW [LazyPinsDataRec ← [ir: ir, getLazyPins: RecordGetLazyPins, data: cellType]];
SetLazyPins: CoreOps.EachWireProc = {
PutLazyPinsProp[mode, wire, lazyPinsData];
};
ClearPublic: CoreOps.EachWireProc = {
CoreProperties.PutWireProp[wire, $Public, NIL];
};
[] ← CoreOps.VisitWire[cellType.public, SetLazyPins];
[] ← CoreOps.VisitWire[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.name], IO.rope[CDDirectory.Name[obj]], IO.atom[obj.class.objectType]];
IF newObj=NIL THEN Signal[$CallerBug]; -- no expand proc found for this object!
RETURN Extract[newObj, mode, CDProperties.DAppendProps[obj.properties, properties], userData];
};
ExtractWire:
PUBLIC ExtractProc = {
wire: Wire;
wire ← CoreOps.CreateWire[];
AddPinsProp[mode, wire, PWPins.NewInstance[obj]];
result ← wire;
};
ExtractPin:
PUBLIC ExtractProc = {
wire: Wire;
instance: CD.Instance ← PWPins.NewInstance[ob: obj, properties: properties];
pinName: ROPE ← CDSymbolicObjects.GetName[instance];
wire ← CoreOps.CreateWire[name: pinName];
AddPinsProp[mode, wire, instance];
result ← wire;
};
ExtractNull: PUBLIC ExtractProc = {result ← NIL};
ExtractError:
PUBLIC ExtractProc = {
PW.WriteF["*** This object should not be extracted\n"]; Signal[$CallerBug];
};
Geometry Primitives
touchProcProp: PUBLIC ATOM ← PW.RegisterProp[$TouchProc, TRUE];
Touch:
PUBLIC TouchProc = {
refProc: REF TouchProc;
IF ~IntersectRect[mode, CDInstances.InstRectO[instance1], CDInstances.InstRectO[instance2]] THEN RETURN;
refProc ← NARROW [CDProperties.GetInstanceProp[instance1, touchProcProp]];
IF refProc=NIL THEN refProc ← NARROW [CDProperties.GetObjectProp[instance1.ob, touchProcProp]];
IF refProc=NIL THEN refProc ← NARROW [CDProperties.GetProp[instance1.ob.class, touchProcProp]];
yes ← (IF refProc=NIL THEN TouchExpand ELSE refProc^)[mode, instance1, instance2];
};
TouchExpand:
PUBLIC TouchProc = {
instance1 ← PWPins.NewInstance[
ob: CDDirectory.Expand[instance1.ob, NIL, NIL].new,
location: instance1.location, orientation: instance1.orientation
];
IF instance1.ob=NIL THEN Signal[$CallerBug];
RETURN [Touch[mode, instance1, instance2]];
};
TouchPin:
PUBLIC TouchProc = {
RETURN [TouchRectObject[mode, instance2, CDInstances.InstRectO[instance1], CDSymbolicObjects.GetLayer[instance1]]];
};
TouchRect:
PUBLIC TouchProc = {
RETURN [TouchRectObject[mode, instance2, CDInstances.InstRectO[instance1], instance1.ob.layer]];
};
TouchCell:
PUBLIC TouchProc = {
cellPtr: CD.CellPtr ← NARROW [instance1.ob.specificRef];
RETURN [TouchList[mode, TransformList[instance1, cellPtr.contents], instance2]];
};
TouchAtomic:
PUBLIC TouchProc = {
FOR rList: CDAtomicObjects.DrawList ←
NARROW [instance1.ob.specificRef, CDAtomicObjects.AtomicObsPtr].rList, rList.rest
WHILE rList#
NIL
DO
IF TouchRectObject[mode, instance2, CDOrient.MapRect[rList.first.r, instance1.ob.size, instance1.orientation, instance1.location], rList.first.lev] THEN RETURN [TRUE];
ENDLOOP;
};
Initialization
RegisterExtractProc[$ExtractCell, ExtractCell];
RegisterExtractProc[$ExtractAbut, ExtractAbut];
RegisterExtractProc[$ExtractExpand, ExtractExpand];
RegisterExtractProc[$ExtractWire, ExtractWire];
RegisterExtractProc[$ExtractPin, ExtractPin];
RegisterExtractProc[$ExtractNull, ExtractNull];
RegisterExtractProc[$ExtractError, ExtractError];
END.