This is an old revision of the document!

prs2net: automated netlist generation

The tool prs2net can be used to automatically generate a hierarchical spice netlist from an .act file. Parameters that control defaults and the output format are specified in a configuration file.

The overall way the tool works is as follows. First, it reads in the .act file. This file is then “expanded”, which means that all parameters are expanded out. This requires instances of cells in the top-level file. In general, it might make sense to have a file test.act that imports the necessary act file and instantiates the top-level cell of interest.

Expanded versions of cells have their expanded template attached to their names. In the following example, after expansion, there are two cells: foo (the unexpanded cell), and foo<> (the expanded cell).

    defproc foo(bool a, b)
      prs {
        a => b-
    foo f;

If the instance foo f; were missing, then foo would not be expanded into foo<> (since it is not used).

Basic Netlist Specification

Production rules are syntactically translated into a netlist. The gate order is taken from left to right, where the power supply is the leftmost node and the output is the rightmost node. This means that the rule

   a & b -> c-

gets translated into a netlist that starts from GND, then has transistors gated by a followed by b, and the drain of the final transistor is connected to c. The netlist corresponding to just this rule is given by:

  M0_ GND a #3 GND nch W=0.3U L=0.12U
  M1_ #3 b c GND nch W=0.3U L=0.12U

(The details may vary depending on your configuration file.) A close examination shows the gate ordering a followed by b, where a is closer to GND.

Widths, Lengths, Flavors. The width and length of any transistor can be specified using size specifiers. For the same rule above, we could specify a different width for a as follows:

  a<20> & b -> c-

This says the width of the transistor for a is 20 units (the multiplier is specified in the configuration file). Once a size specifier is provided, it is used by the expression until another specifier is seen. This is convenient, for example, when you need to specify that an entire stack uses the same width.

Length specifiers can also be used to change the default length of a transistor. The same example with a longer transistor (length 5) is given by:

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

Finally, different transistor flavors can also be specified. The currently recognized flavors are:

  • svt : standard Vt
  • lvt : low Vt
  • hvt : high Vt
  • od18 : overdrive to 1.8V
  • od25 : overdrive to 2.5V

The following shows how these can be used:

   a<20,lvt> & b<20> -> c-
   q<10,5,hvt> & r<10,5> -> d-

In this example, the transistor gated by a is low Vt, while the one gated by q is high Vt. All others are standard Vt.

Pass Transistors. Pass transistors are specified using passn, passp, and transgate. These also take the same width/size/flavor specifiers as described above. The syntax of these three constructs is:

  • passn(gate,src,drain): n-type pass transistor
  • passp(gate,src,drain): p-type pass transistor
  • transgate(ngate,pgate,src,drain): a transmission gate with ngate and pgate corresponding to the signals connected to the n-type and p-type transistor respectively (normally they will be complements of each other).

Each of these can contain a size directive as shown in the examples below:

   prs {
     passn<10,3> (g,s,d)
     passp<10,4,lvt> (_g,s,d)

Internal Precharges. Precharges are only on internal nodes, and new internal nodes are only created with an & operator. So how about we say

  a &{+expr} b

for a precharge to Vdd (indicated by +), and

  a &{-expr} b

for a precharge to GND (indicated by -). Also, the type of the expression indicates which transistor type gets used. So, for instance

  a &{+en} b -> c-

specifies a precharge to Vdd with n-type transistors (since en is uninverted). The same precharge with p-type transistors would be written

  a &{+~_en} b -> c-

(assuming _en was the complement of en.)

Shared Gate Networks

The syntax supports building arbitrary shared-gate networks. This can be done via labels, indicated with the @ symbol. To illustrate, the following example builds a shared gate network:

 a & b -> @x-
 ~@x & w -> r0-
 ~@x & r -> r1-

The idea is that “@” is a label for the internal node. Note that @'s always appeared in the “wrong sense” in the production rule. The label @x should not be confused with a bool variable. This label is only internal to the production rule body.

The second construct is

tree {
  a & b & w -> r0-
  a & b & r -> r1-

This specifies maximal sharing, i.e. build the shared-gate network by maximal prefix-sharing within the group. Finally, we can limit the size of the prefix by specifying a sharing-limit as follows

tree<1> {
  a & b & w -> r0-
  a & b & r -> r1-


Placing parts of a production rule set into a sub-circuit simplifies the job of a layout engineer because it is easier to view the netlist at a slightly higher level of abstraction. To support this, we allow the automated generation of one level of hierarchy using the subckt construct.

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

places the C-element in a sub-cell that is automatically generated. The sub-circuit is generated in isolation, so this has a few implications:

  • State-holding/combinational behavior of an operator is determined in isolation within the sub-circuit. So if you have multiple production rules for a bool, make sure they are either all within the subckt or all outside it.
  • For a state-holding operator, the staticizer is generated within the sub-circuit. This means that if there is an inverter outside the sub-circuit, it will not be used by the staticizer within the sub-circuit.


Attributes can be attached to a production rule or pass transistor/transmission gate using standard .act syntax. Attributes currently recognized by netgen are available on the complete attribute list. Some commonly used ones are:

  • keeper=0 : do not generate a keeper for this node, even if it looks like a state-holding gate.
  • comb=1 : use combinational feedback rather than a standard staticizer if the node is state-holding.
  • output=v : used for pass transistors to force either the source, drain, or both to be labelled as an output. This is useful for the PININFO directive emitted by netgen. If the value v has bit zero set, then the source is marked as an output node. If v has bit one set, then the drain is marked as an output. By default, a node is considered an input if it does not have a production rule driving it.
  • loadcap=v : sets the load capacitance on the output to be a the specified value in fF. This is used to override the default setting specified in the configuration file. Note that netgen will use the last production rule's loadcap! So if you have a pullup and pulldown rule, and the pulldown rule is 2nd in your ACT file, you MUST put loadcap in the pulldown rule. Safest bet is to put it in all your PRS.

The following shows how you can specify two C-elements, one with no staticizer, and another with combinational feedback.

    prs {
      [keeper=0] a & b #> c-
      [comb=1] p & q #> r-