Technical Guide to the 6.111 C Simulator

Author:
Mark W. Eichin


Introduction

The C simulator is a time compression sequential digital logic simulator. It compiles a circuit description into a C program, which can then be executed with varying inputs or with input streams from other programs as appropriate.

This Technical Guide will describe the inner workings and design of the simulator library and macro packages, as well as the flow of control used in building a simulation. While simple instructions are provided for producing straightforward simulations, improvements are only possible with an understanding of the system. Also, it is expected that complex extensions (such as a UART module, for example) would be implemented in a more efficient manner than would happen with high level gate based implementation.

The system is designed to conform to the dpANS C standard\footnote{as tested with the Free Software Foundation GCC v1.21}, as well as being usable with existing C compilers\footnote{4.3BSD Vax Portable C Compiler}. It has also been tested for portability\footnote{MetaWare High C compiler, IBM 4.3BSD release.}\footnote{Macintosh Programmers Workshop C, 1.0, Mac II} on machines of moderately interesting architectures.

Building Submodules

Complex submodules should either be New functions are straightforward to implement, and fast as well, if you are familiar with the internal workings of the simulator.

Submodule

A submodule, which can be either a user defined circuit element or a primitive, has three important components:

Macro Header

Two pieces of information are needed by the compiler to give the user a simple interface to the module: a procedure declaration and a macro ``connector''. The procedure declaration can be simply int my_func(); This is extracted from the source file, where a line #define FUNCTION my_func signals both the start of the header information and the name of the function being defined in this module.

Connector macros

The connector macro is much more complex. To implement a gate's connection to the wires in a circuit, the gate must first be assembled as a list. The first element of the list is a procedure, the rest of the elements are later passed to it as arguments. Thus, a typical list for an xor gate would consist of (xor_func C A B), and the procedure would be called with (C A B) as arguments.

Once the connector list is assembled, it has to be attached to its inputs. (The outputs are already ``attached'' by virtue of their being arguments to the procedure.) The list becomes a property of all the inputs, and when any of them change, the procedure is automatically run.

Connector macro builders

For the user's convenience, make_binop, make_unop, make_tracer are provided to produce circuit one output elements with two inputs, one input, or a no-output element with one input (for ``tracing'' a line, either monitoring for some specific change or recording the values outside of the simulator.) These are constructed\footnote{See chains.c} using the make_anop macro. This macro is called with the list of arguments, the list of dependencies (which are generally subsets, though not strictly so) and the lengths of the argument lists. Due to the lack of a varargs concept for C macros, these lengths are used to index into other macros, which are predefined for a reasonable range of values (0-10). In order to handle longer procedure macros with with make_anop, simply create more macros in the style of make_arg0-10 and make_dep0-9 with the appropriate parameter.

Preprocessing

A custom preprocessor could have been written to perform this operation automatically, however that could have drastically reduced the portability of the system. The only specialized handlers used are two awk scripts: Awk is sufficiently portable that this is not a major concern, and in any case the operations can either be done on a machine that does have them, or done by hand with a text editor.

Control Flow

File organization

The system is currently made of two distinct subsystems, the function libraries (with all of the primitive elements) and the support libraries (the main control loop, the debugging aids, event processing, and memory management tools). When an executable is built, it simply links all of the objects mentioned in the makefile and then links against libfunc.a which contains the individual primitive elements.

Program Flow

A typical simulation flows as follows:

Data Structure

The entire simulation can be chased down from the master wirelist\footnote{Somewhat like pretty-printing a lisp structure.} to show all of the data handled by the simulator.

Document Production

This Technical Document, and the accompanying User Guide, were produced with the \LaTeX\/ document production system. Some tools were created to make this task easier, such as
\begin{figure} \caption{Awk script to make ``wave'' text from simulator data files} \begin{frepcode}{../src/awk/makewave.awk} \end{frepcode} \end{figure}

utility.sty --- generic useful styles including ``code''

\begin{frepcode}{utility.sty} \end{frepcode}

rpulse.sty --- \LaTeX\/ style file for producing waveforms

\begin{frepcode}{rpulse.sty} \end{frepcode}