-- FILE: Main.mesa
-- Last edited by Ousterhout, April 16, 1985 11:36:38 am PST
DIRECTORY
Atom,
BasicTime,
Build,
Check,
CommandTool,
Delay,
DPrint,
Flow,
FS,
Globals,
Hash,
Icons,
IO,
Mark,
Menus,
Model,
Parse,
Printout,
Process,
ProcessProps,
Rope,
SafeStorage,
TypeScript,
ViewerIO,
ViewerOps,
ViewerTools,
VMStatistics;
Main: CEDAR PROGRAM
IMPORTS
Atom,
BasicTime,
Build,
Check,
CommandTool,
Delay,
DPrint,
Flow,
FS,
Globals,
Hash,
Icons,
IO,
Mark,
Menus,
Model,
Parse,
Printout,
Process,
ProcessProps,
SafeStorage,
TypeScript,
ViewerIO,
ViewerOps,
ViewerTools,
VMStatistics =
BEGIN
OPEN Globals;
inStream: IO.STREAM;
typeScript: TypeScript.TS;
crystalProcess: PROCESS;
stopButton, resetButton: 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..31) OF Rope.ROPE ←
[
"build file",
"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",
"thyme file",
"transistor",
"undump file",
"watch node node ..."
];
CmdProcs: ARRAY[0..31) OF CmdProc ←
[
BuildCmd,
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,
DPrint.RecomputeCmd,
Mark.ResCmd,
Mark.SetCmd,
SourceCmd,
StatsCmd,
ThymeCmd,
Model.TransistorCmd,
DPrint.UndumpCmd,
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..31) 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
circuit, -- thyme
model, -- transistor
anytime, -- undump
anytime -- watch
];
HelpCmd: 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;
BuildCmd: CmdProc =
-- Reads in the .sim file.
BEGIN
msg: Rope.ROPE;
IF args = NIL THEN
BEGIN
IO.PutRope[StdOut, "No file name given.\n"];
RETURN;
END;
msg ← Build.NetFromSim[args.rope];
IF msg # NIL THEN
IO.PutF[StdOut, "%s\n", IO.rope[msg]];
END;
ThymeCmd: CmdProc =
-- Reads in a Thyme-format file.
BEGIN
IF args = NIL THEN
BEGIN
IO.PutRope[StdOut, "No file name given.\n"];
RETURN;
END;
Build.NetFromThyme[args.rope];
END;
SourceCmd: 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];
IO.Close[stream];
END;
StatsCmd: 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: 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 ←args.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;
ENDCASE;
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: 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.
BEGIN
Hash.Enumerate[table: NodeTable, pattern: "*", proc: clearProc,
errorStream: StdOut];
-- 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 GroundNode # NIL THEN
BEGIN
GroundNode.input ← TRUE;
GroundNode.always0 ← TRUE;
END;
IF VddNode # NIL THEN
BEGIN
VddNode.input ← TRUE;
VddNode.always1 ← TRUE;
END;
DPrint.Clear[];
END;
clearProc: Hash.EnumProc =
BEGIN
p: Pointer;
f: Fet;
node: Node ← NARROW[entry.clientData];
IF node.inPath THEN
BEGIN
IO.PutF[StdOut, "Crystal bug: inPath left set at %s.\n",
IO.rope[Printout.NodeRope[node]]];
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;
IntrpCmds: PROC[stream: IO.STREAM, interactive: BOOLEAN] =
-- 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;
pulses: BasicTime.Pulses;
secs: REAL;
faults: INT;
words: INT;
{ENABLE IO.Error => CONTINUE;
WHILE TRUE DO
IF IO.EndOf[stream] THEN RETURN;
IF Stop↑ THEN
BEGIN
IF interactive THEN IO.PutRope [StdOut, "[Interrupted]\n"]
ELSE RETURN;
END;
IF interactive THEN IO.PutRope[StdOut, ": "];
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];
IO.PutRope[StdOut, "Setting Vdd to 1...\n"];
Mark.SetNodeValue[node: VddNode, value: 1, propAnyway: TRUE];
IO.PutRope[StdOut, "Setting GND to 0...\n"];
Mark.SetNodeValue[node: GroundNode, value: 0, propAnyway: TRUE];
marked ← TRUE;
END;
IF curClass < class THEN curClass ← class;
END;
clear =>
BEGIN
marked ← FALSE;
curClass ← afterClear;
END;
ENDCASE;
-- Actually exeute the command.
pulses ← BasicTime.GetClockPulses[];
TRUSTED
{
faults ← VMStatistics.pageFaults;
words ← SafeStorage.NWordsAllocated[];
};
(CmdProcs[index])[args.next];
pulses ← BasicTime.GetClockPulses[] - pulses;
secs ← BasicTime.PulsesToSeconds[pulses];
TRUSTED
{
faults ← VMStatistics.pageFaults - faults;
words ← SafeStorage.NWordsAllocated[] - words;
};
IO.PutF[StdOut, "[%.2f secs, %d words, %d faults]\n",
IO.real[secs], IO.int[words], IO.int[faults]];
END;
ENDLOOP;
};
TypeScript.Destroy[typeScript];
END;
MarkDynamicCmd: 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: NodeTable, pattern: name, proc: mdProc,
errorStream: StdOut];
args ← args.next;
ENDLOOP;
Mark.MarkDynamic[];
ClearCmd[NIL];
marked ← FALSE;
END;
mdProc: Hash.EnumProc =
BEGIN
node: Node ← NARROW[entry.clientData];
Mark.SetNodeValue[node, ivalue, TRUE];
END;
StopProc: Menus.ClickProc =
BEGIN
Stop↑ ← TRUE;
END;
ResetProc: Menus.ClickProc =
BEGIN
TypeScript.Reset[typeScript];
Init[];
IO.PutRope[StdOut, ": "];
END;
Init: PROC =
BEGIN
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];
ViewerOps.PaintViewer[viewer: typeScript, hint: menu];
IO.PutRope[StdOut, "Crystal, version 2X\n"];
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] =
BEGIN
inner: PROC[] =
BEGIN
IntrpCmds[stream, interactive];
END;
ProcessProps.AddPropList[Atom.PutPropOnList[NIL, $WorkingDirectory, wd], inner];
END;
typeScript ← TypeScript.Create[info: [name: "Crystal", iconic: FALSE]];
[in:inStream, out:StdOut] ← ViewerIO.CreateViewerStreams[name: "Crystal",
viewer: typeScript];
Init[];
typeScript.icon ← Icons.NewIconFromFile["Crystal.icons", 2
!ANY =>
BEGIN
IO.PutRope[StdOut, "Couldn't find Crystal.icons.\n"];
IO.PutRope[StdOut, "I hope you're satisfied with the normal boring icon.\n"];
CONTINUE;
END];
TRUSTED {crystalProcess ← FORK TopLevelProc[inStream, TRUE,
CommandTool.CurrentWorkingDirectory[]]};
TRUSTED {Process.Detach[crystalProcess]};
END.