DIRECTORY CD, CDBasics, CDCells, CDDirectory, CDInstances, CDOrient, CDProperties, CDSymbolicObjects, Commander, Core, CoreClasses, CoreCreate, CoreFlat, CoreOps, CoreProperties, HashTable, IO, ProcessProps, PW, PWCore, PWObjects, Rope, Sinix, SinixCMos, CoreStructuralComparison; PWCoreImpl: CEDAR PROGRAM IMPORTS CD, CDBasics, CDCells, CDDirectory, CDInstances, CDProperties, CDSymbolicObjects, CoreClasses, CoreCreate, CoreFlat, CoreOps, CoreProperties, HashTable, IO, ProcessProps, PW, PWObjects, Sinix, SinixCMos, CoreStructuralComparison EXPORTS PWCore SHARES CDCells, CDDirectory = BEGIN OPEN PWCore; Signal: PUBLIC SIGNAL = CODE; layoutAtomsTable: HashTable.Table _ HashTable.Create[]; LayoutAtomData: TYPE = REF LayoutAtomDataRec; LayoutAtomDataRec: TYPE = RECORD [layoutProc: LayoutProc, decorateProc: DecorateProc]; RegisterLayoutAtom: PUBLIC PROC [layoutAtom: ATOM, layoutProc: LayoutProc, decorateProc: DecorateProc] RETURNS [sameAtom: ATOM] = { sameAtom _ layoutAtom; IF ~HashTable.Store[layoutAtomsTable, layoutAtom, NEW [LayoutAtomDataRec _ [layoutProc: layoutProc, decorateProc: decorateProc]]] THEN PW.WriteF["LayoutProc and DecorateProc for %g overwritten\n", IO.atom[layoutAtom]]; }; testLichen: PUBLIC BOOL _ FALSE; checkAbuts: PUBLIC BOOL _ FALSE; extractMode: PUBLIC Sinix.Mode _ SinixCMos.extractBMode; maskSuffix: PUBLIC ROPE _ ".mask"; layoutAtomProp: PUBLIC ATOM _ CoreProperties.RegisterProperty[$Layout, CoreProperties.Props[[CoreProperties.propCopy, CoreProperties.PropDoCopy]]]; SetLayout: PUBLIC PROC [cellType: CellType, layoutAtom: ATOM, userDataProp: ATOM _ NIL, userData: REF _ NIL] = { CoreProperties.PutCellTypeProp[cellType, layoutAtomProp, layoutAtom]; IF userDataProp#NIL THEN CoreProperties.PutCellTypeProp[cellType, userDataProp, userData]; }; layoutProp: PRIVATE ATOM _ CoreProperties.RegisterProperty[$PWCoreLayout]; -- cache on CellTypes HasLayout: PUBLIC PROC [cellType: CellType] RETURNS [BOOL] = { RETURN [CoreProperties.GetCellTypeProp[cellType, layoutProp]#NIL]; }; CancelLayout: PUBLIC PROC [cellType: CellType] = { CoreProperties.PutCellTypeProp[cellType, layoutProp, NIL]; }; Layout: PUBLIC PROC [cellType: CellType] RETURNS [obj: Object] = { obj _ NARROW [CoreProperties.GetCellTypeProp[cellType, layoutProp]]; IF obj#NIL THEN RETURN; BEGIN CheckPinsNonNil: PROC [public: Wire] = { IF Sinix.GetPinsProp[extractMode, public]=NIL THEN Signal[]; }; layoutAtom: ATOM _ NARROW [CoreProperties.GetCellTypeProp[cellType, layoutAtomProp]]; layoutAtomData: LayoutAtomData; IF layoutAtom=NIL THEN layoutAtom _ NARROW [CoreProperties.GetCellClassProp[cellType.class, layoutAtomProp]]; IF layoutAtom=NIL THEN { PW.WriteF["*** Impossible to find a layoutAtom for this cellType\n"]; Signal[]; }; layoutAtomData _ NARROW [HashTable.Fetch[layoutAtomsTable, layoutAtom].value]; IF layoutAtomData=NIL THEN { PW.WriteF["*** LayoutAtom %g has not been registered\n", IO.atom[layoutAtom]]; Signal[]; }; obj _ layoutAtomData.layoutProc[cellType]; layoutAtomData.decorateProc[cellType, obj]; CoreOps.VisitAtomicWires[cellType.public, CheckPinsNonNil]; obj _ PWObjects.CreateIndirect[obj]; CDProperties.PutObjectProp[obj, extractMode.cacheProp, cellType]; CoreProperties.PutCellTypeProp[cellType, layoutProp, obj]; IF CoreOps.GetCellTypeName[cellType]#NIL AND CDDirectory.Name[obj]=NIL THEN NARROW [obj.class.directoryProcs, REF CDDirectory.DirectoryProcs].setName[obj, CoreOps.GetCellTypeName[cellType]]; IF Sinix.Extract[obj, extractMode].result#cellType THEN Signal[]; -- internal Bug of PWCore END; }; GetAndAddSuffix: PROC [source: CD.Design, name: ROPE] RETURNS [obj: CD.Object] = { obj _ PW.Get[source, IO.PutR[IO.rope[name], IO.rope[maskSuffix]]]; }; DecorateCorrespondingPublic: PROC [sourcePublic, extractedPublic: Wire] RETURNS [extractedToSource: HashTable.Table] = { FindInExtractedPublic: CoreOps.EachWireProc = { xwire: Wire _ wire; -- to avoid name clash names: LIST OF ROPE _ CoreOps.GetFullWireNames[sourcePublic, wire]; name: ROPE; FindInExtractedPublicInternal: CoreOps.EachWireProc = { source: Wire; IF ~CoreOps.IsFullWireName[extractedPublic, wire, name] THEN RETURN; source _ NARROW [HashTable.Fetch[extractedToSource, wire].value]; IF source=xwire THEN RETURN; IF source#NIL THEN Signal[]; -- extracted wire corresponds to 2 source publics [] _ HashTable.Store[extractedToSource, wire, xwire]; IF wire.size=0 THEN SourcePinsFromExtracted[xwire, wire, TRUE]; }; IF names=NIL OR names.rest#NIL THEN IF xwire.size#0 THEN RETURN ELSE Signal[]; -- more than one full name name _ names.first; [] _ CoreOps.VisitWire[extractedPublic, FindInExtractedPublicInternal]; IF xwire.size=0 AND Sinix.GetPinsProp[extractMode, xwire]=NIL THEN Signal[]; -- source public not found }; extractedToSource _ HashTable.Create[extractedPublic.size]; [] _ CoreOps.VisitWire[sourcePublic, FindInExtractedPublic]; }; FromLayout: PUBLIC PROC [public: Wire, obj: CD.Object] RETURNS [cellType: CellType] = { CreateActual: PROC [extractedPublic: Wire] RETURNS [actual: Wire] = { sourceWire: Wire _ NARROW [HashTable.Fetch[extractedToSource, extractedPublic].value]; IF sourceWire#NIL THEN RETURN [sourceWire]; sourceWire _ CoreOps.CreateWires[size: extractedPublic.size]; [] _ HashTable.Store[extractedToSource, extractedPublic, sourceWire]; FOR i: NAT IN [0 .. extractedPublic.size) DO sourceWire[i] _ CreateActual[extractedPublic[i]]; ENDLOOP; RETURN [sourceWire]; }; actual: Wire; extractedToSource: HashTable.Table; extractedCellType: CellType _ NARROW [Sinix.Extract[obj, extractMode].result]; extractedToSource _ DecorateCorrespondingPublic[public, extractedCellType.public]; actual _ CreateActual[extractedCellType.public]; cellType _ CoreClasses.CreateRecordCell[public, CoreOps.CreateWire[LIST[public, actual]], LIST [CoreClasses.CreateInstance[actual: CreateActual[extractedCellType.public], type: extractedCellType]]]; CDProperties.PutObjectProp[obj, extractMode.cacheProp, cellType]; CoreProperties.PutCellTypeProp[cellType, layoutProp, obj]; }; FromLayoutWithoutPublic: PUBLIC PROC [obj: CD.Object] RETURNS [cellType: CellType] = { cellType _ NARROW [Sinix.Extract[obj, extractMode].result]; CoreProperties.PutCellTypeProp[cellType, layoutProp, obj]; }; IndirectGetPins: PROC [mode: Sinix.Mode, wire: Wire, ir: CD.Rect, data: REF] RETURNS [pins: LIST OF CD.Instance _ NIL] = { pins _ Sinix.GetPinsProp[mode, NARROW [data]]; }; SourcePinsFromExtracted: PROC [source, extracted: Wire, checkPins: BOOL] = { pins: LIST OF CD.Instance _ Sinix.GetPinsProp[extractMode, extracted]; IF pins=NIL THEN IF checkPins THEN Signal[] ELSE RETURN; IF Sinix.GetPinsProp[extractMode, source]=NIL THEN Sinix.PutLazyPinsProp[extractMode, source, NEW [Sinix.LazyPinsDataRec _ [ir: CDBasics.empty, data: extracted, getLazyPins: IndirectGetPins]]] -- anything would do for ir, since it is not checked by IndirectGetPins ELSE Sinix.AppendPinsProp[extractMode, source, pins]; }; Get: PUBLIC LayoutProc = { source: CD.Design _ NARROW [CoreProperties.GetCellTypeProp[cellType, $PWCoreSourceDesign]]; obj _ GetAndAddSuffix[source, CoreOps.GetCellTypeName[cellType]]; }; GetAndFlatten: PUBLIC LayoutProc = { source: CD.Design _ NARROW [CoreProperties.GetCellTypeProp[cellType, $PWCoreSourceDesign]]; obj _ PW.Flatten[GetAndAddSuffix[source, CoreOps.GetCellTypeName[cellType]]]; }; AbutX: PUBLIC LayoutProc = { data: CoreClasses.RecordCellType _ NARROW [cellType.data]; subObjects: LIST OF CD.Object _ NIL; FOR i: NAT DECREASING IN [0 .. data.size) DO subObjects _ CONS [Layout[data[i].type], subObjects]; ENDLOOP; obj _ PW.AbutListX[subObjects]; }; AbutY: PUBLIC LayoutProc = { data: CoreClasses.RecordCellType _ NARROW [cellType.data]; subObjects: LIST OF CD.Object _ NIL; FOR i: NAT DECREASING IN [0 .. data.size) DO subObjects _ CONS [Layout[data[i].type], subObjects]; ENDLOOP; obj _ PW.AbutListY[subObjects]; }; ReverseAbutX: PUBLIC LayoutProc = { data: CoreClasses.RecordCellType _ NARROW [cellType.data]; subObjects: LIST OF CD.Object _ NIL; FOR i: NAT IN [0 .. data.size) DO subObjects _ CONS [Layout[data[i].type], subObjects]; ENDLOOP; obj _ PW.AbutListX[subObjects]; }; ReverseAbutY: PUBLIC LayoutProc = { data: CoreClasses.RecordCellType _ NARROW [cellType.data]; subObjects: LIST OF CD.Object _ NIL; FOR i: NAT IN [0 .. data.size) DO subObjects _ CONS [Layout[data[i].type], subObjects]; ENDLOOP; obj _ PW.AbutListY[subObjects]; }; ArrayX: PUBLIC LayoutProc = { recasted: CellType _ CoreOps.Recast[cellType]; SetLayout[recasted, $AbutX]; obj _ Layout[recasted]; }; ArrayY: PUBLIC LayoutProc = { recasted: CellType _ CoreOps.Recast[cellType]; SetLayout[recasted, $AbutY]; obj _ Layout[recasted]; }; ReverseArrayX: PUBLIC LayoutProc = { recasted: CellType _ CoreOps.Recast[cellType]; SetLayout[recasted, $ReverseAbutX]; obj _ Layout[recasted]; }; ReverseArrayY: PUBLIC LayoutProc = { recasted: CellType _ CoreOps.Recast[cellType]; SetLayout[recasted, $ReverseAbutY]; obj _ Layout[recasted]; }; FlipX: PUBLIC LayoutProc = { IF cellType.class#CoreClasses.identityCellClass THEN Signal[]; obj _ PW.FlipX[Layout[NARROW [cellType.data]]]; }; FlipY: PUBLIC LayoutProc = { IF cellType.class#CoreClasses.identityCellClass THEN Signal[]; obj _ PW.FlipY[Layout[NARROW [cellType.data]]]; }; Rot90: PUBLIC LayoutProc = { IF cellType.class#CoreClasses.identityCellClass THEN Signal[]; obj _ PW.Rot90[Layout[NARROW [cellType.data]]]; }; Rot180: PUBLIC LayoutProc = { IF cellType.class#CoreClasses.identityCellClass THEN Signal[]; obj _ PW.Rot180[Layout[NARROW [cellType.data]]]; }; Rot270: PUBLIC LayoutProc = { IF cellType.class#CoreClasses.identityCellClass THEN Signal[]; obj _ PW.Rot270[Layout[NARROW [cellType.data]]]; }; Recast: LayoutProc = { obj _ Layout[CoreOps.Recast[cellType]]; }; StructureWarnings: TYPE = RECORD [ kind: CoreStructuralComparison.WarningKind, fromA, fromB: LIST OF REF]; FuseTransistors: PROC [recordCell: CellType] RETURNS [new: CellType] = { oldData, newData, data: CoreClasses.RecordCellType; nb, fused: NAT _ 0; IF recordCell.class#CoreClasses.recordCellClass THEN RETURN [recordCell]; oldData _ NARROW [recordCell.data]; data _ NEW [CoreClasses.RecordCellTypeRec[oldData.size]]; data.internal _ oldData.internal; FOR i: NAT IN [0 .. data.size) DO data[i] _ oldData[i] ENDLOOP; FOR i: NAT IN [0 .. data.size) DO IF data[i]=NIL THEN LOOP; data[i].type _ FuseTransistors[data[i].type]; IF data[i].type.class#CoreClasses.transistorCellClass THEN LOOP; FOR j: NAT IN [0 .. i) DO IF data[j]=NIL THEN LOOP; IF data[j].type.class#CoreClasses.transistorCellClass THEN LOOP; IF data[i].actual[0]=data[j].actual[0] AND ((data[i].actual[1]=data[j].actual[1] AND data[i].actual[2]=data[j].actual[2]) OR (data[i].actual[2]=data[j].actual[1] AND data[i].actual[1]=data[j].actual[2])) THEN { t1: CoreClasses.Transistor _ NARROW [data[i].type.data]; t2: CoreClasses.Transistor _ NARROW [data[j].type.data]; IF t1.type=t2.type AND t1.length=t2.length THEN { data[i] _ CoreClasses.CreateInstance[ actual: data[i].actual, type: CoreClasses.CreateTransistor[ args: [type: t1.type, length: t1.length, width: t1.width + t2.width], props: data[i].type.properties ], props: data[i].properties ]; data[j] _ NIL; fused _ fused + 1; }; }; ENDLOOP; ENDLOOP; IF fused=0 THEN RETURN [recordCell]; PW.WriteF["*** Lichen for PWCore.Get: fused %g transistors.\n", IO.int[fused]]; new _ CoreOps.CreateCellType[CoreClasses.recordCellClass, recordCell.public, NEW [CoreClasses.RecordCellTypeRec[data.size-fused]], NIL, recordCell.properties]; newData _ NARROW [new.data]; newData.internal _ data.internal; FOR i: NAT IN [0 .. data.size) DO IF data[i]=NIL THEN LOOP; newData[nb] _ data[i]; nb _ nb + 1; ENDLOOP; }; DecorateGet: PUBLIC DecorateProc = { extractedCT: CellType _ NARROW [Sinix.Extract[obj, extractMode].result]; InsertHint: PROC [key, value: REF ANY] RETURNS [quit: BOOL _ FALSE] = { sourceWire: CoreFlat.FlatWire _ NEW [CoreFlat.FlatWireRec _ [path: CoreFlat.NullPath, wire: NARROW [value]]]; extractedWire: CoreFlat.FlatWire _ NEW [CoreFlat.FlatWireRec _ [path: CoreFlat.NullPath, wire: NARROW [key]]]; IF NOT HashTable.Insert[table: wireHints, key: sourceWire, value: extractedWire] THEN ERROR; }; extractedToSource: HashTable.Table _ DecorateCorrespondingPublic[cellType.public, extractedCT.public]; wireHints: HashTable.Table _ HashTable.Create[]; warnings: LIST OF StructureWarnings _ NIL; IF testLichen THEN { [] _ HashTable.Pairs[table: extractedToSource, action: InsertHint]; extractedCT _ FuseTransistors[extractedCT]; IF ~CoreStructuralComparison.FlattenAndCompare[ctA: cellType, ctB: extractedCT, wireHints: wireHints, instanceHints: HashTable.Create[] ! CoreStructuralComparison.Warning => {warnings _ CONS[[kind, fromA, fromB], warnings]; RESUME}] THEN {PrintWarnings[cellType, extractedCT, warnings, TerminalIO.TOS[]]; Signal[]}; }; }; PrintWarnings: PROC [cellType: Core.CellType, extractedCT: Core.CellType, warnings: LIST OF StructureWarnings, out: Core.STREAM _ NIL] = { PrintDescendants: PROC [descendants: LIST OF REF, root: Core.CellType] = { FOR descendants _ descendants, descendants.rest UNTIL descendants=NIL DO WITH descendants.first SELECT FROM in: CoreFlat.FlatInstance => { IO.PutRope[out, CoreFlat.InstancePathRope[root, in^]]; IO.PutRope[out, "/"]; IO.PutRope[out, "?"]; }; wire: CoreFlat.FlatWire => { IO.PutRope[out, CoreFlat.WirePathRope[root, wire^]]; }; ENDCASE => ERROR; IO.PutRope[out, " "]; ENDLOOP; }; IF out=NIL THEN out _ NARROW[ProcessProps.GetProp[$CommanderHandle], Commander.Handle].out; FOR warns: LIST OF StructureWarnings _ warnings, warns.rest UNTIL warns=NIL DO warning: StructureWarnings _ warns.first; IO.PutRope[out, IF warning.kind=stuck THEN "stuck " ELSE "difference "]; IO.PutRope[out, "\n fromA: "]; PrintDescendants[warning.fromA, cellType]; IO.PutRope[out, "\n fromB: "]; PrintDescendants[warning.fromB, extractedCT]; IO.PutRope[out, "\n"]; ENDLOOP; }; EqualCellType: PROC [cellType1, cellType2: CellType] RETURNS [equal: BOOL] = { equal _ SELECT TRUE FROM cellType1=cellType2 => TRUE, cellType1.class=CoreClasses.identityCellClass => EqualCellType[NARROW [cellType1.data], cellType2], cellType2.class=CoreClasses.identityCellClass => EqualCellType[cellType1, NARROW [cellType2.data]], ENDCASE => FALSE; }; UncheckedDecorateAbut: PROC [cellType: CellType, obj: CD.Object, inOrder: OrderProc] = { lazyPinsData: Sinix.LazyPinsData _ NEW [Sinix.LazyPinsDataRec _ [ir: CD.InterestRect[obj], getLazyPins: Sinix.RecordGetLazyPins, data: cellType]]; InsertInOrder: PROC [element: CD.Instance, sorted: LIST OF CD.Instance] RETURNS [new: LIST OF CD.Instance] = { IF sorted=NIL THEN RETURN [LIST [element]]; IF inOrder[CDBasics.BaseOfRect[CDInstances.InstRectO[element]], CDBasics.BaseOfRect[CDInstances.InstRectO[sorted.first]]] THEN RETURN [CONS [element, sorted]]; RETURN [CONS [sorted.first, InsertInOrder[element, sorted.rest]]]; }; SetLazyPins: PROC [wire: Wire] = {Sinix.PutLazyPinsProp[extractMode, wire, lazyPinsData]}; instances: LIST OF CD.Instance; recordData: CoreClasses.RecordCellType _ NARROW [cellType.data]; WHILE obj.class#CDCells.cellClass DO obj _ CDDirectory.Expand[obj].new ENDLOOP; FOR list: LIST OF CD.Instance _ NARROW [obj.specificRef, CD.CellPtr].contents, list.rest WHILE list#NIL DO instances _ InsertInOrder[list.first, instances]; ENDLOOP; FOR i: NAT IN [0 .. recordData.size) DO CoreProperties.PutCellInstanceProp[recordData[i], extractMode.instanceProp, instances.first]; instances _ instances.rest; ENDLOOP; IF instances#NIL THEN Signal[]; CoreOps.VisitAtomicWires[cellType.public, SetLazyPins]; }; InternalDecorateAbutX: PUBLIC DecorateProc = { IF checkAbuts THEN DecorateAbutX[cellType, obj] ELSE UncheckedDecorateAbut[cellType, obj, SortInX]; }; InternalDecorateAbutY: PUBLIC DecorateProc = { IF checkAbuts THEN DecorateAbutY[cellType, obj] ELSE UncheckedDecorateAbut[cellType, obj, SortInY]; }; InternalDecorateReverseAbutX: PUBLIC DecorateProc = { IF checkAbuts THEN DecorateReverseAbutX[cellType, obj] ELSE UncheckedDecorateAbut[cellType, obj, ReverseSortInX]; }; InternalDecorateReverseAbutY: PUBLIC DecorateProc = { IF checkAbuts THEN DecorateReverseAbutY[cellType, obj] ELSE UncheckedDecorateAbut[cellType, obj, ReverseSortInY]; }; DecorateAbutX: PUBLIC DecorateProc = { DecorateFlatten[cellType, obj, SortInX]; }; DecorateAbutY: PUBLIC DecorateProc = { DecorateFlatten[cellType, obj, SortInY]; }; DecorateReverseAbutX: PUBLIC DecorateProc = { DecorateFlatten[cellType, obj, ReverseSortInX]; }; DecorateReverseAbutY: PUBLIC DecorateProc = { DecorateFlatten[cellType, obj, ReverseSortInY]; }; SortInX: PUBLIC PROC [pos1, pos2: CD.Position] RETURNS [BOOL] = {RETURN [pos1.xpos2.x]}; ReverseSortInY: PUBLIC PROC [pos1, pos2: CD.Position] RETURNS [BOOL] = {RETURN [pos1.y>pos2.y]}; PositionnedInstance: TYPE = REF PositionnedInstanceRec; PositionnedInstanceRec: TYPE = RECORD [ instance: CoreClasses.CellInstance, bindings: HashTable.Table, transf: CD.Instance ]; PositionnedWire: TYPE = REF PositionnedWireRec; PositionnedWireRec: TYPE = RECORD [ wire: Wire, bindings: HashTable.Table, transf: CD.Instance ]; GetPositionnedInstances: PROC [obj: CD.Object, inOrder: OrderProc] RETURNS [positionnedInstances: LIST OF PositionnedInstance _ NIL, positionnedWires: LIST OF PositionnedWire _ NIL] = { transfs: LIST OF CD.Instance _ LIST [CDInstances.NewInst[obj]]; extractedCT: CellType _ NARROW [Sinix.Extract[obj, extractMode].result]; InsertInOrdered: PROC [element: PositionnedInstance, sorted: LIST OF PositionnedInstance] RETURNS [new: LIST OF PositionnedInstance] = { IF sorted=NIL THEN RETURN [LIST [element]]; IF inOrder[CDBasics.BaseOfRect[CDInstances.InstRectO[element.transf]], CDBasics.BaseOfRect[CDInstances.InstRectO[sorted.first.transf]]] THEN RETURN [CONS [element, sorted]]; RETURN [CONS [sorted.first, InsertInOrdered[element, sorted.rest]]]; }; EachInstance: CoreFlat.EachInstanceProc = { transf: CD.Instance _ Sinix.Transform[transfs.first, InstanceTransf[instance]]; flatten _ NOT HasLayout[instance.type]; IF flatten THEN {transfs _ CONS [transf, transfs]; RETURN}; positionnedInstances _ InsertInOrdered [ NEW [PositionnedInstanceRec _ [instance: instance, bindings: bindings, transf: transf]], positionnedInstances]; }; RecordGeometry: CoreFlat.EachCellTypeProc = { recordCellData: CoreClasses.RecordCellType _ NARROW [cellType.data]; RecordWireGeometry: PROC [wire: Wire] = { IF Sinix.GetWireGeometryProp[extractMode, wire]=NIL THEN RETURN; positionnedWires _ CONS [ NEW [PositionnedWireRec _ [wire: wire, bindings: bindings, transf: transfs.first]], positionnedWires]; }; CoreOps.VisitAtomicWires[recordCellData.internal, RecordWireGeometry]; }; PopTransf: CoreFlat.EachCellTypeProc = {transfs _ transfs.rest}; CoreFlat.EnumerateLeaves[extractedCT, EachInstance, RecordGeometry, RecordGeometry, PopTransf]; }; DecorateFlatten: PUBLIC PROC [cellType: CellType, obj: CD.Object, inOrder: PROC [CD.Position, CD.Position] RETURNS [BOOL]] = { ir: CD.Rect _ CD.InterestRect[obj]; extractedCT: CellType _ NARROW [Sinix.Extract[obj, extractMode].result]; extractedToSource: HashTable.Table _ HashTable.Create[CoreOps.WireBits[extractedCT.public]]; extractedCTData: CoreClasses.RecordCellType _ NARROW [extractedCT.data]; cellTypeData: CoreClasses.RecordCellType _ NARROW [cellType.data]; DecorateActual: PROC [bindings: HashTable.Table, sourceActual, deepActual: Wire] = { wireBind: CoreFlat.WireBind _ NARROW [HashTable.Fetch[bindings, deepActual].value]; extractedActual: Wire; wire: Wire; FOR i: NAT IN [0 .. sourceActual.size) DO DecorateActual[bindings, sourceActual[i], deepActual[i]] ENDLOOP; IF deepActual.size#0 THEN RETURN; IF wireBind=NIL THEN Signal[]; -- something is really wrong! IF wireBind.path.length#0 THEN Signal[]; -- a deep internal wire extractedActual _ wireBind.wire; IF sourceActual.size#extractedActual.size THEN Signal[]; wire _ NARROW [HashTable.Fetch[extractedToSource, extractedActual].value]; IF wire=sourceActual THEN RETURN; IF wire#NIL THEN { PW.WriteF[ "*** Mismatch between extracted layout and source description: atomic extracted wire '%g' corresponds to 2 different atomic source wires '%g' and '%g'\n", IO.rope[CoreOps.GetFullWireNames[extractedCTData.internal, extractedActual].first], IO.rope[CoreOps.GetFullWireNames[cellType.public, wire].first], IO.rope[CoreOps.GetFullWireNames[cellType.public, sourceActual].first] ]; Signal[]; }; SourcePinsFromExtracted[sourceActual, extractedActual, FALSE]; [] _ HashTable.Insert[extractedToSource, extractedActual, sourceActual]; }; positionnedInstances: LIST OF PositionnedInstance; positionnedWires: LIST OF PositionnedWire; length: INT _ 0; [positionnedInstances, positionnedWires] _ GetPositionnedInstances[obj, inOrder]; FOR list: LIST OF PositionnedInstance _ positionnedInstances, list.rest WHILE list#NIL DO length _ length+1 ENDLOOP; IF cellTypeData.size#length THEN { PW.WriteF["*** Mismatch between extracted layout (%g instances) and source description (%g instances)\n", IO.int[length], IO.int[cellTypeData.size]]; Signal[]; }; FOR i: NAT IN [0 .. cellTypeData.size) DO positionnedInstance: PositionnedInstance _ positionnedInstances.first; positionnedInstances _ positionnedInstances.rest; IF NOT EqualCellType[cellTypeData[i].type, positionnedInstance.instance.type] THEN { PW.WriteF["*** Mismatch between the %gth instance of the extracted layout and the %gth instance of the source description: cellTypes are different\n", IO.int[i], IO.int[i]]; Signal[]; }; CoreProperties.PutCellInstanceProp[cellTypeData[i], extractMode.instanceProp, positionnedInstance.transf]; IF ~ CoreOps.Conform[cellTypeData[i].actual, positionnedInstance.instance.actual] THEN Signal[]; FOR j: NAT IN [0 .. positionnedInstance.instance.actual.size) DO DecorateActual[positionnedInstance.bindings, cellTypeData[i].actual[j], positionnedInstance.instance.actual[j]]; ENDLOOP; ENDLOOP; WHILE positionnedWires#NIL DO positionnedWire: PositionnedWire _ positionnedWires.first; wireBind: CoreFlat.WireBind; name: ROPE _ CoreOps.GetShortWireName[positionnedWire.wire]; source: Wire; geometry: LIST OF CD.Instance _ Sinix.GetWireGeometryProp[extractMode, positionnedWire.wire]; wireBind _ NARROW [HashTable.Fetch[positionnedWire.bindings, positionnedWire.wire].value]; IF wireBind=NIL THEN Signal[]; -- internal bug! IF wireBind.path#CoreFlat.NullPath THEN Signal[]; -- not yet implemented if it is not part of the internal of extractedCT source _ NARROW [HashTable.Fetch[extractedToSource, wireBind.wire].value]; IF source=NIL THEN source _ CoreOps.FindWire[cellTypeData.internal, name]; IF source=NIL THEN Signal[]; geometry _ Sinix.TransformList[positionnedWire.transf, geometry]; Sinix.AppendWireGeometryProp[extractMode, source, geometry]; FOR pins: LIST OF CD.Instance _ geometry, pins.rest WHILE pins#NIL DO pin: CD.Instance _ pins.first; IF ~CDBasics.Inside[CDInstances.InstRectO[pin], CDBasics.Extend[ir, -1]] THEN Sinix.AddPinsProp[extractMode, source, pin]; ENDLOOP; positionnedWires _ positionnedWires.rest; ENDLOOP; }; DecorateRotated: PUBLIC DecorateProc = { extractedCT: CellType _ NARROW [Sinix.Extract[obj, extractMode].result]; extractedToSource: HashTable.Table _ HashTable.Create[CoreOps.WireBits[extractedCT.public]]; DecorateActual: PROC [sourceActual, extractedActual: Wire] = { wire: Wire _ NARROW [HashTable.Fetch[extractedToSource, extractedActual].value]; IF sourceActual.size#extractedActual.size THEN Signal[]; FOR i: NAT IN [0 .. sourceActual.size) DO DecorateActual[sourceActual[i], extractedActual[i]] ENDLOOP; IF sourceActual.size#0 THEN RETURN; IF wire=sourceActual THEN RETURN; IF wire#NIL THEN { PW.WriteF[ "*** Mismatch between extracted layout and source description: atomic extracted wire '%g' corresponds to 2 different atomic source wires '%g' and '%g'\n", IO.rope[CoreOps.GetFullWireNames[extractedCTData.internal, extractedActual].first], IO.rope[CoreOps.GetFullWireNames[cellType.public, wire].first], IO.rope[CoreOps.GetFullWireNames[cellType.public, sourceActual].first] ]; Signal[]; }; SourcePinsFromExtracted[sourceActual, extractedActual, FALSE]; [] _ HashTable.Insert[extractedToSource, extractedActual, sourceActual]; }; extractedCTData: CoreClasses.RecordCellType _ NARROW [extractedCT.data]; IF cellType.class#CoreClasses.identityCellClass OR extractedCTData.size#1 THEN { PW.WriteF["*** Mismatch between extracted layout (%g instances) and source description (%g)\n", IO.int[extractedCTData.size], IO.rope[cellType.class.name]]; Signal[]; }; DecorateActual[cellType.public, extractedCTData[0].actual]; }; DecorateRecasted: PUBLIC DecorateProc = { extractedCT: CellType _ NARROW [Sinix.Extract[obj, extractMode].result]; CopyPinsFromExtractedToSourceProp: CoreOps.EachWirePairProc = { IF publicWire.size#0 THEN RETURN; SourcePinsFromExtracted[publicWire, actualWire, TRUE]; }; IF CoreOps.VisitBinding[extractedCT.public, cellType.public, CopyPinsFromExtractedToSourceProp] THEN Signal[]; }; InstanceTransf: PROC [cellInst: CoreClasses.CellInstance] RETURNS [transf: CD.Instance] = { transf _ NARROW [CoreProperties.GetCellInstanceProp[cellInst, extractMode.instanceProp]]; }; InstanceLocation: PROC [cellInst: CoreClasses.CellInstance] RETURNS [location: CD.Position] = { location _ CDBasics.BaseOfRect[CDInstances.InstRectO[InstanceTransf[cellInst]]]; }; SortInstances: PUBLIC PROC [cellType: CellType, inOrder: PROC [CD.Position, CD.Position] RETURNS [BOOL]] = { data: CoreClasses.RecordCellType _ NARROW [cellType.data]; FOR i: NAT IN [0 .. data.size-1) DO FOR j: NAT IN (i .. data.size) DO IF ~inOrder[InstanceLocation[data[i]], InstanceLocation[data[j]]] THEN { w: CoreClasses.CellInstance _ data[i]; data[i] _ data[j]; data[j] _ w; }; ENDLOOP; ENDLOOP; }; AbutCell: PUBLIC PROC [public: Wire, abutInstances: LIST OF AbutInstance, inX: BOOL, shared: BOOL _ FALSE, name: ROPE _ NIL, props: Properties _ NIL] RETURNS [recordCell: CellType] = { positionnedInstances: LIST OF PositionnedInstance _ NIL; ObjectsFromAbutInstances: PROC [abutInstances: LIST OF AbutInstance] RETURNS [LIST OF CD.Object] = { IF abutInstances=NIL THEN RETURN [NIL]; BEGIN obj: CD.Object _ abutInstances.first.object; -- to allow 2 instances of the same object to have different bindings ct: CellType _ FromLayoutWithoutPublic[obj]; RETURN [CONS [obj, ObjectsFromAbutInstances[abutInstances.rest]]]; END; }; AnalyzePositionnedInstances: PROC = { FOR abuts: LIST OF AbutInstance _ abutInstances, abuts.rest WHILE abuts#NIL DO positionnedInstance: PositionnedInstance _ positionnedInstances.first; instance: CoreClasses.CellInstance _ positionnedInstance.instance; IF abuts.first.object#Layout[instance.type] THEN Signal[]; FOR pas: LIST OF CoreCreate.PA _ abuts.first.pas, pas.rest WHILE pas#NIL DO pub: Wire _ CoreCreate.FindWire[instance.type.public, pas.first.public]; act: Wire _ CoreCreate.FindWire[public, pas.first.actual]; wireBind: CoreFlat.WireBind _ NARROW [HashTable.Fetch[positionnedInstance.bindings, CoreClasses.CorrespondingActual[instance, pub]].value]; IF pub=NIL OR act=NIL THEN Signal[]; IF wireBind.path.length#0 THEN Signal[]; -- a deep internal wire IF ~CoreOps.RecursiveMember[extractedCT.public, wireBind.wire] THEN Signal[]; -- corresponding wire does not end up being public RegisterExtractedToSource[wireBind.wire, act, TRUE]; ENDLOOP; positionnedInstances _ positionnedInstances.rest; ENDLOOP; IF positionnedInstances#NIL THEN Signal[]; }; RegisterExtractedToSource: PROC [extracted, source: Wire, checkPins: BOOL] = { pins: LIST OF CD.Instance _ Sinix.GetPinsProp[extractMode, extracted]; prevAct: Wire _ NARROW [HashTable.Fetch[extractedToSource, extracted].value]; IF extracted.size#source.size THEN Signal[]; -- extracted wire and truth public do not have same structure! FOR i: NAT IN [0 .. extracted.size) DO RegisterExtractedToSource[extracted[i], source[i], checkPins] ENDLOOP; IF prevAct=source THEN RETURN; IF prevAct#NIL THEN Signal[]; -- extracted wire connected to 2 parts of the public [] _ HashTable.Store[extractedToSource, extracted, source]; IF source.size=0 THEN SourcePinsFromExtracted[source, extracted, checkPins]; -- sub-public should have pins! }; Replace: PROC [extractedWire: Wire] RETURNS [wire: Wire] = { wire _ NARROW [HashTable.Fetch[extractedToSource, extractedWire].value]; IF wire#NIL THEN RETURN; wire _ CoreOps.CreateWires[size: extractedWire.size]; internals _ CONS [wire, internals]; FOR i: NAT IN [0 .. extractedWire.size) DO wire[i] _ Replace[extractedWire[i]]; ENDLOOP; RegisterExtractedToSource[extractedWire, wire, FALSE]; }; CheckHasPins: PROC [wire: Wire] = { pins: LIST OF CD.Instance _ Sinix.GetPinsProp[extractMode, wire]; IF pins#NIL THEN RETURN; PW.WriteF["Public %g does not have have any pins. Probably because you forgot to specify in each abutInstance the sub-public bound to it\n", IO.rope[CoreOps.GetFullWireNames[public, wire].first]]; Signal[]; }; internals: LIST OF Wire _ LIST [public]; i: NAT _ 0; instances: LIST OF CoreClasses.CellInstance; obj: CD.Object _ IF shared THEN IF abutInstances=NIL OR abutInstances.rest=NIL THEN PW.AbutListX[ObjectsFromAbutInstances[abutInstances]] ELSE (IF inX THEN PW.SharedAbutListX ELSE PW.SharedAbutListY)[ObjectsFromAbutInstances[abutInstances]] -- the last level of abutting is for the case of only one abutInstances ELSE (IF inX THEN PW.AbutListX ELSE PW.AbutListY)[ObjectsFromAbutInstances[abutInstances]]; extractedCT: CellType _ NARROW [Sinix.Extract[obj, extractMode].result]; extractedData: CoreClasses.RecordCellType _ NARROW [extractedCT.data]; extractedToSource: HashTable.Table _ HashTable.Create[CoreOps.WireBits[public]]; positionnedWires: LIST OF PositionnedWire; [positionnedInstances, positionnedWires] _ GetPositionnedInstances[obj, IF inX THEN SortInX ELSE SortInY]; IF positionnedWires#NIL THEN Signal[]; -- They were not really abuts ? AnalyzePositionnedInstances[]; CoreOps.VisitAtomicWires[public, CheckHasPins]; FOR i: NAT DECREASING IN [0 .. extractedData.size) DO actual: Wire _ CoreOps.CreateWires[size: extractedData[i].actual.size]; FOR j: NAT IN [0 .. extractedData[i].actual.size) DO actual[j] _ Replace[extractedData[i].actual[j]]; ENDLOOP; instances _ CONS [CoreClasses.CreateInstance[actual: actual, type: extractedData[i].type, props: extractedData[i].properties], instances]; ENDLOOP; recordCell _ CoreClasses.CreateRecordCell[public, CoreOps.CreateWire[internals], instances, name, props]; CoreProperties.PutCellTypeProp[recordCell, layoutProp, obj]; -- The truth is the layout! CDProperties.PutObjectProp[obj, extractMode.cacheProp, recordCell]; -- Fake the extraction! IF Sinix.Extract[Layout[recordCell], extractMode].result#recordCell THEN Signal[]; -- invariant failed }; EnumerateWirePins: PUBLIC PROC [cellType: CellType, eachWirePin: EachWirePinProc] RETURNS [quit: BOOL] = { EachWire: CoreOps.EachWireProc = { EachPin: EachPinProc = { quit _ eachWirePin[wire, instance, min, max, side, layer]; }; IF wire.size#0 THEN RETURN; quit _ EnumeratePins[cellType, wire, EachPin]; }; quit _ CoreOps.VisitWire[cellType.public, EachWire]; }; WIMMSL: TYPE = RECORD [wire: Wire, instance: CD.Instance, min, max: INT, side: Side, layer: CD.Layer]; EnumerateNonOverlappingWirePins: PUBLIC PROC [cellType: CellType, eachWirePin: EachWirePinProc] RETURNS [quit: BOOL] = { list: LIST OF WIMMSL _ NIL; InsertInList: PROC [wimmsl: WIMMSL, list: LIST OF WIMMSL] RETURNS [newList: LIST OF WIMMSL] = { WHILE list#NIL DO IF wimmsl.wire=list.first.wire AND wimmsl.layer=list.first.layer AND wimmsl.side=list.first.side AND wimmsl.min<=list.first.max AND list.first.min<=wimmsl.max THEN {wimmsl.min _ MIN [wimmsl.min, list.first.min]; wimmsl.max _ MAX [wimmsl.max, list.first.max]} ELSE newList _ CONS [list.first, newList]; list _ list.rest; ENDLOOP; newList _ CONS [wimmsl, newList]; }; EachWirePin: EachWirePinProc = { list _ InsertInList [[wire, instance, min, max, side, layer], list]; }; [] _ EnumerateWirePins[cellType, EachWirePin]; WHILE list#NIL DO quit _ eachWirePin[list.first.wire, list.first.instance, list.first.min, list.first.max, list.first.side, list.first.layer]; IF quit THEN EXIT; list _ list.rest; ENDLOOP; }; EnumeratePins: PUBLIC PROC [cellType: CellType, wire: Wire, eachPin: EachPinProc] RETURNS [quit: BOOL _ FALSE] = { obj: CD.Object _ Layout[cellType]; objSize: CD.Position _ CD.InterestSize[obj]; FOR pins: LIST OF CD.Instance _ Sinix.GetPinsProp[extractMode, wire], pins.rest WHILE pins#NIL DO pin: CD.Instance _ pins.first; side: Side; min, max: INT _ 0; sides: ARRAY Side OF BOOL _ ALL [FALSE]; pinRect: CD.Rect _ CDBasics.MoveRect[CDInstances.InstRectO[pin], CDBasics.SubPoints[[0, 0], CDBasics.BaseOfRect[CD.InterestRect[obj]]]]; -- bbox of the pin in the obj coordinate system of the outer layer: CD.Layer _ IF CDSymbolicObjects.IsSymbolicOb[pin.ob] THEN CDSymbolicObjects.GetLayer[pin] ELSE pin.ob.layer; sides[top] _ CDBasics.Intersect[pinRect, [0, objSize.y-1, objSize.x, objSize.y]]; sides[bottom] _ CDBasics.Intersect[pinRect, [0, 0, objSize.x, 1]]; sides[left] _ CDBasics.Intersect[pinRect, [0, 0, 1, objSize.y]]; sides[right] _ CDBasics.Intersect[pinRect, [objSize.x-1, 0, objSize.x, objSize.y]]; FOR s: Side IN Side DO IF ~sides[s] THEN LOOP; side _ s; IF side=top OR side=bottom THEN {min _ pinRect.x1; max _ pinRect.x2}; IF side=left OR side=right THEN {min _ pinRect.y1; max _ pinRect.y2}; IF (quit _ eachPin[pin, min, max, side, layer]) THEN RETURN; ENDLOOP; ENDLOOP; }; EnumerateSortedPins: PUBLIC PROC [cellType: CellType, side: Side, eachSortedPin: EachSortedPinProc] RETURNS [quit: BOOL] = { thisSide: Side _ side; list: LIST OF WIMMSL _ NIL; InsertInList: PROC [wimmsl: WIMMSL, list: LIST OF WIMMSL] RETURNS [LIST OF WIMMSL] = { IF list=NIL THEN RETURN [LIST [wimmsl]]; RETURN [IF list.first.minKšžœ7žœ˜BK˜—š  œžœžœ˜2Kšœ5žœ˜:K˜—š œžœžœžœ˜BKšœžœ8˜DKšžœžœžœžœ˜šž˜š œžœ˜(Kšžœ(žœžœ ˜Kšœžœžœ˜/K˜—šŸœžœ˜Jšžœ.žœ ˜>Kšœžœžœ˜/K˜—šŸœžœ˜Jšžœ.žœ ˜>Kšœžœžœ˜/K˜—šŸœžœ˜Jšžœ.žœ ˜>Kšœžœžœ˜0K˜—šŸœžœ˜Jšžœ.žœ ˜>Kšœžœžœ˜0K˜—K˜šŸœ˜Kšœ'˜'K˜—K˜šœžœžœ˜"J˜+Kšœžœžœžœ˜K˜—š œžœžœ˜HKšœ3˜3Kšœ žœ˜Kšžœ.žœžœ˜IKšœ žœ˜#Kšœžœ/˜9Kšœ!˜!Kš žœžœžœžœžœ˜?šžœžœžœž˜!Kšžœ žœžœžœ˜Kšœ-˜-Kšžœ4žœžœ˜@šžœžœžœ žœ˜Kšžœ žœžœžœ˜Kšžœ4žœžœ˜@š žœ%žœ'žœ&žœ&žœ'žœ˜าKšœžœ˜8Kšœžœ˜8šžœžœžœ˜1šœ%˜%Kšœ˜šœ#˜#KšœF˜FKšœ˜Kšœ˜—Kšœ˜Kšœ˜—Kšœ žœ˜K˜K˜—K˜—Kšžœ˜—Kšžœ˜—Kšžœ žœžœ˜$Kšžœ>žœ ˜OKšœMžœ3žœ˜ŸKšœ žœ ˜Kšœ!˜!šžœžœžœž˜!Kšžœ žœžœžœ˜Kšœ#˜#Kšžœ˜—˜K˜——šŸ œžœ˜$Jšœžœ*˜Hš  œžœžœžœžœžœžœ˜GKšœ žœ9žœ ˜mKšœ#žœ9žœ ˜nKšžœžœKžœžœ˜\K˜K˜—Jšœf˜fJšœ0˜0Jšœ žœžœžœ˜*šžœ ž˜JšœC˜CJšœ+˜+Jš žœธžœ"žœžœ<žœ˜ปJ˜—Kšœ˜—K˜š   œžœAžœžœžœžœ˜ŠK˜š  œžœžœžœžœ˜Jšžœ-žœ žœž˜Hšžœžœž˜"šœ˜Kšžœ4˜6Kšžœ˜Kšžœ˜Kšœ˜—šœ˜Kšžœ2˜4Kšœ˜—Kšžœžœ˜—Kšžœ˜Kšžœ˜—K˜K˜—Kšžœžœžœžœ?˜[š žœžœžœ*žœžœž˜NKšœ)˜)Kšžœžœžœ žœ˜HKšžœ˜Kšœ*˜*Kšžœ˜Kšœ-˜-Kšžœ˜Kšžœ˜—J˜K˜—š  œžœ"žœ žœ˜Nšœžœžœž˜˜Kšœžœ˜—˜-Kšœžœ˜5—˜-Kšœžœ˜5—šž˜Kšœžœ˜ ——J˜K˜—š œžœžœ ˜XJšœ#žœžœK˜’š  œžœ žœžœžœžœ žœžœžœžœ˜nKš žœžœžœžœžœ ˜+Kšžœxžœžœžœ˜ŸKšžœžœ6˜BK˜—Kš  œžœI˜ZKšœ žœžœžœ ˜Jšœ)žœ˜@Kšžœžœ#žœ˜Ošžœžœžœžœ žœžœžœžœž˜jKšœ1˜1Kšžœ˜—šžœžœžœž˜'Jšœ]˜]Jšœ˜Jšžœ˜—Jšžœ žœžœ ˜Jšœ7˜7K˜K˜—š œžœ˜.šžœ ˜Kšžœ˜"Kšžœ/˜3—Kšœ˜—K˜š œžœ˜.šžœ ˜Kšžœ˜"Kšžœ/˜3—Kšœ˜K˜—š œžœ˜5šžœ ˜Kšžœ%˜)Kšžœ6˜:—Kšœ˜K˜—š œžœ˜5šžœ ˜Kšžœ%˜)Kšžœ6˜:—Kšœ˜K˜—š  œžœ˜&Kšœ(˜(Kšœ˜K˜—š  œžœ˜&Kšœ(˜(Kšœ˜K˜—š œžœ˜-Kšœ/˜/Kšœ˜K˜—š œžœ˜-Kšœ/˜/Kšœ˜K˜—Kš œžœžœžœ žœžœžœ˜YKš œžœžœžœ žœžœžœ˜YKš œžœžœžœ žœžœžœ˜`Kš œžœžœžœ žœžœžœ˜`K˜Kšœžœžœ˜7šœžœžœ˜'Kšœ#˜#Kšœ˜Kšœžœ ˜K˜—Kšœžœžœ˜/šœžœžœ˜#K˜ Kšœ˜Kšœžœ ˜K˜K˜—KšœU™UKšœE™Eš œžœžœžœžœžœžœžœžœžœ˜นJšœ ž œ,˜?Jšœžœ*˜Hš œžœ(žœžœžœžœžœ˜ˆKš žœžœžœžœžœ ˜+Kšžœ†žœžœžœ˜ญKšžœžœ8˜DK˜—š  œ˜+JšœžœE˜OJšœ žœ˜'Jšžœ žœ žœžœ˜;šœ(˜(JšžœV˜YJšœ˜—J˜—š œ˜-Jšœ-žœ˜Dš œžœ˜)Jšžœ.žœžœžœ˜@šœžœ˜JšžœQ˜TJšœ˜—J˜—JšœF˜FJ˜—Jš  œ7˜@Jšœ_˜_K˜K˜—š œžœžœžœžœžœ žœ žœžœ˜~Jšœžœžœ˜#Jšœžœ*˜HKšœ\˜\Kšœ.žœ˜HKšœ+žœ˜Bš œžœ@˜TJšœžœ/˜SJšœ˜Kšœ ˜ Kš žœžœžœžœ:žœ˜kKšžœžœžœ˜!Kšžœ žœžœ ข˜˜@KšžœD˜FKšœ˜—Kšœ ˜ K˜—Kšœ7žœ˜>KšœH˜HK˜—Kšœžœžœ˜2Kšœžœžœ˜*Kšœžœ˜KšœQ˜QKšžœžœžœ7žœžœžœžœ˜tšžœžœ˜"Kšžœhžœžœ˜•Kšœ ˜ K˜—šžœžœžœž˜)KšœF˜FKšœ1˜1šžœžœHžœ˜TKšžœ•žœ žœ ˜ญKšœ ˜ K˜—Kšœj˜jKšžœPžœ ˜`šžœžœžœ1ž˜@Kšœp˜pKšžœ˜—Kšžœ˜—Kšœœ™œšžœžœž˜Kšœ:˜:Kšœ˜Kšœžœ2˜Kšœ žœ=˜PKšžœ(žœ ˜8Kš žœžœžœžœ5žœ˜fKšžœžœžœ˜#Kšžœžœžœ˜!šžœžœžœ˜šžœ˜ Kšœ›˜›KšžœR˜TKšžœ>˜@KšžœD˜FKšœ˜—Kšœ ˜ K˜—Kšœ7žœ˜>KšœH˜HK˜—Kšœ.žœ˜Hšžœ.žœžœ˜PKšžœ^žœžœ˜œKšœ ˜ K˜—Kšœ;˜;Kšœ˜—K˜šŸœžœ˜)Jšœžœ*˜HšŸ!œ˜?Kšžœžœžœ˜!Kšœ0žœ˜6K˜—Kšœ‡™‡Kšžœ^žœ ˜nKšœ˜—K˜š œžœ&žœ žœ˜[Kšœ žœJ˜YK˜—š œžœ&žœ žœ˜_KšœP˜PK˜—š  œžœžœžœžœ žœ žœžœ˜lKšœ#žœ˜:šžœžœžœž˜#šžœžœžœž˜!šžœ@žœ˜HKšœ'˜'Kšœ˜Kšœ˜—Kšžœ˜—Kšžœ˜—K˜——™%š œžœžœžœžœžœ žœžœžœžœžœžœ˜ธKšœžœžœžœ˜8š œžœžœžœžœžœžœžœ ˜dKš žœžœžœžœžœ˜'šž˜Kšœžœ&ขE˜rKšœ,˜,Kšžœžœ6˜BKšžœ˜—K˜—š œžœ˜%š žœžœžœ*žœžœž˜NKšœF˜FKšœB˜BJšžœ*žœ ˜:š žœžœžœ žœžœžœž˜KKšœH˜HKšœ:˜:Jšœžœg˜‹Kš žœžœžœžœžœ ˜$Jšžœžœ ข˜@Jšžœ=žœ ข2˜€Jšœ.žœ˜4Kšžœ˜—Kšœ1˜1Kšžœ˜—Kšžœžœžœ ˜*K˜—š œžœ&žœ˜NKšœžœžœžœ6˜FKšœžœ7˜MKšžœžœ ข>˜kKš žœžœžœžœ?žœ˜mKšžœžœžœ˜Kšžœ žœžœ ข4˜RKšœ;˜;Kšžœžœ8ข˜lK˜—š œžœžœ˜˜วšœžœ žœ(˜˜>Jšœ>˜>JšœS˜SJšœS˜SJšœ;˜;Jšœ;˜;JšœI˜IJšœI˜IJšœ8˜8Jšœ8˜8Jšœ8˜8Jšœ:˜:Jšœ:˜:Jšœ;˜;J˜JšœX˜XJ˜—Kšžœ˜K™—…—Š–ณ~