DIRECTORY Atom, BitOps, BoolEx, PLAGen, FSM, CDDirectory, CDEnvironment, Convert, Core, CoreClasses, CoreCreate, CoreFlat, CoreOps, CoreProperties, FileNames, IO, Logic, PLAOps, Ports, PWCore, RefTab, Rope, RopeList, Rosemary, RosemaryUser, Sisyph, SParse, SymTab, TerminalIO; FSMImpl: CEDAR PROGRAM IMPORTS Atom, BitOps, BoolEx, PLAGen, CDDirectory, CDEnvironment, Convert, CoreClasses, CoreCreate, CoreFlat, CoreOps, CoreProperties, FileNames, IO, Logic, PLAOps, Ports, PWCore, RefTab, Rope, RopeList, Rosemary, RosemaryUser, Sisyph, SParse, SymTab, TerminalIO EXPORTS FSM = BEGIN Expression: TYPE = REF; OpIndex: TYPE = BoolEx.OpIndex; FSMData: TYPE = FSM.FSMData; States: TYPE = FSM.States; State: TYPE = FSM.State; StateRec: TYPE = FSM.StateRec; Transitions: TYPE = FSM.Transitions; Transition: TYPE = FSM.Transition; RegisterType: TYPE = FSM.RegisterType; trace: BOOL _ FALSE; xilinxKey: ATOM = $Xilinx; fsmClassName: IO.ROPE _ Rosemary.Register["FSM", InitFSM, EvalFSM, NIL, TRUE]; fsmClass: Core.CellClass _ CoreFlat.CellClassCutLabels[ Rosemary.BindCellClass[ NEW [Core.CellClassRec _ [name: fsmClassName, recast: RecastFSM, layersProps: FALSE]], fsmClassName], fsmClassName]; fsmExtension: IO.ROPE _ ".fsm"; sourceName: IO.ROPE _ "Source"; targetName: IO.ROPE _ "Target"; SchMachine: PUBLIC PROC[ cx: Sisyph.Context, name: IO.ROPE _ NIL, register: RegisterType _ edgeTriggered, exprImplKey: ATOM _ $SC ] RETURNS [ct: Core.CellType] = { fsmData: FSMData _ NEW[FSM.FSMDataRec]; rct: CoreClasses.RecordCellType _ NIL; foundInit: BOOL _ FALSE; srcWireStates: RefTab.Ref _ RefTab.Create[]; targetWireState: RefTab.Ref _ RefTab.Create[]; ref: REF; registerAtom: ATOM; assertRp: IO.ROPE; EachAssert: EachSortedPropProc = {assertRp _ Rope.Cat[assertRp, NARROW[value, IO.ROPE]]}; IF name=NIL THEN { objectName: IO.ROPE _ CDDirectory.Name[Sisyph.GetCDObj[cx], Sisyph.GetDesign[cx]]; index: INT _ Rope.Find[s1: objectName, s2: ".icon"]; length: INT _ Rope.Length[objectName]; IF index<0 OR index#length-5 THEN ERROR; -- .icon not in string or not at end of string name _ Rope.Substr[objectName, 0, length-5]}; IF name.Find["."]=-1 THEN name _ name.Cat[fsmExtension]; fsmData.srcCellType _ Sisyph.ExtractSchematicByName[name, cx]; fsmData.name _ CoreOps.GetCellTypeName[fsmData.srcCellType]; fsmData.name _ Rope.Substr[fsmData.name, 0, Rope.Index[fsmData.name, 0, "."]]; [] _ CoreOps.SetCellTypeName[fsmData.srcCellType, fsmData.name]; ref _ CoreProperties.GetCellTypeProp[fsmData.srcCellType, $FSMKey]; IF ref#NIL THEN exprImplKey _ NARROW[ref]; registerAtom _ NARROW[CoreProperties.GetCellTypeProp[fsmData.srcCellType,$FSMReg]]; EnumerateSortedProps[fsmData.srcCellType.properties, 'a, EachAssert]; IF assertRp#NIL THEN fsmData.assert _ SParse.ToTree[assertRp]; fsmData.register _ SELECT registerAtom FROM $EdgeTriggered => edgeTriggered, $TwoPhase => twoPhase, $None => none, ENDCASE => register; rct _ NARROW[fsmData.srcCellType.data]; FOR inst: NAT IN [0..rct.size) DO IF CoreOps.ToBasic[rct[inst].type].class=stateClass THEN { EachOutput: EachSortedPropProc = { outputName: IO.ROPE _ NARROW[value, IO.ROPE]; inv: BOOL _ outputName.Fetch[]='~; IF inv THEN outputName _ outputName.Substr[1]; IF inv THEN {IF NOT RopeList.Memb[thisState.outputsInv, outputName] THEN thisState.outputsInv _ CONS[outputName, thisState.outputsInv]} ELSE {IF NOT RopeList.Memb[thisState.outputs, outputName] THEN thisState.outputs _ CONS[outputName, thisState.outputs]}}; thisState: State _ NEW[StateRec]; sourceStates: States _ NARROW [RefTab.Fetch[srcWireStates, rct[inst].actual[stateSource]].val]; thisState.srcCellInst _ rct[inst]; thisState.name _ CoreClasses.GetCellInstanceName[rct[inst]]; IF thisState.name.Length[]=0 THEN thisState.name _ IO.PutFR["STATE%02g", IO.int[inst]]; fsmData.states _ CONS[thisState, fsmData.states]; IF Rope.Equal[thisState.name, "Init"] THEN {IF foundInit THEN ERROR; foundInit _ TRUE; fsmData.initialState _ thisState}; EnumerateSortedProps[rct[inst].properties, 'o, EachOutput]; sourceStates _ CONS[thisState, sourceStates]; []_RefTab.Store[srcWireStates, rct[inst].actual[stateSource], sourceStates]; IF ~RefTab.Insert[targetWireState, rct[inst].actual[stateTarget], thisState] THEN ERROR}; ENDLOOP; FOR states: States _ fsmData.states, states.rest UNTIL states=NIL DO thisState: State _ states.first; srcStates: States _ NARROW [RefTab.Fetch[srcWireStates, thisState.srcCellInst.actual[stateTarget]].val]; FOR srcStates _ srcStates, srcStates.rest WHILE srcStates#NIL DO fakeTransition: Transition _ NEW[FSM.TransitionRec]; srcState: State _ srcStates.first; fakeTransition.target _ thisState; srcState.outTrans _ CONS[fakeTransition, srcState.outTrans]; TerminalIO.PutF["State %g is directly driven by %g\n", IO.rope[thisState.name], IO.rope[srcState.name]]; ENDLOOP; ENDLOOP; FOR inst: NAT IN [0..rct.size) DO IF CoreOps.ToBasic[rct[inst].type].class=transitionClass THEN { EachPartialExpression: EachSortedPropProc = {expr _ Rope.Cat[expr, NARROW[value, IO.ROPE]]}; expr: IO.ROPE _ NIL; enable: REF _ NIL; srcStates: States _ NARROW [RefTab.Fetch[srcWireStates, rct[inst].actual[transitionSource]].val]; target: State _ NARROW [RefTab.Fetch[targetWireState, rct[inst].actual[transitionTarget]].val]; EnumerateSortedProps[rct[inst].properties, 't, EachPartialExpression]; IF expr#NIL THEN enable _ SParse.ToTree[expr ! SParse.BadRope => { TerminalIO.PutF["*** Bad transition expression: %g\n", IO.rope[expr]]; enable _ NIL; CONTINUE}]; IF target=NIL OR srcStates=NIL THEN { srcNm: IO.ROPE _ IF srcStates=NIL THEN "(unknown)" ELSE srcStates.first.name; tgtNm: IO.ROPE _ IF target=NIL THEN "(unknown)" ELSE target.name; TerminalIO.PutF["*** Bad transition source or target: \n Source: %g\n Target: %g\n Expression: %g", IO.rope[srcNm], IO.rope[tgtNm], IO.rope[SParse.ToRope[enable]]]; ERROR}; FOR srcStates _ srcStates, srcStates.rest WHILE srcStates#NIL DO thisTransition: Transition _ NEW[FSM.TransitionRec _ [ srcCellInst: rct[inst], target: target, enable: BoolEx.Copy[enable]]]; srcStates.first.outTrans _ CONS[thisTransition, srcStates.first.outTrans] ENDLOOP}; ENDLOOP; IF NOT foundInit THEN ERROR; ct _ CodeMachine[fsmData, exprImplKey]}; SchStateCell: PUBLIC PROC RETURNS [ct: Core.CellType] = {ct _ stateCellType}; stateClass: Core.CellClass _ NEW[Core.CellClassRec _ [name: "FSMState", layersProps: FALSE]]; stateCellType: Core.CellType _ CoreOps.CreateCellType [ class: stateClass, public: CoreCreate.Wires[targetName, sourceName]]; stateTarget: NAT _ Ports.PortIndex[stateCellType.public, targetName]; stateSource: NAT _ Ports.PortIndex[stateCellType.public, sourceName]; SchTransitionCell: PUBLIC PROC RETURNS [ct: Core.CellType] = {ct _ transitionCellType}; transitionClass: Core.CellClass _ NEW[Core.CellClassRec _ [name: "FSMTransition", layersProps: FALSE]]; transitionCellType: Core.CellType _ CoreOps.CreateCellType [ class: transitionClass, public: CoreCreate.Wires[sourceName, targetName]]; transitionTarget: NAT _ Ports.PortIndex[transitionCellType.public, targetName]; transitionSource: NAT _ Ports.PortIndex[transitionCellType.public, sourceName]; EachSortedPropProc: TYPE = PROC [value: REF ANY]; IndexedPropList: TYPE = LIST OF IndexedProp; IndexedProp: TYPE = RECORD[index: CARD _ 0, value: REF ANY _ NIL]; EnumerateSortedProps: PROC [properties: Core.Properties, propPrefix: CHAR, eachProp: EachSortedPropProc] = { EachProp: PROC [prop: ATOM, value: REF ANY] = { TwoIndexedProp: TYPE = RECORD[i0, i1: IndexedProp]; propName: IO.ROPE _ Atom.GetPName[prop]; IF Rope.Fetch[propName]=propPrefix THEN { props _ CONS[[Convert.CardFromRope[Rope.Substr[propName, 1]], value], props]; FOR ps: IndexedPropList _ props, ps.rest WHILE ps#NIL AND ps.rest#NIL DO IF ps.first.index> ps.rest.first.index THEN [ps.first, ps.rest.first] _ TwoIndexedProp[ps.rest.first, ps.first] ENDLOOP} }; props: IndexedPropList _ NIL; CoreProperties.Enumerate[properties, EachProp]; FOR ps: IndexedPropList _ props, ps.rest UNTIL ps=NIL DO eachProp[ps.first.value] ENDLOOP}; CodeMachine: PUBLIC PROC [fsm: FSMData, exprImplKey: ATOM _ $SC] RETURNS [ct: Core.CellType] = { AddInput: PROC[name: IO.ROPE] = { IF ~SymTab.Fetch[inTab, name].found THEN ERROR; -- just checking IF ~RopeList.Memb[ins, name] THEN ins _ CONS[name, ins]}; AddOutput: PROC[name: IO.ROPE] = { IF ~SymTab.Fetch[inTab, name].found THEN {AddNonStateOutput[name]; RETURN}; IF fsm.register=none THEN { outNm: IO.ROPE _ Rope.Cat["Nxt", name]; IF ~RopeList.Memb[inSt, name] THEN inSt _ CONS[name, inSt]; IF ~RopeList.Memb[outSt, outNm] THEN outSt _ CONS[outNm, outSt]} ELSE { IF IsXtra[name] AND DoesInternalRegisterFeedBack[exprImplKey] THEN RETURN; IF ~RopeList.Memb[outs, name] THEN outs _ CONS[name, outs]}}; AddNonStateOutput: PROC[name: IO.ROPE] = { IF ~SymTab.Fetch[outTab, name].found THEN ERROR; IF fsm.register=none THEN name _ Rope.Cat["Nxt", name]; IF ~RopeList.Memb[outSt, name] AND ~RopeList.Memb[outs, name] THEN outs _ CONS[name, outs]}; MakePublic: PROC[list: ROPES] RETURNS[Core.Wire] = { publics: LIST OF CoreCreate.WR _ NIL; FOR list _ RopeList.Reverse[list], list.rest UNTIL list=NIL DO publics _ CONS[list.first, publics]; fsm.publics _ CONS[list.first, fsm.publics] ENDLOOP; RETURN[CoreCreate.WireList[publics]]}; ins: ROPES _ NIL; inSt: ROPES _ NIL; outs: ROPES _ NIL; outSt: ROPES _ NIL; sysIns: ROPES _ NIL; pubs: ROPES _ NIL; inTab: SymTab.Ref _ SymTab.Create[]; outTab: SymTab.Ref _ SymTab.Create[]; ValidateFSM[fsm]; EncodeStates[fsm]; -- finds common outs, may add xtras fsm.publics _ NIL; fsm.mach _ ConvertToExpXilinx[fsm, exprImplKey=xilinxKey]; -- for verification - outs which are ins [, inTab, outTab] _ BoolEx.GetExpressionTables[fsm.mach]; FOR sts: States _ fsm.states, sts.rest WHILE sts#NIL DO FOR outs: ROPES _ sts.first.outputs, outs.rest WHILE outs#NIL DO AddOutput[outs.first] ENDLOOP; FOR outs: ROPES _ sts.first.outputsInv, outs.rest WHILE outs#NIL DO AddOutput[outs.first] ENDLOOP; FOR trans: Transitions _ sts.first.outTrans, trans.rest WHILE trans#NIL DO IF trans.first.enable#NIL THEN []_BoolEx.ScanExpression[trans.first.enable,AddInput]; ENDLOOP; ENDLOOP; FOR sts: States _ fsm.states, sts.rest WHILE sts#NIL DO FOR trans: Transitions _ sts.first.outTrans, trans.rest WHILE trans#NIL DO FOR outs: ROPES _ trans.first.outputs, outs.rest WHILE outs#NIL DO AddNonStateOutput[outs.first] ENDLOOP ENDLOOP ENDLOOP; IF exprImplKey#xilinxKey THEN sysIns _ CONS["Reset", sysIns]; SELECT fsm.register FROM twoPhase => {sysIns _ CONS["PhA", sysIns]; sysIns _ CONS["PhB", sysIns]}; edgeTriggered => {sysIns _ CONS["Clock", sysIns]} ENDCASE; sysIns _ CONS["Vdd", sysIns]; sysIns _ CONS["Gnd", sysIns]; pubs _ RopeList.Append[pubs, RopeList.Sort[ins, RopeList.IgnoreCase]]; pubs _ RopeList.Append[pubs, RopeList.Sort[inSt, RopeList.IgnoreCase]]; pubs _ RopeList.Append[pubs, RopeList.Reverse[sysIns]]; pubs _ RopeList.Append[pubs, RopeList.Sort[outs, RopeList.IgnoreCase]]; pubs _ RopeList.Append[pubs, RopeList.Sort[outSt, RopeList.IgnoreCase]]; ct _ CoreOps.CreateCellType [ class: fsmClass, public: MakePublic[pubs], data: fsm, name: fsm.name, props: CoreProperties.PutProp[NIL, $ExprImplKey, exprImplKey]]; FOR pw: NAT IN [0..ct.public.size) DO [] _ Ports.InitPort[ct.public[pw], l]; ENDLOOP}; arrayWds: NAT = 6; arrayBits: NAT = arrayWds*16; ROPES: TYPE = LIST OF IO.ROPE; Groups: TYPE = LIST OF Group; Group: TYPE = RECORD[conSets, varSets: StatePatternSets, maxCnt: NAT]; StatePatternSets: TYPE = LIST OF StatePatterns; StatePatterns: TYPE = LIST OF StatePattern; StatePattern: TYPE = RECORD[state: State, ones, zeros, locVar, locOne: Bits]; Bits: TYPE = REF BitArray; BitArray: TYPE = PACKED ARRAY [0..arrayBits) OF BOOL _ ALL[FALSE]; StatePatternComp: TYPE = {indistinct, aLrg, bLrg}; PatternBitCode: TYPE = {F,T,t,v,d}; -- False, True, locTrue, locVar, dontcare useLocOnes: BOOL _ TRUE; emptyArray: Bits _ NEW[BitArray _ ALL[FALSE]]; xtraNm: IO.ROPE _ "Xtra"; EncodeStates: PUBLIC PROC[fsm: FSMData] = { Mod: PROC[r1, r2, r3, r4: IO.ROPE _ NIL] = {firstMod _ FALSE; RETURN}; varOuts: Bits _ NEW[BitArray _ ALL[FALSE]]; stateOuts: Bits _ NEW[BitArray _ ALL[FALSE]]; statePats: StatePatterns _ NIL; statePatSets: StatePatternSets _ NIL; groups: Groups _ NIL; log: IO.STREAM _ TerminalIO.TOS[]; firstMod: BOOL _ TRUE; firstXtra: INT _ 0; maxStNmLgth: INT _ 0; xtraCnts: ARRAY [0..20) OF NAT _ ALL[0]; xtraOrder: ARRAY [0..20) OF NAT _ ALL[0]; maxXtraCnt: NAT _ 0; outTab: SymTab.Ref _ SymTab.Create[]; outTabIndex: INT _ 0; OutIndex: PROC[out: IO.ROPE] RETURNS[index: INT] = { refInt: REF INT _ NARROW[SymTab.Fetch[outTab, out].val]; IF refInt=NIL THEN { IF ~fsm.outInAll AND ~RopeList.Memb[fsm.outIns, out] THEN RETURN[-1]; refInt _ NEW[INT_outTabIndex]; outTabIndex _ outTabIndex+1; []_SymTab.Store[outTab, out, refInt]}; RETURN[refInt^]}; IndexOut: PROC[index: INT] RETURNS[out: IO.ROPE] = { Find: SymTab.EachPairAction = {IF index=NARROW[val, REF INT]^ THEN {out_key; RETURN[TRUE]}}; IF NOT SymTab.Pairs[outTab, Find] THEN ERROR}; allOuts: ROPES; IF NOT trace THEN log.PutF["Encoding %g states ... ", IO.rope[fsm.name]]; IF trace THEN log.PutRope["Cross link InTransitions\n"]; CrossLinkTransitions[fsm]; IF trace THEN log.PutRope["Register sorted outs\n"]; FOR states: States _ fsm.states, states.rest WHILE states#NIL DO FOR outs: ROPES _ states.first.outputs, outs.rest WHILE outs#NIL DO allOuts _ CONS[outs.first, allOuts] ENDLOOP; FOR trans: Transitions _ states.first.outTrans, trans.rest WHILE trans#NIL DO FOR outs: ROPES _ trans.first.outputs, outs.rest WHILE outs#NIL DO allOuts _ CONS[outs.first, allOuts] ENDLOOP ENDLOOP ENDLOOP; FOR outs: ROPES _ Sort[allOuts], outs.rest WHILE outs#NIL DO IF OutIndex[outs.first]#-1 AND IsXtra[outs.first] THEN { ii: INT _ Convert.IntFromRope[outs.first.Substr[4]]; IF trace AND (ii+1)>firstXtra THEN log.PutF["First extra state index set to: %g\n", IO.int[ii+1]]; firstXtra _ MAX[ii+1, firstXtra]} ENDLOOP; IncludeStateOutputsInTransitions[fsm]; IF trace THEN log.PutRope["Fill in State Patterns\n"]; FOR states: States _ fsm.states, states.rest WHILE states#NIL DO TwoBits: TYPE = RECORD[ones, zeros: LIST OF Bits]; list: LIST OF TwoBits _ NIL; first: BOOL _ TRUE; stp: StatePattern _ [ states.first, NEW[BitArray_ALL[FALSE]], NEW[BitArray_ALL[FALSE]], NEW[BitArray_ALL[FALSE]], NEW[BitArray_ALL[FALSE]]]; FOR outs: ROPES _ states.first.outputsInv, outs.rest WHILE outs#NIL DO index: INT _ OutIndex[outs.first]; IF index = -1 THEN ERROR; stp.zeros[index] _ TRUE ENDLOOP; FOR trans: Transitions _ states.first.inTrans, trans.rest WHILE trans#NIL DO ones: BitArray _ ALL[FALSE]; FOR outs: ROPES _ trans.first.outputs, outs.rest WHILE outs#NIL DO index: INT _ OutIndex[outs.first]; IF index#-1 THEN ones[index] _ TRUE; ENDLOOP; FOR out: INT IN [0..outTabIndex) DO IF first THEN stp.ones[out] _ ones[out]; IF ones[out] AND stp.zeros[out] THEN ERROR; stateOuts[out] _ stateOuts[out] OR ones[out]; IF ones[out] # stp.ones[out] THEN { varOuts[out] _ TRUE; stp.locVar[out] _ TRUE; -- not consistantly true in this state stp.ones[out] _ FALSE}; ENDLOOP; first _ FALSE ENDLOOP; statePats _ CONS[stp, statePats] ENDLOOP; IF trace THEN log.PutRope["Fixup consistant state outputs\n"]; FOR out: INT IN [0..outTabIndex) DO stateOuts[out] _ stateOuts[out] AND NOT varOuts[out] ENDLOOP; IF trace THEN log.PutRope["Build main pattern sets\n"]; FOR stps: StatePatterns _ statePats, stps.rest WHILE stps#NIL DO stp: StatePattern _ stps.first; stp.state.outputsInv _ NIL; stp.state.outputs _ NIL; maxStNmLgth _ MAX[maxStNmLgth, stp.state.name.Length[]]; FOR out: INT IN [0..outTabIndex) DO IF stp.ones[out] AND NOT stateOuts[out] THEN { stp.locOne[out] _ TRUE; -- always true in this state but flakey in some others stp.ones[out] _ FALSE}; IF stp.zeros[out] AND NOT stateOuts[out] THEN ERROR; IF stp.zeros[out] THEN stp.state.outputsInv _ CONS[IndexOut[out], stp.state.outputsInv]; IF stp.ones[out] THEN stp.state.outputs _ CONS[IndexOut[out], stp.state.outputs]; ENDLOOP; FOR ss: StatePatternSets _ statePatSets, ss.rest WHILE ss#NIL DO IF stp.ones^ = ss.first.first.ones^ THEN {ss.first _ CONS[stp, ss.first]; EXIT}; REPEAT FINISHED => statePatSets _ CONS[LIST[stp], statePatSets] ENDLOOP; ENDLOOP; IF trace THEN { other: ROPES; states: ROPES; vars: ROPES; FOR out: INT IN [0..outTabIndex) DO IF varOuts[out] THEN vars _ CONS[IndexOut[out], vars] ELSE IF stateOuts[out] THEN states _ CONS[IndexOut[out], states] ELSE other _ CONS[IndexOut[out], other] ENDLOOP; PrintList["State Outputs", Sort[states], log]; PrintList["Variable Outputs", Sort[vars], log]; PrintList["Other Outputs", Sort[other], log]; FOR ss: StatePatternSets _ statePatSets, ss.rest WHILE ss#NIL DO log.PutRope["\n"]; FOR s: StatePatterns _ ss.first, s.rest WHILE s#NIL DO PrintStatePattern[s.first, maxStNmLgth, outTabIndex, log] ENDLOOP; ENDLOOP; log.PutRope["\n"]}; IF trace THEN log.PutRope["Partition each main set into groups\n"]; FOR main: StatePatternSets _ statePatSets, main.rest WHILE main#NIL DO grp: Group; variants: StatePatterns; varsNonUnique: StatePatterns; varsUnique: StatePatterns; mask: Bits _ NEW[BitArray _ ALL[FALSE]]; FOR s: StatePatterns _ main.first, s.rest WHILE s#NIL DO stp: StatePattern _ s.first; IF stp.locVar^ # emptyArray^ THEN variants _ CONS[stp, variants] ELSE FOR ss: StatePatternSets _ grp.conSets, ss.rest WHILE ss#NIL DO IF (~useLocOnes OR stp.locOne^ = ss.first.first.locOne^) THEN {ss.first _ CONS[stp, ss.first]; EXIT}; REPEAT FINISHED => grp.conSets _ CONS[LIST[stp], grp.conSets] ENDLOOP; ENDLOOP; FOR gv: StatePatterns _ variants, gv.rest WHILE gv#NIL DO FOR sub: StatePatternSets _ grp.conSets, sub.rest WHILE sub#NIL DO IF ComparePatterns[useLocOnes, gv.first, sub.first.first, outTabIndex].comp = indistinct THEN {varsNonUnique _ CONS[gv.first, varsNonUnique]; GOTO Loop}; REPEAT Loop => LOOP ENDLOOP; FOR otherGV: StatePatterns _ variants, otherGV.rest WHILE otherGV#NIL DO IF otherGV.first.state # gv.first.state AND ComparePatterns[useLocOnes, gv.first, otherGV.first, outTabIndex].comp = indistinct THEN {varsNonUnique _ CONS[gv.first, varsNonUnique]; EXIT}; REPEAT FINISHED => varsUnique _ CONS[gv.first, varsUnique] ENDLOOP; ENDLOOP; FOR gv: StatePatterns _ varsNonUnique, gv.rest WHILE gv#NIL DO FOR out: INT IN [0..outTabIndex) DO mask[out] _ mask[out] OR gv.first.locVar[out] ENDLOOP ENDLOOP; FOR gv: StatePatterns _ varsNonUnique, gv.rest WHILE gv#NIL DO stp: StatePattern _ gv.first; FOR ss: StatePatternSets _ grp.varSets, ss.rest WHILE ss#NIL DO IF ComparePatterns[useLocOnes, gv.first, ss.first.first, outTabIndex, mask].comp= indistinct THEN {ss.first _ CONS[stp, ss.first]; EXIT}; REPEAT FINISHED => grp.varSets _ CONS[LIST[stp], grp.varSets] ENDLOOP; ENDLOOP; FOR gv: StatePatterns _ varsUnique, gv.rest WHILE gv#NIL DO grp.varSets _ CONS[LIST[gv.first], grp.varSets] ENDLOOP; groups _ CONS[grp, groups]; ENDLOOP; IF trace THEN log.PutRope["Calc max group cnts and overall max cnt\n"]; FOR grps: Groups _ groups, grps.rest WHILE grps#NIL DO bias: NAT _ 0; grps.first.maxCnt _ 0; FOR ss: StatePatternSets _ grps.first.conSets, ss.rest WHILE ss#NIL DO grps.first.maxCnt _ MAX[grps.first.maxCnt, GetSizeIndex[useLocOnes, ss.first].size] ENDLOOP; bias _ grps.first.maxCnt; IF bias>0 THEN bias_ BitOps.TwoToThe[NBits[bias]]; FOR ss: StatePatternSets _ grps.first.varSets, ss.rest WHILE ss#NIL DO grps.first.maxCnt _ MAX[grps.first.maxCnt, bias+GetSizeIndex[useLocOnes, ss.first, bias>0].size] ENDLOOP; maxXtraCnt _ MAX[maxXtraCnt, grps.first.maxCnt]; ENDLOOP; FOR bit: INT IN [0..NBits[maxXtraCnt]) DO stateBitNm: IO.ROPE _ IO.PutFR["%g%g", IO.rope[xtraNm], IO.int[firstXtra+bit]]; []_OutIndex[stateBitNm] ENDLOOP; IF trace THEN log.PutF[" Max Xtra Cnt: %g\n", IO.int[maxXtraCnt]]; IF trace THEN log.PutRope["Sort groups and their sets by decreasing size\n"]; DO TwoGroups: TYPE = RECORD[g0, g1: Group]; done: BOOL _ TRUE; FOR grps: Groups _ groups, grps.rest WHILE grps#NIL AND grps.rest#NIL DO IF grps.first.maxCnt < grps.rest.first.maxCnt THEN { [grps.first, grps.rest.first] _ TwoGroups[grps.rest.first, grps.first]; done _ FALSE}; ENDLOOP; IF done THEN EXIT ENDLOOP; FOR grps: Groups _ groups, grps.rest WHILE grps#NIL AND grps.rest#NIL DO TwoSPs: TYPE = RECORD[g0, g1: StatePatterns]; DO done: BOOL _ TRUE; FOR ss: StatePatternSets _ grps.first.conSets, ss.rest WHILE ss#NIL AND ss.rest#NIL DO IF GetSizeIndex[useLocOnes, ss.first].size < GetSizeIndex[useLocOnes, ss.rest.first].size THEN {[ss.first, ss.rest.first] _ TwoSPs[ss.rest.first, ss.first]; done _ FALSE}; ENDLOOP; IF done THEN EXIT ENDLOOP; DO done: BOOL _ TRUE; FOR ss: StatePatternSets _ grps.first.varSets, ss.rest WHILE ss#NIL AND ss.rest#NIL DO IF GetSizeIndex[useLocOnes, ss.first].size < GetSizeIndex[useLocOnes, ss.rest.first].size THEN {[ss.first, ss.rest.first] _ TwoSPs[ss.rest.first, ss.first]; done _ FALSE}; ENDLOOP; IF done THEN EXIT ENDLOOP; ENDLOOP; IF trace THEN log.PutRope["Assign Xtra State bits\n"]; FOR bit: INT IN [0..20) DO xtraOrder[bit] _ bit ENDLOOP; FOR grps: Groups _ groups, grps.rest WHILE grps#NIL DO ReOrderXtras: PROC[biasBit: INT _ -1] = { -- biasBit is last DO done: BOOL _ TRUE; TwoBits: TYPE = RECORD[b0, b1: NAT]; FOR bit: INT IN [1..NBits[maxXtraCnt]) DO i1: INT _ xtraOrder[bit-1]; i2: INT _ xtraOrder[bit]; IF i1=biasBit OR i2#biasBit AND (xtraCnts[i1] > xtraCnts[i2]) THEN { [xtraOrder[bit-1], xtraOrder[bit]] _ TwoBits [xtraOrder[bit], xtraOrder[bit-1]]; done _ FALSE} ENDLOOP; IF done THEN EXIT ENDLOOP}; MakeAssignments: PROC[stps: StatePatterns, size, index: NAT, biasIt: BOOL] = { FOR s: StatePatterns _ stps, s.rest WHILE s#NIL DO AddBit: PROC[idx: INT] = { stateBitNm: IO.ROPE _ IO.PutFR["%g%g", IO.rope[xtraNm], IO.int[firstXtra+idx]]; FOR lst: Transitions _ stp.state.inTrans, lst.rest WHILE lst#NIL DO xtraCnts[idx] _ xtraCnts[idx] + 1 ENDLOOP; -- times it will prob be used IF ~fsm.outInAll AND ~RopeList.Memb[fsm.outIns, stateBitNm] THEN fsm.outIns _ CONS[stateBitNm, fsm.outIns]; stp.state.outputs _ CONS[stateBitNm, stp.state.outputs]; stp.ones[OutIndex[stateBitNm]] _ TRUE; Mod[" Adding output ", stateBitNm, " to state ", stp.state.name.Cat[".\n"]]}; stp: StatePattern _ s.first; numb: INT _ index; index _ index+1; IF biasIt THEN AddBit[biasBit]; FOR bit: INT IN [0..NBits[size]) DO IF (numb MOD 2)=1 THEN AddBit[xtraOrder[bit]]; numb _ numb/2 ENDLOOP; ENDLOOP}; size, index: NAT _ 0; biasBitIndex: INT _ MAX[0, NBits[maxXtraCnt]-2]; -- pick next to last biasBit: INT _ xtraOrder[biasBitIndex]; ReOrderXtras[biasBit]; IF trace THEN log.PutF["Bias bit: %g\n", IO.int[biasBit]]; FOR ss: StatePatternSets _ grps.first.conSets, ss.rest WHILE ss#NIL DO [size, index] _ GetSizeIndex[useLocOnes, ss.first]; MakeAssignments[ss.first, size, index, FALSE]; IF ~trace THEN LOOP; TerminalIO.PutRope["\n"]; FOR s: StatePatterns _ ss.first, s.rest WHILE s#NIL DO PrintStatePattern[s.first, maxStNmLgth, outTabIndex, log] ENDLOOP; ReOrderXtras[biasBit] ENDLOOP; FOR ss: StatePatternSets _ grps.first.varSets, ss.rest WHILE ss#NIL DO [size, index] _ GetSizeIndex[useLocOnes, ss.first, grps.first.conSets#NIL]; MakeAssignments[ss.first, size, index, grps.first.conSets#NIL]; IF ~trace THEN LOOP; TerminalIO.PutRope["\n"]; FOR s: StatePatterns _ ss.first, s.rest WHILE s#NIL DO PrintStatePattern[s.first, maxStNmLgth, outTabIndex, log]; ENDLOOP; ReOrderXtras[biasBit] ENDLOOP; ReOrderXtras[-1]; IF trace THEN TerminalIO.PutRope["\n"]; ENDLOOP; IF trace THEN log.PutRope["Check pairs and add inverted bits\n"]; FOR test1: StatePatterns _ statePats, test1.rest WHILE test1#NIL DO FOR test2: StatePatterns _ test1.rest, test2.rest WHILE test2#NIL DO comp: StatePatternComp; loc: INT _ -1; bitNm: IO.ROPE; smSp: StatePattern; lrgSp: StatePattern; [comp, loc] _ ComparePatterns[useLocOnes, test1.first, test2.first, outTabIndex]; IF comp=indistinct THEN ERROR; bitNm _ IndexOut[loc]; smSp _ IF comp=aLrg THEN test2.first ELSE test1.first; lrgSp _ IF comp=aLrg THEN test1.first ELSE test2.first; IF NOT RopeList.Memb[smSp.state.outputsInv, bitNm] THEN smSp.state.outputsInv _ CONS[bitNm, smSp.state.outputsInv]; IF NOT RopeList.Memb[lrgSp.state.outputs, bitNm] THEN lrgSp.state.outputs _ CONS[bitNm, lrgSp.state.outputs]; smSp.zeros[loc] _ TRUE; Mod[" Adding ~", bitNm, " to state ", smSp.state.name.Cat[" to exclude ", lrgSp.state.name, ".\n"]]; ENDLOOP; ENDLOOP; RemoveStateOutputsFromInTransitions[fsm]; IF trace THEN log.PutRope["Sort statePats by state name\n"]; DO TwoStatePatterns: TYPE = RECORD[s1, s2: StatePattern]; done: BOOL _ TRUE; FOR sps: StatePatterns _ statePats, sps.rest WHILE sps#NIL AND sps.rest#NIL DO IF Rope.Compare[sps.first.state.name, sps.rest.first.state.name] = greater THEN {[sps.first, sps.rest.first] _ TwoStatePatterns[sps.rest.first, sps.first]; done _ FALSE} ENDLOOP; IF done THEN EXIT ENDLOOP; IF NOT trace THEN { IF firstMod THEN log.PutRope["done - state definitions unchanged\n"] ELSE log.PutRope["done - state definitions changed\n"]; RETURN}; log.PutF["\nSummary of state specificatons for %g: \n\n", IO.rope[fsm.name]]; FOR sps: StatePatterns _ statePats, sps.rest WHILE sps#NIL DO sps.first.state.outputs _ Sort[sps.first.state.outputs]; sps.first.state.outputsInv _ Sort[sps.first.state.outputsInv]; log.PutF["%g\n", IO.rope[sps.first.state.name]]; FOR outs: ROPES _ sps.first.state.outputs, outs.rest WHILE outs#NIL DO log.PutF[" %g\n", IO.rope[outs.first]]; ENDLOOP; FOR outs: ROPES _ sps.first.state.outputsInv, outs.rest WHILE outs#NIL DO log.PutF[" ~%g\n", IO.rope[outs.first]]; ENDLOOP ENDLOOP; FOR sps: StatePatterns _ statePats, sps.rest WHILE sps#NIL DO PrintStatePattern[sps.first, maxStNmLgth, outTabIndex, log] ENDLOOP; log.PutRope["Outputs:\n"]; FOR out: INT IN [0..outTabIndex) DO log.PutF["%4g: %g\n", IO.int[out], IO.rope[IndexOut[out]]] ENDLOOP; log.PutChar[IO.CR]}; GetPatternBitCode: PROC[pat: StatePattern, bit: INT] RETURNS[code: PatternBitCode] = {RETURN[SELECT TRUE FROM pat.zeros [bit] => F, pat.ones [bit] => T, pat.locOne [bit] => t, pat.locVar [bit] => v, ENDCASE => d]}; GetSizeIndex: PROC[useLocOnes: BOOL, stps: StatePatterns, bias: BOOL _ FALSE] RETURNS[size, index: NAT _ 0] = { FOR s: StatePatterns _ stps, s.rest WHILE s#NIL DO size _ size+1 ENDLOOP; size _ size + index}; PrintList: PROC[head: IO.ROPE, list: ROPES, log: IO.STREAM _ NIL] = { IF log=NIL THEN log _ TerminalIO.TOS[]; log.PutF["%g\n", IO.rope[head]]; FOR outs: ROPES _ list, outs.rest WHILE outs#NIL DO log.PutF[" %g\n", IO.rope[outs.first]]; ENDLOOP}; PrintStatePattern: PROC [sp: StatePattern, maxStNmLgth, size: NAT, log: IO.STREAM _ NIL] = { IF log=NIL THEN log _ TerminalIO.TOS[]; log.PutF["%g", IO.rope[sp.state.name]]; FOR ii: INT IN [sp.state.name.Length..maxStNmLgth] DO log.PutChar[IO.SP] ENDLOOP; FOR out: INT IN [0..size) DO char: CHAR _ SELECT GetPatternBitCode[sp, out] FROM F => '0, T => '1, t => 't, v => 'v, d => '., ENDCASE => ERROR; IF (out MOD 4) = 0 THEN log.PutChar[IO.SP]; log.PutChar[char] ENDLOOP; log.PutChar[IO.CR]}; ComparePatterns: PROC [useLocOnes: BOOL, a, b: StatePattern, size: NAT _ arrayBits, skip: Bits _ NIL] RETURNS[comp: StatePatternComp _ indistinct, loc: INT _ -1] = { priority: NAT _ 0; FOR bit: INT IN [0..size) DO PatternBitCodePair: TYPE = RECORD[a, b: PatternBitCode]; pp: PatternBitCodePair; IF skip#NIL AND skip[bit] THEN LOOP; pp _ [GetPatternBitCode[a, bit], GetPatternBitCode[b, bit]]; SELECT pp FROM [T,F] => {comp _ aLrg; priority _ 4; loc _ bit}; [F,T] => {comp _ bLrg; priority _ 4; loc _ bit}; [T,d] => IF priority<4 THEN {comp _ aLrg; priority _ 3; loc _ bit}; [d,T] => IF priority<4 THEN {comp _ bLrg; priority _ 3; loc _ bit}; [t,F] => IF priority<3 THEN {comp _ aLrg; priority _ 2; loc _ bit}; [F,t] => IF priority<3 THEN {comp _ bLrg; priority _ 2; loc _ bit}; [t,d] => IF priority<2 THEN {comp _ aLrg; priority _ 1; loc _ bit}; [d,t] => IF priority<2 THEN {comp _ bLrg; priority _ 1; loc _ bit}; ENDCASE => LOOP ENDLOOP; IF priority<3 AND ~useLocOnes THEN RETURN[indistinct, -1]}; IncludeStateOutputsInTransitions: PROC[fsm: FSMData] = { FOR states: States _ fsm.states, states.rest WHILE states#NIL DO FOR outs: ROPES _ states.first.outputs, outs.rest WHILE outs#NIL DO FOR trans: Transitions _ states.first.inTrans, trans.rest WHILE trans#NIL DO IF ~ RopeList.Memb[trans.first.outputs, outs.first] THEN trans.first.outputs _ CONS[outs.first, trans.first.outputs] ENDLOOP; ENDLOOP; ENDLOOP}; RemoveStateOutputsFromInTransitions: PROC[fsm: FSMData] = { FOR states: States _ fsm.states, states.rest WHILE states#NIL DO FOR outs: ROPES _ states.first.outputs, outs.rest WHILE outs#NIL DO FOR trans: Transitions _ states.first.inTrans, trans.rest WHILE trans#NIL DO trans.first.outputs _ RopeList.Remove[trans.first.outputs, outs.first]; ENDLOOP; ENDLOOP; ENDLOOP; FOR trans: Transitions _ fsm.initialState.inTrans, trans.rest WHILE trans#NIL DO IF trans.first.enable=NIL THEN { FOR outs: ROPES _ trans.first.outputs, outs.rest WHILE outs#NIL DO IF ~ RopeList.Memb[fsm.initialState.outputs, outs.first] THEN fsm.initialState.outputs _ CONS[outs.first, fsm.initialState.outputs] ENDLOOP; EXIT} ENDLOOP}; NBits: PUBLIC PROC [n: INT] RETURNS [INT] = { -- BitOps.NBits is wrong at 1 val: INT _ 1; IF n<=0 THEN RETURN [0]; FOR i: NAT IN [0..33] DO IF val>=n THEN RETURN[i]; val _ val*2; ENDLOOP; ERROR}; Sort: PROC[list: ROPES] RETURNS[sorted: ROPES] = {RETURN[RopeList.Sort[list, RopeList.Compare]]}; IsXtra: PROC[nm: IO.ROPE] RETURNS[BOOL] = {RETURN[nm.Substr[0,4].Equal[xtraNm]]}; CrossLinkTransitions: PROC[fsm: FSMData] = { FOR states: States _ fsm.states, states.rest WHILE states#NIL DO IF states.first.inTrans # NIL THEN ERROR ENDLOOP; -- only done once fsm.initialState.inTrans _ LIST[NEW[FSM.TransitionRec _ [target: fsm.initialState]]]; FOR states: States _ fsm.states, states.rest WHILE states#NIL DO FOR trans: Transitions _ states.first.outTrans, trans.rest WHILE trans#NIL DO trans.first.target.inTrans _ CONS[trans.first, trans.first.target.inTrans]; ENDLOOP; ENDLOOP}; SplitVariableStates: PROC[fsm: FSMData] = { SameInTransOuts: PROC[l1, l2: ROPES] RETURNS[same: BOOL _ TRUE] = { FOR list: ROPES _ l1, list.rest WHILE list#NIL DO IF NOT RopeList.Memb[l2, list.first] THEN RETURN[FALSE] ENDLOOP; FOR list: ROPES _ l2, list.rest WHILE list#NIL DO IF NOT RopeList.Memb[l1, list.first] THEN RETURN[FALSE] ENDLOOP}; newFSMStates: States; CrossLinkTransitions[fsm]; IncludeStateOutputsInTransitions[fsm]; FOR states: States _ fsm.states, states.rest WHILE states#NIL DO newStates: States; index: INT _ 0; sets: LIST OF Transitions; FOR trans: Transitions _ states.first.inTrans, trans.rest WHILE trans#NIL DO FOR ss: LIST OF Transitions _ sets, ss.rest WHILE ss#NIL DO IF SameInTransOuts[trans.first.outputs, ss.first.first.outputs] THEN {ss.first _ CONS[trans.first, ss.first]; EXIT}; REPEAT FINISHED => sets _ CONS[LIST[trans.first], sets] ENDLOOP; ENDLOOP; IF sets=NIL THEN ERROR; IF sets.rest=NIL THEN {newFSMStates _ CONS[states.first, newFSMStates]; LOOP}; FOR ss: LIST OF Transitions _ sets, ss.rest WHILE ss#NIL DO newState: State _ NEW[FSM.StateRec _ [ name: IO.PutFR["%gAlt%g", IO.rope[states.first.name], IO.int[index]], outputs: RopeList.CopyTopList[states.first.outputs], outputsInv: RopeList.CopyTopList[states.first.outputsInv], outTrans: NIL, inTrans: ss.first, srcCellInst: states.first.srcCellInst ]]; newFSMStates _ CONS[newState, newFSMStates]; newStates _ CONS[newState, newStates]; FOR trans: Transitions _ newState.inTrans, trans.rest WHILE trans#NIL DO trans.first.target _ newState ENDLOOP; index _ index+1; ENDLOOP; FOR trans: Transitions _ states.first.outTrans, trans.rest WHILE trans#NIL DO list: Transitions; FOR oldIns: Transitions _ trans.first.target.inTrans, oldIns.rest WHILE oldIns#NIL DO IF oldIns.first#trans.first THEN list _ CONS[oldIns.first, list] ENDLOOP; trans.first.target.inTrans _ list ENDLOOP; FOR trans: Transitions _ states.first.outTrans, trans.rest WHILE trans#NIL DO FOR nStates: States _ newStates, nStates.rest WHILE nStates#NIL DO newTrans: Transition _ NEW[FSM.TransitionRec _ [ enable: BoolEx.Copy[trans.first.enable], outputs: RopeList.CopyTopList[trans.first.outputs], target: trans.first.target, srcCellInst: trans.first.srcCellInst ] ]; nStates.first.outTrans _ CONS[newTrans, nStates.first.outTrans]; newTrans.target.inTrans _ CONS[newTrans, newTrans.target.inTrans] ENDLOOP ENDLOOP; ENDLOOP; fsm.states _ newFSMStates}; RecastFSM: Core.RecastProc = { fsmData: FSMData _ NARROW[me.data]; exprImplKey: ATOM _ NARROW[CoreProperties.GetCellTypeProp[me, $ExprImplKey].value]; table: RefTab.Ref; impl: Core.CellType _ ImplementExpr[fsmData.mach, exprImplKey, fsmData.register, fsmData.initialState.outputs]; table _ CreateNmBindingTable[impl.public, me.public]; new _ CoreClasses.CreatePermutedRecordCell[ iconPublic: me.public, schCell: impl, table: table, name: fsmData.name.Cat["Xlate"]]; SELECT exprImplKey FROM $PLACompress, $PLA=> PWCore.SetAbutY[new]; $SC, $SCompress, xilinxKey => { }; -- PWCore.SetLayout[new, $SC]; ENDCASE => ERROR; }; ImplementExprFile: PUBLIC PROC[file: IO.ROPE, register: RegisterType, exprImplKey: ATOM] RETURNS[Core.CellType] = { mach: Expression _ BoolEx.ReadExprFile[file]; impl: Core.CellType _ ImplementExpr[mach, exprImplKey, register]; RETURN[impl]}; DoesInternalRegisterFeedBack: PROC[exprImplKey: ATOM] RETURNS[BOOL] = { RETURN[SELECT exprImplKey FROM $SC, $SCompress, xilinxKey => TRUE ENDCASE => FALSE]}; ImplementExpr: PROC[mach: Expression, exprImplKey: ATOM, reg: RegisterType, initOne: LIST OF IO.ROPE _ NIL] RETURNS[impl: Core.CellType] = { name: IO.ROPE _ NARROW[NARROW[mach, LIST OF REF].rest.first]; log: IO.STREAM _ TerminalIO.TOS[]; SELECT exprImplKey FROM $PLA, $PLACompress => {InstallPackage["PLAGen"]}; $SC, $SCompress, xilinxKey => {InstallPackage["CellLibraries"]}; ENDCASE => ERROR; log.PutF["Recasting FSM: %g, Implementation: %g\n", IO.rope[name], IO.atom[exprImplKey]]; SELECT exprImplKey FROM $PLA, $PLACompress => { compSum: BOOL _ exprImplKey=$PLACompress; IF compSum THEN { log.PutRope[" full compression ... "]; mach _ PLAOps.CompressExpression[mach, TRUE]; } ELSE log.PutRope[" fast compression ... "]; log.PutRope["build it.\n"]; SELECT reg FROM edgeTriggered => impl _ PLAGen.ExpToPlaCell[mach, $clockedRt]; twoPhase => impl _ PLAGen.ExpToPlaCell[mach, $latchedRt]; -- or $latchedBot ? none => impl _ PLAGen.ExpToPlaCell[mach, $bufferedBot]; ENDCASE => ERROR}; $SC, $SCompress, xilinxKey => { compSum: BOOL _ exprImplKey=$SCompress; key: Expression; multiLevel: Expression; cacheNm: IO.ROPE _ IF compSum THEN name.Cat["Comp"] ELSE name; [key, multiLevel] _ GetExprTranslationCache[cacheNm]; IF BoolEx.Compare[key, mach]=equal THEN log.PutRope["using cache ... "] ELSE { compExp: Expression _ mach; IF compSum THEN { log.PutRope[" full compression ... "]; compExp _ PLAOps.CompressExpression[compExp, TRUE]; } ELSE log.PutRope[" fast compression ... "]; log.PutRope["factor ... "]; multiLevel _ PLAOps.FactorExpression[compExp]; IF exprImplKey#xilinxKey THEN { log.PutRope["buffer fanouts ... "]; BufferLargeFanouts[multiLevel, 4]; }; log.PutRope["cache ... "]; SetExprTranslationCache[cacheNm, mach, multiLevel]; }; log.PutRope["build it.\n"]; SELECT reg FROM edgeTriggered => impl _ BuildSCMachineInternal[multiLevel, initOne]; none => impl _ BuildSCGates[multiLevel]; ENDCASE => ERROR}; ENDCASE => ERROR; }; CreateNmBindingTable: PROC [wire1, wire2: Core.Wire] RETURNS [table: RefTab.Ref] = { RegisterNames: PROC [w: Core.Wire] = { nm: IO.ROPE _ CoreOps.GetShortWireName[w]; IF nm.Length[]=0 THEN ERROR; -- unnamed atomic IF NOT SymTab.Insert[symTab, nm, w] AND SymTab.Fetch[symTab, nm].val#w THEN ERROR}; -- atomics with identical short names MakeTable: PROC [w: Core.Wire] = { nm: IO.ROPE _ CoreOps.GetShortWireName[w]; cw: Core.Wire _ NARROW[SymTab.Fetch[symTab, nm].val]; IF cw=NIL THEN TerminalIO.PutF["Wire corresponding to %g not found\n", IO.rope[nm]] ELSE IF NOT RefTab.Insert[table, w, cw] AND RefTab.Fetch[table, w].val#cw THEN ERROR}; -- can't happen symTab: SymTab.Ref _ SymTab.Create[]; table _ RefTab.Create[]; CoreOps.VisitRootAtomics[wire2, RegisterNames]; CoreOps.VisitRootAtomics[wire1, MakeTable]}; ConvertToExp: PUBLIC PROC[fsm: FSMData] RETURNS[machine: LIST OF REF] = {machine _ ConvertToExpXilinx[fsm]}; ConvertToExpXilinx: PROC[fsm: FSMData, xilinx: BOOL _ FALSE] RETURNS[machine: LIST OF REF] = { OrOutTerm: PROC[out: IO.ROPE, term: Expression] = { IF term = NIL THEN term _ SymTab.Fetch[outTab, out].val ELSE IF SymTab.Fetch[outTab, out].found THEN term _ BoolEx.OpExpr[or, term, SymTab.Fetch[outTab, out].val]; []_SymTab.Store[outTab, out, term]}; BuildOutputDefs: SymTab.EachPairAction = { output: LIST OF REF _ LIST[BoolEx.OpNm[out], key, val]; machine _ CONS[output, machine]}; resetTrue: IO.ROPE _ "Reset"; resetFalse: IO.ROPE _ "NOTReset"; resetFalseExpr: LIST OF REF _ LIST[BoolEx.OpNm[not], resetTrue]; resetFalseDef: LIST OF REF _ LIST[BoolEx.OpNm[var], resetFalse, resetFalseExpr]; outTab: SymTab.Ref _ SymTab.Create[]; stateTransIdx: INT _ 0; machine _ BuildStateDefs[fsm].rest.rest; FOR sts: States _ fsm.states, sts.rest WHILE sts#NIL DO stateExpr: LIST OF REF _ LIST[sts.first.name]; FOR outTrans: Transitions _ sts.first.outTrans, outTrans.rest WHILE outTrans#NIL DO enableAndState: BoolEx.Expression _ IF outTrans.first.enable=NIL THEN sts.first.name ELSE BoolEx.OpExpr[and, sts.first.name, outTrans.first.enable]; term: BoolEx.Expression _ IF xilinx THEN enableAndState ELSE NARROW[BoolEx.OpExpr[and, resetFalse, enableAndState]]; stateTransNm: IO.ROPE _ IO.PutFR["xSt%02g", IO.int[stateTransIdx]]; stateTransDef: LIST OF REF _ LIST[BoolEx.OpNm[var], stateTransNm, term]; stateTransIdx _ stateTransIdx + 1; machine _ CONS[stateTransDef, machine]; FOR outs: ROPES _ outTrans.first.target.outputs, outs.rest WHILE outs#NIL DO OrOutTerm[outs.first, stateTransNm] ENDLOOP; FOR outs: ROPES _ outTrans.first.outputs, outs.rest WHILE outs#NIL DO IF NOT RopeList.Memb[outTrans.first.target.outputs, outs.first] THEN OrOutTerm[outs.first, stateTransNm] ENDLOOP ENDLOOP ENDLOOP; IF NOT xilinx THEN FOR outs: ROPES _ fsm.initialState.outputs, outs.rest WHILE outs#NIL DO OrOutTerm[outs.first, resetTrue]; ENDLOOP; [] _ SymTab.Pairs[outTab, BuildOutputDefs]; IF NOT xilinx THEN machine _ CONS[resetFalseDef, machine]; machine _ CONS[BoolEx.OpNm[mach], CONS[fsm.name, machine]]; machine _ NARROW[PLAOps.CompressExpression[machine, FALSE]]; BoolEx.Sort[machine]}; BuildStateDefs: PROC[fsmData: FSMData] RETURNS[statesExp: LIST OF REF] = { inTab: SymTab.Ref _ SymTab.Create[]; FOR sts: States _ fsmData.states, sts.rest WHILE sts#NIL DO term: LIST OF REF; def: LIST OF REF; name: IO.ROPE _ sts.first.name; IF name.Length[]=0 THEN ERROR; FOR outputs: ROPES _ sts.first.outputsInv, outputs.rest WHILE outputs#NIL DO inv: LIST OF REF _ LIST[BoolEx.OpNm[not], outputs.first]; term _ CONS[inv, term] ENDLOOP; FOR outputs: ROPES _ sts.first.outputs, outputs.rest WHILE outputs#NIL DO IF ~fsmData.outInAll AND ~RopeList.Memb[fsmData.outIns, outputs.first] THEN LOOP; term _ CONS[outputs.first, term] ENDLOOP; term _ CONS[BoolEx.OpNm[and], term]; def _ LIST[BoolEx.OpNm[var], name, term]; IF NOT SymTab.Insert[inTab, name, term] THEN { TerminalIO.PutF["There are multiple states with the name: %g\n", IO.rope[name]]; ERROR}; statesExp _ CONS[def, statesExp] ENDLOOP; statesExp _ CONS[BoolEx.OpNm[mach], CONS[fsmData.name, statesExp]]; BoolEx.Sort[statesExp]}; FSMSimState: TYPE = REF FSMSimStateRec; FSMSimStateRec: TYPE = RECORD [ cell: Core.CellType _ NIL, resetPort: Ports.Port _ NIL, clockPort: Ports.Port _ NIL, phAPort: Ports.Port _ NIL, phBPort: Ports.Port _ NIL, currentTrans: FSM.TransitionRec _ [ ], nextTrans: FSM.TransitionRec _ [ ], portTable: SymTab.Ref _ NIL, lastClockState: Ports.Level _ L, previousTarget: FSM.State _ NIL, recordState: Ports.LevelSequence _ NIL, forceInit: BOOL _ TRUE]; FSMStateToMaxChars: RosemaryUser.StateToMaxCharsProc = { state: FSMSimState _ NARROW[stateAny]; fsmData: FSMData _ NARROW[state.cell.data]; maxChars _ Rope.Length[unknownStateName]; FOR states: FSM.States _ fsmData.states, states.rest UNTIL states=NIL DO stateLength: NAT _ Rope.Length[states.first.name]; maxChars _ MAX[maxChars, stateLength]; ENDLOOP; }; DWordAsBits: TYPE = PACKED ARRAY [0..32) OF BOOL; unknownStateName: Rope.ROPE _ "XX"; FSMStateToRope: RosemaryUser.StateToRopeProc = { IF value[0]=X THEN rope _ unknownStateName ELSE { asBits: DWordAsBits _ ALL[FALSE]; targetAny: REF ANY _ NIL; target: FSM.State _ NIL; FOR bit: NAT IN [0..32) DO asBits[bit] _ IF value[bit]=H THEN TRUE ELSE FALSE; ENDLOOP; TRUSTED{targetAny _ LOOPHOLE[asBits]}; target _ NARROW[targetAny]; rope _ target.name} }; InitFSM: Rosemary.InitProc = { SetOutDrive: PROC[name: IO.ROPE] = { outNm: IO.ROPE _ IF fsmData.register#none THEN name ELSE Rope.Cat["Nxt", name]; port: Ports.Port _ NARROW[SymTab.Fetch[state.portTable, outNm].val]; IF port#NIL THEN Ports.PutDrive[port, drive]}; state: FSMSimState _ NEW[FSMSimStateRec]; fsmData: FSMData _ NARROW[cellType.data]; public: Core.Wire _ cellType.public; state.cell _ cellType; state.resetPort _ IF NARROW[CoreProperties.GetCellTypeProp[cellType, $ExprImplKey], ATOM]=xilinxKey THEN NIL ELSE FindPortFromPublic[public, p, "Reset"]; SELECT fsmData.register FROM edgeTriggered => state.clockPort _ FindPortFromPublic[public, p, "Clock"]; twoPhase => { state.phAPort _ FindPortFromPublic[public, p, "PhA"]; state.phBPort _ FindPortFromPublic[public, p, "PhB"]}; none => { }; ENDCASE => ERROR; state.portTable _ SymTab.Create[]; FOR publics: ROPES _ fsmData.publics, publics.rest UNTIL publics=NIL DO IF NOT SymTab.Insert[state.portTable, publics.first, FindPortFromPublic[public, p, publics.first]] THEN ERROR ENDLOOP; FOR states: States _ fsmData.states, states.rest UNTIL states=NIL DO FOR outputs: ROPES _ states.first.outputs, outputs.rest UNTIL outputs=NIL DO SetOutDrive[outputs.first] ENDLOOP; FOR ts: Transitions _ states.first.outTrans, ts.rest UNTIL ts=NIL DO FOR outputs: ROPES _ ts.first.outputs, outputs.rest UNTIL outputs=NIL DO SetOutDrive[outputs.first] ENDLOOP ENDLOOP ENDLOOP; state.recordState _ NEW[Ports.LevelSequenceRec[32]]; FOR i: NAT IN [0..32) DO state.recordState[i] _ X ENDLOOP; stateValue _ state.recordState; stateAny _ state; }; FindPortFromPublic: PROC [rootWire: Core.Wire, rootPort: Ports.Port, name: IO.ROPE] RETURNS [port: Ports.Port] = { port _ rootPort[Ports.PortIndex[rootWire, name]] }; EvalFSM: Rosemary.EvalProc = { EvalExpr: PROC [expr: Expression] RETURNS [value: Ports.Level] = { WITH expr SELECT FROM r: IO.ROPE => { port: Ports.Port _ NARROW[SymTab.Fetch[state.portTable, r].val]; value _ port.l }; te: LIST OF REF => { op: OpIndex _ BoolEx.NmOp[NARROW[te.first]]; SELECT op FROM not => { value _ SELECT EvalExpr[te.rest.first] FROM L => H, H => L, X => X, ENDCASE => ERROR }; and => { value _ H; FOR subExprs: LIST OF REF _ te.rest, subExprs.rest UNTIL subExprs=NIL DO subValue: Ports.Level _ EvalExpr[subExprs.first]; value _ SELECT TRUE FROM value=H AND subValue=H => H, value=L OR subValue=L => L, ENDCASE => X; IF value=L THEN EXIT; ENDLOOP }; or => { value _ L; FOR subExprs: LIST OF REF _ te.rest, subExprs.rest UNTIL subExprs=NIL DO subValue: Ports.Level _ EvalExpr[subExprs.first]; value _ SELECT TRUE FROM value=L AND subValue=L => L, value=H OR subValue=H => H, ENDCASE => X; IF value=H THEN EXIT; ENDLOOP }; ENDCASE => ERROR }; ENDCASE => ERROR }; SetRopeListToLevel: PROC [ropeList: ROPES, level: Ports.Level] = { FOR os: ROPES _ ropeList, os.rest UNTIL os=NIL DO outId: IO.ROPE _ IF fsmData.register=none THEN Rope.Cat["Nxt", os.first] ELSE os.first; port: Ports.Port _ NARROW[SymTab.Fetch[state.portTable, outId].val]; IF port = NIL THEN port _ NARROW[SymTab.Fetch[state.portTable, os.first].val]; IF port#NIL THEN port.l _ level; ENDLOOP }; CheckStateRopeList: PROC [ropeList: ROPES, level: Ports.Level] RETURNS[ok: BOOL _ TRUE] = { FOR os: ROPES _ ropeList, os.rest WHILE os#NIL AND ok DO port: Ports.Port; IF ~fsmData.outInAll AND ~RopeList.Memb[fsmData.outIns, os.first] THEN LOOP; port _ NARROW[SymTab.Fetch[state.portTable, os.first].val]; ok _ ok AND (port.l = level) ENDLOOP }; state: FSMSimState _ NARROW[stateAny]; fsmData: FSMData _ NARROW[state.cell.data]; clockBad: BOOL _ (fsmData.register=edgeTriggered AND ~clockEval AND state.clockPort.l=X) OR (fsmData.register=twoPhase AND (state.phAPort.l=X OR state.phBPort.l=X OR (state.phAPort.l=H AND state.phBPort.l=H))); clockBefore: BOOL _ NOT clockBad AND (fsmData.register=edgeTriggered AND ~clockEval AND state.clockPort.l=L) OR (fsmData.register=twoPhase AND state.phBPort.l=H); clockNew: BOOL _ NOT clockBefore AND (fsmData.register=edgeTriggered AND ~clockEval AND state.clockPort.l=H AND state.lastClockState=L) OR (fsmData.register=twoPhase AND state.phAPort.l=H); IF fsmData.register=none THEN { -- current state based on inputs alone state.currentTrans _ [ ]; FOR sl: States _ fsmData.states, sl.rest UNTIL sl=NIL DO IF NOT CheckStateRopeList[sl.first.outputs, H] THEN LOOP; IF NOT CheckStateRopeList[sl.first.outputsInv, L] THEN LOOP; IF state.currentTrans.target#NIL THEN {state.currentTrans.target_NIL; EXIT}; state.currentTrans.target _ sl.first; ENDLOOP}; IF clockBad THEN { state.previousTarget _ NIL; state.nextTrans _ []; state.currentTrans _ []}; IF clockBefore OR fsmData.register=none THEN {-- next trans using inputs and current SELECT TRUE FROM state.forceInit OR (state.resetPort#NIL AND state.resetPort.l=H) => { state.nextTrans _ [target: fsmData.initialState]; state.forceInit _ FALSE; }; state.resetPort=NIL OR state.resetPort.l=L => { state.nextTrans _ []; IF state.currentTrans.target#NIL THEN { FOR ts: Transitions _ state.currentTrans.target.outTrans, ts.rest UNTIL ts=NIL DO enableLevel: Ports.Level _ IF ts.first.enable=NIL THEN H ELSE EvalExpr[ts.first.enable]; SELECT enableLevel FROM X => {state.nextTrans _ []; EXIT}; H => { IF state.nextTrans.target#NIL THEN {state.nextTrans _ []; EXIT}; state.nextTrans _ ts.first^ }; ENDCASE; ENDLOOP } }; state.resetPort.l=X => state.nextTrans _ []; ENDCASE => ERROR }; IF clockNew OR fsmData.register=none THEN state.currentTrans _ state.nextTrans; FOR sl: States _ fsmData.states, sl.rest UNTIL sl=NIL DO level: Ports.Level _ IF state.currentTrans.target=NIL THEN X ELSE L; SetRopeListToLevel[sl.first.outputs, level]; FOR ts: Transitions _ sl.first.outTrans, ts.rest UNTIL ts=NIL DO SetRopeListToLevel[ts.first.outputs, level] ENDLOOP ENDLOOP; IF state.currentTrans.target#NIL THEN { SetRopeListToLevel[state.currentTrans.outputs, H]; SetRopeListToLevel[state.currentTrans.target.outputs, H]; IF state.currentTrans.target#state.previousTarget THEN { asBits: DWordAsBits _ LOOPHOLE[state.currentTrans.target]; FOR bit: NAT IN [0..32) DO state.recordState[bit] _ IF asBits[bit] THEN H ELSE L; ENDLOOP; stateValue _ state.recordState; state.previousTarget _ state.currentTrans.target} } ELSE IF state.previousTarget#NIL THEN { FOR bit: NAT IN [0..32) DO state.recordState[bit] _ X ENDLOOP; stateValue _ state.recordState; state.previousTarget _ NIL}; IF ~clockEval AND fsmData.register=edgeTriggered THEN state.lastClockState _ state.clockPort.l }; BuildSCMachine: PUBLIC PROC [mach: Expression] RETURNS [Core.CellType] = {RETURN[BuildSCMachineInternal[mach]]}; BuildSCMachineInternal: PROC [mach: Expression, initOne: LIST OF IO.ROPE _ NIL] RETURNS [Core.CellType] = { AddPA: PROC[type: {pub, int}, wire: Core.Wire] = { name: IO.ROPE _ CoreOps.GetShortWireName[wire]; IF type=pub AND ~IsXtra[name] THEN { IF ~RopeList.Memb[publics, name] THEN publics _ CONS[name, publics]; IF RopeList.Memb[intOnlys, name] THEN ERROR} ELSE { IF ~RopeList.Memb[intOnlys, name] THEN intOnlys _ CONS[name, intOnlys]; IF RopeList.Memb[publics, name] THEN ERROR}; pas _ CONS[[wire, name], pas]}; logic: Core.CellType _ BuildSCGates[mach]; reg: Core.CellType _ BuildSCRegisterInternal[mach, initOne]; vdd: IO.ROPE _ "Vdd"; gnd: IO.ROPE _ "Gnd"; clock: IO.ROPE _ "Clock"; instances: CoreCreate.CellInstances; machName: IO.ROPE _ NARROW[NARROW[mach, LIST OF REF].rest.first]; pas: LIST OF CoreCreate.PA _ NIL; in: INT = 0; out: INT = 1; publics: ROPES _ LIST [vdd, gnd, clock]; intOnlys: ROPES _ NIL; FOR i: INT IN [0..logic.public[in].size) DO AddPA[pub, logic.public[in][i]] ENDLOOP; FOR i: INT IN [0..logic.public[out].size) DO AddPA[int, logic.public[out][i]] ENDLOOP; instances _ CONS [CoreCreate.InstanceList[logic, pas], instances]; pas _ NIL; FOR i: INT IN [0..reg.public[in].size) DO AddPA[int, reg.public[in][i]] ENDLOOP; FOR i: INT IN [0..reg.public[out].size) DO AddPA[pub, reg.public[out][i]] ENDLOOP; instances _ CONS [CoreCreate.InstanceList[reg, pas], instances]; publics _ Sort[publics]; intOnlys _ Sort[intOnlys]; RETURN[CoreCreate.Cell[ public: CoreCreate.WireList[RopesToWR[publics]], onlyInternal: CoreCreate.WireList[RopesToWR[intOnlys]], instances: instances, name: machName ] ]}; BuildSCGates: PUBLIC PROC [mach: Expression] RETURNS [Core.CellType] = { machName: IO.ROPE; vdd: Core.Wire _ CoreOps.CreateWires[0, "Vdd"]; gnd: Core.Wire _ CoreOps.CreateWires[0, "Gnd"]; publicIn: Core.Wire; publicOut: Core.Wire; ins: LIST OF CoreCreate.WR _ NIL; outs: LIST OF CoreCreate.WR _ NIL; internals: LIST OF CoreCreate.WR _ LIST[vdd, gnd]; inWires: SymTab.Ref _ SymTab.Create[]; outWires: SymTab.Ref _ SymTab.Create[]; termWires: SymTab.Ref _ SymTab.Create[]; inTab: SymTab.Ref; outTab: SymTab.Ref; instances: CoreCreate.CellInstances; NewWire: PROC[name: IO.ROPE] RETURNS[wire: Core.Wire] = {wire _ CoreOps.CreateWires[0, name]; internals _ CONS[wire, internals]}; RegInWire: PROC[wire: Core.Wire] ={ name: IO.ROPE _ CoreOps.GetShortWireName[wire]; []_SymTab.Insert[termWires, name, wire]; []_SymTab.Insert[inWires, name, wire]; ins _ CONS[wire, ins]}; MkInNm: SymTab.EachPairAction = {IF val=key THEN RegInWire[ NewWire[key] ]}; MkOutNm: SymTab.EachPairAction = { out: Core.Wire _ GetSimpleExpressionWire[val]; name: IO.ROPE _ CoreOps.GetShortWireName[out]; outNm: IO.ROPE; IF SymTab.Fetch[inWires, name].found OR SymTab.Fetch[outWires, name].found THEN { buffer: IO.ROPE _ "Buffer"; ref1: REF INT _ NEW[INT _ 1]; elist: LIST OF REF _ LIST[buffer, ref1, name]; out _ BindInstance[elist]; name _ NIL; TerminalIO.PutF["*** Adding extra buffer to %g to generate output %g\n", IO.rope[name], IO.rope[key]]}; outNm _ Rope.Cat["Nxt", key]; IF SymTab.Fetch[inWires, outNm].found OR SymTab.Fetch[outWires, outNm].found OR SymTab.Fetch[termWires, outNm].found THEN ERROR; IF trace THEN TerminalIO.PutF ["Changing output name from %g to %g\n", IO.rope[key], IO.rope[outNm]]; IF trace AND name#NIL AND NOT outNm.Equal[name] THEN TerminalIO.PutF ["Changing wire name from %g to %g\n", IO.rope[name], IO.rope[outNm]]; []_CoreOps.SetShortWireName[out, outNm]; []_SymTab.Insert[outWires, outNm, out]; outs _ CONS[out, outs]}; GetSimpleExpressionWire: PROC[exp: Expression] RETURNS[wire: Core.Wire] = { WITH exp SELECT FROM name: IO.ROPE => { def: REF _ SymTab.Fetch[inTab, name].val; wire _ NARROW[SymTab.Fetch[termWires, name].val]; IF wire=NIL THEN { wire _ GetSimpleExpressionWire[def]; IF CoreOps.GetShortWireName[wire]=NIL THEN []_CoreOps.SetShortWireName[wire, name]; []_SymTab.Insert[termWires, name, wire]} }; elist: LIST OF REF => wire _ BindInstance[elist]; ENDCASE => ERROR}; BindInstance: PROC[elist: LIST OF REF] RETURNS[wire: Core.Wire] = { opName: IO.ROPE _ NARROW[elist.first]; op: OpIndex _ BoolEx.NmOp[opName]; gate: Core.CellType; cnt: INT _ 0; pas: LIST OF CoreCreate.PA; args: LIST OF REF _ elist.rest; wire _ NewWire[NIL]; FOR el: LIST OF REF _ args, el.rest WHILE el#NIL DO cnt _ cnt +1 ENDLOOP; SELECT TRUE FROM op=not => gate _ Logic.Inv[]; op=and => gate _ Logic.And[cnt]; op=nand => gate _ Logic.Nand[cnt]; op=or => gate _ Logic.Or[cnt]; op=nor => gate _ Logic.Nor[cnt]; opName.Equal["Buffer"] => { size: REF INT _ NARROW[args.first]; args _ args.rest; IF (cnt _ cnt-1) #1 THEN ERROR; gate _ Logic.Driver[size^]}; ENDCASE => ERROR; IF gate=NIL THEN ERROR; pas _ LIST[["X", wire], ["Vdd", vdd], ["Gnd", gnd]]; IF cnt=1 THEN pas _ CONS[["I", GetSimpleExpressionWire[args.first]], pas] ELSE { input: Core.Wire _ CoreOps.FindWire[gate.public, "I"]; cnt_0; FOR el: LIST OF REF _ args, el.rest WHILE el#NIL DO pas _ CONS[[input[cnt], GetSimpleExpressionWire[el.first]], pas]; cnt _ cnt+1 ENDLOOP}; instances _ CONS [CoreCreate.InstanceList[gate, pas], instances]}; [machName, inTab, outTab] _ BoolEx.GetExpressionTables[mach]; []_ SymTab.Pairs[inTab, MkInNm]; []_ SymTab.Pairs[outTab, MkOutNm]; publicIn _ CoreCreate.WireList[ins, "in"]; publicOut _ CoreCreate.WireList[outs, "out"]; RETURN [ CoreCreate.Cell[ public: CoreCreate.WireList[LIST[publicIn, publicOut, vdd, gnd]], internal: CoreCreate.WireList[CONS[publicIn, CONS[publicOut, internals]]], instances: instances, name: machName.Cat["Logic"] ] ] }; BufferLargeFanouts: PROC[mach: Expression, limit: INT _ 4] = { IncrementInstanceCounts: PROC[expr: Expression] = { WITH expr SELECT FROM rope: IO.ROPE => { basic: IO.ROPE _ ToBasic[rope]; useCnt: REF INT _ NARROW[SymTab.Fetch[useCntTab, basic].val]; IF useCnt=NIL THEN {useCnt _ NEW[INT _ 0]; []_SymTab.Store[useCntTab, basic, useCnt]}; useCnt^ _ useCnt^ + 1; maxCnt _ MAX[maxCnt, useCnt^]}; elist: LIST OF REF => { FOR elist _ elist.rest, elist.rest WHILE elist#NIL DO IncrementInstanceCounts[elist.first] ENDLOOP}; ENDCASE => ERROR}; CountUses: SymTab.EachPairAction = {IF IsInput[key, val] THEN RETURN ELSE IncrementInstanceCounts[val]}; ToBasic: PROC[id: IO.ROPE] RETURNS[basic: IO.ROPE] = { WITH SymTab.Fetch[inTab, id].val SELECT FROM rope: IO.ROPE => RETURN[IF rope.Equal[id] THEN id ELSE ToBasic[rope]]; ENDCASE => RETURN[id]}; ScanUseCntTab: SymTab.EachPairAction = { buffer: IO.ROPE _ "Buffer"; original: IO.ROPE _ key; useCnt: REF INT _ NARROW[val]; IF useCnt#NIL AND useCnt^ > limit THEN { newNm: IO.ROPE _ IO.PutFR["%gBufX%g", IO.rope[original], IO.int[useCnt^]]; newExp: LIST OF REF _ LIST[buffer, NEW[INT _ useCnt^], original]; origExp: REF _ SymTab.Fetch[inTab, original].val; isInput: BOOL _ IsInput[original, origExp]; useCnt^ _ 1; BoolEx.ReplaceID[original, newNm, inTab]; BoolEx.ReplaceID[original, newNm, outTab]; IF isInput THEN []_SymTab.Store[inTab, original, original]; []_SymTab.Store[inTab, newNm, newExp]}}; IsInput: PROC[in: IO.ROPE, ref: REF] RETURNS[BOOL] = {RETURN[ISTYPE[ref, IO.ROPE] AND in.Equal[NARROW[ref]]]}; CollectIns: SymTab.EachPairAction = { def: LIST OF REF _ LIST[BoolEx.OpNm[var], key, val]; IF ~IsInput[key, val] THEN defs _ CONS[def, defs]}; CollectOuts: SymTab.EachPairAction = { def: LIST OF REF _ LIST[BoolEx.OpNm[out], key, val]; defs _ CONS[def, defs]}; name: IO.ROPE; maxCnt: INT _ 0; inTab: SymTab.Ref; outTab: SymTab.Ref; useCntTab: SymTab.Ref _ SymTab.Create[]; defs: LIST OF REF; [name, inTab, outTab] _ BoolEx.GetExpressionTables[mach]; []_SymTab.Pairs[outTab, CountUses]; []_SymTab.Pairs[inTab, CountUses]; TerminalIO.PutF["Max count: %g\n", IO.int[maxCnt]]; []_SymTab.Pairs[useCntTab, ScanUseCntTab]; []_SymTab.Pairs[inTab, CollectIns]; []_SymTab.Pairs[outTab, CollectOuts]; NARROW[mach, LIST OF REF].rest.rest _ defs}; SeeTerminal: SIGNAL = CODE; RopesToWR: PROC[ropes: ROPES] RETURNS[wrs: LIST OF CoreCreate.WR _ NIL] = { FOR ropes _ RopeList.Reverse[ropes], ropes.rest WHILE ropes#NIL DO wrs _ CONS[ropes.first, wrs] ENDLOOP}; BuildSCRegister: PUBLIC PROC [mach: Expression] RETURNS [Core.CellType] = {RETURN[BuildSCRegisterInternal[mach]]}; BuildSCRegisterInternal: PROC [mach: Expression, initOne: LIST OF IO.ROPE _ NIL] RETURNS [Core.CellType] = { machName: IO.ROPE _ NARROW[NARROW[mach, LIST OF REF].rest.first]; ins: LIST OF CoreCreate.WR; outs: LIST OF CoreCreate.WR; outTab: SymTab.Ref; internals: LIST OF CoreCreate.WR; instances: CoreCreate.CellInstances; fflop: Core.CellType _ Logic.FlipFlop[]; inv: Core.CellType _ Logic.Inv[]; vdd: Core.Wire _ NewWire["Vdd"]; gnd: Core.Wire _ NewWire["Gnd"]; clock: Core.Wire _ NewWire["Clock"]; MkOutNm: SymTab.EachPairAction = { wireIn: Core.Wire _ NewWire[Rope.Cat["Nxt", key]]; wireOut: Core.Wire _ NewWire[key]; blk: Core.Wire _ NewWire[NIL]; invert: BOOL _ RopeList.Memb[initOne, key]; invertOut: Core.Wire _ IF invert THEN NewWire[NIL] ELSE NIL; CoreProperties.PutWireProp[blk, $Static, $UnconnectedOk]; ins _ CONS[wireIn, ins]; outs _ CONS[wireOut, outs]; instances _ CONS [CoreCreate.Instance[fflop, ["D", IF invert THEN invertOut ELSE wireIn], ["Q", IF invert THEN blk ELSE wireOut], ["CK", clock], ["NQ", IF invert THEN wireOut ELSE blk], ["Vdd", vdd], ["Gnd", gnd]], instances]; IF invert THEN instances _ CONS [CoreCreate.Instance[inv, ["I", wireIn], ["X", invertOut], ["Vdd", vdd], ["Gnd", gnd]], instances]; }; NewWire: PROC[name: IO.ROPE] RETURNS[wire: Core.Wire] = { wire _ CoreOps.CreateWires[0, name]; IF name=NIL THEN internals _ CONS[wire, internals]}; [machName, , outTab] _ BoolEx.GetExpressionTables[mach]; []_SymTab.Pairs[outTab, MkOutNm]; RETURN[CoreCreate.Cell[ public: CoreCreate.WireList[ LIST[ CoreCreate.WireList[ins, "in"], CoreCreate.WireList[outs, "out"], clock, vdd, gnd ]], onlyInternal: CoreCreate.WireList[internals], instances: instances, name: machName.Cat["Reg"] ] ]}; GetExprTranslationCache: PROC[name: IO.ROPE] RETURNS[key, multiLevel: Expression] = { cache: LIST OF REF _ NARROW[BoolEx.ReadExprFile[name.Cat["XCache"]]]; IF cache=NIL THEN RETURN[NIL, NIL]; key _ cache.rest.rest.first; multiLevel _ cache.rest.rest.rest.first}; SetExprTranslationCache: PROC[name: IO.ROPE, key, multiLevel: Expression] = { xCache: IO.ROPE _ "XCache"; cache: LIST OF REF _ NIL; IF name=NIL OR key=NIL OR multiLevel=NIL THEN ERROR; cache _ LIST[xCache, name.Cat[xCache], key, multiLevel]; BoolEx.WriteExprFile[cache, 3]}; typeBias: ARRAY FSM.InOut OF NAT = [ -- for simple type checking default - 3000, default - 2000, default - 1000 ]; Context: TYPE = FSM.Context; default: NAT = FSM.default; InOut: TYPE = FSM.InOut; Create: PUBLIC PROC[name: IO.ROPE] RETURNS[ctx: Context] = { ctx _ NEW[FSM.ContextRec _ [ fsm: NEW[FSM.FSMDataRec], logic: NIL, data: NIL, state: NIL, declares: NIL, -- Used during declarations signals: NIL, -- Used after all declarations invTab: SymTab.Create[], stateTab: SymTab.Create[] ] ]; ctx.fsm.name _ name}; Declare: PUBLIC PROC [ctx: Context, name: IO.ROPE, io: InOut, size: NAT _ 1] RETURNS [ix: NAT] = { ix _ IF ctx.declares=NIL THEN 0 ELSE ctx.declares.first.index+1; ctx.declares _ CONS[[name, io, size, ix], ctx.declares]; RETURN[ix + typeBias[io]]}; IfState: PUBLIC PROC[ctx: Context, s: IO.ROPE] = { ctx.state _ s; IF NOT AtTop[ctx] THEN ERROR; ctx.logic _ LIST[NIL]; ctx.data _ LIST[NIL]}; If: PUBLIC PROC[ctx: Context, f, v: NAT, m: NAT _ default] = { currentLogic: FSM.CurrentLogic _ ctx.logic.first; currentData: FSM.CurrentData _ ctx.data.first; IF ctx.state = NIL THEN ERROR; IF AtTop[ctx] THEN ERROR; IF f = default OR v = default THEN ERROR; IF f NOT IN [typeBias[in]..typeBias[out]) THEN ERROR; currentLogic _ CONS[[f-typeBias[in], v, m], currentLogic]; ctx.logic _ CONS[currentLogic, ctx.logic]; ctx.data _ CONS[currentData, ctx.data]}; And: PUBLIC PROC[ctx: Context, f, v: NAT, m: NAT _ default] = { currentLogic: FSM.CurrentLogic _ ctx.logic.first; IF f = default OR v = default THEN ERROR; IF f NOT IN [typeBias[in]..typeBias[out]) THEN ERROR; currentLogic _ CONS[[f-typeBias[in], v, m], currentLogic]; ctx.logic.first _ currentLogic}; AddOut: PUBLIC PROC[ctx: Context, f, v: NAT, m: NAT _ default] = { currentData: FSM.CurrentData _ ctx.data.first; IF f = default OR v = default THEN ERROR; SELECT f FROM IN [typeBias[out] .. typeBias[outIn]) => f _ f - typeBias[out]; IN [typeBias[outIn] .. default) => f _ f - typeBias[outIn]; ENDCASE => ERROR; currentData _ CONS[[f, v, m], currentData]; ctx.data.first _ currentData}; JmpState: PUBLIC PROC[ctx: Context, s: IO.ROPE]= { init: BOOL _ EnsureSignalsInitialized[ctx]; source: State _ GetState[ctx, ctx.state]; target: State _ GetState[ctx, s]; enable: REF _ LogicToTerm [ctx, ctx.logic.first]; preOuts: ROPES _ IF target=NIL THEN NIL ELSE target.outputs; outputs: ROPES _ DataToOutList [ctx, ctx.data.first, preOuts]; ctx.logic _ ctx.logic.rest; ctx.data _ ctx.data.rest; IF target=NIL THEN RETURN; IF source=NIL THEN { IF enable#NIL THEN ERROR; target.outputs _ outputs} ELSE { trans: Transition _ NEW[FSM.TransitionRec _ [enable, outputs, target]]; source.outTrans _ CONS[trans, source.outTrans]}}; Finish: PUBLIC PROC[ctx: Context] RETURNS[fsm: FSMData] = { IF ctx.logic #NIL THEN ERROR; IF ctx.data #NIL THEN ERROR; IF (ctx.fsm.initialState _ NARROW[SymTab.Fetch[ctx.stateTab, "Init"].val])=NIL THEN ERROR; RETURN[ctx.fsm]}; AtTop: PROC[ctx: Context] RETURNS[BOOL] = {RETURN[ctx.logic = NIL AND ctx.data = NIL]}; DataToOutList: PROC[ctx: Context, data: FSM.CurrentData, list: ROPES _NIL] RETURNS[ROPES] = { Add: PROC[nm: IO.ROPE] = {IF ~RopeList.Memb[list, nm] THEN list _ CONS[nm, list]}; FOR data _ data, data.rest WHILE data#NIL DO name: IO.ROPE _ ctx.signals[data.first.f].name; size: NAT _ ctx.signals[data.first.f].size; v: NAT _ data.first.v; IF ctx.signals[data.first.f].io=in THEN ERROR; IF size=1 THEN {IF (v MOD 2)=1 THEN Add[name]} ELSE FOR bit: NAT DECREASING IN [0..size) DO bitOne: BOOL _ (v MOD 2)=1; v _ v/2; IF bitOne THEN Add[IBit[name, bit]] ENDLOOP; ENDLOOP; RETURN[list]}; EnsureSignalsInitialized: PROC[ctx: Context] RETURNS[init: BOOL _ TRUE] = { IF ctx.signals#NIL THEN RETURN; ctx.signals _ NEW [FSM.SignalSeqRec[ctx.declares.first.index+1]]; FOR decl: LIST OF FSM.Declaration _ ctx.declares, decl.rest WHILE decl#NIL DO ctx.signals[decl.first.index] _ decl.first; IF decl.first.io = outIn THEN IF decl.first.size>1 THEN FOR i: INT IN [0..decl.first.size) DO ctx.fsm.outIns _ CONS[IBit[decl.first.name, i], ctx.fsm.outIns] ENDLOOP ELSE ctx.fsm.outIns _ CONS[decl.first.name, ctx.fsm.outIns]; ENDLOOP; ctx.fsm.outIns _ Sort[ctx.fsm.outIns]}; GetState: PROC[ctx: Context, stateNm: IO.ROPE] RETURNS[state: State] = { IF stateNm.Length[]=0 THEN RETURN[NIL]; IF (state _ NARROW[SymTab.Fetch[ctx.stateTab, stateNm].val])=NIL THEN { state _ NEW[StateRec _ [name: stateNm]]; ctx.fsm.states _ CONS[state, ctx.fsm.states]; []_SymTab.Store[ctx.stateTab, stateNm, state]} }; IBit: PROC[nm: IO.ROPE, i: INT] RETURNS[IO.ROPE] = {RETURN[IO.PutFR["%g%g", IO.rope[nm], IO.int[i]]]}; LogicToTerm: PROC[ctx: Context, logic: FSM.CurrentLogic] RETURNS[result: REF _ NIL] = { GetInv: PROC[nm: IO.ROPE] RETURNS[inv: LIST OF REF] = {RETURN[LIST[BoolEx.OpNm[not], nm]]}; term: LIST OF REF _ NIL; FOR logic _ logic, logic.rest WHILE logic#NIL DO name: IO.ROPE _ ctx.signals[logic.first.f].name; size: NAT _ ctx.signals[logic.first.f].size; v: NAT _ logic.first.v; m: NAT _ logic.first.m; IF ctx.signals[logic.first.f].io#in THEN ERROR; IF size=1 THEN {IF (m MOD 2)=1 THEN term _ CONS[(IF (v MOD 2)=1 THEN name ELSE GetInv[name]), term]} ELSE FOR bit: NAT DECREASING IN [0..size) DO bitOne: BOOL _ (v MOD 2)=1; msk: BOOL _ (m MOD 2)=1; v _ v/2; m _ m/2; IF ~msk THEN LOOP; term _ CONS[(IF bitOne THEN IBit[name, bit] ELSE GetInv[IBit[name, bit]]), term] ENDLOOP; ENDLOOP; IF term#NIL THEN IF term.rest=NIL THEN result _ term.first ELSE result _ CONS[BoolEx.OpNm[and], term]}; true: IO.ROPE _ "TRUE"; false: IO.ROPE _ "FALSE"; TrueIfNIL: PROC[e: REF] RETURNS[REF] = {RETURN[IF e = NIL THEN true ELSE e]}; ValidateFSM: PROC [fsm: FSMData] ~ { PrintNames: PROC[list: ROPES] = { FOR list _ list, list.rest WHILE list#NIL DO TerminalIO.PutF["%g\n", IO.rope[list.first]] ENDLOOP}; RegisterVars: PROC[e: REF] = { IF e=NIL THEN RETURN; WITH e SELECT FROM rope: IO.ROPE => IF ~RopeList.Memb[varNms, rope] THEN varNms _ CONS[rope, varNms]; elist: LIST OF REF => FOR elist _ elist.rest, elist.rest WHILE elist#NIL DO RegisterVars[elist.first] ENDLOOP; ENDCASE => ERROR}; varNms: ROPES; stateNms: ROPES; outNms: ROPES; assert: Expression _ TrueIfNIL[fsm.assert]; bad: BOOL _ FALSE; IF fsm.srcCellType#NIL AND CoreProperties.GetCellTypeProp[fsm.srcCellType, $FSMDoNotValidate]#NIL THEN {TerminalIO.PutF["\nWarning: Unvalidated FSM %g:\n", IO.rope[fsm.name ]]; RETURN}; TerminalIO.PutF["\nValidate FSM %g:\n", IO.rope[fsm.name ]]; FOR states: States _ fsm.states, states.rest WHILE states#NIL DO from: State = states.first; all: LIST OF REF _ LIST[BoolEx.OpNm[not], assert]; stateNms _ CONS[states.first.name, stateNms]; FOR outs: ROPES _ from.outputs, outs.rest WHILE outs#NIL DO IF ~RopeList.Memb[outNms, outs.first] THEN outNms _ CONS[outs.first, outNms]; ENDLOOP; FOR outs: ROPES _ from.outputsInv, outs.rest WHILE outs#NIL DO IF ~RopeList.Memb[outNms, outs.first] THEN outNms _ CONS[outs.first, outNms]; ENDLOOP; FOR transitions: Transitions _ from.outTrans, transitions.rest WHILE transitions#NIL DO e1: Expression = TrueIfNIL[transitions.first.enable]; s1: State = transitions.first.target; FOR outs: ROPES _ transitions.first.outputs, outs.rest WHILE outs#NIL DO IF ~RopeList.Memb[outNms, outs.first] THEN outNms _ CONS[outs.first, outNms]; ENDLOOP; RegisterVars[transitions.first.enable]; all _ NARROW[BoolEx.OpExpr[or, e1, all]]; FOR others: Transitions _ transitions.rest, others.rest WHILE others#NIL DO e2: Expression = TrueIfNIL[others.first.enable]; s2: State = others.first.target; IF ~BoolEx.Equal[LIST[BoolEx.OpNm[and], e1, e2, assert], false] THEN { TerminalIO.PutF["FSM: %g.\n Conflicting transitions from: %g to:\n", IO.rope[fsm.name], IO.rope[from.name]]; TerminalIO.PutF[" st1: %g t: %g\n st2: %g t: %g\n assert: %g\n", IO.rope[s1.name], IO.rope[SParse.ToRope[e1]], IO.rope[s2.name], IO.rope[SParse.ToRope[e2]], IO.rope[SParse.ToRope[assert]] ]; SIGNAL SeeTerminal}; ENDLOOP; ENDLOOP; IF ~BoolEx.Equal[all, true] THEN { TerminalIO.PutF["FSM: %g.\n Transitions from %g do not cover all cases.\n", IO.rope[fsm.name], IO.rope[from.name]]; SIGNAL SeeTerminal}; ENDLOOP; stateNms _ Sort[stateNms]; outNms _ Sort[outNms]; varNms _ Sort[varNms]; bad _ Check["States", stateNms, outNms, varNms] OR bad; bad _ Check["Outputs", outNms, varNms, stateNms] OR bad; bad _ Check["Inputs", varNms, stateNms, outNms] OR bad; IF bad THEN {TerminalIO.PutRope["***Names are not unique\n"]; SIGNAL SeeTerminal}; TerminalIO.PutRope["\n"]}; Check: PROC[id: IO.ROPE, l0, l1, l2: ROPES] RETURNS[bad: BOOL _ FALSE] = { TerminalIO.PutF[" %g:\n", IO.rope[id]]; FOR list: ROPES _ l0, list.rest WHILE list#NIL DO bad1: BOOL _ RopeList.Memb[l1, list.first]; bad2: BOOL _ RopeList.Memb[l2, list.first]; bad _ bad OR bad1 OR bad2; TerminalIO.PutF[" %g %g\n", IO.rope[IF bad1 OR bad2 THEN "x" ELSE ""], IO.rope[list.first]] ENDLOOP}; curWDir: IO.ROPE _ FileNames.CurrentWorkingDirectory[]; -- when module runs searchPath: ROPES _ LIST[curWDir, "///7.0/Commands/", "///7.0/System/"]; InstallPackage: PROC[name: IO.ROPE] = {[]_CDEnvironment.StuffToCommandTool[Rope.Cat["Install ", name], curWDir, searchPath]}; CoreProperties.PutCellClassProp[fsmClass, RosemaryUser.stateToMaxCharsProcProp, NEW[RosemaryUser.StateToMaxCharsProc _ FSMStateToMaxChars]]; CoreProperties.PutCellClassProp[fsmClass, RosemaryUser.stateToRopeProcProp, NEW[RosemaryUser.StateToRopeProc _ FSMStateToRope]]; END. xFSMImpl.mesa Copyright Σ 1986, 1987 by Xerox Corporation. All rights reserved. Barth, October 24, 1989 1:37:02 pm PDT Bertrand Serlet April 2, 1987 9:39:46 pm PST Don Curry October 11, 1988 8:22:27 am PDT FSMData has been modified to enable the expression of Mealy machines as well as Moore. Implementation Choices for exprImplKey Current: $SC, $SCompress, $PLA, $PLACompress, $Xilinx Future: $ALPs?, $SCOptimized? Common Types and Declarations Creation From Schematics Find all States Find all Transitions Creation from FSMData Order: ins Reset, Clock, PhA, PhB, Vdd Gnd, outs - used by icon builder. Expand State definitions EncodeStates The encoding will be the state outputs except that initialstate might have additional outputs. These can be filtered using fsm.outInAll and fsm.outIns. This is necessary because the output transistions from each state to init on Reset are implicit so these none state outputs can't just be left on initialstate's inTransitions (currently only used by EncodeStates). Future: Make Reset transistions explicit => would need the notion of a transistion which does not depend on the source state. Make the inTransitions the reference location of all outputs or all non-state outputs. Mod: PROC[r1, r2, r3, r4: IO.ROPE _ NIL] = { -- Verbose IF NOT trace THEN {firstMod _ FALSE; RETURN}; IF firstMod THEN { log.PutF["\nThe state expressions in %g are being\n", IO.rope[fsm.name]]; log.PutRope[" firstMod to insure that all states are mutually exclusive.\n"]}; log.PutRope[Rope.Cat[r1, r2, r3, r4]]; firstMod _ FALSE}; Display results for debugging Second Level Stuff At this point, each statePatSet is differenciable from every other set using only consistant state outputs. We will now break up each of these sets into subsets corresponding to unique t patterns. Care is taken to segregate states which have any variable outputs into a separate group of subsets where the legal t patterns are filtered by the union of all the variable outputs in the group of variable subsets. Before this last partitioning occurs though, we try to throw out variable patterned states which are individually distinguishable. Build subsets for non-variant states and collect variant states; Now try to find unique variants Accumulate variant outputs into mask Insert variant PatternState into variant subsets Add uniques to grp varients as one element sets (just for completeness) Just get the xtra bits registered in order Do non-variant state assignments; Do variant state assignments; Print new State specifications. IF ~bias AND stps.first.ones^ = emptyArray^ AND (~useLocOnes OR stps.first.locOne^ = emptyArray^) THEN index _ 1; -- don't use zero as code This is included for possible future use. Just putting it in before the state assignments made the SmallCachePmCode blow up to ~twice as many terms with much more heavily loaded outputs(x3). Splitting states misses the opportunity to have only one term decribe the transition out of it. Partition in transistions into sets For each set, replace targets with new states Remove each out transition from its target's in transition list NewState loops removed here get replaced later with new transitions Copy each old out transition onto new state out list and target in list Recast to specific implementaton twoPhase => impl _ BuildSCMachine[multiLevel, FALSE]; This should be in CoreOps. Assumes that wire1 and wire2 have the same atomic names but possibly different structures. table contains parts of wire1 as keys and corresponding parts of wire2 as values. Convert to machine expression Simulation procedures Standard Cell Implementation TerminalIO.PutF["%g %g %g\n", IO.rope[rope], IO.rope[basic], IO.int[useCnt^]]; Expression Cache I/O TerminalIO.PutF["Reading expression cache: %g.expr\n", IO.rope[name]]; TerminalIO.PutF["Writing expression cache: %g.expr\n", IO.rope[name]]; FSMData generation aids FSMData Validation Runtime Loading Initialization ΚXL˜šœ ™ JšœB™BJšœ&™&Jšœ)Οk™,Jšœ)™)—J™J™VJ™Jš œœœtœs˜”J˜šΟnœœ˜Jšœœnœr˜†Jšœ˜ Jšœ˜—headšΠbl&™&Jšœ5™5Jšœ™—šŸ™Jšœ œœ˜Jšœ œ˜!Jšœ œœ ˜Jšœ œœ˜Jšœ œœ˜Jšœ œœ ˜ Jšœœœ ˜%Jšœ œœ ˜#Jšœœœ˜&Jšœ œœ˜Jšœ œ ˜J˜Icodešœœœ. œ˜Nšœ˜šœ˜šœ˜JšœKœ˜VJšœ˜—Jšœ˜———šŸ™Jšœœœ ˜Jšœ œœ ˜ Jšœ œœ ˜ J˜šž œœœ˜Jšœ˜Jšœœœœ˜Jšœ(˜(šœ œ ˜Jšœ˜—Jšœœœ ˜/Jšœ%œ˜)Jšœ œ œ˜ Jšœ1˜1Jšœ3˜3Jšœœ˜ Jšœœ˜Jšœ œœ˜šž œ˜ Jšœœœœ˜8—šœœœ˜Jšœ œœ?˜RJšœ œ+˜7Jšœ œ˜(Jš œ œœœΟc.˜XJšœ-˜-—Jšœœ˜8Jšœ>˜>Jšœ<˜˜TJšœE˜EJšœ œœ*˜>šœœ˜+Jšœ ˜ Jšœ˜Jšœ˜Jšœ˜—Jšœœ˜'JšΟb™šœœœ˜!šœ2œ˜:šž œ˜"Jš œ œœœœœ˜-Jšœœ˜$Jšœœ#˜.šœ˜šœœœ0˜Jšœ œ%œœ˜Y—Jšœ ˜&—Jšœœœ˜Jšœœœ˜Jšœœœ˜Jšœœœ˜Jšœœœ˜Jšœœœ˜Jšœ'˜'Jšœ(˜(Jšœ˜Jš‘ œ #˜6Jšœœ˜Lšœ; )˜dJšœ9˜9šœ$œœ˜7š œœ œœ˜@Jšž œ œ˜—š œœ#œœ˜CJšž œ œ˜—šœ5œœ˜JJšœœœ-‘œ˜UJš˜—Jš˜—šœ$œœ˜7šœ5œœ˜Jš œœ"œœ˜BJšžœ œ˜6———Jšœœ œ˜=šœ˜Jšœœœ˜LJšœœœ˜:—Jšœ œ˜Jšœ œ˜Jšžœ0˜FJšžœ1˜GJšžœ!˜7Jšžœ1˜GJšžœ2˜HJš‘H™Hšœ˜Jšœ˜Jšœ˜Jšœ ˜ Jšœ˜Jšœœ˜@—Jš œœœœ(œ˜V——šŸ™Jšœ œ˜Jšœœ˜ Jš œœœœœœ˜!Jšœ œœœ˜ Jšœ œœ-œ˜IJšœœœœ˜0Jšœœœœ˜-Jšœœœ2˜OJšœ œœ ˜Jšœ œœœœœœœ˜EJšœœ˜2Jšœœ )˜NJšœœœ˜Jšœœ œœ˜1Jšœ œœ ˜J˜šΠbn ™ šœΙ£œ£ œ ™ψJšœv™vJšœW™W——J˜š£ œœœ˜+Jšžœœœœœœœ˜Fš žœœœœœ™7Jš œœ‘œœ œœ™-šœ œ™Jšœ6œ™IJšœO™O—Jšœ2œ™9—Jšœœ œœ˜-Jšœœ œœ˜/Jšœ œ˜$Jšœ$œ˜(Jšœœ˜Jšœœœœ˜(Jšœ œœ˜Jšœ œ ˜Jšœ œ ˜Jš œ œ œœœ˜*Jš œ œ œœœ˜*Jšœ œ˜Jšœ*˜*Jšœœ ˜š žœœœœœœ˜4Jšœœœœ ˜8šœœœ˜Jšœœ!œœ˜EJšœ œœ˜Jšœ˜Jšœ&˜&—Jšœ ˜—š žœœœœœœ˜4šžœ˜Jšœœœœœœ œœ˜>—Jšœœœœ˜.—Jšœ œ˜Jšœœœ%œ˜IJšœœ‘œ˜8Jšœ˜Jšœœ‘œ˜4šœ*œœ˜@š œœ#œœ˜CJšœ œœ˜,—šœ8œœ˜Mš œœ"œœ˜BJš œ œœœœ˜<———š œœœœ˜<šœœœ˜8Jšœœ-˜4šœ‘œœ˜Jšœ2œ ˜D—Jšœ œœ˜*——Jšœ&˜&Jšœœ‘œ˜6šœ*œœ˜@Jš œ œœœœ˜2Jšœœœ œ˜Jšœœœ˜šœ˜Jšœ ˜ Jšœ œœ˜Jšœ œœ˜Jšœ œœ˜Jšœ œœ˜—š œœ&œœ˜FJšœœ˜"Jšœ œœ˜Jšœœœ˜ —šœ7œœ˜LJšœœœ˜š œœ"œœ˜BJš œœœ œœ˜GJšœ˜—šœœœ˜#Jšœœ˜(Jšœ œœœ˜+Jšœ œ ˜-šœœ˜#Jšœœ˜Jšœœ &˜>Jšœœ˜—Jšœ˜—Jšœœ˜—Jšœ œœ˜)—Jšœœ‘œ˜>šœœœ˜#Jšœ œœœ˜=—Jšœœ‘œ˜7šœ,œœ˜@Jšœ ˜ Jšœœ˜Jšœœ˜Jšœœ'˜8šœœœ˜#šœœœœ˜.Jšœœ 6˜NJšœœ˜—Jš œœœœœ˜4Jšœœœ&˜XJšœœœ#˜SJšœ˜—šœ.œœ˜@Jšœ"œ œœ˜PJš œœœœœ˜H—Jšœ˜—Jš‘™šœœ˜Jšœœ˜ Jšœœ˜Jšœœ˜ šœœœ˜#šœ ˜Jšœœ˜%šœœ˜Jšœ œ˜)Jšœ œ˜0———Jšœ/˜/Jšœ/˜/Jšœ.˜.š œœœœ˜@Jšœ˜šœ%œœ˜6Jšœ:œ˜B—Jšœ˜—Jšœ˜—J™Jš‘™Jšœ ™ J˜Jšœœ‘#œ˜Cš œœœœ˜FJšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœœ œœ˜+Jš‘@™@šœ'œœ˜8Jšœ˜šœ˜Jšœ œ˜#š œœœœ˜Dšœœ&˜8Jšœ œœ˜,—Jš œœœœ˜F——Jšœ˜—Jš‘™šœ'œœ˜9šœ/œœ˜BšœV˜XJšœœœ˜@—Jšœ œœ˜—šœ1œ œ˜Hšœ%˜'šœT˜WJšœœœ˜;——Jšœœœœ˜C—Jšœ˜—Jš‘$™$šœ,œœ˜>šœœœ˜#Jšœœœœ˜>——Jš‘0™0šœ,œœ˜>Jšœ˜šœ-œœ˜?šœZ˜\Jšœ œœ˜,—Jš œœœœœ˜F—Jšœ˜—Jš‘G™Gšœ)œœ˜;Jšœœœœ˜8—Jšœ œ˜Jšœ˜—J˜Jšœœ‘'œ˜Gšœ"œœ˜6Jšœœ˜Jšœ˜šœ4œœ˜Fšœ˜Jšœž œœ˜H——Jšœ˜Jšœœ$˜2šœ4œœ˜Fšœ˜Jšœž œ$˜L—Jšœ˜—Jšœ œ ˜0Jšœ˜—Jšœ*™*šœœœ˜)Jš œ œœœœœ˜OJšœœ˜!—Jšœœ!œ˜BJ˜Jšœœ‘-œ˜Mš˜Jšœ œœ˜(Jšœœœ˜š œ"œœœ œ˜Hšœ,œ˜4JšœG˜GJšœœ˜—Jšœ˜—Jšœœœœ˜—š œ"œœœ œ˜HJšœœœ˜-š˜Jšœœœ˜š œ4œœœ œ˜VšœW˜YJšœFœ˜Q—Jšœ˜—Jšœœœœ˜—š˜Jšœœœ˜š œ4œœœ œ˜VšœW˜YJšœFœ˜Q—Jšœ˜—Jšœœœœ˜—Jšœ˜—J˜Jšœœ‘œ˜6Jš œœœ œœ˜8šœ"œœ˜6šž œœ œ˜<š˜Jšœœœ˜Jšœ œœ œ˜$šœœœ˜)Jšœœ˜Jšœœ˜šœ œ œœ˜Dšœ,˜,Jšœ#˜#—Jšœœœ˜——Jšœœœœ˜——šžœœ#œ œ˜Nšœ!œœ˜2šžœœœ˜Jš œ œœœœœ˜Ošœ0œœ˜CJšœ"œ ˜H—šœœ'˜;Jšœœœ˜0—Jšœœ ˜8Jšœ!œ˜&JšœM˜M—Jšœ˜Jšœœ ˜Jšœ˜Jšœœ˜šœœœ˜#Jšœœœ˜.Jšœœ˜—Jšœ˜ ——Jšœ œ˜Jšœœœ ˜EJšœ œ˜(Jšœ˜Jšœœ ‘ œœ˜:Jš‘!™!šœ4œœ˜FJšœ3˜3Jšœ.˜.Jšœœ˜Jšœ˜šœ%œœ˜6Jšœ:˜B—Jšœœ˜—Jš‘™šœ4œœ˜FJšœFœ˜KJšœ:œ˜?Jšœœœ˜Jšœ˜šœ%œœ˜6Jšœ:˜:Jšœ˜—Jšœœ˜—Jšœ˜Jšœœ˜'Jšœ˜—J˜Jšœœ‘!œ˜Ašœ.œœ˜Cšœ/œœ˜DJšœ˜Jšœœ˜Jšœœœ˜Jšœ˜Jšœ˜JšœQ˜QJšœœœ˜Jšœ˜Jšœœ œ œ ˜6Jšœœ œ œ ˜7šœœ,˜2Jšœœ˜@—šœœ*˜0Jšœœ˜=—Jšœœ˜šœ%˜%Jšœ>˜>—Jšœ˜—Jšœ˜—Jšœ)˜)J˜Jšœœ‘œ˜<š˜Jšœœœ˜6Jšœ œœ˜š œ*œœœ œ˜NšœI˜OJšœSœ˜Y—Jšœ˜—Jšœœœœ˜—J™Jš‘™šœœœ˜šœ ˜ Jšœ4˜8Jšœ3˜7—Jšœ˜—Jšœ:œ˜Mšœ*œœ˜=J˜9J˜>Jšœœ˜0š œœ&œœ˜FJšœœœ˜0—š œœ)œœ˜IJšœœœœ˜9——šœ*œœ˜=Jšœ<œ˜D—Jšœ˜šœœœ˜#Jšœœ œœ˜D—Jšœ œœ˜J˜—šžœœœ˜4š œœœœ˜8Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ ˜——š ž œœ œœœ˜Mšœœ ˜!Jš œ!œœœœ˜Išœ™šœ™"šœœ"™5Jšœ  ™)———Jšœ˜——šž œœœœœœœœ˜EJšœœœœ˜'Jšœœ ˜ š œœœœ˜3Jšœœœ˜1——šžœ˜Jš œ&œœœœ˜DJšœœœœ˜'Jšœœ˜'Jšœœœ%œ œœœ˜Qšœœœ ˜šœœœ˜3Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ œœ˜—Jš œœœ œœ˜+Jšœœ˜—Jšœ œœ˜—šžœ˜šœ œœœ˜OJšœ+œ ˜?—Jšœ œ˜šœœœ ˜Jšœœœ˜8Jšœ˜Jš œœœ œœ˜$Jšœ<˜<šœ˜Jšœ7˜7Jšœ7˜7Jšœ œ œ)˜DJšœ œ œ)˜DJšœ œ œ)˜DJšœ œ œ)˜DJšœ œ œ)˜DJšœ œ œ)˜DJšœœ˜—Jšœ œ œœ˜;——šž œœ˜8šœ*œœ˜@š œœ#œœ˜Cšœ7œœ˜Lšœ2˜8Jšœœ!˜;—Jšœ˜—Jšœ˜—Jšœ˜ ——šž#œœ˜;šœ*œœ˜@š œœ#œœ˜Cšœ7œœ˜LJšœG˜GJšœ˜—Jšœ˜—Jš˜—šœ;œœ˜Pšœœœ˜ š œœ"œœ˜Bšœ7˜=Jšœœ&˜E—Jšœ˜—Jšœœ˜———š žœœœœœœ ˜KLšœœ˜ Lšœœœ˜Lšœœœ œœœœœœ˜P—š žœœœœ œ˜1Jšœœ)˜0—Jšžœœœœœœœ ˜Qšžœœ˜,šœ*œœ˜@Jš œœœ œ ˜D—Jšœœœœ.˜Ušœ*œœ˜@šœ8œœ˜MJšœœ*˜KJšœ˜—Jšœ˜ —J˜—šžœœ˜+Jšœ ™ š žœœ œœœœ˜Cš œœœœ˜1Jš œœœœœœ˜@—š œœœœ˜1Jš œœœœœœ˜A——Jšœ˜Jšœ˜Jšœ&˜&šœ*œœ˜@Jšœ˜Jšœ œ˜Jšœœœ ˜Jš‘#™#šœ7œœ˜Lš œœœœœ˜;šœ=˜?Jšœ œœ˜4—Jš œœ œœœ˜@—Jšœ˜—Jšœœœœ˜Jš œ œœœœ˜NJš‘-™-š œœœœœ˜;šœœœ ˜&Jšœœœœ ˜GJšœ5˜5Jšœ:˜:Jšœ œ˜Jšœ˜Jšœ)˜)—Jšœœ˜-Jšœœ˜)šœ3œœ˜HJšœœ˜&—Jšœ˜Jšœ˜—š‘?™?Jš‘D™D—šœ8œœ˜MJšœ˜šœ?œœ˜UJšœœœœ˜I—Jšœ!œ˜*—Jš‘G™Gšœ8œœ˜Mšœ+œ œ˜Bšœœœ˜0Jšœ)˜)Jšœ4˜4Jšœ˜Jšœ)˜)—Jšœœ"˜AJšœœ#œ˜R——Jšœ˜—Jšœ˜——š’ ™ šž œ˜Lšœœ ˜#Lšœ œœ9˜SLšœ˜Lšœo˜oLšœ5˜5šœ+˜+Lšœ˜Lšœ˜Lšœ ˜ Lšœ!˜!—šœ ˜Lšœ*˜*Lšœ# ˜ALšœ˜—Lšœ˜J˜—š žœœœœœ'œ˜Xšœ˜Jšœ.˜.JšœA˜AJšœ˜—J˜—š žœœœœœ˜Gšœœ ˜Jšœ˜#Jšœœ˜——J˜šž œœ œœœœœœœ˜ŒLšœœœœœœœœ˜=Lšœœœœ˜"šœ ˜Lšœ1˜1Lšœ@˜@Lšœœ˜—Lšœ4œ œ˜Yšœ ˜šœ˜Lšœ œ˜)šœ œ˜Lšœ'˜'Lšœ'œ˜-Lšœ˜—Lšœ(˜,Lšœ˜šœ˜Lšœœ ˜>Lšœœ! ˜MLšœœ"˜7Lšœœ˜——šœ˜Lšœ œ˜'Lšœ˜Lšœ˜Lš œ œœœ œœ˜>Lšœ‘œ ˜5Lšœ œ ˜Gšœ˜Lšœ˜šœ œ˜Lšœ'˜'Lšœ-œ˜3Lšœ˜—Lšœ(˜,Lšœ˜Lšœ‘œ ˜.šœœ˜Lšœ#˜#Lšžœ˜"L˜—Lšœ˜Lšœ3˜3Lšœ˜—Lšœ˜šœ˜LšœD˜DLšœ.œ™5Lšœ(˜(Lšœœ˜——Lšœ˜—Lšœ˜J˜—JšœΙ™Ιšžœœœ˜Tšž œœ˜&Jšœœœ˜*Jšœœœ ˜.šœœœ˜FJšœœ %˜2——šž œœ˜"Jšœœœ ˜+Jšœœ˜5šœ˜ Jšœ9œ ˜Išœœœœ˜IJšœœ ˜———Jšœ%˜%Jšœ˜Jšœ/˜/Jšœ,˜,——šŸ™šž œœœœ œœœ(˜lJ˜—šžœœœœœ œœœ˜^šž œœœœ˜3šœ˜ Jšœ-˜1šœœ ˜'Jšœ?˜C——Jšœ$˜$—šžœ˜*Jš œœœœœ˜7Jšœ œ˜!—Jšœ œœ ˜Jšœ œœ˜#Jš œœœœœ˜@Jš œœœœœ/˜PJšœ'˜'Jšœœ˜Jšœ ‘œ˜*šœ$œœ˜7Jš œ œœœœ˜.šœ;œ œ˜SLš œ$œœœœ;˜”Lš œœœœœ1˜tJš œœœœœ˜CJš œœœœœ'˜HJšœ"˜"Jšœ œ˜'š œœ,œœ˜LJšœ$œ˜,—š œœ%œœ˜Ešœœ:˜DJšœ$œœœ˜<————šœœœœœ'œœ˜ZJšœ!˜!Jšœ˜—Jšœ+˜+Jšœœœ œ˜:Jšœ œœ˜;Jšœ œ$œ˜Jšœœœ˜š œœœœœ˜8Jšœ˜Jšœœ*œœ˜LJšœœ.˜;Jšœœœ˜'——Jšœœ ˜(Jšœœ˜-šœ œ˜Jšœ œ œ˜Jšœ˜ Jšœœ˜+Jšœœ˜,——šœ œœ ˜$Jšœ œ œ˜JJšœœ˜4—šœ œœ ˜$šœ œ ˜.Jšœœ˜6—Jšœœ˜2—šœœ &˜JJšœ˜šœ&œœ˜8Jšœœ*œœ˜:Jšœœ,œœ˜Lšœ˜Lšœœ˜——šœ œ˜0Jšœ,˜0———šœ™Lš žœœœœœ ˜pJ˜šžœœœœœœœœ˜kšžœœ'˜2Jšœœœ"˜/šœ œ˜šœ˜Jšœœ œ˜DJšœ œœ˜-—šœ˜Jšœ œ œ˜GJšœœœ˜-——Jšœœ˜—Jšœ+˜+Jšœ=˜=Jšœœœ ˜Jšœœœ ˜Jšœœœ ˜Jšœ%˜%Jšœ œœœœœœœ˜AJš œœœ œœ˜#Jšœœ˜ Jšœœ˜ Jšœ œœ˜(Jšœ œœ˜Jš œœœœ!œ˜UJš œœœœ"œ˜VJšœ œ2˜BJšœœ˜ Jš œœœœœ˜QJš œœœœ œ˜SJšœ œ0˜@Jšœ˜Jšœ˜šœ˜Jšœ1˜1Jšœ7˜7Jšœ˜Jšœ˜—J™—šž œœœœ˜HJšœ œœ˜Jšœ1˜1Jšœ1˜1Jšœ˜Jšœ˜Jš œœœ œœ˜#Jš œœœ œœ˜$Jš œ œœ œœ ˜3Jšœ'˜'Jšœ(˜(Jšœ(˜(Jšœ˜Jšœ˜Jšœ%˜%š žœœœœœ˜9Jšœ2œ˜I—šž œœ˜#Jšœœœ"˜/Jšœ(˜(Jšœ.œ ˜?—Jšžœœ œ˜Lšžœ˜"Jšœ/˜/Jšœœœ!˜/Jšœœœ˜šœ#˜'šœ#œ˜)Jšœœœ ˜Jš œœœœœ˜Jš œœœœœ˜/Jšœ˜Jšœœ˜ šœH˜HJšœ œ ˜———Jšœ˜šœ$˜(Jšœ$˜&Jšœ%œœ˜0—šœœ˜Jšœ)œ œ˜G—š œœœœœœ˜DJšœ'œ œ˜F—Jšœ(˜(Jšœ/œ ˜@—šžœœœ˜Kšœœ˜šœœœ˜Jšœœ!˜)Jšœ œ$˜3šœœœ˜Jšœ$˜$šœ œ˜*Jšœ(˜(—Jšœ+˜+——Jšœœœœ˜1Jšœœ˜——š ž œœœœœœ˜CJšœ œœœ˜'Jšœ$˜$Jšœ˜Jšœœ˜Jšœœœ œ˜Jšœœœœ˜!Jšœœ˜Jšœœœœœœœœ˜Išœœ˜Jšœ"˜"Jšœ%˜%Jšœ'˜'Jšœ$˜$Jšœ%˜%šœ˜Jšœœœœ ˜#Jšœ˜Jšœœœ˜Jšœ˜—Jšœ œ˜—Jšœœœœ˜Jšœœ*˜4šœ˜Jšœœ1˜@šœ˜Jšœ6˜6Jšœ˜š œœœœœœ˜3Jšœœ7˜AJšœ œ˜———Jšœ œ2˜B—Jšœ=˜=Jšœ!˜!Jšœ#˜#Jšœ+˜+Jšœ-˜-šœ˜Jšœœ!˜AJšœœ œ˜JJšœ˜Jšœ#˜#—J˜—šžœœœ ˜>šžœœœ˜3šœœ˜šœœœ˜Jšœœœ˜ Jšœœœœ%˜=Jš œœœ œœ2˜VJšœ˜Jšœœ œœ™NJšœ œ˜ —šœœœœ˜šœ œ˜2Jšœ&œ˜1——Jšœœ˜——šž œ˜"Jš œœœœœ˜E—šžœœœœœœœ˜6šœœ˜,Jš œœœœœœœ˜FJšœœ˜——šž œ˜(Jšœœœ ˜Jšœ œœ˜Jšœœœœ˜šœœœœ˜(Jš œœœœœœ˜LJš œ œœœœ œœ˜BJšœ œ%˜2Jšœ œ˜,Jšœ ˜ Jšœ)˜)Jšœ*˜*Jšœ œ,˜;Jšœ(˜(——šžœœœœœœœ˜4Jš œœœœœœ œ ˜9—šž œ˜%Jš œœœœœ˜4Jšœœœœ ˜3—šž œ˜&Jš œœœœœ˜4Jšœœ ˜—Jšœœœ˜Jšœ œ˜Jšœ˜Jšœ˜Jšœ(˜(Jšœœœœ˜Jšœ9˜9Jšœ$˜$Jšœ$˜$Jšœ#œ˜3Jšœ*˜*Jšœ%˜%Jšœ&˜&Jšœœœœ˜,J™—šž œœœ˜J˜—šž œœœœœœ œœ˜Kšœ-œœ˜BJšœœœ˜&—J˜—Lš žœœœœœ!˜ršžœœœœœœœœ˜lJšœ œœœœœœœ˜AJšœœœ œ˜Jšœœœ œ˜Jšœ˜Jšœ œœ œ˜"Jšœ%˜%Jšœ,˜,Jšœ%˜%Jšœ&˜&Jšœ&˜&Jšœ*˜*šžœ˜"Jšœ2˜2Jšœ"˜"Jšœœ˜Jšœœ˜+Jš œœœ œœœ˜Jšœœ ˜1Jšœœ˜0Jšœ œœœ˜Jšœ œœ˜Jšœ œœœ˜*Jš œœœœœ˜5Jšœœ'˜:Jšœ œ˜-Jšœ œ˜+J˜—š žœœœœœ˜?Jšœœ ˜1Jšœ œœœ˜*Jš œœœœœ˜5Jšœœ'˜:Jšœ ˜ J˜—š žœœœœœ˜BJšœ œ˜.Jšœ œ œœ˜)šœ˜ Jšœ=˜?Jšœ;˜=Jšœ œ˜—Jšœœ˜+Jšœ˜J˜—š žœœœœœ˜2Jšœœžœ˜/Jšœ,˜,Jšœ%˜%Jšœœž œ˜4Jš œ œœœœœœ˜Jšœ˜Jšœ˜Jšœœœœ˜šœ˜ šœ˜Jšœœœœ˜Jšœ˜—šœ˜Jšœœœ,˜GJšœœ˜1———J˜šžœœœœ˜;Jšœ œœœ˜Jšœ œœœ˜Jš œœ*œœœ˜ZJšœ ˜J˜—šžœœœœ˜)Jš œœ œœ œ˜-J˜—š ‘ œœœœœ˜JJšœœ˜Jšžœœœœœœœ ˜Ršœœœ˜,Jšœœœ#˜0Jšœœ$˜-Jšœœ˜Jšœ!œœ˜.šœ˜ Jš œœœœžœ˜$š œœœ œœ ˜,Jšœœœ˜Jšœ˜Jšœœžœœ˜,——Jšœ˜—Jšœ˜J˜—š žœœœœœ˜KJšœ œœœ˜Jšœœœ+˜Aš œœœœ'œœ˜MJšœ+˜+šœœœ˜2š œœœœ˜*Jšœœ+˜G—Jšœœ"˜<—Jš˜—Jšœ'˜'J˜—š žœœœœœ˜HJšœœœœ˜'šœ œ+œœ˜GJšœœ˜(Jšœœ˜-Jšœ1˜1—J˜—šžœœœœœœœœ˜2Jš œœœœ œ ˜3J˜—š ž œœœœ  œ˜Wšžœœœœœœœœ˜5Jšœœœ˜%—Jš œœœœœ˜šœœœ˜0Jšœœœ#˜1Jšœœ$˜.Jšœœ˜Jšœœ˜Jšœ"œœ˜/šœ˜ šœœœ˜Jš œœœœœœ˜@—š œœœ œœ ˜,Jšœœœ˜Jšœœœ˜Jšœ˜Jšœœœ˜šœœœ˜Jšœ˜Jšœ!œ˜-———Jšœ˜—šœœ˜šœ ˜Jšœ˜Jšœ œ˜,————šŸž ™Jšœœœ ˜Jšœœœ ˜J˜Jšž œœœœœœœœœœ˜MJ˜šž œœ˜$šž œœœ˜!šœœ˜)Jšœœœ˜9——šž œœœ˜Jšœœœœ˜šœœ˜šœœœ˜Jšœ žœœ œ˜A—š œœœœœ œ˜HJšœœ˜%—Jšœœ˜——Jšœ œ˜Jšœ œ˜Jšœ œ˜Jšœ,˜,Jšœœœ˜šœœ˜JšœCœ˜KJšœ5œœ˜R—Jšœ(œ˜<šœ*œœ˜@Jšœ˜Jš œœœœœ˜2Jšœ œ˜-š œœœœ˜;Jšœ$œ œ˜MJš˜—š œœœœ˜>Jšœ$œ œ˜MJš˜—šœ<œ œ˜WJšœ6˜6Jšœ(˜(š œœ(œœ˜HJšœ$œ œ˜MJš˜—Jšž œ˜'Jšœœ˜)šœ5œœ˜KJšœ0˜0Jšœ"˜"šœœ+œ˜FšœE˜EJšœ˜Jšœ˜—šœN˜NJšœœ˜-Jšœœ˜-Jšœ˜!—Jšœ˜—Jšœ˜—Jšœ˜—šœœ˜"šœK˜KJšœ˜Jšœ˜—Jšœ˜—Jš˜—Jšœ˜Jšœ˜Jšœ˜Jšœ1œ˜8Jšœ1œ˜8Jšœ1œ˜8šœ˜ Jšœ2œ˜F—Jšœ˜—J˜šžœœœœœœœœ˜JJšœœ ˜(š œœœœ˜1Jšœœ!˜+Jšœœ!˜+Jšœ œœ˜šœ˜Jš œœœœœ˜*Jšœœ˜————šŸ™Jšœ œœ( ˜MJšœ œœ0˜Išžœœœœ˜%JšœW˜W—J˜—šŸ™JšœPœ9˜ŒJšœLœ1˜€J˜—Jšœ˜J˜J˜—…—Dp