DIRECTORY BitOps, CD, CoreCreate, CoreFlat, CoreProperties, Logic, LogicUtils, Ports, PW, PWCore, Rosemary, Sisyph, TilingClass; LogicMemImpl: CEDAR PROGRAM IMPORTS BitOps, CoreCreate, CoreFlat, CoreProperties, Logic, LogicUtils, Ports, PW, PWCore, Rosemary, Sisyph, TilingClass EXPORTS Logic = BEGIN OPEN Logic, CoreCreate; ramDesignName: ROPE = "LogicRam2"; ramShortDesignName: ROPE = "LogicRam2Short"; ramDesign, ramShortDesign: CD.Design; ramCx, ramShortCx: Sisyph.Context _ NIL; Extract: PUBLIC PROC [schName: ROPE, cx: Sisyph.Context, parms: LIST OF LogicUtils.Value _ NIL] RETURNS [ct: CellType] ~ { tmpCx: Sisyph.Context; IF cx=NIL THEN ERROR; tmpCx _ IF parms=NIL THEN cx ELSE Sisyph.Copy[cx]; WHILE parms#NIL DO Sisyph.Store[tmpCx, parms.first.name, NEW[INT _ parms.first.val]]; parms _ parms.rest; ENDLOOP; ct _ Sisyph.ES[schName, tmpCx]; }; TakeCareOfContext: PROC [short: BOOL] RETURNS [Sisyph.Context] ~ { IF short THEN{ IF ramShortDesign=NIL THEN ramShortDesign _ PW.OpenDesign[ramShortDesignName]; ramShortCx _ Sisyph.Create[ramShortDesign]; RETURN[ramShortCx]; } ELSE { IF ramDesign=NIL THEN ramDesign _ PW.OpenDesign[ramDesignName]; ramCx _ Sisyph.Create[ramDesign]; RETURN[ramCx]; }; }; Ram2Name: ROPE = Rosemary.Register[roseClassName: "Ram2", init: Ram2Init, evalSimple: Ram2Simple]; Ram2: PUBLIC PROC [b, n: NAT, sameSide, short: BOOL _ FALSE] RETURNS [ct: CellType] = { a: NAT = BitOps.NBits[n]; -- number of address bits schName: ROPE = IF sameSide THEN "SameSideRam.sch" ELSE "BothSidesRam.sch"; context: Sisyph.Context = TakeCareOfContext[short]; IF b=0 OR n=0 THEN LogicUtils.Error["Please provide parameters for ram2"]; IF BitOps.ODD[n] THEN n_n+1; -- number of words must be even IF short AND BitOps.ODD[b] THEN LogicUtils.Error["b must be even for short ram"]; ct _ Extract[schName, context, LIST[["b", b], ["n", n]]]; [] _ Rosemary.BindCellType[cellType: ct, roseClassName: Ram2Name]; CoreProperties.PutCellTypeProp[ct, $n, NEW[NAT _ n]]; [] _ CoreFlat.CellTypeCutLabels[ct, logicCutSet, "Memory"]; Ports.InitPorts[ct, l, none, "Vdd", "Gnd", "enW"]; Ports.InitPorts[ct, ls, none, "Input", "RAdr", "WAdr"]; Ports.InitPorts[ct, ls, drive, "Output"]; }; Decoder: PROC [n: NAT, adrOnTop: BOOL, cx: Sisyph.Context] RETURNS [ct: CellType] ~ { dec0: CellType _ Sisyph.ES["dec0.sch", cx]; dec1: CellType _ PWCore.RotateCellType[dec0, $FlipX]; decf0: CellType _ PWCore.RotateCellType[dec0, $FlipY]; decf1: CellType _ PWCore.RotateCellType[dec0, $Rot180]; adrAmpli: CellType _ Sisyph.ES["adrAmpli.sch", cx]; adrInv: CellType _ Sisyph.ES["adrInv.sch", cx]; botCt: CellType _ IF adrOnTop THEN PWCore.RotateCellType[adrInv, $FlipY] ELSE adrAmpli; topCt: CellType _ IF adrOnTop THEN PWCore.RotateCellType[adrAmpli, $FlipY] ELSE adrInv; a: NAT _ BitOps.NBits[n]; -- number of address bits norOut: Wire _ Seq["norOut", n]; adr: Wire _ Seq["adr", a]; tileArray: TilingClass.TileArray _ NEW[TilingClass.TileArrayRec[n+2]]; FOR row: NAT IN [0..n+2) DO -- create the rows tileArray[row] _ NEW[TilingClass.TileRowRec[a]]; ENDLOOP; FOR i: NAT IN [0..a) DO -- column 0 is on the left tileArray[0][i] _ NEW[TilingClass.TileRec _ [ type: botCt, renaming: IF adrOnTop THEN LIST[ ["Gnd", "Gnd"], ["Vdd", "Vdd"] ] ELSE LIST[ ["ad", adr[i]], ["Gnd", "Gnd"], ["Vdd", "Vdd"] ]]]; FOR row: NAT IN [0..n) DO tileArray[row+1][i] _ NEW[TilingClass.TileRec _ [ type: SELECT TRUE FROM BitOps.EVEN[row] AND BitOps.EBFW[row, 15-i] => dec0, BitOps.EVEN[row] AND ~BitOps.EBFW[row, 15-i] => dec1, BitOps.ODD[row] AND BitOps.EBFW[row, 15-i] => decf0, BitOps.ODD[row] AND ~BitOps.EBFW[row, 15-i] => decf1, ENDCASE => ERROR, renaming: LIST[ ["Gnd", "Gnd"], ["Vdd", "Vdd"], ["norOut", norOut[row]]]]]; ENDLOOP; tileArray[n+1][i] _ NEW[TilingClass.TileRec _ [ type: topCt, renaming: IF adrOnTop THEN LIST[ ["ad", adr[i]], ["Gnd", "Gnd"], ["Vdd", "Vdd"] ] ELSE LIST[ ["Gnd", "Gnd"], ["Vdd", "Vdd"] ]]]; ENDLOOP; ct _ TilingClass.CreateTiling[ name: "Decoder", public: Wires[adr, norOut, "Vdd", "Gnd"], tileArray: tileArray, neighborX: TilingClass.LayoutNeighborX, neighborY: TilingClass.LayoutNeighborY ]; }; DecoderShort: PROC [n: NAT, adrOnTop: BOOL, cx: Sisyph.Context] RETURNS [ct: CellType] ~ { dec0: CellType _ Sisyph.ES["dec0.sch", cx]; dec1: CellType _ PWCore.RotateCellType[dec0, $FlipX]; decf0: CellType _ PWCore.RotateCellType[dec0, $FlipY]; decf1: CellType _ PWCore.RotateCellType[dec0, $Rot180]; adrAmpli: CellType _ Sisyph.ES["adrAmpli.sch", cx]; adrInv: CellType _ Sisyph.ES["adrInv.sch", cx]; botCt: CellType _ IF adrOnTop THEN PWCore.RotateCellType[adrInv, $FlipY] ELSE adrAmpli; topCt: CellType _ IF adrOnTop THEN PWCore.RotateCellType[adrAmpli, $FlipY] ELSE adrInv; a: NAT _ BitOps.NBits[n]; -- number of address bits norOut: Wire _ Seq["norOut", n]; adr: Wire _ Seq["adr", a]; tileArray: TilingClass.TileArray _ NEW[TilingClass.TileArrayRec[n+2]]; -- from the bottom up: botCt, rows 0, ... n-1, topCt FOR row: NAT IN [0..n+2) DO -- create the rows tileArray[row] _ NEW[TilingClass.TileRowRec[a]]; ENDLOOP; FOR i: NAT IN [0..a) DO -- column 0 is on the left tileArray[0][i] _ NEW[TilingClass.TileRec _ [ type: botCt, renaming: IF adrOnTop THEN LIST[ ["Gnd", "Gnd"], ["Vdd", "Vdd"] ] ELSE LIST[ ["ad", adr[i]], ["Gnd", "Gnd"], ["Vdd", "Vdd"] ]]]; FOR row: NAT IN [0..n) DO tileArray[row+1][i] _ NEW[TilingClass.TileRec _ [ type: SELECT TRUE FROM BitOps.EVEN[row] AND BitOps.EBFW[row, 15-i] => dec0, BitOps.EVEN[row] AND ~BitOps.EBFW[row, 15-i] => dec1, BitOps.ODD[row] AND BitOps.EBFW[row, 15-i] => decf0, BitOps.ODD[row] AND ~BitOps.EBFW[row, 15-i] => decf1, ENDCASE => ERROR, renaming: LIST[ ["Gnd", "Gnd"], ["norOut", norOut[row]]]]]; ENDLOOP; tileArray[n+1][i] _ NEW[TilingClass.TileRec _ [ type: topCt, renaming: IF adrOnTop THEN LIST[ ["ad", adr[i]], ["Gnd", "Gnd"], ["Vdd", "Vdd"] ] ELSE LIST[ ["Gnd", "Gnd"], ["Vdd", "Vdd"] ]]]; ENDLOOP; ct _ TilingClass.CreateTiling[ name: "Decoder", public: Wires[adr, norOut, "Vdd", "Gnd"], tileArray: tileArray, neighborX: TilingClass.LayoutNeighborX, neighborY: TilingClass.LayoutNeighborY ]; }; Ram2State: TYPE = REF Ram2StateRec; Ram2StateRec: TYPE = RECORD [ input, output, rAdr, wAdr, enW: NAT _ LAST[NAT], val: SEQUENCE size: NAT OF Ports.LevelSequence]; Ram2Init: Rosemary.InitProc = { n: NAT _ NARROW[CoreProperties.GetCellTypeProp[cellType, $n], REF NAT]^; b: NAT; state: Ram2State _ NEW[Ram2StateRec[n]]; [state.input, state.output, state.rAdr, state.wAdr, state.enW] _ Ports.PortIndexes[cellType.public, "Input", "Output", "RAdr", "WAdr", "enW"]; b _ p[state.input].ls.size; FOR i: NAT IN [0..n) DO state.val[i] _ NEW[Ports.LevelSequenceRec[b]]; Ports.SetLS[state.val[i], X]; ENDLOOP; stateAny _ state; }; Ram2Simple: Rosemary.EvalProc = { state: Ram2State _ NARROW[stateAny]; n: NAT _ state.size; SELECT TRUE FROM p[state.enW].l#L AND Ports.HasX[p[state.wAdr].ls] => { FOR i: NAT IN [0..n) DO Ports.SetLS[state.val[i], X] ENDLOOP; }; p[state.enW].l=X AND ~Ports.HasX[p[state.wAdr].ls] => { writeAdr: CARDINAL _ Ports.LSToC[p[state.wAdr].ls]; IF writeAdr IN [0..n) THEN Ports.SetLS[state.val[writeAdr], X] }; p[state.enW].l=H AND ~Ports.HasX[p[state.wAdr].ls] => { writeAdr: CARDINAL _ Ports.LSToC[p[state.wAdr].ls]; IF writeAdr IN [0..n) THEN Ports.CopyLS[from: p[state.input].ls, to: state.val[writeAdr]]; }; ENDCASE => NULL; IF Ports.HasX[p[state.rAdr].ls] THEN Ports.SetLS[p[state.output].ls, X] ELSE { readAdr: CARDINAL _ Ports.LSToC[p[state.rAdr].ls]; IF readAdr NOT IN [0..n) THEN Ports.SetLS[p[state.output].ls, X]; Ports.CopyLS[from: state.val[readAdr], to: p[state.output].ls]; }; }; END. Ram2MCName: ROPE = Rosemary.Register[roseClassName: "Ram2MC", init: Ram2MCInit, evalSimple: Ram2MCSimple]; Ram2MC: PUBLIC PROC [b, n: NAT] RETURNS [ct: CellType] = { a: NAT _ BitOps.NBits[n]; -- number of address bits IF b=0 OR n=0 THEN LogicUtils.Error["Please provide parameters for ram2MC"]; ct _ CoreClasses.CreateUnspecified[name: Ram2MCName, public: Wires["Vdd", "Gnd", Seq["Input", b], Seq["Output", b], Seq["RAdr", a], Seq["WAdr", a], "enW", "CK"]]; [] _ Rosemary.BindCellType[cellType: ct, roseClassName: Ram2MCName]; CoreProperties.PutCellTypeProp[ct, $n, NEW[NAT _ n]]; [] _ CoreFlat.CellTypeCutLabels[ct, logicCutSet]; Ports.InitPorts[ct, l, none, "Vdd", "Gnd", "enW", "CK"]; Ports.InitPorts[ct, ls, none, "Input", "RAdr", "WAdr"]; Ports.InitPorts[ct, ls, drive, "Output"]; }; Ram2MCState: TYPE = REF Ram2MCStateRec; Ram2MCStateRec: TYPE = RECORD [ input, output, rAdr, wAdr, enW, ck: NAT _ LAST[NAT], val: SEQUENCE size: NAT OF Ports.LevelSequence]; Ram2MCInit: Rosemary.InitProc = { n: NAT _ NARROW[CoreProperties.GetCellTypeProp[cellType, $n], REF NAT]^; b: NAT; state: Ram2MCState _ NEW[Ram2MCStateRec[n]]; [state.input, state.output, state.rAdr, state.wAdr, state.enW, state.ck] _ Ports.PortIndexes[cellType.public, "Input", "Output", "RAdr", "WAdr", "enW", "CK"]; b _ p[state.input].ls.size; FOR i: NAT IN [0..n) DO state.val[i] _ NEW[Ports.LevelSequenceRec[b]]; Ports.SetLS[state.val[i], X]; ENDLOOP; stateAny _ state; }; Ram2MCSimple: Rosemary.EvalProc = { state: Ram2MCState _ NARROW[stateAny]; n: NAT _ state.size; IF Ports.HasX[p[state.rAdr].ls] THEN Ports.SetLS[p[state.output].ls, X] ELSE { readAdr: CARDINAL _ Ports.LSToC[p[state.rAdr].ls]; IF readAdr NOT IN [0..n) THEN Ports.SetLS[p[state.output].ls, X]; Ports.CopyLS[from: state.val[readAdr], to: p[state.output].ls]; }; IF p[state.ck].l=H THEN RETURN; -- no write when clock is high SELECT TRUE FROM p[state.enW].l#L AND Ports.HasX[p[state.wAdr].ls] => { FOR i: NAT IN [0..n) DO Ports.SetLS[state.val[i], X] ENDLOOP; }; p[state.enW].l=X AND ~Ports.HasX[p[state.wAdr].ls] => { writeAdr: CARDINAL _ Ports.LSToC[p[state.wAdr].ls]; IF writeAdr IN [0..n) THEN Ports.SetLS[state.val[writeAdr], X] }; p[state.enW].l=H AND ~Ports.HasX[p[state.wAdr].ls] => { writeAdr: CARDINAL _ Ports.LSToC[p[state.wAdr].ls]; IF writeAdr IN [0..n) THEN Ports.CopyLS[from: p[state.input].ls, to: state.val[writeAdr]]; }; ENDCASE => NULL; }; Array: PROC [b, n: NAT, sameSide: BOOL, cx: Sisyph.Context] RETURNS [ct: CellType] ~ { wFiller: CellType _ Sisyph.ES["wFiller.sch", cx]; writeWLDr: CellType _ Sisyph.ES["writeWLDr.sch", cx]; flipWriteWLDr: CellType _ PWCore.RotateCellType[writeWLDr, $FlipY]; topCell: CellType _ Sisyph.ES[IF sameSide THEN "readWriteTop.sch" ELSE "writeTop.sch", cx]; ramCell: CellType _ Sisyph.ES["ram2Cell.sch", cx]; flipYRamCell: CellType _ PWCore.RotateCellType[ramCell, $FlipY]; bottomCell: CellType _ Sisyph.ES[IF sameSide THEN "readWriteBottom.sch" ELSE "readBottom.sch", cx]; rFiller: CellType _ Sisyph.ES["rFiller.sch", cx]; readWLDr: CellType _ Sisyph.ES["readWLDr.sch", cx]; flipReadWLDr: CellType _ PWCore.RotateCellType[readWLDr, $FlipY]; in: Wire _ Seq["Input", b]; out: Wire _ Seq["Output", b]; rNorOut: Wire _ Seq["rNorOut", n]; wNorOut: Wire _ Seq["wNorOut", n]; tileArray: TilingClass.TileArray _ NEW[TilingClass.TileArrayRec[n+2]]; FOR row: NAT IN [0..n+2) DO -- create the rows tileArray[row] _ NEW[TilingClass.TileRowRec[b+2]]; ENDLOOP; tileArray[0][0] _ NEW[TilingClass.TileRec _ [ type: PWCore.RotateCellType[wFiller, $FlipY], renaming: LIST[ ["enW", "enW"], ["Gnd", "Gnd"], ["Vdd", "Vdd"] ]]]; FOR row: NAT IN [0..n) DO -- the write word-line drivers tileArray[row+1][0] _ NEW[TilingClass.TileRec _ [ type: IF BitOps.EVEN[row] THEN writeWLDr ELSE flipWriteWLDr, renaming: LIST[ ["Gnd", "Gnd"], ["Vdd", "Vdd"], ["norOut", wNorOut[row]]]]]; ENDLOOP; tileArray[n+1][0] _ NEW[TilingClass.TileRec _ [ type: wFiller, renaming: LIST[ ["enW", "enW"], ["Gnd", "Gnd"], ["Vdd", "Vdd"] ]]]; FOR i: NAT IN [0..b) DO -- column 0 is on the left tileArray[0][i+1] _ NEW[TilingClass.TileRec _ [ type: bottomCell, renaming: IF ~sameSide THEN LIST[ ["Output", out[i]], ["Gnd", "Gnd"], ["Vdd", "Vdd"] ] ELSE LIST[ ["Input", in[i]], ["Output", out[i]], ["Gnd", "Gnd"], ["Vdd", "Vdd"] ]]]; FOR row: NAT IN [0..n) DO tileArray[row+1][i+1] _ NEW[TilingClass.TileRec _ [ type: IF BitOps.EVEN[row] THEN ramCell ELSE flipYRamCell, renaming: LIST[ ["Gnd", "Gnd"], ["Vdd", "Vdd"] ]]]; ENDLOOP; tileArray[n+1][i+1] _ NEW[TilingClass.TileRec _ [ type: topCell, renaming: IF sameSide THEN LIST[ ["Gnd", "Gnd"], ["Vdd", "Vdd"] ] ELSE LIST[ ["Input", in[i]], ["Gnd", "Gnd"], ["Vdd", "Vdd"] ]]]; ENDLOOP; tileArray[0][b+1] _ NEW[TilingClass.TileRec _ [ type: PWCore.RotateCellType[rFiller, $FlipY], renaming: LIST[ ["Gnd", "Gnd"], ["Vdd", "Vdd"] ]]]; FOR row: NAT IN [0..n) DO -- the read word-line drivers tileArray[row+1][b+1] _ NEW[TilingClass.TileRec _ [ type: IF BitOps.EVEN[row] THEN readWLDr ELSE flipReadWLDr, renaming: LIST[ ["Gnd", "Gnd"], ["Vdd", "Vdd"], ["norOut", rNorOut[row]]]]]; ENDLOOP; tileArray[n+1][b+1] _ NEW[TilingClass.TileRec _ [ type: rFiller, renaming: LIST[ ["Gnd", "Gnd"], ["Vdd", "Vdd"] ]]]; ct _ TilingClass.CreateTiling[ name: "RamArray", public: Wires["Vdd", "Gnd", in, out, rNorOut, wNorOut, "enW"], tileArray: tileArray, neighborX: TilingClass.LayoutNeighborX, neighborY: TilingClass.LayoutNeighborY ]; }; Extract: PUBLIC PROC [schName: ROPE, cx: Sisyph.Context, parms: LIST OF LogicUtils.Value _ NIL] RETURNS [ct: CellType] ~ { tmpCx: Sisyph.Context; IF cx=NIL THEN ERROR; tmpCx _ IF parms=NIL THEN cx ELSE Sisyph.Copy[cx]; WHILE parms#NIL DO Sisyph.Store[tmpCx, parms.first.name, NEW[INT _ parms.first.val]]; parms _ parms.rest; ENDLOOP; ct _ Sisyph.ES[schName, tmpCx]; }; Ram2Name: ROPE = Rosemary.Register[roseClassName: "Ram2", init: Ram2Init, evalSimple: Ram2Simple]; Ram2: PUBLIC PROC [b, n: NAT, sameSide, short: BOOL _ FALSE] RETURNS [ct: CellType] = { a: NAT _ BitOps.NBits[n]; -- number of address bits IF b=0 OR n=0 THEN LogicUtils.Error["Please provide parameters for ram2"]; IF BitOps.ODD[n] THEN n_n+1; -- number of words must be even IF short AND BitOps.ODD[b] THEN LogicUtils.Error["b must be even for short ram"]; ct _ (IF short THEN RamAssemblyShort ELSE RamAssembly)[b, n, sameSide]; [] _ Rosemary.BindCellType[cellType: ct, roseClassName: Ram2Name]; CoreProperties.PutCellTypeProp[ct, $n, NEW[NAT _ n]]; [] _ CoreFlat.CellTypeCutLabels[ct, logicCutSet, "Memory"]; Ports.InitPorts[ct, l, none, "Vdd", "Gnd", "enW"]; Ports.InitPorts[ct, ls, none, "Input", "RAdr", "WAdr"]; Ports.InitPorts[ct, ls, drive, "Output"]; }; Ram2State: TYPE = REF Ram2StateRec; Ram2StateRec: TYPE = RECORD [ input, output, rAdr, wAdr, enW: NAT _ LAST[NAT], val: SEQUENCE size: NAT OF Ports.LevelSequence]; Ram2Init: Rosemary.InitProc = { n: NAT _ NARROW[CoreProperties.GetCellTypeProp[cellType, $n], REF NAT]^; b: NAT; state: Ram2State _ NEW[Ram2StateRec[n]]; [state.input, state.output, state.rAdr, state.wAdr, state.enW] _ Ports.PortIndexes[cellType.public, "Input", "Output", "RAdr", "WAdr", "enW"]; b _ p[state.input].ls.size; FOR i: NAT IN [0..n) DO state.val[i] _ NEW[Ports.LevelSequenceRec[b]]; Ports.SetLS[state.val[i], X]; ENDLOOP; stateAny _ state; }; Ram2Simple: Rosemary.EvalProc = { state: Ram2State _ NARROW[stateAny]; n: NAT _ state.size; SELECT TRUE FROM p[state.enW].l#L AND Ports.HasX[p[state.wAdr].ls] => { FOR i: NAT IN [0..n) DO Ports.SetLS[state.val[i], X] ENDLOOP; }; p[state.enW].l=X AND ~Ports.HasX[p[state.wAdr].ls] => { writeAdr: CARDINAL _ Ports.LSToC[p[state.wAdr].ls]; IF writeAdr IN [0..n) THEN Ports.SetLS[state.val[writeAdr], X] }; p[state.enW].l=H AND ~Ports.HasX[p[state.wAdr].ls] => { writeAdr: CARDINAL _ Ports.LSToC[p[state.wAdr].ls]; IF writeAdr IN [0..n) THEN Ports.CopyLS[from: p[state.input].ls, to: state.val[writeAdr]]; }; ENDCASE => NULL; IF Ports.HasX[p[state.rAdr].ls] THEN Ports.SetLS[p[state.output].ls, X] ELSE { readAdr: CARDINAL _ Ports.LSToC[p[state.rAdr].ls]; IF readAdr NOT IN [0..n) THEN Ports.SetLS[p[state.output].ls, X]; Ports.CopyLS[from: state.val[readAdr], to: p[state.output].ls]; }; }; ramDesign, ramShortDesign: CD.Design; ramCx: Sisyph.Context _ NIL; ramShortCx: Sisyph.Context _ NIL; Array: PROC [b, n: NAT, sameSide: BOOL, cx: Sisyph.Context] RETURNS [ct: CellType] ~ { wFiller: CellType _ Sisyph.ES["wFiller.sch", cx]; writeWLDr: CellType _ Sisyph.ES["writeWLDr.sch", cx]; flipWriteWLDr: CellType _ PWCore.RotateCellType[writeWLDr, $FlipY]; topCells: CellType _ Extract[IF sameSide THEN "readWriteTops.sch" ELSE "writeTops.sch", cx, LIST[["b", b]]]; ramCells: CellType _ Extract["ram2Cells.sch", cx, LIST[["b", b]]]; flipYRamCells: CellType _ PWCore.RotateCellType[ramCells, $FlipY]; bottomCells: CellType _ Extract[IF sameSide THEN "readWriteBottoms.sch" ELSE "readBottoms.sch", cx, LIST[["b", b]]]; rFiller: CellType _ Sisyph.ES["rFiller.sch", cx]; readWLDr: CellType _ Sisyph.ES["readWLDr.sch", cx]; flipReadWLDr: CellType _ PWCore.RotateCellType[readWLDr, $FlipY]; in: Wire _ Seq["Input", b]; out: Wire _ Seq["Output", b]; rNorOut: Wire _ Seq["rNorOut", n]; wNorOut: Wire _ Seq["wNorOut", n]; tileArray: TilingClass.TileArray _ NEW[TilingClass.TileArrayRec[n+2]]; FOR row: NAT IN [0..n+2) DO -- create the rows tileArray[row] _ NEW[TilingClass.TileRowRec[3]]; ENDLOOP; tileArray[0][0] _ NEW[TilingClass.TileRec _ [ type: PWCore.RotateCellType[wFiller, $FlipY], renaming: LIST[ ["enW", "enW"], ["Gnd", "Gnd"], ["Vdd", "Vdd"] ]]]; FOR row: NAT IN [0..n) DO -- the write word-line drivers tileArray[row+1][0] _ NEW[TilingClass.TileRec _ [ type: IF BitOps.EVEN[row] THEN writeWLDr ELSE flipWriteWLDr, renaming: LIST[ ["Gnd", "Gnd"], ["Vdd", "Vdd"], ["norOut", wNorOut[row]]]]]; ENDLOOP; tileArray[n+1][0] _ NEW[TilingClass.TileRec _ [ type: wFiller, renaming: LIST[ ["enW", "enW"], ["Gnd", "Gnd"], ["Vdd", "Vdd"] ]]]; tileArray[0][1] _ NEW[TilingClass.TileRec _ [ type: bottomCells, renaming: IF ~sameSide THEN LIST[ ["Output", out], ["Gnd", "Gnd"], ["Vdd", "Vdd"] ] ELSE LIST[ ["Input", in], ["Output", out], ["Gnd", "Gnd"], ["Vdd", "Vdd"] ]]]; FOR row: NAT IN [0..n) DO tileArray[row+1][1] _ NEW[TilingClass.TileRec _ [ type: IF BitOps.EVEN[row] THEN ramCells ELSE flipYRamCells, renaming: LIST[ ["Gnd", "Gnd"], ["Vdd", "Vdd"] ]]]; ENDLOOP; tileArray[n+1][1] _ NEW[TilingClass.TileRec _ [ type: topCells, renaming: IF sameSide THEN LIST[ ["Gnd", "Gnd"], ["Vdd", "Vdd"] ] ELSE LIST[ ["Input", in], ["Gnd", "Gnd"], ["Vdd", "Vdd"] ]]]; tileArray[0][2] _ NEW[TilingClass.TileRec _ [ type: PWCore.RotateCellType[rFiller, $FlipY], renaming: LIST[ ["Gnd", "Gnd"], ["Vdd", "Vdd"] ]]]; FOR row: NAT IN [0..n) DO -- the read word-line drivers tileArray[row+1][2] _ NEW[TilingClass.TileRec _ [ type: IF BitOps.EVEN[row] THEN readWLDr ELSE flipReadWLDr, renaming: LIST[ ["Gnd", "Gnd"], ["Vdd", "Vdd"], ["norOut", rNorOut[row]]]]]; ENDLOOP; tileArray[n+1][2] _ NEW[TilingClass.TileRec _ [ type: rFiller, renaming: LIST[ ["Gnd", "Gnd"], ["Vdd", "Vdd"] ]]]; ct _ TilingClass.CreateTiling[ name: "RamArray", public: Wires["Vdd", "Gnd", in, out, rNorOut, wNorOut, "enW"], tileArray: tileArray, neighborX: TilingClass.LayoutNeighborX, neighborY: TilingClass.LayoutNeighborY ]; }; Decoder: PROC [n: NAT, adrOnTop: BOOL, cx: Sisyph.Context] RETURNS [ct: CellType] ~ { dec0: CellType _ Sisyph.ES["dec0.sch", cx]; dec1: CellType _ PWCore.RotateCellType[dec0, $FlipX]; decf0: CellType _ PWCore.RotateCellType[dec0, $FlipY]; decf1: CellType _ PWCore.RotateCellType[dec0, $Rot180]; adrAmpli: CellType _ Sisyph.ES["adrAmpli.sch", cx]; adrInv: CellType _ Sisyph.ES["adrInv.sch", cx]; botCt: CellType _ IF adrOnTop THEN PWCore.RotateCellType[adrInv, $FlipY] ELSE adrAmpli; topCt: CellType _ IF adrOnTop THEN PWCore.RotateCellType[adrAmpli, $FlipY] ELSE adrInv; a: NAT _ BitOps.NBits[n]; -- number of address bits norOut: Wire _ Seq["norOut", n]; adr: Wire _ Seq["adr", a]; tileArray: TilingClass.TileArray _ NEW[TilingClass.TileArrayRec[n+2]]; FOR row: NAT IN [0..n+2) DO -- create the rows tileArray[row] _ NEW[TilingClass.TileRowRec[a]]; ENDLOOP; FOR i: NAT IN [0..a) DO -- column 0 is on the left tileArray[0][i] _ NEW[TilingClass.TileRec _ [ type: botCt, renaming: IF adrOnTop THEN LIST[ ["Gnd", "Gnd"], ["Vdd", "Vdd"] ] ELSE LIST[ ["ad", adr[i]], ["Gnd", "Gnd"], ["Vdd", "Vdd"] ]]]; FOR row: NAT IN [0..n) DO tileArray[row+1][i] _ NEW[TilingClass.TileRec _ [ type: SELECT TRUE FROM BitOps.EVEN[row] AND BitOps.EBFW[row, 15-i] => dec0, BitOps.EVEN[row] AND ~BitOps.EBFW[row, 15-i] => dec1, BitOps.ODD[row] AND BitOps.EBFW[row, 15-i] => decf0, BitOps.ODD[row] AND ~BitOps.EBFW[row, 15-i] => decf1, ENDCASE => ERROR, renaming: LIST[ ["Gnd", "Gnd"], ["Vdd", "Vdd"], ["norOut", norOut[row]]]]]; ENDLOOP; tileArray[n+1][i] _ NEW[TilingClass.TileRec _ [ type: topCt, renaming: IF adrOnTop THEN LIST[ ["ad", adr[i]], ["Gnd", "Gnd"], ["Vdd", "Vdd"] ] ELSE LIST[ ["Gnd", "Gnd"], ["Vdd", "Vdd"] ]]]; ENDLOOP; ct _ TilingClass.CreateTiling[ name: "Decoder", public: Wires[adr, norOut, "Vdd", "Gnd"], tileArray: tileArray, neighborX: TilingClass.LayoutNeighborX, neighborY: TilingClass.LayoutNeighborY ]; }; RamAssembly: PROC [b, n: NAT, sameSide: BOOL] RETURNS [ct: CellType] ~ { a: NAT _ BitOps.NBits[n]; -- number of address bits array, writeDecoder, readDecoder: CellType; wNorOut, rNorOut, wAdr, rAdr: Wire; IF ramDesign=NIL THEN ramDesign _ PW.OpenDesign["LogicRam2"]; ramCx _ Sisyph.Create[ramDesign]; array _ Extract[IF sameSide THEN "ArraySameSide.sch" ELSE "ArrayThrough.sch", ramCx, LIST[["b", b], ["n", n]]]; writeDecoder _ Decoder[n: n, adrOnTop: ~sameSide, cx: ramCx]; readDecoder _ Decoder[n: n, adrOnTop: FALSE, cx: ramCx]; wNorOut _ Seq["wNorOut", n]; rNorOut _ Seq["rNorOut", n]; wAdr _ Seq["WAdr", a]; rAdr _ Seq["RAdr", a]; ct _ Cell[ public: Wires["Vdd", "Gnd", Seq["Input", b], Seq["Output", b], rAdr, wAdr, "enW"], onlyInternal: Wires[wNorOut, rNorOut], instances: LIST[ Instance[writeDecoder, ["adr", wAdr], ["norOut", wNorOut]], Instance[array, ["Input", "Input"], ["Output", "Output"], ["wNorOut", wNorOut], ["rNorOut", rNorOut]], Instance[readDecoder, ["adr", rAdr], ["norOut", rNorOut]] ] ]; PWCore.SetAbutX[ct]; }; ArrayShort: PROC [b, n: NAT, sameSide: BOOL, cx: Sisyph.Context] RETURNS [ct: CellType] ~ { wFiller: CellType _ Sisyph.ES["wFiller.sch", cx]; writeWLDr: CellType _ Sisyph.ES["writeWLDr.sch", cx]; flipWriteWLDr: CellType _ PWCore.RotateCellType[writeWLDr, $FlipY]; topCell: CellType _ Sisyph.ES[IF sameSide THEN "readWriteTop.sch" ELSE "writeTop.sch", cx]; flipXTopCell: CellType _ PWCore.RotateCellType[topCell, $FlipX]; ramCell: CellType _ Sisyph.ES["ram2Cell.sch", cx]; flipYRamCell: CellType _ PWCore.RotateCellType[ramCell, $FlipY]; flipXRamCell: CellType _ PWCore.RotateCellType[ramCell, $FlipX]; flipXYRamCell: CellType _ PWCore.RotateCellType[ramCell, $Rot180]; bottomCell: CellType _ Sisyph.ES[IF sameSide THEN "readWriteBottom.sch" ELSE "readBottom.sch", cx]; flipXBottomCell: CellType _ PWCore.RotateCellType[bottomCell, $FlipX]; rFiller: CellType _ Sisyph.ES["rFiller.sch", cx]; readWLDr: CellType _ Sisyph.ES["readWLDr.sch", cx]; flipReadWLDr: CellType _ PWCore.RotateCellType[readWLDr, $FlipY]; in: Wire _ Seq["Input", b]; out: Wire _ Seq["Output", b]; rNorOut: Wire _ Seq["rNorOut", n]; wNorOut: Wire _ Seq["wNorOut", n]; tileArray: TilingClass.TileArray _ NEW[TilingClass.TileArrayRec[n+2]]; FOR row: NAT IN [0..n+2) DO -- create the rows tileArray[row] _ NEW[TilingClass.TileRowRec[b+2]]; ENDLOOP; tileArray[0][0] _ NEW[TilingClass.TileRec _ [ type: PWCore.RotateCellType[wFiller, $FlipY], renaming: LIST[ ["enW", "enW"], ["Gnd", "Gnd"], ["Vdd", "Vdd"] ]]]; FOR row: NAT IN [0..n) DO -- the write word-line drivers tileArray[row+1][0] _ NEW[TilingClass.TileRec _ [ type: IF BitOps.EVEN[row] THEN writeWLDr ELSE flipWriteWLDr, renaming: LIST[ ["Gnd", "Gnd"], ["Vdd", "Vdd"], ["norOut", wNorOut[row]]]]]; ENDLOOP; tileArray[n+1][0] _ NEW[TilingClass.TileRec _ [ type: wFiller, renaming: LIST[ ["enW", "enW"], ["Gnd", "Gnd"], ["Vdd", "Vdd"] ]]]; FOR i: NAT IN [0..b) DO -- column 0 is on the left tileArray[0][i+1] _ NEW[TilingClass.TileRec _ [ type: IF BitOps.EVEN[i] THEN bottomCell ELSE flipXBottomCell, renaming: IF ~sameSide THEN LIST[ ["Output", out[i]], ["Gnd", "Gnd"], ["Vdd", "Vdd"] ] ELSE LIST[ ["Input", in[i]], ["Output", out[i]], ["Gnd", "Gnd"], ["Vdd", "Vdd"] ]]]; FOR row: NAT IN [0..n) DO tileArray[row+1][i+1] _ NEW[TilingClass.TileRec _ [ type: SELECT TRUE FROM BitOps.EVEN[row] AND BitOps.EVEN[i] => ramCell, BitOps.ODD[row] AND BitOps.EVEN[i] => flipYRamCell, BitOps.EVEN[row] AND BitOps.ODD[i] => flipXRamCell, BitOps.ODD[row] AND BitOps.ODD[i] => flipXYRamCell, ENDCASE => ERROR, renaming: LIST[ ["Gnd", "Gnd"], ["Vdd", "Vdd"] ]]]; ENDLOOP; tileArray[n+1][i+1] _ NEW[TilingClass.TileRec _ [ type: IF BitOps.EVEN[i] THEN topCell ELSE flipXTopCell, renaming: IF sameSide THEN LIST[ ["Gnd", "Gnd"], ["Vdd", "Vdd"] ] ELSE LIST[ ["Input", in[i]], ["Gnd", "Gnd"], ["Vdd", "Vdd"] ]]]; ENDLOOP; tileArray[0][b+1] _ NEW[TilingClass.TileRec _ [ type: PWCore.RotateCellType[rFiller, $FlipY], renaming: LIST[ ["Gnd", "Gnd"], ["Vdd", "Vdd"] ]]]; FOR row: NAT IN [0..n) DO -- the read word-line drivers tileArray[row+1][b+1] _ NEW[TilingClass.TileRec _ [ type: IF BitOps.EVEN[row] THEN readWLDr ELSE flipReadWLDr, renaming: LIST[ ["Gnd", "Gnd"], ["Vdd", "Vdd"], ["norOut", rNorOut[row]]]]]; ENDLOOP; tileArray[n+1][b+1] _ NEW[TilingClass.TileRec _ [ type: rFiller, renaming: LIST[ ["Gnd", "Gnd"], ["Vdd", "Vdd"] ]]]; ct _ TilingClass.CreateTiling[ name: "RamArray", public: Wires["Vdd", "Gnd", in, out, rNorOut, wNorOut, "enW"], tileArray: tileArray, neighborX: TilingClass.LayoutNeighborX, neighborY: TilingClass.LayoutNeighborY ]; }; RamAssemblyShort: PROC [b, n: NAT, sameSide: BOOL] RETURNS [ct: CellType] ~ { a: NAT _ BitOps.NBits[n]; -- number of address bits array, writeDecoder, readDecoder: CellType; wNorOut, rNorOut, wAdr, rAdr: Wire; IF ramShortDesign=NIL THEN ramShortDesign _ PW.OpenDesign["LogicRam2Short"]; ramShortCx _ Sisyph.Create[ramShortDesign]; array _ ArrayShort[b, n, sameSide, ramShortCx]; writeDecoder _ Decoder[n: n, adrOnTop: ~sameSide, cx: ramShortCx]; readDecoder _ Decoder[n: n, adrOnTop: FALSE, cx: ramShortCx]; wNorOut _ Seq["wNorOut", n]; rNorOut _ Seq["rNorOut", n]; wAdr _ Seq["WAdr", a]; rAdr _ Seq["RAdr", a]; ct _ Cell[ public: Wires["Vdd", "Gnd", Seq["Input", b], Seq["Output", b], rAdr, wAdr, "enW"], onlyInternal: Wires[wNorOut, rNorOut], instances: LIST[ Instance[writeDecoder, ["adr", wAdr], ["norOut", wNorOut]], Instance[array, ["Input", "Input"], ["Output", "Output"], ["wNorOut", wNorOut], ["rNorOut", rNorOut]], Instance[readDecoder, ["adr", rAdr], ["norOut", rNorOut]] ] ]; PWCore.SetAbutX[ct]; }; 6ΨLogicMemImpl.mesa Copyright c 1986 by Xerox Corporation. All rights reserved. Last Edited by: Louis Monier April 28, 1988 5:02:09 pm PDT Last Edited by: Gasbarro December 17, 1986 10:54:15 am PST Jean-Marc Frailong September 13, 1987 9:53:09 pm PDT Bertrand Serlet April 8, 1987 11:20:05 pm PDT Barth, October 10, 1986 5:33:58 pm PDT Ram2 Extracts from the Logic library with the given (INT) parameters. -- from the bottom up: botCt, rows 0, ... n-1, topCt -- The write happens before the read, so that if the read and write addresses are equal, the value gets transfered from the Input to the Output port. -- Layout of the original ram -- Array of size n+2 by b+2: read on bottom, write on top, writeWL driver on the left, and readWL driver on the right Array: PROC [b, n: NAT, sameSide: BOOL, cx: Sisyph.Context] RETURNS [ct: CellType] ~ { wFiller: CellType _ Sisyph.ES["wFiller.sch", cx]; writeWLDr: CellType _ Sisyph.ES["writeWLDr.sch", cx]; flipWriteWLDr: CellType _ PWCore.RotateCellType[writeWLDr, $FlipY]; topCells: CellType _ Extract[IF sameSide THEN "readWriteTops.sch" ELSE "writeTops.sch", cx, LIST[["b", b]]]; ramCells: CellType _ Extract["ram2Cells.sch", cx, LIST[["b", b]]]; flipYRamCells: CellType _ PWCore.RotateCellType[ramCells, $FlipY]; bottomCells: CellType _ Extract[IF sameSide THEN "readWriteBottoms.sch" ELSE "readBottoms.sch", cx, LIST[["b", b]]]; rFiller: CellType _ Sisyph.ES["rFiller.sch", cx]; readWLDr: CellType _ Sisyph.ES["readWLDr.sch", cx]; flipReadWLDr: CellType _ PWCore.RotateCellType[readWLDr, $FlipY]; in: Wire _ Seq["Input", b]; out: Wire _ Seq["Output", b]; rNorOut: Wire _ Seq["rNorOut", n]; wNorOut: Wire _ Seq["wNorOut", n]; tileArray: TilingClass.TileArray _ NEW[TilingClass.TileArrayRec[n+2]]; from the bottom up: read, rows 0, ... n-1, write FOR row: NAT IN [0..n+2) DO -- create the rows tileArray[row] _ NEW[TilingClass.TileRowRec[3]]; ENDLOOP; -- left row tileArray[0][0] _ NEW[TilingClass.TileRec _ [ type: PWCore.RotateCellType[wFiller, $FlipY], renaming: LIST[ ["enW", "enW"], ["Gnd", "Gnd"], ["Vdd", "Vdd"] ]]]; FOR row: NAT IN [0..n) DO -- the write word-line drivers tileArray[row+1][0] _ NEW[TilingClass.TileRec _ [ type: IF BitOps.EVEN[row] THEN writeWLDr ELSE flipWriteWLDr, renaming: LIST[ ["Gnd", "Gnd"], ["Vdd", "Vdd"], ["norOut", wNorOut[row]]]]]; ENDLOOP; tileArray[n+1][0] _ NEW[TilingClass.TileRec _ [ type: wFiller, renaming: LIST[ ["enW", "enW"], ["Gnd", "Gnd"], ["Vdd", "Vdd"] ]]]; -- main array tileArray[0][1] _ NEW[TilingClass.TileRec _ [ type: bottomCells, renaming: IF ~sameSide THEN LIST[ ["Output", out], ["Gnd", "Gnd"], ["Vdd", "Vdd"] ] ELSE LIST[ ["Input", in], ["Output", out], ["Gnd", "Gnd"], ["Vdd", "Vdd"] ]]]; FOR row: NAT IN [0..n) DO tileArray[row+1][1] _ NEW[TilingClass.TileRec _ [ type: IF BitOps.EVEN[row] THEN ramCells ELSE flipYRamCells, renaming: LIST[ ["Gnd", "Gnd"], ["Vdd", "Vdd"] ]]]; ENDLOOP; tileArray[n+1][1] _ NEW[TilingClass.TileRec _ [ type: topCells, renaming: IF sameSide THEN LIST[ ["Gnd", "Gnd"], ["Vdd", "Vdd"] ] ELSE LIST[ ["Input", in], ["Gnd", "Gnd"], ["Vdd", "Vdd"] ]]]; -- right row tileArray[0][2] _ NEW[TilingClass.TileRec _ [ type: PWCore.RotateCellType[rFiller, $FlipY], renaming: LIST[ ["Gnd", "Gnd"], ["Vdd", "Vdd"] ]]]; FOR row: NAT IN [0..n) DO -- the read word-line drivers tileArray[row+1][2] _ NEW[TilingClass.TileRec _ [ type: IF BitOps.EVEN[row] THEN readWLDr ELSE flipReadWLDr, renaming: LIST[ ["Gnd", "Gnd"], ["Vdd", "Vdd"], ["norOut", rNorOut[row]]]]]; ENDLOOP; tileArray[n+1][2] _ NEW[TilingClass.TileRec _ [ type: rFiller, renaming: LIST[ ["Gnd", "Gnd"], ["Vdd", "Vdd"] ]]]; ct _ TilingClass.CreateTiling[ name: "RamArray", public: Wires["Vdd", "Gnd", in, out, rNorOut, wNorOut, "enW"], tileArray: tileArray, neighborX: TilingClass.LayoutNeighborX, neighborY: TilingClass.LayoutNeighborY ]; }; RamAssembly: PROC [b, n: NAT, sameSide: BOOL] RETURNS [ct: CellType] ~ { a: NAT _ BitOps.NBits[n]; -- number of address bits array, writeDecoder, readDecoder: CellType; wNorOut, rNorOut, wAdr, rAdr: Wire; IF ramDesign=NIL THEN ramDesign _ PW.OpenDesign["LogicRam2"]; ramCx _ Sisyph.Create[ramDesign]; array _ Array[b, n, sameSide, ramCx]; writeDecoder _ Decoder[n: n, adrOnTop: ~sameSide, cx: ramCx]; readDecoder _ Decoder[n: n, adrOnTop: FALSE, cx: ramCx]; wNorOut _ Seq["wNorOut", n]; rNorOut _ Seq["rNorOut", n]; wAdr _ Seq["WAdr", a]; rAdr _ Seq["RAdr", a]; ct _ Cell[ public: Wires["Vdd", "Gnd", Seq["Input", b], Seq["Output", b], rAdr, wAdr, "enW"], onlyInternal: Wires[wNorOut, rNorOut], instances: LIST[ Instance[writeDecoder, ["adr", wAdr], ["norOut", wNorOut]], Instance[array, ["Input", "Input"], ["Output", "Output"], ["wNorOut", wNorOut], ["rNorOut", rNorOut]], Instance[readDecoder, ["adr", rAdr], ["norOut", rNorOut]] ] ]; PWCore.SetAbutX[ct]; }; -- Layout of the short ram ArrayShort: PROC [b, n: NAT, sameSide: BOOL, cx: Sisyph.Context] RETURNS [ct: CellType] ~ { wFiller: CellType _ Sisyph.ES["wFiller.sch", cx]; writeWLDr: CellType _ Sisyph.ES["writeWLDr.sch", cx]; flipWriteWLDr: CellType _ PWCore.RotateCellType[writeWLDr, $FlipY]; topCell: CellType _ Sisyph.ES[IF sameSide THEN "readWriteTop.sch" ELSE "writeTop.sch", cx]; flipXTopCell: CellType _ PWCore.RotateCellType[topCell, $FlipX]; ramCell: CellType _ Sisyph.ES["ram2Cell.sch", cx]; flipYRamCell: CellType _ PWCore.RotateCellType[ramCell, $FlipY]; flipXRamCell: CellType _ PWCore.RotateCellType[ramCell, $FlipX]; flipXYRamCell: CellType _ PWCore.RotateCellType[ramCell, $Rot180]; bottomCell: CellType _ Sisyph.ES[IF sameSide THEN "readWriteBottom.sch" ELSE "readBottom.sch", cx]; flipXBottomCell: CellType _ PWCore.RotateCellType[bottomCell, $FlipX]; rFiller: CellType _ Sisyph.ES["rFiller.sch", cx]; readWLDr: CellType _ Sisyph.ES["readWLDr.sch", cx]; flipReadWLDr: CellType _ PWCore.RotateCellType[readWLDr, $FlipY]; in: Wire _ Seq["Input", b]; out: Wire _ Seq["Output", b]; rNorOut: Wire _ Seq["rNorOut", n]; wNorOut: Wire _ Seq["wNorOut", n]; tileArray: TilingClass.TileArray _ NEW[TilingClass.TileArrayRec[n+2]]; from the bottom up: read, rows 0, ... n-1, write FOR row: NAT IN [0..n+2) DO -- create the rows tileArray[row] _ NEW[TilingClass.TileRowRec[b+2]]; ENDLOOP; -- left row tileArray[0][0] _ NEW[TilingClass.TileRec _ [ type: PWCore.RotateCellType[wFiller, $FlipY], renaming: LIST[ ["enW", "enW"], ["Gnd", "Gnd"], ["Vdd", "Vdd"] ]]]; FOR row: NAT IN [0..n) DO -- the write word-line drivers tileArray[row+1][0] _ NEW[TilingClass.TileRec _ [ type: IF BitOps.EVEN[row] THEN writeWLDr ELSE flipWriteWLDr, renaming: LIST[ ["Gnd", "Gnd"], ["Vdd", "Vdd"], ["norOut", wNorOut[row]]]]]; ENDLOOP; tileArray[n+1][0] _ NEW[TilingClass.TileRec _ [ type: wFiller, renaming: LIST[ ["enW", "enW"], ["Gnd", "Gnd"], ["Vdd", "Vdd"] ]]]; -- main array FOR i: NAT IN [0..b) DO -- column 0 is on the left tileArray[0][i+1] _ NEW[TilingClass.TileRec _ [ type: IF BitOps.EVEN[i] THEN bottomCell ELSE flipXBottomCell, renaming: IF ~sameSide THEN LIST[ ["Output", out[i]], ["Gnd", "Gnd"], ["Vdd", "Vdd"] ] ELSE LIST[ ["Input", in[i]], ["Output", out[i]], ["Gnd", "Gnd"], ["Vdd", "Vdd"] ]]]; FOR row: NAT IN [0..n) DO tileArray[row+1][i+1] _ NEW[TilingClass.TileRec _ [ type: SELECT TRUE FROM BitOps.EVEN[row] AND BitOps.EVEN[i] => ramCell, BitOps.ODD[row] AND BitOps.EVEN[i] => flipYRamCell, BitOps.EVEN[row] AND BitOps.ODD[i] => flipXRamCell, BitOps.ODD[row] AND BitOps.ODD[i] => flipXYRamCell, ENDCASE => ERROR, renaming: LIST[ ["Gnd", "Gnd"], ["Vdd", "Vdd"] ]]]; ENDLOOP; tileArray[n+1][i+1] _ NEW[TilingClass.TileRec _ [ type: IF BitOps.EVEN[i] THEN topCell ELSE flipXTopCell, renaming: IF sameSide THEN LIST[ ["Gnd", "Gnd"], ["Vdd", "Vdd"] ] ELSE LIST[ ["Input", in[i]], ["Gnd", "Gnd"], ["Vdd", "Vdd"] ]]]; ENDLOOP; -- right row tileArray[0][b+1] _ NEW[TilingClass.TileRec _ [ type: PWCore.RotateCellType[rFiller, $FlipY], renaming: LIST[ ["Gnd", "Gnd"], ["Vdd", "Vdd"] ]]]; FOR row: NAT IN [0..n) DO -- the read word-line drivers tileArray[row+1][b+1] _ NEW[TilingClass.TileRec _ [ type: IF BitOps.EVEN[row] THEN readWLDr ELSE flipReadWLDr, renaming: LIST[ ["Gnd", "Gnd"], ["Vdd", "Vdd"], ["norOut", rNorOut[row]]]]]; ENDLOOP; tileArray[n+1][b+1] _ NEW[TilingClass.TileRec _ [ type: rFiller, renaming: LIST[ ["Gnd", "Gnd"], ["Vdd", "Vdd"] ]]]; ct _ TilingClass.CreateTiling[ name: "RamArray", public: Wires["Vdd", "Gnd", in, out, rNorOut, wNorOut, "enW"], tileArray: tileArray, neighborX: TilingClass.LayoutNeighborX, neighborY: TilingClass.LayoutNeighborY ]; }; RamAssemblyShort: PROC [b, n: NAT, sameSide: BOOL] RETURNS [ct: CellType] ~ { a: NAT _ BitOps.NBits[n]; -- number of address bits array, writeDecoder, readDecoder: CellType; wNorOut, rNorOut, wAdr, rAdr: Wire; IF ramShortDesign=NIL THEN ramShortDesign _ PW.OpenDesign["LogicRam2Short"]; ramShortCx _ Sisyph.Create[ramShortDesign]; array _ ArrayShort[b, n, sameSide, ramShortCx]; writeDecoder _ DecoderShort[n: n, adrOnTop: ~sameSide, cx: ramShortCx]; readDecoder _ DecoderShort[n: n, adrOnTop: FALSE, cx: ramShortCx]; wNorOut _ Seq["wNorOut", n]; rNorOut _ Seq["rNorOut", n]; wAdr _ Seq["WAdr", a]; rAdr _ Seq["RAdr", a]; ct _ Cell[ public: Wires["Vdd", "Gnd", Seq["Input", b], Seq["Output", b], rAdr, wAdr, "enW"], onlyInternal: Wires[wNorOut, rNorOut], instances: LIST[ Instance[writeDecoder, ["adr", wAdr], ["norOut", wNorOut]], Instance[array, ["Input", "Input"], ["Output", "Output"], ["wNorOut", wNorOut], ["rNorOut", rNorOut]], Instance[readDecoder, ["adr", rAdr], ["norOut", rNorOut]] ] ]; PWCore.SetAbutX[ct]; }; Fifo FifoName: ROPE = Rosemary.Register[roseClassName: "Fifo", init: FifoInit, evalSimple: FifoSimple]; Fifo: PUBLIC PROC [b, n, nbFreeNF: NAT] RETURNS [ct: CellType] = { ct _ CoreClasses.CreateUnspecified[name: FifoName, public: Wires["Vdd", "Gnd", "CK", Seq["Input", b], Seq["Output", b], "Load", "UnLoad", "Reset", "DataAv", "Full", "NF"]]; [] _ Rosemary.BindCellType[cellType: ct, roseClassName: FifoName]; CoreProperties.PutCellTypeProp[ct, $n, NEW[NAT _ n]]; CoreProperties.PutCellTypeProp[ct, $nbFreeNF, NEW[NAT _ nbFreeNF]]; [] _ CoreFlat.CellTypeCutLabels[ct, macroCutSet]; Ports.InitPorts[ct, l, none, "Vdd", "Gnd", "CK", "Load", "UnLoad", "Reset"]; Ports.InitPorts[ct, l, drive, "DataAv", "Full", "NF"]; Ports.InitPorts[ct, ls, none, "Input"]; Ports.InitPorts[ct, ls, drive, "Output"]; }; fifoDesignName: ROPE = "Fifo"; fifoDesign: CD.Design _ NIL; fifoCx: Sisyph.Context _ NIL; FifoArray: PROC [b, n: NAT] RETURNS [ct: CellType] ~ { cell, row: CellType; IF fifoDesign=NIL THEN fifoDesign _ PW.OpenDesign[fifoDesignName]; IF fifoCx=NIL THEN fifoCx _ Sisyph.Create[fifoDesign]; cell _ Sisyph.ES["fifoCell.sch", fifoCx]; PWCore.SetGet[cell, fifoDesign]; row _ SequenceCell[baseCell: cell, count: b, sequencePorts: Wires["bw", "br"]]; PWCore.SetLayout[row, $ReverseArrayX]; ct _ SequenceCell[baseCell: row, count: n, sequencePorts: Wires["read", "write", "nWrite"]]; PWCore.SetLayout[ct, $ReverseArrayY]; }; FifoWriteShReg: PROC [n: NAT] RETURNS [ct: CellType] ~ { }; FifoReadShReg: PROC [n: NAT] RETURNS [ct: CellType] ~ { }; FifoState: TYPE = REF FifoStateRec; FifoStateRec: TYPE = RECORD [ ck, in, out, load, unLoad, reset, dataAv, full, nearlyFull: NAT _ LAST[NAT], prevCK, loadSt, unloadSt, resetSt: Ports.Level _ X, master: Ports.LevelSequence, valid: BOOL _ FALSE, nbWords: INT _ 0, nbFreeNF: NAT _ 0, tail: NAT _ 0, -- "tail" points to the first word used (unless empty) head: NAT _ 0, -- "head" points to the first free word val: SEQUENCE size: NAT OF Ports.LevelSequence]; FifoInit: Rosemary.InitProc = { n: NAT _ NARROW[CoreProperties.GetCellTypeProp[cellType, $n], REF NAT]^; nbFreeNF: NAT _ NARROW[CoreProperties.GetCellTypeProp[cellType, $nbFreeNF], REF NAT]^; b: NAT; state: FifoState _ NEW[FifoStateRec[n]]; [state.ck, state.in, state.out, state.load, state.unLoad, state.reset, state.dataAv, state.full, state.nearlyFull] _ Ports.PortIndexes[cellType.public, "CK", "Input", "Output", "Load", "UnLoad", "Reset", "DataAv", "Full", "NF"]; b _ p[state.in].ls.size; state.nbFreeNF _ nbFreeNF; state.prevCK _ X; FOR i: NAT IN [0..n) DO state.val[i] _ NEW[Ports.LevelSequenceRec[b]]; Ports.SetLS[state.val[i], X]; ENDLOOP; state.master _ NEW[Ports.LevelSequenceRec[b]]; Ports.SetLS[state.master, X]; stateAny _ state; }; FifoSimple: Rosemary.EvalProc = { state: FifoState _ NARROW[stateAny]; {OPEN state; n: NAT _ size; SELECT TRUE FROM p[ck].l=L => { loadSt _ p[load].l; unloadSt _ p[unLoad].l; resetSt _ p[reset].l; Ports.CopyLS[from: p[state.in].ls, to: state.master] }; prevCK=L AND p[ck].l=H => SELECT TRUE FROM resetSt=H => { valid _ TRUE; nbWords _ 0; tail _ head _ 0; }; resetSt=X OR unloadSt=X OR loadSt=X => valid _ FALSE; ENDCASE => IF valid THEN { IF loadSt=H THEN { nbWords _ nbWords+1; Ports.CopyLS[from: state.master, to: val[head]]; head _ (head+1) MOD n; }; IF unloadSt=H THEN { nbWords _ nbWords-1; tail _ (tail+1) MOD n; }; }; ENDCASE; IF valid THEN { Ports.CopyLS[from: val[tail], to: p[out].ls]; p[dataAv].l _ IF nbWords>0 THEN H ELSE L; p[nearlyFull].l _ IF n-nbWords=n THEN H ELSE L; } ELSE { Ports.SetLS[p[out].ls, X]; p[dataAv].l _ p[nearlyFull].l _ p[full].l _ X; }; prevCK _ p[ck].l; IF nbWords>n THEN Rosemary.Stop["Fifo overflow"]; IF nbWords<0 THEN Rosemary.Stop["Fifo underflow"]; }}; Single-cycle write Ram2 from the bottom up: read, rows 0, ... n-1, write -- left row -- main array -- right row Ram2 Extracts from the Logic library with the given (INT) parameters. -- The write happens before the read, so that if the read and write addresses are equal, the value gets transfered from the Input to the Output port. -- Layout of the original ram -- Array of size n+2 by b+2: read on bottom, write on top, writeWL driver on the left, and readWL driver on the right from the bottom up: read, rows 0, ... n-1, write -- left row -- main array -- right row -- from the bottom up: botCt, rows 0, ... n-1, topCt -- Layout of the short ram from the bottom up: read, rows 0, ... n-1, write -- left row -- main array -- right row Κ)έ– "cedar" style˜codešœ™Kšœ Οmœ1™——šžœžœžœž˜šœžœ˜1šœžœžœž˜Kšœžœžœžœ˜4Kšœžœžœ žœ˜5Kšœžœžœžœ˜4Kšœžœžœ žœ˜5Kšžœžœ˜—Kšœ žœ=˜K—Kšžœ˜—šœžœ˜/Kšœ ˜ šœ žœ žœžœ2˜QKšžœžœ%˜.——Kšžœ˜—šœ˜Kšœ˜Kšœ*˜*Kšœ˜Kšœ'˜'Kšœ&˜&Kšœ˜—K˜K˜—š Ÿ œžœžœ žœžœ˜ZKšœžœ˜+Kšœ5˜5Kšœ6˜6Kšœ7˜7Kšœžœ˜3Kšœžœ˜/Kšœžœ žœ'žœ ˜WKšœžœ žœ)žœ˜WKšœžœ‘˜3Kšœ ˜ Kšœ˜K˜šœ#žœ ˜FKš‘4˜4—š žœžœžœ žœ‘˜.Kšœžœ˜0Kšžœ˜—š žœžœžœžœ‘˜2šœžœ˜-Kšœ ˜ šœ žœ žœžœ"˜AKšžœžœ5˜>——šžœžœžœž˜šœžœ˜1šœžœžœž˜Kšœžœžœžœ˜4Kšœžœžœ žœ˜5Kšœžœžœžœ˜4Kšœžœžœ žœ˜5Kšžœžœ˜—Kšœ žœ-˜;—Kšžœ˜—šœžœ˜/Kšœ ˜ šœ žœ žœžœ2˜QKšžœžœ%˜.——Kšžœ˜—šœ˜Kšœ˜Kšœ*˜*Kšœ˜Kšœ'˜'Kšœ&˜&Kšœ˜—K˜—Kšœ žœžœ˜#šœžœžœ˜Kšœ žœžœžœ˜0Kšœžœžœžœ˜0—K˜šŸœ˜Kš œžœžœ/žœžœ˜HKšœžœ˜Kšœžœ˜(KšœŽ˜ŽKšœ˜šžœžœžœž˜Kšœžœ˜.Kšœ˜Kšžœ˜—Kšœ˜Kšœ˜—K™•šŸ œ˜!Kšœžœ ˜$Kšœžœ˜šžœžœž˜šœžœ"˜6Kš žœžœžœžœžœ˜=K˜—šœžœ#˜7Kšœ žœ!˜3Kšžœ žœžœ$˜>K˜—šœžœ#˜7Kšœ žœ!˜3šžœ žœžœ˜Kšœ?˜?—K˜—Kšžœžœ˜—Kšžœžœ#˜Gšžœ˜Kšœ žœ!˜2Kšžœ žœžœžœ$˜AKšœ?˜?Kšœ˜—Kšœ˜—K˜—K˜šžœ˜K™˜K˜—KšΟl™K™uš Ÿœžœžœ žœžœ™VKšœ1™1Kšœ5™5KšœC™CKš œžœ žœžœžœ ™lKšœ2žœ ™BKšœB™BKš œ žœ žœžœžœ ™tKšœ1™1Kšœ3™3KšœA™AK™Kšœ™Kšœ™Kšœ"™"Kšœ"™"K™šœ#žœ ™FKšœ0™0—š žœžœžœ žœ‘™.Kšœžœ™0Kšžœ™—K™Kš‘ ™ šœžœ™-Kšœ-™-Kšœ žœ5™C—š žœžœžœžœ‘™8šœžœ™1Kš œžœžœžœ žœ™™L—Kšžœ™—šœžœ™/Kšœ™Kšœ žœ5™C—K™Kš‘ ™ šœžœ™-Kšœ™šœ žœ žœžœ3™SKšžœžœE™N——šžœžœžœž™šœžœ™1Kš œžœžœžœ žœ™;Kšœ žœ%™3—Kšžœ™—šœžœ™/Kšœ™šœ žœ žœžœ"™AKšžœžœ4™=——K™Kš‘ ™ šœžœ™-Kšœ-™-Kšœ žœ%™3—š žœžœžœžœ‘™7šœžœ™1Kš œžœžœžœ žœ™:Kšœ žœ>™L—Kšžœ™—šœžœ™/Kšœ™Kšœ žœ%™3—K™šœ™Kšœ™Kšœ?™?Kšœ™Kšœ'™'Kšœ&™&Kšœ™—K™—š Ÿ œžœžœ žœžœ™HKšœžœ‘™3Kšœ+™+Kšœ#™#Kšžœ žœžœ žœ™=Kšœ!™!Kšœ%™%Kšœ=™=Kšœ&žœ ™8Kšœ™Kšœ™Kšœ™Kšœ™šœ ™ KšœS™SKšœ'™'šœ žœ™Kšœ;™;Kšœf™fKšœ9™9Kšœ™—Kšœ™—Kšœ™K™—Kš’™š Ÿ œžœžœ žœžœ™[Kšœ1™1Kšœ5™5KšœC™CKšœžœ žœžœ™[Kšœ@™@Kšœ2™2Kšœ@™@Kšœ@™@KšœB™BKšœ!žœ žœžœ™cKšœF™FKšœ1™1Kšœ3™3KšœA™AK™Kšœ™Kšœ™Kšœ"™"Kšœ"™"K™šœ#žœ ™FKšœ0™0—š žœžœžœ žœ‘™.Kšœžœ™2Kšžœ™—K™Kš‘ ™ šœžœ™-Kšœ-™-Kšœ žœ5™C—š žœžœžœžœ‘™8šœžœ™1Kš œžœžœžœ žœ™™L—Kšžœ™—šœžœ™/Kšœ™Kšœ žœ5™C—K™Kš‘ ™ š žœžœžœžœ‘™2šœžœ™/Kš œžœžœžœ žœ™=šœ žœ žœžœ6™VKšžœžœK™T——šžœžœžœž™šœžœ™3šœžœž™Kšœžœžœžœ™/Kšœžœžœžœ™3Kšœžœžœžœ™3Kšœžœžœžœ™3Kšžœžœ™—Kšœ žœ%™3—Kšžœ™—šœžœ™1Kš œžœžœžœ žœ™7šœ žœ žœžœ"™AKšžœžœ7™@——Kšžœ™—K™Kš‘ ™ šœžœ™/Kšœ-™-Kšœ žœ%™3—š žœžœžœžœ‘™7šœžœ™3Kš œžœžœžœ žœ™:Kšœ žœ>™L—Kšžœ™—šœžœ™1Kšœ™Kšœ žœ%™3—K™šœ™Kšœ™Kšœ?™?Kšœ™Kšœ'™'Kšœ&™&Kšœ™—K™—š Ÿœžœžœ žœžœ™MKšœžœ‘™3Kšœ+™+Kšœ#™#Kšžœžœžœžœ™LKšœ+™+Kšœ/™/KšœG™GKšœ+žœ™BKšœ™Kšœ™Kšœ™Kšœ™šœ ™ KšœS™SKšœ'™'šœ žœ™Kšœ;™;Kšœf™fKšœ9™9Kšœ™—Kšœ™—Kšœ™K™——™KšŸœžœT™bš Ÿœžœžœžœžœ™Bšœ2™2Kšœy™y—KšœB™BKšœ'žœžœ™5Kšœ.žœžœ™CKšœ1™1KšœL™LKšœ6™6Kšœ'™'Kšœ)™)šœ™K™——Kšœžœ ™Kšœ žœ žœ™Kšœžœ™šŸ œžœžœžœ™6Kšœ™Kšžœ žœžœžœ™BKšžœžœžœ$™6Kšœžœ™)Kšœ ™ KšœO™OKšœ&™&Kšœ\™\Kšœ%™%K™—šŸœžœžœžœ™8K™—šŸ œžœžœžœ™7K™—K™Kšœ žœžœ™#šœžœžœ™Kšœ<žœžœžœ™LKšœ3™3Kšœ™Kšœžœžœ™Kšœ žœ™Kšœ žœ™Kšœžœ‘7™FKšœžœ‘'™6Kšœžœžœžœ™0—K™šŸœ™Kš œžœžœ/žœžœ™HKš œ žœžœ6žœžœ™VKšœžœ™Kšœžœ™(Kšœδ™δKšœ™Kšœ™Kšœ™šžœžœžœž™Kšœžœ™.Kšœ™Kšžœ™—Kšœžœ™.Kšœ™Kšœ™Kšœ™K™—šŸ œ™!Kšœžœ ™$Kšœžœ™ Kšœžœ™šžœžœž™šœ™Kšœ™Kšœ™Kšœ™Kšœ4™4K™—šœ žœžœžœž™*šœ™Kšœžœ™ Kšœ ™ Kšœ™Kšœ™—Kšœ žœ žœžœ™5šžœžœžœ™šžœ žœ™Kšœ™Kšœ0™0Kšœžœ™Kšœ™—šžœ žœ™Kšœ™Kšœžœ™Kšœ™—K™——Kšžœ™—šžœžœ™Kšœ-™-Kšœžœ žœžœ™)Kšœžœžœžœ™6Kšœ žœ žœžœ™(K™—šžœ™Kšœ™Kšœ.™.K™—Kšœ™Kšžœ žœ ™1Kšžœ žœ!™2Kšœ™——K˜™KšŸ œžœZ˜jš Ÿœžœžœžœžœ˜:Kšœžœ‘˜3Kšžœžœžœ:˜Lšœ4˜4Kšœm˜m—KšœD˜DKšœ'žœžœ˜5Kšœ1˜1Kšœ8˜8Kšœ7˜7Kšœ)˜)Kšœ˜—K˜Kšœ žœžœ˜'šœžœžœ˜Kšœ$žœžœžœ˜4Kšœžœžœžœ˜0—K˜šŸ œ˜!Kš œžœžœ/žœžœ˜HKšœžœ˜Kšœžœ˜,Kšœž˜žKšœ˜šžœžœžœž˜Kšœžœ˜.Kšœ˜Kšžœ˜—Kšœ˜Kšœ˜K˜—šŸ œ˜#Kšœžœ ˜&Kšœžœ˜Kšžœžœ#˜Gšžœ˜Kšœ žœ!˜2Kšžœ žœžœžœ$˜AKšœ?˜?Kšœ˜—Kšžœžœžœ‘˜>šžœžœž˜šœžœ"˜6Kš žœžœžœžœžœ˜=K˜—šœžœ#˜7Kšœ žœ!˜3Kšžœ žœžœ$˜>K˜—šœžœ#˜7Kšœ žœ!˜3šžœ žœžœ˜Kšœ?˜?—K˜—Kšžœžœ˜—Kšœ˜——K˜š Ÿœžœžœ žœžœ˜VKšœ1˜1Kšœ5˜5KšœC˜CKšœžœ žœžœ˜[Kšœ2˜2Kšœ@˜@Kšœ!žœ žœžœ˜cKšœ1˜1Kšœ3˜3KšœA˜AK˜Kšœ˜Kšœ˜Kšœ"˜"Kšœ"˜"K˜šœ#žœ ˜FKšœ0™0—š žœžœžœ žœ‘˜.Kšœžœ˜2Kšžœ˜—K™Kš‘ ™ šœžœ˜-Kšœ-˜-Kšœ žœ5˜C—š žœžœžœžœ‘˜8šœžœ˜1Kš œžœžœžœ žœ˜˜L—Kšžœ˜—šœžœ˜/Kšœ˜Kšœ žœ5˜C—K™Kš‘ ™ š žœžœžœžœ‘˜2šœžœ˜/Kšœ˜šœ žœ žœžœ6˜VKšžœžœK˜T——šžœžœžœž˜šœžœ˜3Kš œžœžœžœ žœ˜9Kšœ žœ%˜3—Kšžœ˜—šœžœ˜1Kšœ˜šœ žœ žœžœ"˜AKšžœžœ7˜@——Kšžœ˜—K™Kš‘ ™ šœžœ˜/Kšœ-˜-Kšœ žœ%˜3—š žœžœžœžœ‘˜7šœžœ˜3Kš œžœžœžœ žœ˜:Kšœ žœ>˜L—Kšžœ˜—šœžœ˜1Kšœ˜Kšœ žœ%˜3—K˜šœ˜Kšœ˜Kšœ?˜?Kšœ˜Kšœ'˜'Kšœ&˜&Kšœ˜—K˜—™šŸœžœžœ žœžœžœžœžœ˜zKšœ0žœ ™@Kšœ˜Kšžœžœžœžœ˜Kš œžœžœžœžœ˜2šžœžœž˜Kšœ&žœžœ˜BK˜Kšžœ˜—Kšœ žœ˜Kšœ˜K˜—K˜KšŸœžœT˜bšŸœžœžœžœžœžœžœ˜WKšœžœ‘˜3Kšžœžœžœ8˜JKšžœžœžœ‘˜K˜—šœžœ#˜7Kšœ žœ!˜3šžœ žœžœ˜Kšœ?˜?—K˜—Kšžœžœ˜—Kšžœžœ#˜Gšžœ˜Kšœ žœ!˜2Kšžœ žœžœžœ$˜AKšœ?˜?Kšœ˜—Kšœ˜K˜—Kš’™Kšœžœ˜%Kšœžœ˜Kšœžœ˜!K™uš Ÿœžœžœ žœžœ˜VKšœ1˜1Kšœ5˜5KšœC˜CKš œžœ žœžœžœ ˜lKšœ2žœ ˜BKšœB˜BKš œ žœ žœžœžœ ˜tKšœ1˜1Kšœ3˜3KšœA˜AK˜Kšœ˜Kšœ˜Kšœ"˜"Kšœ"˜"K˜šœ#žœ ˜FKšœ0™0—š žœžœžœ žœ‘˜.Kšœžœ˜0Kšžœ˜—K™Kš‘ ™ šœžœ˜-Kšœ-˜-Kšœ žœ5˜C—š žœžœžœžœ‘˜8šœžœ˜1Kš œžœžœžœ žœ˜˜L—Kšžœ˜—šœžœ˜/Kšœ˜Kšœ žœ5˜C—K™Kš‘ ™ šœžœ˜-Kšœ˜šœ žœ žœžœ3˜SKšžœžœE˜N——šžœžœžœž˜šœžœ˜1Kš œžœžœžœ žœ˜;Kšœ žœ%˜3—Kšžœ˜—šœžœ˜/Kšœ˜šœ žœ žœžœ"˜AKšžœžœ4˜=——K™Kš‘ ™ šœžœ˜-Kšœ-˜-Kšœ žœ%˜3—š žœžœžœžœ‘˜7šœžœ˜1Kš œžœžœžœ žœ˜:Kšœ žœ>˜L—Kšžœ˜—šœžœ˜/Kšœ˜Kšœ žœ%˜3—K˜šœ˜Kšœ˜Kšœ?˜?Kšœ˜Kšœ'˜'Kšœ&˜&Kšœ˜—K˜—š Ÿœžœžœ žœžœ˜UKšœžœ˜+Kšœ5˜5Kšœ6˜6Kšœ7˜7Kšœžœ˜3Kšœžœ˜/Kšœžœ žœ'žœ ˜WKšœžœ žœ)žœ˜WKšœžœ‘˜3Kšœ ˜ Kšœ˜K˜šœ#žœ ˜FKš‘4™4—š žœžœžœ žœ‘˜.Kšœžœ˜0Kšžœ˜—š žœžœžœžœ‘˜2šœžœ˜-Kšœ ˜ šœ žœ žœžœ"˜AKšžœžœ5˜>——šžœžœžœž˜šœžœ˜1šœžœžœž˜Kšœžœžœžœ˜4Kšœžœžœ žœ˜5Kšœžœžœžœ˜4Kšœžœžœ žœ˜5Kšžœžœ˜—Kšœ žœ=˜K—Kšžœ˜—šœžœ˜/Kšœ ˜ šœ žœ žœžœ2˜QKšžœžœ%˜.——Kšžœ˜—šœ˜Kšœ˜Kšœ*˜*Kšœ˜Kšœ'˜'Kšœ&˜&Kšœ˜—K˜—š Ÿ œžœžœ žœžœ˜HKšœžœ‘˜3Kšœ+˜+Kšœ#˜#Kšžœ žœžœ žœ˜=Kšœ!˜!šœžœ žœžœ˜NKšœžœ˜!—Kšœ=˜=Kšœ&žœ ˜8Kšœ˜Kšœ˜Kšœ˜Kšœ˜šœ ˜ KšœS˜SKšœ'˜'šœ žœ˜Kšœ;˜;Kšœf˜fKšœ9˜9Kšœ˜—Kšœ˜—Kšœ˜K˜—Kš’™š Ÿ œžœžœ žœžœ˜[Kšœ1˜1Kšœ5˜5KšœC˜CKšœžœ žœžœ˜[Kšœ@˜@Kšœ2˜2Kšœ@˜@Kšœ@˜@KšœB˜BKšœ!žœ žœžœ˜cKšœF˜FKšœ1˜1Kšœ3˜3KšœA˜AK˜Kšœ˜Kšœ˜Kšœ"˜"Kšœ"˜"K˜šœ#žœ ˜FKšœ0™0—š žœžœžœ žœ‘˜.Kšœžœ˜2Kšžœ˜—K™Kš‘ ™ šœžœ˜-Kšœ-˜-Kšœ žœ5˜C—š žœžœžœžœ‘˜8šœžœ˜1Kš œžœžœžœ žœ˜˜L—Kšžœ˜—šœžœ˜/Kšœ˜Kšœ žœ5˜C—K™Kš‘ ™ š žœžœžœžœ‘˜2šœžœ˜/Kš œžœžœžœ žœ˜=šœ žœ žœžœ6˜VKšžœžœK˜T——šžœžœžœž˜šœžœ˜3šœžœž˜Kšœžœžœžœ˜/Kšœžœžœžœ˜3Kšœžœžœžœ˜3Kšœžœžœžœ˜3Kšžœžœ˜—Kšœ žœ%˜3—Kšžœ˜—šœžœ˜1Kš œžœžœžœ žœ˜7šœ žœ žœžœ"˜AKšžœžœ7˜@——Kšžœ˜—K™Kš‘ ™ šœžœ˜/Kšœ-˜-Kšœ žœ%˜3—š žœžœžœžœ‘˜7šœžœ˜3Kš œžœžœžœ žœ˜:Kšœ žœ>˜L—Kšžœ˜—šœžœ˜1Kšœ˜Kšœ žœ%˜3—K˜šœ˜Kšœ˜Kšœ?˜?Kšœ˜Kšœ'˜'Kšœ&˜&Kšœ˜—K˜—š Ÿœžœžœ žœžœ˜MKšœžœ‘˜3Kšœ+˜+Kšœ#˜#Kšžœžœžœžœ˜LKšœ+˜+Kšœ/˜/KšœB˜BKšœ&žœ˜=Kšœ˜Kšœ˜Kšœ˜Kšœ˜šœ ˜ KšœS˜SKšœ'˜'šœ žœ˜Kšœ;˜;Kšœf˜fKšœ9˜9Kšœ˜—Kšœ˜—Kšœ˜K˜——K˜—…—fτΗ©