FILE: Main.mesa
Last edited by Ousterhout, April 16, 1985 11:36:38 am PST
Christian LeCocq December 19, 1986 2:15:37 pm PST
DIRECTORY
Atom,
Build,
Check,
CommandTool,
Delay,
DPrint,
Flow,
FS,
Globals,
HashTable,
Icons,
IO,
Mark,
Menus,
Model,
Parse,
Printout,
Process,
ProcessProps,
Rope,
TerminalIO;
TypeScript,
ViewerIO,
ViewerOps,
ViewerTools;
Main: CEDAR PROGRAM
IMPORTS
Atom,
Build,
Check,
CommandTool,
Delay,
DPrint,
Flow,
FS,
HashTable,
Icons,
IO,
Mark,
Menus,
Model,
Parse,
Printout,
Process,
ProcessProps,
TerminalIO
TypeScript,
ViewerIO,
ViewerOps,
ViewerTools
BEGIN
OPEN Globals;
StdOut: PUBLIC IO.STREAM ← TerminalIO.TOS[];
DupsOK: PUBLIC BOOLEAN ← FALSE;
Stop: PUBLIC REF BOOLEAN ← NEW[BOOLEAN ← FALSE];
notQuit: BOOLEAN ← TRUE; -- FALSE means ending Crystal.
quitButton,
stopButton,
resetButton,
checkButton,
clearButton,
critButton,
helpButton,
statButton: Menus.MenuEntry;
The following variables hold information passed between high-level procedures and hash table enumeration procedures.
ivalue: INT;
This page defines the Crystal commands.
CmdTable:
ARRAY[0..30)
OF Rope.
ROPE ←
[
"quit",
"bus node node ...",
"capacitance pfs node node ...",
"check",
"clear",
"critical [file] [index{m}]",
"delay",
"dump file",
"flow in/out/off/ignore/normal flow flow ...",
"help",
"input node node ...",
"markdynamic node value node value ...",
"model [name]",
"options [name value] [name value] ...",
"output node node ...",
"parameter",
"prcapacitance [-t pfs] [node node ...]",
"precharged node node ...",
"prfets [node node ...]",
"prnodes [node node ...]",
"prresistance [-t ohms] [node node ...]",
"ratio [limit value] [limit value] ...",
"recompute",
"resistance ohms node node ...",
"set 0/1 node node ...",
"source file",
"statistics",
"transistor",
"undump file",
"watch node node ..."
];
CmdProcs:
ARRAY[0..30)
OF Globals.CmdProc ←
[
QuitCmd,
Mark.BusCmd,
Mark.CapCmd,
Check.CheckCmd,
ClearCmd,
DPrint.CriticalCmd,
Delay.DelayCmd,
DPrint.DumpCmd,
Flow.FlowCmd,
HelpCmd,
Mark.InputCmd,
MarkDynamicCmd,
Model.ModelCmd,
OptionsCmd,
Mark.OutputCmd,
Model.ParmCmd,
Printout.PrintCap,
Mark.PrechargedCmd,
Printout.PrintFets,
Printout.PrintNodes,
Printout.PrintRes,
Check.RatioCmd,
DummyCmd,
DPrint.RecomputeCmd,
Mark.ResCmd,
Mark.SetCmd,
SourceCmd,
StatsCmd,
Model.TransistorCmd,
DPrint.UndumpCmd,
DummyCmd,
Mark.WatchCmd
];
The stuff on this page defines the class of each command, so that we can make sure that comands are issued in the right order.
CmdClass: TYPE = {model, afterClear, circuit, dynamic, check, setup, delay, clear, anytime};
curClass: CmdClass ← model;
marked:
BOOLEAN;
Marked is TRUE if flow and power rails have been marked,
FALSE means we still have to do it at some point.
classNames:
ARRAY CmdClass
OF Rope.
ROPE ←
[
"model",
"clear",
"circuit",
"markdynamic",
"check",
"setup",
"delay",
"clear",
"miscellaneous"
];
classes:
ARRAY[0..30)
OF CmdClass ←
[
circuit, -- build
circuit, -- bus
circuit, -- capacitance
check, -- check
clear, -- clear
anytime, -- critical
delay, -- delay
anytime, -- dump
circuit, -- flow
anytime, -- help
circuit, -- input
dynamic, -- markdynamic
model, -- model
anytime, -- options
circuit, -- output
model, -- parameter
anytime, -- prcapacitance
setup, -- precharged
anytime, -- prfets
anytime, -- prnodes
anytime, -- prresistance
check, -- ratio
anytime, -- recompute
circuit, -- resistance
setup, -- set
anytime, -- source
anytime, -- stats
model, -- transistor
anytime, -- undump
anytime -- watch
];
DummyCmd: Globals.CmdProc =
template forc suppressed cmds
BEGIN
IO.PutRope[StdOut, "this command is no longer supported\n"];
END;
HelpCmd: Globals.CmdProc =
Prints out a synopsis of the commands.
BEGIN
FOR i:
INTEGER
IN [0..
LENGTH[CmdTable])
DO
IO.PutF[StdOut, "%s\n", IO.rope[CmdTable[i]]];
ENDLOOP;
END;
QuitCmd: Globals.CmdProc =
BEGIN
notQuit ← FALSE;
END;
SourceCmd: Globals.CmdProc =
Reads in more commands from a command file.
BEGIN
stream: IO.STREAM;
msg: Rope.ROPE;
IF args =
NIL
THEN
BEGIN
IO.PutRope[StdOut, "No file name given.\n"];
RETURN;
END;
stream ←
FS.StreamOpen[args.rope
! FS.Error => IF error.group # bug
THEN {msg ← error.explanation; CONTINUE}];
IF msg #
NIL
THEN
BEGIN
IO.PutF[StdOut, "Couldn't open %s: %s.\n", IO.rope[args.rope], IO.rope[msg]];
RETURN;
END;
IntrpCmds[stream, FALSE, globalVars];
IO.Close[stream];
END;
StatsCmd: Globals.CmdProc =
Calls various routines to print out statistics about what Crystal has done.
BEGIN
Build.Stats[];
Flow.Stats[];
Mark.Stats[];
Delay.Stats[];
DPrint.Stats[];
END;
OptionsCmd: Globals.CmdProc =
This command procedure reads "name value" argument pairs and sets internal options. Some of the options require no values. If no names or values are given, then the current option settings are printed.
BEGIN
optionTable:
ARRAY[0..19)
OF Rope.
ROPE ←
[
"bus",
"graphics",
"limit",
"mempaths",
"noprintedgespeeds",
"noseedelays",
"noseedynamic",
"noseesettings",
"paths",
"printedgespeeds",
"ratiodups",
"ratiolimit",
"seealldelays",
"seeallsettings",
"seedelays",
"seedynamic",
"seesettings",
"units",
"watchpaths"
];
index, i: INT;
r: REAL;
ok: BOOLEAN;
anyOptions: BOOLEAN ← FALSE;
WHILE args #
NIL
DO
anyOptions ← TRUE;
TRUSTED {index ← Parse.Lookup[rope: args.rope, table: DESCRIPTOR[optionTable]]};
IF index = -1 THEN IO.PutF[StdOut, "Ambiguous option name: %s.\n", IO.rope[args.rope]]
ELSE IF index = -2 THEN IO.PutF[StdOut, "Unknown option name: %s.\n", IO.rope[args.rope]]
ELSE SELECT index FROM
bus: set bus threshold.
0 =>
BEGIN
args ← args.next;
[ok, r] ← Parse.Real[args];
IF NOT ok THEN IO.PutRope[StdOut, "No bus threshold given.\n"]
ELSE Delay.BusThreshold ← r;
END;
graphics: set graphical output format.
1 =>
BEGIN
args ← args.next;
IF args = NIL THEN IO.PutRope[StdOut, "No graphical style given.\n"]
ELSE IO.PutRope[StdOut, "Sorry, \"graphics\" isn't implemented yet.\n"];
END;
limit: set maximum number of delay calculations before despair.
2 =>
BEGIN
args ← args.next;
[ok, i] ← Parse.Int[args];
IF NOT ok THEN IO.PutRope[StdOut, "No delay limit value given.\n"]
ELSE Delay.DelayLimit ← i;
END;
mempaths: set count of how many memory paths to record.
3 =>
BEGIN
args ← args.next;
[ok, i] ← Parse.Int[args];
IF NOT ok THEN IO.PutRope[StdOut, "No memory path count given.\n"]
ELSE IF i > DPrint.MaxPaths THEN IO.PutF[StdOut, "Can't record more than %d paths.\n", IO.int[DPrint.MaxPaths]]
ELSE DPrint.NumPaths[memory] ← i;
END;
noprintedgespeeds: set boolean not to print out edge speeds in "critical" command.
4 =>
BEGIN
DPrint.PrintEdgeSpeeds ← FALSE;
END;
noseedelays: set flag not to print out delays as they are computed.
5 =>
BEGIN
Delay.Print ← FALSE;
Delay.PrintAll ← FALSE;
END;
noseedynamic: set flag not to print out dynamic nodes as they are found.
6=>
Mark.SeeDynamic ← FALSE;
noseesettings: set flag not to print out node settings when they are made.
7 =>
BEGIN
Mark.SeeSettings ← FALSE;
Mark.SeeAllSettings ← FALSE;
END;
paths: set count of how many paths to record.
8 =>
BEGIN
args ← args.next;
[ok, i] ← Parse.Int[args];
IF NOT ok THEN IO.PutRope[StdOut, "No path count given.\n"]
ELSE IF i > DPrint.MaxPaths THEN IO.PutF[StdOut, "Can't record more than %d paths.\n", IO.int[DPrint.MaxPaths]]
ELSE DPrint.NumPaths[any] ← i;
END;
printedgespeeds: set boolean to print out edge speeds.
9 =>
BEGIN
DPrint.PrintEdgeSpeeds ← TRUE;
END;
ratiodups: set count of how ratio messages per ratio type.
10 =>
BEGIN
args 𡤊rgs.next;
[ok, i] ← Parse.Int[args];
IF NOT ok THEN IO.PutRope[StdOut, "No duplicate ratio limit given.\n"]
ELSE Check.ratioLimit ← i;
END;
ratiolimit: set count of maximum allowabl ratio error messages.
11 =>
BEGIN
args ← args.next;
[ok, i] ← Parse.Int[args];
IF NOT ok THEN IO.PutRope[StdOut, "No ratio error limit given.\n"]
ELSE Check.totalRatioLimit ← i;
END;
seealldelays: set flag to print out all delays as they are computed.
12 =>
Delay.PrintAll ← TRUE;
seeallsettings: set flag to print out all node settings as they are made.
13 =>
Mark.SeeAllSettings ← TRUE;
seedelays: set flag to print out delays for named nodes only.
14 =>
BEGIN
Delay.PrintAll ← FALSE;
Delay.Print ← TRUE;
END;
seedynamic: set flag to print out dynamic nodes as they are found.
15 =>
Mark.SeeDynamic ← TRUE;
seesettings: see settings of named nodes only.
16 =>
BEGIN
Mark.SeeAllSettings ← FALSE;
Mark.SeeSettings ← TRUE;
END;
units: set units for printout and read-in.
17 =>
BEGIN
args ← args.next;
[ok, r] ← Parse.Real[args];
IF NOT ok THEN IO.PutRope[StdOut, "No units value given.\n"]
ELSE
BEGIN
Build.Units ← r;
Printout.Units ← r;
END;
END;
watchpaths: set count of how many watched paths to record.
18 =>
BEGIN
args ← args.next;
[ok, i] ← Parse.Int[args];
IF NOT ok THEN IO.PutRope[StdOut, "No watched path count given.\n"]
ELSE IF i > DPrint.MaxPaths THEN IO.PutF[StdOut, "Can't record more than %d paths.\n", IO.int[DPrint.MaxPaths]]
ELSE DPrint.NumPaths[watched] ← i;
END;
IF args # NIL THEN args ← args.next;
ENDLOOP;
IF
NOT anyOptions
THEN
BEGIN
IO.PutF[StdOut, "Bus threshold is %.1f pf\n", IO.real[Delay.BusThreshold]];
IO.PutF[StdOut, "Delay limit is %d stages\n", IO.int[Delay.DelayLimit]];
IO.PutF[StdOut, "Print units are %.1f microns per lambda\n", IO.real[Printout.Units]];
IO.PutF[StdOut, "Recording %d memory paths, %d arbitrary paths", IO.int[DPrint.NumPaths[memory]], IO.int[DPrint.NumPaths[any]]];
IO.PutF[StdOut, ", %d watched paths\n", IO.int[DPrint.NumPaths[watched]]];
IO.PutF[StdOut, "Ratio limits are %d errors per ratio, %d total\n", IO.int[Check.ratioLimit], IO.int[Check.totalRatioLimit]];
IF Delay.PrintAll
THEN
IO.PutRope[StdOut, "Printing all new delays to nodes\n"]
ELSE
IF Delay.Print
THEN
IO.PutRope[StdOut, "Printing new delays to named nodes\n"]
ELSE IO.PutRope[StdOut, "Not printing new delays\n"];
IF Mark.SeeDynamic
THEN
IO.PutRope[StdOut, "Printing out dynamic node settings\n"]
ELSE IO.PutRope[StdOut, "Not printing dynamic nodes\n"];
IF Mark.SeeAllSettings
THEN
IO.PutRope[StdOut, "Printing all node settings\n"]
ELSE
IF Mark.SeeSettings
THEN
IO.PutRope[StdOut, "Printing named node settings\n"]
ELSE IO.PutRope[StdOut, "Not printing node settings\n"];
IF DPrint.PrintEdgeSpeeds
THEN
IO.PutRope[StdOut, "Printing edge speeds\n"]
ELSE IO.PutRope[StdOut, "Not printing edge speeds\n"];
END;
END;
ClearCmd: Globals.CmdProc = {
This command procedure reinitializes timing and flag information in all of the nodes and transistors. Any information set by "set", "flow", and "delay" commands is cleared. The procedure also checks to be sure that certain flags, like inPath, haven't accidentally been left set.
ClearProc: HashTable.EachPairAction =
BEGIN
p: Globals.Pointer;
f: Globals.Fet;
node: Globals.Node ← NARROW[value];
IF node.inPath
THEN
BEGIN
IO.PutF[StdOut, "Crystal bug: inPath left set at %s.\n", IO.rope[Printout.NodeRope[node, globalVars]]];
node.inPath ← FALSE;
END;
node.always0 ← FALSE;
node.always1 ← FALSE;
node.precharged ← FALSE;
node.ratioError ← FALSE;
node.watched ← FALSE;
node.hiTime ← -1;
node.loTime ← -1;
p ← node.firstPointer;
WHILE p #
NIL
DO
f ← p.fet;
p ← p.next;
IF f.gate # node THEN LOOP;
f.flowFromSource ← FALSE;
f.flowFromDrain ← FALSE;
f.forcedOn ← FALSE;
f.forcedOff ← FALSE;
ENDLOOP;
END;
Hash.Enumerate[table: globalVars.nodeTable, pattern: "*", proc: clearProc, errorStream: StdOut];
[] ← HashTable.Pairs[globalVars.nodeTable, ClearProc];
It is crucial that Vdd and Ground get left marked as inputs and as fixed in value. Otherwise the marking routines will misbehave a most horrendous manner.
IF globalVars.GroundNode #
NIL
THEN
BEGIN
globalVars.GroundNode.input ← TRUE;
globalVars.GroundNode.always0 ← TRUE;
END;
IF globalVars.VddNode #
NIL
THEN
BEGIN
globalVars.VddNode.input ← TRUE;
globalVars.VddNode.always1 ← TRUE;
END;
DPrint.Clear[];
};
IntrpCmds:
PROC[stream:
IO.
STREAM, interactive:
BOOLEAN, globalVars: Globals.GlobalVars] =
This procedure reads command lines from stream, parses them up, looks up the commands in CmdTable, and invokes command routines. This routine returns only upon receiving an end of file or a "quit" command. Interactive specifies whether or not this is an interactive interpreter. If it is TRUE, then prompts are output on StdOut before each command.
BEGIN
line: Rope.ROPE;
args: Arg;
index: INT;
class: CmdClass;
{ENABLE IO.Error => CONTINUE;
WHILE notQuit
DO
IF ~interactive AND IO.EndOf[stream] THEN RETURN;
IF Stop^
THEN
BEGIN
IF interactive THEN IO.PutRope [StdOut, "[Interrupted]\n"]
ELSE RETURN;
END;
IF interactive THEN line ← TerminalIO.RequestRope[": "]
ELSE line ← IO.GetLineRope[stream];
args ← Parse.Args[line];
IF interactive THEN Stop^ ← FALSE;
IF args = NIL THEN LOOP;
IF NOT interactive THEN IO.PutF[StdOut, ": %s\n", IO.rope[line]];
TRUSTED {index ← Parse.Lookup[args.rope, DESCRIPTOR[CmdTable]]};
IF index = -1
THEN
IO.PutF[StdOut, "Ambiguous command: %s\n", IO.rope[args.rope]]
ELSE
IF index = -2
THEN
IO.PutF[StdOut, "Unknown command: %s\n", IO.rope[args.rope]]
ELSE
BEGIN
Make sure that the command is being given in the right order.
Also take care of automatic flow and Vdd/Ground marking.
class ← classes[index];
IF class < curClass
THEN
BEGIN
IO.PutF[StdOut, "The %s command shouldn't be", IO.rope[args.rope]];
IO.PutF[StdOut, " invoked after a %s command.\n", IO.rope[classNames[curClass]]];
END;
SELECT class FROM
anytime => NULL;
model, circuit => IF class > curClass THEN curClass ← class;
dynamic, check, setup, delay =>
BEGIN
IF
NOT marked
THEN
BEGIN
IO.PutRope[StdOut, "Marking transistor flow...\n"];
Flow.Mark[NIL, globalVars];
IO.PutRope[StdOut, "Setting Vdd to 1...\n"];
Mark.SetNodeValue[node: globalVars.VddNode, value: 1, propAnyway: TRUE, globalVars: globalVars];
IO.PutRope[StdOut, "Setting GND to 0...\n"];
Mark.SetNodeValue[node: globalVars.GroundNode, value: 0, propAnyway: TRUE, globalVars: globalVars];
marked ← TRUE;
END;
IF curClass < class THEN curClass ← class;
END;
clear =>
BEGIN
marked ← FALSE;
curClass ← afterClear;
END;
ENDCASE;
Actually execute the command.
(CmdProcs[index])[args.next, globalVars];
END;
ENDLOOP;
};
TypeScript.Destroy[typeScript];
END;
MarkDynamicCmd: Globals.CmdProc =
BEGIN
ok: BOOLEAN;
name: Rope.ROPE;
WHILE args #
NIL
DO
name ← args.rope;
args ← args.next;
IF args =
NIL
THEN
BEGIN
IO.PutF[StdOut, "No value given for %s.\n", IO.rope[name]];
EXIT;
END;
[ok, ivalue] ← Parse.Int[args];
IF ivalue < 0
OR ivalue > 1
OR
NOT ok
THEN
BEGIN
IO.PutF[StdOut, "Bad value given for %s: %s. Try 0 or 1.\n", IO.rope[name], IO.rope[args.rope]];
args ← args.next;
LOOP;
END;
Hash.Enumerate[table: globalVars.nodeTable, pattern: name, proc: mdProc, errorStream: StdOut];
Mark.SetNodeValue[Build.NodeFromRope[name, globalVars], ivalue, TRUE, globalVars];
args ← args.next;
ENDLOOP;
Mark.MarkDynamic[globalVars];
ClearCmd[NIL, globalVars];
marked ← FALSE;
END;
mdProc: Hash.EnumProc =
BEGIN
node: Node ← NARROW[entry.clientData];
Mark.SetNodeValue[node, ivalue, TRUE];
END;
CheckProc: Menus.ClickProc =
BEGIN
IO.PutRope[StdOut, "check\n"];
Check.CheckCmd[NIL];
IO.PutRope[StdOut, ": "];
END;
ClearProc: Menus.ClickProc =
BEGIN
IO.PutRope[StdOut, "clear\n"];
marked ← FALSE;
curClass ← afterClear;
ClearCmd[NIL];
IO.PutRope[StdOut, ": "];
END;
CritProc: Menus.ClickProc =
BEGIN
IO.PutRope[StdOut, "crit\n"];
DPrint.CriticalCmd[NIL];
IO.PutRope[StdOut, ": "];
END;
DelayProc: Menus.ClickProc =
BEGIN
IO.PutRope[StdOut, "delay\n"];
Delay.DelayCmd[NIL];
IO.PutRope[StdOut, ": "];
END;
HelpProc: Menus.ClickProc =
BEGIN
IO.PutRope[StdOut, "help\n"];
HelpCmd[NIL];
IO.PutRope[StdOut, ": "];
END;
StatProc: Menus.ClickProc =
BEGIN
IO.PutRope[StdOut, "stat\n"];
Stats2Cmd[NIL];
IO.PutRope[StdOut, ": "];
END;
StopProc: Menus.ClickProc =
BEGIN
Stop^ ← TRUE;
END;
QuitProc: Menus.ClickProc =
BEGIN
notQuit ← FALSE;
IO.Close[self: inStream,abort: TRUE];
END;
ResetProc: Menus.ClickProc =
BEGIN
TypeScript.Reset[typeScript];
Init[];
IO.PutRope[StdOut, ": "];
END;
Init: PROC =
BEGIN
Menus.ChangeNumberOfLines [menu: typeScript.menu, newLines: 2];
statButton ← Menus.CreateEntry[name: "stat", proc: StatProc, fork: FALSE];
Menus.InsertMenuEntry[menu: typeScript.menu, entry: statButton, line: 1];
helpButton ← Menus.CreateEntry[name: "help", proc: HelpProc, fork: FALSE];
Menus.InsertMenuEntry[menu: typeScript.menu, entry: helpButton, line: 1];
delayButton ← Menus.CreateEntry[name: "delay", proc: DelayProc, fork: FALSE];
Menus.InsertMenuEntry[menu: typeScript.menu, entry: delayButton, line: 1];
critButton ← Menus.CreateEntry[name: "crit", proc: CritProc, fork: FALSE];
Menus.InsertMenuEntry[menu: typeScript.menu, entry: critButton, line: 1];
clearButton ← Menus.CreateEntry[name: "clear", proc: ClearProc, fork: FALSE];
Menus.InsertMenuEntry[menu: typeScript.menu, entry: clearButton, line: 1];
checkButton ← Menus.CreateEntry[name: "check", proc: CheckProc, fork: FALSE];
Menus.InsertMenuEntry[menu: typeScript.menu, entry: checkButton, line: 1];
resetButton ← Menus.CreateEntry[name: "Reset", proc: ResetProc, fork: FALSE];
Menus.SetGuarded[resetButton, TRUE];
Menus.InsertMenuEntry[menu: typeScript.menu, entry: resetButton];
stopButton ← Menus.CreateEntry[name: "STOP!", proc: StopProc, fork: FALSE];
Menus.InsertMenuEntry[menu: typeScript.menu, entry: stopButton];
quitButton ← Menus.CreateEntry[name: "Quit", proc: QuitProc, fork: FALSE];
Menus.SetGuarded[quitButton, TRUE];
Menus.InsertMenuEntry[menu: typeScript.menu, entry: quitButton];
ViewerOps.PaintViewer[viewer: typeScript, hint: all];
ViewerTools.SetSelection[viewer: typeScript];
NodeTable ← Hash.NewTable[];
VddNode ← Build.BuildNode["Vdd"];
GroundNode ← Build.BuildNode["GND"];
END;
Initialization and startup:
TopLevelProc: PROC[stream: IO.STREAM, interactive: BOOLEAN, wd: Rope.ROPE] =
inner: PROC[] =
BEGIN
IntrpCmds[stream, interactive];
END;
ProcessProps.AddPropList[Atom.PutPropOnList[NIL, $WorkingDirectory, wd], inner];
END;
RunCrystal:
PUBLIC
PROC [globalVars: Globals.GlobalVars] ~ {
IntrpCmds[NIL, TRUE, globalVars];
notQuit ← TRUE;
};
typeScript ← TypeScript.Create[info: [name: "Crystal2", iconic: TRUE, icon: Icons.NewIconFromFile["Crystal.icons", 2
!ANY =>
BEGIN
IO.PutRope[StdOut, "Couldn't find Crystal.icons.\n I hope you're satisfied with the normal boring icon.\n"];
CONTINUE;
END]]];
Init[];
[in: inStream, out: StdOut] ← ViewerIO.CreateViewerStreams[name: "Crystal2",viewer: typeScript];
IO.PutRope[StdOut, "Crystal2, version 0.1\n"];
TRUSTED {crystalProcess ← FORK TopLevelProc[inStream,TRUE,CommandTool.CurrentWorkingDirectory[]]};
TRUSTED {Process.Detach[crystalProcess]};
END.