<> <> <> DIRECTORY CD, CDCells, CMos, CDPinObjects, Convert, EuControl, EuGen, EUtils, PW, PWCmos, PWDescr, PWPins, Rope; EUtilsImpl: CEDAR PROGRAM IMPORTS CD, CDCells, CMos, CDPinObjects, Convert, EuControl, EuGen, PW, PWCmos, PWDescr, PWPins, Rope EXPORTS EUtils = BEGIN OPEN EUtils; <<>> <<-- Cells for making registers>> ctTopCell, ctOpRBus, ctOpLBus, ctBSBus, ctAPBus, ctKBus, ctCBus, ctRBus, ctAnyBusComplement, basicTstDriver: PW.ObPtr; <<-- Control descriptors>> regsInitialised: BOOL _ FALSE; <<-- A special filler for the control: if there is enough space, inserts contacts poly/m2 on every poly wire>> CtTopFiller : PROC [design: CD.Design, obj: PW.ObPtr, height: INT, selectNameProc: PW.SelectNamesProc _ PW.KeepAll] RETURNS [filler: PW.ObPtr] = BEGIN PutContact: PW.ForEachPinProc = BEGIN layer: CD.Layer _ CDPinObjects.GetLayer[app]; SELECT TRUE FROM ~selectNameProc[CDPinObjects.GetName[app]] => obj _ NIL; layer=CMos.pol => obj _ contactAndPolyWire; ENDCASE => obj _ PWCmos.Rect[layer, [app.ob.size.x, height]]; END; minDy: INT; polyM2Contact, contactAndPolyWire: PW.ObPtr; polyM2Contact _ PW.Get[design, "polyM2Contact"]; minDy _ PW.Size[polyM2Contact].y; contactAndPolyWire _ PW.AbutY[design, polyM2Contact, PWCmos.Rect[CMos.pol, [4, height-minDy]]]; minDy _ PW.Size[polyM2Contact].y; SELECT height FROM <0 => ERROR; =0 => filler _ NIL; filler _ PW.TopFillerCell[design, obj, height, selectNameProc]; ENDCASE => filler _ PW.TransferCell[design, obj, PWPins.top, height, PutContact]; END; ContactFiller: PUBLIC PROC [design: CD.Design, ctrl, data: PW.ObPtr, selectNameProc: PW.SelectNamesProc _ PW.KeepAll] RETURNS [filler: PW.ObPtr] = BEGIN dy: INT _ PW.Size[data].y-PW.Size[ctrl].y; filler _ CtTopFiller[design, ctrl, dy, selectNameProc]; END; <> <> <> <> <> <> <<<0 => ERROR;>> <<=0 => filler _ NIL;>> << filler _ PW.TopFillerCell[design, ctrl, dy, selectNameProc];>> < filler _ PW.TransferCell[design, ctrl, PWPins.top, dy, PutContact];>> <> <<-- Returns a filler for the control: ctrl is the template, data shows the height; >> CtrlFiller: PUBLIC PROC [design: CD.Design, ctrl, data: PW.ObPtr, selectNameProc: PW.SelectNamesProc _ PW.KeepAll] RETURNS [filler: PW.ObPtr] = BEGIN filler _ ContactFiller[design, ctrl, data, selectNameProc]; END; <> <> <> <> <> <> <<>> <<-- Makes a very common assembly: control, data, filler(s) and routing; >> Assemble: PUBLIC PROC [design: CD.Design, ctrl, data: PW.ObPtr, selectNameProc: PW.SelectNamesProc _ PW.KeepAll, fillerPos: Position _ Center] RETURNS [hSlice: PW.ObPtr] = BEGIN filler, wholeCtrl: PW.ObPtr; SELECT fillerPos FROM Top => {filler _ CtrlFiller[design, ctrl, data, selectNameProc]; wholeCtrl _ IF filler=NIL THEN ctrl ELSE PW.AbutY[design, ctrl, filler]; }; Bottom => {filler _ CtrlFiller[design, ctrl, data, selectNameProc]; wholeCtrl _ IF filler=NIL THEN ctrl ELSE PW.AbutY[design, filler, ctrl]; }; Center => {topFiller, botFiller: PW.ObPtr; dyTop, dyBot: INT; dy: INT _ PW.Size[data].y-PW.Size[ctrl].y; IF dy<0 THEN ERROR; dyBot _ dy/2; dyTop _ dy-dyBot; topFiller _ IF dyTop=0 THEN NIL ELSE CtTopFiller[design, ctrl, dyTop, selectNameProc]; botFiller _ IF dyBot=0 THEN NIL ELSE PW.TopFillerCell[design, ctrl, dyBot, selectNameProc]; wholeCtrl _ PW.AbutY[design, botFiller, ctrl, topFiller]; }; ENDCASE => ERROR; hSlice _ PW.AbutX[design, wholeCtrl, QadCR[design, wholeCtrl, data, EuGen.glueWidth], data]; END; <<-- Must be run before making any register>> InitMakeReg: PUBLIC PROC [design: CD.Design] = BEGIN IF regsInitialised THEN RETURN ELSE regsInitialised _ TRUE; ctAnyBusComplement _ PW.Get[design, "ctAnyBusComplement"]; ctTopCell _ PW.AbutX[design, PW.Get[design, "ctTopCell"], ctAnyBusComplement]; ctOpRBus _ PW.AbutX[design, PW.Get[design, "ctOpRBus"], ctAnyBusComplement]; ctOpLBus _ PW.AbutX[design, PW.Get[design, "ctOpLBus"], ctAnyBusComplement]; ctBSBus _ PW.AbutX[design, PW.Get[design, "ctBSBus"], ctAnyBusComplement]; ctAPBus _ PW.AbutX[design, PW.Get[design, "ctAPBus"], ctAnyBusComplement]; ctKBus _ PW.AbutX[design, PW.Get[design, "ctKBus"], ctAnyBusComplement]; ctCBus _ PW.AbutX[design, PW.Get[design, "ctCBus"], ctAnyBusComplement]; ctRBus _ PW.AbutX[design, PW.Get[design, "ctRBus"], ctAnyBusComplement]; basicTstDriver _ PW.Get[design, "basicTstDriver"]; END; BusNameToMuxObj: PROC [name: ROPE] RETURNS [obj: PW.ObPtr] = BEGIN obj _ SELECT TRUE FROM Rope.Equal[name, "r"] => ctRBus, Rope.Equal[name, "c"] => ctCBus, Rope.Equal[name, "k"] => ctKBus, Rope.Equal[name, "ap"] => ctAPBus, Rope.Equal[name, "bs"] => ctBSBus, Rope.Equal[name, "opL"] => ctOpLBus, Rope.Equal[name, "opR"] => ctOpRBus, Rope.Equal[name, "top"] => ctTopCell, ENDCASE => ERROR; END; <<-- Register only, no control>> MakeRegDP: PUBLIC PROC [design: CD.Design, regD: RegDescr] RETURNS [reg: PW.ObPtr] = BEGIN basicReg, interruptBuses: PW.ObPtr; revListOb, listOb: PW.ListOb _ NIL; flip: BOOL _ FALSE; keepInReg: LIST OF ROPE _ CONS[regD.out]; InitMakeReg[design]; <<-- Keep only one contact to output>> IF ~regD.cutTopBot THEN keepInReg _ CONS["topBus", keepInReg]; basicReg _ PW.Inst[design, PW.Get[design, "basicRegister"], keepInReg, FALSE]; <<-- Add a short cell which interrupts some of the buses, if required>> IF regD.interruptBuses#NIL THEN { interruptBuses _ PW.Inst[design, PW.Get[design, "interruptBusses"], regD.interruptBuses]; basicReg _ PW.AbutY[design, interruptBuses, basicReg];}; <<-- Form the multiplexer>> FOR l: LIST OF ROPE _ regD.inListOb, l.rest WHILE l#NIL DO revListOb _ CONS[BusNameToMuxObj[l.first], revListOb]; ENDLOOP; -- revListOb is the list of objects, from bottom to top, no flip FOR l: PW.ListOb _ revListOb, l.rest WHILE l#NIL DO listOb _ CONS[IF flip THEN PW.FlipY[design, l.first] ELSE l.first, listOb]; flip _ ~flip; ENDLOOP; -- listOb is the list of objects, from top to bottom, correct flip revListOb _ CONS[basicReg, PW.Reverse[listOb]]; -- add the register reg _ PW.ArrayX[design, PW.AbutListY[design, revListOb], EuGen.nbSlices]; END; <<-- "out" should be "r", "c", "k", "ap", "bs", "opL", "opR", or "down">> <<-- "inListOb" and "ctrlListOb" must be specified from TOP to BOTTOM>> <<-- Returns a register with its control>> MakeReg: PUBLIC PROC [design: CD.Design, regD: RegDescr] RETURNS [reg: PW.ObPtr] = BEGIN regDP, ctrl: PW.ObPtr; regDP _ MakeRegDP[design, regD]; <<-- Control>> ctrl _ PW.AbutListY[design, regD.ctrlListOb]; <<-- The assembly>> reg _ Assemble[design, ctrl, regDP,, regD.fillerPos]; END; MakeTstDriver: PUBLIC PROC [design: CD.Design, in: ROPE, out: ROPE, ctrlListOb: PW.ListOb _ NIL] RETURNS [tstDriver: PW.ObPtr] = BEGIN driver, ctrl: PW.ObPtr; InitMakeReg[design]; <<-- Make a cell with the correct input bus, and output on kBus>> driver _ PW.Inst[design, basicTstDriver, LIST[in, out], FALSE]; <<-- Control>> ctrl _ PW.AbutListY[design, ctrlListOb]; <<-- The assembly>> tstDriver _ Assemble[design, ctrl, PW.ArrayX[design, driver, EuGen.nbSlices]]; END; MakeDBus: PUBLIC PROC [design: CD.Design, in: ROPE, dStAd: INT] RETURNS [dBus: PW.ObPtr] = BEGIN dBusGadget, ctrl: PW.ObPtr; regscdpd, regscdpu: PWDescr.Descriptor; <<-- Make a cell with the correct input bus, and output on kBus>> dBusGadget _ PW.Inst[design, basicTstDriver, LIST[in, "kOut"], FALSE]; <<-- Control>> regscdpd _ EuGen.EmptyDescrRegsPD[]; regscdpu _ EuGen.EmptyDescrRegsPU[]; PWDescr.SetBit[regscdpu, "nPhB", TRUE]; PWDescr.SetBit[regscdpu, "Vbias", TRUE]; PWDescr.SetBit[regscdpd, "DExecute", TRUE]; <> PWDescr.SetInt[regscdpd, "DStateAddress", dStAd]; ctrl _ EuControl.Nand[design, regscdpd, regscdpu]; <<-- The assembly>> dBus _ Assemble[design, ctrl, PW.ArrayX[design, dBusGadget, EuGen.nbSlices]]; END; MakeRegWithDBus: PUBLIC PROC [design: CD.Design, regD: RegDescr, dStAd: INT] RETURNS [both: PW.ObPtr] = BEGIN reg, dBus: PW.ObPtr; reg _ MakeReg[design, regD]; dBus _ MakeDBus[design, regD.out, dStAd]; both _ PW.AbutY[design, reg, dBus]; END; <<-- Route the control to the datapath; >> QadCR, QuickandDirtyChannelRouter: PUBLIC PROC [design: CD.Design, left, right: PW.ObPtr, width: INT, useGnd: BOOL _ TRUE] RETURNS [channel: PW.ObPtr] = BEGIN ParseLeft: PWPins.AppEnumerator = BEGIN side: PWPins.Side _ PWPins.GetSide[left, app].side; IF side#PWPins.right THEN RETURN; IF Rope.Equal[CDPinObjects.GetName[app], powerName] THEN RETURN; IF Rope.Equal[CDPinObjects.GetName[app], otherPower] THEN RETURN; leftPins[nbL] _ app; nbL _ nbL+1; END; ParseRightAndGnd: PWPins.AppEnumerator = BEGIN side: PWPins.Side _ PWPins.GetSide[left, app].side; IF side#PWPins.left THEN RETURN; IF Rope.Equal[CDPinObjects.GetName[app], otherPower] THEN RETURN; IF Rope.Equal[CDPinObjects.GetName[app], powerName] THEN {gndPins[nbG] _ app; nbG _ nbG+1} ELSE {rightPins[nbR] _ app; nbR _ nbR+1}; END; SortArray: PROC [pins: ArrayPins, nb: INT] RETURNS [sortedPins: ArrayPins] = BEGIN MinY: PROC RETURNS [minPin: CD.ApplicationPtr] = BEGIN minY: INT _ LAST[INT]; -- y-coord of minPin minN: INT _ 0; -- location of minPin in array FOR i: INT IN [0..nb) DO IF pins[i]#NIL THEN IF pins[i].location.y> IF firstTime THEN {firstTime _ FALSE; wasGoingUp _ goingUp _ leftY> reset _ (wasGoingUp AND leftY>=prevRightY+okDY) OR (~wasGoingUp AND rightY>=prevLeftY+okDY); IF reset THEN {IF goingUp THEN trackX _ initTackX ELSE trackX _ 6} ELSE {IF goingUp THEN trackX _ trackX-incrTrackX ELSE trackX _ trackX+incrTrackX}; <<-- Draw the m2 wire on the right side>> PW.IncludeInCell[design, channel, PWCmos.Rect[CMos.met2, [6, 8]], [width-6, rightY]]; <<-- via: centered on the m2 wire>> PW.IncludeInCell[design, channel, PWCmos.Contact[CMos.met2, CMos.met], [width-16, rightY-1]]; <<-- "top" horizontal m1 wire>> PW.IncludeInCell[design, channel, PWCmos.Rect[CMos.met, [width-trackX-16, 8]], [trackX, rightY]]; <<-- vertical m1 wire (track)>> PW.IncludeInCell[design, channel, PWCmos.Rect[CMos.met, [8, ABS[rightY-leftY]+8]], [trackX, MIN[leftY, rightY]]]; <<-- "bottom" horizontal m1 wire>> PW.IncludeInCell[design, channel, PWCmos.Rect[CMos.met, [trackX, leftPin.ob.size.y]], [0, leftY]]; END; ArrayPins: TYPE = ARRAY[0..50) OF CD.ApplicationPtr; leftPins, rightPins, gndPins: ArrayPins; nbL, nbR, nbG: INT _ 0; initTackX: INT _ width-24; incrTrackX: INT _ 16; -- 8 lambdas okDY: INT _ 16; -- 8 lambdas trackX, prevLeftY, prevRightY, leftY, rightY: INT; powerName, otherPower: ROPE; goingUp, wasGoingUp, firstTime: BOOL _ TRUE; iRectL, iRectR: CD.Rect; IF useGnd THEN {powerName _ "gnd"; otherPower _ "vdd"} ELSE {powerName _ "vdd"; otherPower _ "gnd"}; IF PW.Size[left].y#PW.Size[right].y THEN ERROR; channel _ PW.CreateEmptyCell[]; iRectL _ CD.InterestRect[left]; iRectR _ CD.InterestRect[right]; CDCells.SetInterestRect[channel, [0, 0, width, PW.Size[left].y]]; -- set interestRect of channel <<-- Get the three sets of pins in their respective arrays>> [] _ PWPins.EnumerateEdgePins[left, ParseLeft]; [] _ PWPins.EnumerateEdgePins[right, ParseRightAndGnd]; <<-- Sort them by y-coordinate>> IF nbL#nbR THEN ERROR; leftPins _ SortArray[leftPins, nbL]; rightPins _ SortArray[rightPins, nbR]; <<-- The gnd pins: a straight m2 wire>> FOR i: INT IN [0..nbG) DO PW.IncludeInCell[design, channel, PWCmos.Rect[CMos.met2, [width, gndPins[i].ob.size.y]], [0, gndPins[i].location.y-iRectR.y1]]; -- where it lands ENDLOOP; <<-- For each pair, put 3 wires of m1, one via, one wire m2, and update the x-coord of the vertical track>> FOR i: INT IN [0..nbL) DO RouteTrack[leftPins[i], rightPins[i]]; ENDLOOP; PW.IncludeInDirectory[design, channel, "channel"]; END; <<>> <<-- Used by the carry propagator to route cells in a tree-like topology>> TreeRouter: PUBLIC PROC [design: CD.Design, template: PW.ObPtr, height: INT] RETURNS [channel: PW.ObPtr] = BEGIN <<-- The level is the position of the rightmost zero in the binary representation of the index>> <<-- level starts at zero>> <<-- a pin at level n will use track p=3*n+d where d=0 (g), d=1 (p), d=2 (c)>> Level: PROC [index: INT] RETURNS [level: INT] = BEGIN FOR i: INT IN [0..31) DO IF PW.XthBitOfN[i, index] THEN {level _ i; EXIT}; ENDLOOP; END; <<-- Routes the busses and stick the pins of the tree in two arrays>> ParsePins: PWPins.AppEnumerator = BEGIN side: PWPins.Side _ PWPins.GetSide[template, app].side; IF side#PWPins.top THEN RETURN; IF Rope.Equal[CDPinObjects.GetName[app], "bus"] THEN PW.IncludeInCell[design, channel, bus, [app.location.x-IRect.x1, 0]] ELSE { val, d, index, lr: INT; [val, d, lr] _ ParseName[app]; index _ 6*val+2*d+lr; -- just a unique id IF otherPins[index]#NIL THEN ERROR; IF firstPins[index]=NIL THEN firstPins[index] _ app ELSE otherPins[index] _ app;}; END; <<-- A pin named "p1[17]" returns val=17, d=1 (p), and lr=1>> ParseName: PROC [pin: CD.ApplicationPtr] RETURNS [val, d, lr: INT] = BEGIN s, length: INT; name: ROPE; IF pin=NIL THEN RETURN; name _ CDPinObjects.GetName[pin]; <<-- Find the opening bracket>> s _ Rope.Index[name, 0, "["]; <<-- Distinguish g, p, and c>> SELECT TRUE FROM Rope.Match["g*", name] => {d _ 0}; Rope.Match["p*", name] => {d _ 1}; Rope.Match["c*", name] => {d _ 2}; ENDCASE => ERROR; <<-- Distinguish g0 from g1>> SELECT TRUE FROM Rope.Match["*0[*", name] => {lr _ 0}; Rope.Match["*1[*", name] => {lr _ 1}; ENDCASE => ERROR; <<-- Recover the index 17 from "p1[17]">> length _ Rope.Length[name]; name _ Rope.Substr[name, s+1, length-s-2]; -- now only the number is left val _ Convert.IntFromRope[name]; END; <<-- Both pins carry the same name, and correspond to a track>> RouteTrack: PROC [pin1, pin2: CD.ApplicationPtr] = BEGIN n, p, val, d, x1, x2, y: INT; m1Track, m2Track, ct: PW.ObPtr; IF pin1=NIL OR pin2=NIL THEN RETURN; [val, d] _ ParseName[pin1]; n _ Level[val]; p _ 3*n+d; -- p is the number of the track, start from the ground x1 _ pin1.location.x-IRect.x1; x2 _ pin2.location.x-IRect.x1; y _ (p-3 )*spacing; m1Track _ PWCmos.Rect[CMos.met, [pin1.ob.size.x, y]]; ct _ PWCmos.Contact[CMos.met, CMos.met2]; m2Track _ PWCmos.Rect[CMos.met2, [ABS[x1-x2], 8]]; PW.IncludeInCell[design, channel, m1Track, [x1, 0]]; PW.IncludeInCell[design, channel, m1Track, [x2, 0]]; PW.IncludeInCell[design, channel, ct, [x1, y]]; PW.IncludeInCell[design, channel, ct, [x2, y]]; PW.IncludeInCell[design, channel, m2Track, [MIN[x1, x2], y]]; END; spacing: INT = 18; -- use PWCmos to make it more meta ArrayPins: TYPE = ARRAY[0..400) OF CD.ApplicationPtr; firstPins, otherPins: ArrayPins; -- pairs to route together IRect: CD.Rect _ CD.InterestRect[template]; bus: PW.ObPtr _ PWCmos.Rect[CMos.met, [6, height]]; <<-- Prepare empty channel>> channel _ PW.CreateEmptyCell[]; CDCells.SetInterestRect[channel, [0, 0, PW.Size[template].x, height]]; <<-- Get the pins, and place the vertical busses unrelated to the routing>> [] _ PWPins.EnumerateEdgePins[template, ParsePins]; <<-- For each pair of pins, put 2 wires of m1, two vias, and one wire m2>> FOR i: INT IN [0..200) DO RouteTrack[firstPins[i], otherPins[i]]; ENDLOOP; PW.IncludeInDirectory[design, channel, "channel"]; END; END.