Basic gate-level simulation

Gates in the ACT language are specified using production rules, which can specify the Boolean logic for the pull-up and pull-down switching networks for a gate. Since production rules are general enough to specify arbitrary pull-up and pull-down networks, ACT can be used to describe both synchronous and asynchronous circuits. The ACT language contains many constructs that can be used to create design hierarchy, simplifying the description of the circuit. Components can be arrayed, grouped together, or connected to each other using simple constructs.

The following ACT file describes a process that corresponds to one inverter:

inv.act
defproc inv (bool? in; bool! out)
{
   prs {
     in -> out-
   ~in -> out+
   }
}

The parameters in parentheses are the ports for the process (i.e. what you can connect to externally), and the signals are of type bool to indicate they are Boolean-valued variables. Direction flags (! and ?) are used to specify that in is an input, and out is an output. Assuming the file above is called inv.act, the following ACT file can be used to create a single inverter:

test_inv.act
import "inv.act";
inv i;

This program imports the previously defined ACT file, and then creates an /instance/ of an inverter called i. The process inv (technically inv<>, an inverter without template parameters) can be viewed as the type of the instance i.

Simulating with prsim

If the file above is called test_inv.act, a production rule file can be created from the ACT file by:

% aflat test_inv.act > test_inv.prs

The output file is the following:

"i.in"->"i.out"-
~"i.in"->"i.out"+

Note that ACT uses . (like standard programming languages) as a separator between the name of the instance and internal nodes within it. (Note that magic, irsim use / as a separator; Xyce uses colon).

If the file is saved as test_inv.prs, it can be simulated using the production rule simulator called prsim.

% prsim inv.prs    

(Prsim) initialize 
(Prsim) watch i.in
(Prsim) watch i.out
(Prsim) status X
(Prsim) set i.in 0
(Prsim) cycle
(Prsim) set i.in 1
(Prsim) cycle

prsim will check if the production rules being run are stable and non-interfering. It doesn't check all possible delay configurations, but just reports errors if it observes unstable or interfering production rules while the simulation is running. Try help as a prsim command to see the range of commands supported by prsim.

One of the useful features of prsim is that it can automatically randomize the delays of production rule firings. To do this, use

(Prsim) random

After this command, all delays are randomized. This is a useful test to see if your production rules are stable and non-interfering. If prsim finds that a production rule is unstable, it sets its output to X (for undefined). These Xs can propagate through the circuit.

Local variables can be defined in ACT in the usual way.

defproc sigbuf (bool? in; bool! out)
{
   bool mid;
   prs {
     in -> mid-
   ~in -> mid+
 
     mid -> out-
    ~mid -> out+
   }
}

In this example, mid is a local variable and cannot be accessed from outside the process. Note that all Booleans used in production rules have to be declared before they are used.

defproc invarray (bool in[8], out[8])
{
   inv x[8];
 
   (i : 8 : x[i].in = in[i]; x[i].out = out[i]; ) 
}

This process creates an array of 8 inverters. The second statement is a loop construct. The loop index is i, the range is 0 to 7, and the body of the loop contains two statements: x[i].in = in[i], and x[i].out = out[i]. Both of them connect two signals to each other.