The prs sublanguage

The prs sublanguage is used to specify production rules. Production rules are the syntax used by ACT to specify pull-up and pull-down networks for a gate. For example, an inverter with input a and output b would be specified by the following production rule:

   a -> b-
  ~a -> b+

The left-hand side of a production rule is a Boolean expression, and the right hand side specifies the action to be taken when the Boolean expression is true. A more complete example of an inverter in ACT would be:

bool a, b;
prs {
   a -> b-
  ~a -> b+

A two-input NAND gate with inputs a and b and output c would be:

prs {
  a & b -> c-
 ~a | ~b -> c+

A two-input inverting C-element would be:

prs {
  a & b -> c-
~a & ~b -> c+

Note that the expressive power of production rules makes it equally easy to specify state-holding gates (like an inverting C-element) and combinational gates (like a NAND gate).

The three types of arrows

The syntax above is sufficient to be able to specify the Boolean conditions for arbitrary pull-up and pull-down networks, and uses a “simple” arrow (- >). In addition, there are two common cases of gates for which the prs language has special syntax.

The first common case is a combinational gate like an inverter or NAND. In a combinational gate, the Boolean expression for the pull-up is the complement of the Boolean expression for the pull-down. In this case we can simply specify one, and use the = > arrow to let ACT know that this is a combinational gate—i.e., the second production rule can be automatically generated from the given rule. The following is the same two-input NAND gate specified using a more compact syntax:

prs {
   a & b => c-

Note that this is the same as specifying

prs {
   ~a | ~b => c+

In both cases, ACT generates the second production rule from the first one by complementing the Boolean expression and changing the direction of the signal transition (+ to - and vice versa).

The second common case is that of C-elements. To specify the two-input C-element above, you can also write

prs {
   a & b #> c-

In this case, ACT generates the second production rule by complementing each variable in the Boolean expression.


When defining parameterized circuits, it is helpful to also have the corresponding parametrized production rules that implement the circuit. There are two loop constructs available that use the same “repetition” syntax within the body of the prs sublanguage.

bool x[5], y[5];
prs {
  (i:5: x[i] => y[i]-)

The example above creates five inverters when the circuit is expanded. i is the loop variable, and the body of the loop is simply replicated.

bool x[5], y;
prs {
  (&i:5: x[i]) => y-

The example above is a 5-input NAND gate. Here the replication construct is used with & as the separator, which is the same as:

prs {
  x[0] & x[1] & x[2] & x[3] & x[4] => y-

Similarly, | can also be used as a separator. Note that these can be combined with other expressions as needed, as in the following example:

prs { 
  en & (|i:5: x[i]) -> y-
  ~en -> y+

CMOS implementation

The ACT tools provide automated support for converting production rules into a transistor-level implementation. This is done in a way that is entirely under user control, so the way the production rules are written is strictly followed when creating the transistors that implement the production rules. The tool that performs this conversion is prs2net.

Implementable rules

In what follows, we assume that the production rule expression is written in negation-normal form1).

For a production rule to be CMOS-implementable, we require that all variables in the pull-up network are inverted, and all variables in the pull-down network are un-inverted. So in the example below, the first rule is not CMOS-implementable while the second and third rules are.

  a & b -> c+
  a & b -> d-
  ~a | ~b -> e+

This is because prs2net uses CMOS to implement the rules. In CMOS, pull-down networks are implemented with n-type transistors that turn on when the input is high (true). while pull-up networks are implemented with p-type transistors that turn on when the input is low (false).

Transistor sizing and flavors

The width, length, flavor, and folding of a transistor can be specified as part of the syntax of production rules. The following examples illustrate the syntax.

  a <10> & b <10> -> c-

In this example, the widths of the transistor for a and b are both 10 units. The value is dimensionless, and is converted into physical dimensions using the lambda parameter specified in the netlist configuration options file.

  a <20,4> & b <20,4> -> c-

In this example, the transistor specified is one with a width of 20 units and length of 4 units. The default values of width and length for p-type and n-type devices are specified in the netlist configuration file. In the earlier example, the standard length for the transistors would be used (which is typically set to the minimum valid length for the technology in the case of digital circuits).

  a <10,lvt> & b<10,lvt> -> c-

Transistors come in different flavors in a modern technology. The flavors supported by ACT are specified in the top-level ACT configuration. Here, the lvt flavor is being specified. The standard names that we use are svt (for standard Vt), lvt (for low Vt), and hvt (for high Vt).

Finally, it is often the case that multiple transistors in an expression use the same sizing information.

 a <10> & b -> c-

In this example, the sizing for b is omitted. However, it inherits the sizing for a. This implicit sizing only applies to a single production rule.

Gate ordering

A rule of the form

 a & b -> c-

it could be implemented with a series transistor chain. However, if the gates of the two series transistors are exchanged, the Boolean expression is still logically the same value although the circuit characteristics (e.g. delay) might be different.

ACT imposes a strict interpretation on gate ordering, since a circuit designer might have carefully chosen the gate ordering for their circuit. The interpretation is that, since the output is on the right hand side of the production rule, the gate closest to the output corresponds to the rightmost variable. So, in the example above, the gate order would be that a is closest to ground, and b is closest to c.

Multi-finger transistors

A multi-fingered transistor can be manually specified if necessary. The example

  (a <10> | a<10>) & (b<10> | b<10>) -> c-

would have the effect of using two multi-fingered transistors, with an effective width of 20 units each. This can be also specified as follows:

  a<20;2> & b<20;2> -> c-

or, using the implicit sizing rules, as

 a<20;2> & b -> c-

Some additional details of how you can control the circuit being generated from the production rules is described in the prs2net documentation.

Leakage adjustment

The leakage adjustment parameter can be used to increase the length of any minimum length device. This can be turned on in the sizing body, or by using

prs * {

The * before the opening brace says that the leakage adjustment parameter should be used. Note that leakage adjustment applies to the entire process/cell, even if the parameter is specified only in one of the prs blocks or sizing blocks.

Direct transistor specifications

In case this syntax is not sufficient, the prs language also includes support for specifying individual transistors. Since these are typically used as pass gates or transmission gates in digital circuits, we use the following syntax:

prs {
   passp <10,4> (gate, source, drain)   /* p-type device, with specified sizing */
   passn <20,8> (gate, source, drain)   /* n-type device with specified sizing */

Note that automatic staticizer (keeper) generation is only triggered when a production rule is specified. If part of your circuit for an output signal is described using production rules and part of it is described using transistors, then staticizer generation will likely be triggered; you can turn this off using the [keeper=0] attribute.

Explicit capacitors

The prs sublanguage can also be used for designing mixed-signal circuits. Explicit capacitors can be specified in the following manner:

prs {
  cap (node1, node2); /* explicit capacitor of one unit size between the two nodes */
  cap<10> (node1, node2); /* capacitor of size 10 units */
  cap<10,5> (node1, node2) /* capacitor of size 10*5 units */

The unit capacitor is specified in the netlist configuration file; if it is unspecified, the value of 1fF is used.

Negation-normal form for a Boolean expression corresponds to a Boolean expression where the negation symbol can only appear on a variable. In other words, an expression like ~(a|b) is not in negation-normal form; it can easily be converted to negation-normal form using DeMorgan's rule to obtain ~a&~b.