Funcsim_rules 4.58 KB
	    RULES FOR WRITING FUNCTIONAL SIMULATORS
		msg 8/17/93


First, read KONA/doc/arch/sim.sho for background on the methodology,
overall structure, and a coding example (may be out of date).

The C simulators are intended to be fast, bit-accurate and as clock-accurate
as possible.  At a minimum all interfaces are clock synchronous.


GENERAL STRUCTURE

Typically a functional module (chip) contains positive-edge-triggered
register stages with asynchronous logic in between.  The asynchronous logic
produces the next clock's value for the registers.

Each module type operates on a data structure containing, for a particular
instance:
    * storage for internal nodes, including
	- asynchronous results
	- register outputs
	- next-clock register values (optional: see below)
    * storage for input wire/bus nodes
    * pointers to output wire/bus nodes

Modules are invoked by an enclosing level of heirarchy that contains
instances of all interconnecting wires and buses.


CLOCKING

All changes to module internal nodes and module outputs should occur at
the rising edge of the provoking clock.  These changes should result from a
single evaluation of the current state of the internal nodes and module inputs.


CODE ORDERING

There are three possible methods for correctly modeling the behavior of this
structure.  The first is applicable for simple models with no internal feedback
paths.  The second is the preferred method, since it has the best balance
between order enforcement and coding flexibility.  The third is the only
method that is order-insensitive in complex or schematic-derived designs.

1)   a) Update the last stage of register outputs, using any required
	asynchronous logic processing on the previous stage register values.
	Propagate them to the module outputs.
     b) Then update the previous stage register outputs; then the previous
	stage; etc.
     c) Then update the nodes directly affected by the module inputs.

No explicit next-clock register values are needed in the first scheme.

2)   a) Transfer all next-clock register values to register outputs.
     b) Propagate any resulting changes to module outputs (including
	any asynchronous logic following the last register stage).
     c) Then update all next-clock register values, using the register outputs
	updated in a) and the asynchronous logic in between.

The next-clock values can optionally be updated on the falling edge of the
clock, as long as the entire module is synchronous to a single clock.

3)   a) Transfer all next-clock register values to register outputs.
     b) do { evaluate all asynchronous logic elements }
	while (any next-state register values changed after this evaluation)

ASYNCHRONOUS ELEMENTS

A special problem arises for modules containing asynchronous logic, i.e.
unclocked inputs that affect outputs.  (Output enable pins are a good example
of this.)  How can it be guarantee that the input has been brought current at
the time it is consumed?  The enclosing level of heirarchy should guarantee
this; but suppose it cannot?  A good coding practice for this case is to
propagate asynchronous inputs to the module outputs unconditioned by the clock
edge detector.  This way the module can be reentered any number of times after
the active clock edge to update the asynchronous paths through the system.


BIDIRECTIONAL BUSES

To accommodate multi-process simulations, the enclosing level of heirarchy is
in charge of synchronizing propagation on buses that connect modules.  Each
module is required to have a separate datum set aside for the input value of
any bus it uses, even if it also drives that bus.  So at the interconnect
level, this sequence is followed:

    1) copy buses to module inputs
[ 1.5) assign bus to default value (e.g. PUP or undefined) ]
    2) evaluate modules, each of which updates the bus via its output pointer
	IFF its output enable is active

INITIALIZATION

The module initialization routine should perform the following tasks:

    1) Reset the clock edge detector
    2) Allocate (malloc) any large memories
    3) Randomize (or set to 0xdeadbeef) all nodes if approximation
	of power-up behavior is desired.  (This is not the same as reset
	behavior resulting from a reset input to the module.)

VECTORS

The programmer must construct buses and long integers from appropriate machine
elements.  There are routines lying around for doing arithmetic on 64-bit
integers.  For instance:
    vdev:  usr/src/gfx/VENICE/tools/gefsim/pack.c

The programmer need not break out I/O into individual signal wires.  This can
be done by the "logic analyzer" or test vector handling routines that are
separate from the functional simulation.