FSMImpl.mesa
Copyright Ó 1986, 1987 by Xerox Corporation. All rights reserved.
Barth, December 23, 1987 10:09:03 am PST
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.
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
Implementation Choices for exprImplKey
Current: $SC, $SCompress, $PLA, $PLACompress
Future: $ALPs?, $SCOptimized?
Common Types and Declarations
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:    BOOLFALSE;
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];
Creation From Schematics
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];
Find all States
FOR inst: NAT IN [0..rct.size) DO
IF CoreOps.ToBasic[rct[inst].type].class=stateClass THEN {
EachOutput: EachSortedPropProc = {
outputName: IO.ROPENARROW[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;
Find all Transitions
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.ROPENIL;
enable: REFNIL;
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.ROPEIF srcStates=NIL
THEN "(unknown)" ELSE srcStates.first.name;
tgtNm: IO.ROPEIF 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 ANYNIL];
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};
Creation from FSMData
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  ← ConvertToExp[fsm]; -- 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 []𡤋oolEx.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;
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]];
Order: ins Reset, Clock, PhA, PhB, Vdd Gnd, outs - used by icon builder.
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};
Expand State definitions
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 BOOLALL[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
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.
EncodeStates: PUBLIC PROC[fsm: FSMData] = {
Mod: PROC[r1, r2, r3, r4: IO.ROPENIL] = {firstMod ← FALSE; RETURN};
Mod: PROC[r1, r2, r3, r4: IO.ROPENIL] = { -- 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};
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 NATALL[0];
xtraOrder:  ARRAY [0..20) OF NATALL[0];
maxXtraCnt:  NAT ← 0;
outTab:   SymTab.Ref    ← SymTab.Create[];
outTabIndex:  INT      ← 0;
OutIndex: PROC[out: IO.ROPE] RETURNS[index: INT] = {
refInt: REF INTNARROW[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:  BOOLTRUE;
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;
Display results for debugging
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"]};
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.
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]];
Build subsets for non-variant states and collect variant states;
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;
Now try to find unique variants
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;
Accumulate variant outputs into mask
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;
Insert variant PatternState into variant subsets
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;
Add uniques to grp varients as one element sets (just for completeness)
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;
Just get the xtra bits registered in order
FOR bit: INT IN [0..NBits[maxXtraCnt]) DO
stateBitNm: IO.ROPEIO.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: BOOLTRUE;
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: BOOLTRUE;
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: BOOLTRUE;
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:  BOOLTRUE;
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.ROPEIO.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: INTMAX[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]];
Do non-variant state assignments;
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;
Do variant state assignments;
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:     BOOLTRUE;
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;
Print new State specifications.
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: BOOLFALSE]
RETURNS[size, index: NAT ← 0] = {
FOR s: StatePatterns ← stps, s.rest WHILE s#NIL DO size ← size+1 ENDLOOP;
IF ~bias
AND stps.first.ones^ = emptyArray^
AND (~useLocOnes OR stps.first.locOne^ = emptyArray^)
THEN index ← 1; -- don't use zero as code
size ← size + index};
PrintList: PROC[head: IO.ROPE, list: ROPES, log: IO.STREAMNIL] = {
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.STREAMNIL] = {
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: CHARSELECT 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] = {
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.
SameInTransOuts: PROC[l1, l2: ROPES] RETURNS[same: BOOLTRUE] = {
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;
Partition in transistions into sets
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=NILTHEN {newFSMStates ← CONS[states.first, newFSMStates]; LOOP};
For each set, replace targets with new states
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;
Remove each out transition from its target's in transition list
NewState loops removed here get replaced later with new transitions
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;
Copy each old out transition onto new state out list and target in list
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};
Recast to specific implementaton
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];
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  => { }; -- 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  => TRUE
ENDCASE    => FALSE]};
ImplementExpr: PROC[mach: Expression, exprImplKey: ATOM, reg: RegisterType]
RETURNS[impl: Core.CellType] = {
name:   IO.ROPENARROW[NARROW[mach, LIST OF REF].rest.first];
log:   IO.STREAM ← TerminalIO.TOS[];
SELECT exprImplKey FROM
$PLA, $PLACompress => {InstallPackage["PLAGen"]};
$SC,  $SCompress  => {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 => {
compSum: BOOL   ← exprImplKey=$SCompress;
key:   Expression;
multiLevel: Expression;
cacheNm: IO.ROPEIF 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];
log.PutRope["buffer fanouts ... "];
BufferLargeFanouts[multiLevel, 4];
log.PutRope["cache ... "];
SetExprTranslationCache[cacheNm, mach, multiLevel]};
log.PutRope["build it.\n"];
SELECT reg FROM
edgeTriggered => impl ← BuildSCMachine[multiLevel];
twoPhase   => impl ← BuildSCMachine[multiLevel, FALSE];
none    => impl ← BuildSCGates[multiLevel];
ENDCASE   => ERROR};
ENDCASE => ERROR};
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.
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]]
ELSEIF 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]};
Convert to machine expression
ConvertToExp: PUBLIC PROC[fsm: FSMData] 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 REFLIST[sts.first.name];
FOR outTrans: Transitions ← sts.first.outTrans, outTrans.rest WHILE outTrans#NIL DO
term: LIST OF REFNARROW[
BoolEx.OpExpr[and, resetFalse, IF outTrans.first.enable=NIL
THEN       sts.first.name
ELSE BoolEx.OpExpr[and, sts.first.name, outTrans.first.enable]]];
stateTransNm: IO.ROPEIO.PutFR["xSt%02g", IO.int[stateTransIdx]];
stateTransDef: LIST OF REFLIST[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;
FOR outs: ROPES ← fsm.initialState.outputs, outs.rest WHILE outs#NIL DO
OrOutTerm[outs.first, resetTrue] ENDLOOP;
[] ← SymTab.Pairs[outTab, BuildOutputDefs];
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 REFLIST[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]};
Simulation procedures
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];
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 ANYNIL;
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 ← 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.ROPEIF 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: BOOLTRUE] = {
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: BOOLNOT clockBad AND
(fsmData.register=edgeTriggered AND ~clockEval AND state.clockPort.l=L) OR
(fsmData.register=twoPhase   AND state.phBPort.l=H);
clockNew: BOOLNOT 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 state.resetPort.l FROM
X => state.nextTrans ← [];
H => state.nextTrans ← [target: fsmData.initialState];
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 } };
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 };
Standard Cell Implementation
BuildSCMachine: PUBLIC PROC [mach: Expression] 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 ← BuildSCRegister [mach];
vdd:  IO.ROPE ← "Vdd";
gnd:  IO.ROPE ← "Gnd";
clock:  IO.ROPE ← "Clock";
instances:  CoreCreate.CellInstances;
machName: IO.ROPENARROW[NARROW[mach, LIST OF REF].rest.first];
pas:   LIST OF CoreCreate.PANIL;
in:  INT = 0;
out: INT = 1;
publics: ROPESLIST [vdd, gnd, clock];
intOnlys: ROPESNIL;
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.WRNIL;
outs:   LIST OF CoreCreate.WRNIL;
internals:  LIST OF CoreCreate.WRLIST[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 INTNEW[INT ← 1];
elist:  LIST OF REFLIST[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]];
[]𡤌oreOps.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
[]𡤌oreOps.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 INTNARROW[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𡤀
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 INTNARROW[SymTab.Fetch[useCntTab, basic].val];
IF useCnt=NIL THEN {useCnt ← NEW[INT ← 0]; []←SymTab.Store[useCntTab, basic, useCnt]};
useCnt^  ← useCnt^ + 1;
TerminalIO.PutF["%g %g %g\n", IO.rope[rope], IO.rope[basic], IO.int[useCnt^]];
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 REFLIST[BoolEx.OpNm[var], key, val];
IF ~IsInput[key, val] THEN defs ← CONS[def, defs]};
CollectOuts: SymTab.EachPairAction = {
def: LIST OF REFLIST[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.WRNIL] = {
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] = {
machName: IO.ROPENARROW[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[];
vdd:   Core.Wire     ← NewWire["Vdd"];
gnd:   Core.Wire     ← NewWire["Gnd"];
clock:   Core.Wire     ← NewWire["Clock"];
MkOutNm: SymTab.EachPairAction = {
pas:  LIST OF CoreCreate.PANIL;
wireIn: Core.Wire ← NewWire[Rope.Cat["Nxt", key]];
wireOut: Core.Wire ← NewWire[      key];
blk:  Core.Wire ← NewWire[NIL];
CoreProperties.PutWireProp[blk, $Static, $UnconnectedOk];
ins  ← CONS[wireIn,  ins];
outs  ← CONS[wireOut, outs];
pas ← LIST[
["D", wireIn], ["Q", wireOut], ["CK", clock], ["NQ", blk], ["Vdd", vdd], ["Gnd", gnd]];
instances ← CONS [CoreCreate.InstanceList[fflop, pas], 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"] ] ]};
Expression Cache I/O
GetExprTranslationCache: PROC[name: IO.ROPE]
RETURNS[key, multiLevel: Expression] = {
cache: LIST OF REFNARROW[BoolEx.ReadExprFile[name.Cat["XCache"]]];
IF cache=NIL THEN RETURN[NIL, NIL];
TerminalIO.PutF["Reading expression cache: %g.expr\n", IO.rope[name]];
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 REFNIL;
TerminalIO.PutF["Writing expression cache: %g.expr\n", IO.rope[name]];
IF name=NIL OR key=NIL OR multiLevel=NIL THEN ERROR;
cache ← LIST[xCache, name.Cat[xCache], key, multiLevel];
BoolEx.WriteExprFile[cache, 3]};
FSMData generation aids
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 = NILTHEN 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: ROPESNIL]
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: BOOLTRUE] = {
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 REFNIL;
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]};
FSMData Validation
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:  BOOLFALSE;
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 REFLIST[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: BOOLFALSE] = {
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};
Runtime Loading
curWDir:   IO.ROPE ← FileNames.CurrentWorkingDirectory[]; -- when module runs
searchPath:  ROPESLIST[curWDir, "///7.0/Commands/", "///7.0/System/"];
InstallPackage: PROC[name: IO.ROPE] =
{[]�nvironment.StuffToCommandTool[Rope.Cat["Install ", name], curWDir, searchPath]};
Initialization
CoreProperties.PutCellClassProp[fsmClass, RosemaryUser.stateToMaxCharsProcProp, NEW[RosemaryUser.StateToMaxCharsProc ← FSMStateToMaxChars]];
CoreProperties.PutCellClassProp[fsmClass, RosemaryUser.stateToRopeProcProp, NEW[RosemaryUser.StateToRopeProc ← FSMStateToRope]];
END.