1. The world according to Rosemary
1.1 Circuit Designs
A Circuit Design consists of a collection of Subcircuit Designs. A Circuit Design is a hierarchical decomposition, and Subcircuit Designs are the nodes in the hierarchy.
A Subcircuit Design has an Interface, through which it communicates with the rest of the design. A Subcircuit Design's Interface is a set of Items, each of which has a name (text string), an Abstract Type (described later), and a Direction (one of In, Out, or Both).
Given that we have restricted ourselves to describing systems to be built in silicon, we might insist that all Interface Items have the same Abstract Type: that Type would be the only one, the one corresponding to a single electrical node. Or we might distinguish more finely, and make three, one for each of the three primary layers of interconnect (poly, metal, and diffusion). Aside from that issue, we could think of Interfaces as trafficing in Boolean values, or voltages, or any one of a number of other things. Still further, we could imagine treating a collection of wires as a single unit (i.e., a single Interface Item), rather than many individuals. Abstract Types do not make any of those decisions. As far as we are concerned here, Abstract Types have nothing but an identity. They are used only in the following way: when two things are connected, we insist that they have the same Abstract Type.
A Subcircuit Design might, but does not need to, include a description of how it is composed of other Subcircuits (its Structural Description). Such a description is in terms of Instances of other Subcircuit Designs, and Abstract Networks connecting them. An Instance of a Subcircuit Design includes an association between each of the Items of the Interface of that Subcircuit Design and an Abstract Network. Furthermore, each of the Interface Items of the Subcircuit Design we are elaborating must also be associated with an Abstract Network. Thus, these associations describe how things are connected: all of the Interface Items associated with a particular Abstract Network are considered connected together. We insist that they all be of the same Abstract Type.
A Subcircuit Design might also, but also does not need to, include a description of what the outputs of an Instance of that Subcircuit would be, given the inputs (its Behavioral Description). This description might be a procedure, which is allowed to keep a state record, in some programming language.
A Subcircuit Design might also, but also does not need to, include a non-constructive Behavioral Description; one that can provide stimuli, observe the results, and approve or dissapprove. This description is called the Subcircuit Design's Test. It may also be a procedure in some programming language.
1.3 Rosemary
OK, now you can stop forgetting section 1.1. Rosemary has a Cedar data structure that represents concepts from both Circuit Designs and Simulation Structures. Rosemary simulates, by virtue of the efforts of possibly more than one Simulation Structure, and even other simulators, the operation of an Instance of a Circuit Design (that is, an Instance of a certain distinguished Subcircuit: its Root).
In Rosemary, Cells are used to represent Instances of Subcircuit Designs. For every Instance there is exactly one Cell. Furthermore, there are no Cells that do not represent Instances.
Rosemary includes an implementation of the simulator discussed in section 1.2. Its Modules are also represented by Cells, although it is not the case that all Cells must represent Modules.
Rosemary has another TYPE of object, Node, that is used to represent both the Abstract Networks of Subcircuit Designs, and the Distribution Lists of Simulation Structures.
When Instantiating a Subcircuit Design for simulation, either its Structural Description or its Behavioral Description could be used (assuming, of course, they are both present). When creating the data structure for a Rosemary simulation, a Subcircuit Design is always Instantiated in the context of some Simulation Structure. If the Subcircuit's Behavioral Description is to be used, a Module is introduced into that Simulation Structure. The Evaluation PROCEDURE of that Module is the Subcircuit's Behavioral Description.
If, on the other hand, the Subcircuit Design's Structural Description is to be used, there is a choice of how to use it. One alternative, Nested, also introduces a new Module into the Simulation Structure. It then introduces a new Simulation Structure, "inside" the Module, to implement that Subcircuit in terms of its components. The Evaluation PROCEDURE of the Module is another instance of the main loop of the Structural Simulator, directed at the inner Simulation Structure.
When the Nested alternative is used, new Nodes are created, in the inner Simulation Structure, corresponding to the Nodes in the outer Simulation Structure that are on the Interface of the Cell. That is, there are two sets of interface Nodes for such a Cell: one seen from the outside, and one seen from the inside. The ones seen from the outside are the ones entered in the interfaceNodes field in the Cell. The ones seen from the inside are put in the internalNodes SymbolTable.
The other alternative, Inline, does not introduce a new Module, nor a new Simulation Structure. It causes the components to be Instantiated in the same Simulation Structure.
The practical import of choosing one or the other is in the ordering of evaluations. In the Nested choice, when the nested Module is evaluated, it is run to completion before anything on the outside world may continue. In the Inline choice, there is no distinction between inside and outside; they mix freely. Inline is probably the one you want. The real reason for having Nested around it to start to debug ideas about plugging different simulators together.
Here are some highlights from Rosemary.Mesa, a DEFINITIONS file that has most of the relevant definitions in it:
Cell: TYPE = REF CellRep;
CellRep:
TYPE =
RECORD [
name: ROPE,
class: CellClass,
nextInstance: Cell,
parent, leftChild, rightSibling: Cell,
firstInternalNode: Node,
internalNodes, components: SymbolTable,
interfaceNodes: NodeS,
other: AList,
type: CellType,
expansion: ExpandDecision,
realCellStuff: RealCellStuff -- non-NIL only for Real Cells
];
CellType: TYPE = {Real, Shadow};
RealCellStuff: TYPE = REF RealCellStuffRep;
RealCellStuffRep:
TYPE =
RECORD [
schedNext: Cell,
newIO, oldIO: REF ANY,
newIOAsWP, oldIOAsWP: WordPtr,
locked: BOOLEAN ← FALSE,
state: REF ANY,
eval: EvalProc,
schedWatchers, evalWatchers: CellWatcherList ← NIL];
NodeS: TYPE = REF NodeSR;
NodeSR: TYPE = RECORD [nodes: SEQUENCE length: CARDINAL OF Node];
The type of a Cell distinguishes those that represent Modules from those that do not; Real Cells represent Modules, and Shadow Cells represent Instances of Subcircuit Designs that have no Module (i.e., are Inlines). A CellClass represents a Subcircuit Design. The internalNodes SymbolTable of a Cell holds the Nodes that are used in connecting up the components of the Cell. The components SymbolTable holds the Subcircuit Design Instances from which that Cell is composed.
The interfaceNodes field holds the association between Interface Items of the Subcircuit Design and the Abstract Networks they are associated with. For I in [0 .. class.ports.length), interfaceNodes[I] represents the Abstract Network associated with the Interface Item represented by class.ports[I].
For Real Cells (that is, ones that also represent Modules in a Simulation Structure) there is an additional record that holds additional information: the realCellStuff. The Schedule of the Simulation Structure is a linked list, and the links are the schedNext fields. The newIO field holds a REF to the RECORD used by the Evaluation PROCEDURE. For the benefit of the simulator, it is duplicated as a LONG POINTER TO CARDINAL in newIOAsWP. The oldIO is used for detecting changes in newIO. The eval field holds the Module's Evaluation PROCEDURE, and the state field is for whatever use the Evaluation PROCEDURE wants to put it to.
Node: TYPE = REF NodeRep;
NodeRep:
TYPE =
RECORD [
name: ROPE,
type: SignalType ← NIL,
visible: Socket ← [NIL, LAST[CARDINAL]],
unwriteability: INTEGER ← 0,
readers: SocketList ← NIL,
watchers: ARRAY Priority OF NodeWatcherList ← ALL[NIL],
next: Node ← NIL,
other: AList ← NIL];
SocketList: TYPE = LIST OF Socket;
Socket: TYPE = RECORD [cell: Cell, index: CARDINAL];
Nodes hold the Distribution Lists of the Structural Simulator. A Distribution List is represented by a SocketList. The index in a Socket is into a table stored with the Class of the Cell referred to, which tells the simulator where to stuff the bits.
As was mentioned earlier, all of the Interface Items associated with a given Abstract Network must be of the same Abstract Type. That Type is stored in the Node. SignalTypes are used to represent Abstract Types.
SignalType: TYPE = REF SignalTypeRep;
SignalTypeRep:
TYPE =
RECORD [
toRope: ToRopeProc,
fromRope: FromRopeProc,
init: TypeInitProc,
maxWidth: MaxWidthProc,
parseTest: TestParseProc,
unParseTest: TestUnParseProc,
typeData: REF ANY];
Although Abstract Types have nothing but an identity, SignalTypes have more. But what they have is (mostly) just for constructing user interface; as far as most of Rosemary is concerned, only their identity is important. The ToRopeProc, FromRopeProc, MaxWidthProc, TestParseProc, and TestUnParseProc are for formatting and parsing Tests and Values (see discussion of mode in section 3.3). The TypeInitProc is used to provide a default initial value for fields in newIO.
SignalTypes are usually gotten by calling some peripheral procedure that creates them. "IntTypes.Mesa" is a file containing an interface that provides such a procedure; it can be found in the example. "Mnemonics.Mesa" is another.
CellClass: TYPE = REF CellClassRep;
CellClassRep:
TYPE =
RECORD [
name: ROPE,
expand: ExpandProc,
ioCreator: IOCreator,
initializer: Initializer,
eval: EvalProc,
test: EvalProc,
ports: Ports,
ioWordCount: CARDINAL,
ioTemplate: REF ANY,
firstInstance: Cell];
ExpandProc: TYPE = PROCEDURE [thisCell: Cell, initData: REF ANY];
CreateNode: PROCEDURE [within: Cell, name: ROPE, type: SignalType] RETURNS [node: Node];
CreateCell: PROCEDURE [within: Cell, instanceName, className, interfaceNodes: ROPE, initData: REF ANY ← NIL] RETURNS [cell: Cell];
IOCreator: TYPE = PROC [cell: Cell, initData: REF ANY];
Initializer: TYPE = PROCEDURE [cell: Cell, initData: REF ANY, leafily: BOOLEAN];
EvalProc: TYPE = PROCEDURE [cell: Cell];
Ports: TYPE = REF PortsRep;
PortsRep:
TYPE =
RECORD [
ports: SEQUENCE length: CARDINAL OF Port];
Port:
TYPE =
RECORD [
firstWord, wordCount: CARDINAL,
name: ROPE,
type: SignalType,
input, output: BOOLEAN ← FALSE];
A CellClass represents a Subcircuit Design. The ExpandProc holds its Structural Description. It is a procedure, which, when executed, calls certain other procedures (CreateNode and CreateCell); these calls describe to Rosemary how an Instance of this Subcircuit Design is constructed out of Instances of others.
In CreateNode and CreateCell, the within parameter identifies the Instance being elaborated.
When calling CreateCell, the association between the Interface Items of the Instance being created and Abstract Networks is specified in the interfaceNodes parameter. It is of the format <id> ":" <id> ( "," <id> ":" <id> ) * . In each pair, the first <id> identifies the Interface Item (by the name of a Port), and the second identifies the Abstract Network (by the name of a Node). An alternative format elides the first <id> and colon of all the pairs; the association is then positional.
The IOCreator is called when the Class is Instantiated using its Behavioral Description, or using its Structural Description in the Nested fashion. The sole purpose of the IOCreator is to allocate RECORDS of the appropriate TYPE and put REFS to them in newIO and oldIO.
The Initializer is called under the same circumstances as the IOCreator. The reason for splitting them is to allow the fields of the IO RECORDS to be initialized according to the whims of their SignalTypes. The Initializer can then override those initializations, if it is so desired. The Initializer is passed a further flag, leafily, which distinguishes the two cases in which it might be called. It is TRUE when the Behavioral Description is being used. In this case, the Initialzer is also responsible for allocating the state RECORD that the Behavioral Description will use, and putting a REF to it in the state field of the Cell.
The ExpandProc, the IOCreator, and the Initializer all take a parameter, initData, which is for whatever use they choose to put it to. The initData handed to CreateCell is what will be given to the ExpandProc, IOCreator, and Initializer of the Class of Cell being created.
The eval EvalProc holds the Behavioral Description of the Subcircuit Design. It is a procedure which computes from the input fields of newIO the new values for the output fields of newIO. The way changes in the output fields are detected is as follows. Before doing anything else, the EvalProc must copy all the ouptut fields of newIO into the output fields of oldIO. Then it computes, and updates the output fields of newIO. The simulator then compares the bits in the output fields of newIO with those of oldIO. Note that the simulator treats these simply as a collection of bits. Thus, putting pointers there probably won't work "right" (and putting REFs there is almost certain to confuse the garbage collector). The Structural Simulator uses the information in the ports sequence of the Class of the Cell to determine where each field begins and ends (note that the fields must be aligned on word boundaries -- where necessary, pad them out with fixed bits).
The test EvalProc hold the Test of the Subcircuit Design. It is a procedure which repeatedly: sets some inputs in the Cell's newIO, calls a procedure (Rosemary.Eval) to run the Cell, and compares the results in newIO with what it expected. Note that since communication is only through the Cell's newIO, this Test can be applied to both the Behavioral Description and the behavior engendered by the Structural Description (when expanded Nested).
The ports of a Class represent the Interface of the Subcircuit Design; each Port represents an Interface Item.
RegisterCellClass:
PROCEDURE [
className: ROPE,
expandProc: ExpandProc ← NIL,
ioCreator: IOCreator ← NIL,
initializer: Initializer ← NIL,
evalProc, testProc: EvalProc,
ports: Ports,
ioTemplate: REF ANY ← Unspecified];
Clients of Rosemary make CellClasses known to Rosemary via calls on RegisterCellClass.
The ioTemplate is left over from the bad old days; ignore it.
CreateTopCell: PROC [instanceName, className: ROPE, initData: REF ANY ← NIL, decider: ExpandDeciderClosure] RETURNS [cell: Cell];
ExpandDeciderClosure: TYPE = REF ExpandDeciderClosureRep;
ExpandDeciderClosureRep:
TYPE =
RECORD [
Decide: ExpandDecider, otherData: REF ANY];
ExpandDecider: TYPE = PROC [cell: Cell, otherData: REF ANY] RETURNS [ExpandDecision];
ExpandDecision: TYPE = {Leaf, Inline, Nested};
This is how you get started. CreateTopCell is used to Instantiate the Root Subcircuit of a Circuit Design. That Subcircuit must have an empty Interface.
The ExpandDeciderClosure is the vehicle that communicates the user's decisions, for each Instance, about whether to use the Behavioral Description or the Structural one (and in what fashion). The DEFINITIONS module RoseHelp.Mesa provides two ways of making those decisions:
ask: ExpandDeciderClosure;
DecideFromFile: PROC [fileName: ROPE] RETURNS [ExpandDeciderClosure];
The first, ask, uses a button-pushing screen interface. When RoseHelpImpl.bcd is loaded and started, it creates a Viewer. The Viewer has slots for displaying the instanceName and className of the Instantiation under question. It has a slot for the decision. It starts out with a possible suggestion, which then may be edited. When a decision is being requested, a Button appears, which, when pushed, causes the decision currently being displayed to be used.
The second,
DecideFromFile, reads the decisions from a file. The file should be a sentance of the language defined by the following grammar (with start symbol <cell>):
<cell> => "(" <instance name> [ <decision> <cell>* ] ")"
<decision> => "I" | "N" | "L"
<instance name> => a sequence of letters and numbers (starting with a letter), or something enclosed in double quotes.
If the <decision> for a <cell> is elided, it is taken to be "L" (Leaf -- use the Behavioral Description).