-- 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.